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
#include <linux/ccic/s2mm005_usbpd.h>
#include <linux/ccic/s2mm005_usbpd_phy.h>
#include <linux/ccic/usbpd_typec.h>
-#include <linux/ccic/usbpd_msg.h>
+#include <linux/ccic/s2mm005_usbpd_msg.h>
static char VDM_MSG_IRQ_State_Print[9][40] = {
{"bFLAG_Vdm_Reserve_b0"},
--- /dev/null
+/*
+ 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 <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+
+#include <linux/ccic/usbpd.h>
+#include <linux/ccic/usbpd-s2mu106.h>
+
+#include <linux/muic/muic.h>
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+#ifdef CONFIG_BATTERY_SAMSUNG
+#include <linux/sec_batt.h>
+#include <linux/battery/sec_charging_common.h>
+#else
+#include <linux/power_supply.h>
+#endif
+#if defined(CONFIG_USB_HOST_NOTIFY) || defined(CONFIG_USB_HW_PARAM)
+#include <linux/usb_notify.h>
+#endif
+#include <linux/regulator/consumer.h>
+
+#if (defined CONFIG_IFCONN_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF)
+#include <linux/ccic/usbpd_ext.h>
+#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");
#include <linux/ccic/core.h>
-#include <linux/ccic/usbpd_msg.h>
+#include <linux/ccic/s2mm005_usbpd_msg.h>
#include <linux/ccic/usbpd_typec.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
--- /dev/null
+/*
+* USB PD Driver - Protocol Layer
+*/
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/ccic/usbpd.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#ifdef CONFIG_CCIC_SYSFS
+#include <linux/ccic/usbpd_sysfs.h>
+#include <linux/sec_sysfs.h>
+#endif
+#ifdef CONFIG_IFCONN_NOTIFIER
+#include <linux/ifconn/ifconn_notifier.h>
+
+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;
+}
--- /dev/null
+/*
+ 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 <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/usb_notify.h>
+
+#if (defined CONFIG_CCIC_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF \
+ || defined CONFIG_IFCONN_NOTIFIER)
+#include <linux/ccic/usbpd_ext.h>
+#endif
+#if defined CONFIG_IFCONN_NOTIFIER
+#include <linux/ifconn/ifconn_notifier.h>
+#endif
+#include <linux/ccic/usbpd.h>
+#include <linux/ccic/usbpd-s2mu106.h>
+
+#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/<property>" */
+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 <value> >
+ * /sys/class/dual_role_usb/<name>/<property>"
+ * Block until the entire final state is reached.
+ * Blocking is one of the better ways to signal when the operation
+ * is done.
+ * This function tries to switch to Attached.SRC or Attached.SNK
+ * by forcing the mode into SRC or SNK.
+ * On failure, we fall back to Try.SNK state machine.
+ */
+int 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
--- /dev/null
+/*
+* USB PD Driver - Device Policy Manager
+*/
+
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/ccic/usbpd.h>
+#include <linux/ccic/usbpd-s2mu106.h>
+#include <linux/of_gpio.h>
+
+#include <linux/muic/muic.h>
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+#if (defined CONFIG_CCIC_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF\
+ || CONFIG_IFCONN_NOTIFIER)
+#include <linux/ccic/usbpd_ext.h>
+#endif
+
+/* switch device header */
+#if defined(CONFIG_SWITCH)
+#include <linux/switch.h>
+#endif /* CONFIG_SWITCH */
+
+#ifdef CONFIG_USB_HOST_NOTIFY
+#include <linux/usb_notify.h>
+#endif
+
+#include <linux/completion.h>
+#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;
+}
--- /dev/null
+/*
+* USB PD Driver - Policy Engine
+*/
+
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/ccic/usbpd.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/time.h>
+
+#include <linux/muic/muic.h>
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#include <linux/usb_notify.h>
+
+#if (defined CONFIG_IFCONN_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF)
+#include <linux/ccic/usbpd_ext.h>
+#endif
+
+#if defined(CONFIG_IFCONN_NOTIFIER)
+#include <linux/ifconn/ifconn_notifier.h>
+#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);
+}
+
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/ccic/usbpd_sysfs.h>
+#ifdef CONFIG_CCIC_S2MM005
#include <linux/ccic/s2mm005_usbpd.h>
#include <linux/ccic/s2mm005_usbpd_fw.h>
#include <linux/ccic/s2mm005_usbpd_phy.h>
+#endif
+#ifdef CONFIG_CCIC_S2MU106
+#include <linux/ccic/usbpd.h>
+#endif
+#ifdef CONFIG_CCIC_S2MM005
static ssize_t s2mm005_cur_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
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
};
#include <linux/device.h>
#include <linux/types.h>
-#include <linux/ccic/usbpd_msg.h>
+#include <linux/ccic/s2mm005_usbpd_msg.h>
#include <linux/ccic/usbpd_typec.h>
#include <linux/ccic/usbpd_config.h>
#if defined(CONFIG_SAMSUNG_BATTERY)
--- /dev/null
+#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
+
--- /dev/null
+/*
+ * 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 <linux/wakelock.h>
+#include <linux/ccic/usbpd_msg.h>
+#if defined CONFIG_IFCONN_NOTIFIER
+#include <linux/ifconn/ifconn_notifier.h>
+#endif
+
+#include <linux/power_supply.h>
+
+#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__ */
--- /dev/null
+#ifndef __USBPD_H__
+#define __USBPD_H__
+
+#include <linux/ccic/usbpd_msg.h>
+#include <linux/muic/muic.h>
+#ifdef CONFIG_IFCONN_NOTIFIER
+#include <linux/ifconn/ifconn_notifier.h>
+#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
--- /dev/null
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+#if defined(CONFIG_DUAL_ROLE_USB_INTF)
+#include <linux/usb/class-dual-role.h>
+#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
#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];
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 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;
} 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;
unsigned hpd_state:1;
unsigned irq_hpd:1;
unsigned rsvd:23;
- } dp_status;
+ } displayport_status;
struct{
unsigned select_configuration:2;
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