From: Junhan Bae Date: Tue, 12 Feb 2019 04:53:33 +0000 (+0900) Subject: [RAMEN9610-12059] ccic : sync s2mu106 ccic code X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=eefd96ddbb95d99e5956cda0f8bdf8f31991693a;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [RAMEN9610-12059] ccic : sync s2mu106 ccic code Change-Id: I3fc4fee2eccd286d6dad4eab33746c2a30ba7261 Signed-off-by: Junhan Bae --- diff --git a/drivers/ccic/Makefile b/drivers/ccic/Makefile index 1502c7a3c8c6..5c422fc5620a 100644 --- a/drivers/ccic/Makefile +++ b/drivers/ccic/Makefile @@ -10,3 +10,7 @@ obj-$(CONFIG_CCIC_S2MM005) += s2mm005_fw.o s2mm005_cc.o s2mm005_pd.o sec_pd.o s obj-$(CONFIG_CCIC_MAX77705) += max77705_cc.o max77705_pd.o sec_pd.o max77705_usbc.o max77705_alternate.o obj-$(CONFIG_CCIC_MAX77705_DEBUG) += max77705_debug.o obj-$(CONFIG_USBPD_S2MM005) += s2mm005_usbpd_fw.o s2mm005_usbpd_phy.o s2mm005_usbpd.o s2mm005_usbpd_i2c.o s2mm005_usbpd_interface.o usbpd_sysfs.o +obj-$(CONFIG_CCIC_S2MU106) += s2mu106-usbpd.o +obj-$(CONFIG_USE_CCIC) += usbpd.o usbpd_cc.o +obj-$(CONFIG_USE_CCIC) += usbpd_policy.o usbpd_manager.o +obj-$(CONFIG_CCIC_SYSFS) += usbpd_sysfs.o diff --git a/drivers/ccic/s2mm005_usbpd_phy.c b/drivers/ccic/s2mm005_usbpd_phy.c index 5b9ed2e1d2af..60b792dddead 100644 --- a/drivers/ccic/s2mm005_usbpd_phy.c +++ b/drivers/ccic/s2mm005_usbpd_phy.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include static char VDM_MSG_IRQ_State_Print[9][40] = { {"bFLAG_Vdm_Reserve_b0"}, diff --git a/drivers/ccic/s2mu106-usbpd.c b/drivers/ccic/s2mu106-usbpd.c new file mode 100644 index 000000000000..133ce1c6fde7 --- /dev/null +++ b/drivers/ccic/s2mu106-usbpd.c @@ -0,0 +1,2886 @@ +/* + driver/usbpd/s2mu106.c - s2mu106 USB PD(Power Delivery) device driver + * + * Copyright (C) 2018 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#if defined(CONFIG_MUIC_NOTIFIER) +#include +#endif /* CONFIG_MUIC_NOTIFIER */ +#ifdef CONFIG_BATTERY_SAMSUNG +#include +#include +#else +#include +#endif +#if defined(CONFIG_USB_HOST_NOTIFY) || defined(CONFIG_USB_HW_PARAM) +#include +#endif +#include + +#if (defined CONFIG_IFCONN_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF) +#include +#endif +#if defined CONFIG_IFCONN_NOTIFIER +struct pdic_notifier_data pd_noti; +#endif +/* +*VARIABLE DEFINITION +*/ +static usbpd_phy_ops_type s2mu106_ops; +struct i2c_client *test_i2c; + +static enum power_supply_property ccic_props[] = { +}; + +static char *ccic_supplied_to[] = { + "battery", +}; +/* +*FUNCTION DEFINITION +*/ +static int s2mu106_receive_message(void *data); +static int s2mu106_check_port_detect(struct s2mu106_usbpd_data *pdic_data); +static int s2mu106_usbpd_reg_init(struct s2mu106_usbpd_data *_data); +static void s2mu106_dfp(struct i2c_client *i2c); +static void s2mu106_ufp(struct i2c_client *i2c); +#ifdef CONFIG_CCIC_VDM +static int s2mu106_usbpd_check_vdm_msg(void *_data, u64 *val); +#endif +static void s2mu106_src(struct i2c_client *i2c); +static void s2mu106_snk(struct i2c_client *i2c); +static void s2mu106_assert_rd(void *_data); +static void s2mu106_assert_rp(void *_data); +static int s2mu106_set_attach(struct s2mu106_usbpd_data *pdic_data, u8 mode); +static int s2mu106_set_detach(struct s2mu106_usbpd_data *pdic_data, u8 mode); +static void s2mu106_usbpd_check_rid(struct s2mu106_usbpd_data *pdic_data); +static int s2mu106_usbpd_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +static int s2mu106_usbpd_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +#ifndef CONFIG_SEC_FACTORY +static void s2mu106_usbpd_set_threshold(struct s2mu106_usbpd_data *pdic_data, + CCIC_RP_RD_SEL port_sel, CCIC_THRESHOLD_SEL threshold_sel); +static void s2mu106_usbpd_notify_detach(struct s2mu106_usbpd_data *pdic_data); +#endif +static void s2mu106_usbpd_detach_init(struct s2mu106_usbpd_data *pdic_data); +static int s2mu106_usbpd_set_cc_control(struct s2mu106_usbpd_data *pdic_data, int val); + +char *rid_text[] = { + "UNDEFINED", + "RID ERROR", + "RID ERROR", + "RID 255K", + "RID 301K", + "RID 523K", + "RID 619K" +}; + +static void s2mu106_usbpd_test_read(struct s2mu106_usbpd_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + u8 data[10]; + + s2mu106_usbpd_read_reg(i2c, 0x1, &data[0]); + s2mu106_usbpd_read_reg(i2c, 0x18, &data[1]); + s2mu106_usbpd_read_reg(i2c, 0x27, &data[2]); + s2mu106_usbpd_read_reg(i2c, 0x28, &data[3]); + s2mu106_usbpd_read_reg(i2c, 0x40, &data[4]); + s2mu106_usbpd_read_reg(i2c, 0xe2, &data[5]); + s2mu106_usbpd_read_reg(i2c, 0xb3, &data[6]); + s2mu106_usbpd_read_reg(i2c, 0xb4, &data[7]); + + pr_info("%s, 0x1(%x) 0x18(%x) 0x27(%x) 0x28(%x) 0x40(%x) 0xe2(%x) 0xb3(%x) 0xb4(%x)\n", + __func__, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); +} + +void s2mu106_rprd_mode_change(struct s2mu106_usbpd_data *usbpd_data, u8 mode) +{ + u8 data = 0; + struct i2c_client *i2c = usbpd_data->i2c; + pr_info("%s, mode=0x%x\n", __func__, mode); + + mutex_lock(&usbpd_data->lpm_mutex); + if (usbpd_data->lpm_mode) + goto skip; + + switch (mode) { + case TYPE_C_ATTACH_DFP: /* SRC */ + s2mu106_set_detach(usbpd_data, mode); + msleep(S2MU106_ROLE_SWAP_TIME_MS); + s2mu106_set_attach(usbpd_data, mode); + break; + case TYPE_C_ATTACH_UFP: /* SNK */ + s2mu106_set_detach(usbpd_data, mode); + msleep(S2MU106_ROLE_SWAP_TIME_MS); + s2mu106_set_attach(usbpd_data, mode); + break; + case TYPE_C_ATTACH_DRP: /* DRP */ + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data |= S2MU106_REG_PLUG_CTRL_DRP; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + break; + }; +skip: + mutex_unlock(&usbpd_data->lpm_mutex); +} + +void vbus_turn_on_ctrl(struct s2mu106_usbpd_data *usbpd_data, bool enable) +{ + struct power_supply *psy_otg; + union power_supply_propval val; + int on = !!enable; + int ret = 0, retry_cnt = 0; + + pr_info("%s %d, enable=%d\n", __func__, __LINE__, enable); + psy_otg = power_supply_get_by_name("otg"); + + if (psy_otg) { + val.intval = enable; + usbpd_data->is_otg_vboost = enable; + ret = psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val); + } else { + pr_err("%s: Fail to get psy battery\n", __func__); + + return; + } + if (ret) { + pr_err("%s: fail to set power_suppy ONLINE property(%d)\n", + __func__, ret); + } else { + if (enable == VBUS_ON) { + for (retry_cnt = 0; retry_cnt < 5; retry_cnt++) { + psy_otg->desc->get_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val); + if (val.intval == VBUS_OFF) { + msleep(100); + val.intval = enable; + psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val); + } else + break; + } + } + pr_info("otg accessory power = %d\n", on); + } + +} + +#if defined(CONFIG_IFCONN_NOTIFIER) +static void process_dr_swap(struct s2mu106_usbpd_data *usbpd_data) +{ + struct i2c_client *i2c = usbpd_data->i2c; + dev_info(&i2c->dev, "%s : before - is_host : %d, is_client : %d\n", + __func__, usbpd_data->is_host, usbpd_data->is_client); + if (usbpd_data->is_host == HOST_ON) { + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_ID_USB, IFCONN_NOTIFY_EVENT_DETACH, NULL); + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP, NULL); + usbpd_data->is_host = HOST_OFF; + usbpd_data->is_client = CLIENT_ON; + } else if (usbpd_data->is_client == CLIENT_ON) { + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_DETACH, NULL); + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_MUIC, IFCONN_NOTIFY_ID_ATTACH, + IFCONN_NOTIFY_EVENT_ATTACH, NULL); + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP, NULL); + usbpd_data->is_host = HOST_ON; + usbpd_data->is_client = CLIENT_OFF; + } + dev_info(&i2c->dev, "%s : after - is_host : %d, is_client : %d\n", + __func__, usbpd_data->is_host, usbpd_data->is_client); +} +#endif + +static int s2mu106_usbpd_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + int ret; + struct device *dev = &i2c->dev; +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) { + dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret); +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + return ret; + } + ret &= 0xff; + *dest = ret; + return 0; +} + +static int s2mu106_usbpd_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + int ret; + struct device *dev = &i2c->dev; +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif +#ifdef CONFIG_SEC_FACTORY + int retry = 0; +#endif + + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); +#ifdef CONFIG_SEC_FACTORY + for (retry = 0; retry < 5; retry++) { + if (ret < 0) { + dev_err(dev, "%s reg(0x%x), ret(%d) retry(%d) after now\n", + __func__, reg, ret, retry); + msleep(40); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + } else + break; + } + + if (ret < 0) { + dev_err(dev, "%s failed to read reg, ret(%d)\n", __func__, ret); +#else + if (ret < 0) { + dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret); +#endif + +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + return ret; + } + + return 0; +} + +static int s2mu106_usbpd_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + int ret; + struct device *dev = &i2c->dev; +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + ret = i2c_smbus_write_byte_data(i2c, reg, value); + if (ret < 0) { + dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret); +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + } + return ret; +} + +static int s2mu106_usbpd_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + int ret; + struct device *dev = &i2c->dev; +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + if (ret < 0) { + dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret); +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT); +#endif + return ret; + } + return 0; +} + +static int s2mu106_usbpd_update_bit(struct i2c_client *i2c, + u8 reg, u8 mask, u8 shift, u8 value) +{ + int ret; + u8 reg_val = 0; + + ret = s2mu106_usbpd_read_reg(i2c, reg, ®_val); + if (ret < 0) { + pr_err("%s: Reg = 0x%X, val = 0x%X, read err : %d\n", + __func__, reg, reg_val, ret); + } + reg_val &= ~mask; + reg_val |= value << shift; + ret = s2mu106_usbpd_write_reg(i2c, reg, reg_val); + if (ret < 0) { + pr_err("%s: Reg = 0x%X, mask = 0x%X, val = 0x%X, write err : %d\n", + __func__, reg, mask, value, ret); + } + + return ret; +} + +static int s2mu106_write_msg_header(struct i2c_client *i2c, u8 *buf) +{ + int ret; + + ret = s2mu106_usbpd_bulk_write(i2c, S2MU106_REG_MSG_TX_HEADER_L, 2, buf); + + return ret; +} + +static int s2mu106_write_msg_obj(struct i2c_client *i2c, int count, data_obj_type *obj) +{ + int ret = 0; + int i = 0; + struct device *dev = &i2c->dev; + + if (count > S2MU106_MAX_NUM_MSG_OBJ) + dev_err(dev, "%s, not invalid obj count number\n", __func__); + else + for (i = 0; i < count; i++) { + ret = s2mu106_usbpd_bulk_write(i2c, + S2MU106_REG_MSG_TX_OBJECT0_0_L + (4 * i), + 4, obj[i].byte); + } + + return ret; +} + +static int s2mu106_send_msg(struct i2c_client *i2c) +{ + int ret; + u8 reg = S2MU106_REG_MSG_SEND_CON; + u8 val = S2MU106_REG_MSG_SEND_CON_OP_MODE + | S2MU106_REG_MSG_SEND_CON_SEND_MSG_EN; + + s2mu106_usbpd_write_reg(i2c, reg, val); + + ret = s2mu106_usbpd_write_reg(i2c, reg, S2MU106_REG_MSG_SEND_CON_OP_MODE); + + return ret; +} + +static int s2mu106_read_msg_header(struct i2c_client *i2c, msg_header_type *header) +{ + int ret; + + ret = s2mu106_usbpd_bulk_read(i2c, S2MU106_REG_MSG_RX_HEADER_L, 2, header->byte); + + return ret; +} + +static int s2mu106_read_msg_obj(struct i2c_client *i2c, int count, data_obj_type *obj) +{ + int ret = 0; + int i = 0; + struct device *dev = &i2c->dev; + + if (count > S2MU106_MAX_NUM_MSG_OBJ) { + dev_err(dev, "%s, not invalid obj count number\n", __func__); + ret = -EINVAL; /*TODO: check fail case */ + } else { + for (i = 0; i < count; i++) { + ret = s2mu106_usbpd_bulk_read(i2c, + S2MU106_REG_MSG_RX_OBJECT0_0_L + (4 * i), + 4, obj[i].byte); + } + } + + return ret; +} + +static void s2mu106_set_irq_enable(struct s2mu106_usbpd_data *_data, + u8 int0, u8 int1, u8 int2, u8 int3, u8 int4, u8 int5) +{ + u8 int_mask[S2MU106_MAX_NUM_INT_STATUS] + = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + int ret = 0; + struct i2c_client *i2c = _data->i2c; + struct device *dev = &i2c->dev; + + int_mask[0] &= ~int0; + int_mask[1] &= ~int1; + int_mask[2] &= ~int2; + int_mask[3] &= ~int3; + int_mask[4] &= ~int4; + int_mask[5] &= ~int5; + + ret = i2c_smbus_write_i2c_block_data(i2c, S2MU106_REG_INT_MASK0, + S2MU106_MAX_NUM_INT_STATUS, int_mask); + + if (ret < 0) + dev_err(dev, "err write interrupt mask \n"); +} + +static void s2mu106_self_soft_reset(struct i2c_client *i2c) +{ + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_ETC, + S2MU106_REG_ETC_SOFT_RESET_EN); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_ETC, + S2MU106_REG_ETC_SOFT_RESET_DIS); +} + +static void s2mu106_driver_reset(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + int i; + + pdic_data->status_reg = 0; + data->wait_for_msg_arrived = 0; + pdic_data->header.word = 0; + for (i = 0; i < S2MU106_MAX_NUM_MSG_OBJ; i++) + pdic_data->obj[i].object = 0; + + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5); +} + +static void s2mu106_assert_drp(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 val; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &val); + val &= ~S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, val); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &val); + val &= ~S2MU106_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, val); +} + +static void s2mu106_assert_rd(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 val; + u8 cc1_val, cc2_val; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, &val); + + cc1_val = val & S2MU106_REG_CTRL_MON_CC1_MASK; + cc2_val = (val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + if (cc1_val == 2) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &val); + val = (val & ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK) | + S2MU106_REG_PLUG_CTRL_CC1_MANUAL_ON; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, val); + + if (pdic_data->vconn_en) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &val); + val = (val & ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK) | + S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN | + S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, val); + } + } + + if (cc2_val == 2) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &val); + val = (val & ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK) | + S2MU106_REG_PLUG_CTRL_CC2_MANUAL_ON; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, val); + + if (pdic_data->vconn_en) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &val); + val = (val & ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK) | + S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN | + S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, val); + } + } + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &val); + val &= ~S2MU106_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK; + val |= S2MU106_REG_PLUG_CTRL_FSM_ATTACHED_SNK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, val); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &val); + val |= S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, val); +} + +static void s2mu106_assert_rp(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 val; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &val); + val &= ~S2MU106_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK; + val |= S2MU106_REG_PLUG_CTRL_FSM_ATTACHED_SRC; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, val); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &val); + val |= S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, val); +} + +static unsigned s2mu106_get_status(void *_data, unsigned flag) +{ + unsigned ret; + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + if (pdic_data->status_reg & flag) { + ret = pdic_data->status_reg & flag; + pdic_data->status_reg &= ~flag; /* clear the flag */ + return ret; + } else { + return 0; + } +} + +static bool s2mu106_poll_status(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct policy_data *policy = &data->policy; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + u8 intr[S2MU106_MAX_NUM_INT_STATUS] = {0}; + int ret = 0, retry = 0; + u64 status_reg_val = 0; + + ret = s2mu106_usbpd_bulk_read(i2c, S2MU106_REG_INT_STATUS0, + S2MU106_MAX_NUM_INT_STATUS, intr); + + dev_info(dev, "%s status[0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x]\n", + __func__, intr[0], intr[1], intr[2], intr[3], intr[4], intr[5], intr[6]); + + if ((intr[0] | intr[1] | intr[2] | intr[3] | intr[4] | intr[5]) == 0) { + status_reg_val |= MSG_NONE; + goto out; + } + + if ((intr[2] & S2MU106_REG_INT_STATUS2_WAKEUP) || + (intr[4] & S2MU106_REG_INT_STATUS4_CC12_DET_IRQ)) + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5); + + if (intr[5] & S2MU106_REG_INT_STATUS5_HARD_RESET) { + status_reg_val |= MSG_HARDRESET; + goto out; + } + + /* when occur detach & attach atomic */ + if (intr[4] & S2MU106_REG_INT_STATUS4_USB_DETACH) { + status_reg_val |= PLUG_DETACH; + } + + mutex_lock(&pdic_data->lpm_mutex); + if ((intr[4] & S2MU106_REG_INT_STATUS4_PLUG_IRQ) && + !pdic_data->lpm_mode && !pdic_data->is_water_detect) + status_reg_val |= PLUG_ATTACH; + else if (pdic_data->lpm_mode && + (intr[4] & S2MU106_REG_INT_STATUS4_PLUG_IRQ) && + !pdic_data->is_water_detect) + retry = 1; + mutex_unlock(&pdic_data->lpm_mutex); + + if (retry) { + msleep(40); + mutex_lock(&pdic_data->lpm_mutex); + if ((intr[4] & S2MU106_REG_INT_STATUS4_PLUG_IRQ) && + !pdic_data->lpm_mode && !pdic_data->is_water_detect) + status_reg_val |= PLUG_ATTACH; + mutex_unlock(&pdic_data->lpm_mutex); + } + +#ifdef CONFIG_CCIC_VDM + /* function that support dp control */ + if (pdic_data->check_msg_pass) { + if (intr[4] & S2MU106_REG_INT_STATUS4_MSG_PASS) + status_reg_val |= MSG_PASS; + } +#endif +/* #if defined(CONFIG_CCIC_FACTORY) */ + if ((intr[4] & S2MU106_REG_INT_STATUS4_MSG_PASS) && + (intr[3] & S2MU106_REG_INT_STATUS3_UNS_CMD_DATA)) { + status_reg_val |= MSG_RID; + } +/* #endif */ + + if (intr[0] & S2MU106_REG_INT_STATUS0_MSG_GOODCRC + || intr[4] & S2MU106_REG_INT_STATUS4_MSG_SENT) + status_reg_val |= MSG_GOODCRC; + + if (intr[0] & S2MU106_REG_INT_STATUS0_MSG_ACCEPT) + status_reg_val |= MSG_ACCEPT; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_PSRDY) + status_reg_val |= MSG_PSRDY; + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_REQUEST) + status_reg_val |= MSG_REQUEST; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_REJECT) + status_reg_val |= MSG_REJECT; + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_WAIT) + status_reg_val |= MSG_WAIT; + + if (intr[4] & S2MU106_REG_INT_STATUS4_MSG_ERROR) + status_reg_val |= MSG_ERROR; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_PING) + status_reg_val |= MSG_PING; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_GETSNKCAP) + status_reg_val |= MSG_GET_SNK_CAP; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_GETSRCCAP) + status_reg_val |= MSG_GET_SRC_CAP; + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_SRC_CAP) { + if (!policy->plug_valid) + pdic_data->status_reg |= PLUG_ATTACH; + status_reg_val |= MSG_SRC_CAP; + } + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_SNK_CAP) + status_reg_val |= MSG_SNK_CAP; + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_SOFTRESET) + status_reg_val |= MSG_SOFTRESET; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_PR_SWAP) + status_reg_val |= MSG_PR_SWAP; + + if (intr[2] & S2MU106_REG_INT_STATUS2_MSG_VCONN_SWAP) + status_reg_val |= MSG_VCONN_SWAP; + + if (intr[1] & S2MU106_REG_INT_STATUS1_MSG_DR_SWAP) + status_reg_val |= MSG_DR_SWAP; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_DISCOVER_ID) + status_reg_val |= VDM_DISCOVER_IDENTITY; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_DISCOVER_SVID) + status_reg_val |= VDM_DISCOVER_SVID; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_DISCOVER_MODE) + status_reg_val |= VDM_DISCOVER_MODE; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_ENTER) + status_reg_val |= VDM_ENTER_MODE; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_EXIT) + status_reg_val |= VDM_EXIT_MODE; + + if (intr[0] & S2MU106_REG_INT_STATUS0_VDM_ATTENTION) + status_reg_val |= VDM_ATTENTION; +/* disable function that support dp control */ +#ifdef CONFIG_CCIC_VDM + /* read message if data object message */ + if (status_reg_val & + (MSG_REQUEST | MSG_SNK_CAP + | VDM_DISCOVER_IDENTITY | VDM_DISCOVER_SVID + | VDM_DISCOVER_MODE | VDM_ENTER_MODE | VDM_EXIT_MODE + | VDM_ATTENTION | MSG_PASS)) { + usbpd_protocol_rx(data); + if (status_reg_val & MSG_PASS) + s2mu106_usbpd_check_vdm_msg(data, &status_reg_val); + } +#else + /* read message if data object message */ + if (status_reg_val & + (MSG_REQUEST | MSG_SNK_CAP + | VDM_DISCOVER_IDENTITY | VDM_DISCOVER_SVID + | VDM_DISCOVER_MODE | VDM_ENTER_MODE | VDM_EXIT_MODE + | VDM_ATTENTION)) { + usbpd_protocol_rx(data); + } +#endif +out: + pdic_data->status_reg |= status_reg_val; + + /* complete wait msg */ + if (pdic_data->status_reg & data->wait_for_msg_arrived) { + data->wait_for_msg_arrived = 0; + complete(&data->msg_arrived); + } + + return 0; +} + +static int s2mu106_hard_reset(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + int ret; + u8 reg; + + if (pdic_data->rid != REG_RID_UNDF && pdic_data->rid != REG_RID_MAX) + return 0; + + reg = S2MU106_REG_MSG_SEND_CON; + + ret = s2mu106_usbpd_write_reg(i2c, reg, S2MU106_REG_MSG_SEND_CON_SOP_HardRST + | S2MU106_REG_MSG_SEND_CON_OP_MODE); + if (ret < 0) + goto fail; + udelay(5); + ret = s2mu106_usbpd_write_reg(i2c, reg, S2MU106_REG_MSG_SEND_CON_SOP_HardRST + | S2MU106_REG_MSG_SEND_CON_OP_MODE + | S2MU106_REG_MSG_SEND_CON_SEND_MSG_EN); + if (ret < 0) + goto fail; + udelay(1); + ret = s2mu106_usbpd_write_reg(i2c, reg, S2MU106_REG_MSG_SEND_CON_OP_MODE); + udelay(1); + ret = s2mu106_usbpd_write_reg(i2c, reg, S2MU106_RESET_REG_00); + if (ret < 0) + goto fail; + + s2mu106_self_soft_reset(i2c); + + pdic_data->status_reg = 0; + + return 0; + +fail: + return -EIO; +} + +static int s2mu106_receive_message(void *data) +{ + struct s2mu106_usbpd_data *pdic_data = data; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + int obj_num = 0; + int ret = 0; + + ret = s2mu106_read_msg_header(i2c, &pdic_data->header); + if (ret < 0) + dev_err(dev, "%s read msg header error\n", __func__); + + obj_num = pdic_data->header.num_data_objs; + + if (obj_num > 0) { + ret = s2mu106_read_msg_obj(i2c, + obj_num, &pdic_data->obj[0]); + } + + return ret; +} + +static int s2mu106_tx_msg(void *_data, + msg_header_type *header, data_obj_type *obj) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + int ret = 0; + int count = 0; + u8 reg_data = 0; + u8 msg_id = 0; + + mutex_lock(&pdic_data->_mutex); + + /* if there is no attach, skip tx msg */ + if (pdic_data->detach_valid) + goto done; + + /* using msg id counter at s2mu106 */ + s2mu106_usbpd_read_reg(pdic_data->i2c, S2MU106_REG_ID_MONITOR, ®_data); + msg_id = reg_data & S2MU106_REG_ID_MONITOR_MSG_ID_MASK; + header->msg_id = msg_id; + + ret = s2mu106_write_msg_header(i2c, header->byte); + if (ret < 0) + goto done; + + count = header->num_data_objs; + + if (count > 0) { + ret = s2mu106_write_msg_obj(i2c, count, obj); + if (ret < 0) + goto done; + } + + s2mu106_send_msg(i2c); + + pdic_data->status_reg = 0; + data->wait_for_msg_arrived = 0; + +done: + mutex_unlock(&pdic_data->_mutex); + return ret; +} + +static int s2mu106_rx_msg(void *_data, + msg_header_type *header, data_obj_type *obj) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + int i; + int count = 0; + + if (!s2mu106_receive_message(pdic_data)) { + header->word = pdic_data->header.word; + count = pdic_data->header.num_data_objs; + if (count > 0) { + for (i = 0; i < count; i++) + obj[i].object = pdic_data->obj[i].object; + } + pdic_data->header.word = 0; /* To clear for duplicated call */ + return 0; + } else { + return -EINVAL; + } +} + +static int s2mu106_set_otg_control(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + if (val) + vbus_turn_on_ctrl(pdic_data, VBUS_ON); + else + vbus_turn_on_ctrl(pdic_data, VBUS_OFF); + + return 0; +} + +static int s2mu106_set_cc_control(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + return s2mu106_usbpd_set_cc_control(pdic_data, val); +} + +#if defined(CONFIG_CHECK_CTYPE_SIDE) || defined(CONFIG_CCIC_SYSFS) +static int s2mu106_get_side_check(void *_data) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 val, cc1_val, cc2_val; + + s2mu106_usbpd_test_read(pdic_data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, &val); + + cc1_val = val & S2MU106_REG_CTRL_MON_CC1_MASK; + cc2_val = (val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + if (cc1_val == USBPD_Rd) + return USBPD_UP_SIDE; + else if (cc2_val == USBPD_Rd) + return USBPD_DOWN_SIDE; + else + return USBPD_UNDEFFINED_SIDE; +} +#endif +static int s2mu106_set_vconn_source(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 reg_data = 0, reg_val = 0, cc1_val = 0, cc2_val = 0; + + if (!pdic_data->vconn_en) { + pr_err("%s, not support vconn source\n", __func__); + return -1; + } + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, ®_val); + cc1_val = (reg_val & S2MU106_REG_CTRL_MON_CC1_MASK) >> S2MU106_REG_CTRL_MON_CC1_SHIFT; + cc2_val = (reg_val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + if (val == USBPD_VCONN_ON) { + if (cc1_val == USBPD_Rd) { + if (cc2_val == USBPD_Ra) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, ®_data); + reg_data &= ~S2MU106_REG_PLUG_CTRL_RpRd_VCONN_MASK; + reg_data |= (S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN | + S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, reg_data); + } + } + if (cc2_val == USBPD_Rd) { + if (cc1_val == USBPD_Ra) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, ®_data); + reg_data &= ~S2MU106_REG_PLUG_CTRL_RpRd_VCONN_MASK; + reg_data |= (S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN | + S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, reg_data); + } + } + } else if (val == USBPD_VCONN_OFF) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, ®_data); + reg_data &= ~S2MU106_REG_PLUG_CTRL_RpRd_VCONN_MASK; + reg_data |= S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, reg_data); + } else + return(-1); + + pdic_data->vconn_source = val; + return 0; +} + +static void s2mu106_usbpd_set_vconn_manual(struct s2mu106_usbpd_data *pdic_data, bool enable) +{ + u8 reg_data = 0; + + s2mu106_usbpd_read_reg(pdic_data->i2c, S2MU106_REG_PLUG_CTRL_RpRd, ®_data); + reg_data &= ~S2MU106_REG_PLUG_CTRL_RpRd_VCONN_MASK; + + if (enable) + reg_data |= S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN; + + s2mu106_usbpd_write_reg(pdic_data->i2c, S2MU106_REG_PLUG_CTRL_RpRd, reg_data); +} + +static int s2mu106_get_vconn_source(void *_data, int *val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + /* TODO + set s2mu106 pdic register control */ + + if (pdic_data->vconn_source != *val) { + dev_info(pdic_data->dev, "%s, vconn_source(%d) != gpio val(%d)\n", + __func__, pdic_data->vconn_source, *val); + pdic_data->vconn_source = *val; + } + + return 0; +} + +/* val : sink(0) or source(1) */ +static int s2mu106_set_power_role(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + pr_info("%s, power_role(%d)\n", __func__, val); + + if (val == USBPD_SINK) { + pdic_data->is_pr_swap = true; + s2mu106_assert_rd(data); + s2mu106_snk(pdic_data->i2c); + } else if (val == USBPD_SOURCE) { + pdic_data->is_pr_swap = true; + s2mu106_assert_rp(data); + s2mu106_src(pdic_data->i2c); + } else if (val == USBPD_DRP) { + pdic_data->is_pr_swap = false; + s2mu106_assert_drp(data); + return 0; + } else + return(-1); + + pdic_data->power_role = val; + return 0; +} + +static int s2mu106_get_power_role(void *_data, int *val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + *val = pdic_data->power_role; + + return 0; +} + +static int s2mu106_set_data_role(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + struct i2c_client *i2c = pdic_data->i2c; + u8 val_port, data_role; + + /* DATA_ROLE (0x18[2]) + * 0 : UFP + * 1 : DFP + */ + if (val == USBPD_UFP) { + data_role = S2MU106_REG_MSG_DATA_ROLE_UFP; + s2mu106_ufp(i2c); + } else {/* (val == USBPD_DFP) */ + data_role = S2MU106_REG_MSG_DATA_ROLE_DFP; + s2mu106_dfp(i2c); + } + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, &val_port); + val_port = (val_port & ~S2MU106_REG_MSG_DATA_ROLE_MASK) | data_role; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, val_port); + + pdic_data->data_role = val; + +#if defined(CONFIG_IFCONN_NOTIFIER) + process_dr_swap(pdic_data); +#endif + return 0; +} + +static int s2mu106_get_data_role(void *_data, int *val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + *val = pdic_data->data_role; + return 0; +} + +static void s2mu106_get_vbus_short_check(void *_data, bool *val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + *val = pdic_data->vbus_short; +} + +static int s2mu106_set_check_msg_pass(void *_data, int val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + struct s2mu106_usbpd_data *pdic_data = data->phy_driver_data; + + dev_info(pdic_data->dev, "%s: check_msg_pass val(%d)\n", __func__, val); + + pdic_data->check_msg_pass = val; + + return 0; +} +#ifndef CONFIG_SEC_FACTORY +static void s2mu106_usbpd_set_threshold(struct s2mu106_usbpd_data *pdic_data, + CCIC_RP_RD_SEL port_sel, CCIC_THRESHOLD_SEL threshold_sel) +{ + struct i2c_client *i2c = pdic_data->i2c; + + if (threshold_sel > S2MU106_THRESHOLD_MAX) { + dev_err(pdic_data->dev, "%s : threshold overflow!!\n", __func__); + return; + } else { + if (port_sel == PLUG_CTRL_RD) + s2mu106_usbpd_write_reg(i2c, + S2MU106_REG_PLUG_CTRL_SET_RD, threshold_sel); + else if (port_sel == PLUG_CTRL_RP) + s2mu106_usbpd_write_reg(i2c, + S2MU106_REG_PLUG_CTRL_SET_RP, threshold_sel); + } +} + +static void s2mu106_usbpd_set_rp_scr_sel(struct s2mu106_usbpd_data *pdic_data, + CCIC_RP_SCR_SEL scr_sel) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 data = 0; + pr_info("%s: scr_sel : (%d)\n", __func__, scr_sel); + switch (scr_sel) { + case PLUG_CTRL_RP80: + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~S2MU106_REG_PLUG_CTRL_RP_SEL_MASK; + data |= S2MU106_REG_PLUG_CTRL_RP80; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + s2mu106_usbpd_set_threshold(pdic_data, PLUG_CTRL_RD, + S2MU106_THRESHOLD_214MV); + s2mu106_usbpd_set_threshold(pdic_data, PLUG_CTRL_RP, + S2MU106_THRESHOLD_1628MV); + break; + case PLUG_CTRL_RP180: + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~S2MU106_REG_PLUG_CTRL_RP_SEL_MASK; + data |= S2MU106_REG_PLUG_CTRL_RP180; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + s2mu106_usbpd_set_threshold(pdic_data, PLUG_CTRL_RD, + S2MU106_THRESHOLD_428MV); + s2mu106_usbpd_set_threshold(pdic_data, PLUG_CTRL_RP, + S2MU106_THRESHOLD_2057MV); + break; + default: + break; + } + return; +} +#endif + +#ifdef CONFIG_CCIC_VDM +int s2mu106_usbpd_check_vdm_msg(void *_data, u64 *val) +{ + struct usbpd_data *data = (struct usbpd_data *) _data; + int vdm_command = 0, vdm_type = 0; + + dev_info(data->dev, "%s ++\n", __func__); + if (data->protocol_rx.msg_header.num_data_objs == 0) { + dev_info(data->dev, "%s data_obj null\n", __func__); + return 0; + } + + if (data->protocol_rx.msg_header.msg_type != USBPD_Vendor_Defined) { + dev_info(data->dev, "%s msg type is wrong\n", __func__); + return 0; + } + + vdm_command = data->protocol_rx.data_obj[0].structured_vdm.command; + vdm_type = data->protocol_rx.data_obj[0].structured_vdm.vdm_type; + + if (vdm_type == Unstructured_VDM) { + dev_info(data->dev, "%s : uvdm msg received!\n", __func__); + *val |= UVDM_MSG; + return 0; + } + +#if 0 + switch (vdm_command) { + case DisplayPort_Status_Update: + *val |= VDM_DP_STATUS_UPDATE; + break; + case DisplayPort_Configure: + *val |= VDM_DP_CONFIGURE; + break; + default: + return 0; + } +#endif + dev_info(data->dev, "%s: check vdm mag val(%d)\n", __func__, vdm_command); + + return 0; +} +#endif + +static int s2mu106_usbpd_set_cc_control(struct s2mu106_usbpd_data *pdic_data, int val) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 data = 0; + + dev_info(pdic_data->dev, "%s, (%d)\n", __func__, val); + mutex_lock(&pdic_data->cc_mutex); + + if (pdic_data->detach_valid) + goto out; + + val = 1; + + if (val) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &data); + data &= ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, data); + } else { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, &data); + data &= ~S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK; + data |= S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC12, data); + } +out: + mutex_unlock(&pdic_data->cc_mutex); + + return 0; +} + +static void s2mu106_dfp(struct i2c_client *i2c) +{ + u8 data; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, &data); + data |= S2MU106_REG_MSG_DATA_ROLE_MASK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, data); +} + +static void s2mu106_ufp(struct i2c_client *i2c) +{ + u8 data; + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, &data); + data &= ~S2MU106_REG_MSG_DATA_ROLE_MASK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, data); +} + +static void s2mu106_src(struct i2c_client *i2c) +{ + u8 data; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, &data); + data = (data & ~S2MU106_REG_MSG_POWER_ROLE_MASK) | S2MU106_REG_MSG_POWER_ROLE_SOURCE; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, data); +} + +static void s2mu106_snk(struct i2c_client *i2c) +{ + u8 data; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, &data); + data = (data & ~S2MU106_REG_MSG_POWER_ROLE_MASK) | S2MU106_REG_MSG_POWER_ROLE_SINK; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_MSG, data); +} + +#if defined(CONFIG_IFCONN_NOTIFIER) +static void s2mu106_notify_pdic_rid(struct s2mu106_usbpd_data *pdic_data, int rid) +{ + pdic_data->is_factory_mode = false; + if (rid == RID_523K) + pdic_data->is_factory_mode = true; + /* rid */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_RID, rid, NULL); + + if (rid == REG_RID_523K || rid == REG_RID_619K || rid == REG_RID_OPEN) { + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_ATTACH, NULL); + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_ID_USB, IFCONN_NOTIFY_EVENT_DETACH, NULL); + pdic_data->is_host = HOST_OFF; + pdic_data->is_client = CLIENT_OFF; + } else if (rid == REG_RID_301K) { + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP, NULL); + pdic_data->is_host = HOST_OFF; + pdic_data->is_client = CLIENT_ON; + } + + dev_info(pdic_data->dev, "%s : attached rid state(%d)", __func__, rid); +} +#endif + +static void s2mu106_usbpd_check_rid(struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 rid; + int prev_rid = pdic_data->rid; + + msleep(200); + + if (pdic_data->check_rid_wa) + rid = REG_RID_619K; + else { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_ADC_STATUS, &rid); + rid = (rid & S2MU106_PDIC_RID_MASK) >> S2MU106_PDIC_RID_SHIFT; + } + dev_info(pdic_data->dev, "%s : attached rid state(%d)", __func__, rid); + + if (rid) { + if (pdic_data->rid != rid) { + pdic_data->rid = rid; + if (prev_rid >= REG_RID_OPEN && rid >= REG_RID_OPEN) + dev_err(pdic_data->dev, + "%s : rid is not changed, skip notify(%d)", __func__, rid); + else + s2mu106_notify_pdic_rid(pdic_data, rid); + } + + if (rid >= REG_RID_MAX) { + dev_err(pdic_data->dev, "%s : overflow rid value", __func__); + return; + } + } +} + +static int s2mu106_set_attach(struct s2mu106_usbpd_data *pdic_data, u8 mode) +{ + u8 data; + int ret = 0; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= mode | S2MU106_REG_PLUG_CTRL_RP180; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data); + data &= ~S2MU106_REG_LPM_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data); + + dev_info(dev, "%s s2mu106 force to attach\n", __func__); + + return ret; +} + +static int s2mu106_set_detach(struct s2mu106_usbpd_data *pdic_data, u8 mode) +{ + u8 data; + int ret = 0; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + + if (mode == TYPE_C_ATTACH_DFP) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &data); + data |= S2MU106_REG_PLUG_CTRL_RpRd_Rp_Source_Mode; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, data); + } else if (mode == TYPE_C_ATTACH_UFP) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &data); + data |= S2MU106_REG_PLUG_CTRL_RpRd_Rd_Sink_Mode; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, data); + } + + msleep(50); + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, S2MU106_RESET_REG_00); + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DFP | S2MU106_REG_PLUG_CTRL_RP0; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data); + data |= S2MU106_REG_LPM_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data); + + dev_info(dev, "%s s2mu106 force to detach\n", __func__); + + return ret; +} + +int s2mu106_set_normal_mode(struct s2mu106_usbpd_data *pdic_data) +{ + u8 data; + u8 data_lpm; + int ret = 0; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DRP | S2MU106_REG_PLUG_CTRL_RP180; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data_lpm); + data_lpm &= ~S2MU106_REG_LPM_EN; + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data_lpm); + + pdic_data->lpm_mode = false; + + s2mu106_set_irq_enable(pdic_data, 0, 0, + S2MU106_REG_INT_STATUS2_MSG_SRC_CAP, ENABLED_INT_3, + ENABLED_INT_4, 0); + + dev_info(dev, "%s s2mu106 exit lpm mode\n", __func__); + + return ret; +} + +int s2mu106_usbpd_lpm_check(struct s2mu106_usbpd_data *pdic_data) +{ + u8 data_lpm = 0; + struct i2c_client *i2c = pdic_data->i2c; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data_lpm); + + return (data_lpm & S2MU106_REG_LPM_EN); +} + +void s2mu106_usbpd_set_mode(struct s2mu106_usbpd_data *pdic_data, + CCIC_LPM_MODE_SEL mode) +{ + u8 data_lpm = 0; + struct i2c_client *i2c = pdic_data->i2c; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data_lpm); + if (mode == PD_LPM_MODE) + data_lpm |= S2MU106_REG_LPM_EN; + else if (mode == PD_NORMAL_MODE) + data_lpm &= ~S2MU106_REG_LPM_EN; + else { + pr_info("%s mode val(%d) is invalid\n", __func__, mode); + return; + } + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data_lpm); +} + +void s2mu106_usbpd_set_vbus_wakeup(struct s2mu106_usbpd_data *pdic_data, + CCIC_VBUS_WAKEUP_SEL sel) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 data = 0; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_TRIM, &data); + if (sel == VBUS_WAKEUP_ENABLE) + data &= ~S2MU106_REG_VBUS_WAKEUP_DIS; + else if (sel == VBUS_WAKEUP_DISABLE) + data |= S2MU106_REG_VBUS_WAKEUP_DIS; + else { + pr_info("%s sel val(%d) is invalid\n", __func__, sel); + return; + } + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_TRIM, data); +} + +int s2mu106_get_plug_monitor(struct s2mu106_usbpd_data *pdic_data, u8 *data) +{ + u8 reg_val; + int ret = 0; + struct i2c_client *i2c = pdic_data->i2c; + + if (&data[0] == NULL || &data[1] == NULL) { + pr_err("%s NULL point data\n", __func__); + return -1; + } + + ret = s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, ®_val); + if (ret < 0) { + pr_err("%s: S2MU106_REG_PLUG_MON1 Read err : %d\n", __func__, ret); + return ret; + } + + data[0] = reg_val & S2MU106_REG_CTRL_MON_CC1_MASK; + data[1] = (reg_val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + pr_info("%s, water cc mon cc1 : 0x%X, cc2 : 0x%X\n", __func__, data[0], data[1]); + + return ret; +} + +int s2mu106_set_lpm_mode(struct s2mu106_usbpd_data *pdic_data) +{ + u8 data, data_lpm; + int ret = 0; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + u8 intr[S2MU106_MAX_NUM_INT_STATUS] = {0}; + + pdic_data->lpm_mode = true; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DFP | S2MU106_REG_PLUG_CTRL_RP0; + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data_lpm); + data_lpm |= S2MU106_REG_LPM_EN; + + s2mu106_set_irq_enable(pdic_data, 0, 0, 0, 0, 0, 0); + + ret = s2mu106_usbpd_bulk_read(i2c, S2MU106_REG_INT_STATUS0, + S2MU106_MAX_NUM_INT_STATUS, intr); + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data_lpm); + + if (pdic_data->detach_valid == false) { + s2mu106_usbpd_detach_init(pdic_data); + s2mu106_usbpd_notify_detach(pdic_data); + } + + dev_info(dev, "%s s2mu106 enter lpm mode\n", __func__); + + return ret; +} + +void s2mu106_set_water_detect_pre_cond(struct s2mu106_usbpd_data *pdic_data) +{ + int i; + u8 cc_val[2] = {0,}; + + s2mu106_set_normal_mode(pdic_data); + mdelay(10); + + for (i = 0; i < 14; i++) { + if (s2mu106_get_plug_monitor(pdic_data, cc_val) < 0) { + pr_info("%s abnormal", __func__); + mdelay(10); + } else { + if (IS_CC_RP(cc_val[0], cc_val[1])) + break; + else { + pr_info("%s Not Rp yet. ", __func__); + mdelay(10); + } + } + } +} + +void s2mu106_set_water_1st_detect(struct s2mu106_usbpd_data *pdic_data) +{ + u8 data, data_lpm; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + + pdic_data->lpm_mode = true; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RP, S2MU106_THRESHOLD_MAX); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DFP | S2MU106_REG_PLUG_CTRL_RP80 + | S2MU106_REG_PLUG_CTRL_DETECT_BAT_DISABLE_MASK + | S2MU106_REG_PLUG_CTRL_DETECT_OCP_DISABLE_MASK; + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, &data_lpm); + data_lpm |= S2MU106_REG_LPM_EN; + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, data_lpm); + + s2mu106_set_irq_enable(pdic_data, 0, 0, 0, 0, 0, 0); + + usleep_range(500, 900); + + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DFP | S2MU106_REG_PLUG_CTRL_RP0; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + + dev_info(dev, "%s s2mu106 enter water chk lpm mode\n", __func__); +} + +static bool s2mu106_is_water_detected_2nd_seq(struct s2mu106_usbpd_data *pdic_data, u8 *cc_val) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 cc_chk[2] = {0,}; + + if (cc_val[0] == USBPD_Rp) + cc_chk[0] = 1; + if (cc_val[1] == USBPD_Rp) + cc_chk[1] = 1; + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, + S2MU106_THRESHOLD_214MV); + s2mu106_set_lpm_mode(pdic_data); + s2mu106_usbpd_update_bit(i2c, S2MU106_REG_PD_CTRL, + S2MU106_REG_LPM_EN, 0, 0); + msleep(300); + + if (s2mu106_get_plug_monitor(pdic_data, cc_val) < 0) { + pr_err("%s Failed to get the plug monitor.\n", __func__); + return false; + } + + /* Rd is detected due to the water CAPACITOR. */ + if (((cc_chk[0] && !cc_chk[1]) && (cc_val[0] == USBPD_Rd)) || + ((cc_chk[1] && !cc_chk[0]) && (cc_val[1] == USBPD_Rd)) || + ((cc_chk[0] && cc_chk[1]) && ((cc_val[0] == USBPD_Rd) && (cc_val[1] == USBPD_Rd)))) { + return true; + } + + return false; +} + +static void _S2MU106_PDIC_enter_to_water(struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; +#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + struct otg_notify *o_notify = get_otg_notify(); +#endif + +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_ATTACH, 0); +#endif + pdic_data->is_water_detect = true; + pdic_data->water_detect_cnt = 0; + s2mu106_set_lpm_mode(pdic_data); + s2mu106_usbpd_update_bit(i2c, S2MU106_REG_PD_CTRL, + S2MU106_REG_LPM_EN, 0, 0); +#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_WATER_INT_COUNT); +#endif +} + +static void S2MU106_PDIC_water_detect_handler(struct work_struct *work) +{ + struct s2mu106_usbpd_data *pdic_data = + container_of(work, struct s2mu106_usbpd_data, water_detect_handler.work); + struct i2c_client *i2c = pdic_data->i2c; + + u8 cc_val[2] = {0,}; + + pr_info("%s enter", __func__); + mutex_lock(&pdic_data->_mutex); + + /* + * Cancel the detect handler, + * in case the muic notifies cable attach or dry signal, + * or the water chk cnt is over, + * or ccic already detected the water. + */ + if (!pdic_data->is_muic_water_detect + || pdic_data->water_detect_cnt > WATER_CHK_RETRY_CNT + || pdic_data->is_water_detect) { + pr_info("%s: detect handler is canceled", __func__); + goto WATER_OUT; + } + + s2mu106_set_water_detect_pre_cond(pdic_data); + s2mu106_set_water_1st_detect(pdic_data); + msleep(400); + + if (s2mu106_get_plug_monitor(pdic_data, cc_val) < 0) { + pr_err("%s Failed to get the plug monitor.\n", __func__); + goto WATER_MODE_OUT; + } + + if (IS_CC_WATER(cc_val[0], cc_val[1])) { + pr_info("%s, water is detected, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + _S2MU106_PDIC_enter_to_water(pdic_data); + goto WATER_MODE_OUT; + } else { + pr_info("%s, 1st chk is not water, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + if (s2mu106_is_water_detected_2nd_seq(pdic_data, cc_val)) { + pr_info("%s, 2nd seq, water is detected, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + _S2MU106_PDIC_enter_to_water(pdic_data); + goto WATER_MODE_OUT; + } + + pr_info("%s, 2nd chk : not water, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + + if (pdic_data->water_detect_cnt++ >= WATER_CHK_RETRY_CNT) { + pdic_data->is_water_detect = false; + pdic_data->water_detect_cnt = 0; +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0); +#endif + } else { + /* + * Retry the cc check, + * in case of invalid status. + * (200ms interval + 175ms check duration) * 5 times + */ + cancel_delayed_work(&pdic_data->water_detect_handler); + schedule_delayed_work(&pdic_data->water_detect_handler, + msecs_to_jiffies(S2MU106_WATER_CHK_INTERVAL_TIME)); + } + } +WATER_MODE_OUT: + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, + S2MU106_THRESHOLD_428MV); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RP, + S2MU106_THRESHOLD_2057MV); +WATER_OUT: + mutex_unlock(&pdic_data->_mutex); + return; +} + +static void S2MU106_PDIC_water_dry_handler(struct work_struct *work) +{ + struct s2mu106_usbpd_data *pdic_data = + container_of(work, struct s2mu106_usbpd_data, water_dry_handler.work); + struct i2c_client *i2c = pdic_data->i2c; + u8 cc_val[2] = {0,}; + + pr_info("%s enter", __func__); + mutex_lock(&pdic_data->_mutex); + + s2mu106_set_water_detect_pre_cond(pdic_data); + s2mu106_set_water_1st_detect(pdic_data); + msleep(400); + + if (s2mu106_get_plug_monitor(pdic_data, cc_val) < 0) { + pr_err("%s Failed to get the plug monitor.\n", __func__); + } + + if (IS_CC_RP(cc_val[0], cc_val[1])) { + pr_info("%s, 1st, water DRY is detected, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, + S2MU106_THRESHOLD_428MV); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RP, + S2MU106_THRESHOLD_2057MV); +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0); +#endif + } else { + pr_info("%s, It is not DRIED yet, cc1 : 0x%X, cc2 : 0x%X\n", + __func__, cc_val[0], cc_val[1]); + +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_ATTACH, 0); +#endif + s2mu106_set_lpm_mode(pdic_data); + s2mu106_usbpd_update_bit(i2c, S2MU106_REG_PD_CTRL, + S2MU106_REG_LPM_EN, 0, 0); + } + + mutex_unlock(&pdic_data->_mutex); + return; +} + +static void s2mu106_usbpd_otg_attach(struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + + /* otg */ + pdic_data->is_host = HOST_ON; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SRC; +#endif +#if defined(CONFIG_IFCONN_NOTIFIER) + /* USB */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP, NULL); +#endif + /* add to turn on external 5V */ + vbus_turn_on_ctrl(pdic_data, VBUS_ON); + + usbpd_manager_acc_handler_cancel(dev); +} + +#if defined(CONFIG_MUIC_NOTIFIER) +static int type3_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ +#if defined(CONFIG_CCIC_NOTIFIER) + CC_NOTI_ATTACH_TYPEDEF *p_noti = (CC_NOTI_ATTACH_TYPEDEF *)data; + muic_attached_dev_t attached_dev = p_noti->cable_type; +#else + muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; +#endif + struct s2mu106_usbpd_data *pdic_data = + container_of(nb, struct s2mu106_usbpd_data, + type3_nb); + struct i2c_client *i2c = pdic_data->i2c; + u8 reg_data = 0; + +#if (defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)) || \ + (!defined(CONFIG_SEC_FACTORY) && defined(CONFIG_USB_HOST_NOTIFY)) + struct otg_notify *o_notify = get_otg_notify(); +#endif + mutex_lock(&pdic_data->lpm_mutex); + pr_info("%s action:%d, attached_dev:%d, lpm:%d, pdic_data->is_otg_vboost:%d, pdic_data->is_otg_reboost:%d\n", + __func__, (int)action, (int)attached_dev, pdic_data->lpm_mode, + (int)pdic_data->is_otg_vboost, (int)pdic_data->is_otg_reboost); + + if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) && + (attached_dev == ATTACHED_DEV_TYPE3_MUIC)) { + pdic_data->is_muic_water_detect = false; + pdic_data->water_detect_cnt = 0; + if (pdic_data->lpm_mode) { + pr_info("%s try to exit lpm mode-->\n", __func__); + s2mu106_set_normal_mode(pdic_data); + pr_info("%s after exit lpm mode<--\n", __func__); + } + } else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) && + attached_dev == ATTACHED_DEV_CHK_WATER_REQ) { + pr_info("%s, ATTACH : MUIC REQUESTED WATER CHECK\n", __func__); + s2mu106_usbpd_set_vconn_manual(pdic_data, true); + pdic_data->is_muic_water_detect = true; + pdic_data->water_detect_cnt = 0; + pdic_data->is_water_detect = false; + cancel_delayed_work(&pdic_data->water_detect_handler); + schedule_delayed_work(&pdic_data->water_detect_handler, msecs_to_jiffies(100)); + } else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) && + attached_dev == ATTACHED_DEV_CHK_WATER_DRY_REQ) { + pr_info("%s, ATTACH : MUIC REQUESTED WATER DRY CHECK\n", __func__); + cancel_delayed_work(&pdic_data->water_dry_handler); + schedule_delayed_work(&pdic_data->water_dry_handler, msecs_to_jiffies(100)); + } else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) && + attached_dev == ATTACHED_DEV_OTG_MUIC) { + s2mu106_usbpd_otg_attach(pdic_data); + } else if ((action == MUIC_PDIC_NOTIFY_CMD_DETACH) && + attached_dev == ATTACHED_DEV_UNDEFINED_RANGE_MUIC) { + pr_info("%s, DETACH : ATTACHED_DEV_UNDEFINED_RANGE_MUIC(Water DRY)\n", __func__); + pdic_data->is_muic_water_detect = false; + pdic_data->water_detect_cnt = 0; + pdic_data->is_water_detect = false; + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL, ®_data); + reg_data |= S2MU106_REG_LPM_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL, reg_data); +#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_DRY_INT_COUNT); +#endif +#if defined(CONFIG_CCIC_NOTIFIER) + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0); +#endif + } else if (action == MUIC_PDIC_NOTIFY_CMD_DETACH) { + if (!pdic_data->lpm_mode) { + pr_info("%s try to enter lpm mode-->\n", __func__); + s2mu106_set_lpm_mode(pdic_data); + pr_info("%s after enter lpm mode<--\n", __func__); + } + } +#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_USB_HOST_NOTIFY) + else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) + && (attached_dev == ATTACHED_DEV_CHECK_OCP) + && pdic_data->is_otg_vboost + && pdic_data->data_role_dual == + IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP) { + if (o_notify) { + if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST)) { + pr_info("%s, upsm mode, skip OCP handling\n", __func__); + goto EOH; + } + } + if (pdic_data->is_otg_reboost) { + /* todo : over current event to platform */ + pr_info("%s, CHECK_OCP, Can't afford it(OVERCURRENT)\n", __func__); + if (o_notify) + send_otg_notify(o_notify, NOTIFY_EVENT_OVERCURRENT, 0); + goto EOH; + } + ccic_event_work(pdic_data, + CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/); + + pr_info("%s, CHECK_OCP, start OCP W/A\n", __func__); + pdic_data->is_otg_reboost = true; + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC_HOLD, ®_data); + reg_data |= S2MU106_REG_PLUG_CTRL_CC_HOLD_BIT; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC_HOLD, reg_data); + + s2mu106_usbpd_set_rp_scr_sel(pdic_data, PLUG_CTRL_RP80); + vbus_turn_on_ctrl(pdic_data, VBUS_OFF); + vbus_turn_on_ctrl(pdic_data, VBUS_ON); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_CC_HOLD, ®_data); + reg_data &= ~S2MU106_REG_PLUG_CTRL_CC_HOLD_BIT; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_CC_HOLD, reg_data); + } +EOH: +#endif + mutex_unlock(&pdic_data->lpm_mutex); + + return 0; +} +#endif + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +static void s2mu106_usbpd_control_cc12_rd(struct s2mu106_usbpd_data *pdic_data, + bool enable) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 data = 0; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + dev_info(pdic_data->dev, "%s, enable : %s current reg value(%x)\n", __func__, + enable ? "ON" : "OFF", data); + + if (enable) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~S2MU106_REG_PLUG_CTRL_MODE_MASK; + data |= S2MU106_REG_PLUG_CTRL_UFP; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + } else { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~S2MU106_REG_PLUG_CTRL_MODE_MASK; + data |= S2MU106_REG_PLUG_CTRL_DRP; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + } +} +#endif + +static void s2mu106_usbpd_prevent_watchdog_reset( + struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; + u8 val = 0; + + mutex_lock(&pdic_data->lpm_mutex); + wake_lock(&pdic_data->wake_lock); + if (!pdic_data->lpm_mode) { + if (s2mu106_usbpd_lpm_check(pdic_data) == 0) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_INT_STATUS2, &val); + s2mu106_usbpd_set_vbus_wakeup(pdic_data, VBUS_WAKEUP_DISABLE); + pr_info("%s force to lpm mode\n", __func__); + s2mu106_usbpd_set_mode(pdic_data, PD_LPM_MODE); + /* enable wakeup to check prevent function */ + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2_WAKEUP, ENABLED_INT_3, ENABLED_INT_4, + ENABLED_INT_5); + s2mu106_usbpd_set_vbus_wakeup(pdic_data, VBUS_WAKEUP_ENABLE); + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_INT_STATUS2, &val); + if (val & S2MU106_REG_INT_STATUS2_WAKEUP) + pr_info("%s auto wakeup success\n", __func__); + else + s2mu106_usbpd_set_mode(pdic_data, PD_NORMAL_MODE); + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, + ENABLED_INT_5); + } + } + wake_unlock(&pdic_data->wake_lock); + mutex_unlock(&pdic_data->lpm_mutex); +} + +static void s2mu106_vbus_short_check(struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = pdic_data->dev; + u8 val = 0; + u8 cc1_val = 0, cc2_val = 0; + u8 rp_currentlvl = 0; + + if (pdic_data->vbus_short_check) + return; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_FSM_MON, &val); + + cc1_val = val & S2MU106_REG_CTRL_MON_CC1_MASK; + cc2_val = (val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + dev_info(dev, "%s, 10k check : cc1_val(%x), cc2_val(%x)\n", + __func__, cc1_val, cc2_val); + + if (cc1_val == USBPD_10k || cc2_val == USBPD_10k) + rp_currentlvl = RP_CURRENT_LEVEL3; + else if (cc1_val == USBPD_22k || cc2_val == USBPD_22k) + rp_currentlvl = RP_CURRENT_LEVEL2; + else if (cc1_val == USBPD_56k || cc2_val == USBPD_56k) + rp_currentlvl = RP_CURRENT_LEVEL_DEFAULT; + +#ifdef CONFIG_IFCONN_NOTIFIER + pd_noti.sink_status.rp_currentlvl = rp_currentlvl; + pd_noti.event = IFCONN_NOTIFY_EVENT_ATTACH; + ifconn_event_work(pdic_data, IFCONN_NOTIFY_BATTERY, + IFCONN_NOTIFY_ID_POWER_STATUS, + IFCONN_NOTIFY_EVENT_ATTACH, &pd_noti); +#endif + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, &val); + + cc1_val = val & S2MU106_REG_CTRL_MON_CC1_MASK; + cc2_val = (val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + dev_info(dev, "%s, vbus short check : cc1_val(%x), cc2_val(%x)\n", + __func__, cc1_val, cc2_val); + + if (cc1_val == USBPD_Rp || cc2_val == USBPD_Rp) { + pdic_data->vbus_short = true; + } else { + pdic_data->vbus_short = false; + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_TA, IFCONN_NOTIFY_EVENT_ATTACH, NULL); + } + + pdic_data->vbus_short_check = true; +} + +static void s2mu106_usbpd_detach_init(struct s2mu106_usbpd_data *pdic_data) +{ + struct device *dev = pdic_data->dev; + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct i2c_client *i2c = pdic_data->i2c; + int ret = 0; + + dev_info(dev, "%s\n", __func__); + + s2mu106_usbpd_set_cc_control(pdic_data, USBPD_CC_OFF); + + usbpd_manager_plug_detach(dev, 0); + pdic_data->status_reg = 0; + usbpd_reinit(dev); + /* for ccic hw detect */ + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_MSG_SEND_CON, S2MU106_RESET_REG_00); + pdic_data->rid = REG_RID_MAX; + pdic_data->check_rid_wa = false; + pdic_data->detach_valid = true; + pdic_data->is_factory_mode = false; + pdic_data->is_pr_swap = false; + pdic_data->vbus_short_check = false; + pdic_data->vbus_short = false; + if (pdic_data->regulator_en) + ret = regulator_disable(pdic_data->regulator); +#ifdef CONFIG_BATTERY_SAMSUNG +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + pd_noti.sink_status.current_pdo_num = 0; + pd_noti.sink_status.selected_pdo_num = 0; + pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE; +#endif +#endif + s2mu106_usbpd_reg_init(pdic_data); + s2mu106_set_vconn_source(pd_data, USBPD_VCONN_OFF); +} + +static void s2mu106_usbpd_notify_detach(struct s2mu106_usbpd_data *pdic_data) +{ + struct device *dev = pdic_data->dev; + struct usbpd_data *pd_data = dev_get_drvdata(pdic_data->dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + if (manager->dp_is_connect == 1) + usbpd_dp_detach(pd_data); + +#if defined(CONFIG_IFCONN_NOTIFIER) + /* MUIC */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, IFCONN_NOTIFY_ID_ATTACH, + IFCONN_NOTIFY_EVENT_DETACH, NULL); + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, IFCONN_NOTIFY_ID_RID, + IFCONN_NOTIFY_EVENT_DETACH, NULL); + + if (pdic_data->is_host > HOST_OFF || pdic_data->is_client > CLIENT_OFF) { +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (pdic_data->is_host > HOST_OFF || + pdic_data->power_role_dual == DUAL_ROLE_PROP_PR_SRC) { +#else + if (pdic_data->is_host > HOST_OFF) { +#endif + vbus_turn_on_ctrl(pdic_data, VBUS_OFF); + } + usbpd_manager_acc_detach(dev); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pr_info("%s, data_role (%d)\n", __func__, pdic_data->data_role_dual); + if (pdic_data->data_role_dual == IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP && + !pdic_data->try_state_change) { + s2mu106_usbpd_control_cc12_rd(pdic_data, true); + msleep(S2MU106_WAIT_RD_DETACH_DELAY_MS); + s2mu106_usbpd_control_cc12_rd(pdic_data, false); + } +#endif + /* usb or otg */ + dev_info(dev, "%s %d: is_host = %d, is_client = %d\n", __func__, + __LINE__, pdic_data->is_host, pdic_data->is_client); + pdic_data->is_host = HOST_OFF; + pdic_data->is_client = CLIENT_OFF; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE; +#endif + /* USB */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_DETACH, NULL); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (!pdic_data->try_state_change) + s2mu106_rprd_mode_change(pdic_data, TYPE_C_ATTACH_DRP); +#else + s2mu106_rprd_mode_change(pdic_data, TYPE_C_ATTACH_DRP); +#endif + } +#else + usbpd_manager_plug_detach(dev, 1); +#endif +} +#ifdef CONFIG_CCIC_TRY_SNK +static void s2mu106_usbpd_try_snk(struct s2mu106_usbpd_data *pdic_data) +{ + struct device *dev = pdic_data->dev; + struct i2c_client *i2c = pdic_data->i2c; + u8 data; + u8 intr[S2MU106_MAX_NUM_INT_STATUS] = {0}; + + dev_info(dev, "%s\n", __func__); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~ S2MU106_REG_PLUG_CTRL_MODE_MASK; + data |= S2MU106_REG_PLUG_CTRL_UFP; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + + msleep(250); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON2, &data); + if ((data & S2MU106_PR_MASK) != S2MU106_PDIC_SINK) { + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~ S2MU106_REG_PLUG_CTRL_MODE_MASK; + data |= S2MU106_REG_PLUG_CTRL_DFP; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); + + msleep(200); + } + + s2mu106_usbpd_bulk_read(i2c, S2MU106_REG_INT_STATUS0, + S2MU106_MAX_NUM_INT_STATUS, intr); +} +#endif +static void s2mu106_usbpd_check_host(struct s2mu106_usbpd_data *pdic_data, + CCIC_HOST_REASON host) +{ + struct usbpd_data *pd_data = dev_get_drvdata(pdic_data->dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + if (host == HOST_ON && pdic_data->is_host == HOST_ON) { + if (manager->dp_is_connect == 1) + usbpd_dp_detach(pd_data); + + dev_info(pdic_data->dev, "%s %d: turn off host\n", __func__, __LINE__); + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE; +#endif + /* add to turn off external 5V */ + vbus_turn_on_ctrl(pdic_data, VBUS_OFF); + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_DETACH, NULL); + pdic_data->is_host = HOST_OFF; + msleep(300); + } else if (host == HOST_OFF && pdic_data->is_host == HOST_OFF) { + /* muic */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_OTG, IFCONN_NOTIFY_EVENT_ATTACH, NULL); + s2mu106_usbpd_otg_attach(pdic_data); + } +} + +static void s2mu106_usbpd_check_client(struct s2mu106_usbpd_data *pdic_data, + CCIC_DEVICE_REASON client) +{ + if (client == CLIENT_ON && pdic_data->is_client == CLIENT_ON) { + dev_info(pdic_data->dev, "%s %d: turn off client\n", __func__, __LINE__); + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE; +#endif + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_ID_USB, IFCONN_NOTIFY_EVENT_DETACH, NULL); + pdic_data->is_client = CLIENT_OFF; + } +} + +static int s2mu106_check_port_detect(struct s2mu106_usbpd_data *pdic_data) +{ + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + struct usbpd_data *pd_data = dev_get_drvdata(dev); + u8 data, val; + u8 cc1_val = 0, cc2_val = 0; + int ret = 0; + + ret = s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON2, &data); + if (ret < 0) + dev_err(dev, "%s, i2c read PLUG_MON2 error\n", __func__); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON1, &val); + + cc1_val = val & S2MU106_REG_CTRL_MON_CC1_MASK; + cc2_val = (val & S2MU106_REG_CTRL_MON_CC2_MASK) >> S2MU106_REG_CTRL_MON_CC2_SHIFT; + + dev_info(dev, "%s, attach cc pin check cc1_val(%x), cc2_val(%x)\n", + __func__, cc1_val, cc2_val); + +#ifdef CONFIG_CCIC_DP + if (cc1_val == USBPD_Rd) + gpio_direction_output(pdic_data->usb_sw_sel, 1); + else if (cc2_val == USBPD_Rd) + gpio_direction_output(pdic_data->usb_sw_sel, 0); +#endif +#ifdef CONFIG_CCIC_TRY_SNK + if ((data & S2MU106_PR_MASK) == S2MU106_PDIC_SOURCE) { +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (!pdic_data->try_state_change) { + s2mu106_usbpd_try_snk(pdic_data); + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON2, &data); + } +#else + s2mu106_usbpd_try_snk(pdic_data); + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_MON2, &data); +#endif + } +#endif + if ((data & S2MU106_PR_MASK) == S2MU106_PDIC_SINK) { + dev_info(dev, "SINK\n"); + pdic_data->power_role = PDIC_SINK; + pdic_data->data_role = USBPD_UFP; + s2mu106_snk(i2c); + s2mu106_ufp(i2c); + s2mu106_usbpd_prevent_watchdog_reset(pdic_data); + usbpd_policy_reset(pd_data, PLUG_EVENT); +#if defined(CONFIG_IFCONN_NOTIFIER) + dev_info(&i2c->dev, "%s %d: is_host = %d, is_client = %d\n", __func__, + __LINE__, pdic_data->is_host, pdic_data->is_client); + if (pdic_data->regulator_en) { + ret = regulator_enable(pdic_data->regulator); + if (ret) + dev_err(&i2c->dev, "Failed to enable vconn LDO: %d\n", ret); + } + + s2mu106_usbpd_check_host(pdic_data, HOST_ON); + /* muic */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_ATTACH, NULL); + if (!(pdic_data->rid == REG_RID_523K || pdic_data->rid == REG_RID_619K)) { + if (pdic_data->is_client == CLIENT_OFF && pdic_data->is_host == HOST_OFF) { + /* usb */ + pdic_data->is_client = CLIENT_ON; +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SNK; +#endif + ifconn_event_work(pdic_data, + IFCONN_NOTIFY_USB, IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP, NULL); + } + } +#endif + s2mu106_vbus_short_check(pdic_data); + } else if ((data & S2MU106_PR_MASK) == S2MU106_PDIC_SOURCE) { + dev_info(dev, "SOURCE\n"); + pdic_data->power_role = PDIC_SOURCE; + pdic_data->data_role = USBPD_DFP; + s2mu106_dfp(i2c); + s2mu106_src(i2c); + usbpd_policy_reset(pd_data, PLUG_EVENT); +#if defined(CONFIG_IFCONN_NOTIFIER) + dev_info(&i2c->dev, "%s %d: is_host = %d, is_client = %d\n", __func__, + __LINE__, pdic_data->is_host, pdic_data->is_client); + s2mu106_usbpd_check_client(pdic_data, CLIENT_ON); + s2mu106_usbpd_check_host(pdic_data, HOST_OFF); +#endif + if (pdic_data->regulator_en) { + ret = regulator_enable(pdic_data->regulator); + if (ret) + dev_err(&i2c->dev, "Failed to enable vconn LDO: %d\n", ret); + } + + s2mu106_set_vconn_source(pd_data, USBPD_VCONN_ON); + + msleep(tTypeCSinkWaitCap); /* dont over 310~620ms(tTypeCSinkWaitCap) */ + } else { + dev_err(dev, "%s, PLUG Error\n", __func__); +#ifdef CONFIG_CCIC_TRY_SNK + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_MODE_MASK | S2MU106_REG_PLUG_CTRL_RP_SEL_MASK); + data |= S2MU106_REG_PLUG_CTRL_DRP | S2MU106_REG_PLUG_CTRL_RP180; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_PORT, data); +#endif + return -1; + } + + pdic_data->detach_valid = false; + + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5); + + return ret; +} + +#if defined(CONFIG_SEC_FACTORY) +static int s2mu106_usbpd_check_619k(struct s2mu106_usbpd_data *pdic_data) +{ + u8 rid = 0; + + if (pdic_data->rid != REG_RID_619K) + return false; + + msleep(250); + s2mu106_usbpd_read_reg(pdic_data->i2c, S2MU106_REG_ADC_STATUS, &rid); + rid = (rid & S2MU106_PDIC_RID_MASK) >> S2MU106_PDIC_RID_SHIFT; + dev_info(pdic_data->dev, "%s %d: Detached, check if still 619K? => 0x%X\n", + __func__, __LINE__, rid); + + if (rid == REG_RID_619K) + return true; + return false; +} +#endif + +#ifndef CONFIG_SEC_FACTORY +static void s2mu106_usbpd_check_reboost(struct s2mu106_usbpd_data *pdic_data) +{ + if (!pdic_data->is_otg_reboost) + return; + + dev_info(pdic_data->dev, "%s %d: Detached, go back to 180uA\n", + __func__, __LINE__); + s2mu106_usbpd_set_rp_scr_sel(pdic_data, PLUG_CTRL_RP180); + pdic_data->is_otg_reboost = false; + + return; +} +#endif + +static irqreturn_t s2mu106_irq_thread(int irq, void *data) +{ + struct s2mu106_usbpd_data *pdic_data = data; + struct i2c_client *i2c = pdic_data->i2c; + struct device *dev = &i2c->dev; + struct usbpd_data *pd_data = dev_get_drvdata(dev); + int ret = 0; + unsigned attach_status = 0, rid_status = 0; + + dev_info(dev, "%s\n", __func__); + + mutex_lock(&pdic_data->_mutex); + + s2mu106_poll_status(pd_data); + +#ifndef CONFIG_SEC_FACTORY + if (pdic_data->lpcharge_water) + goto out; +#endif + + if (s2mu106_get_status(pd_data, MSG_NONE)) + goto out; + + if (s2mu106_get_status(pd_data, MSG_HARDRESET)) { + s2mu106_usbpd_set_cc_control(pdic_data, USBPD_CC_OFF); + s2mu106_self_soft_reset(i2c); + pdic_data->status_reg = 0; + usbpd_rx_hard_reset(dev); + usbpd_kick_policy_work(dev); + goto out; + } + + if (s2mu106_get_status(pd_data, MSG_SOFTRESET)) + usbpd_rx_soft_reset(pd_data); + + if (s2mu106_get_status(pd_data, PLUG_DETACH)) { +#if defined(CONFIG_SEC_FACTORY) + ret = s2mu106_usbpd_check_619k(pdic_data); + if (ret) + goto skip_detach; +#endif /* CONFIG_SEC_FACTORY */ +#ifndef CONFIG_SEC_FACTORY + s2mu106_usbpd_check_reboost(pdic_data); +#endif + attach_status = s2mu106_get_status(pd_data, PLUG_ATTACH); + rid_status = s2mu106_get_status(pd_data, MSG_RID); + s2mu106_usbpd_detach_init(pdic_data); + if (attach_status) { + ret = s2mu106_check_port_detect(pdic_data); + if (ret >= 0) { + if (rid_status) { + pdic_data->check_rid_wa = true; + s2mu106_usbpd_check_rid(pdic_data); + } + goto hard_reset; + } + } + s2mu106_usbpd_notify_detach(pdic_data); + mutex_lock(&pdic_data->lpm_mutex); + if (!pdic_data->lpm_mode) { + if (s2mu106_usbpd_lpm_check(pdic_data) > 0) { + pr_info("%s force to normal mode\n", __func__); + s2mu106_usbpd_set_mode(pdic_data, PD_NORMAL_MODE); + } + s2mu106_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1, + ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5); + } + mutex_unlock(&pdic_data->lpm_mutex); + goto out; + } +#if defined(CONFIG_SEC_FACTORY) +skip_detach: +#endif /* CONFIG_SEC_FACTORY */ + if (s2mu106_get_status(pd_data, PLUG_ATTACH) && !pdic_data->is_pr_swap) { + if (s2mu106_check_port_detect(data) < 0) + goto out; + } + + if (s2mu106_get_status(pd_data, MSG_RID)) { + pdic_data->check_rid_wa = true; + s2mu106_usbpd_check_rid(pdic_data); + } + +hard_reset: + mutex_lock(&pdic_data->lpm_mutex); + if (!pdic_data->lpm_mode) + usbpd_kick_policy_work(dev); + mutex_unlock(&pdic_data->lpm_mutex); +out: + mutex_unlock(&pdic_data->_mutex); + + return IRQ_HANDLED; +} + +static int s2mu106_usbpd_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_AUTHENTIC: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int s2mu106_usbpd_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct s2mu106_usbpd_data *pdic_data = + power_supply_get_drvdata(psy); + struct i2c_client *i2c = pdic_data->i2c; + u8 data = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_AUTHENTIC: + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_VBUS_MUX, &data); + data &= ~(S2MU106_REG_RD_OR_VBUS_MUX_SEL); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_VBUS_MUX, data); + break; + case POWER_SUPPLY_PROP_USBPD_RESET: + s2mu106_usbpd_set_vbus_wakeup(pdic_data, VBUS_WAKEUP_DISABLE); + s2mu106_usbpd_set_vbus_wakeup(pdic_data, VBUS_WAKEUP_ENABLE); + break; + default: + return -EINVAL; + } + return 0; +} + +int s2mu106_usbpd_psy_init(struct s2mu106_usbpd_data *_data, struct device *parent) +{ + struct power_supply_config psy_cfg = {}; + int ret = 0; + + if (_data == NULL || parent == NULL) { + pr_err("%s NULL data\n", __func__); + return -1; + } + + _data->ccic_desc.name = "s2mu106-usbpd"; + _data->ccic_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + _data->ccic_desc.get_property = s2mu106_usbpd_get_property; + _data->ccic_desc.set_property = s2mu106_usbpd_set_property; + _data->ccic_desc.properties = ccic_props; + _data->ccic_desc.num_properties = ARRAY_SIZE(ccic_props); + + psy_cfg.drv_data = _data; + psy_cfg.supplied_to = ccic_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(ccic_supplied_to); + + _data->psy_ccic = power_supply_register(parent, &_data->ccic_desc, &psy_cfg); + if (IS_ERR(_data->psy_ccic)) { + ret = (int)PTR_ERR(_data->psy_ccic); + pr_err("%s: Failed to Register psy_ccic, ret : %d\n", __func__, ret); + } + return ret; +} + +static int s2mu106_usbpd_reg_init(struct s2mu106_usbpd_data *_data) +{ + struct i2c_client *i2c = _data->i2c; + u8 data = 0; + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL, &data); + data |= S2MU106_REG_PLUG_CTRL_VDM_DISABLE | + S2MU106_REG_PLUG_CTRL_ECO_SRC_CAP_RDY; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PHY_CTRL_IFG, &data); + data |= S2MU106_PHY_IFG_35US << S2MU106_REG_IFG_SHIFT; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PHY_CTRL_IFG, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PD_CTRL_2, &data); + data &= ~S2MU106_REG_CC_OCP_MASK; + data |= S2MU106_CC_OCP_575MV << S2MU106_REG_CC_OCP_SHIFT; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PD_CTRL_2, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_VBUS_MUX, &data); + data &= ~S2MU106_REG_DET_RD_OR_VBUS; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_VBUS_MUX, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, &data); + data |= S2MU106_REG_USB31_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, data); + + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL, &data); + data &= ~S2MU106_REG_PLUG_CTRL_UFP_ATTACH_OPT; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL, data); + + /* set Rd threshold to 400mV */ + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD_2, S2MU106_THRESHOLD_600MV); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RP_2, S2MU106_THRESHOLD_1200MV); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RD, S2MU106_THRESHOLD_214MV); + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_SET_RP, S2MU106_THRESHOLD_2057MV); + + if (_data->vconn_en) { + /* Off Manual Rd setup & On Manual Vconn setup */ + s2mu106_usbpd_read_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, &data); + data &= ~(S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_MASK); + data |= S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN; + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, data); + } + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_PLUG_CTRL_RpRd, S2MU106_RESET_REG_00); + + s2mu106_usbpd_write_reg(i2c, S2MU106_REG_MSG_SEND_CON, S2MU106_RESET_REG_00); + + s2mu106_usbpd_set_vconn_manual(_data, true); + + return 0; +} + +static irqreturn_t s2mu106_irq_isr(int irq, void *data) +{ + return IRQ_WAKE_THREAD; +} + +static int s2mu106_usbpd_irq_init(struct s2mu106_usbpd_data *_data) +{ + struct i2c_client *i2c = _data->i2c; + struct device *dev = &i2c->dev; + int ret = 0; + + if (!_data->irq_gpio) { + dev_err(dev, "%s No interrupt specified\n", __func__); + return -ENXIO; + } + +/* s2mu106_usbpd_bulk_read(i2c, S2MU106_REG_INT_STATUS0, + S2MU106_MAX_NUM_INT_STATUS, intr); + + pr_info("%s status[0x%x 0x%x 0x%x 0x%x 0x%x]\n", + __func__, intr[0], intr[1], intr[2], intr[3], intr[4]); +*/ + i2c->irq = gpio_to_irq(_data->irq_gpio); + + if (i2c->irq) { + ret = request_threaded_irq(i2c->irq, s2mu106_irq_isr, + s2mu106_irq_thread, + (IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND), + "s2mu106-usbpd", _data); + if (ret < 0) { + dev_err(dev, "%s failed to request irq(%d)\n", + __func__, i2c->irq); + return ret; + } + + ret = enable_irq_wake(i2c->irq); + if (ret < 0) + dev_err(dev, "%s failed to enable wakeup src\n", + __func__); + } + + if (_data->lpm_mode) + s2mu106_set_irq_enable(_data, 0, 0, 0, 0, 0, 0); + else + s2mu106_set_irq_enable(_data, 0, 0, 0, 0, ENABLED_INT_4, 0); + + return ret; +} + +static void s2mu106_usbpd_init_configure(struct s2mu106_usbpd_data *_data) +{ + s2mu106_set_normal_mode(_data); + msleep(25); + _data->detach_valid = true; + s2mu106_set_lpm_mode(_data); + _data->detach_valid = false; + s2mu106_usbpd_set_cc_control(_data, USBPD_CC_OFF); + _data->lpm_mode = true; + msleep(150); /* for abnormal PD TA */ + _data->is_factory_mode = false; + s2mu106_set_normal_mode(_data); + _data->lpm_mode = false; +} + +static void s2mu106_usbpd_pdic_data_init(struct s2mu106_usbpd_data *_data) +{ + _data->check_msg_pass = false; + _data->vconn_source = USBPD_VCONN_OFF; + _data->rid = REG_RID_MAX; + _data->is_host = 0; + _data->is_client = 0; + _data->data_role_dual = 0; + _data->power_role_dual = 0; + _data->is_water_detect = false; + _data->is_muic_water_detect = false; + _data->detach_valid = true; + _data->is_otg_vboost = false; + _data->is_otg_reboost = false; + _data->is_pr_swap = false; + _data->vbus_short = false; + _data->vbus_short_check = false; +#ifndef CONFIG_SEC_FACTORY + _data->lpcharge_water = false; +#endif + _data->check_rid_wa = false; +} + +static int of_s2mu106_dt(struct device *dev, + struct s2mu106_usbpd_data *_data) +{ + struct device_node *np_usbpd = dev->of_node; + int ret = 0; + + if (np_usbpd == NULL) { + dev_err(dev, "%s np NULL\n", __func__); + return -EINVAL; + } else { + _data->irq_gpio = of_get_named_gpio(np_usbpd, + "usbpd,usbpd_int", 0); + if (_data->irq_gpio < 0) { + dev_err(dev, "error reading usbpd irq = %d\n", + _data->irq_gpio); + _data->irq_gpio = 0; + } + if (of_find_property(np_usbpd, "vconn-en", NULL)) + _data->vconn_en = true; + else + _data->vconn_en = false; + + if (of_find_property(np_usbpd, "regulator-en", NULL)) + _data->regulator_en = true; + else + _data->regulator_en = false; +#ifdef CONFIG_CCIC_DP + _data->usb_sw_sel = of_get_named_gpio(np_usbpd, "usb_sw_sel", 0); + + if (!gpio_is_valid(_data->usb_sw_sel)) + dev_err(dev, "failed to get gpio usb_sw_sel\n"); +#endif + } + return ret; +} + +static int s2mu106_usbpd_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent); + struct s2mu106_usbpd_data *pdic_data; + struct device *dev = &i2c->dev; + int ret = 0; + + dev_info(dev, "%s\n", __func__); + test_i2c = i2c; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(dev, "%s: i2c functionality check error\n", __func__); + ret = -EIO; + goto err_return; + } + + pdic_data = kzalloc(sizeof(struct s2mu106_usbpd_data), GFP_KERNEL); + if (!pdic_data) { + dev_err(dev, "%s: failed to allocate driver data\n", __func__); + ret = -ENOMEM; + goto err_return; + } + + /* save platfom data for gpio control functions */ + pdic_data->dev = &i2c->dev; + pdic_data->i2c = i2c; + i2c_set_clientdata(i2c, pdic_data); + + ret = of_s2mu106_dt(&i2c->dev, pdic_data); + if (ret < 0) + dev_err(dev, "%s: not found dt!\n", __func__); + + mutex_init(&pdic_data->_mutex); + mutex_init(&pdic_data->lpm_mutex); + mutex_init(&pdic_data->cc_mutex); + wake_lock_init(&pdic_data->wake_lock, WAKE_LOCK_SUSPEND, "pdic_wake"); + + s2mu106_usbpd_init_configure(pdic_data); + s2mu106_usbpd_pdic_data_init(pdic_data); + + if (pdic_data->regulator_en) { + pdic_data->regulator = devm_regulator_get(dev, "vconn"); + if (IS_ERR(pdic_data->regulator)) { + dev_err(dev, "%s: not found regulator vconn\n", __func__); + pdic_data->regulator_en = false; + } else + ret = regulator_disable(pdic_data->regulator); + } + + ret = usbpd_init(dev, pdic_data); + if (ret < 0) { + dev_err(dev, "failed on usbpd_init\n"); + goto err_return; + } + + usbpd_set_ops(dev, &s2mu106_ops); + + s2mu106_usbpd_reg_init(pdic_data); + + pdic_data->pdic_queue = + alloc_workqueue(dev_name(dev), WQ_MEM_RECLAIM, 1); + if (!pdic_data->pdic_queue) { + dev_err(dev, + "%s: Fail to Create Workqueue\n", __func__); + goto err_return; + } + +#if defined(CONFIG_IFCONN_NOTIFIER) + /* Create a work queue for the ccic irq thread */ + pdic_data->ifconn_wq + = create_singlethread_workqueue("ifconn_notify_event"); + if (!pdic_data->ifconn_wq) { + pr_err("%s failed to create work queue for ccic notifier\n", + __func__); + goto err_return; + } +#endif + if (pdic_data->rid == REG_RID_UNDF) + pdic_data->rid = REG_RID_MAX; + + ret = s2mu106_usbpd_irq_init(pdic_data); + if (ret) { + dev_err(dev, "%s: failed to init irq(%d)\n", __func__, ret); + goto fail_init_irq; + } + INIT_DELAYED_WORK(&pdic_data->water_detect_handler, S2MU106_PDIC_water_detect_handler); + INIT_DELAYED_WORK(&pdic_data->water_dry_handler, S2MU106_PDIC_water_dry_handler); + + if (pdic_data->detach_valid) { + mutex_lock(&pdic_data->_mutex); + s2mu106_check_port_detect(pdic_data); + s2mu106_usbpd_check_rid(pdic_data); + mutex_unlock(&pdic_data->_mutex); + } + + s2mu106_irq_thread(-1, pdic_data); + +#if defined(CONFIG_MUIC_NOTIFIER) + muic_ccic_notifier_register(&pdic_data->type3_nb, + type3_handle_notification, + MUIC_NOTIFY_DEV_PDIC); +#endif +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + ret = dual_role_init(pdic_data); + if (ret < 0) { + pr_err("unable to allocate dual role descriptor\n"); + goto fail_init_irq; + } + +#endif + ret = s2mu106_usbpd_psy_init(pdic_data, &i2c->dev); + if (ret < 0) { + pr_err("faled to register the ccic psy.\n"); + } + + dev_info(dev, "%s s2mu106 usbpd driver uploaded!\n", __func__); + + return 0; + +fail_init_irq: + if (i2c->irq) + free_irq(i2c->irq, pdic_data); +err_return: + return ret; +} + +#if defined CONFIG_PM +static int s2mu106_usbpd_suspend(struct device *dev) +{ + struct usbpd_data *_data = dev_get_drvdata(dev); + struct s2mu106_usbpd_data *pdic_data = _data->phy_driver_data; + + if (device_may_wakeup(dev)) + enable_irq_wake(pdic_data->i2c->irq); + +#ifndef CONFIG_SEC_FACTORY + disable_irq(pdic_data->i2c->irq); +#endif + return 0; +} + +static int s2mu106_usbpd_resume(struct device *dev) +{ + struct usbpd_data *_data = dev_get_drvdata(dev); + struct s2mu106_usbpd_data *pdic_data = _data->phy_driver_data; + + if (device_may_wakeup(dev)) + disable_irq_wake(pdic_data->i2c->irq); + +#ifndef CONFIG_SEC_FACTORY + enable_irq(pdic_data->i2c->irq); +#endif + return 0; +} +#else +#define s2mu106_muic_suspend NULL +#define s2mu106_muic_resume NULL +#endif + +static int s2mu106_usbpd_remove(struct i2c_client *i2c) +{ + struct s2mu106_usbpd_data *_data = i2c_get_clientdata(i2c); + + if (_data) { +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + devm_dual_role_instance_unregister(_data->dev, + _data->dual_role); + devm_kfree(_data->dev, _data->desc); +#endif + disable_irq_wake(_data->i2c->irq); + free_irq(_data->i2c->irq, _data); + mutex_destroy(&_data->_mutex); + i2c_set_clientdata(_data->i2c, NULL); + kfree(_data); + } + return 0; +} + +static const struct i2c_device_id s2mu106_usbpd_i2c_id[] = { + { USBPD_DEV_NAME, 1 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, s2mu106_i2c_id); + +static struct of_device_id sec_usbpd_i2c_dt_ids[] = { + { .compatible = "sec-usbpd,i2c" }, + { } +}; + +static void s2mu106_usbpd_shutdown(struct i2c_client *i2c) +{ + struct s2mu106_usbpd_data *_data = i2c_get_clientdata(i2c); + + if (!_data->i2c) + return; +} + +static usbpd_phy_ops_type s2mu106_ops = { + .tx_msg = s2mu106_tx_msg, + .rx_msg = s2mu106_rx_msg, + .hard_reset = s2mu106_hard_reset, + .set_power_role = s2mu106_set_power_role, + .get_power_role = s2mu106_get_power_role, + .set_data_role = s2mu106_set_data_role, + .get_data_role = s2mu106_get_data_role, + .set_vconn_source = s2mu106_set_vconn_source, + .get_vconn_source = s2mu106_get_vconn_source, + .set_check_msg_pass = s2mu106_set_check_msg_pass, + .get_status = s2mu106_get_status, + .poll_status = s2mu106_poll_status, + .driver_reset = s2mu106_driver_reset, + .set_otg_control = s2mu106_set_otg_control, + .get_vbus_short_check = s2mu106_get_vbus_short_check, + .set_cc_control = s2mu106_set_cc_control, + .get_side_check = s2mu106_get_side_check, +}; + +#if defined CONFIG_PM +const struct dev_pm_ops s2mu106_usbpd_pm = { + .suspend = s2mu106_usbpd_suspend, + .resume = s2mu106_usbpd_resume, +}; +#endif + +static struct i2c_driver s2mu106_usbpd_driver = { + .driver = { + .name = USBPD_DEV_NAME, + .of_match_table = sec_usbpd_i2c_dt_ids, +#if defined CONFIG_PM + .pm = &s2mu106_usbpd_pm, +#endif /* CONFIG_PM */ + }, + .probe = s2mu106_usbpd_probe, + .remove = s2mu106_usbpd_remove, + .shutdown = s2mu106_usbpd_shutdown, + .id_table = s2mu106_usbpd_i2c_id, +}; + +static int __init s2mu106_usbpd_init(void) +{ + return i2c_add_driver(&s2mu106_usbpd_driver); +} +late_initcall(s2mu106_usbpd_init); + +static void __exit s2mu106_usbpd_exit(void) +{ + i2c_del_driver(&s2mu106_usbpd_driver); +} +module_exit(s2mu106_usbpd_exit); + +MODULE_DESCRIPTION("s2mu106 USB PD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ccic/usbpd-manager.c b/drivers/ccic/usbpd-manager.c index 9db290a8b6b3..c646773d0549 100644 --- a/drivers/ccic/usbpd-manager.c +++ b/drivers/ccic/usbpd-manager.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/drivers/ccic/usbpd.c b/drivers/ccic/usbpd.c new file mode 100644 index 000000000000..37cd7691db03 --- /dev/null +++ b/drivers/ccic/usbpd.c @@ -0,0 +1,603 @@ +/* +* USB PD Driver - Protocol Layer +*/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CCIC_SYSFS +#include +#include +#endif +#ifdef CONFIG_IFCONN_NOTIFIER +#include + +extern struct pdic_notifier_data pd_noti; +#endif + +static void increase_message_id_counter(struct usbpd_data *pd_data) +{ + pd_data->counter.message_id_counter++; + pd_data->counter.message_id_counter %= 8; +/* + if (pd_data->counter.message_id_counter++ > USBPD_nMessageIDCount) + pd_data->counter.message_id_counter = 0; +*/ +} + +static void rx_layer_init(struct protocol_data *rx) +{ + int i; + + rx->stored_message_id = USBPD_nMessageIDCount+1; + rx->msg_header.word = 0; + rx->state = 0; + rx->status = DEFAULT_PROTOCOL_NONE; + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) + rx->data_obj[i].object = 0; +} + +static void tx_layer_init(struct protocol_data *tx) +{ + int i; + + tx->stored_message_id = USBPD_nMessageIDCount+1; + tx->msg_header.word = 0; + tx->state = 0; + tx->status = DEFAULT_PROTOCOL_NONE; + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) + tx->data_obj[i].object = 0; +} + +static void tx_discard_message(struct protocol_data *tx) +{ + int i; + + tx->msg_header.word = 0; + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) + tx->data_obj[i].object = 0; +} + +void usbpd_init_protocol(struct usbpd_data *pd_data) +{ + rx_layer_init(&pd_data->protocol_rx); + tx_layer_init(&pd_data->protocol_tx); +} + +void usbpd_init_counters(struct usbpd_data *pd_data) +{ + pr_info("%s: init counter\n", __func__); + pd_data->counter.retry_counter = 0; + pd_data->counter.message_id_counter = 0; + pd_data->counter.caps_counter = 0; + pd_data->counter.hard_reset_counter = 0; + pd_data->counter.swap_hard_reset_counter = 0; + pd_data->counter.discover_identity_counter = 0; +} + +void usbpd_policy_reset(struct usbpd_data *pd_data, unsigned flag) +{ + if (flag == HARDRESET_RECEIVED) { + pd_data->policy.rx_hardreset = 1; + dev_info(pd_data->dev, "%s Hard reset\n", __func__); + } else if (flag == SOFTRESET_RECEIVED) { + pd_data->policy.rx_softreset = 1; + dev_info(pd_data->dev, "%s Soft reset\n", __func__); + } else if (flag == PLUG_EVENT) { + if (!pd_data->policy.plug_valid) + pd_data->policy.plug = 1; + pd_data->policy.plug_valid = 1; + dev_info(pd_data->dev, "%s ATTACHED\n", __func__); + } else if (flag == PLUG_DETACHED) { + pd_data->policy.plug_valid = 0; + dev_info(pd_data->dev, "%s DETACHED\n", __func__); + } +} + +protocol_state usbpd_protocol_tx_phy_layer_reset(struct protocol_data *tx) +{ + return PRL_Tx_Wait_for_Message_Request; +} + +protocol_state usbpd_protocol_tx_wait_for_message_request(struct protocol_data + *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + protocol_state state = PRL_Tx_Wait_for_Message_Request; + + /* S2MM004 PDIC already retry. + if (pd_data->counter.retry_counter > USBPD_nRetryCount) { + pd_data->counter.retry_counter = 0; + return state; + } + */ + if (pd_data->counter.retry_counter > 0) { + pd_data->counter.retry_counter = 0; + return state; + } + + pd_data->counter.retry_counter = 0; + + if (!tx->msg_header.word) + return state; + + if (tx->msg_header.num_data_objs == 0 && + tx->msg_header.msg_type == USBPD_Soft_Reset) + state = PRL_Tx_Layer_Reset_for_Transmit; + else + state = PRL_Tx_Construct_Message; + + return state; +} + +protocol_state usbpd_protocol_tx_layer_reset_for_transmit(struct protocol_data *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->counter.message_id_counter = 0; + pd_data->protocol_rx.state = PRL_Rx_Wait_for_PHY_Message; + + /* TODO: check Layer Reset Complete */ + return PRL_Tx_Construct_Message; +} + +protocol_state usbpd_protocol_tx_construct_message(struct protocol_data *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + tx->msg_header.msg_id = pd_data->counter.message_id_counter; + tx->status = DEFAULT_PROTOCOL_NONE; + + if (pd_data->phy_ops.tx_msg(pd_data, &tx->msg_header, tx->data_obj)) { + dev_err(pd_data->dev, "%s error\n", __func__); + return PRL_Tx_Construct_Message; + } + return PRL_Tx_Wait_for_PHY_Response; +} + +protocol_state usbpd_protocol_tx_wait_for_phy_response(struct protocol_data *tx) +{ +#if 0 + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + protocol_state state = PRL_Tx_Wait_for_PHY_Response; + u8 CrcCheck_cnt = 0; + + /* wait to get goodcrc */ + /* mdelay(1); */ + + /* polling */ + /* pd_data->phy_ops.poll_status(pd_data); */ + + for (CrcCheck_cnt = 0; CrcCheck_cnt < 2; CrcCheck_cnt++) { + if (pd_data->phy_ops.get_status(pd_data, MSG_GOODCRC)) { + pr_info("%s : %p\n", __func__, pd_data); + state = PRL_Tx_Message_Sent; + dev_info(pd_data->dev, "got GoodCRC.\n"); + return state; + } + + if (!CrcCheck_cnt) + pd_data->phy_ops.poll_status(pd_data); /* polling */ + } + + return PRL_Tx_Check_RetryCounter; +#endif + return PRL_Tx_Message_Sent; +} + +protocol_state usbpd_protocol_tx_match_messageid(struct protocol_data *tx) +{ + /* We don't use this function. + S2MM004 PDIC already check message id for incoming GoodCRC. + + struct usbpd_data *pd_data = protocol_tx_to_usbpd(protocol_tx); + protocol_state state = PRL_Tx_Match_MessageID; + + dev_info(pd_data->dev, "%s\n",__func__); + + if (pd_data->protocol_rx.msg_header.msg_id + == pd_data->counter.message_id_counter) + state = PRL_Tx_Message_Sent; + else + state = PRL_Tx_Check_RetryCounter; + + return state; + */ + return PRL_Tx_Message_Sent; +} + +protocol_state usbpd_protocol_tx_message_sent(struct protocol_data *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + increase_message_id_counter(pd_data); + tx->status = MESSAGE_SENT; + /* clear protocol header buffer */ + tx->msg_header.word = 0; + + return PRL_Tx_Wait_for_Message_Request; +} + +protocol_state usbpd_protocol_tx_check_retrycounter(struct protocol_data *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + /* S2MM004 PDIC already do retry. + Driver SW doesn't do retry. + + if (++pd_data->counter.retry_counter > USBPD_nRetryCount) { + state = PRL_Tx_Transmission_Error; + } else { + state = PRL_Tx_Construct_Message; + } + + return PRL_Tx_Check_RetryCounter; + */ + ++pd_data->counter.retry_counter; + return PRL_Tx_Transmission_Error; +} + +protocol_state usbpd_protocol_tx_transmission_error(struct protocol_data *tx) +{ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + dev_err(pd_data->dev, "%s\n", __func__); + + increase_message_id_counter(pd_data); + tx->status = TRANSMISSION_ERROR; + + return PRL_Tx_Wait_for_Message_Request; +} + +protocol_state usbpd_protocol_tx_discard_message(struct protocol_data *tx) +{ + /* This state is for Only Ping message */ + struct usbpd_data *pd_data = protocol_tx_to_usbpd(tx); + + dev_err(pd_data->dev, "%s\n", __func__); + tx_discard_message(tx); + increase_message_id_counter(pd_data); + + return PRL_Tx_PHY_Layer_Reset; +} + +void usbpd_set_ops(struct device *dev, usbpd_phy_ops_type *ops) +{ + struct usbpd_data *pd_data = (struct usbpd_data *) dev_get_drvdata(dev); + + pd_data->phy_ops.tx_msg = ops->tx_msg; + pd_data->phy_ops.rx_msg = ops->rx_msg; + pd_data->phy_ops.hard_reset = ops->hard_reset; + pd_data->phy_ops.set_power_role = ops->set_power_role; + pd_data->phy_ops.get_power_role = ops->get_power_role; + pd_data->phy_ops.set_data_role = ops->set_data_role; + pd_data->phy_ops.get_data_role = ops->get_data_role; + pd_data->phy_ops.get_vconn_source = ops->get_vconn_source; + pd_data->phy_ops.set_vconn_source = ops->set_vconn_source; + pd_data->phy_ops.set_check_msg_pass = ops->set_check_msg_pass; + pd_data->phy_ops.get_status = ops->get_status; + pd_data->phy_ops.poll_status = ops->poll_status; + pd_data->phy_ops.driver_reset = ops->driver_reset; + pd_data->phy_ops.set_otg_control = ops->set_otg_control; + pd_data->phy_ops.get_vbus_short_check = ops->get_vbus_short_check; + pd_data->phy_ops.set_cc_control = ops->set_cc_control; + pd_data->phy_ops.get_side_check = ops->get_side_check; +} + +protocol_state usbpd_protocol_rx_layer_reset_for_receive(struct protocol_data *rx) +{ + struct usbpd_data *pd_data = protocol_rx_to_usbpd(rx); + + dev_info(pd_data->dev, "%s\n", __func__); + /* + rx_layer_init(protocol_rx); + pd_data->protocol_tx.state = PRL_Tx_PHY_Layer_Reset; + */ + + usbpd_rx_soft_reset(pd_data); + return PRL_Rx_Layer_Reset_for_Receive; + + /*return PRL_Rx_Send_GoodCRC;*/ +} + +protocol_state usbpd_protocol_rx_wait_for_phy_message(struct protocol_data *rx) +{ + struct usbpd_data *pd_data = protocol_rx_to_usbpd(rx); + protocol_state state = PRL_Rx_Wait_for_PHY_Message; + + if (pd_data->phy_ops.rx_msg(pd_data, &rx->msg_header, rx->data_obj)) { + dev_err(pd_data->dev, "%s IO Error\n", __func__); + return state; + } else { + if (rx->msg_header.word == 0) { + dev_err(pd_data->dev, "%s No Message\n", __func__); + return state; /* no message */ + } else if (pd_data->phy_ops.get_status(pd_data, MSG_SOFTRESET)) { + dev_err(pd_data->dev, "[Rx] Got SOFTRESET.\n"); + state = PRL_Rx_Layer_Reset_for_Receive; + } else { + if (rx->stored_message_id == rx->msg_header.msg_id) + return state; + + dev_err(pd_data->dev, "[Rx] [0x%x] [0x%x]\n", + rx->msg_header.word, rx->data_obj[0].object); + /* new message is coming */ + state = PRL_Rx_Send_GoodCRC; + } + } + return state; +} + +protocol_state usbpd_protocol_rx_send_goodcrc(struct protocol_data *rx) +{ + /* Goodcrc sent by PDIC(HW) */ + return PRL_Rx_Check_MessageID; +} + +protocol_state usbpd_protocol_rx_store_messageid(struct protocol_data *rx) +{ + struct usbpd_data *pd_data = protocol_rx_to_usbpd(rx); + + rx->stored_message_id = rx->msg_header.msg_id; + usbpd_read_msg(pd_data); +/* + return PRL_Rx_Wait_for_PHY_Message; +*/ + return PRL_Rx_Store_MessageID; +} + +protocol_state usbpd_protocol_rx_check_messageid(struct protocol_data *rx) +{ + protocol_state state; + + if (rx->stored_message_id == rx->msg_header.msg_id) + state = PRL_Rx_Wait_for_PHY_Message; + else + state = PRL_Rx_Store_MessageID; + return state; +} + +void usbpd_protocol_tx(struct usbpd_data *pd_data) +{ + struct protocol_data *tx = &pd_data->protocol_tx; + protocol_state next_state = tx->state; + protocol_state saved_state; + + do { + saved_state = next_state; + switch (next_state) { + case PRL_Tx_PHY_Layer_Reset: + next_state = usbpd_protocol_tx_phy_layer_reset(tx); + break; + case PRL_Tx_Wait_for_Message_Request: + next_state = usbpd_protocol_tx_wait_for_message_request(tx); + break; + case PRL_Tx_Layer_Reset_for_Transmit: + next_state = usbpd_protocol_tx_layer_reset_for_transmit(tx); + break; + case PRL_Tx_Construct_Message: + next_state = usbpd_protocol_tx_construct_message(tx); + break; + case PRL_Tx_Wait_for_PHY_Response: + next_state = usbpd_protocol_tx_wait_for_phy_response(tx); + break; + case PRL_Tx_Match_MessageID: + next_state = usbpd_protocol_tx_match_messageid(tx); + break; + case PRL_Tx_Message_Sent: + next_state = usbpd_protocol_tx_message_sent(tx); + break; + case PRL_Tx_Check_RetryCounter: + next_state = usbpd_protocol_tx_check_retrycounter(tx); + break; + case PRL_Tx_Transmission_Error: + next_state = usbpd_protocol_tx_transmission_error(tx); + break; + case PRL_Tx_Discard_Message: + next_state = usbpd_protocol_tx_discard_message(tx); + break; + default: + next_state = PRL_Tx_Wait_for_Message_Request; + break; + } + } while (saved_state != next_state); + + tx->state = next_state; +} + +void usbpd_protocol_rx(struct usbpd_data *pd_data) +{ + struct protocol_data *rx = &pd_data->protocol_rx; + protocol_state next_state = rx->state; + protocol_state saved_state; + + do { + saved_state = next_state; + switch (next_state) { + case PRL_Rx_Layer_Reset_for_Receive: + next_state = usbpd_protocol_rx_layer_reset_for_receive(rx); + break; + case PRL_Rx_Wait_for_PHY_Message: + next_state = usbpd_protocol_rx_wait_for_phy_message(rx); + break; + case PRL_Rx_Send_GoodCRC: + next_state = usbpd_protocol_rx_send_goodcrc(rx); + break; + case PRL_Rx_Store_MessageID: + next_state = usbpd_protocol_rx_store_messageid(rx); + break; + case PRL_Rx_Check_MessageID: + next_state = usbpd_protocol_rx_check_messageid(rx); + break; + default: + next_state = PRL_Rx_Wait_for_PHY_Message; + break; + } + } while (saved_state != next_state); +/* + rx->state = next_state; +*/ + rx->state = PRL_Rx_Wait_for_PHY_Message; +} + +void usbpd_read_msg(struct usbpd_data *pd_data) +{ + int i; + + pd_data->policy.rx_msg_header.word + = pd_data->protocol_rx.msg_header.word; + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) { + pd_data->policy.rx_data_obj[i].object + = pd_data->protocol_rx.data_obj[i].object; + } +} + +/* return 1: sent with goodcrc, 0: fail */ +bool usbpd_send_msg(struct usbpd_data *pd_data, msg_header_type *header, + data_obj_type *obj) +{ + int i; + + if (obj) + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) + pd_data->protocol_tx.data_obj[i].object = obj[i].object; + else + header->num_data_objs = 0; + + header->spec_revision = USBPD_REV_20; + pd_data->protocol_tx.msg_header.word = header->word; + usbpd_protocol_tx(pd_data); + + if (pd_data->protocol_tx.status == MESSAGE_SENT) + return true; + else + return false; +} + +inline bool usbpd_send_ctrl_msg(struct usbpd_data *d, msg_header_type *h, + unsigned msg, unsigned dr, unsigned pr) +{ + h->msg_type = msg; + h->port_data_role = dr; + h->port_power_role = pr; + h->num_data_objs = 0; + return usbpd_send_msg(d, h, 0); +} + +/* return: 0 if timed out, positive is status */ +inline unsigned usbpd_wait_msg(struct usbpd_data *pd_data, + unsigned msg_status, unsigned ms) +{ + unsigned long ret; + + ret = pd_data->phy_ops.get_status(pd_data, msg_status); + if (ret) { + pd_data->policy.abnormal_state = false; + return ret; + } + + pr_info("%s, %d\n", __func__, __LINE__); + /* wait */ + reinit_completion(&pd_data->msg_arrived); + pd_data->wait_for_msg_arrived = msg_status; + ret = wait_for_completion_timeout(&pd_data->msg_arrived, + msecs_to_jiffies(ms)); + + if (!pd_data->policy.state) { + dev_err(pd_data->dev, "%s : return for policy state error\n", __func__); + pd_data->policy.abnormal_state = true; + return 0; + } + + pd_data->policy.abnormal_state = false; + + return pd_data->phy_ops.get_status(pd_data, msg_status); +} + +void usbpd_rx_hard_reset(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + + usbpd_reinit(dev); + usbpd_policy_reset(pd_data, HARDRESET_RECEIVED); +} + +void usbpd_rx_soft_reset(struct usbpd_data *pd_data) +{ + usbpd_reinit(pd_data->dev); + usbpd_policy_reset(pd_data, SOFTRESET_RECEIVED); +} + +void usbpd_reinit(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + + usbpd_init_counters(pd_data); + usbpd_init_protocol(pd_data); + usbpd_init_policy(pd_data); + usbpd_init_manager_val(pd_data); + reinit_completion(&pd_data->msg_arrived); + pd_data->wait_for_msg_arrived = 0; + complete(&pd_data->msg_arrived); +} + +/* + * usbpd_init - alloc usbpd data + * + * Returns 0 on success; negative errno on failure +*/ +int usbpd_init(struct device *dev, void *phy_driver_data) +{ + struct usbpd_data *pd_data; +#ifdef CONFIG_CCIC_SYSFS + int ret = 0; +#endif + + if (!dev) + return -EINVAL; + + pd_data = kzalloc(sizeof(struct usbpd_data), GFP_KERNEL); + + if (!pd_data) + return -ENOMEM; + + pd_data->dev = dev; + pd_data->phy_driver_data = phy_driver_data; + dev_set_drvdata(dev, pd_data); + +#ifdef CONFIG_CCIC_SYSFS + /* create sysfs group */ + pd_data->ccic_dev = sec_device_create(NULL, "ccic"); + if (IS_ERR(pd_data->ccic_dev)) + pr_err("%s Failed to create device(switch)!\n", __func__); + else { + /* create sysfs group */ + ret = sysfs_create_group(&pd_data->ccic_dev->kobj, &ccic_sysfs_group); + if (ret) + pr_err("%s: ccic sysfs fail, ret %d", __func__, ret); + else + dev_set_drvdata(pd_data->ccic_dev, pd_data); + } +#endif + +#ifdef CONFIG_IFCONN_NOTIFIER + pd_noti.pd_data = pd_data; + pd_noti.sink_status.current_pdo_num = 0; + pd_noti.sink_status.selected_pdo_num = 0; +#endif + usbpd_init_counters(pd_data); + usbpd_init_protocol(pd_data); + usbpd_init_policy(pd_data); + usbpd_init_manager(pd_data); + + INIT_WORK(&pd_data->worker, usbpd_policy_work); + init_completion(&pd_data->msg_arrived); + + return 0; +} diff --git a/drivers/ccic/usbpd_cc.c b/drivers/ccic/usbpd_cc.c new file mode 100644 index 000000000000..4c472c27a859 --- /dev/null +++ b/drivers/ccic/usbpd_cc.c @@ -0,0 +1,424 @@ +/* + driver/usbpd/usbpd_cc.c - USB PD(Power Delivery) device driver + * + * Copyright (C) 2017 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (defined CONFIG_CCIC_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF \ + || defined CONFIG_IFCONN_NOTIFIER) +#include +#endif +#if defined CONFIG_IFCONN_NOTIFIER +#include +#endif +#include +#include + +#if defined(CONFIG_CCIC_NOTIFIER) +static void ccic_event_notifier(struct work_struct *data) +{ + struct ccic_state_work *event_work = + container_of(data, struct ccic_state_work, ccic_work); + CC_NOTI_TYPEDEF ccic_noti; + + switch (event_work->dest) { + case CCIC_NOTIFY_DEV_USB: + pr_info("usb:%s, dest=%s, id=%s, attach=%s, drp=%s, event_work=%p\n", __func__, + CCIC_NOTI_DEST_Print[event_work->dest], + CCIC_NOTI_ID_Print[event_work->id], + event_work->attach ? "Attached" : "Detached", + CCIC_NOTI_USB_STATUS_Print[event_work->event], + event_work); + break; + default: + pr_info("usb:%s, dest=%s, id=%s, attach=%d, event=%d, event_work=%p\n", __func__, + CCIC_NOTI_DEST_Print[event_work->dest], + CCIC_NOTI_ID_Print[event_work->id], + event_work->attach, + event_work->event, + event_work); + break; + } + ccic_noti.src = CCIC_NOTIFY_DEV_CCIC; + ccic_noti.dest = event_work->dest; + ccic_noti.id = event_work->id; + ccic_noti.sub1 = event_work->attach; + ccic_noti.sub2 = event_work->event; + ccic_noti.sub3 = 0; +#ifdef CONFIG_BATTERY_SAMSUNG +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + ccic_noti.pd = &pd_noti; +#endif +#endif + ccic_notifier_notify((CC_NOTI_TYPEDEF *)&ccic_noti, NULL, 0); + + kfree(event_work); +} + +extern void ccic_event_work(void *data, int dest, int id, int attach, int event) +{ + struct s2mu106_usbpd_data *usbpd_data = data; + struct ccic_state_work *event_work; + + + event_work = kmalloc(sizeof(struct ccic_state_work), GFP_ATOMIC); + pr_info("usb: %s,event_work(%p)\n", __func__, event_work); + INIT_WORK(&event_work->ccic_work, ccic_event_notifier); + + event_work->dest = dest; + event_work->id = id; + event_work->attach = attach; + event_work->event = event; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (id == CCIC_NOTIFY_ID_USB) { + pr_info("usb: %s, dest=%d, event=%d, usbpd_data->data_role_dual=%d, usbpd_data->try_state_change=%d\n", + __func__, dest, event, usbpd_data->data_role_dual, usbpd_data->try_state_change); + + usbpd_data->data_role_dual = event; + + if (usbpd_data->dual_role != NULL) + dual_role_instance_changed(usbpd_data->dual_role); + + if (usbpd_data->try_state_change && + (usbpd_data->data_role_dual != IFCONN_NOTIFY_EVENT_DETACH)) { + /* Role change try and new mode detected */ + pr_info("usb: %s, reverse_completion\n", __func__); + complete(&usbpd_data->reverse_completion); + } + } +#endif + + if (queue_work(usbpd_data->ccic_wq, &event_work->ccic_work) == 0) { + pr_info("usb: %s, event_work(%p) is dropped\n", __func__, event_work); + kfree(event_work); + } +} +#endif + +#if defined(CONFIG_IFCONN_NOTIFIER) +static void ifconn_event_notifier(struct work_struct *data) +{ + struct ifconn_state_work *event_work = + container_of(data, struct ifconn_state_work, ifconn_work); + + switch (event_work->id) { + case IFCONN_NOTIFY_ID_POWER_STATUS: + case IFCONN_NOTIFY_ID_DP_CONNECT: + case IFCONN_NOTIFY_ID_DP_HPD: + case IFCONN_NOTIFY_ID_DP_LINK_CONF: + ifconn_notifier_notify(IFCONN_NOTIFY_PDIC, event_work->dest, + event_work->id, event_work->event, + IFCONN_NOTIFY_PARAM_DATA, event_work->data); + break; + default: + ifconn_notifier_notify(IFCONN_NOTIFY_CCIC, event_work->dest, + event_work->id, event_work->event, + IFCONN_NOTIFY_PARAM_DATA, event_work->data); + break; + } + + kfree(event_work); +} + +extern void ifconn_event_work(void *pd_data, int dest, int id, int event, void *data) +{ + struct s2mu106_usbpd_data *usbpd_data = pd_data; + struct ifconn_state_work *event_work; + + event_work = kmalloc(sizeof(struct ifconn_state_work), GFP_ATOMIC); + INIT_WORK(&event_work->ifconn_work, ifconn_event_notifier); + + event_work->dest = dest; + event_work->id = id; + event_work->event = event; + event_work->data = data; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + if (id == IFCONN_NOTIFY_ID_USB) { + pr_info("usb: %s, dest=%d, event=%d, usbpd_data->data_role_dual=%d, usbpd_data->try_state_change=%d\n", + __func__, dest, event, usbpd_data->data_role_dual, usbpd_data->try_state_change); + + usbpd_data->data_role_dual = event; + + if (usbpd_data->dual_role != NULL) + dual_role_instance_changed(usbpd_data->dual_role); + + if (usbpd_data->try_state_change && + (usbpd_data->data_role_dual != IFCONN_NOTIFY_EVENT_DETACH)) { + /* Role change try and new mode detected */ + pr_info("usb: %s, reverse_completion\n", __func__); + complete(&usbpd_data->reverse_completion); + } + } +#endif + + if (queue_work(usbpd_data->ifconn_wq, &event_work->ifconn_work) == 0) { + pr_info("usb: %s, event_work(%p) is dropped\n", __func__, event_work); + kfree(event_work); + } +} +#endif + +#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, +}; + +void role_swap_check(struct work_struct *wk) +{ + struct delayed_work *delay_work = + container_of(wk, struct delayed_work, work); + struct s2mu106_usbpd_data *usbpd_data = + container_of(delay_work, struct s2mu106_usbpd_data, role_swap_work); + int mode = 0; + + pr_info("%s: ccic_set_dual_role check again.\n", __func__); + usbpd_data->try_state_change = 0; + + if (usbpd_data->detach_valid) { /* modify here using pd_state */ + pr_err("%s: ccic_set_dual_role reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + mode = TYPE_C_ATTACH_DRP; + s2mu106_rprd_mode_change(usbpd_data, mode); + enable_irq(usbpd_data->irq); + } +} + +static int ccic_set_dual_role(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + struct s2mu106_usbpd_data *usbpd_data = dual_role_get_drvdata(dual_role); + struct i2c_client *i2c; + + ifconn_notifier_event_t attached_state; + int mode; + int timeout = 0; + int ret = 0; + + if (!usbpd_data) { + pr_err("%s : usbpd_data is null \n", __func__); + return -EINVAL; + } + + i2c = usbpd_data->i2c; + + /* Get Current Role */ + attached_state = usbpd_data->data_role_dual; + pr_info("%s : request prop = %d , attached_state = %d\n", __func__, prop, attached_state); + + if (attached_state != IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP + && attached_state != IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP) { + pr_err("%s : current mode : %d - just return \n", __func__, attached_state); + return 0; + } + + if (attached_state == IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP + && *val == DUAL_ROLE_PROP_MODE_DFP) { + pr_err("%s : current mode : %d - request mode : %d just return \n", + __func__, attached_state, *val); + return 0; + } + + if (attached_state == IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP + && *val == DUAL_ROLE_PROP_MODE_UFP) { + pr_err("%s : current mode : %d - request mode : %d just return \n", + __func__, attached_state, *val); + return 0; + } + + if (attached_state == IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP) { + /* Current mode DFP and Source */ + pr_info("%s: try reversing, from Source to Sink\n", __func__); + /* turns off VBUS first */ + vbus_turn_on_ctrl(usbpd_data, 0); +#if defined(CONFIG_IFCONN_NOTIFIER) + /* muic */ + ifconn_event_work(usbpd_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); +#endif + /* exit from Disabled state and set mode to UFP */ + mode = TYPE_C_ATTACH_UFP; + usbpd_data->try_state_change = TYPE_C_ATTACH_UFP; + s2mu106_rprd_mode_change(usbpd_data, mode); + } else { + /* Current mode UFP and Sink */ + pr_info("%s: try reversing, from Sink to Source\n", __func__); + /* exit from Disabled state and set mode to UFP */ + mode = TYPE_C_ATTACH_DFP; + usbpd_data->try_state_change = TYPE_C_ATTACH_DFP; + s2mu106_rprd_mode_change(usbpd_data, mode); + } + + reinit_completion(&usbpd_data->reverse_completion); + timeout = + wait_for_completion_timeout(&usbpd_data->reverse_completion, + msecs_to_jiffies + (DUAL_ROLE_SET_MODE_WAIT_MS)); + + if (!timeout) { + usbpd_data->try_state_change = 0; + pr_err("%s: reverse failed, set mode to DRP\n", __func__); + disable_irq(usbpd_data->irq); + /* exit from Disabled state and set mode to DRP */ + mode = TYPE_C_ATTACH_DRP; + s2mu106_rprd_mode_change(usbpd_data, mode); + enable_irq(usbpd_data->irq); + ret = -EIO; + } else { + pr_err("%s: reverse success, one more check\n", __func__); + schedule_delayed_work(&usbpd_data->role_swap_work, msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS)); + } + + dev_info(&i2c->dev, "%s -> data role : %d\n", __func__, *val); + return ret; +} + +/* Decides whether userspace can change a specific property */ +int dual_role_is_writeable(struct dual_role_phy_instance *drp, + enum dual_role_property prop) +{ + if (prop == DUAL_ROLE_PROP_MODE) + return 1; + else + return 0; +} + +/* Callback for "cat /sys/class/dual_role_usb/otg_default/" */ +int dual_role_get_local_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val) +{ + struct s2mu106_usbpd_data *usbpd_data = dual_role_get_drvdata(dual_role); + + ifconn_notifier_event_t attached_state; + int power_role_dual; + + if (!usbpd_data) { + pr_err("%s : usbpd_data is null : request prop = %d \n", __func__, prop); + return -EINVAL; + } + attached_state = usbpd_data->data_role_dual; + power_role_dual = usbpd_data->power_role_dual; + + pr_info("%s : request prop = %d , attached_state = %d, power_role_dual = %d\n", + __func__, prop, attached_state, power_role_dual); + + 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 == IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP) { + if (prop == DUAL_ROLE_PROP_MODE) + *val = DUAL_ROLE_PROP_MODE_DFP; + else if (prop == DUAL_ROLE_PROP_PR) + *val = power_role_dual; + else if (prop == DUAL_ROLE_PROP_DR) + *val = DUAL_ROLE_PROP_DR_HOST; + else + return -EINVAL; + } else if (attached_state == IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP) { + if (prop == DUAL_ROLE_PROP_MODE) + *val = DUAL_ROLE_PROP_MODE_UFP; + else if (prop == DUAL_ROLE_PROP_PR) + *val = power_role_dual; + 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 > + * /sys/class/dual_role_usb//" + * Block until the entire final state is reached. + * Blocking is one of the better ways to signal when the operation + * is done. + * This function tries to switch to Attached.SRC or Attached.SNK + * by forcing the mode into SRC or SNK. + * On failure, we fall back to Try.SNK state machine. + */ +int dual_role_set_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + pr_info("%s : request prop = %d , *val = %d \n", __func__, prop, *val); + if (prop == DUAL_ROLE_PROP_MODE) + return ccic_set_dual_role(dual_role, prop, val); + else + return -EINVAL; +} + +int dual_role_init(void *_data) +{ + struct s2mu106_usbpd_data *pdic_data = _data; + struct dual_role_phy_desc *desc; + struct dual_role_phy_instance *dual_role; + + desc = devm_kzalloc(pdic_data->dev, + sizeof(struct dual_role_phy_desc), GFP_KERNEL); + if (!desc) { + pr_err("unable to allocate dual role descriptor\n"); + return -1; + } + + desc->name = "otg_default"; + desc->supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP; + desc->get_property = dual_role_get_local_prop; + desc->set_property = dual_role_set_prop; + desc->properties = fusb_drp_properties; + desc->num_properties = ARRAY_SIZE(fusb_drp_properties); + desc->property_is_writeable = dual_role_is_writeable; + dual_role = + devm_dual_role_instance_register(pdic_data->dev, desc); + dual_role->drv_data = pdic_data; + pdic_data->dual_role = dual_role; + pdic_data->desc = desc; + init_completion(&pdic_data->reverse_completion); + INIT_DELAYED_WORK(&pdic_data->role_swap_work, role_swap_check); + + return 0; +} +#endif diff --git a/drivers/ccic/usbpd_manager.c b/drivers/ccic/usbpd_manager.c new file mode 100644 index 000000000000..bbdce419ef11 --- /dev/null +++ b/drivers/ccic/usbpd_manager.c @@ -0,0 +1,1123 @@ +/* +* USB PD Driver - Device Policy Manager +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#if defined(CONFIG_MUIC_NOTIFIER) +#include +#endif /* CONFIG_MUIC_NOTIFIER */ + +#if defined(CONFIG_CCIC_NOTIFIER) +#include +#endif + +#if (defined CONFIG_CCIC_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF\ + || CONFIG_IFCONN_NOTIFIER) +#include +#endif + +/* switch device header */ +#if defined(CONFIG_SWITCH) +#include +#endif /* CONFIG_SWITCH */ + +#ifdef CONFIG_USB_HOST_NOTIFY +#include +#endif + +#include +#if defined(CONFIG_SWITCH) +static struct switch_dev switch_dock = { + .name = "ccic_dock", +}; +#endif + +static char DP_Pin_Assignment_Print[7][40] = { + {"DP_Pin_Assignment_None"}, + {"DP_Pin_Assignment_A"}, + {"DP_Pin_Assignment_B"}, + {"DP_Pin_Assignment_C"}, + {"DP_Pin_Assignment_D"}, + {"DP_Pin_Assignment_E"}, + {"DP_Pin_Assignment_F"}, + +}; + +#ifdef CONFIG_IFCONN_NOTIFIER +void select_pdo(int num); +void s2mu106_select_pdo(int num); +void (*fp_select_pdo)(int num); + +void s2mu106_select_pdo(int num) +{ + struct usbpd_data *pd_data = pd_noti.pd_data; + struct usbpd_manager_data *manager = &pd_data->manager; + bool vbus_short; + + pd_data->phy_ops.get_vbus_short_check(pd_data, &vbus_short); + + if (vbus_short) { + pr_info(" %s : PDO(%d) is ignored becasue of vbus short\n", + __func__, pd_noti.sink_status.selected_pdo_num); + return; + } + + if (pd_noti.sink_status.selected_pdo_num == num) + return; + else if (num > pd_noti.sink_status.available_pdo_num) + pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.available_pdo_num; + else if (num < 1) + pd_noti.sink_status.selected_pdo_num = 1; + else + pd_noti.sink_status.selected_pdo_num = num; + pr_info(" %s : PDO(%d) is selected to change\n", __func__, pd_noti.sink_status.selected_pdo_num); + + schedule_delayed_work(&manager->select_pdo_handler, msecs_to_jiffies(50)); +} + +void select_pdo(int num) +{ + if (fp_select_pdo) + fp_select_pdo(num); +} +#endif + +#ifdef CONFIG_CHECK_CTYPE_SIDE +int usbpd_manager_get_side_check(void) +{ + struct usbpd_data *pd_data = pd_noti.pd_data; + int ret = 0; + + ret = pd_data->phy_ops.get_side_check(pd_data); + + return ret; +} +#endif +void usbpd_manager_select_pdo_handler(struct work_struct *work) +{ + pr_info("%s: call select pdo handler\n", __func__); + + usbpd_manager_inform_event(pd_noti.pd_data, + MANAGER_NEW_POWER_SRC); + +} + +void usbpd_manager_select_pdo_cancel(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + cancel_delayed_work_sync(&manager->select_pdo_handler); +} + +void usbpd_manager_start_discover_msg_handler(struct work_struct *work) +{ + struct usbpd_manager_data *manager = + container_of(work, struct usbpd_manager_data, + start_discover_msg_handler.work); + pr_info("%s: call handler\n", __func__); + + mutex_lock(&manager->vdm_mutex); + if (manager->alt_sended == 0 && manager->vdm_en == 1) { + usbpd_manager_inform_event(pd_noti.pd_data, + MANAGER_START_DISCOVER_IDENTITY); + manager->alt_sended = 1; + } + mutex_unlock(&manager->vdm_mutex); +} + +void usbpd_manager_start_discover_msg_cancel(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + cancel_delayed_work_sync(&manager->start_discover_msg_handler); +} + +static void init_source_cap_data(struct usbpd_manager_data *_data) +{ +/* struct usbpd_data *pd_data = manager_to_usbpd(_data); + int val; */ + msg_header_type *msg_header = &_data->pd_data->source_msg_header; + data_obj_type *data_obj = &_data->pd_data->source_data_obj; + + msg_header->msg_type = USBPD_Source_Capabilities; +/* pd_data->phy_ops.get_power_role(pd_data, &val); */ + msg_header->port_data_role = USBPD_DFP; + msg_header->spec_revision = 1; + msg_header->port_power_role = USBPD_SOURCE; + msg_header->num_data_objs = 1; + + data_obj->power_data_obj.max_current = 500 / 10; + data_obj->power_data_obj.voltage = 5000 / 50; + data_obj->power_data_obj.supply = POWER_TYPE_FIXED; + data_obj->power_data_obj.data_role_swap = 1; + data_obj->power_data_obj.dual_role_power = 1; + data_obj->power_data_obj.usb_suspend_support = 1; + data_obj->power_data_obj.usb_comm_capable = 1; + +} + +static void init_sink_cap_data(struct usbpd_manager_data *_data) +{ +/* struct usbpd_data *pd_data = manager_to_usbpd(_data); + int val; */ + msg_header_type *msg_header = &_data->pd_data->sink_msg_header; + data_obj_type *data_obj = _data->pd_data->sink_data_obj; + + msg_header->msg_type = USBPD_Sink_Capabilities; +/* pd_data->phy_ops.get_power_role(pd_data, &val); */ + msg_header->port_data_role = USBPD_UFP; + msg_header->spec_revision = 1; + msg_header->port_power_role = USBPD_SINK; + msg_header->num_data_objs = 2; + + data_obj->power_data_obj_sink.supply_type = POWER_TYPE_FIXED; + data_obj->power_data_obj_sink.dual_role_power = 1; + data_obj->power_data_obj_sink.higher_capability = 1; + data_obj->power_data_obj_sink.externally_powered = 0; + data_obj->power_data_obj_sink.usb_comm_capable = 1; + data_obj->power_data_obj_sink.data_role_swap = 1; + data_obj->power_data_obj_sink.voltage = 5000/50; + data_obj->power_data_obj_sink.op_current = 500/10; + + (data_obj + 1)->power_data_obj_variable.supply_type = POWER_TYPE_VARIABLE; + (data_obj + 1)->power_data_obj_variable.max_voltage = _data->sink_cap_max_volt / 50; + (data_obj + 1)->power_data_obj_variable.min_voltage = 5000 / 50; + (data_obj + 1)->power_data_obj_variable.max_current = 500 / 10; +} + +void usbpd_manager_receive_samsung_uvdm_message(struct usbpd_data *pd_data) +{ +} + +void usbpd_manager_plug_attach(struct device *dev) +{ +#if defined(CONFIG_IFCONN_NOTIFIER) + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + struct policy_data *policy = &pd_data->policy; + struct usbpd_manager_data *manager = &pd_data->manager; + + if (policy->send_sink_cap) { + pd_noti.event = IFCONN_NOTIFY_EVENT_PD_SINK_CAP; + policy->send_sink_cap = 0; + } else + pd_noti.event = IFCONN_NOTIFY_EVENT_PD_SINK; + manager->template.data = &pd_noti.sink_status; + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_POWER_STATUS, IFCONN_NOTIFY_EVENT_ATTACH, &pd_noti); +#endif +} + +void usbpd_manager_plug_detach(struct device *dev, bool notify) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + + pr_info("%s: usbpd plug detached\n", __func__); + + usbpd_policy_reset(pd_data, PLUG_DETACHED); +} + +void usbpd_manager_acc_detach(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + pr_info("%s\n", __func__); + if ( manager->acc_type != CCIC_DOCK_DETACHED ) { + pr_info("%s: schedule_delayed_work \n", __func__); + if ( manager->acc_type == CCIC_DOCK_HMT ) + schedule_delayed_work(&manager->acc_detach_handler, msecs_to_jiffies(1000)); + else + schedule_delayed_work(&manager->acc_detach_handler, msecs_to_jiffies(0)); + } +} + +int usbpd_manager_command_to_policy(struct device *dev, + usbpd_manager_command_type command) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + manager->cmd |= command; + + usbpd_kick_policy_work(dev); + + /* TODO: check result + if (manager->event) { + ... + } + */ + return 0; +} + +void usbpd_manager_inform_event(struct usbpd_data *pd_data, + usbpd_manager_event_type event) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + int ret = 0; + + manager->event = event; + + switch (event) { + case MANAGER_DISCOVER_IDENTITY_ACKED: + usbpd_manager_get_identity(pd_data); + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_DISCOVER_SVID); + break; + case MANAGER_DISCOVER_SVID_ACKED: + usbpd_manager_get_svids(pd_data); + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_DISCOVER_MODE); + break; + case MANAGER_DISCOVER_MODE_ACKED: + ret = usbpd_manager_get_modes(pd_data); + if (ret == USBPD_DP_SUPPORT) + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_ENTER_MODE); + break; + case MANAGER_ENTER_MODE_ACKED: + usbpd_manager_enter_mode(pd_data); + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_STATUS_UPDATE); + break; + case MANAGER_STATUS_UPDATE_ACKED: + usbpd_manager_get_status(pd_data); + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_DisplayPort_Configure); + break; + case MANAGER_DisplayPort_Configure_ACKED: + usbpd_manager_get_configure(pd_data); + break; + case MANAGER_ATTENTION_REQUEST: + usbpd_manager_get_attention(pd_data); + break; + case MANAGER_NEW_POWER_SRC: + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_NEW_POWER_SRC); + break; + case MANAGER_UVDM_SEND_MESSAGE: + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_UVDM_SEND_MESSAGE); + break; + case MANAGER_UVDM_RECEIVE_MESSAGE: + usbpd_manager_receive_samsung_uvdm_message(pd_data); + break; + case MANAGER_START_DISCOVER_IDENTITY: + usbpd_manager_command_to_policy(pd_data->dev, + MANAGER_REQ_VDM_DISCOVER_IDENTITY); + break; + default: + pr_info("%s: not matched event(%d)\n", __func__, event); + } +} + +bool usbpd_manager_vdm_request_enabled(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + /* TODO : checking cable discovering + if (pd_data->counter.discover_identity_counter + < USBPD_nDiscoverIdentityCount) + + struct usbpd_manager_data *manager = &pd_data->manager; + if (manager->event != MANAGER_DISCOVER_IDENTITY_ACKED + || manager->event != MANAGER_DISCOVER_IDENTITY_NAKED) + + return(1); + */ + + manager->vdm_en = 1; + + schedule_delayed_work(&manager->start_discover_msg_handler, + msecs_to_jiffies(50)); + return true; +} + +bool usbpd_manager_power_role_swap(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + + return manager->power_role_swap; +} + +bool usbpd_manager_vconn_source_swap(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + + return manager->vconn_source_swap; +} + +void usbpd_manager_turn_off_vconn(struct usbpd_data *pd_data) +{ + /* TODO : Turn off vconn */ +} + +void usbpd_manager_turn_on_source(struct usbpd_data *pd_data) +{ + pr_info("%s: usbpd plug attached\n", __func__); + + /* TODO : Turn on source */ +} + +void usbpd_manager_turn_off_power_supply(struct usbpd_data *pd_data) +{ + pr_info("%s: usbpd plug detached\n", __func__); + + /* TODO : Turn off power supply */ +} + +void usbpd_manager_turn_off_power_sink(struct usbpd_data *pd_data) +{ + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + + pr_info("%s: usbpd sink turn off\n", __func__); + + /* TODO : Turn off power sink */ + pd_noti.event = IFCONN_NOTIFY_EVENT_DETACH; + ifconn_event_work(pdic_data, IFCONN_NOTIFY_BATTERY, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); +} + +bool usbpd_manager_data_role_swap(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + + return manager->data_role_swap; +} + +int usbpd_manager_register_switch_device(int mode) +{ +#ifdef CONFIG_SWITCH + int ret = 0; + if (mode) { + ret = switch_dev_register(&switch_dock); + if (ret < 0) { + pr_err("%s: Failed to register dock switch(%d)\n", + __func__, ret); + return -ENODEV; + } + } else { + switch_dev_unregister(&switch_dock); + } +#endif /* CONFIG_SWITCH */ + return 0; +} + +static void usbpd_manager_send_dock_intent(int type) +{ + pr_info("%s: CCIC dock type(%d)\n", __func__, type); +#ifdef CONFIG_SWITCH + switch_set_state(&switch_dock, type); +#endif /* CONFIG_SWITCH */ +} + +void usbpd_manager_send_dock_uevent(u32 vid, u32 pid, int state) +{ + char switch_string[32]; + char pd_ids_string[32]; + + pr_info("%s: CCIC dock : USBPD_IPS=%04x:%04x SWITCH_STATE=%d\n", + __func__, + le16_to_cpu(vid), + le16_to_cpu(pid), + state); + + + snprintf(switch_string, 32, "SWITCH_STATE=%d", state); + snprintf(pd_ids_string, 32, "USBPD_IDS=%04x:%04x", + le16_to_cpu(vid), + le16_to_cpu(pid)); +} + +void usbpd_manager_acc_detach_handler(struct work_struct *wk) +{ + struct usbpd_manager_data *manager = + container_of(wk, struct usbpd_manager_data, acc_detach_handler.work); + + pr_info("%s: ccic dock type %d\n", __func__, + manager->acc_type); + if (manager->acc_type != CCIC_DOCK_DETACHED) { + if (manager->acc_type != CCIC_DOCK_NEW) + usbpd_manager_send_dock_intent(CCIC_DOCK_DETACHED); + usbpd_manager_send_dock_uevent(manager->Vendor_ID, manager->Product_ID, + CCIC_DOCK_DETACHED); + manager->acc_type = CCIC_DOCK_DETACHED; + manager->Vendor_ID = 0; + manager->Product_ID = 0; + manager->is_samsung_accessory_enter_mode = false; + } +} + +void usbpd_manager_acc_handler_cancel(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + struct usbpd_manager_data *manager = &pd_data->manager; + + if (manager->acc_type != CCIC_DOCK_DETACHED) { + pr_info("%s: cancel_delayed_work_sync \n", __func__); + cancel_delayed_work_sync(&manager->acc_detach_handler); + } +} + +static int usbpd_manager_check_accessory(struct usbpd_manager_data *manager) +{ +#if defined(CONFIG_USB_HW_PARAM) + struct otg_notify *o_notify = get_otg_notify(); +#endif + uint16_t vid = manager->Vendor_ID; + uint16_t pid = manager->Product_ID; + uint16_t acc_type = CCIC_DOCK_DETACHED; + + /* detect Gear VR */ + if (manager->acc_type == CCIC_DOCK_DETACHED) { + if (vid == SAMSUNG_VENDOR_ID) { + switch (pid) { + /* GearVR: Reserved GearVR PID+6 */ + case GEARVR_PRODUCT_ID: + case GEARVR_PRODUCT_ID_1: + case GEARVR_PRODUCT_ID_2: + case GEARVR_PRODUCT_ID_3: + case GEARVR_PRODUCT_ID_4: + case GEARVR_PRODUCT_ID_5: + acc_type = CCIC_DOCK_HMT; + pr_info("%s : Samsung Gear VR connected.\n", __func__); +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_VR_USE_COUNT); +#endif + break; + case DEXDOCK_PRODUCT_ID: + acc_type = CCIC_DOCK_DEX; + pr_info("%s : Samsung DEX connected.\n", __func__); +#if defined(CONFIG_USB_HW_PARAM) + if (o_notify) + inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT); +#endif + break; + case HDMI_PRODUCT_ID: + acc_type = CCIC_DOCK_HDMI; + pr_info("%s : Samsung HDMI connected.\n", __func__); + break; + default: + acc_type = CCIC_DOCK_NEW; + pr_info("%s : default device connected.\n", __func__); + break; + } + } else if (vid == SAMSUNG_MPA_VENDOR_ID) { + switch(pid) { + case MPA_PRODUCT_ID: + acc_type = CCIC_DOCK_MPA; + pr_info("%s : Samsung MPA connected.\n", __func__); + break; + default: + acc_type = CCIC_DOCK_NEW; + pr_info("%s : default device connected.\n", __func__); + break; + } + } + manager->acc_type = acc_type; + } else + acc_type = manager->acc_type; + + if (acc_type != CCIC_DOCK_NEW) + usbpd_manager_send_dock_intent(acc_type); + + usbpd_manager_send_dock_uevent(vid, pid, acc_type); + return 1; +} + +/* Ok : 0, NAK: -1 */ +int usbpd_manager_get_identity(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct usbpd_manager_data *manager = &pd_data->manager; + + manager->Vendor_ID = policy->rx_data_obj[1].id_header_vdo.USB_Vendor_ID; + manager->Product_ID = policy->rx_data_obj[3].product_vdo.USB_Product_ID; + manager->Device_Version = policy->rx_data_obj[3].product_vdo.Device_Version; + + pr_info("%s, Vendor_ID : 0x%x, Product_ID : 0x%x, Device Version : 0x%x\n", + __func__, manager->Vendor_ID, manager->Product_ID, manager->Device_Version); + + if (usbpd_manager_check_accessory(manager)) + pr_info("%s, Samsung Accessory Connected.\n", __func__); + + return 0; +} + +/* Ok : 0, NAK: -1 */ +int usbpd_manager_get_svids(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct usbpd_manager_data *manager = &pd_data->manager; + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + + manager->SVID_0 = policy->rx_data_obj[1].vdm_svid.svid_0; + manager->SVID_1 = policy->rx_data_obj[1].vdm_svid.svid_1; + + pr_info("%s, SVID_0 : 0x%x, SVID_1 : 0x%x\n", __func__, + manager->SVID_0, manager->SVID_1); + + if (manager->SVID_0 == TypeC_DP_SUPPORT) { +#if defined(CONFIG_IFCONN_NOTIFIER) + if (pdic_data->is_client == CLIENT_ON) { + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_DETACH, NULL); +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + pdic_data->power_role = DUAL_ROLE_PROP_PR_NONE; +#endif + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_ID_USB, IFCONN_NOTIFY_EVENT_DETACH, NULL); + pdic_data->is_client = CLIENT_OFF; + } + + if (pdic_data->is_host == HOST_OFF) { + /* muic */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MUIC, + IFCONN_NOTIFY_ID_ATTACH, IFCONN_NOTIFY_EVENT_ATTACH, NULL); + /* otg */ + pdic_data->is_host = HOST_ON; + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_ID_USB, + IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP, NULL); + } +#endif + manager->dp_is_connect = 1; + /* If you want to support USB SuperSpeed when you connect + * Display port dongle, You should change dp_hs_connect depend + * on Pin assignment.If DP use 4lane(Pin Assignment C,E,A), + * dp_hs_connect is 1. USB can support HS.If DP use 2lane(Pin Assigment B,D,F), dp_hs_connect is 0. USB + * can support SS */ + manager->dp_hs_connect = 1; + + /* sub is only used here to pass the Product_ID */ + /* template->sub1 = pd_info->Product_ID; */ + /* USBPD_SEND_DATA_NOTI_DP(DP_CONNECT, + pd_info->Vendor_ID, &pd_info->Product_ID); */ + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_DP_CONNECT, + IFCONN_NOTIFY_EVENT_ATTACH, manager); + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_USB_DP, manager->dp_hs_connect, manager); + } + + return 0; +} + +/* Ok : 0, NAK: -1 */ +int usbpd_manager_get_modes(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct usbpd_manager_data *manager = &pd_data->manager; + data_obj_type *pd_obj = &policy->rx_data_obj[1]; + + manager->Standard_Vendor_ID = policy->rx_data_obj[0].structured_vdm.svid; + + pr_info("%s, Standard_Vendor_ID = 0x%x\n", __func__, + manager->Standard_Vendor_ID); + + if (manager->Standard_Vendor_ID == TypeC_DP_SUPPORT && + manager->SVID_0 == TypeC_DP_SUPPORT) { + if (policy->rx_msg_header.num_data_objs > 1) { + if (((pd_obj->displayport_capabilities.port_capability == num_UFP_D_Capable) + && (pd_obj->displayport_capabilities.receptacle_indication == num_USB_TYPE_C_Receptacle)) + || ((pd_obj->displayport_capabilities.port_capability == num_DFP_D_Capable) + && (pd_obj->displayport_capabilities.receptacle_indication == num_USB_TYPE_C_PLUG))) { + + manager->pin_assignment = pd_obj->displayport_capabilities.ufp_d_pin_assignments; + pr_info("%s, support UFP_D %d\n", __func__, manager->pin_assignment); + } else if (((pd_obj->displayport_capabilities.port_capability == num_DFP_D_Capable) + && (pd_obj->displayport_capabilities.receptacle_indication == num_USB_TYPE_C_Receptacle)) + || ((pd_obj->displayport_capabilities.port_capability == num_UFP_D_Capable) + && (pd_obj->displayport_capabilities.receptacle_indication == num_USB_TYPE_C_PLUG))) { + + manager->pin_assignment = pd_obj->displayport_capabilities.dfp_d_pin_assignments; + pr_info("%s, support DFP_D %d\n", __func__, manager->pin_assignment); + } else if (pd_obj->displayport_capabilities.port_capability == num_DFP_D_and_UFP_D_Capable) { + if (pd_obj->displayport_capabilities.receptacle_indication == num_USB_TYPE_C_PLUG) { + + manager->pin_assignment = pd_obj->displayport_capabilities.dfp_d_pin_assignments; + pr_info("%s, support DFP_D %d\n", __func__, manager->pin_assignment); + } else { + manager->pin_assignment = pd_obj->displayport_capabilities.ufp_d_pin_assignments; + pr_info("%s, support UFP_D %d\n", __func__, manager->pin_assignment); + } + } else { + manager->pin_assignment = DP_PIN_ASSIGNMENT_NODE; + pr_info("%s, there is not valid object %d\n", __func__, manager->pin_assignment); + } + } + + return USBPD_DP_SUPPORT; + } + + return USBPD_NOT_DP; +} + +int usbpd_manager_enter_mode(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct usbpd_manager_data *manager = &pd_data->manager; + manager->Standard_Vendor_ID = policy->rx_data_obj[0].structured_vdm.svid; + manager->is_samsung_accessory_enter_mode = true; + return 0; +} + +int usbpd_manager_exit_mode(struct usbpd_data *pd_data, unsigned mode) +{ + return 0; +} + +int usbpd_manager_get_status(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + struct usbpd_manager_data *manager = &pd_data->manager; + bool multi_func_preference = 0; + int pin_assignment = 0; + data_obj_type *pd_obj = &policy->rx_data_obj[1]; + + if (manager->SVID_0 != TypeC_DP_SUPPORT) + return 0; + + if (pd_obj->displayport_status.port_connected == 0) { + pr_info("%s, port disconnected!\n", __func__); + } + + if (manager->is_sent_pin_configuration) { + pr_info("%s, already sent pin configuration\n", __func__); + } + + if (pd_obj->displayport_status.port_connected && + !manager->is_sent_pin_configuration) { + multi_func_preference = pd_obj->displayport_status.multi_function_preferred; + + if (multi_func_preference) { + if(manager->pin_assignment & DP_PIN_ASSIGNMENT_D) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_D; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_B) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_B; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_F) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_F; + } else { + pr_info("wrong pin assignment value\n"); + } + } else { + if(manager->pin_assignment & DP_PIN_ASSIGNMENT_C) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_C; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_E) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_E; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_A) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_A; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_D) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_D; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_B) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_B; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_F) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_F; + } else { + pr_info("wrong pin assignment value\n"); + } + } + manager->dp_selected_pin = pin_assignment; + + manager->is_sent_pin_configuration = 1; + + pr_info("%s multi_func_preference %d %s\n", __func__, + multi_func_preference, DP_Pin_Assignment_Print[pin_assignment]); + } + + if (pd_obj->displayport_status.hpd_state) + manager->hpd = IFCONN_NOTIFY_HIGH; + else + manager->hpd = IFCONN_NOTIFY_LOW; + + if (pd_obj->displayport_status.irq_hpd) + manager->hpdirq = IFCONN_NOTIFY_IRQ; + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_DP_HPD, manager->hpdirq, manager); + + return 0; +} + +int usbpd_manager_get_configure(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + + if (manager->SVID_0 == TypeC_DP_SUPPORT) + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_DP_LINK_CONF, + IFCONN_NOTIFY_EVENT_ATTACH, manager); + + return 0; +} + +int usbpd_manager_get_attention(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + struct usbpd_manager_data *manager = &pd_data->manager; + bool multi_func_preference = 0; + int pin_assignment = 0; + data_obj_type *pd_obj = &policy->rx_data_obj[1]; + + if (manager->SVID_0 != TypeC_DP_SUPPORT) + return 0; + + if (pd_obj->displayport_status.port_connected == 0) { + pr_info("%s, port disconnected!\n", __func__); + } + + if (manager->is_sent_pin_configuration) { + pr_info("%s, already sent pin configuration\n", __func__); + } + + if (pd_obj->displayport_status.port_connected && + !manager->is_sent_pin_configuration) { + multi_func_preference = pd_obj->displayport_status.multi_function_preferred; + + if (multi_func_preference) { + if(manager->pin_assignment & DP_PIN_ASSIGNMENT_D) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_D; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_B) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_B; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_F) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_F; + } else { + pr_info("wrong pin assignment value\n"); + } + } else { + if(manager->pin_assignment & DP_PIN_ASSIGNMENT_C) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_C; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_E) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_E; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_A) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_A; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_D) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_D; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_B) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_B; + } else if(manager->pin_assignment & DP_PIN_ASSIGNMENT_F) { + pin_assignment = IFCONN_NOTIFY_DP_PIN_F; + } else { + pr_info("wrong pin assignment value\n"); + } + } + manager->dp_selected_pin = pin_assignment; + + manager->is_sent_pin_configuration = 1; + + pr_info("%s multi_func_preference %d %s\n", __func__, + multi_func_preference, DP_Pin_Assignment_Print[pin_assignment]); + } + + if (pd_obj->displayport_status.hpd_state) + manager->hpd = IFCONN_NOTIFY_HIGH; + else + manager->hpd = IFCONN_NOTIFY_LOW; + + if (pd_obj->displayport_status.irq_hpd) + manager->hpdirq = IFCONN_NOTIFY_IRQ; + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_DP_HPD, manager->hpdirq, manager); + + return 0; +} + +void usbpd_dp_detach(struct usbpd_data *pd_data) +{ + struct s2mu106_usbpd_data *pdic_data = pd_data->phy_driver_data; + struct usbpd_manager_data *manager = &pd_data->manager; + + dev_info(pd_data->dev, "%s: dp_is_connect %d\n", __func__, manager->dp_is_connect); + + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_USB_DP, manager->dp_hs_connect, NULL); + ifconn_event_work(pdic_data, IFCONN_NOTIFY_MANAGER, + IFCONN_NOTIFY_ID_DP_CONNECT, IFCONN_NOTIFY_EVENT_DETACH, NULL); + + manager->dp_is_connect = 0; + manager->dp_hs_connect = 0; + manager->is_sent_pin_configuration = 0; + + return; +} + +data_obj_type usbpd_manager_select_capability(struct usbpd_data *pd_data) +{ + /* TODO: Request from present capabilities + indicate if other capabilities would be required */ + data_obj_type obj; +#ifdef CONFIG_IFCONN_NOTIFIER + int pdo_num = pd_noti.sink_status.selected_pdo_num; +#endif + obj.request_data_object.no_usb_suspend = 1; + obj.request_data_object.usb_comm_capable = 1; + obj.request_data_object.capability_mismatch = 0; + obj.request_data_object.give_back = 0; +#ifdef CONFIG_IFCONN_NOTIFIER + obj.request_data_object.min_current = pd_noti.sink_status.power_list[pdo_num].max_current / USBPD_CURRENT_UNIT; + obj.request_data_object.op_current = pd_noti.sink_status.power_list[pdo_num].max_current / USBPD_CURRENT_UNIT; + obj.request_data_object.object_position = pd_noti.sink_status.selected_pdo_num; +#endif + + return obj; +} + +/* + usbpd_manager_evaluate_capability + : Policy engine ask Device Policy Manager to evaluate option + based on supplied capabilities + return >0 : request object number + 0 : no selected option +*/ +int usbpd_manager_evaluate_capability(struct usbpd_data *pd_data) +{ + struct policy_data *policy = &pd_data->policy; + int i = 0; + int power_type = 0; + int pd_volt = 0, pd_current; +#ifdef CONFIG_IFCONN_NOTIFIER + int available_pdo_num = 0; + ifconn_pd_sink_status_t *pdic_sink_status = &pd_noti.sink_status; +#endif + data_obj_type *pd_obj; + + for (i = 0; i < policy->rx_msg_header.num_data_objs; i++) { + pd_obj = &policy->rx_data_obj[i]; + power_type = pd_obj->power_data_obj_supply_type.supply_type; + switch (power_type) { + case POWER_TYPE_FIXED: + pd_volt = pd_obj->power_data_obj.voltage; + pd_current = pd_obj->power_data_obj.max_current; + dev_info(pd_data->dev, "[%d] FIXED volt(%d)mV, max current(%d)\n", + i+1, pd_volt * USBPD_VOLT_UNIT, pd_current * USBPD_CURRENT_UNIT); +#ifdef CONFIG_IFCONN_NOTIFIER + if (pd_volt * USBPD_VOLT_UNIT <= MAX_CHARGING_VOLT) + available_pdo_num = i + 1; + pdic_sink_status->power_list[i + 1].max_voltage = pd_volt * USBPD_VOLT_UNIT; + pdic_sink_status->power_list[i + 1].max_current = pd_current * USBPD_CURRENT_UNIT; +#endif + break; + case POWER_TYPE_BATTERY: + pd_volt = pd_obj->power_data_obj_battery.max_voltage; + dev_info(pd_data->dev, "[%d] BATTERY volt(%d)mV\n", + i+1, pd_volt * USBPD_VOLT_UNIT); + break; + case POWER_TYPE_VARIABLE: + pd_volt = pd_obj->power_data_obj_variable.max_voltage; + dev_info(pd_data->dev, "[%d] VARIABLE volt(%d)mV\n", + i+1, pd_volt * USBPD_VOLT_UNIT); + break; + default: + dev_err(pd_data->dev, "[%d] Power Type Error\n", i+1); + break; + } + } +#ifdef CONFIG_IFCONN_NOTIFIER + pdic_sink_status->available_pdo_num = available_pdo_num; + return available_pdo_num; +#else + return 1; /* select default first obj */ +#endif +} + +/* return: 0: cab be met, -1: cannot be met, -2: could be met later */ +int usbpd_manager_match_request(struct usbpd_data *pd_data) +{ + /* TODO: Evaluation of sink request */ + + unsigned supply_type + = pd_data->source_request_obj.power_data_obj_supply_type.supply_type; + unsigned mismatch, max_min, op, pos; + + if (supply_type == POWER_TYPE_FIXED) { + pr_info("REQUEST: FIXED\n"); + goto log_fixed_variable; + } else if (supply_type == POWER_TYPE_VARIABLE) { + pr_info("REQUEST: VARIABLE\n"); + goto log_fixed_variable; + } else if (supply_type == POWER_TYPE_BATTERY) { + pr_info("REQUEST: BATTERY\n"); + goto log_battery; + } else { + pr_info("REQUEST: UNKNOWN Supply type.\n"); + return -1; + } + +log_fixed_variable: + mismatch = pd_data->source_request_obj.request_data_object.capability_mismatch; + max_min = pd_data->source_request_obj.request_data_object.min_current; + op = pd_data->source_request_obj.request_data_object.op_current; + pos = pd_data->source_request_obj.request_data_object.object_position; + pr_info("Obj position: %d\n", pos); + pr_info("Mismatch: %d\n", mismatch); + pr_info("Operating Current: %d mA\n", op*10); + if (pd_data->source_request_obj.request_data_object.give_back) + pr_info("Min current: %d mA\n", max_min*10); + else + pr_info("Max current: %d mA\n", max_min*10); + + return 0; + +log_battery: + mismatch = pd_data->source_request_obj.request_data_object_battery.capability_mismatch; + return 0; +} + +#ifdef CONFIG_OF +static int of_usbpd_manager_dt(struct usbpd_manager_data *_data) +{ + int ret = 0; + struct device_node *np = + of_find_node_by_name(NULL, "pdic-manager"); + + if (np == NULL) { + pr_err("%s np NULL\n", __func__); + return -EINVAL; + } else { + ret = of_property_read_u32(np, "pdic,max_power", + &_data->max_power); + if (ret < 0) + pr_err("%s error reading max_power %d\n", + __func__, _data->max_power); + + ret = of_property_read_u32(np, "pdic,op_power", + &_data->op_power); + if (ret < 0) + pr_err("%s error reading op_power %d\n", + __func__, _data->max_power); + + ret = of_property_read_u32(np, "pdic,max_current", + &_data->max_current); + if (ret < 0) + pr_err("%s error reading max_current %d\n", + __func__, _data->max_current); + + ret = of_property_read_u32(np, "pdic,min_current", + &_data->min_current); + if (ret < 0) + pr_err("%s error reading min_current %d\n", + __func__, _data->min_current); + + _data->giveback = of_property_read_bool(np, + "pdic,giveback"); + _data->usb_com_capable = of_property_read_bool(np, + "pdic,usb_com_capable"); + _data->no_usb_suspend = of_property_read_bool(np, + "pdic,no_usb_suspend"); + + /* source capability */ + ret = of_property_read_u32(np, "source,max_voltage", + &_data->source_max_volt); + ret = of_property_read_u32(np, "source,min_voltage", + &_data->source_min_volt); + ret = of_property_read_u32(np, "source,max_power", + &_data->source_max_power); + + /* sink capability */ + ret = of_property_read_u32(np, "sink,capable_max_voltage", + &_data->sink_cap_max_volt); + if (ret < 0) { + _data->sink_cap_max_volt = 5000; + pr_err("%s error reading sink_cap_max_volt %d\n", + __func__, _data->sink_cap_max_volt); + } + } + + return ret; +} +#endif + +void usbpd_init_manager_val(struct usbpd_data *pd_data) +{ + struct usbpd_manager_data *manager = &pd_data->manager; + + pr_info("%s\n", __func__); + manager->alt_sended = 0; + manager->cmd = 0; + manager->vdm_en = 0; + manager->Vendor_ID = 0; + manager->Product_ID = 0; + manager->Device_Version = 0; + manager->SVID_0 = 0; + manager->SVID_1 = 0; + manager->Standard_Vendor_ID = 0; + manager->dp_is_connect = 0; + manager->dp_hs_connect = 0; + manager->is_sent_pin_configuration = 0; + manager->pin_assignment = 0; + manager->dp_selected_pin = 0; + manager->hpd = 0; + manager->hpdirq = 0; + init_completion(&manager->uvdm_out_wait); + init_completion(&manager->uvdm_in_wait); + usbpd_manager_select_pdo_cancel(pd_data->dev); + usbpd_manager_start_discover_msg_cancel(pd_data->dev); +} + +int usbpd_init_manager(struct usbpd_data *pd_data) +{ + int ret = 0; + struct usbpd_manager_data *manager = &pd_data->manager; + + pr_info("%s\n", __func__); + if (manager == NULL) { + pr_err("%s, usbpd manager data is error!!\n", __func__); + return -ENOMEM; + } else + ret = of_usbpd_manager_dt(manager); +#ifdef CONFIG_BATTERY_SAMSUNG +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + fp_select_pdo = s2mu106_select_pdo; +#endif +#endif + mutex_init(&manager->vdm_mutex); + manager->pd_data = pd_data; + manager->power_role_swap = true; + manager->data_role_swap = true; + manager->vconn_source_swap = true; + manager->alt_sended = 0; + manager->vdm_en = 0; + manager->acc_type = 0; + manager->Vendor_ID = 0; + manager->Product_ID = 0; + manager->Device_Version = 0; + manager->SVID_0 = 0; + manager->SVID_1 = 0; + manager->Standard_Vendor_ID = 0; + manager->dp_is_connect = 0; + manager->dp_hs_connect = 0; + manager->is_sent_pin_configuration = 0; + manager->pin_assignment = 0; + manager->dp_selected_pin = 0; + manager->hpd = 0; + manager->hpdirq = 0; + init_completion(&manager->uvdm_out_wait); + init_completion(&manager->uvdm_in_wait); + + usbpd_manager_register_switch_device(1); + init_source_cap_data(manager); + init_sink_cap_data(manager); + INIT_DELAYED_WORK(&manager->acc_detach_handler, usbpd_manager_acc_detach_handler); + INIT_DELAYED_WORK(&manager->select_pdo_handler, usbpd_manager_select_pdo_handler); + INIT_DELAYED_WORK(&manager->start_discover_msg_handler, + usbpd_manager_start_discover_msg_handler); + + pr_info("%s done\n", __func__); + return ret; +} diff --git a/drivers/ccic/usbpd_policy.c b/drivers/ccic/usbpd_policy.c new file mode 100644 index 000000000000..88dccc42d706 --- /dev/null +++ b/drivers/ccic/usbpd_policy.c @@ -0,0 +1,2783 @@ +/* +* USB PD Driver - Policy Engine +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if defined(CONFIG_MUIC_NOTIFIER) +#include +#endif /* CONFIG_MUIC_NOTIFIER */ + +#include + +#if (defined CONFIG_IFCONN_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF) +#include +#endif + +#if defined(CONFIG_IFCONN_NOTIFIER) +#include +#endif + +#define CHECK_MSG(pd, msg, ret) do {\ + if (pd->phy_ops.get_status(pd, msg))\ + return ret;\ + } while (0); + +#define CHECK_CMD(pd, event, ret) do {\ + if (pd->manager.cmd & event) {\ + pd->manager.cmd &= ~event; \ + return ret;\ + } \ + } while (0); + +policy_state usbpd_policy_src_startup(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->counter.caps_counter = 0; + usbpd_init_protocol(pd_data); + + pd_data->phy_ops.set_cc_control(pd_data, USBPD_CC_ON); + + return PE_SRC_Send_Capabilities; +} + +policy_state usbpd_policy_src_discovery(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + msleep(tSendSourceCap); + if (pd_data->counter.caps_counter <= USBPD_nCapsCount) + return PE_SRC_Send_Capabilities; + else + return PE_SRC_Disabled; +} + +policy_state usbpd_policy_src_send_capabilities(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + policy->tx_msg_header.word = pd_data->source_msg_header.word; + policy->tx_data_obj[0].object = pd_data->source_data_obj.object; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->counter.caps_counter++; + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_SRC_Send_Capabilities; + if (usbpd_wait_msg(pd_data, MSG_REQUEST, tSenderResponseSRC)) { + if (policy->rx_msg_header.msg_type == USBPD_Request && + policy->rx_msg_header.num_data_objs > 0) { + pd_data->counter.hard_reset_counter = 0; + pd_data->counter.caps_counter = 0; + pd_data->source_request_obj.object + = policy->rx_data_obj[0].object; + dev_info(pd_data->dev, "got Request.\n"); + return PE_SRC_Negotiate_Capability; + + } else { + dev_err(pd_data->dev, + "Not get request object\n"); + goto hard_reset; + } + } else if (pd_data->phy_ops.get_status(pd_data, MSG_GOODCRC)) { + if (policy->abnormal_state) + return PE_SRC_Send_Capabilities; + pd_data->counter.caps_counter = 0; + dev_err(pd_data->dev, + "%s NoResponseTimer\n", __func__); + goto hard_reset; + } else { /* not receive good crc */ + if (policy->abnormal_state) + return PE_SRC_Send_Capabilities; + return PE_SRC_Discovery; + } + } else + return PE_SRC_Discovery; + +hard_reset: + if (pd_data->counter.hard_reset_counter > USBPD_nHardResetCount) + return Error_Recovery; + + return PE_SRC_Hard_Reset; +} +#if 0 +policy_state usbpd_policy_src_send_capabilities(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + policy->tx_msg_header.word = pd_data->source_msg_header.word; + policy->tx_data_obj[0].object = pd_data->source_data_obj.object; + + dev_info(pd_data->dev, "%s 0x%x 0x%x\n", __func__ + , policy->tx_msg_header.word + , policy->tx_data_obj[0].object); + + pd_data->counter.caps_counter++; + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->counter.hard_reset_counter = 0; + pd_data->counter.caps_counter = 0; + if (usbpd_wait_msg(pd_data, MSG_REQUEST, tSenderResponse)) { + if (policy->rx_msg_header.msg_type == USBPD_Request && + policy->rx_msg_header.num_data_objs > 0) { + pd_data->source_request_obj.object + = policy->rx_data_obj[0].object; + dev_info(pd_data->dev, "got Request.\n"); + return PE_SRC_Negotiate_Capability; + } else { + dev_err(pd_data->dev, + "Not get request object\n"); + return Error_Recovery; + } + } else { + dev_err(pd_data->dev, + "%s NoResponseTimer\n", __func__); + return PE_SRC_Hard_Reset; + } + } else + return PE_SRC_Discovery; + + if (pd_data->counter.hard_reset_counter > USBPD_nHardResetCount) + return Error_Recovery; + + return PE_SRC_Send_Capabilities; +} +#endif +policy_state usbpd_policy_src_negotiate_capability(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + if (usbpd_manager_match_request(pd_data) == 0) + return PE_SRC_Transition_Supply; /* Accept */ + else + return PE_SRC_Capability_Response; /* Reject */ +} + +policy_state usbpd_policy_src_transition_supply(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + /* TODO: If GotoMin send GotoMin message */ + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, USBPD_Accept, + USBPD_DFP, USBPD_SOURCE)) { + msleep(tSrcTransition); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PS_RDY, USBPD_DFP, USBPD_SOURCE)) + return PE_SRC_Ready; + else + return PE_SRC_Send_Soft_Reset; + } else { + return PE_SRC_Send_Soft_Reset; + } + return PE_SRC_Transition_Supply; +} + +policy_state usbpd_policy_src_ready(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int data_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + CHECK_MSG(pd_data, MSG_GET_SRC_CAP, PE_SRC_Give_Source_Cap); + CHECK_MSG(pd_data, MSG_REQUEST, PE_SRC_Negotiate_Capability); + CHECK_MSG(pd_data, MSG_PR_SWAP, PE_PRS_SRC_SNK_Evaluate_Swap); + CHECK_MSG(pd_data, MSG_DR_SWAP, PE_DRS_Evaluate_Port); + CHECK_MSG(pd_data, MSG_VCONN_SWAP, PE_VCS_Evaluate_Swap); + CHECK_MSG(pd_data, VDM_DISCOVER_IDENTITY, PE_UFP_VDM_Get_Identity); + CHECK_MSG(pd_data, VDM_DISCOVER_SVID, PE_UFP_VDM_Get_SVIDs); + CHECK_MSG(pd_data, VDM_DISCOVER_MODE, PE_UFP_VDM_Get_Modes); + CHECK_MSG(pd_data, VDM_ENTER_MODE, PE_UFP_VDM_Evaluate_Mode_Entry); + CHECK_MSG(pd_data, VDM_ATTENTION, PE_DFP_VDM_Attention_Request); + CHECK_MSG(pd_data, VDM_DP_STATUS_UPDATE, PE_UFP_VDM_Evaluate_Status); + CHECK_MSG(pd_data, VDM_DP_CONFIGURE, PE_UFP_VDM_Evaluate_Configure); + CHECK_MSG(pd_data, UVDM_MSG, PE_DFP_UVDM_Receive_Message); + + CHECK_CMD(pd_data, MANAGER_REQ_GET_SNKCAP, PE_SRC_Get_Sink_Cap); + CHECK_CMD(pd_data, MANAGER_REQ_GOTOMIN, PE_SRC_Transition_Supply); + CHECK_CMD(pd_data, MANAGER_REQ_SRCCAP_CHANGE, PE_SRC_Send_Capabilities); + CHECK_CMD(pd_data, MANAGER_REQ_PR_SWAP, PE_PRS_SRC_SNK_Send_Swap); + CHECK_CMD(pd_data, MANAGER_REQ_DR_SWAP, PE_DRS_Evaluate_Send_Port); + CHECK_CMD(pd_data, MANAGER_REQ_VCONN_SWAP, PE_VCS_Send_Swap); + CHECK_CMD(pd_data, MANAGER_REQ_UVDM_SEND_MESSAGE, + PE_DFP_UVDM_Send_Message); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_IDENTITY, PE_DFP_VDM_Identity_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_SVID, PE_DFP_VDM_SVIDs_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_MODE, PE_DFP_VDM_Modes_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_ENTER_MODE, PE_DFP_VDM_Mode_Entry_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_STATUS_UPDATE, PE_DFP_VDM_Status_Update); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DisplayPort_Configure, PE_DFP_VDM_DisplayPort_Configure); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_ATTENTION, PE_UFP_VDM_Attention_Request); + +/* for data role swap test + if (usbpd_manager_vdm_request_enabled(pd_data)) { + msleep(tDiscoverIdentity); + return PE_DRS_Evaluate_Send_Port; + } +*/ + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + if (data_role == USBPD_DFP) + usbpd_manager_vdm_request_enabled(pd_data); + + return PE_SRC_Ready; +} + +policy_state usbpd_policy_src_disabled(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + return PE_SRC_Disabled; +} + +policy_state usbpd_policy_src_capability_response(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, USBPD_Reject, + USBPD_DFP, USBPD_SOURCE)) { + return PE_SRC_Ready; + /* TODO: if (Contract Invalid) + return(PE_SRC_Hard_Reset) */ + } + /* + else if (no Explicit Contract && Reject message sent + || Wait message sent) + return(PE_SRC_Wait_New_Capabilities); + */ + return PE_SRC_Capability_Response; +} + +policy_state usbpd_policy_src_hard_reset(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + msleep(tPSHardReset); + + pd_data->phy_ops.hard_reset(pd_data); + pd_data->phy_ops.set_cc_control(pd_data, USBPD_CC_OFF); + pd_data->counter.hard_reset_counter++; + + return PE_SRC_Transition_to_default; +} + +policy_state usbpd_policy_src_hard_reset_received(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + msleep(tPSHardReset); + + return PE_SRC_Transition_to_default; +} + +policy_state usbpd_policy_src_transition_to_default(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.driver_reset(pd_data); + /* + Request Device Policy Manager to request power + supply Hard Resets to vSafe5V via vSafe0V + + If(Type-C request Device Policy Manager to set Port Data Role to DFP) + turn off VCONN + */ + + /* + Request Device Policy Manager to turn on VCONN + Initialize and start NoResponseTimer + Inform Protocol Layer Hard Reset complete + */ + return PE_SRC_Startup; +} + +policy_state usbpd_policy_src_give_source_cap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + /* + TODO: Request source capabilities from Device Policy Manager + Send Capabilities message + */ + + policy->tx_msg_header.msg_type = USBPD_Source_Capabilities; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = USBPD_SOURCE; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].power_data_obj.max_current = 100; + policy->tx_data_obj[0].power_data_obj.voltage = 100; + policy->tx_data_obj[0].power_data_obj.peak_current = 0; + policy->tx_data_obj[0].power_data_obj.data_role_swap = 1; + policy->tx_data_obj[0].power_data_obj.usb_comm_capable = 1; + policy->tx_data_obj[0].power_data_obj.externally_powered = 0; + policy->tx_data_obj[0].power_data_obj.usb_suspend_support = 1; + policy->tx_data_obj[0].power_data_obj.dual_role_power = 1; + policy->tx_data_obj[0].power_data_obj.supply = 0; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) + return PE_SRC_Ready; + else + return PE_SRC_Give_Source_Cap; +} + +policy_state usbpd_policy_src_get_sink_cap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Get_Sink_Cap, USBPD_DFP, USBPD_SOURCE)) { + pd_data->policy.state = PE_SRC_Get_Sink_Cap; + if (usbpd_wait_msg(pd_data, MSG_SNK_CAP, tSenderResponse)) { + /* TODO: pass sink cap to device policy manager */ + dev_info(pd_data->dev, "got SinkCap.\n"); + } + } + return PE_SRC_Ready; +} + +policy_state usbpd_policy_src_wait_new_capabilities(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return PE_SRC_Send_Capabilities; +} + +policy_state usbpd_policy_src_send_soft_reset(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + usbpd_init_protocol(pd_data); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Soft_Reset, USBPD_DFP, USBPD_SOURCE)) { + pd_data->policy.state = PE_SRC_Send_Soft_Reset; + if (usbpd_wait_msg(pd_data, MSG_ACCEPT, tSenderResponse)) + return PE_SRC_Send_Capabilities; + if (policy->abnormal_state) + return PE_SRC_Send_Soft_Reset; + } + return PE_SRC_Hard_Reset; +} + +policy_state usbpd_policy_src_soft_reset(struct policy_data *policy) +{ +#if 0 + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_DFP, USBPD_SOURCE)) + return PE_SRC_Send_Capabilities; + else + return PE_SRC_Hard_Reset; +#endif + return PE_SRC_Send_Capabilities; +} + +policy_state usbpd_policy_snk_startup(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + usbpd_init_protocol(pd_data); + + pd_data->phy_ops.set_cc_control(pd_data, USBPD_CC_ON); + + return PE_SNK_Discovery; +} + +policy_state usbpd_policy_snk_discovery(struct policy_data *policy) +{ + /* TODO: wait vbus */ + /* if coming from HardReset + && NoResponseTimer timeout + && HardResetCounter <= nHardResetCount, + return(PE_SNK_Hard_Reset) */ + return PE_SNK_Wait_for_Capabilities; +} + +policy_state usbpd_policy_snk_wait_for_capabilities(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->policy.state = PE_SNK_Wait_for_Capabilities; + if (usbpd_wait_msg(pd_data, MSG_SRC_CAP, tSinkWaitCap)) + return PE_SNK_Evaluate_Capability; + + if (policy->abnormal_state) + return PE_SNK_Wait_for_Capabilities; +#if !defined(CONFIG_SEC_FACTORY) + if (pd_data->counter.hard_reset_counter <= USBPD_nHardResetCount) + return PE_SNK_Hard_Reset; + else + return Error_Recovery; +#endif + return PE_SNK_Wait_for_Capabilities; +} + +policy_state usbpd_policy_snk_evaluate_capability(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int sink_request_obj_num = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + usbpd_protocol_rx(pd_data); +#ifdef CONFIG_IFCONN_NOTIFIER + if (pd_noti.sink_status.selected_pdo_num == 0) { + pd_noti.sink_status.selected_pdo_num = 1; + if (policy->sink_cap_received) { + policy->send_sink_cap = 1; + policy->sink_cap_received = 0; + } + } +#endif + sink_request_obj_num = usbpd_manager_evaluate_capability(pd_data); + + if (sink_request_obj_num > 0) + return PE_SNK_Select_Capability; + else + return PE_SNK_Hard_Reset; +} + +policy_state usbpd_policy_snk_select_capability(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + policy->tx_msg_header.msg_type = USBPD_Request; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = USBPD_SINK; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0] = usbpd_manager_select_capability(pd_data); + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + unsigned msg; + pd_data->policy.state = PE_SNK_Select_Capability; + msg = usbpd_wait_msg(pd_data, MSG_ACCEPT | MSG_REJECT + | MSG_WAIT, tSenderResponse); + if (policy->abnormal_state) + return PE_SNK_Select_Capability; + if (msg & MSG_ACCEPT) + return PE_SNK_Transition_Sink; + else if (msg & (MSG_REJECT | MSG_WAIT)) + return PE_SNK_Ready; + else + return PE_SNK_Hard_Reset; + + /* If no explicit contract + policy->state = PE_SNK_Wait_for_Capabilities; + */ + } + return Error_Recovery; +} + +policy_state usbpd_policy_snk_transition_sink(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + policy->state = PE_SNK_Transition_Sink; + if (usbpd_wait_msg(pd_data, MSG_PSRDY, tPSTransition)) { + dev_info(pd_data->dev, "got PS_READY.\n"); +#ifdef CONFIG_IFCONN_NOTIFIER + pd_noti.sink_status.current_pdo_num = pd_noti.sink_status.selected_pdo_num; +#endif + return PE_SNK_Ready; + } + + if (policy->abnormal_state) + return PE_SNK_Transition_Sink; + + return PE_SNK_Hard_Reset; +} + +policy_state usbpd_policy_snk_ready(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int data_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + usbpd_manager_plug_attach(pd_data->dev); + + CHECK_MSG(pd_data, MSG_GET_SNK_CAP, PE_SNK_Give_Sink_Cap); + CHECK_MSG(pd_data, MSG_SRC_CAP, PE_SNK_Evaluate_Capability); + CHECK_MSG(pd_data, MSG_PR_SWAP, PE_PRS_SNK_SRC_Evaluate_Swap); + CHECK_MSG(pd_data, MSG_DR_SWAP, PE_DRS_Evaluate_Port); + CHECK_MSG(pd_data, MSG_VCONN_SWAP, PE_VCS_Evaluate_Swap); + CHECK_MSG(pd_data, VDM_DISCOVER_IDENTITY, PE_UFP_VDM_Get_Identity); + CHECK_MSG(pd_data, VDM_DISCOVER_SVID, PE_UFP_VDM_Get_SVIDs); + CHECK_MSG(pd_data, VDM_DISCOVER_MODE, PE_UFP_VDM_Get_Modes); + CHECK_MSG(pd_data, VDM_ENTER_MODE, PE_UFP_VDM_Evaluate_Mode_Entry); + CHECK_MSG(pd_data, VDM_ATTENTION, PE_DFP_VDM_Attention_Request); + CHECK_MSG(pd_data, VDM_DP_STATUS_UPDATE, PE_UFP_VDM_Evaluate_Status); + CHECK_MSG(pd_data, VDM_DP_CONFIGURE, PE_UFP_VDM_Evaluate_Configure); + CHECK_MSG(pd_data, UVDM_MSG, PE_DFP_UVDM_Receive_Message); + + CHECK_CMD(pd_data, MANAGER_REQ_NEW_POWER_SRC, PE_SNK_Select_Capability); + CHECK_CMD(pd_data, MANAGER_REQ_PR_SWAP, PE_PRS_SNK_SRC_Send_Swap); + CHECK_CMD(pd_data, MANAGER_REQ_DR_SWAP, PE_DRS_Evaluate_Send_Port); + CHECK_CMD(pd_data, MANAGER_REQ_VCONN_SWAP, PE_VCS_Send_Swap); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_IDENTITY, PE_DFP_VDM_Identity_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_SVID, PE_DFP_VDM_SVIDs_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DISCOVER_MODE, PE_DFP_VDM_Modes_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_ATTENTION, PE_UFP_VDM_Attention_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_ENTER_MODE, PE_DFP_VDM_Mode_Entry_Request); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_STATUS_UPDATE, PE_DFP_VDM_Status_Update); + CHECK_CMD(pd_data, MANAGER_REQ_VDM_DisplayPort_Configure, PE_DFP_VDM_DisplayPort_Configure); + CHECK_CMD(pd_data, MANAGER_REQ_UVDM_SEND_MESSAGE,PE_DFP_UVDM_Send_Message); + + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + if (data_role == USBPD_DFP) + usbpd_manager_vdm_request_enabled(pd_data); + + return PE_SNK_Ready; +} + +policy_state usbpd_policy_snk_hard_reset(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.hard_reset(pd_data); + pd_data->phy_ops.set_cc_control(pd_data, USBPD_CC_OFF); + /* increase hard reset counter */ + pd_data->counter.hard_reset_counter++; + + return PE_SNK_Transition_to_default; +} + +policy_state usbpd_policy_snk_transition_to_default(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.driver_reset(pd_data); + + usbpd_manager_turn_off_vconn(pd_data); + +/* pd_data->phy_ops.set_data_role(pd_data, USBPD_UFP); */ + + return PE_SNK_Startup; +} + +policy_state usbpd_policy_snk_give_sink_cap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + +#ifdef CONFIG_IFCONN_NOTIFIER + pd_noti.sink_status.selected_pdo_num = 0; +#endif + policy->tx_msg_header.word = pd_data->sink_msg_header.word; + policy->tx_data_obj[0].object = pd_data->sink_data_obj[0].object; + policy->tx_data_obj[1].object = pd_data->sink_data_obj[1].object; + + policy->sink_cap_received = 1; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) + return PE_SNK_Ready; + else + return PE_SNK_Give_Sink_Cap; +} + +policy_state usbpd_policy_snk_get_source_cap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Get_Source_Cap, USBPD_UFP, USBPD_SINK)) + return PE_SNK_Ready; + else + return PE_SNK_Get_Source_Cap; +} + +policy_state usbpd_policy_snk_soft_reset(struct policy_data *policy) +{ +#if 0 + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_UFP, USBPD_SINK)) + return PE_SNK_Wait_for_Capabilities; + else + return PE_SNK_Hard_Reset; +#endif + return PE_SNK_Wait_for_Capabilities; +} + +policy_state usbpd_policy_drs_evaluate_port(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int data_role = 0; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + if (policy->modal_operation) { + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Hard_Reset; + else + return PE_SNK_Hard_Reset; + } + + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + if (data_role == USBPD_DFP) + return PE_DRS_DFP_UFP_Evaluate_DR_Swap; + else + return PE_DRS_UFP_DFP_Evaluate_DR_Swap; +} + +policy_state usbpd_policy_drs_evaluate_send_port(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int data_role = 0; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + if (policy->modal_operation) { + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Hard_Reset; + else + return PE_SNK_Hard_Reset; + } + + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + if (data_role == USBPD_DFP) + return PE_DRS_DFP_UFP_Send_DR_Swap; + else + return PE_DRS_UFP_DFP_Send_DR_Swap; +} + +policy_state usbpd_policy_drs_dfp_ufp_evaluate_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + bool drs_ok; + + dev_info(pd_data->dev, "%s\n", __func__); + + drs_ok = usbpd_manager_data_role_swap(pd_data); + + if (drs_ok) + return PE_DRS_DFP_UFP_Accept_DR_Swap; + else + return PE_DRS_DFP_UFP_Reject_DR_Swap; +} + +policy_state usbpd_policy_drs_dfp_ufp_accept_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_DFP, power_role)) { + return PE_DRS_DFP_UFP_Change_to_UFP; + } + + return PE_DRS_DFP_UFP_Accept_DR_Swap; +} + +policy_state usbpd_policy_drs_dfp_ufp_change_to_ufp(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_data_role(pd_data, USBPD_UFP); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_drs_dfp_ufp_send_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_DR_Swap, USBPD_DFP, power_role)) { + unsigned msg; + pd_data->policy.state = PE_DRS_DFP_UFP_Send_DR_Swap; + msg = usbpd_wait_msg(pd_data, MSG_ACCEPT | MSG_REJECT + | MSG_WAIT, tSenderResponse); + if (policy->abnormal_state) + return PE_DRS_DFP_UFP_Send_DR_Swap; + + if (msg & MSG_ACCEPT) + return PE_DRS_DFP_UFP_Change_to_UFP; + } + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_drs_dfp_ufp_reject_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Reject, USBPD_DFP, power_role)) { + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; + } + + return PE_DRS_DFP_UFP_Reject_DR_Swap; +} + +policy_state usbpd_policy_drs_ufp_dfp_evaluate_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + bool drs_ok; + + dev_info(pd_data->dev, "%s\n", __func__); + + drs_ok = usbpd_manager_data_role_swap(pd_data); + + if (drs_ok) + return PE_DRS_UFP_DFP_Accept_DR_Swap; + else + return PE_DRS_UFP_DFP_Reject_DR_Swap; +} + +policy_state usbpd_policy_drs_ufp_dfp_accept_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_UFP, power_role)) { + return PE_DRS_UFP_DFP_Change_to_DFP; + } + return PE_DRS_UFP_DFP_Accept_DR_Swap; +} + +policy_state usbpd_policy_drs_ufp_dfp_change_to_dfp(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_data_role(pd_data, USBPD_DFP); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_drs_ufp_dfp_send_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_DR_Swap, USBPD_UFP, power_role)) { + unsigned msg; + pd_data->policy.state = PE_DRS_UFP_DFP_Send_DR_Swap; + msg = usbpd_wait_msg(pd_data, MSG_ACCEPT | MSG_REJECT + | MSG_WAIT, tSenderResponse); + if (policy->abnormal_state) + return PE_DRS_UFP_DFP_Send_DR_Swap; + + if (msg & MSG_ACCEPT) + return PE_DRS_UFP_DFP_Change_to_DFP; + } + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_drs_ufp_dfp_reject_dr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Reject, USBPD_UFP, USBPD_SINK)) { + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; + } + + return PE_DRS_UFP_DFP_Reject_DR_Swap; +} + +policy_state usbpd_policy_prs_src_snk_reject_pr_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Reject, USBPD_DFP, USBPD_SOURCE)) + return PE_SRC_Ready; + + return PE_PRS_SRC_SNK_Reject_PR_Swap; +} + +policy_state usbpd_policy_prs_src_snk_evaluate_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + bool prs_ok; + + dev_info(pd_data->dev, "%s\n", __func__); + + prs_ok = usbpd_manager_power_role_swap(pd_data); + + if (prs_ok) + return PE_PRS_SRC_SNK_Accept_Swap; + else + return PE_PRS_SRC_SNK_Reject_PR_Swap; +} + +policy_state usbpd_policy_prs_src_snk_send_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PR_Swap, USBPD_DFP, USBPD_SOURCE)) { + unsigned msg; + + pd_data->policy.state = PE_PRS_SRC_SNK_Send_Swap; + msg = usbpd_wait_msg(pd_data, MSG_ACCEPT | MSG_REJECT + | MSG_WAIT, tSenderResponse); + if (policy->abnormal_state) + return PE_PRS_SRC_SNK_Send_Swap; + + if (msg & MSG_ACCEPT) + return PE_PRS_SRC_SNK_Transition_off; + } + return PE_SRC_Ready; +} + +policy_state usbpd_policy_prs_src_snk_accept_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_DFP, USBPD_SOURCE)) + return PE_PRS_SRC_SNK_Transition_off; + + return PE_PRS_SRC_SNK_Accept_Swap; +} + +policy_state usbpd_policy_prs_src_snk_transition_to_off(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + + pd_data->phy_ops.set_otg_control(pd_data, 0); + + pr_info("%s, %d\n", __func__, manager->acc_type); + + /* skip delay when GEARVR is attached */ + if (manager->acc_type != CCIC_DOCK_HMT || manager->SVID_0 == 0) + msleep(150); + + return PE_PRS_SRC_SNK_Assert_Rd; +} + +policy_state usbpd_policy_prs_src_snk_assert_rd(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + pd_data->phy_ops.set_power_role(pd_data, USBPD_SINK); + + return PE_PRS_SRC_SNK_Wait_Source_on; +} + +policy_state usbpd_policy_prs_src_snk_wait_source_on(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + int wait_time = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PS_RDY, USBPD_DFP, USBPD_SINK)) { + pd_data->policy.state = PE_PRS_SRC_SNK_Wait_Source_on; + if (manager->acc_type == CCIC_DOCK_HMT) + wait_time = 2000; + else + wait_time = tPSSourceOn; + if (usbpd_wait_msg(pd_data, MSG_PSRDY, tPSSourceOn)) { + pd_data->counter.swap_hard_reset_counter = 0; + dev_info(pd_data->dev, "got PSRDY.\n"); + mdelay(10); + pd_data->phy_ops.set_power_role(pd_data, USBPD_DRP); + return PE_SNK_Startup; + } else { + if (policy->abnormal_state) + return PE_PRS_SRC_SNK_Wait_Source_on; + goto hard_reset; + } + } + return PE_PRS_SRC_SNK_Wait_Source_on; + +hard_reset: + if (pd_data->counter.swap_hard_reset_counter > USBPD_nHardResetCount) + return Error_Recovery; + + return PE_SNK_Hard_Reset; +} + +policy_state usbpd_policy_prs_snk_src_reject_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Reject, USBPD_UFP, USBPD_SINK)) + return PE_SNK_Ready; + + return PE_PRS_SNK_SRC_Reject_Swap; +} + +policy_state usbpd_policy_prs_snk_src_evaluate_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + bool prs_ok; + dev_info(pd_data->dev, "%s\n", __func__); + + prs_ok = usbpd_manager_power_role_swap(pd_data); + + if (prs_ok) + return PE_PRS_SNK_SRC_Accept_Swap; + else + return PE_PRS_SNK_SRC_Reject_Swap; +} + +policy_state usbpd_policy_prs_snk_src_send_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PR_Swap, USBPD_UFP, USBPD_SINK)) { + unsigned msg; + + pd_data->policy.state = PE_PRS_SNK_SRC_Send_Swap; + msg = usbpd_wait_msg(pd_data, MSG_ACCEPT | MSG_REJECT + | MSG_WAIT, tSenderResponse); + if (policy->abnormal_state) + return PE_PRS_SNK_SRC_Send_Swap; + if (msg & MSG_ACCEPT) + return PE_PRS_SNK_SRC_Transition_off; + } + return PE_SNK_Ready; +} + +policy_state usbpd_policy_prs_snk_src_accept_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, USBPD_DFP, USBPD_SINK)) + return PE_PRS_SNK_SRC_Transition_off; + + return PE_PRS_SNK_SRC_Accept_Swap; +} + +policy_state usbpd_policy_prs_snk_src_transition_to_off(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + usbpd_manager_turn_off_power_sink(pd_data); + + pd_data->policy.state = PE_PRS_SNK_SRC_Transition_off; + if (usbpd_wait_msg(pd_data, MSG_PSRDY, tPSSourceOff)) { + pd_data->counter.swap_hard_reset_counter = 0; + dev_info(pd_data->dev, "got PSRDY.\n"); + return PE_PRS_SNK_SRC_Assert_Rp; + } + if (policy->abnormal_state) + return PE_PRS_SNK_SRC_Transition_off; + + if (pd_data->counter.swap_hard_reset_counter > USBPD_nHardResetCount) + return Error_Recovery; + + return PE_SRC_Hard_Reset; +} + +policy_state usbpd_policy_prs_snk_src_assert_rp(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_power_role(pd_data, USBPD_SOURCE); + + return PE_PRS_SNK_SRC_Source_on; +} + +policy_state usbpd_policy_prs_snk_src_source_on(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_otg_control(pd_data, 1); + + msleep(150); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PS_RDY, USBPD_DFP, USBPD_SOURCE)) { + msleep(tSwapSourceStart); /* 20ms */ + pd_data->phy_ops.set_power_role(pd_data, USBPD_DRP); + return PE_SRC_Startup; + } + return PE_PRS_SNK_SRC_Source_on; +} + +policy_state usbpd_policy_vcs_evaluate_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + bool vcs_ok; + + dev_info(pd_data->dev, "%s\n", __func__); + vcs_ok = usbpd_manager_vconn_source_swap(pd_data); + + if (vcs_ok) + return PE_VCS_Accept_Swap; + else + return PE_VCS_Reject_VCONN_Swap; +} + +policy_state usbpd_policy_vcs_accept_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int vconn_source = 0; + int power_role = 0; + int data_role = 0; + + pd_data->phy_ops.get_vconn_source(pd_data, &vconn_source); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Accept, data_role, power_role)) { + if (vconn_source) + return PE_VCS_Wait_for_VCONN; + else + return PE_VCS_Turn_On_VCONN; + } + + return PE_VCS_Accept_Swap; +} + +policy_state usbpd_policy_vcs_send_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int vconn_source = 0; + int power_role = 0; + + pd_data->phy_ops.get_vconn_source(pd_data, &vconn_source); + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_VCONN_Swap, USBPD_DFP, power_role)) { + if (vconn_source) + return PE_VCS_Wait_for_VCONN; + else + return PE_VCS_Turn_On_VCONN; + } + + return PE_VCS_Send_Swap; +} + +policy_state usbpd_policy_vcs_wait_for_vconn(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->policy.state = PE_VCS_Wait_for_VCONN; + if (usbpd_wait_msg(pd_data, MSG_PSRDY, tVCONNSourceOn)) { + pd_data->counter.swap_hard_reset_counter = 0; + dev_info(pd_data->dev, "got PSRDY.\n"); + return PE_VCS_Turn_Off_VCONN; + } + if (policy->abnormal_state) + return PE_VCS_Wait_for_VCONN; + + if (pd_data->counter.swap_hard_reset_counter > USBPD_nHardResetCount) + return Error_Recovery; + + return PE_SNK_Hard_Reset; +} + +policy_state usbpd_policy_vcs_turn_off_vconn(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_vconn_source(pd_data, USBPD_VCONN_OFF); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_vcs_turn_on_vconn(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_vconn_source(pd_data, USBPD_VCONN_ON); + + return PE_VCS_Send_PS_RDY; +} + +policy_state usbpd_policy_vcs_send_ps_rdy(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + int data_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + pd_data->phy_ops.get_data_role(pd_data, &data_role); + + mdelay(5); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_PS_RDY, data_role, data_role)) { + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; + } + + return PE_VCS_Send_PS_RDY; +} + +policy_state usbpd_policy_vcs_reject_vconn_swap(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (usbpd_send_ctrl_msg(pd_data, &policy->tx_msg_header, + USBPD_Reject, USBPD_DFP, power_role)) { + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; + } + + return PE_VCS_Reject_VCONN_Swap; +} + +policy_state usbpd_policy_ufp_vdm_get_identity(struct policy_data *policy) +{ + return PE_UFP_VDM_Get_Identity_NAK; +} + +policy_state usbpd_policy_ufp_vdm_send_identity(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = Discover_Identity; + + /* TODO: data object should be prepared from device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Send_Identity; +} + +policy_state usbpd_policy_ufp_vdm_get_identity_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = Discover_Identity; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Get_Identity_NAK; +} + +policy_state usbpd_policy_ufp_vdm_get_svids(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_manager_get_svids(pd_data) == 0) + return PE_UFP_VDM_Send_SVIDs; + else + return PE_UFP_VDM_Get_SVIDs_NAK; + +} + +policy_state usbpd_policy_ufp_vdm_send_svids(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 2; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = Discover_SVIDs; + + policy->tx_data_obj[1].vdm_svid.svid_0 = PD_SID; + policy->tx_data_obj[1].vdm_svid.svid_1 = 0xFF01; + + /* TODO: data object should be prepared from device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + + return PE_UFP_VDM_Send_SVIDs; +} + +policy_state usbpd_policy_ufp_vdm_get_svids_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = Discover_SVIDs; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Get_SVIDs_NAK; +} + +policy_state usbpd_policy_ufp_vdm_get_modes(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + if (usbpd_manager_get_modes(pd_data) == 0) + return PE_UFP_VDM_Send_Modes; + else + return PE_UFP_VDM_Get_Modes_NAK; +} + +policy_state usbpd_policy_ufp_vdm_send_modes(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 2; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = Discover_Modes; + + /* TODO: data object should be prepared from device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Send_Modes; +} + +policy_state usbpd_policy_ufp_vdm_get_modes_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = Discover_Modes; + + /* TODO: data object should be prepared from device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Get_Modes_NAK; +} + +policy_state usbpd_policy_ufp_vdm_evaluate_mode_entry(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + /* Todo + check DPM evaluate request to enter a mode + */ +/* + if (usbpd_manager_enter_mode(pd_data, mode_pos, + mode_vdo) == 0) + return PE_UFP_VDM_Mode_Entry_ACK; + else + return PE_UFP_VDM_Mode_Entry_NAK; +*/ + return PE_UFP_VDM_Evaluate_Mode_Entry; +} + +policy_state usbpd_policy_ufp_vdm_mode_entry_ack(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = Enter_Mode; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Mode_Entry_ACK; +} + +policy_state usbpd_policy_ufp_vdm_mode_entry_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = Enter_Mode; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Mode_Entry_NAK; +} + +policy_state usbpd_policy_ufp_vdm_mode_exit(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + if (pd_data->phy_ops.get_status(pd_data, VDM_EXIT_MODE)) { + if (policy->rx_data_obj[0].structured_vdm.command + == Exit_Mode) { + unsigned mode_pos; + + /* get mode to exit */ + mode_pos = policy->rx_data_obj[0].structured_vdm.obj_pos; + if (usbpd_manager_exit_mode(pd_data, mode_pos) == 0) + return PE_UFP_VDM_Mode_Exit_ACK; + else + return PE_UFP_VDM_Mode_Exit_NAK; + } + } + return PE_UFP_VDM_Mode_Exit; + +} + +policy_state usbpd_policy_ufp_vdm_mode_exit_ack(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = Exit_Mode; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Mode_Exit_NAK; +} + +policy_state usbpd_policy_ufp_vdm_mode_exit_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = Exit_Mode; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Mode_Exit_NAK; +} + +policy_state usbpd_policy_ufp_vdm_attention_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; +/* policy->tx_msg_header.num_data_objs = 1; number of objects*/ + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 0; + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Attention; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Attention_Request; + +} + +policy_state usbpd_policy_ufp_vdm_evaluate_status(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + + /* Todo + check DPM evaluate request to inform status + */ +/* + if (usbpd_manager_enter_mode(pd_data, mode_pos, + mode_vdo) == 0) + return PE_UFP_VDM_Mode_Entry_ACK; + else + return PE_UFP_VDM_Mode_Entry_NAK; +*/ +} + +policy_state usbpd_policy_ufp_vdm_status_ack(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Status_Update; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Status_ACK; +} + +policy_state usbpd_policy_ufp_vdm_status_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Status_Update; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Status_NAK; +} + +policy_state usbpd_policy_ufp_vdm_evaluate_configure(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + + /* Todo + check DPM evaluate request to inform status + */ +/* + if (usbpd_manager_enter_mode(pd_data, mode_pos, + mode_vdo) == 0) + return PE_UFP_VDM_Mode_Entry_ACK; + else + return PE_UFP_VDM_Mode_Entry_NAK; +*/ +} + +policy_state usbpd_policy_ufp_vdm_configure_ack(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_ACK; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Configure; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Configure_ACK; +} + +policy_state usbpd_policy_ufp_vdm_configure_nak(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_UFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Responder_NAK; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Configure; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; + } + return PE_UFP_VDM_Configure_NAK; +} + +/* the end ufp */ + +policy_state usbpd_policy_dfp_vdm_identity_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Discover_Identity; + + pd_data->counter.discover_identity_counter++; + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_Identity_Request; + if (usbpd_wait_msg(pd_data, VDM_DISCOVER_IDENTITY, + tVDMSenderResponse)) { + pd_data->counter.discover_identity_counter = 0; + + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_Identity_ACKed; + } + } + return PE_DFP_VDM_Identity_NAKed; +} + +static policy_state usbpd_policy_dfp_vdm_response(struct policy_data *policy, + usbpd_manager_event_type event) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + usbpd_manager_inform_event(pd_data, event); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SINK) + return PE_SNK_Ready; + else + return PE_SRC_Ready; +} + +policy_state usbpd_policy_dfp_vdm_identity_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_IDENTITY_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_identity_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_IDENTITY_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_svids_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Discover_SVIDs; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_SVIDs_Request; + if (usbpd_wait_msg(pd_data, VDM_DISCOVER_SVID, + tVDMSenderResponse)) { + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_SVIDs_ACKed; + } + } + return PE_DFP_VDM_SVIDs_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_svids_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_SVID_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_svids_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_SVID_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_modes_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = manager->SVID_0; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1; + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Discover_Modes; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_Modes_Request; + if (usbpd_wait_msg(pd_data, VDM_DISCOVER_MODE, + tVDMSenderResponse)) { + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_Modes_ACKed; + } + } + return PE_DFP_VDM_Modes_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_modes_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_MODE_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_modes_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, + MANAGER_DISCOVER_MODE_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_entry_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].object = 0; + policy->tx_data_obj[0].structured_vdm.svid = manager->SVID_0; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1;/* Todo select which_mode */ + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Enter_Mode; + + /* TODO: obj_pos , vdo should be set by device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_Mode_Entry_Request; + if (usbpd_wait_msg(pd_data, VDM_ENTER_MODE, + tVDMWaitModeEntry)) { + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_Mode_Entry_ACKed; + } + } + return PE_DFP_VDM_Mode_Entry_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_entry_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_ENTER_MODE_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_entry_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_ENTER_MODE_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_exit_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 1; + + policy->tx_data_obj[0].structured_vdm.svid = PD_SID; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; +/* policy->tx_data_obj[0].structured_vdm.obj_pos = which_mode; */ + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = Exit_Mode; + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_Mode_Exit_Request; + if (usbpd_wait_msg(pd_data, VDM_EXIT_MODE, + tVDMWaitModeExit)) { + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_Mode_Exit_ACKed; + } + } + return PE_DFP_VDM_Mode_Exit_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_exit_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_EXIT_MODE_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_exit_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_EXIT_MODE_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_attention_request(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_ATTENTION_REQUEST); +} + +policy_state usbpd_policy_dfp_vdm_status_update(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + pd_data->phy_ops.set_check_msg_pass(pd_data, CHECK_MSG_PASS); + + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 2; + + policy->tx_data_obj[0].object = 0; + policy->tx_data_obj[0].structured_vdm.svid = PD_SID_1; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1;/* Todo select which_mode */ + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Status_Update; + + /* second object for vdo */ + policy->tx_data_obj[1].object = 0; + policy->tx_data_obj[1].displayport_status.port_connected = 1; + + /* TODO: obj_pos , vdo should be set by device manager */ + + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_Status_Update; + if (usbpd_wait_msg(pd_data, MSG_PASS, + tVDMWaitModeEntry)) { + pd_data->phy_ops.set_check_msg_pass(pd_data, NONE_CHECK_MSG_PASS); + pr_info("%s : command(%d), command_type(%d), obj_pos(%d), version(%d), vdm_type(%d)\n", + __func__, policy->rx_data_obj[0].structured_vdm.command, + policy->rx_data_obj[0].structured_vdm.command_type, + policy->rx_data_obj[0].structured_vdm.obj_pos, + policy->rx_data_obj[0].structured_vdm.version, + policy->rx_data_obj[0].structured_vdm.vdm_type); + + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_Status_Update_ACKed; + } + } + pd_data->phy_ops.set_check_msg_pass(pd_data, NONE_CHECK_MSG_PASS); + + return PE_DFP_VDM_Status_Update_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_status_update_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_STATUS_UPDATE_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_status_update_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_STATUS_UPDATE_NAKED); +} + +policy_state usbpd_policy_dfp_vdm_displayport_configure(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + pd_data->phy_ops.set_check_msg_pass(pd_data, CHECK_MSG_PASS); + + policy->tx_msg_header.msg_type = USBPD_Vendor_Defined; + policy->tx_msg_header.port_data_role = USBPD_DFP; + policy->tx_msg_header.port_power_role = power_role; + policy->tx_msg_header.num_data_objs = 2; + + policy->tx_data_obj[0].structured_vdm.svid = manager->SVID_0; + policy->tx_data_obj[0].structured_vdm.vdm_type = Structured_VDM; + policy->tx_data_obj[0].structured_vdm.version = 0; + policy->tx_data_obj[0].structured_vdm.obj_pos = 1;/* Todo select which_mode */ + policy->tx_data_obj[0].structured_vdm.command_type = Initiator; + policy->tx_data_obj[0].structured_vdm.command = DisplayPort_Configure; + + /* second object for vdo */ + policy->tx_data_obj[1].object = 0; + policy->tx_data_obj[1].displayport_configurations.select_configuration = + USB_U_AS_UFP_D; + policy->tx_data_obj[1].displayport_configurations.displayport_protocol = + DP_V_1_3; +// policy->tx_data_obj[1].displayport_configurations.ufp_u_pin_assignment = +// manager->pin_assignment; + policy->tx_data_obj[1].displayport_configurations.ufp_u_pin_assignment = + 4; + + /* TODO: obj_pos , vdo should be set by device manager */ + if (usbpd_send_msg(pd_data, &policy->tx_msg_header, + policy->tx_data_obj)) { + pd_data->policy.state = PE_DFP_VDM_DisplayPort_Configure; + if (usbpd_wait_msg(pd_data, MSG_PASS, + tVDMWaitModeEntry)) { + pd_data->phy_ops.set_check_msg_pass(pd_data, NONE_CHECK_MSG_PASS); + if (policy->rx_data_obj[0].structured_vdm.command_type + == Responder_ACK) + return PE_DFP_VDM_DisplayPort_Configure_ACKed; + } + } + pd_data->phy_ops.set_check_msg_pass(pd_data, NONE_CHECK_MSG_PASS); + + return PE_DFP_VDM_DisplayPort_Configure_NAKed; +} + +policy_state usbpd_policy_dfp_vdm_displayport_configure_acked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_DisplayPort_Configure_ACKED); +} + +policy_state usbpd_policy_dfp_vdm_displayport_configure_naked(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_info(pd_data->dev, "%s\n", __func__); + + return usbpd_policy_dfp_vdm_response(policy, MANAGER_DisplayPort_Configure_NACKED); +} + +policy_state usbpd_policy_dfp_uvdm_send_message(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + struct usbpd_manager_data *manager = &pd_data->manager; + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + pd_data->phy_ops.set_check_msg_pass(pd_data, CHECK_MSG_PASS); + + usbpd_send_msg(pd_data, &manager->uvdm_msg_header, manager->uvdm_data_obj); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_policy_dfp_uvdm_receive_message(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + int power_role = 0; + + dev_info(pd_data->dev, "%s\n", __func__); + + usbpd_manager_inform_event(pd_data, MANAGER_UVDM_RECEIVE_MESSAGE); + + pd_data->phy_ops.get_power_role(pd_data, &power_role); + + if (power_role == USBPD_SOURCE) + return PE_SRC_Ready; + else + return PE_SNK_Ready; +} + +policy_state usbpd_error_recovery(struct policy_data *policy) +{ + struct usbpd_data *pd_data = policy_to_usbpd(policy); + + dev_err(pd_data->dev, "%s\n", __func__); + + return Error_Recovery; +} + +void usbpd_policy_work(struct work_struct *work) +{ + struct usbpd_data *pd_data = container_of(work, struct usbpd_data, + worker); + struct policy_data *policy = &pd_data->policy; + int power_role = 0; + policy_state next_state = policy->state; + policy_state saved_state; + + do { + if (!policy->plug_valid) { + pr_info("%s : usbpd cable is empty\n", __func__); + break; + } + + if (policy->rx_hardreset || policy->rx_softreset + || policy->plug) { + saved_state = 0; + next_state = 0; /* default */ + } + saved_state = next_state; + switch (next_state) { + case PE_SRC_Startup: + next_state = usbpd_policy_src_startup(policy); + break; + case PE_SRC_Discovery: + next_state = usbpd_policy_src_discovery(policy); + break; + case PE_SRC_Send_Capabilities: + next_state = usbpd_policy_src_send_capabilities(policy); + break; + case PE_SRC_Negotiate_Capability: + next_state = usbpd_policy_src_negotiate_capability(policy); + break; + case PE_SRC_Transition_Supply: + next_state = usbpd_policy_src_transition_supply(policy); + break; + case PE_SRC_Ready: + next_state = usbpd_policy_src_ready(policy); + break; + case PE_SRC_Disabled: + next_state = usbpd_policy_src_disabled(policy); + break; + case PE_SRC_Capability_Response: + next_state = usbpd_policy_src_capability_response(policy); + break; + case PE_SRC_Hard_Reset: + next_state = usbpd_policy_src_hard_reset(policy); + break; + case PE_SRC_Hard_Reset_Received: + next_state = usbpd_policy_src_hard_reset_received(policy); + break; + case PE_SRC_Transition_to_default: + next_state = usbpd_policy_src_transition_to_default(policy); + break; + case PE_SRC_Give_Source_Cap: + next_state = usbpd_policy_src_give_source_cap(policy); + break; + case PE_SRC_Get_Sink_Cap: + next_state = usbpd_policy_src_get_sink_cap(policy); + break; + case PE_SRC_Wait_New_Capabilities: + next_state = usbpd_policy_src_wait_new_capabilities(policy); + break; + case PE_SRC_Send_Soft_Reset: + next_state = usbpd_policy_src_send_soft_reset(policy); + break; + case PE_SRC_Soft_Reset: + next_state = usbpd_policy_src_soft_reset(policy); + break; + + case PE_SNK_Startup: + next_state = usbpd_policy_snk_startup(policy); + break; + case PE_SNK_Discovery: + next_state = usbpd_policy_snk_discovery(policy); + break; + case PE_SNK_Wait_for_Capabilities: + next_state = usbpd_policy_snk_wait_for_capabilities(policy); + break; + case PE_SNK_Evaluate_Capability: + next_state = usbpd_policy_snk_evaluate_capability(policy); + break; + case PE_SNK_Select_Capability: + next_state = usbpd_policy_snk_select_capability(policy); + break; + case PE_SNK_Transition_Sink: + next_state = usbpd_policy_snk_transition_sink(policy); + break; + case PE_SNK_Ready: + next_state = usbpd_policy_snk_ready(policy); + break; + case PE_SNK_Hard_Reset: + next_state = usbpd_policy_snk_hard_reset(policy); + break; + case PE_SNK_Transition_to_default: + next_state = usbpd_policy_snk_transition_to_default(policy); + break; + case PE_SNK_Give_Sink_Cap: + next_state = usbpd_policy_snk_give_sink_cap(policy); + break; + case PE_SNK_Get_Source_Cap: + next_state = usbpd_policy_snk_get_source_cap(policy); + break; + case PE_SNK_Soft_Reset: + next_state = usbpd_policy_snk_soft_reset(policy); + break; + + case PE_DRS_Evaluate_Port: + next_state = usbpd_policy_drs_evaluate_port(policy); + break; + case PE_DRS_Evaluate_Send_Port: + next_state = usbpd_policy_drs_evaluate_send_port(policy); + break; + case PE_DRS_DFP_UFP_Evaluate_DR_Swap: + next_state = usbpd_policy_drs_dfp_ufp_evaluate_dr_swap(policy); + break; + case PE_DRS_DFP_UFP_Accept_DR_Swap: + next_state = usbpd_policy_drs_dfp_ufp_accept_dr_swap(policy); + break; + case PE_DRS_DFP_UFP_Change_to_UFP: + next_state = usbpd_policy_drs_dfp_ufp_change_to_ufp(policy); + break; + case PE_DRS_DFP_UFP_Send_DR_Swap: + next_state = usbpd_policy_drs_dfp_ufp_send_dr_swap(policy); + break; + case PE_DRS_DFP_UFP_Reject_DR_Swap: + next_state = usbpd_policy_drs_dfp_ufp_reject_dr_swap(policy); + break; + case PE_DRS_UFP_DFP_Evaluate_DR_Swap: + next_state = usbpd_policy_drs_ufp_dfp_evaluate_dr_swap(policy); + break; + case PE_DRS_UFP_DFP_Accept_DR_Swap: + next_state = usbpd_policy_drs_ufp_dfp_accept_dr_swap(policy); + break; + case PE_DRS_UFP_DFP_Change_to_DFP: + next_state = usbpd_policy_drs_ufp_dfp_change_to_dfp(policy); + break; + case PE_DRS_UFP_DFP_Send_DR_Swap: + next_state = usbpd_policy_drs_ufp_dfp_send_dr_swap(policy); + break; + case PE_DRS_UFP_DFP_Reject_DR_Swap: + next_state = usbpd_policy_drs_ufp_dfp_reject_dr_swap(policy); + break; + + case PE_PRS_SRC_SNK_Reject_PR_Swap: + next_state = usbpd_policy_prs_src_snk_reject_pr_swap(policy); + break; + case PE_PRS_SRC_SNK_Evaluate_Swap: + next_state = usbpd_policy_prs_src_snk_evaluate_swap(policy); + break; + case PE_PRS_SRC_SNK_Send_Swap: + next_state = usbpd_policy_prs_src_snk_send_swap(policy); + break; + case PE_PRS_SRC_SNK_Accept_Swap: + next_state = usbpd_policy_prs_src_snk_accept_swap(policy); + break; + case PE_PRS_SRC_SNK_Transition_off: + next_state = usbpd_policy_prs_src_snk_transition_to_off(policy); + break; + case PE_PRS_SRC_SNK_Assert_Rd: + next_state = usbpd_policy_prs_src_snk_assert_rd(policy); + break; + case PE_PRS_SRC_SNK_Wait_Source_on: + next_state = usbpd_policy_prs_src_snk_wait_source_on(policy); + break; + case PE_PRS_SNK_SRC_Reject_Swap: + next_state = usbpd_policy_prs_snk_src_reject_swap(policy); + break; + case PE_PRS_SNK_SRC_Evaluate_Swap: + next_state = usbpd_policy_prs_snk_src_evaluate_swap(policy); + break; + case PE_PRS_SNK_SRC_Send_Swap: + next_state = usbpd_policy_prs_snk_src_send_swap(policy); + break; + case PE_PRS_SNK_SRC_Accept_Swap: + next_state = usbpd_policy_prs_snk_src_accept_swap(policy); + break; + case PE_PRS_SNK_SRC_Transition_off: + next_state = usbpd_policy_prs_snk_src_transition_to_off(policy); + break; + case PE_PRS_SNK_SRC_Assert_Rp: + next_state = usbpd_policy_prs_snk_src_assert_rp(policy); + break; + case PE_PRS_SNK_SRC_Source_on: + next_state = usbpd_policy_prs_snk_src_source_on(policy); + break; + case PE_VCS_Evaluate_Swap: + next_state = usbpd_policy_vcs_evaluate_swap(policy); + break; + case PE_VCS_Accept_Swap: + next_state = usbpd_policy_vcs_accept_swap(policy); + break; + case PE_VCS_Wait_for_VCONN: + next_state = usbpd_policy_vcs_wait_for_vconn(policy); + break; + case PE_VCS_Turn_Off_VCONN: + next_state = usbpd_policy_vcs_turn_off_vconn(policy); + break; + case PE_VCS_Turn_On_VCONN: + next_state = usbpd_policy_vcs_turn_on_vconn(policy); + break; + case PE_VCS_Send_PS_RDY: + next_state = usbpd_policy_vcs_send_ps_rdy(policy); + break; + case PE_VCS_Send_Swap: + next_state = usbpd_policy_vcs_send_swap(policy); + break; + case PE_VCS_Reject_VCONN_Swap: + next_state = usbpd_policy_vcs_reject_vconn_swap(policy); + break; + + case PE_UFP_VDM_Get_Identity: + next_state = usbpd_policy_ufp_vdm_get_identity(policy); + break; + case PE_UFP_VDM_Send_Identity: + next_state = usbpd_policy_ufp_vdm_send_identity(policy); + break; + case PE_UFP_VDM_Get_Identity_NAK: + next_state = usbpd_policy_ufp_vdm_get_identity_nak(policy); + break; + case PE_UFP_VDM_Get_SVIDs: + next_state = usbpd_policy_ufp_vdm_get_svids(policy); + break; + case PE_UFP_VDM_Send_SVIDs: + next_state = usbpd_policy_ufp_vdm_send_svids(policy); + break; + case PE_UFP_VDM_Get_SVIDs_NAK: + next_state = usbpd_policy_ufp_vdm_get_svids_nak(policy); + break; + case PE_UFP_VDM_Get_Modes: + next_state = usbpd_policy_ufp_vdm_get_modes(policy); + break; + case PE_UFP_VDM_Send_Modes: + next_state = usbpd_policy_ufp_vdm_send_modes(policy); + break; + case PE_UFP_VDM_Get_Modes_NAK: + next_state = usbpd_policy_ufp_vdm_get_modes_nak(policy); + break; + case PE_UFP_VDM_Evaluate_Mode_Entry: + next_state = usbpd_policy_ufp_vdm_evaluate_mode_entry(policy); + break; + case PE_UFP_VDM_Mode_Entry_ACK: + next_state = usbpd_policy_ufp_vdm_mode_entry_ack(policy); + break; + case PE_UFP_VDM_Mode_Entry_NAK: + next_state = usbpd_policy_ufp_vdm_mode_entry_nak(policy); + break; + case PE_UFP_VDM_Mode_Exit: + next_state = usbpd_policy_ufp_vdm_mode_exit(policy); + break; + case PE_UFP_VDM_Mode_Exit_ACK: + next_state = usbpd_policy_ufp_vdm_mode_exit_ack(policy); + break; + case PE_UFP_VDM_Mode_Exit_NAK: + next_state = usbpd_policy_ufp_vdm_mode_exit_nak(policy); + break; + case PE_UFP_VDM_Attention_Request: + next_state = usbpd_policy_ufp_vdm_attention_request(policy); + break; + case PE_UFP_VDM_Evaluate_Status: + next_state = usbpd_policy_ufp_vdm_evaluate_status(policy); + break; + case PE_UFP_VDM_Status_ACK: + next_state = usbpd_policy_ufp_vdm_status_ack(policy); + break; + case PE_UFP_VDM_Status_NAK: + next_state = usbpd_policy_ufp_vdm_status_nak(policy); + break; + case PE_UFP_VDM_Evaluate_Configure: + next_state = usbpd_policy_ufp_vdm_evaluate_configure(policy); + break; + case PE_UFP_VDM_Configure_ACK: + next_state = usbpd_policy_ufp_vdm_configure_ack(policy); + break; + case PE_UFP_VDM_Configure_NAK: + next_state = usbpd_policy_ufp_vdm_configure_nak(policy); + break; + case PE_DFP_VDM_Identity_Request: + next_state = usbpd_policy_dfp_vdm_identity_request(policy); + break; + case PE_DFP_VDM_Identity_ACKed: + next_state = usbpd_policy_dfp_vdm_identity_acked(policy); + break; + case PE_DFP_VDM_Identity_NAKed: + next_state = usbpd_policy_dfp_vdm_identity_naked(policy); + break; + case PE_DFP_VDM_SVIDs_Request: + next_state = usbpd_policy_dfp_vdm_svids_request(policy); + break; + case PE_DFP_VDM_SVIDs_ACKed: + next_state = usbpd_policy_dfp_vdm_svids_acked(policy); + break; + case PE_DFP_VDM_SVIDs_NAKed: + next_state = usbpd_policy_dfp_vdm_svids_naked(policy); + break; + case PE_DFP_VDM_Modes_Request: + next_state = usbpd_policy_dfp_vdm_modes_request(policy); + break; + case PE_DFP_VDM_Modes_ACKed: + next_state = usbpd_policy_dfp_vdm_modes_acked(policy); + break; + case PE_DFP_VDM_Modes_NAKed: + next_state = usbpd_policy_dfp_vdm_modes_naked(policy); + break; + case PE_DFP_VDM_Mode_Entry_Request: + next_state = usbpd_policy_dfp_vdm_entry_request(policy); + break; + case PE_DFP_VDM_Mode_Entry_ACKed: + next_state = usbpd_policy_dfp_vdm_entry_acked(policy); + break; + case PE_DFP_VDM_Mode_Entry_NAKed: + next_state = usbpd_policy_dfp_vdm_entry_naked(policy); + break; + case PE_DFP_VDM_Mode_Exit_Request: + next_state = usbpd_policy_dfp_vdm_exit_request(policy); + break; + case PE_DFP_VDM_Mode_Exit_ACKed: + next_state = usbpd_policy_dfp_vdm_exit_acked(policy); + break; + case PE_DFP_VDM_Mode_Exit_NAKed: + next_state = usbpd_policy_dfp_vdm_exit_naked(policy); + break; + case PE_DFP_VDM_Attention_Request: + next_state = usbpd_policy_dfp_vdm_attention_request(policy); + break; + case PE_DFP_VDM_Status_Update: + next_state = usbpd_policy_dfp_vdm_status_update(policy); + break; + case PE_DFP_VDM_Status_Update_ACKed: + next_state = usbpd_policy_dfp_vdm_status_update_acked(policy); + break; + case PE_DFP_VDM_Status_Update_NAKed: + next_state = usbpd_policy_dfp_vdm_status_update_naked(policy); + break; + case PE_DFP_VDM_DisplayPort_Configure: + next_state = usbpd_policy_dfp_vdm_displayport_configure(policy); + break; + case PE_DFP_VDM_DisplayPort_Configure_ACKed: + next_state = usbpd_policy_dfp_vdm_displayport_configure_acked(policy); + break; + case PE_DFP_VDM_DisplayPort_Configure_NAKed: + next_state = usbpd_policy_dfp_vdm_displayport_configure_naked(policy); + break; + case PE_DFP_UVDM_Send_Message: + next_state = usbpd_policy_dfp_uvdm_send_message(policy); + break; + case PE_DFP_UVDM_Receive_Message: + next_state = usbpd_policy_dfp_uvdm_receive_message(policy); + break; + case Error_Recovery: + next_state = usbpd_error_recovery(policy); + break; + + default: + pd_data->phy_ops.get_power_role(pd_data, &power_role); + pr_info("%s, %d\n", __func__, power_role); + + if (power_role == USBPD_SINK) { + pr_info("%s, SINK\n", __func__); + if (policy->rx_hardreset) { + policy->rx_hardreset = 0; + next_state = PE_SNK_Transition_to_default; + } else if (policy->rx_softreset) { + policy->rx_softreset = 0; + next_state = PE_SNK_Soft_Reset; + } else if (policy->plug) { + policy->plug = 0; + next_state = PE_SNK_Startup; + } else { + next_state = PE_SNK_Startup; + } + } else { + pr_info("%s, SOURCE\n", __func__); + if (policy->rx_hardreset) { + policy->rx_hardreset = 0; + next_state = PE_SRC_Hard_Reset_Received; + } else if (policy->rx_softreset) { + policy->rx_softreset = 0; + next_state = PE_SRC_Soft_Reset; + } else if (policy->plug) { + policy->plug = 0; + next_state = PE_SRC_Startup; + } else { + next_state = PE_SRC_Startup; + } + } + break; + } + dev_info(pd_data->dev, "%s saved state %x next_state %x \n", __func__, saved_state, next_state); + } while (saved_state != next_state); + + policy->state = next_state; + dev_info(pd_data->dev, "%s Finished\n", __func__); +} + +void usbpd_init_policy(struct usbpd_data *pd_data) +{ + int i; + struct policy_data *policy = &pd_data->policy; + + policy->state = 0; + policy->rx_hardreset = 0; + policy->rx_softreset = 0; + policy->plug = 0; + policy->rx_msg_header.word = 0; + policy->tx_msg_header.word = 0; + policy->modal_operation = 0; + policy->sink_cap_received = 0; + policy->send_sink_cap = 0; + for (i = 0; i < USBPD_MAX_COUNT_MSG_OBJECT; i++) { + policy->rx_data_obj[i].object = 0; + policy->tx_data_obj[i].object = 0; + } +} + +void usbpd_kick_policy_work(struct device *dev) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + + schedule_work(&pd_data->worker); +} + diff --git a/drivers/ccic/usbpd_sysfs.c b/drivers/ccic/usbpd_sysfs.c index f83e74c75fb1..1bf5076a58ec 100644 --- a/drivers/ccic/usbpd_sysfs.c +++ b/drivers/ccic/usbpd_sysfs.c @@ -24,10 +24,16 @@ #include #include #include +#ifdef CONFIG_CCIC_S2MM005 #include #include #include +#endif +#ifdef CONFIG_CCIC_S2MU106 +#include +#endif +#ifdef CONFIG_CCIC_S2MM005 static ssize_t s2mm005_cur_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -204,11 +210,34 @@ static ssize_t ccic_store_firmware_update(struct device *dev, return size; } static DEVICE_ATTR(fw_update, 0220, NULL, ccic_store_firmware_update); +#endif +#ifdef CONFIG_CCIC_S2MU106 +static ssize_t ccic_connector_side_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usbpd_data *pd_data = dev_get_drvdata(dev); + int ret = 0; + if (!pd_data) { + pr_err("%s pd_data is null!!\n", __func__); + return -ENODEV; + } + + ret = pd_data->phy_ops.get_side_check(pd_data); + pr_info("%s usbpd side check: %d\n", __func__, ret); + return sprintf(buf, "%d\n", ret); +} +static DEVICE_ATTR(usb_direction, 0444, ccic_connector_side_show, NULL); +#endif static struct attribute *ccic_attributes[] = { +#ifdef CONFIG_CCIC_S2MM005 &dev_attr_cur_version.attr, &dev_attr_fw_update.attr, &dev_attr_fw_update_status.attr, +#endif +#ifdef CONFIG_CCIC_S2MU106 + &dev_attr_usb_direction.attr, +#endif NULL }; diff --git a/include/linux/ccic/core.h b/include/linux/ccic/core.h index c3735a6f3d05..3983b0b1f52a 100644 --- a/include/linux/ccic/core.h +++ b/include/linux/ccic/core.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #if defined(CONFIG_SAMSUNG_BATTERY) diff --git a/include/linux/ccic/s2mm005_usbpd_msg.h b/include/linux/ccic/s2mm005_usbpd_msg.h new file mode 100644 index 000000000000..4643f81debd8 --- /dev/null +++ b/include/linux/ccic/s2mm005_usbpd_msg.h @@ -0,0 +1,188 @@ +#ifndef __USBPD_MSG_H__ +#define __USBPD_MSG_H__ + +typedef union { + u16 word; + u8 byte[2]; + + struct { + unsigned msg_type:4; + unsigned:1; + unsigned port_data_role:1; + unsigned spec_revision:2; + unsigned port_power_role:1; + unsigned msg_id:3; + unsigned num_data_objs:3; + unsigned:1; + }; +} msg_header_type; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + + struct { + unsigned:30; + unsigned supply_type:2; + } power_data_obj_supply_type; + + struct { + unsigned max_current:10; /* 10mA units */ + unsigned voltage:10; /* 50mV units */ + unsigned peak_current:2; + unsigned:3; + unsigned data_role_swap:1; + unsigned usb_comm_capable:1; + unsigned externally_powered:1; + unsigned usb_suspend_support:1; + unsigned dual_role_power:1; + unsigned supply:2; + } power_data_obj; + + struct { + unsigned op_current:10; /* 10mA units */ + unsigned voltage:10; /* 50mV units */ + unsigned:5; + unsigned data_role_swap:1; + unsigned usb_comm_capable:1; + unsigned externally_powered:1; + unsigned higher_capability:1; + unsigned dual_role_power:1; + unsigned supply_type:2; + } power_data_obj_sink; + + struct { + unsigned max_current:10; /* 10mA units */ + unsigned voltage:10; /* 50mV units */ + unsigned peak_current:2; + unsigned:3; + unsigned data_role_swap:1; + unsigned usb_comm_capable:1; + unsigned externally_powered:1; + unsigned usb_suspend_support:1; + unsigned dual_role_power:1; + unsigned supply_type:2; + } power_data_obj_fixed; + + struct { + unsigned max_current:10; /* 10mA units */ + unsigned min_voltage:10; /* 50mV units */ + unsigned max_voltage:10; /* 50mV units */ + unsigned supply_type:2; + } power_data_obj_variable; + + struct { + unsigned max_power:10; /* 250mW units */ + unsigned min_voltage:10; /* 50mV units */ + unsigned max_voltage:10; /* 50mV units */ + unsigned supply_type:2; + } power_data_obj_battery; + + struct { + unsigned min_current:10; /* 10mA units */ + unsigned op_current:10; /* 10mA units */ + unsigned:4; + unsigned no_usb_suspend:1; + unsigned usb_comm_capable:1; + unsigned capability_mismatch:1; + unsigned give_back:1; + unsigned object_position:3; + unsigned:1; + } request_data_object; + + struct { + unsigned max_power:10; /* 250mW units */ + unsigned op_power:10; /* 250mW units */ + unsigned:4; + unsigned no_usb_suspend:1; + unsigned usb_comm_capable:1; + unsigned capability_mismatch:1; + unsigned give_back:1; + unsigned object_position:3; + unsigned:1; + } request_data_object_battery; + + struct { + unsigned vendor_defined:15; + unsigned vdm_type:1; + unsigned vendor_id:16; + } unstructured_vdm; + + struct { + unsigned command:5; + unsigned:1; + unsigned command_type:2; + unsigned obj_pos:3; + unsigned:2; + unsigned version:2; + unsigned vdm_type:1; + unsigned svid:16; + } structured_vdm; + + struct { + unsigned usb_vendor_id:16; + unsigned:10; + unsigned modal_operation:1; + unsigned product_type:3; + unsigned data_capable_usb_device:1; + unsigned data_capable_usb_host:1; + } discover_identity_id_header; + + struct { + unsigned device_version:16; + unsigned usb_product_id:16; + } discover_identity_product_vdo; + + struct { + unsigned svid_1:16; + unsigned svid_0:16; + } discover_svids_vdo; + + struct { + unsigned port_capability:2; + unsigned signalling_dp:4; + unsigned receptacle_indication:1; + unsigned usb_2p0_not_used:1; + unsigned dfp_d_pin_assignments:8; + unsigned ufp_d_pin_assignments:8; + unsigned dp_mode_vdo_reserved:8; + } discover_mode_dp_capability; + + struct { + unsigned port_capability:2; + unsigned displayport_protocol:4; + unsigned receptacle_indication:1; + unsigned usb_r2_signaling:1; + unsigned dfp_d_pin_assignments:8; + unsigned ufp_d_pin_assignments:8; + unsigned rsvd:8; + } displayport_capabilities; + + struct { + unsigned port_connected:2; + unsigned power_low:1; + unsigned enabled:1; + unsigned multi_function_preferred:1; + unsigned usb_configuration_request:1; + unsigned exit_displayport_mode_request:1; + unsigned hpd_state:1; + unsigned irq_hpd:1; + unsigned rsvd:23; + } dp_status; + + struct{ + unsigned select_configuration:2; + unsigned displayport_protocol:4; + unsigned rsvd1:2; + unsigned ufp_u_pin_assignment:8; + unsigned rsvd2:16; + } dp_configurations; + + struct{ + unsigned svid_1:16; + unsigned svid_0:16; + } vdm_svid; +} data_obj_type; +#endif + diff --git a/include/linux/ccic/usbpd-s2mu106.h b/include/linux/ccic/usbpd-s2mu106.h new file mode 100644 index 000000000000..13d1b37d4df2 --- /dev/null +++ b/include/linux/ccic/usbpd-s2mu106.h @@ -0,0 +1,688 @@ +/* + * Copyright (C) 2016 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#if defined CONFIG_IFCONN_NOTIFIER +#include +#endif + +#include + +#ifndef __USBPD_S2MU106_H__ +#define __USBPD_S2MU106_H__ + +#define USBPD_DEV_NAME "usbpd-s2mu106" + +/* message buffer */ +#define S2MU106_MAX_NUM_MSG_OBJ (7) +/* INTERRUPT STATUS NUM */ +#define S2MU106_MAX_NUM_INT_STATUS (7) + +#define S2MU106_REG_TYPEC_DIS (1 << 2) + +#define TA_WATER_CHK_DURATION_MS 5000 + +/* define timer */ +#define S2MU106_ROLE_SWAP_TIME_MS (1350) +#define S2MU106_HARD_RESET_DELAY_MS (300) +#define S2MU106_WAIT_RD_DETACH_DELAY_MS (200) +#define S2MU106_WAIT_ATTACH_DELAY_MS (30) +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +#define DUAL_ROLE_SET_MODE_WAIT_MS (2000) +#endif +#define S2MU106_WATER_CHK_INTERVAL_TIME (300) +#define S2MU106_ATTACH_STATE_CHECK_TIME (1000) + +#define WATER_CHK_RETRY_CNT 2 +#define IS_CC_RP(cc1, cc2) ((cc1 == USBPD_Rp) && (cc2 == USBPD_Rp)) +#define IS_CC_WATER(cc1, cc2) ((cc1 != USBPD_Rp) && (cc2 != USBPD_Rp)) +#define IS_ONLY_CC1_WATER(cc1, cc2) ((cc1 != USBPD_Rp) && (cc2 == USBPD_Rp)) +#define IS_ONLY_CC2_WATER(cc1, cc2) ((cc1 == USBPD_Rp) && (cc2 != USBPD_Rp)) + +/*****************************************/ +/***********DEFINITION REGISTER***********/ +/*****************************************/ +#define S2MU106_RESET_REG_00 (0x00) + +/* reg 0x00 VBUS WAKEUP CONTROL */ +#define S2MU106_REG_VBUS_WAKEUP_DIS_SHIFT (1) +#define S2MU106_REG_VBUS_WAKEUP_DIS \ + (0x1 << S2MU106_REG_VBUS_WAKEUP_DIS_SHIFT) /* 0x02 */ + +/* reg 0x01 LPM MODE ENABLE */ +#define S2MU106_REG_LP_LDO_D_SHIFT (0) +#define S2MU106_REG_LPM_EN_SHIFT (1) +#define S2MU106_REG_LP_LDO_D \ + (1 << S2MU106_REG_LP_LDO_D_SHIFT) /* 0x01 */ +#define S2MU106_REG_LPM_EN \ + (0x1 << S2MU106_REG_LPM_EN_SHIFT) /* 0x02 */ + +/* reg 0x02 */ +#define S2MU106_REG_CC_OCP_SHIFT (4) +#define S2MU106_REG_CC_OCP_MASK (0xf << S2MU106_REG_CC_OCP_SHIFT) + +#define S2MU106_REG_IFG_SHIFT (4) +#define S2MU106_REG_IFG_MASK (0xf << S2MU106_REG_IFG_SHIFT) /* 0xf0 */ + +/* reg 0x18 */ +#define S2MU106_REG_PLUG_CTRL_MODE_SHIFT (0) +#define S2MU106_REG_PLUG_CTRL_RP_SEL_SHIFT (4) +#define S2MU106_REG_PLUG_CTRL_DETECT_BAT_DISABLE_SHIFT (6) +#define S2MU106_REG_PLUG_CTRL_DETECT_OCP_DISABLE_SHIFT (7) +#define S2MU106_REG_PLUG_CTRL_DFP \ + (0x1 << S2MU106_REG_PLUG_CTRL_MODE_SHIFT) /* 0x01 */ +#define S2MU106_REG_PLUG_CTRL_UFP \ + (0x2 << S2MU106_REG_PLUG_CTRL_MODE_SHIFT) /* 0x02 */ +#define S2MU106_REG_PLUG_CTRL_DRP \ + (0x3 << S2MU106_REG_PLUG_CTRL_MODE_SHIFT) /* 0x03 */ +#define S2MU106_REG_PLUG_CTRL_RP0 \ + (0x0 << S2MU106_REG_PLUG_CTRL_RP_SEL_SHIFT) /* 0x00 */ +#define S2MU106_REG_PLUG_CTRL_RP80 \ + (0x1 << S2MU106_REG_PLUG_CTRL_RP_SEL_SHIFT) /* 0x10 */ +#define S2MU106_REG_PLUG_CTRL_RP180 \ + (0x2 << S2MU106_REG_PLUG_CTRL_RP_SEL_SHIFT) /* 0x20 */ +#define S2MU106_REG_PLUG_CTRL_MODE_MASK \ + (0x3 << S2MU106_REG_PLUG_CTRL_MODE_SHIFT) /* 0x03 */ +#define S2MU106_REG_PLUG_CTRL_RP_SEL_MASK \ + (0x3 << S2MU106_REG_PLUG_CTRL_RP_SEL_SHIFT)/* 0x30 */ +#define S2MU106_REG_PLUG_CTRL_DETECT_BAT_DISABLE_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_DETECT_BAT_DISABLE_SHIFT)/* 0x40 */ +#define S2MU106_REG_PLUG_CTRL_DETECT_OCP_DISABLE_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_DETECT_OCP_DISABLE_SHIFT)/* 0x80 */ + +/* reg 0x19 */ +#define S2MU106_REG_MSG_DATA_ROLE_SHIFT (5) +#define S2MU106_REG_MSG_POWER_ROLE_SHIFT (6) +#define S2MU106_REG_MSG_DATA_ROLE_UFP \ + (0x0 << S2MU106_REG_MSG_DATA_ROLE_SHIFT) /* 0x00 */ +#define S2MU106_REG_MSG_DATA_ROLE_DFP \ + (0x1 << S2MU106_REG_MSG_DATA_ROLE_SHIFT) /* 0x20 */ +#define S2MU106_REG_MSG_DATA_ROLE_MASK \ + (0x1 << S2MU106_REG_MSG_DATA_ROLE_SHIFT) /* 0x20 */ +#define S2MU106_REG_MSG_POWER_ROLE_SINK \ + (0x0 << S2MU106_REG_MSG_POWER_ROLE_SHIFT) /* 0x00 */ +#define S2MU106_REG_MSG_POWER_ROLE_SOURCE \ + (0x1 << S2MU106_REG_MSG_POWER_ROLE_SHIFT) /* 0x40 */ +#define S2MU106_REG_MSG_POWER_ROLE_MASK \ + (0x1 << S2MU106_REG_MSG_POWER_ROLE_SHIFT) /* 0x40 */ + +#define S2MU106_REG_RD_OR_VBUS_MUX_SEL_SHIFT (4) +#define S2MU106_REG_RD_OR_VBUS_MUX_SEL \ + (0x1 << S2MU106_REG_RD_OR_VBUS_MUX_SEL_SHIFT) + +/* reg 0x1B */ +#define S2MU106_REG_DET_RD_OR_VBUS_SHIFT (4) +#define S2MU106_REG_DET_RD_OR_VBUS \ + (0x1 << S2MU106_REG_DET_RD_OR_VBUS_SHIFT) /* 0x10 */ + +/* reg 0x1E */ +#define S2MU106_REG_USB31_EN_SHIFT (6) +#define S2MU106_REG_USB31_EN \ + (0x1 << S2MU106_REG_USB31_EN_SHIFT) /* 0x40 */ + +/* reg 0x26 */ +#define S2MU106_REG_PLUG_CTRL_CC_HOLD_BIT (0x1) + +/* reg 0x27 */ +#define S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN_SHIFT (2) +#define S2MU106_REG_PLUG_CTRL_RpRd_PLUG_SEL_SHIFT (3) +#define S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN_SHIFT (4) +#define S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN_SHIFT (5) +#define S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN_SHIFT (6) +#define S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_SHIFT (7) + +#define S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN \ + (0x1 << S2MU106_REG_PLUG_CTRL_FSM_MANUAL_EN_SHIFT) /* 0x04 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_PLUG_SEL_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_SHIFT) /* 0x88 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_Rp_Source_Mode \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_PLUG_SEL_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_SHIFT) /* 0x88 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_Rd_Sink_Mode \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_SHIFT) /* 0x80 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_MANUAL_EN_SHIFT) /* 0x80 */ +#define S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN \ + (0x1 << S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN_SHIFT) /* 0x10 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN_SHIFT) /* 0x20 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN \ + (0x1 << S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN_SHIFT) /* 0x40 */ +#define S2MU106_REG_PLUG_CTRL_RpRd_VCONN_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_VCONN_MANUAL_EN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_RpRd_CC1_VCONN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_RpRd_CC2_VCONN_SHIFT) /* 0x70 */ + +/* reg 0x28 */ +#define S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN_SHIFT (4) +#define S2MU106_REG_PLUG_CTRL_CC1_MANUAL_EN_SHIFT (5) +#define S2MU106_REG_PLUG_CTRL_CC2_MANUAL_EN_SHIFT (6) + +#define S2MU106_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK (0xf) +#define S2MU106_REG_PLUG_CTRL_FSM_ATTACHED_SNK (2) +#define S2MU106_REG_PLUG_CTRL_FSM_ATTACHED_SRC (6) +#define S2MU106_REG_PLUG_CTRL_FSM_ATTACH_WAIT_SRC (5) +#define S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN \ + (0x1 << S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN_SHIFT) /* 0x10 */ +#define S2MU106_REG_PLUG_CTRL_CC1_MANUAL_ON \ + (0x1 << S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_CC1_MANUAL_EN_SHIFT) /* 0x30 */ +#define S2MU106_REG_PLUG_CTRL_CC2_MANUAL_ON \ + (0x1 << S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_CC2_MANUAL_EN_SHIFT) /* 0x50 */ +#define S2MU106_REG_PLUG_CTRL_CC_MANUAL_MASK \ + (0x1 << S2MU106_REG_PLUG_CTRL_CC_MANUAL_EN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_CC1_MANUAL_EN_SHIFT | \ + 0x1 << S2MU106_REG_PLUG_CTRL_CC2_MANUAL_EN_SHIFT) /* 0x70 */ + +/* reg 0x2E */ +#define S2MU106_REG_PLUG_CTRL_VDM_DISABLE_SHIFT (1) +#define S2MU106_REG_PLUG_CTRL_UFP_ATTACH_OPT_SHIFT (5) +#define S2MU106_REG_PLUG_CTRL_ECO_SRC_CAP_RDY_SHIFT (6) +#define S2MU106_REG_PLUG_CTRL_UFP_ATTACH_OPT \ + (0x1 << S2MU106_REG_PLUG_CTRL_UFP_ATTACH_OPT_SHIFT) /* 0x20 */ + +#define S2MU106_REG_PLUG_CTRL_VDM_DISABLE \ + (0x1 << S2MU106_REG_PLUG_CTRL_VDM_DISABLE_SHIFT) /* 0x02 */ +#define S2MU106_REG_PLUG_CTRL_ECO_SRC_CAP_RDY \ + (0x1 << S2MU106_REG_PLUG_CTRL_ECO_SRC_CAP_RDY_SHIFT) /* 0x80 */ + +/* reg 0x90 (For S2MU106_REG_MSG_SEND_CON) */ +#define S2MU106_REG_MSG_SEND_CON_SEND_MSG_EN_SHIFT (0) +#define S2MU106_REG_MSG_SEND_CON_OP_MODE_SHIFT (1) +#define S2MU106_REG_MSG_SEND_CON_SOP_SHIFT (2) + +#define S2MU106_REG_MSG_SEND_CON_SEND_MSG_EN \ + (0x1 << S2MU106_REG_MSG_SEND_CON_SEND_MSG_EN_SHIFT) /* 0x01 */ +#define S2MU106_REG_MSG_SEND_CON_OP_MODE \ + (0x1 << S2MU106_REG_MSG_SEND_CON_OP_MODE_SHIFT) /* 0x02 */ +#define S2MU106_REG_MSG_SEND_CON_SOP \ + (0x0 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x00 */ +#define S2MU106_REG_MSG_SEND_CON_SOP_Prime \ + (0x1 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x04 */ +#define S2MU106_REG_MSG_SEND_CON_SOP_DPrime \ + (0x2 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x08 */ +#define S2MU106_REG_MSG_SEND_CON_SOP_PDebug \ + (0x3 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x0C */ +#define S2MU106_REG_MSG_SEND_CON_SOP_DPDebug \ + (0x4 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x10 */ +#define S2MU106_REG_MSG_SEND_CON_SOP_HardRST \ + (0x5 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x14 */ +#define S2MU106_REG_MSG_SEND_CON_SOP_CableRST \ + (0x6 << S2MU106_REG_MSG_SEND_CON_SOP_SHIFT) /* 0x18 */ + +/* reg 0xB2 */ +#define S2MU106_PDIC_RID_SHIFT (5) +#define S2MU106_PDIC_RID_MASK (0x7 << S2MU106_PDIC_RID_SHIFT) /* 0xE0 */ + +/* reg 0xB3 */ +#define S2MU106_REG_CTRL_MON_CC1_SHIFT (0) +#define S2MU106_REG_CTRL_MON_CC2_SHIFT (3) +#define S2MU106_REG_CTRL_MON_CC1_MASK \ + (0x7 << S2MU106_REG_CTRL_MON_CC1_SHIFT) /* 0x07 */ +#define S2MU106_REG_CTRL_MON_CC2_MASK \ + (0x7 << S2MU106_REG_CTRL_MON_CC2_SHIFT) /* 0x38 */ + +/* reg 0xB4 */ +#define S2MU106_PDIC_PLUG_ATTACH_DONE_SHIFT (1) +#define S2MU106_PDIC_SINK_SEL_MONITOR_SHIFT (2) +#define S2MU106_PDIC_SOURCE_SEL_MONITOR_SHIFT (3) + +#define S2MU106_PDIC_SINK (1 << S2MU106_PDIC_SINK_SEL_MONITOR_SHIFT \ + | 1 << S2MU106_PDIC_PLUG_ATTACH_DONE_SHIFT) /* 0x06 */ +#define S2MU106_PDIC_SOURCE (1 << S2MU106_PDIC_SOURCE_SEL_MONITOR_SHIFT \ + | 1 << S2MU106_PDIC_PLUG_ATTACH_DONE_SHIFT) /* 0x0A */ +#define S2MU106_PDIC_ATTACH_MASK (1 << S2MU106_PDIC_PLUG_ATTACH_DONE_SHIFT) /* 0x02 */ +#define S2MU106_PR_MASK (S2MU106_PDIC_SINK | S2MU106_PDIC_SOURCE) /* 0x0E */ + +/* reg 0xF7 */ +#define S2MU106_REG_ETC_SOFT_RESET_EN_SHIFT (1) +#define S2MU106_REG_ETC_SOFT_RESET_EN \ + (0x1 << S2MU106_REG_ETC_SOFT_RESET_EN_SHIFT) /* 0x02 */ +#define S2MU106_REG_ETC_SOFT_RESET_DIS \ + (0x0 << S2MU106_REG_ETC_SOFT_RESET_EN_SHIFT) /* 0x00 */ + +/* reg 0xF8 */ +#define S2MU106_REG_ID_MONITOR_MSG_ID_MASK (0x07) + + +/*****************************************/ +/***********DEFINITION INTERRUPT**********/ +/*****************************************/ +#define S2MU106_REG_INT_STATUS0_MSG_ACCEPT (1<<0) +#define S2MU106_REG_INT_STATUS0_MSG_GOODCRC (1<<1) +#define S2MU106_REG_INT_STATUS0_VDM_ATTENTION (1<<2) +#define S2MU106_REG_INT_STATUS0_VDM_EXIT (1<<3) +#define S2MU106_REG_INT_STATUS0_VDM_ENTER (1<<4) +#define S2MU106_REG_INT_STATUS0_VDM_DISCOVER_MODE (1<<5) +#define S2MU106_REG_INT_STATUS0_VDM_DISCOVER_SVID (1<<6) +#define S2MU106_REG_INT_STATUS0_VDM_DISCOVER_ID (1<<7) + +/* reg 0xE1 */ +#define S2MU106_REG_INT_STATUS1_MSG_PING (1<<7) +#define S2MU106_REG_INT_STATUS1_MSG_GOTOMIN (1<<6) +#define S2MU106_REG_INT_STATUS1_MSG_REJECT (1<<5) +#define S2MU106_REG_INT_STATUS1_MSG_PSRDY (1<<4) +#define S2MU106_REG_INT_STATUS1_MSG_GETSRCCAP (1<<3) +#define S2MU106_REG_INT_STATUS1_MSG_GETSNKCAP (1<<2) +#define S2MU106_REG_INT_STATUS1_MSG_DR_SWAP (1<<1) +#define S2MU106_REG_INT_STATUS1_MSG_PR_SWAP (1<<0) + +/* reg 0xE2 */ +#define S2MU106_REG_INT_STATUS2_MSG_VCONN_SWAP (1<<7) +#define S2MU106_REG_INT_STATUS2_MSG_WAIT (1<<6) +#define S2MU106_REG_INT_STATUS2_MSG_SRC_CAP (1<<5) +#define S2MU106_REG_INT_STATUS2_MSG_SNK_CAP (1<<4) +#define S2MU106_REG_INT_STATUS2_MSG_REQUEST (1<<3) +#define S2MU106_REG_INT_STATUS2_MSG_SOFTRESET (1<<2) +#define S2MU106_REG_INT_STATUS2_WAKEUP (1<<0) + +/* reg 0xE3 */ +#define S2MU106_REG_INT_STATUS3_UNS_CMD_DATA (1<<5) + +/* reg 0xE4 */ +#define S2MU106_REG_INT_STATUS4_CC12_DET_IRQ (1<<6) +#define S2MU106_REG_INT_STATUS4_PLUG_IRQ (1<<5) +#define S2MU106_REG_INT_STATUS4_USB_DETACH (1<<4) +#define S2MU106_REG_INT_STATUS4_MSG_PASS (1<<3) +#define S2MU106_REG_INT_STATUS4_MSG_SENT (1<<2) +#define S2MU106_REG_INT_STATUS4_MSG_ERROR (1<<1) + +/* reg 0xE5 */ +#define S2MU106_REG_INT_STATUS5_HARD_RESET (1<<2) + +/* interrupt for checking message */ +#define ENABLED_INT_0 (S2MU106_REG_INT_STATUS0_MSG_ACCEPT |\ + S2MU106_REG_INT_STATUS0_VDM_DISCOVER_ID |\ + S2MU106_REG_INT_STATUS0_VDM_DISCOVER_SVID |\ + S2MU106_REG_INT_STATUS0_VDM_DISCOVER_MODE) +#define ENABLED_INT_1 (S2MU106_REG_INT_STATUS1_MSG_DR_SWAP |\ + S2MU106_REG_INT_STATUS1_MSG_PR_SWAP |\ + S2MU106_REG_INT_STATUS1_MSG_GETSRCCAP |\ + S2MU106_REG_INT_STATUS1_MSG_GETSNKCAP |\ + S2MU106_REG_INT_STATUS1_MSG_REJECT |\ + S2MU106_REG_INT_STATUS1_MSG_PSRDY) +#define ENABLED_INT_2 (S2MU106_REG_INT_STATUS2_MSG_SRC_CAP |\ + S2MU106_REG_INT_STATUS2_MSG_SNK_CAP |\ + S2MU106_REG_INT_STATUS2_MSG_REQUEST |\ + S2MU106_REG_INT_STATUS2_MSG_SOFTRESET |\ + S2MU106_REG_INT_STATUS2_MSG_VCONN_SWAP) +#define ENABLED_INT_2_WAKEUP (S2MU106_REG_INT_STATUS2_MSG_SRC_CAP |\ + S2MU106_REG_INT_STATUS2_MSG_SNK_CAP |\ + S2MU106_REG_INT_STATUS2_MSG_REQUEST |\ + S2MU106_REG_INT_STATUS2_MSG_SOFTRESET |\ + S2MU106_REG_INT_STATUS2_MSG_VCONN_SWAP |\ + S2MU106_REG_INT_STATUS2_WAKEUP) +#define ENABLED_INT_3 S2MU106_REG_INT_STATUS3_UNS_CMD_DATA +#define ENABLED_INT_4 (S2MU106_REG_INT_STATUS4_USB_DETACH |\ + S2MU106_REG_INT_STATUS4_PLUG_IRQ |\ + S2MU106_REG_INT_STATUS4_MSG_PASS) +#define ENABLED_INT_5 (S2MU106_REG_INT_STATUS5_HARD_RESET) + +/* S2MU106 I2C registers */ +enum s2mu106_usbpd_reg { + S2MU106_REG_PD_TRIM = 0x00, + S2MU106_REG_PD_CTRL = 0x01, + S2MU106_REG_PD_CTRL_2 = 0x02, + S2MU106_REG_ANALOG_OTP_04 = 0x04, + S2MU106_REG_ANALOG_OTP_08 = 0x08, + S2MU106_REG_ANALOG_OTP_0A = 0x0A, + S2MU106_REG_PHY_CTRL_IFG = 0x13, + S2MU106_REG_PLUG_CTRL_PORT = 0x18, + S2MU106_REG_PLUG_CTRL_MSG = 0x19, + S2MU106_REG_PLUG_CTRL_VBUS_MUX = 0x1B, + S2MU106_REG_PLUG_CTRL_SET_RD_2 = 0x1C, + S2MU106_REG_PLUG_CTRL_SET_RP_2 = 0x1D, + S2MU106_REG_PLUG_CTRL_SET_RD = 0x1E, + S2MU106_REG_PLUG_CTRL_SET_RP = 0x1F, + S2MU106_REG_PLUG_CTRL_CC_HOLD = 0x26, + S2MU106_REG_PLUG_CTRL_RpRd = 0x27, + S2MU106_REG_PLUG_CTRL_CC12 = 0x28, + S2MU106_REG_PLUG_CTRL = 0x2E, + S2MU106_REG_CTRL = 0x2F, + + S2MU106_REG_INT_MASK0 = 0x3E, + S2MU106_REG_INT_MASK1 = 0x3F, + S2MU106_REG_INT_MASK2 = 0x40, + S2MU106_REG_INT_MASK3 = 0x41, + S2MU106_REG_INT_MASK4 = 0x42, + S2MU106_REG_INT_STATUS0 = 0xE0, + S2MU106_REG_INT_STATUS1 = 0xE1, + S2MU106_REG_INT_STATUS2 = 0xE2, + S2MU106_REG_INT_STATUS3 = 0xE3, + S2MU106_REG_INT_STATUS4 = 0xE4, + S2MU106_REG_ADC_STATUS = 0xB2, + S2MU106_REG_PLUG_MON1 = 0xB3, + S2MU106_REG_PLUG_MON2 = 0xB4, + S2MU106_REG_PLUG_FSM_MON = 0xB7, + + S2MU106_REG_MSG_SEND_CON = 0x90, + S2MU106_REG_MSG_TX_HEADER_L = 0x91, + S2MU106_REG_MSG_TX_HEADER_H = 0x92, + S2MU106_REG_MSG_TX_OBJECT0_0_L = 0x93, + S2MU106_REG_MSG_TX_OBJECT0_0_H = 0x94, + S2MU106_REG_MSG_TX_OBJECT0_1_L = 0x95, + S2MU106_REG_MSG_TX_OBJECT0_1_H = 0x96, + S2MU106_REG_MSG_TX_OBJECT1_0_L = 0x97, + S2MU106_REG_MSG_TX_OBJECT1_0_H = 0x98, + S2MU106_REG_MSG_TX_OBJECT1_1_L = 0x99, + S2MU106_REG_MSG_TX_OBJECT1_1_H = 0x9A, + S2MU106_REG_MSG_TX_OBJECT2_0_L = 0x9B, + S2MU106_REG_MSG_TX_OBJECT2_0_H = 0x9C, + S2MU106_REG_MSG_TX_OBJECT2_1_L = 0x9D, + S2MU106_REG_MSG_TX_OBJECT2_1_H = 0x9E, + S2MU106_REG_MSG_TX_OBJECT3_0_L = 0x9F, + S2MU106_REG_MSG_TX_OBJECT3_0_H = 0xA0, + S2MU106_REG_MSG_TX_OBJECT3_1_L = 0xA1, + S2MU106_REG_MSG_TX_OBJECT3_1_H = 0xA2, + S2MU106_REG_MSG_TX_OBJECT4_0_L = 0xA3, + S2MU106_REG_MSG_TX_OBJECT4_0_H = 0xA4, + S2MU106_REG_MSG_TX_OBJECT4_1_L = 0xA5, + S2MU106_REG_MSG_TX_OBJECT4_1_H = 0xA6, + S2MU106_REG_MSG_TX_OBJECT5_0_L = 0xA7, + S2MU106_REG_MSG_TX_OBJECT5_0_H = 0xA8, + S2MU106_REG_MSG_TX_OBJECT5_1_L = 0xA9, + S2MU106_REG_MSG_TX_OBJECT5_1_H = 0xAA, + S2MU106_REG_MSG_TX_OBJECT6_0_L = 0xAB, + S2MU106_REG_MSG_TX_OBJECT6_0_H = 0xAC, + S2MU106_REG_MSG_TX_OBJECT6_1_L = 0xAD, + S2MU106_REG_MSG_TX_OBJECT6_1_H = 0xAE, + + S2MU106_REG_MSG_RX_HEADER_L = 0xC1, + S2MU106_REG_MSG_RX_HEADER_H = 0xC2, + S2MU106_REG_MSG_RX_OBJECT0_0_L = 0xC3, + S2MU106_REG_MSG_RX_OBJECT0_0_H = 0xC4, + S2MU106_REG_MSG_RX_OBJECT0_1_L = 0xC5, + S2MU106_REG_MSG_RX_OBJECT0_1_H = 0xC6, + S2MU106_REG_MSG_RX_OBJECT1_0_L = 0xC7, + S2MU106_REG_MSG_RX_OBJECT1_0_H = 0xC8, + S2MU106_REG_MSG_RX_OBJECT1_1_L = 0xC9, + S2MU106_REG_MSG_RX_OBJECT1_1_H = 0xCA, + S2MU106_REG_MSG_RX_OBJECT2_0_L = 0xCB, + S2MU106_REG_MSG_RX_OBJECT2_0_H = 0xCC, + S2MU106_REG_MSG_RX_OBJECT2_1_L = 0xCD, + S2MU106_REG_MSG_RX_OBJECT2_1_H = 0xCE, + S2MU106_REG_MSG_RX_OBJECT3_0_L = 0xCF, + S2MU106_REG_MSG_RX_OBJECT3_0_H = 0xD0, + S2MU106_REG_MSG_RX_OBJECT3_1_L = 0xD1, + S2MU106_REG_MSG_RX_OBJECT3_1_H = 0xD2, + S2MU106_REG_MSG_RX_OBJECT4_0_L = 0xD3, + S2MU106_REG_MSG_RX_OBJECT4_0_H = 0xD4, + S2MU106_REG_MSG_RX_OBJECT4_1_L = 0xD5, + S2MU106_REG_MSG_RX_OBJECT4_1_H = 0xD6, + S2MU106_REG_MSG_RX_OBJECT5_0_L = 0xD7, + S2MU106_REG_MSG_RX_OBJECT5_0_H = 0xD8, + S2MU106_REG_MSG_RX_OBJECT5_1_L = 0xD9, + S2MU106_REG_MSG_RX_OBJECT5_1_H = 0xDA, + S2MU106_REG_MSG_RX_OBJECT6_0_L = 0xDB, + S2MU106_REG_MSG_RX_OBJECT6_0_H = 0xDC, + S2MU106_REG_MSG_RX_OBJECT6_1_L = 0xDD, + S2MU106_REG_MSG_RX_OBJECT6_1_H = 0xDE, + + S2MU106_REG_ETC = 0xF7, + S2MU106_REG_ID_MONITOR = 0xF8 +}; + +typedef enum { + S2MU106_THRESHOLD_128MV = 2, + S2MU106_THRESHOLD_171MV = 3, + S2MU106_THRESHOLD_214MV = 4, + S2MU106_THRESHOLD_257MV = 5, + S2MU106_THRESHOLD_300MV = 6, + S2MU106_THRESHOLD_342MV = 7, + S2MU106_THRESHOLD_385MV = 8, + S2MU106_THRESHOLD_428MV = 9, + S2MU106_THRESHOLD_450MV = 10, + S2MU106_THRESHOLD_471MV = 11, + S2MU106_THRESHOLD_492MV = 12, + S2MU106_THRESHOLD_514MV = 13, + S2MU106_THRESHOLD_535MV = 14, + S2MU106_THRESHOLD_557MV = 15, + S2MU106_THRESHOLD_578MV = 16, + S2MU106_THRESHOLD_600MV = 17, + S2MU106_THRESHOLD_621MV = 18, + S2MU106_THRESHOLD_642MV = 19, + S2MU106_THRESHOLD_685MV = 20, + S2MU106_THRESHOLD_1000MV = 27, + + S2MU106_THRESHOLD_1200MV = 32, + S2MU106_THRESHOLD_1242MV = 33, + S2MU106_THRESHOLD_1285MV = 34, + S2MU106_THRESHOLD_1328MV = 35, + S2MU106_THRESHOLD_1371MV = 36, + S2MU106_THRESHOLD_1414MV = 37, + S2MU106_THRESHOLD_1457MV = 38, + S2MU106_THRESHOLD_1500MV = 39, + S2MU106_THRESHOLD_1542MV = 40, + S2MU106_THRESHOLD_1587MV = 41, + S2MU106_THRESHOLD_1628MV = 42, + S2MU106_THRESHOLD_1671MV = 43, + S2MU106_THRESHOLD_1714MV = 44, + S2MU106_THRESHOLD_1757MV = 45, + S2MU106_THRESHOLD_1799MV = 46, + S2MU106_THRESHOLD_1842MV = 47, + S2MU106_THRESHOLD_1885MV = 48, + S2MU106_THRESHOLD_1928MV = 49, + S2MU106_THRESHOLD_1971MV = 50, + S2MU106_THRESHOLD_2014MV = 51, + S2MU106_THRESHOLD_2057MV = 52, + S2MU106_THRESHOLD_2099MV = 53, + S2MU106_THRESHOLD_2142MV = 54, + S2MU106_THRESHOLD_2185MV = 55, + S2MU106_THRESHOLD_2228MV = 56, + S2MU106_THRESHOLD_2271MV = 57, + + S2MU106_THRESHOLD_MAX = 63 +} CCIC_THRESHOLD_SEL; + +typedef enum { + S2MU106_CC_OCP_255MV = 0, + S2MU106_CC_OCP_262MV = 1, + S2MU106_CC_OCP_273MV = 2, + S2MU106_CC_OCP_282MV = 3, + S2MU106_CC_OCP_301MV = 4, + S2MU106_CC_OCP_311MV = 5, + S2MU106_CC_OCP_327MV = 6, + S2MU106_CC_OCP_339MV = 7, + S2MU106_CC_OCP_375MV = 8, + S2MU106_CC_OCP_390MV = 9, + S2MU106_CC_OCP_415MV = 10, + S2MU106_CC_OCP_433MV = 11, + S2MU106_CC_OCP_478MV = 12, + S2MU106_CC_OCP_502MV = 13, + S2MU106_CC_OCP_542MV = 14, + S2MU106_CC_OCP_575MV = 15, + + S2MU106_CC_OCP_MAX = 16 +} CCIC_CC_OCP_SEL; + +typedef enum { + S2MU106_PHY_IFG_25US = 0, + S2MU106_PHY_IFG_30US = 1, + S2MU106_PHY_IFG_35US = 2, +} CCIC_PHY_IFG_SEL; + +enum s2mu106_power_role { + PDIC_SINK, + PDIC_SOURCE +}; + +enum s2mu106_pdic_rid { + REG_RID_UNDF = 0x00, + REG_RID_255K = 0x03, + REG_RID_301K = 0x04, + REG_RID_523K = 0x05, + REG_RID_619K = 0x06, + REG_RID_OPEN = 0x07, + REG_RID_MAX = 0x08, +}; + +typedef enum { + CLIENT_OFF = 0, + CLIENT_ON = 1, +} CCIC_DEVICE_REASON; + +typedef enum { + HOST_OFF = 0, + HOST_ON = 1, +} CCIC_HOST_REASON; + +typedef enum { + VBUS_OFF = 0, + VBUS_ON = 1, +} CCIC_VBUS_SEL; + +typedef enum { + DET_HARD_RESET = 0, + DET_DETACH = 1, +} CCIC_DETACH_TYPE; + +typedef enum { + PLUG_CTRL_RP0 = 0, + PLUG_CTRL_RP80 = 1, + PLUG_CTRL_RP180 = 2, + PLUG_CTRL_RP330 = 3 +} CCIC_RP_SCR_SEL; + +typedef enum { + TYPE_C_DETACH = 0, + TYPE_C_ATTACH_DFP = 1, /* Host */ + TYPE_C_ATTACH_UFP = 2, /* Device */ + TYPE_C_ATTACH_DRP = 3, /* Dual role */ +} CCIC_OTP_MODE; + +typedef enum { + PLUG_CTRL_RD = 0, + PLUG_CTRL_RP = 1, +} CCIC_RP_RD_SEL; + +typedef enum { + PD_LPM_MODE = 0, + PD_NORMAL_MODE = 1, +} CCIC_LPM_MODE_SEL; + +typedef enum { + VBUS_WAKEUP_ENABLE = 0, + VBUS_WAKEUP_DISABLE = 1, +} CCIC_VBUS_WAKEUP_SEL; + +#if defined(CONFIG_CCIC_NOTIFIER) +struct ccic_state_work { + struct work_struct ccic_work; + int dest; + int id; + int attach; + int event; +}; +#endif + +#if defined(CONFIG_IFCONN_NOTIFIER) +struct ifconn_state_work { + struct work_struct ifconn_work; + int dest; + int id; + int event; + void *data; +}; +#endif + +struct s2mu106_usbpd_data { + struct device *dev; + struct i2c_client *i2c; +#if defined(CONFIG_CCIC_NOTIFIER) + struct workqueue_struct *ccic_wq; +#endif +#if defined(CONFIG_IFCONN_NOTIFIER) + struct workqueue_struct *ifconn_wq; +#endif + struct mutex _mutex; + struct mutex poll_mutex; + struct mutex lpm_mutex; + struct mutex cc_mutex; + int vconn_en; + int regulator_en; + int irq_gpio; + int irq; + int power_role; + int data_role; + int vconn_source; + msg_header_type header; + data_obj_type obj[S2MU106_MAX_NUM_MSG_OBJ]; + u64 status_reg; + bool lpm_mode; + bool detach_valid; + bool is_factory_mode; + bool is_water_detect; + bool is_muic_water_detect; + bool is_otg_vboost; + bool is_otg_reboost; + bool is_pr_swap; + bool is_muic_attached; + bool vbus_short_check; + bool vbus_short; +#ifndef CONFIG_SEC_FACTORY + bool lpcharge_water; +#endif + int check_rid_wa; + + int vbus_short_check_cnt; + int water_detect_cnt; + int check_msg_pass; + int rid; + int is_host; + int is_client; + int data_role_dual; /* data_role for dual role swap */ + int power_role_dual; /* power_role for dual role swap */ + int is_attached; + int usb_sw_sel; + + int dp_is_connect; + +#if defined(CONFIG_DUAL_ROLE_USB_INTF) + struct dual_role_phy_instance *dual_role; + struct dual_role_phy_desc *desc; + struct completion reverse_completion; + int try_state_change; + struct delayed_work role_swap_work; +#endif + struct notifier_block type3_nb; + struct workqueue_struct *pdic_queue; + struct delayed_work plug_work; + struct delayed_work water_detect_handler; + struct delayed_work ta_water_detect_handler; + struct delayed_work water_dry_handler; + + struct power_supply_desc ccic_desc; + struct power_supply *psy_ccic; + + struct regulator *regulator; + + /* struct wakeup_source wakeup_src; */ + struct wake_lock wake_lock; +}; + +extern int s2mu106_usbpd_get_adc(void); +extern void s2mu106_usbpd_set_muic_type(int type); +#if defined(CONFIG_CCIC_NOTIFIER) +extern void s2mu106_control_option_command(struct s2mu106_usbpd_data *usbpd_data, int cmd); +#endif +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +extern void s2mu106_rprd_mode_change(struct s2mu106_usbpd_data *usbpd_data, u8 mode); +#endif +extern void vbus_turn_on_ctrl(struct s2mu106_usbpd_data *usbpd_data, bool enable); +extern int s2mu106_set_lpm_mode(struct s2mu106_usbpd_data *pdic_data); +extern int s2mu106_set_normal_mode(struct s2mu106_usbpd_data *pdic_data); +#endif /* __USBPD_S2MU106_H__ */ diff --git a/include/linux/ccic/usbpd.h b/include/linux/ccic/usbpd.h new file mode 100644 index 000000000000..50cb6467530c --- /dev/null +++ b/include/linux/ccic/usbpd.h @@ -0,0 +1,533 @@ +#ifndef __USBPD_H__ +#define __USBPD_H__ + +#include +#include +#ifdef CONFIG_IFCONN_NOTIFIER +#include +#endif + +#define MAX_CHARGING_VOLT 12000 /* 12V */ +#define USBPD_VOLT_UNIT 50 /* 50mV */ +#define USBPD_CURRENT_UNIT 10 /* 10mA */ + +#define USBPD_MAX_COUNT_MSG_OBJECT (8) /* 0..7 */ + +/* Counter */ +#define USBPD_nMessageIDCount (7) +#define USBPD_nRetryCount (3) +#define USBPD_nHardResetCount (4) +#define USBPD_nCapsCount (16) +#define USBPD_nDiscoverIdentityCount (20) + +/* Timer */ +#define tSrcTransition (35) /* 25~35 ms */ +#define tPSSourceOn (480) /* 390~480 ms */ +#define tPSSourceOff (750) /* 750~960 ms */ +#define tSenderResponse (1000) /* 1000 ms */ +#define tSenderResponseSRC (300) /* 1000 ms */ +#define tSendSourceCap (10) /* 1~2 s */ +#define tPSHardReset (25) /* 25~35 ms */ +#define tSinkWaitCap (2500) /* 2.1~2.5 s */ +#define tPSTransition (5500) /* 450~550 ms */ +#define tVCONNSourceOn (100) /* 24~30 ms */ +#define tVDMSenderResponse (50) /* 24~30 ms */ +#define tVDMWaitModeEntry (50) /* 40~50 ms */ +#define tVDMWaitModeExit (50) /* 40~50 ms */ +#define tDiscoverIdentity (50) /* 40~50 ms */ +#define tSwapSourceStart (20) /* 20 ms */ +#define tTypeCSinkWaitCap (310) /* 310~620 ms */ + +/* Protocol States */ +typedef enum { + /* Rx */ + PRL_Rx_Layer_Reset_for_Receive = 0x11, + PRL_Rx_Wait_for_PHY_Message = 0x12, + PRL_Rx_Send_GoodCRC = 0x13, + PRL_Rx_Store_MessageID = 0x14, + PRL_Rx_Check_MessageID = 0x15, + + /* Tx */ + PRL_Tx_PHY_Layer_Reset = 0x21, + PRL_Tx_Wait_for_Message_Request = 0x22, + PRL_Tx_Layer_Reset_for_Transmit = 0x23, + PRL_Tx_Construct_Message = 0x24, + PRL_Tx_Wait_for_PHY_Response = 0x25, + PRL_Tx_Match_MessageID = 0x26, + PRL_Tx_Message_Sent = 0x27, + PRL_Tx_Check_RetryCounter = 0x28, + PRL_Tx_Transmission_Error = 0x29, + PRL_Tx_Discard_Message = 0x2A, +} protocol_state; + +/* Policy Engine States */ +typedef enum { + /* Source */ + PE_SRC_Startup = 0x30, + PE_SRC_Discovery = 0x31, + PE_SRC_Send_Capabilities = 0x32, + PE_SRC_Negotiate_Capability = 0x33, + PE_SRC_Transition_Supply = 0x34, + PE_SRC_Ready = 0x35, + PE_SRC_Disabled = 0x36, + PE_SRC_Capability_Response = 0x37, + PE_SRC_Hard_Reset = 0x38, + PE_SRC_Hard_Reset_Received = 0x39, + PE_SRC_Transition_to_default = 0x3A, + PE_SRC_Give_Source_Cap = 0x3B, + PE_SRC_Get_Sink_Cap = 0x3C, + PE_SRC_Wait_New_Capabilities = 0x3D, + + /* Sink */ + PE_SNK_Startup = 0x40, + PE_SNK_Discovery = 0x41, + PE_SNK_Wait_for_Capabilities = 0x42, + PE_SNK_Evaluate_Capability = 0x43, + PE_SNK_Select_Capability = 0x44, + PE_SNK_Transition_Sink = 0x45, + PE_SNK_Ready = 0x46, + PE_SNK_Hard_Reset = 0x47, + PE_SNK_Transition_to_default = 0x48, + PE_SNK_Give_Sink_Cap = 0x49, + PE_SNK_Get_Source_Cap = 0x4A, + + /* Source Soft Reset */ + PE_SRC_Send_Soft_Reset = 0x50, + PE_SRC_Soft_Reset = 0x51, + + /* Sink Soft Reset */ + PE_SNK_Send_Soft_Reset = 0x60, + PE_SNK_Soft_Reset = 0x61, + + /* UFP VDM */ + PE_UFP_VDM_Get_Identity = 0x70, + PE_UFP_VDM_Send_Identity = 0x71, + PE_UFP_VDM_Get_Identity_NAK = 0x72, + PE_UFP_VDM_Get_SVIDs = 0x73, + PE_UFP_VDM_Send_SVIDs = 0x74, + PE_UFP_VDM_Get_SVIDs_NAK = 0x75, + PE_UFP_VDM_Get_Modes = 0x76, + PE_UFP_VDM_Send_Modes = 0x77, + PE_UFP_VDM_Get_Modes_NAK = 0x78, + PE_UFP_VDM_Evaluate_Mode_Entry = 0x79, + PE_UFP_VDM_Mode_Entry_ACK = 0x7A, + PE_UFP_VDM_Mode_Entry_NAK = 0x7B, + PE_UFP_VDM_Mode_Exit = 0x7C, + PE_UFP_VDM_Mode_Exit_ACK = 0x7D, + PE_UFP_VDM_Mode_Exit_NAK = 0x7E, + PE_UFP_VDM_Attention_Request = 0x7F, + PE_UFP_VDM_Evaluate_Status = 0x80, + PE_UFP_VDM_Status_ACK = 0x81, + PE_UFP_VDM_Status_NAK = 0x82, + PE_UFP_VDM_Evaluate_Configure = 0x83, + PE_UFP_VDM_Configure_ACK = 0x84, + PE_UFP_VDM_Configure_NAK = 0x85, + + /* DFP VDM */ + PE_DFP_VDM_Identity_Request = 0x8A, + PE_DFP_VDM_Identity_ACKed = 0x8B, + PE_DFP_VDM_Identity_NAKed = 0x8C, + PE_DFP_VDM_SVIDs_Request = 0x8D, + PE_DFP_VDM_SVIDs_ACKed = 0x8E, + PE_DFP_VDM_SVIDs_NAKed = 0x8F, + PE_DFP_VDM_Modes_Request = 0x90, + PE_DFP_VDM_Modes_ACKed = 0x91, + PE_DFP_VDM_Modes_NAKed = 0x92, + PE_DFP_VDM_Mode_Entry_Request = 0x93, + PE_DFP_VDM_Mode_Entry_ACKed = 0x94, + PE_DFP_VDM_Mode_Entry_NAKed = 0x95, + PE_DFP_VDM_Mode_Exit_Request = 0x96, + PE_DFP_VDM_Mode_Exit_ACKed = 0x97, + PE_DFP_VDM_Mode_Exit_NAKed = 0x98, + PE_DFP_VDM_Status_Update = 0x99, + PE_DFP_VDM_Status_Update_ACKed = 0x9A, + PE_DFP_VDM_Status_Update_NAKed = 0x9B, + PE_DFP_VDM_DisplayPort_Configure = 0x9C, + PE_DFP_VDM_DisplayPort_Configure_ACKed = 0x9D, + PE_DFP_VDM_DisplayPort_Configure_NAKed = 0x9E, + PE_DFP_VDM_Attention_Request = 0x9F, + + /* Power Role Swap */ + PE_PRS_SRC_SNK_Reject_PR_Swap = 0xA0, + PE_PRS_SRC_SNK_Evaluate_Swap = 0xA1, + PE_PRS_SRC_SNK_Send_Swap = 0xA2, + PE_PRS_SRC_SNK_Accept_Swap = 0xA3, + PE_PRS_SRC_SNK_Transition_off = 0xA4, + PE_PRS_SRC_SNK_Assert_Rd = 0xA5, + PE_PRS_SRC_SNK_Wait_Source_on = 0xA6, + PE_PRS_SNK_SRC_Reject_Swap = 0xA7, + PE_PRS_SNK_SRC_Evaluate_Swap = 0xA8, + PE_PRS_SNK_SRC_Send_Swap = 0xA9, + PE_PRS_SNK_SRC_Accept_Swap = 0xAA, + PE_PRS_SNK_SRC_Transition_off = 0xAB, + PE_PRS_SNK_SRC_Assert_Rp = 0xAC, + PE_PRS_SNK_SRC_Source_on = 0xAD, + + /* Data Role Swap */ + PE_DRS_DFP_UFP_Evaluate_DR_Swap = 0xAE, + PE_DRS_DFP_UFP_Accept_DR_Swap = 0xAF, + PE_DRS_DFP_UFP_Change_to_UFP = 0xB0, + PE_DRS_DFP_UFP_Send_DR_Swap = 0xB1, + PE_DRS_DFP_UFP_Reject_DR_Swap = 0xB2, + PE_DRS_UFP_DFP_Evaluate_DR_Swap = 0xB3, + PE_DRS_UFP_DFP_Accept_DR_Swap = 0xB4, + PE_DRS_UFP_DFP_Change_to_DFP = 0xB5, + PE_DRS_UFP_DFP_Send_DR_Swap = 0xB6, + PE_DRS_UFP_DFP_Reject_DR_Swap = 0xB7, + PE_DRS_Evaluate_Port = 0xB8, + PE_DRS_Evaluate_Send_Port = 0xB9, + + /* Vconn Source Swap */ + PE_VCS_Evaluate_Swap = 0xC0, + PE_VCS_Accept_Swap = 0xC1, + PE_VCS_Wait_for_VCONN = 0xC2, + PE_VCS_Turn_Off_VCONN = 0xC3, + PE_VCS_Turn_On_VCONN = 0xC4, + PE_VCS_Send_PS_RDY = 0xC5, + PE_VCS_Send_Swap = 0xC6, + PE_VCS_Reject_VCONN_Swap = 0xC7, + + /* UVDM Message */ + PE_DFP_UVDM_Send_Message = 0xD0, + PE_DFP_UVDM_Receive_Message = 0xD1, + + Error_Recovery = 0xFF +} policy_state; + +typedef enum usbpd_manager_command { + MANAGER_REQ_GET_SNKCAP = 1, + MANAGER_REQ_GOTOMIN = 1 << 2, + MANAGER_REQ_SRCCAP_CHANGE = 1 << 3, + MANAGER_REQ_PR_SWAP = 1 << 4, + MANAGER_REQ_DR_SWAP = 1 << 5, + MANAGER_REQ_VCONN_SWAP = 1 << 6, + MANAGER_REQ_VDM_DISCOVER_IDENTITY = 1 << 7, + MANAGER_REQ_VDM_DISCOVER_SVID = 1 << 8, + MANAGER_REQ_VDM_DISCOVER_MODE = 1 << 9, + MANAGER_REQ_VDM_ENTER_MODE = 1 << 10, + MANAGER_REQ_VDM_EXIT_MODE = 1 << 11, + MANAGER_REQ_VDM_ATTENTION = 1 << 12, + MANAGER_REQ_VDM_STATUS_UPDATE = 1 << 13, + MANAGER_REQ_VDM_DisplayPort_Configure = 1 << 14, + MANAGER_REQ_NEW_POWER_SRC = 1 << 15, + MANAGER_REQ_UVDM_SEND_MESSAGE = 1 << 16, + MANAGER_REQ_UVDM_RECEIVE_MESSAGE = 1 << 17, +} usbpd_manager_command_type; + +typedef enum usbpd_manager_event { + MANAGER_DISCOVER_IDENTITY_ACKED = 0, + MANAGER_DISCOVER_IDENTITY_NAKED = 1, + MANAGER_DISCOVER_SVID_ACKED = 2, + MANAGER_DISCOVER_SVID_NAKED = 3, + MANAGER_DISCOVER_MODE_ACKED = 4, + MANAGER_DISCOVER_MODE_NAKED = 5, + MANAGER_ENTER_MODE_ACKED = 6, + MANAGER_ENTER_MODE_NAKED = 7, + MANAGER_EXIT_MODE_ACKED = 8, + MANAGER_EXIT_MODE_NAKED = 9, + MANAGER_ATTENTION_REQUEST = 10, + MANAGER_STATUS_UPDATE_ACKED = 11, + MANAGER_STATUS_UPDATE_NAKED = 12, + MANAGER_DisplayPort_Configure_ACKED = 13, + MANAGER_DisplayPort_Configure_NACKED = 14, + MANAGER_NEW_POWER_SRC = 15, + MANAGER_UVDM_SEND_MESSAGE = 16, + MANAGER_UVDM_RECEIVE_MESSAGE = 17, + MANAGER_START_DISCOVER_IDENTITY = 18, +} usbpd_manager_event_type; + +enum usbpd_msg_status { + MSG_GOODCRC = 1<<0, + MSG_ACCEPT = 1<<1, + MSG_PSRDY = 1<<2, + MSG_REQUEST = 1<<3, + MSG_REJECT = 1<<4, + MSG_WAIT = 1<<5, + MSG_ERROR = 1<<6, + MSG_PING = 1<<7, + MSG_GET_SNK_CAP = 1<<8, + MSG_GET_SRC_CAP = 1<<9, + MSG_SNK_CAP = 1<<10, + MSG_SRC_CAP = 1<<11, + MSG_PR_SWAP = 1<<12, + MSG_DR_SWAP = 1<<13, + MSG_VCONN_SWAP = 1<<14, + VDM_DISCOVER_IDENTITY = 1<<15, + VDM_DISCOVER_SVID = 1<<16, + VDM_DISCOVER_MODE = 1<<17, + VDM_ENTER_MODE = 1<<18, + VDM_EXIT_MODE = 1<<19, + VDM_ATTENTION = 1<<20, + VDM_DP_STATUS_UPDATE = 1<<21, + VDM_DP_CONFIGURE = 1<<22, + MSG_SOFTRESET = 1<<23, + PLUG_DETACH = 1<<24, + PLUG_ATTACH = 1<<25, + MSG_HARDRESET = 1<<26, + CC_DETECT = 1<<27, + UVDM_MSG = 1<<28, + MSG_PASS = 1<<29, + MSG_RID = 1<<30, + MSG_NONE = 1<<31, +}; + +/* Timer */ +enum usbpd_timer_id { + DISCOVER_IDENTITY_TIMER = 1, + HARD_RESET_COMPLETE_TIMER = 2, + NO_RESPONSE_TIMER = 3, + PS_HARD_RESET_TIMER = 4, + PS_SOURCE_OFF_TIMER = 5, + PS_SOURCE_ON_TIMER = 6, + PS_TRANSITION_TIMER = 7, + SENDER_RESPONSE_TIMER = 8, + SINK_ACTIVITY_TIMER = 9, + SINK_REQUEST_TIMER = 10, + SINK_WAIT_CAP_TIMER = 11, + SOURCE_ACTIVITY_TIMER = 12, + SOURCE_CAPABILITY_TIMER = 13, + SWAP_RECOVERY_TIMER = 14, + SWAP_SOURCE_START_TIMER = 15, + VCONN_ON_TIMER = 16, + VDM_MODE_ENTRY_TIMER = 17, + VDM_MODE_EXIT_TIMER = 18, + VDM_RESPONSE_TIMER = 19, + USBPD_TIMER_MAX_COUNT +}; + +enum usbpd_protocol_status { + DEFAULT_PROTOCOL_NONE = 0, + MESSAGE_SENT = 1, + TRANSMISSION_ERROR = 2 +}; + +enum usbpd_policy_informed { + DEFAULT_POLICY_NONE = 0, + HARDRESET_RECEIVED = 1, + SOFTRESET_RECEIVED = 2, + PLUG_EVENT = 3, + PLUG_ATTACHED = 4, + PLUG_DETACHED = 5, +}; + +typedef struct usbpd_phy_ops { + /* 1st param should be 'usbpd_data *' */ + int (*tx_msg)(void *, msg_header_type *, data_obj_type *); + int (*rx_msg)(void *, msg_header_type *, data_obj_type *); + int (*hard_reset)(void *); + int (*set_power_role)(void *, int); + int (*get_power_role)(void *, int *); + int (*set_data_role)(void *, int); + int (*get_data_role)(void *, int *); + int (*set_vconn_source)(void *, int); + int (*get_vconn_source)(void *, int *); + int (*set_check_msg_pass)(void *, int); + unsigned (*get_status)(void *, unsigned); + bool (*poll_status)(void *); + void (*driver_reset)(void *); + int (*set_otg_control)(void *, int); + void (*get_vbus_short_check)(void *, bool *); + int (*set_cc_control)(void *, int); + int (*get_side_check)(void *); +} usbpd_phy_ops_type; + +struct policy_data { + policy_state state; + msg_header_type tx_msg_header; + msg_header_type rx_msg_header; + data_obj_type tx_data_obj[USBPD_MAX_COUNT_MSG_OBJECT]; + data_obj_type rx_data_obj[USBPD_MAX_COUNT_MSG_OBJECT]; + bool rx_hardreset; + bool rx_softreset; + bool plug; + bool plug_valid; + bool modal_operation; + bool abnormal_state; + bool sink_cap_received; + bool send_sink_cap; +}; + +struct protocol_data { + protocol_state state; + unsigned stored_message_id; + msg_header_type msg_header; + data_obj_type data_obj[USBPD_MAX_COUNT_MSG_OBJECT]; + unsigned status; +}; + +struct usbpd_counter { + unsigned retry_counter; + unsigned message_id_counter; + unsigned caps_counter; + unsigned hard_reset_counter; + unsigned discover_identity_counter; + unsigned swap_hard_reset_counter; +}; + +struct usbpd_manager_data { + usbpd_manager_command_type cmd; /* request to policy engine */ + usbpd_manager_event_type event; /* policy engine infromed */ + + msg_header_type uvdm_msg_header; + data_obj_type uvdm_data_obj[USBPD_MAX_COUNT_MSG_OBJECT]; +#if defined(CONFIG_IFCONN_NOTIFIER) + struct ifconn_notifier_template template; +#endif + int alt_sended; + int vdm_en; + /* request */ + int max_power; + int op_power; + int max_current; + int op_current; + int min_current; + bool giveback; + bool usb_com_capable; + bool no_usb_suspend; + + /* source */ + int source_max_volt; + int source_min_volt; + int source_max_power; + + /* sink */ + int sink_max_volt; + int sink_min_volt; + int sink_max_power; + + /* sink cap */ + int sink_cap_max_volt; + + /* power role swap*/ + bool power_role_swap; + /* data role swap*/ + bool data_role_swap; + bool vconn_source_swap; + bool vbus_short; + + bool is_samsung_accessory_enter_mode; + bool uvdm_first_req; + bool uvdm_dir; + struct completion uvdm_out_wait; + struct completion uvdm_in_wait; + + uint16_t Vendor_ID; + uint16_t Product_ID; + uint16_t Device_Version; + int acc_type; + uint16_t SVID_0; + uint16_t SVID_1; + uint16_t Standard_Vendor_ID; + + /* dp */ + int dp_selected_pin; + int hpd; + int hpdirq; + int dp_is_connect; + int dp_hs_connect; + int pin_assignment; + int is_sent_pin_configuration; + + struct mutex vdm_mutex; + + struct usbpd_data *pd_data; + struct delayed_work acc_detach_handler; + struct delayed_work select_pdo_handler; + struct delayed_work start_discover_msg_handler; + muic_attached_dev_t attached_dev; +}; + +struct usbpd_data { + struct device *dev; + struct device *ccic_dev; + void *phy_driver_data; + struct usbpd_counter counter; + struct hrtimer timers[USBPD_TIMER_MAX_COUNT]; + unsigned expired_timers; + usbpd_phy_ops_type phy_ops; + struct protocol_data protocol_tx; + struct protocol_data protocol_rx; + struct policy_data policy; + msg_header_type source_msg_header; + data_obj_type source_data_obj; + msg_header_type sink_msg_header; + data_obj_type sink_data_obj[2]; + data_obj_type source_request_obj; + struct usbpd_manager_data manager; + struct work_struct worker; + struct completion msg_arrived; + unsigned wait_for_msg_arrived; +}; + +static inline struct usbpd_data *protocol_rx_to_usbpd(struct protocol_data *rx) +{ + return container_of(rx, struct usbpd_data, protocol_rx); +} + +static inline struct usbpd_data *protocol_tx_to_usbpd(struct protocol_data *tx) +{ + return container_of(tx, struct usbpd_data, protocol_tx); +} + +static inline struct usbpd_data *policy_to_usbpd(struct policy_data *policy) +{ + return container_of(policy, struct usbpd_data, policy); +} + +static inline struct usbpd_data *manager_to_usbpd(struct usbpd_manager_data *manager) +{ + return container_of(manager, struct usbpd_data, manager); +} + +extern int usbpd_init(struct device *dev, void *phy_driver_data); +extern void usbpd_init_policy(struct usbpd_data *); + +extern void usbpd_init_manager_val(struct usbpd_data *); +extern int usbpd_init_manager(struct usbpd_data *); +extern void usbpd_manager_plug_attach(struct device *); +extern void usbpd_manager_plug_detach(struct device *dev, bool notify); +extern void usbpd_manager_acc_detach(struct device *dev); +extern int usbpd_manager_match_request(struct usbpd_data *); +extern bool usbpd_manager_power_role_swap(struct usbpd_data *); +extern bool usbpd_manager_vconn_source_swap(struct usbpd_data *); +extern void usbpd_manager_turn_on_source(struct usbpd_data *); +extern void usbpd_manager_turn_off_power_supply(struct usbpd_data *); +extern void usbpd_manager_turn_off_power_sink(struct usbpd_data *); +extern void usbpd_manager_turn_off_vconn(struct usbpd_data *); +extern bool usbpd_manager_data_role_swap(struct usbpd_data *); +extern int usbpd_manager_get_identity(struct usbpd_data *); +extern int usbpd_manager_get_svids(struct usbpd_data *); +extern int usbpd_manager_get_modes(struct usbpd_data *); +extern int usbpd_manager_enter_mode(struct usbpd_data *); +extern int usbpd_manager_exit_mode(struct usbpd_data *, unsigned mode); +extern int usbpd_manager_get_status(struct usbpd_data *pd_data); +extern int usbpd_manager_get_configure(struct usbpd_data *pd_data); +extern int usbpd_manager_get_attention(struct usbpd_data *pd_data); +extern void usbpd_dp_detach(struct usbpd_data *pd_data); + +extern void usbpd_manager_inform_event(struct usbpd_data *, + usbpd_manager_event_type); +extern int usbpd_manager_evaluate_capability(struct usbpd_data *); +extern data_obj_type usbpd_manager_select_capability(struct usbpd_data *); +extern bool usbpd_manager_vdm_request_enabled(struct usbpd_data *); +extern void usbpd_manager_acc_handler_cancel(struct device *); +extern void usbpd_manager_acc_detach_handler(struct work_struct *); +extern void usbpd_policy_work(struct work_struct *); +extern void usbpd_protocol_tx(struct usbpd_data *); +extern void usbpd_protocol_rx(struct usbpd_data *); +extern void usbpd_kick_policy_work(struct device *); +extern void usbpd_rx_hard_reset(struct device *); +extern void usbpd_rx_soft_reset(struct usbpd_data *); +extern void usbpd_policy_reset(struct usbpd_data *, unsigned flag); + +extern void usbpd_set_ops(struct device *, usbpd_phy_ops_type *); +extern void usbpd_read_msg(struct usbpd_data *); +extern bool usbpd_send_msg(struct usbpd_data *, msg_header_type *, + data_obj_type *); +extern bool usbpd_send_ctrl_msg(struct usbpd_data *d, msg_header_type *h, + unsigned msg, unsigned dr, unsigned pr); +extern unsigned usbpd_wait_msg(struct usbpd_data *pd_data, unsigned msg_status, + unsigned ms); +extern void usbpd_reinit(struct device *); +extern void usbpd_init_protocol(struct usbpd_data *); +#endif diff --git a/include/linux/ccic/usbpd_ext.h b/include/linux/ccic/usbpd_ext.h new file mode 100644 index 000000000000..ef0e5a2ab442 --- /dev/null +++ b/include/linux/ccic/usbpd_ext.h @@ -0,0 +1,119 @@ +#if defined(CONFIG_CCIC_NOTIFIER) +#include +#endif +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +#include +#endif + + +#ifndef __USBPD_EXT_H__ +#define __USBPD_EXT_H__ + +#ifdef CONFIG_IFCONN_NOTIFIER +extern struct pdic_notifier_data pd_noti; +#endif + +/* CCIC Dock Observer Callback parameter */ +enum { + CCIC_DOCK_DETACHED = 0, + CCIC_DOCK_HMT = 105, /* Samsung Gear VR */ + CCIC_DOCK_ABNORMAL = 106, + CCIC_DOCK_MPA = 109, /* Samsung Multi Port Adaptor */ + CCIC_DOCK_DEX = 110, /* Samsung Dex */ + CCIC_DOCK_HDMI = 111, /* Samsung HDMI Dongle */ + CCIC_DOCK_NEW = 200, /* For New Event */ +}; + +#define GEAR_VR_DETACH_WAIT_MS (1000) + +/* For DP */ +#define TypeC_POWER_SINK_INPUT 0 +#define TypeC_POWER_SOURCE_OUTPUT 1 +#define TypeC_DP_SUPPORT (0xFF01) + +/* Samsung Acc VID */ +#define SAMSUNG_VENDOR_ID 0x04E8 +#define SAMSUNG_MPA_VENDOR_ID 0x04B4 +/* Samsung Acc PID */ +#define GEARVR_PRODUCT_ID 0xA500 +#define GEARVR_PRODUCT_ID_1 0xA501 +#define GEARVR_PRODUCT_ID_2 0xA502 +#define GEARVR_PRODUCT_ID_3 0xA503 +#define GEARVR_PRODUCT_ID_4 0xA504 +#define GEARVR_PRODUCT_ID_5 0xA505 +#define DEXDOCK_PRODUCT_ID 0xA020 +#define HDMI_PRODUCT_ID 0xA025 +#define MPA_PRODUCT_ID 0x2122 +/* Samsung UVDM structure */ +#define SEC_UVDM_SHORT_DATA 0x0 +#define SEC_UVDM_LONG_DATA 0x1 +#define SEC_UVDM_ININIATOR 0x0 +#define SEC_UVDM_RESPONDER_ACK 0x1 +#define SEC_UVDM_RESPONDER_NAK 0x2 +#define SEC_UVDM_RESPONDER_BUSY 0x3 +#define SEC_UVDM_UNSTRUCTURED_VDM 0x4 + +/*For DP Pin Assignment */ +#define DP_PIN_ASSIGNMENT_NODE 0x00000000 +#define DP_PIN_ASSIGNMENT_A 0x00000001 /* ( 1 << 0 ) */ +#define DP_PIN_ASSIGNMENT_B 0x00000002 /* ( 1 << 1 ) */ +#define DP_PIN_ASSIGNMENT_C 0x00000004 /* ( 1 << 2 ) */ +#define DP_PIN_ASSIGNMENT_D 0x00000008 /* ( 1 << 3 ) */ +#define DP_PIN_ASSIGNMENT_E 0x00000010 /* ( 1 << 4 ) */ +#define DP_PIN_ASSIGNMENT_F 0x00000020 /* ( 1 << 5 ) */ + +/* For DP VDM Modes VDO Port_Capability */ +typedef enum { + num_Reserved_Capable = 0, + num_UFP_D_Capable = 1, + num_DFP_D_Capable = 2, + num_DFP_D_and_UFP_D_Capable = 3 +} Num_DP_Port_Capability_Type; + +/* For DP VDM Modes VDO Receptacle_Indication */ +typedef enum { + num_USB_TYPE_C_PLUG = 0, + num_USB_TYPE_C_Receptacle = 1 +} Num_DP_Receptacle_Indication_Type; + + +/* For DP_Status_Update Port_Connected */ +typedef enum { + num_Adaptor_Disable = 0, + num_Connect_DFP_D = 1, + num_Connect_UFP_D = 2, + num_Connect_DFP_D_and_UFP_D = 3 +} Num_DP_Port_Connected_Type; + +/* For DP_Configure Select_Configuration */ +typedef enum { + num_Cfg_for_USB = 0, + num_Cfg_UFP_U_as_DFP_D = 1, + num_Cfg_UFP_U_as_UFP_D = 2, + num_Cfg_Reserved = 3 +} Num_DP_Sel_Configuration_Type; + +#if defined(CONFIG_CCIC_NOTIFIER) +void ccic_event_work(void *data, int dest, int id, int attach, int event); +extern void select_pdo(int num); +#endif +#ifdef CONFIG_CHECK_CTYPE_SIDE +int usbpd_manager_get_side_check(void); +#endif +#if defined(CONFIG_IFCONN_NOTIFIER) +void ifconn_event_work(void *pd_data, int dest, int id, int event, void *data); +extern void select_pdo(int num); +#endif +#if defined(CONFIG_DUAL_ROLE_USB_INTF) +extern void role_swap_check(struct work_struct *wk); +extern int dual_role_is_writeable(struct dual_role_phy_instance *drp, + enum dual_role_property prop); +extern int dual_role_get_local_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); +extern int dual_role_set_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val); +extern int dual_role_init(void *_data); +#endif +#endif diff --git a/include/linux/ccic/usbpd_msg.h b/include/linux/ccic/usbpd_msg.h index 4643f81debd8..3c2f3c4b6aa8 100644 --- a/include/linux/ccic/usbpd_msg.h +++ b/include/linux/ccic/usbpd_msg.h @@ -1,6 +1,11 @@ #ifndef __USBPD_MSG_H__ #define __USBPD_MSG_H__ +/* for header */ +#define USBPD_REV_20 (1) +#define PD_SID (0xFF00) +#define PD_SID_1 (0xFF01) + typedef union { u16 word; u8 byte[2]; @@ -52,19 +57,6 @@ typedef union { unsigned supply_type:2; } power_data_obj_sink; - struct { - unsigned max_current:10; /* 10mA units */ - unsigned voltage:10; /* 50mV units */ - unsigned peak_current:2; - unsigned:3; - unsigned data_role_swap:1; - unsigned usb_comm_capable:1; - unsigned externally_powered:1; - unsigned usb_suspend_support:1; - unsigned dual_role_power:1; - unsigned supply_type:2; - } power_data_obj_fixed; - struct { unsigned max_current:10; /* 10mA units */ unsigned min_voltage:10; /* 50mV units */ @@ -109,6 +101,15 @@ typedef union { unsigned vendor_id:16; } unstructured_vdm; + struct { + unsigned data:8; + unsigned total_number_of_uvdm_set:4; + unsigned rsvd:1; + unsigned cmd_type:2; + unsigned data_type:1; + unsigned pid:16; + } sec_uvdm_header; + struct { unsigned command:5; unsigned:1; @@ -121,33 +122,17 @@ typedef union { } structured_vdm; struct { - unsigned usb_vendor_id:16; - unsigned:10; - unsigned modal_operation:1; - unsigned product_type:3; - unsigned data_capable_usb_device:1; - unsigned data_capable_usb_host:1; - } discover_identity_id_header; + unsigned USB_Vendor_ID:16; + unsigned rsvd:10; + unsigned Product_Type:3; + unsigned Data_Capable_USB_Device:1; + unsigned Data_Capable_USB_Host:1; + } id_header_vdo; struct { - unsigned device_version:16; - unsigned usb_product_id:16; - } discover_identity_product_vdo; - - struct { - unsigned svid_1:16; - unsigned svid_0:16; - } discover_svids_vdo; - - struct { - unsigned port_capability:2; - unsigned signalling_dp:4; - unsigned receptacle_indication:1; - unsigned usb_2p0_not_used:1; - unsigned dfp_d_pin_assignments:8; - unsigned ufp_d_pin_assignments:8; - unsigned dp_mode_vdo_reserved:8; - } discover_mode_dp_capability; + unsigned Device_Version:16; + unsigned USB_Product_ID:16; + } product_vdo; struct { unsigned port_capability:2; @@ -169,7 +154,7 @@ typedef union { unsigned hpd_state:1; unsigned irq_hpd:1; unsigned rsvd:23; - } dp_status; + } displayport_status; struct{ unsigned select_configuration:2; @@ -177,12 +162,209 @@ typedef union { unsigned rsvd1:2; unsigned ufp_u_pin_assignment:8; unsigned rsvd2:16; - } dp_configurations; + } displayport_configurations; struct{ unsigned svid_1:16; unsigned svid_0:16; } vdm_svid; } data_obj_type; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + struct { + unsigned vendor_defined:15; + unsigned vdm_type:1; + unsigned vendor_id:16; + }; +} uvdm_header; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + + struct{ + unsigned data:8; + unsigned total_set_num:4; + unsigned direction:1; + unsigned cmd_type:2; + unsigned data_type:1; + unsigned pid:16; + }; +} s_uvdm_header; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + + struct{ + unsigned cur_size:8; + unsigned total_size:8; + unsigned reserved:12; + unsigned order_cur_set:4; + }; +} s_tx_header; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + + struct{ + unsigned checksum:16; + unsigned reserved:16; + }; +} s_tx_tailer; + +typedef union { + u32 object; + u16 word[2]; + u8 byte[4]; + + struct{ + unsigned reserved:18; + unsigned result_value:2; + unsigned rcv_data_size:8; + unsigned order_cur_set:4; + }; +} s_rx_header; + +typedef enum { + POWER_TYPE_FIXED = 0, + POWER_TYPE_BATTERY, + POWER_TYPE_VARIABLE, +} power_supply_type; + +typedef enum { + SOP_TYPE_SOP, + SOP_TYPE_SOP1, + SOP_TYPE_SOP2, + SOP_TYPE_SOP1_DEBUG, + SOP_TYPE_SOP2_DEBUG +} sop_type; + +enum usbpd_control_msg_type { + USBPD_GoodCRC = 0x1, + USBPD_GotoMin = 0x2, + USBPD_Accept = 0x3, + USBPD_Reject = 0x4, + USBPD_Ping = 0x5, + USBPD_PS_RDY = 0x6, + USBPD_Get_Source_Cap = 0x7, + USBPD_Get_Sink_Cap = 0x8, + USBPD_DR_Swap = 0x9, + USBPD_PR_Swap = 0xA, + USBPD_VCONN_Swap = 0xB, + USBPD_Wait = 0xC, + USBPD_Soft_Reset = 0xD, + USBPD_UVDM_MSG = 0xE +}; + +enum usbpd_check_msg_pass { + NONE_CHECK_MSG_PASS, + CHECK_MSG_PASS, +}; + +enum usbpd_port_data_role { + USBPD_UFP, + USBPD_DFP, +}; + +enum usbpd_port_power_role { + USBPD_SINK, + USBPD_SOURCE, + USBPD_DRP, +}; + +enum usbpd_port_vconn_role { + USBPD_VCONN_OFF, + USBPD_VCONN_ON, +}; + +enum usbpd_port_role { + USBPD_Rp = 0x01, + USBPD_Rd = 0x01 << 1, + USBPD_Ra = 0x01 << 2, +}; + +enum usbpd_port_rp_level { + USBPD_56k = 1, + USBPD_22k = 3, + USBPD_10k = 7, +}; + +enum { + USBPD_CC_OFF, + USBPD_CC_ON, +}; + +enum usbpd_connect_type { + USBPD_UP_SIDE = 1, + USBPD_DOWN_SIDE = 2, + USBPD_UNDEFFINED_SIDE = 3, +}; + +enum vdm_command_type{ + Initiator = 0, + Responder_ACK = 1, + Responder_NAK = 2, + Responder_BUSY = 3 +}; + +enum vdm_type{ + Unstructured_VDM = 0, + Structured_VDM = 1 +}; + +enum vdm_configure_type{ + USB = 0, + USB_U_AS_DFP_D = 1, + USB_U_AS_UFP_D = 2 +}; + +enum vdm_displayport_protocol{ + UNSPECIFIED = 0, + DP_V_1_3 = 1, + GEN_2 = 1 << 1 +}; + +enum dp_support { + USBPD_NOT_DP = 0, + USBPD_DP_SUPPORT = 1, +}; + +enum vdm_pin_assignment{ + DE_SELECT_PIN = 0, + PIN_ASSIGNMENT_A = 1, + PIN_ASSIGNMENT_B = 1 << 1, + PIN_ASSIGNMENT_C = 1 << 2, + PIN_ASSIGNMENT_D = 1 << 3, + PIN_ASSIGNMENT_E = 1 << 4, + PIN_ASSIGNMENT_F = 1 << 5, +}; + +enum vdm_command_msg { + Discover_Identity = 1, + Discover_SVIDs = 2, + Discover_Modes = 3, + Enter_Mode = 4, + Exit_Mode = 5, + Attention = 6, + DisplayPort_Status_Update = 0x10, + DisplayPort_Configure = 0x11, +}; + +enum usbpd_data_msg_type { + USBPD_Source_Capabilities = 0x1, + USBPD_Request = 0x2, + USBPD_BIST = 0x3, + USBPD_Sink_Capabilities = 0x4, + USBPD_Vendor_Defined = 0xF, +}; + #endif