mfd: add driver for fusb302 Type-C PD
authorZain Wang <wzz@rock-chips.com>
Fri, 17 Jun 2016 08:51:37 +0000 (16:51 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 5 Aug 2016 06:50:19 +0000 (14:50 +0800)
New driver only support DRP mode without swap function.
Swap function will be added in later.

Change-Id: I7e0c2c424def069d4be78c3bc8f704c3f7e5be48
Signed-off-by: Zain Wang <wzz@rock-chips.com>
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/fusb302.c [new file with mode: 0644]
drivers/mfd/fusb302.h [new file with mode: 0644]

index 1d9c502..936a701 100644 (file)
@@ -1529,4 +1529,12 @@ config MFD_VEXPRESS_SYSREG
          on the ARM Ltd. Versatile Express board.
 
 endmenu
+
+config FUSB_30X
+       tristate "Fairchild fusb302 Support"
+       depends on I2C=y
+       help
+         This option provides a driver for the Fairchild FUSB302.
+         It support typec-pd functionality by controlled using I2C.
+
 endif
index a8b76b8..50a9a21 100644 (file)
@@ -194,3 +194,4 @@ intel-soc-pmic-objs         := intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)   += intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)       += mt6397-core.o
+obj-$(CONFIG_FUSB_30X)         += fusb302.o
diff --git a/drivers/mfd/fusb302.c b/drivers/mfd/fusb302.c
new file mode 100644 (file)
index 0000000..e4bcd93
--- /dev/null
@@ -0,0 +1,2342 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from chrome ec and fairchild GPL fusb302 driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+#include "fusb302.h"
+
+#define FUSB302_MAX_REG                (FUSB_REG_FIFO + 50)
+#define FUSB_MS_TO_NS(x)       ((s64)x * 1000 * 1000)
+
+#define FUSB_MODE_DRP          0
+#define FUSB_MODE_UFP          1
+#define FUSB_MODE_DFP          2
+#define FUSB_MODE_ASS          3
+
+#define TYPEC_CC_VOLT_OPEN     0
+#define TYPEC_CC_VOLT_RA       1
+#define TYPEC_CC_VOLT_RD       2
+#define TYPEC_CC_VOLT_RP       3
+
+#define EVENT_CC               BIT(0)
+#define EVENT_RX               BIT(1)
+#define EVENT_TX               BIT(2)
+#define EVENT_REC_RESET                BIT(3)
+#define EVENT_WORK_CONTINUE    BIT(5)
+#define EVENT_TIMER_MUX                BIT(6)
+#define EVENT_TIMER_STATE      BIT(7)
+#define FLAG_EVENT             (EVENT_RX | EVENT_TIMER_MUX | \
+                                EVENT_TIMER_STATE)
+
+#define PIN_MAP_A              BIT(0)
+#define PIN_MAP_B              BIT(1)
+#define PIN_MAP_C              BIT(2)
+#define PIN_MAP_D              BIT(3)
+#define PIN_MAP_E              BIT(4)
+#define PIN_MAP_F              BIT(5)
+
+static u8 fusb30x_port_used;
+static struct fusb30x_chip *fusb30x_port_info[256];
+
+static bool is_write_reg(struct device *dev, unsigned int reg)
+{
+       if (reg >= FUSB_REG_FIFO)
+               return true;
+       else
+               return ((reg < (FUSB_REG_CONTROL4 + 1)) && (reg > 0x01)) ?
+                       true : false;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       if (reg > FUSB_REG_CONTROL4)
+               return true;
+
+       switch (reg) {
+       case FUSB_REG_CONTROL0:
+       case FUSB_REG_CONTROL1:
+       case FUSB_REG_CONTROL3:
+       case FUSB_REG_RESET:
+               return true;
+       }
+       return false;
+}
+
+struct regmap_config fusb302_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .writeable_reg = is_write_reg,
+       .volatile_reg = is_volatile_reg,
+       .max_register = FUSB302_MAX_REG,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static void dump_notify_info(struct fusb30x_chip *chip)
+{
+       dev_dbg(chip->dev, "port        %d\n", chip->port_num);
+       dev_dbg(chip->dev, "orientation %d\n", chip->notify.orientation);
+       dev_dbg(chip->dev, "power_role  %d\n", chip->notify.power_role);
+       dev_dbg(chip->dev, "data_role   %d\n", chip->notify.data_role);
+       dev_dbg(chip->dev, "cc          %d\n", chip->notify.is_cc_connected);
+       dev_dbg(chip->dev, "pd          %d\n", chip->notify.is_pd_connected);
+       dev_dbg(chip->dev, "enter_mode  %d\n", chip->notify.is_enter_mode);
+       dev_dbg(chip->dev, "pin support %d\n",
+               chip->notify.pin_assignment_support);
+       dev_dbg(chip->dev, "pin def     %d\n", chip->notify.pin_assignment_def);
+       dev_dbg(chip->dev, "attention   %d\n", chip->notify.attention);
+}
+
+static const unsigned int fusb302_cable[] = {
+       EXTCON_USB,
+       EXTCON_USB_HOST,
+       EXTCON_CHG_USB_SDP,
+       EXTCON_CHG_USB_CDP,
+       EXTCON_CHG_USB_DCP,
+       EXTCON_CHG_USB_SLOW,
+       EXTCON_DISP_DP,
+       EXTCON_NONE,
+};
+
+void fusb_irq_disable(struct fusb30x_chip *chip)
+{
+       unsigned long irqflags = 0;
+
+       spin_lock_irqsave(&chip->irq_lock, irqflags);
+       if (chip->enable_irq) {
+               disable_irq_nosync(chip->gpio_int_irq);
+               chip->enable_irq = 0;
+       } else {
+               dev_warn(chip->dev, "irq have already disabled\n");
+       }
+       spin_unlock_irqrestore(&chip->irq_lock, irqflags);
+}
+
+void fusb_irq_enable(struct fusb30x_chip *chip)
+{
+       unsigned long irqflags = 0;
+
+       spin_lock_irqsave(&chip->irq_lock, irqflags);
+       if (!chip->enable_irq) {
+               enable_irq(chip->gpio_int_irq);
+               chip->enable_irq = 1;
+       }
+       spin_unlock_irqrestore(&chip->irq_lock, irqflags);
+}
+
+static void platform_fusb_notify(struct fusb30x_chip *chip)
+{
+       bool plugged = 0, flip = 0, dfp = 0, ufp = 0, dp = 0;
+       union extcon_property_value property;
+
+       if (chip->notify.is_cc_connected)
+               chip->notify.orientation = chip->cc_polarity + 1;
+
+       /* avoid notify repeated */
+       if (memcmp(&chip->notify, &chip->notify_cmp,
+                  sizeof(struct notify_info))) {
+               dump_notify_info(chip);
+               chip->notify.attention = 0;
+               memcpy(&chip->notify_cmp, &chip->notify,
+                      sizeof(struct notify_info));
+
+               plugged = chip->notify.is_cc_connected ||
+                         chip->notify.is_pd_connected;
+               flip = chip->notify.orientation ?
+                      (chip->notify.orientation - 1) : 0;
+               dp = chip->notify.is_enter_mode;
+
+               if (dp)
+                       dfp = (chip->notify.pin_assignment_def &
+                               (PIN_MAP_B | PIN_MAP_D | PIN_MAP_F)) ? 1 : 0;
+               else if (chip->notify.data_role)
+                       dfp = 1;
+               else if (plugged)
+                       ufp = 1;
+
+               property.intval = flip;
+               extcon_set_property(chip->extcon, EXTCON_USB,
+                                   EXTCON_PROP_USB_TYPEC_POLARITY, property);
+               extcon_set_property(chip->extcon, EXTCON_USB_HOST,
+                                   EXTCON_PROP_USB_TYPEC_POLARITY, property);
+               extcon_set_property(chip->extcon, EXTCON_DISP_DP,
+                                   EXTCON_PROP_USB_TYPEC_POLARITY, property);
+               extcon_set_state(chip->extcon, EXTCON_USB, ufp);
+               extcon_set_state(chip->extcon, EXTCON_USB_HOST, dfp);
+               extcon_set_state(chip->extcon, EXTCON_DISP_DP, dp);
+               extcon_sync(chip->extcon, EXTCON_USB);
+               extcon_sync(chip->extcon, EXTCON_USB_HOST);
+               extcon_sync(chip->extcon, EXTCON_DISP_DP);
+       }
+}
+
+static bool platform_get_device_irq_state(struct fusb30x_chip *chip)
+{
+       return !gpiod_get_value(chip->gpio_int);
+}
+
+static void fusb_timer_start(struct hrtimer *timer, int ms)
+{
+       ktime_t ktime;
+
+       ktime = ktime_set(0, FUSB_MS_TO_NS(ms));
+       hrtimer_start(timer, ktime, HRTIMER_MODE_REL);
+}
+
+static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v,
+                                        int vbus_other)
+{
+       if (chip->gpio_vbus_5v)
+               gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_5v);
+
+       if (chip->gpio_vbus_other)
+               gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_other);
+}
+
+static void set_state(struct fusb30x_chip *chip, enum connection_state state)
+{
+       dev_dbg(chip->dev, "port %d, state %d\n", chip->port_num, state);
+       if (!state)
+               dev_info(chip->dev, "PD disabled\n");
+       chip->conn_state = state;
+       chip->sub_state = 0;
+       chip->val_tmp = 0;
+       chip->work_continue = 1;
+}
+
+static int tcpm_get_message(struct fusb30x_chip *chip)
+{
+       u8 buf[32];
+       int len;
+
+       regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, 3);
+       chip->rec_head = (buf[1] & 0xff) | ((buf[2] << 8) & 0xff00);
+
+       len = PD_HEADER_CNT(chip->rec_head) << 2;
+       regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, len + 4);
+
+       memcpy(chip->rec_load, buf, len);
+
+       return 0;
+}
+
+static void fusb302_flush_rx_fifo(struct fusb30x_chip *chip)
+{
+       tcpm_get_message(chip);
+}
+
+static int tcpm_get_cc(struct fusb30x_chip *chip, int *CC1, int *CC2)
+{
+       u8 val;
+       int *CC_MEASURE;
+       u8 store;
+
+       *CC1 = TYPEC_CC_VOLT_OPEN;
+       *CC2 = TYPEC_CC_VOLT_OPEN;
+
+       if (chip->cc_state & 0x01)
+               CC_MEASURE = CC1;
+       else
+               CC_MEASURE = CC2;
+
+       if (chip->cc_state & 0x04) {
+               regmap_read(chip->regmap, FUSB_REG_SWITCHES0, (u32 *)(&store));
+
+               /* measure cc1 first */
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+                                  SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+                                  SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+                                  SWITCHES0_PDWN1 | SWITCHES0_PDWN2 |
+                                  SWITCHES0_MEAS_CC1);
+               usleep_range(250, 300);
+
+               regmap_read(chip->regmap, FUSB_REG_STATUS0, (u32 *)(&val));
+               val &= STATUS0_BC_LVL;
+               if (val)
+                       *CC1 = val;
+
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+                                  SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+                                  SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+                                  SWITCHES0_PDWN1 | SWITCHES0_PDWN2 |
+                                  SWITCHES0_MEAS_CC2);
+               usleep_range(250, 300);
+
+               regmap_read(chip->regmap, FUSB_REG_STATUS0, (u32 *)(&val));
+               val &= STATUS0_BC_LVL;
+               if (val)
+                       *CC2 = val;
+
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+                                  store);
+       } else {
+               if (chip->cc_state & 0x01) {
+                       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                          SWITCHES0_MEAS_CC1 |
+                                          SWITCHES0_MEAS_CC2 |
+                                          SWITCHES0_PU_EN1 |
+                                          SWITCHES0_PU_EN2 |
+                                          SWITCHES0_PDWN1 |
+                                          SWITCHES0_PDWN2,
+                                          SWITCHES0_MEAS_CC1 |
+                                          SWITCHES0_PU_EN1);
+               } else {
+                       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                          SWITCHES0_MEAS_CC1 |
+                                          SWITCHES0_MEAS_CC2 |
+                                          SWITCHES0_PU_EN1 |
+                                          SWITCHES0_PU_EN2 |
+                                          SWITCHES0_PDWN1 |
+                                          SWITCHES0_PDWN2,
+                                          SWITCHES0_MEAS_CC2 |
+                                          SWITCHES0_PU_EN2);
+               }
+
+               regmap_write(chip->regmap, FUSB_REG_MEASURE, 0x26 << 2);
+               usleep_range(250, 300);
+
+               regmap_read(chip->regmap, FUSB_REG_STATUS0, (u32 *)(&val));
+
+               if (val & STATUS0_COMP) {
+                       *CC_MEASURE = TYPEC_CC_VOLT_OPEN;
+               } else {
+                       regmap_write(chip->regmap, FUSB_REG_MEASURE, 0x05 << 2);
+                       usleep_range(250, 300);
+
+                       regmap_read(chip->regmap, FUSB_REG_STATUS0,
+                                   (u32 *)(&val));
+
+                       if (val & STATUS0_COMP)
+                               *CC_MEASURE = TYPEC_CC_VOLT_RA;
+                       else
+                               *CC_MEASURE = TYPEC_CC_VOLT_RD;
+               }
+       }
+       return 0;
+}
+
+static int tcpm_set_cc(struct fusb30x_chip *chip, int mode)
+{
+       u8 val = 0, mask;
+
+       val &= ~(SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+                SWITCHES0_PDWN1 | SWITCHES0_PDWN2);
+
+       mask = ~val;
+
+       switch (mode) {
+       case FUSB_MODE_DFP:
+               if (chip->togdone_pullup)
+                       val |= SWITCHES0_PU_EN2;
+               else
+                       val |= SWITCHES0_PU_EN1;
+               break;
+       case FUSB_MODE_UFP:
+               val |= SWITCHES0_PDWN1 | SWITCHES0_PDWN2;
+               break;
+       case FUSB_MODE_DRP:
+               val |= SWITCHES0_PDWN1 | SWITCHES0_PDWN2;
+               break;
+       case FUSB_MODE_ASS:
+               break;
+       }
+
+       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, mask, val);
+       return 0;
+}
+
+static int tcpm_set_rx_enable(struct fusb30x_chip *chip, int enable)
+{
+       u8 val = 0;
+
+       if (enable) {
+               if (chip->cc_polarity)
+                       val |= SWITCHES0_MEAS_CC2;
+               else
+                       val |= SWITCHES0_MEAS_CC1;
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+                                  val);
+               fusb302_flush_rx_fifo(chip);
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+                                  SWITCHES1_AUTO_CRC, SWITCHES1_AUTO_CRC);
+       } else {
+               /*
+                * bit of a hack here.
+                * when this function is called to disable rx (enable=0)
+                * using it as an indication of detach (gulp!)
+                * to reset our knowledge of where
+                * the toggle state machine landed.
+                */
+               chip->togdone_pullup = 0;
+
+#ifdef FUSB_HAVE_DRP
+               tcpm_set_cc(chip, FUSB_MODE_DRP);
+               regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+                                  CONTROL2_TOG_RD_ONLY,
+                                  CONTROL2_TOG_RD_ONLY);
+#endif
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+                                  0);
+               regmap_update_bits(chip->regmap,
+                                  FUSB_REG_SWITCHES1, SWITCHES1_AUTO_CRC, 0);
+       }
+
+       return 0;
+}
+
+static int tcpm_set_msg_header(struct fusb30x_chip *chip)
+{
+       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+                          SWITCHES1_POWERROLE | SWITCHES1_DATAROLE,
+                          (chip->notify.power_role << 7) |
+                          (chip->notify.data_role << 4));
+       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+                          SWITCHES1_SPECREV, 2 << 5);
+       return 0;
+}
+
+static int tcpm_set_polarity(struct fusb30x_chip *chip, bool polarity)
+{
+       u8 val = 0;
+
+#ifdef FUSB_VCONN_SUPPORT
+       if (chip->vconn_enabled) {
+               if (polarity)
+                       val |= SWITCHES0_VCONN_CC1;
+               else
+                       val |= SWITCHES0_VCONN_CC2;
+       }
+#endif
+
+       if (polarity)
+               val |= SWITCHES0_MEAS_CC2;
+       else
+               val |= SWITCHES0_MEAS_CC1;
+
+       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                          SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2 |
+                          SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+                          val);
+
+       val = 0;
+       if (polarity)
+               val |= SWITCHES1_TXCC2;
+       else
+               val |= SWITCHES1_TXCC1;
+       regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1,
+                          SWITCHES1_TXCC1 | SWITCHES1_TXCC2,
+                          val);
+
+       chip->cc_polarity = polarity;
+
+       return 0;
+}
+
+static int tcpm_set_vconn(struct fusb30x_chip *chip, int enable)
+{
+       u8 val = 0;
+
+       if (enable) {
+               tcpm_set_polarity(chip, chip->cc_polarity);
+       } else {
+               val &= ~(SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2);
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2,
+                                  val);
+       }
+       chip->vconn_enabled = enable;
+       return 0;
+}
+
+static void fusb302_pd_reset(struct fusb30x_chip *chip)
+{
+       regmap_write(chip->regmap, FUSB_REG_RESET, RESET_PD_RESET);
+       regmap_reinit_cache(chip->regmap, &fusb302_regmap_config);
+}
+
+static void tcpm_init(struct fusb30x_chip *chip)
+{
+       u8 val;
+
+       regmap_read(chip->regmap, FUSB_REG_DEVICEID, (u32 *)(&chip->chip_id));
+       platform_set_vbus_lvl_enable(chip, 0, 0);
+       chip->notify.is_cc_connected = 0;
+       chip->cc_state = 0;
+
+       /* restore default settings */
+       regmap_update_bits(chip->regmap, FUSB_REG_RESET, RESET_SW_RESET,
+                          RESET_SW_RESET);
+       fusb302_pd_reset(chip);
+       /* set auto_retry and number of retries */
+       regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3,
+                          CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES,
+                          CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES),
+
+       /* set interrupts */
+       val = 0xff;
+       val &= ~(MASK_M_BC_LVL | MASK_M_COLLISION | MASK_M_ALERT |
+                MASK_M_VBUSOK);
+       regmap_write(chip->regmap, FUSB_REG_MASK, val);
+
+       val = 0xff;
+       val &= ~(MASKA_M_TOGDONE | MASKA_M_RETRYFAIL | MASKA_M_HARDSENT |
+                MASKA_M_TXSENT | MASKA_M_HARDRST);
+       regmap_write(chip->regmap, FUSB_REG_MASKA, val);
+
+       val = 0xff;
+       val = ~MASKB_M_GCRCSEND;
+       regmap_write(chip->regmap, FUSB_REG_MASKB, val);
+
+#ifdef FUSB_HAVE_DRP
+               regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+                                  CONTROL2_MODE | CONTROL2_TOGGLE,
+                                  (1 << 1) | CONTROL2_TOGGLE);
+
+               regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+                                  CONTROL2_TOG_RD_ONLY,
+                                  CONTROL2_TOG_RD_ONLY);
+#endif
+       /* Interrupts Enable */
+       regmap_update_bits(chip->regmap, FUSB_REG_CONTROL0, CONTROL0_INT_MASK,
+                          ~CONTROL0_INT_MASK);
+
+       tcpm_set_polarity(chip, 0);
+       tcpm_set_vconn(chip, 0);
+
+       regmap_write(chip->regmap, FUSB_REG_POWER, 0xf);
+}
+
+static void pd_execute_hard_reset(struct fusb30x_chip *chip)
+{
+       chip->msg_id = 0;
+       chip->vdm_state = 0;
+       if (chip->notify.power_role)
+               set_state(chip, policy_src_transition_default);
+       else
+               set_state(chip, policy_snk_transition_default);
+}
+
+static void tcpc_alert(struct fusb30x_chip *chip, int *evt)
+{
+       int interrupt, interrupta, interruptb;
+       u32 val;
+
+       regmap_read(chip->regmap, FUSB_REG_INTERRUPT, &interrupt);
+       regmap_read(chip->regmap, FUSB_REG_INTERRUPTA, &interrupta);
+       regmap_read(chip->regmap, FUSB_REG_INTERRUPTB, &interruptb);
+
+       if (interrupt & INTERRUPT_BC_LVL) {
+               if (chip->notify.is_cc_connected)
+                       *evt |= EVENT_CC;
+       }
+
+       if (interrupt & INTERRUPT_VBUSOK) {
+               if (chip->notify.is_cc_connected)
+                       *evt |= EVENT_CC;
+       }
+
+       if (interrupta & INTERRUPTA_TOGDONE) {
+               *evt |= EVENT_CC;
+               regmap_read(chip->regmap, FUSB_REG_STATUS1A, &val);
+               chip->cc_state = ((u8)val >> 3) & 0x07;
+
+               regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+                                  CONTROL2_TOGGLE,
+                                  0);
+
+               val &= ~(SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+                        SWITCHES0_PDWN1 | SWITCHES0_PDWN2);
+
+               if (chip->cc_state | 0x01)
+                       val |= SWITCHES0_PU_EN1;
+               else
+                       val |= SWITCHES0_PU_EN2;
+
+               regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+                                  SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+                                  SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+                                  val);
+       }
+
+       if (interrupta & INTERRUPTA_TXSENT) {
+               *evt |= EVENT_TX;
+               fusb302_flush_rx_fifo(chip);
+               chip->tx_state = tx_success;
+       }
+
+       if (interruptb & INTERRUPTB_GCRCSENT)
+               *evt |= EVENT_RX;
+
+       if (interrupta & INTERRUPTA_HARDRST) {
+               fusb302_pd_reset(chip);
+               pd_execute_hard_reset(chip);
+               *evt |= EVENT_REC_RESET;
+       }
+
+       if (interrupta & INTERRUPTA_RETRYFAIL) {
+               *evt |= EVENT_TX;
+               chip->tx_state = tx_failed;
+       }
+
+       if (interrupta & INTERRUPTA_HARDSENT) {
+               chip->tx_state = tx_success;
+               chip->timer_state = T_DISABLED;
+               *evt |= EVENT_TX;
+       }
+}
+
+static void mux_alert(struct fusb30x_chip *chip, int *evt)
+{
+       if (!chip->timer_mux) {
+               *evt |= EVENT_TIMER_MUX;
+               chip->timer_mux = T_DISABLED;
+       }
+
+       if (!chip->timer_state) {
+               *evt |= EVENT_TIMER_STATE;
+               chip->timer_state = T_DISABLED;
+       }
+
+       if (chip->work_continue) {
+               *evt |= EVENT_WORK_CONTINUE;
+               chip->work_continue = 0;
+       }
+}
+
+static void set_state_unattached(struct fusb30x_chip *chip)
+{
+       dev_info(chip->dev, "connection has disconnected\n");
+       tcpm_init(chip);
+       tcpm_set_rx_enable(chip, 0);
+       chip->conn_state = unattached;
+       tcpm_set_cc(chip, FUSB_MODE_DRP);
+
+       /* claer notify_info */
+       memset(&chip->notify, 0, sizeof(struct notify_info));
+       platform_fusb_notify(chip);
+
+       msleep(100);
+}
+
+static int tcpm_check_vbus(struct fusb30x_chip *chip)
+{
+       u8 val;
+
+       /* Read status register */
+       regmap_read(chip->regmap, FUSB_REG_STATUS0, (u32 *)&val);
+
+       return (val & STATUS0_VBUSOK) ? 1 : 0;
+}
+
+static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT)
+{
+       int i;
+       struct PD_CAP_INFO *pd_cap_info = &chip->pd_cap_info;
+
+       chip->send_head = ((chip->msg_id & 0x7) << 9) |
+                        ((chip->notify.power_role & 0x1) << 8) |
+                        (1 << 6) |
+                        ((chip->notify.data_role & 0x1) << 5);
+
+       if (is_DMT) {
+               switch (cmd) {
+               case DMT_SOURCECAPABILITIES:
+                       chip->send_head |= ((chip->n_caps_used & 0x3) << 12) | (cmd & 0xf);
+
+                       for (i = 0; i < chip->n_caps_used; i++) {
+                               chip->send_load[i] = (pd_cap_info->supply_type << 30) |
+                                                   (pd_cap_info->dual_role_power << 29) |
+                                                   (pd_cap_info->usb_suspend_support << 28) |
+                                                   (pd_cap_info->externally_powered << 27) |
+                                                   (pd_cap_info->usb_communications_cap << 26) |
+                                                   (pd_cap_info->data_role_swap << 25) |
+                                                   (pd_cap_info->peak_current << 20) |
+                                                   (chip->source_power_supply[i] << 10) |
+                                                   (chip->source_max_current[i]);
+                       }
+                       break;
+               case DMT_REQUEST:
+                       chip->send_head |= ((1 << 12) | (cmd & 0xf));
+                       /* send request with FVRDO */
+                       chip->send_load[0] = (chip->pos_power << 28) |
+                                           (0 << 27) |
+                                           (1 << 26) |
+                                           (0 << 25) |
+                                           (0 << 24);
+
+                       switch (CAP_POWER_TYPE(chip->rec_load[chip->pos_power - 1])) {
+                       case 0:
+                               /* Fixed Supply */
+                               chip->send_load[0] |= ((CAP_FPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff);
+                               chip->send_load[0] |= (CAP_FPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff);
+                               break;
+                       case 1:
+                               /* Battery */
+                               chip->send_load[0] |= ((CAP_VPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff);
+                               chip->send_load[0] |= (CAP_VPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff);
+                               break;
+                       default:
+                               /* not meet battery caps */
+                               break;
+                       }
+                       break;
+               case DMT_SINKCAPABILITIES:
+                       break;
+               case DMT_VENDERDEFINED:
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               chip->send_head |= (cmd & 0xf);
+       }
+}
+
+static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode)
+{
+       chip->send_head = (chip->msg_id & 0x7) << 9;
+       chip->send_head |= (chip->notify.power_role & 0x1) << 8;
+
+       chip->send_head = ((chip->msg_id & 0x7) << 9) |
+                        ((chip->notify.power_role & 0x1) << 8) |
+                        (1 << 6) |
+                        ((chip->notify.data_role & 0x1) << 5) |
+                        (DMT_VENDERDEFINED & 0xf);
+
+       chip->send_load[0] = (1 << 15) |
+                           (0 << 13) |
+                           (type << 6) |
+                           (cmd);
+
+       switch (cmd) {
+       case VDM_DISCOVERY_ID:
+       case VDM_DISCOVERY_SVIDS:
+       case VDM_ATTENTION:
+               chip->send_load[0] |= (0xff00 << 16);
+               chip->send_head |= (1 << 12);
+               break;
+       case VDM_DISCOVERY_MODES:
+               chip->send_load[0] |=
+                       (chip->vdm_svid[chip->val_tmp >> 1] << 16);
+               chip->send_head |= (1 << 12);
+               break;
+       case VDM_ENTER_MODE:
+               chip->send_head |= (1 << 12);
+               chip->send_load[0] |= (mode << 8) | (0xff01 << 16);
+               break;
+       case VDM_EXIT_MODE:
+               chip->send_head |= (1 << 12);
+               chip->send_load[0] |= (0x0f << 8) | (0xff01 << 16);
+               break;
+       case VDM_DP_STATUS_UPDATE:
+               chip->send_head |= (2 << 12);
+               chip->send_load[0] |= (1 << 8) | (0xff01 << 16);
+               chip->send_load[1] = 5;
+               break;
+       case VDM_DP_CONFIG:
+               chip->send_head |= (2 << 12);
+               chip->send_load[0] |= (1 << 8) | (0xff01 << 16);
+               chip->send_load[1] = (chip->notify.pin_assignment_def << 8) |
+                                   (1 << 2) | 2;
+               break;
+       default:
+               break;
+       }
+}
+
+static enum tx_state policy_send_hardrst(struct fusb30x_chip *chip, int evt)
+{
+       switch (chip->tx_state) {
+       case 0:
+               regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3,
+                                  CONTROL3_SEND_HARDRESET,
+                                  CONTROL3_SEND_HARDRESET);
+               chip->tx_state = tx_busy;
+               chip->timer_state = T_BMC_TIMEOUT;
+               fusb_timer_start(&chip->timer_state_machine,
+                                chip->timer_state);
+               break;
+       default:
+               if (evt & EVENT_TIMER_STATE)
+                       chip->tx_state = tx_success;
+               break;
+       }
+       return chip->tx_state;
+}
+
+static enum tx_state policy_send_data(struct fusb30x_chip *chip)
+{
+       u8 senddata[40];
+       int pos = 0;
+       u8 len;
+
+       switch (chip->tx_state) {
+       case 0:
+               senddata[pos++] = FUSB_TKN_SYNC1;
+               senddata[pos++] = FUSB_TKN_SYNC1;
+               senddata[pos++] = FUSB_TKN_SYNC1;
+               senddata[pos++] = FUSB_TKN_SYNC2;
+
+               len = PD_HEADER_CNT(chip->send_head) << 2;
+               senddata[pos++] = FUSB_TKN_PACKSYM | ((len + 2) & 0x1f);
+
+               senddata[pos++] = chip->send_head & 0xff;
+               senddata[pos++] = (chip->send_head >> 8) & 0xff;
+
+               memcpy(&senddata[pos], chip->send_load, len);
+               pos += len;
+
+               senddata[pos++] = FUSB_TKN_JAMCRC;
+               senddata[pos++] = FUSB_TKN_EOP;
+               senddata[pos++] = FUSB_TKN_TXOFF;
+               senddata[pos++] = FUSB_TKN_TXON;
+
+               regmap_raw_write(chip->regmap, FUSB_REG_FIFO, senddata, pos);
+               chip->tx_state = tx_busy;
+               break;
+
+       default:
+               /* wait Tx result */
+               break;
+       }
+
+       return chip->tx_state;
+}
+
+static void process_vdm_msg(struct fusb30x_chip *chip)
+{
+       u32 vdm_header = chip->rec_load[0];
+       int i;
+       u32 tmp;
+
+       /* can't procee unstructed vdm msg */
+       if (!GET_VDMHEAD_STRUCT_TYPE(vdm_header))
+               return;
+
+       switch (GET_VDMHEAD_CMD_TYPE(vdm_header)) {
+       case VDM_TYPE_INIT:
+               switch (GET_VDMHEAD_CMD(vdm_header)) {
+               case VDM_ATTENTION:
+                       dev_info(chip->dev, "attention, dp_status %x\n",
+                                chip->rec_load[1]);
+                       chip->notify.attention = 1;
+                       chip->vdm_state = 6;
+                       break;
+               default:
+                       dev_warn(chip->dev, "rec unknown init vdm msg\n");
+                       break;
+               }
+               break;
+       case VDM_TYPE_ACK:
+               switch (GET_VDMHEAD_CMD(vdm_header)) {
+               case VDM_DISCOVERY_ID:
+                       chip->vdm_id = chip->rec_load[1];
+                       break;
+               case VDM_DISCOVERY_SVIDS:
+                       for (i = 0; i < 6; i++) {
+                               tmp = (chip->rec_load[i + 1] >> 16) &
+                                     0x0000ffff;
+                               if (tmp) {
+                                       chip->vdm_svid[i * 2] = tmp;
+                                       chip->vdm_svid_num++;
+                               } else {
+                                       break;
+                               }
+
+                               tmp = (chip->rec_load[i + 1] & 0x0000ffff);
+                               if (tmp) {
+                                       chip->vdm_svid[i * 2 + 1] = tmp;
+                                       chip->vdm_svid_num++;
+                               } else {
+                                       break;
+                               }
+                       }
+                       break;
+               case VDM_DISCOVERY_MODES:
+                       /* indicate there are some vdo modes */
+                       if (PD_HEADER_CNT(chip->rec_head) > 1) {
+                               /*
+                                * store mode config,
+                                * enter first mode default
+                                */
+                               if (!((chip->rec_load[1] >> 8) & 0x3f)) {
+                                       chip->val_tmp |= 1;
+                                       break;
+                               }
+                               chip->notify.pin_assignment_support = 0;
+                               chip->notify.pin_assignment_def = 0;
+                               chip->notify.pin_assignment_support =
+                                       (chip->rec_load[1] >> 8) & 0x3f;
+                               tmp = chip->notify.pin_assignment_support;
+                               for (i = 0; i < 6; i++) {
+                                       if (!(tmp & 0x20))
+                                               tmp = tmp << 1;
+                                       else
+                                               break;
+                               }
+                               chip->notify.pin_assignment_def = 0x20 >> i;
+                               chip->val_tmp |= 1;
+                       }
+                       break;
+               case VDM_ENTER_MODE:
+                       chip->val_tmp = 1;
+                       break;
+               case VDM_DP_STATUS_UPDATE:
+                       dev_dbg(chip->dev, "dp_status 0x%x\n",
+                               chip->rec_load[1]);
+                       chip->val_tmp = 1;
+                       break;
+               case VDM_DP_CONFIG:
+                       chip->val_tmp = 1;
+                       dev_info(chip->dev,
+                                "DP config successful, pin_assignment 0x%x\n",
+                                chip->notify.pin_assignment_def);
+                       chip->notify.is_enter_mode = 1;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case VDM_TYPE_NACK:
+                       dev_warn(chip->dev, "REC NACK for 0x%x\n",
+                                GET_VDMHEAD_CMD(vdm_header));
+                       /* disable vdm */
+                       chip->vdm_state = 0xff;
+               break;
+       }
+}
+
+static int vdm_send_discoveryid(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       switch (chip->vdm_send_state) {
+       case 0:
+               set_vdm_mesg(chip, VDM_DISCOVERY_ID, VDM_TYPE_INIT, 0);
+               chip->vdm_id = 0;
+               chip->tx_state = 0;
+               chip->vdm_send_state++;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->vdm_send_state++;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       dev_warn(chip->dev, "VDM_DISCOVERY_ID send failed\n");
+                       /* disable auto_vdm_machine */
+                       chip->vdm_state = 0xff;
+               }
+
+               if (chip->vdm_send_state != 2)
+                       break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n");
+                       chip->vdm_state = 0xff;
+                       chip->work_continue = 1;
+               }
+
+               if (!chip->vdm_id)
+                       break;
+               chip->vdm_send_state = 0;
+               return 0;
+       }
+       return -EINPROGRESS;
+}
+
+static int vdm_send_discoverysvid(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       switch (chip->vdm_send_state) {
+       case 0:
+               set_vdm_mesg(chip, VDM_DISCOVERY_SVIDS, VDM_TYPE_INIT, 0);
+               memset(chip->vdm_svid, 0, 12);
+               chip->vdm_svid_num = 0;
+               chip->tx_state = 0;
+               chip->vdm_send_state++;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->vdm_send_state++;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS send failed\n");
+                       /* disable auto_vdm_machine */
+                       chip->vdm_state = 0xff;
+               }
+
+               if (chip->vdm_send_state != 2)
+                       break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n");
+                       chip->vdm_state = 0xff;
+                       chip->work_continue = 1;
+               }
+
+               if (!chip->vdm_svid_num)
+                       break;
+               chip->vdm_send_state = 0;
+               return 0;
+       }
+       return -EINPROGRESS;
+}
+
+static int vdm_send_discoverymodes(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       if ((chip->val_tmp >> 1) != chip->vdm_svid_num) {
+               switch (chip->vdm_send_state) {
+               case 0:
+                       set_vdm_mesg(chip, VDM_DISCOVERY_MODES,
+                                    VDM_TYPE_INIT, 0);
+                       chip->tx_state = 0;
+                       chip->vdm_send_state++;
+               case 1:
+                       tmp = policy_send_data(chip);
+                       if (tmp == tx_success) {
+                               chip->vdm_send_state++;
+                               chip->timer_state = T_SENDER_RESPONSE;
+                               fusb_timer_start(&chip->timer_state_machine,
+                                                chip->timer_state);
+                       } else if (tmp == tx_failed) {
+                               dev_warn(chip->dev,
+                                        "VDM_DISCOVERY_MODES send failed\n");
+                               chip->vdm_state = 0xff;
+                       }
+
+                       if (chip->vdm_send_state != 2)
+                               break;
+               default:
+                       if (evt & EVENT_TIMER_STATE) {
+                               dev_warn(chip->dev,
+                                        "VDM_DISCOVERY_MODES time out\n");
+                               chip->vdm_state = 0xff;
+                               chip->work_continue = 1;
+                       }
+
+                       if (!(chip->val_tmp & 1))
+                               break;
+                       chip->val_tmp &= 0xfe;
+                       chip->val_tmp += 2;
+                       chip->vdm_send_state = 0;
+                       chip->work_continue = 1;
+                       break;
+               }
+       } else {
+               chip->val_tmp = 0;
+               return 0;
+       }
+
+       return -EINPROGRESS;
+}
+
+static int vdm_send_entermode(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       switch (chip->vdm_send_state) {
+       case 0:
+               set_vdm_mesg(chip, VDM_ENTER_MODE, VDM_TYPE_INIT, 1);
+               chip->tx_state = 0;
+               chip->vdm_send_state++;
+               chip->notify.is_enter_mode = 0;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->vdm_send_state++;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       dev_warn(chip->dev, "VDM_ENTER_MODE send failed\n");
+                       /* disable auto_vdm_machine */
+                       chip->vdm_state = 0xff;
+               }
+
+               if (chip->vdm_send_state != 2)
+                       break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "VDM_ENTER_MODE time out\n");
+                       chip->vdm_state = 0xff;
+                       chip->work_continue = 1;
+               }
+
+               if (!chip->val_tmp)
+                       break;
+               chip->val_tmp = 0;
+               chip->vdm_send_state = 0;
+               return 0;
+       }
+       return -EINPROGRESS;
+}
+
+static int vdm_send_getdpstatus(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       switch (chip->vdm_send_state) {
+       case 0:
+               set_vdm_mesg(chip, VDM_DP_STATUS_UPDATE, VDM_TYPE_INIT, 1);
+               chip->tx_state = 0;
+               chip->vdm_send_state++;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->vdm_send_state++;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       dev_warn(chip->dev,
+                                "VDM_DP_STATUS_UPDATE send failed\n");
+                       /* disable auto_vdm_machine */
+                       chip->vdm_state = 0xff;
+               }
+
+               if (chip->vdm_send_state != 2)
+                       break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n");
+                       chip->vdm_state = 0xff;
+                       chip->work_continue = 1;
+               }
+
+               if (!chip->val_tmp)
+                       break;
+               chip->val_tmp = 0;
+               chip->vdm_send_state = 0;
+               return 0;
+       }
+       return -EINPROGRESS;
+}
+
+static int vdm_send_dpconfig(struct fusb30x_chip *chip, int evt)
+{
+       int tmp;
+
+       switch (chip->vdm_send_state) {
+       case 0:
+               set_vdm_mesg(chip, VDM_DP_CONFIG, VDM_TYPE_INIT, 0);
+               chip->tx_state = 0;
+               chip->vdm_send_state++;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->vdm_send_state++;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       dev_warn(chip->dev, "vdm_send_dpconfig send failed\n");
+                       /* disable auto_vdm_machine */
+                       chip->vdm_state = 0xff;
+               }
+
+               if (chip->vdm_send_state != 2)
+                       break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "vdm_send_dpconfig time out\n");
+                       chip->vdm_state = 0xff;
+                       chip->work_continue = 1;
+               }
+
+               if (!chip->val_tmp)
+                       break;
+               chip->val_tmp = 0;
+               chip->vdm_send_state = 0;
+               return 0;
+       }
+       return -EINPROGRESS;
+}
+
+static void auto_vdm_machine(struct fusb30x_chip *chip, int evt)
+{
+       switch (chip->vdm_state) {
+       case 0:
+               if (vdm_send_discoveryid(chip, evt))
+                       break;
+               chip->vdm_state++;
+               /* without break */
+       case 1:
+               if (vdm_send_discoverysvid(chip, evt))
+                       break;
+               chip->vdm_state++;
+               /* without break */
+       case 2:
+               if (vdm_send_discoverymodes(chip, evt))
+                       break;
+               chip->vdm_state++;
+               /* without break */
+       case 3:
+               if (vdm_send_entermode(chip, evt))
+                       break;
+               chip->vdm_state++;
+               /* without break */
+       case 4:
+               if (vdm_send_dpconfig(chip, evt))
+                       break;
+               chip->vdm_state = 6;
+               /* without break */
+       case 5:
+               if (vdm_send_getdpstatus(chip, evt))
+                       break;
+               chip->vdm_state++;
+               /* without break */
+       default:
+               platform_fusb_notify(chip);
+               break;
+       }
+}
+
+static void fusb_state_disabled(struct fusb30x_chip *chip, int evt)
+{
+       platform_fusb_notify(chip);
+}
+
+static void fusb_state_unattached(struct fusb30x_chip *chip, int evt)
+{
+       chip->notify.is_cc_connected = 0;
+       if ((evt & EVENT_CC) && chip->cc_state) {
+               if (chip->cc_state & 0x04)
+                       set_state(chip, attach_wait_sink);
+               else
+                       set_state(chip, attach_wait_source);
+
+               tcpm_get_cc(chip, &chip->cc1, &chip->cc2);
+               chip->debounce_cnt = 0;
+               chip->timer_mux = 2;
+               fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux);
+       }
+}
+
+static void fusb_state_attach_wait_sink(struct fusb30x_chip *chip, int evt)
+{
+       int cc1, cc2;
+
+       if (evt & EVENT_TIMER_MUX) {
+               tcpm_get_cc(chip, &cc1, &cc2);
+
+               if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) {
+                       chip->debounce_cnt++;
+               } else {
+                       chip->cc1 = cc1;
+                       chip->cc2 = cc2;
+                       chip->debounce_cnt = 0;
+               }
+
+               if (chip->debounce_cnt > N_DEBOUNCE_CNT) {
+                       if ((chip->cc1 != chip->cc2) &&
+                           ((!chip->cc1) || (!chip->cc2))) {
+                               set_state(chip, attached_sink);
+                       } else {
+                               set_state_unattached(chip);
+                       }
+                       return;
+               }
+
+               chip->timer_mux = 2;
+               fusb_timer_start(&chip->timer_mux_machine,
+                                chip->timer_mux);
+       }
+}
+
+static void fusb_state_attach_wait_source(struct fusb30x_chip *chip, int evt)
+{
+       int cc1, cc2;
+
+       if (evt & EVENT_TIMER_MUX) {
+               tcpm_get_cc(chip, &cc1, &cc2);
+
+               if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) {
+                       chip->debounce_cnt++;
+                       tcpm_set_polarity(chip, !(chip->cc_state & 0x01));
+                       platform_set_vbus_lvl_enable(chip, 1, 0);
+                       tcpm_set_vconn(chip, 1);
+               } else {
+                       chip->cc1 = cc1;
+                       chip->cc2 = cc2;
+                       chip->debounce_cnt = 0;
+               }
+
+               if (chip->debounce_cnt > N_DEBOUNCE_CNT) {
+                       if (((!chip->cc1) || (!chip->cc2)) &&
+                           ((chip->cc1 == TYPEC_CC_VOLT_RD) ||
+                            (chip->cc2 == TYPEC_CC_VOLT_RD))) {
+                               set_state(chip, attached_source);
+                       } else {
+                               set_state_unattached(chip);
+                       }
+
+                       return;
+               }
+
+               chip->timer_mux = 2;
+               fusb_timer_start(&chip->timer_mux_machine,
+                                chip->timer_mux);
+       }
+}
+
+static void fusb_state_attached_source(struct fusb30x_chip *chip, int evt)
+{
+       chip->notify.is_cc_connected = 1;
+       if (chip->cc_state & 0x01)
+               chip->cc_polarity = 0;
+       else
+               chip->cc_polarity = 1;
+
+       chip->notify.power_role = 1;
+       chip->notify.data_role = 1;
+       chip->hardrst_count = 0;
+       set_state(chip, policy_src_startup);
+       dev_info(chip->dev, "CC connected in %d as DFP\n", chip->cc_polarity);
+}
+
+static void fusb_state_attached_sink(struct fusb30x_chip *chip, int evt)
+{
+       chip->notify.is_cc_connected = 1;
+       if (chip->cc_state & 0x01)
+               chip->cc_polarity = 0;
+       else
+               chip->cc_polarity = 1;
+
+       chip->notify.power_role = 0;
+       chip->notify.data_role = 0;
+       chip->hardrst_count = 0;
+       set_state(chip, policy_snk_startup);
+       dev_info(chip->dev, "CC connected in %d as UFP\n", chip->cc_polarity);
+}
+
+static void fusb_state_src_startup(struct fusb30x_chip *chip, int evt)
+{
+       chip->caps_counter = 0;
+       chip->notify.is_pd_connected = 0;
+       chip->msg_id = 0;
+       chip->vdm_state = 0;
+       chip->vdm_substate = 0;
+       chip->vdm_send_state = 0;
+       chip->val_tmp = 0;
+
+       memset(chip->partner_cap, 0, sizeof(chip->partner_cap));
+
+       tcpm_set_msg_header(chip);
+       tcpm_set_polarity(chip, chip->cc_polarity);
+       tcpm_set_rx_enable(chip, 1);
+
+       set_state(chip, policy_src_send_caps);
+}
+
+static void fusb_state_src_discovery(struct fusb30x_chip *chip, int evt)
+{
+       switch (chip->sub_state) {
+       case 0:
+               chip->caps_counter++;
+
+               if (chip->caps_counter < N_CAPS_COUNT) {
+                       chip->timer_state = T_TYPEC_SEND_SOURCECAP;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+                       chip->sub_state = 1;
+               } else {
+                       set_state(chip, disabled);
+               }
+               break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_src_send_caps);
+               } else if ((evt & EVENT_TIMER_MUX) &&
+                          (chip->hardrst_count > N_HARDRESET_COUNT)) {
+                       if (chip->notify.is_pd_connected)
+                               set_state(chip, error_recovery);
+                       else
+                               set_state(chip, disabled);
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_send_caps(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, DMT_SOURCECAPABILITIES, DATAMESSAGE);
+               chip->sub_state = 1;
+               chip->tx_state = tx_idle;
+               /* without break */
+       case 1:
+               tmp = policy_send_data(chip);
+
+               if (tmp == tx_success) {
+                       chip->hardrst_count = 0;
+                       chip->caps_counter = 0;
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+                       chip->timer_mux = T_DISABLED;
+                       chip->sub_state++;
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_discovery);
+                       break;
+               }
+
+               if (!(evt & FLAG_EVENT))
+                       break;
+       default:
+               if (evt & EVENT_RX) {
+                       if ((PD_HEADER_CNT(chip->rec_head) == 1) &&
+                           (PD_HEADER_TYPE(chip->rec_head) == DMT_REQUEST)) {
+                               set_state(chip, policy_src_negotiate_cap);
+                       } else {
+                               set_state(chip, policy_src_send_softrst);
+                       }
+               } else if (evt & EVENT_TIMER_STATE) {
+                       if (chip->hardrst_count <= N_HARDRESET_COUNT)
+                               set_state(chip, policy_src_send_hardrst);
+                       else
+                               set_state(chip, disabled);
+               } else if (evt & EVENT_TIMER_MUX) {
+                       if (chip->notify.is_pd_connected)
+                               set_state(chip, disabled);
+                       else
+                               set_state(chip, error_recovery);
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_negotiate_cap(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       /* base on evb1 */
+       tmp = (chip->rec_load[0] >> 28) & 0x07;
+       if (tmp > chip->n_caps_used)
+               set_state(chip, policy_src_cap_response);
+       else
+               set_state(chip, policy_src_transition_supply);
+}
+
+static void fusb_state_src_transition_supply(struct fusb30x_chip *chip,
+                                            int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+               /* without break */
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->timer_state = T_SRC_TRANSITION;
+                       chip->sub_state++;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_send_softrst);
+               }
+               break;
+       case 2:
+               if (evt & EVENT_TIMER_STATE) {
+                       chip->notify.is_pd_connected = 1;
+                       platform_set_vbus_lvl_enable(chip, 1, 0);
+                       set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+                       chip->tx_state = tx_idle;
+                       chip->sub_state++;
+                       chip->work_continue = 1;
+               }
+               break;
+       default:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       dev_info(chip->dev,
+                                "PD connected as DFP, supporting 5V\n");
+                       set_state(chip, policy_src_ready);
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_send_softrst);
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_cap_response(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, CMT_REJECT, CONTROLMESSAGE);
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+               /* without break */
+       default:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       if (chip->notify.is_pd_connected) {
+                               dev_info(chip->dev,
+                                        "PD connected as DFP, supporting 5V\n");
+                               set_state(chip, policy_src_ready);
+                       } else {
+                               set_state(chip, policy_src_send_hardrst);
+                       }
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_send_softrst);
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_transition_default(struct fusb30x_chip *chip,
+                                             int evt)
+{
+       switch (chip->sub_state) {
+       case 0:
+               chip->notify.is_pd_connected = 0;
+               platform_set_vbus_lvl_enable(chip, 0, 0);
+               if (chip->notify.data_role)
+                       regmap_update_bits(chip->regmap,
+                                          FUSB_REG_SWITCHES1,
+                                          SWITCHES1_DATAROLE,
+                                          SWITCHES1_DATAROLE);
+               else
+                       regmap_update_bits(chip->regmap,
+                                          FUSB_REG_SWITCHES1,
+                                          SWITCHES1_DATAROLE,
+                                          0);
+
+               chip->timer_state = T_SRC_RECOVER;
+               fusb_timer_start(&chip->timer_state_machine,
+                                chip->timer_state);
+               chip->sub_state++;
+               break;
+       default:
+               if (evt & EVENT_TIMER_STATE) {
+                       platform_set_vbus_lvl_enable(chip, 1, 0);
+                       chip->timer_mux = T_NO_RESPONSE;
+                       fusb_timer_start(&chip->timer_mux_machine,
+                                        chip->timer_mux);
+                       set_state(chip, policy_src_startup);
+                       dev_dbg(chip->dev, "reset over-> src startup\n");
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_ready(struct fusb30x_chip *chip, int evt)
+{
+       if (evt & EVENT_RX) {
+               if ((PD_HEADER_CNT(chip->rec_head)) &&
+                   (PD_HEADER_TYPE(chip->rec_head) == DMT_VENDERDEFINED)) {
+                       process_vdm_msg(chip);
+                       chip->work_continue = 1;
+                       chip->timer_state = T_DISABLED;
+               }
+       }
+
+       /* TODO: swap function would be added here later on*/
+
+       if (!chip->partner_cap[0])
+               set_state(chip, policy_src_get_sink_caps);
+       else
+               auto_vdm_machine(chip, evt);
+}
+
+static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, CMT_GETSINKCAP, CONTROLMESSAGE);
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+               /* without break */
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       chip->sub_state++;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_send_softrst);
+               }
+
+               if (!(evt & FLAG_EVENT))
+                       break;
+       default:
+               if (evt & EVENT_RX) {
+                       if ((PD_HEADER_CNT(chip->rec_head)) &&
+                           (PD_HEADER_TYPE(chip->rec_head) ==
+                            DMT_SINKCAPABILITIES)) {
+                               for (tmp = 0;
+                                    tmp < PD_HEADER_CNT(chip->rec_head);
+                                    tmp++) {
+                                       chip->partner_cap[tmp] =
+                                               chip->rec_load[tmp];
+                               }
+                               set_state(chip, policy_src_ready);
+                       } else {
+                               chip->partner_cap[0] = 0xffffffff;
+                               set_state(chip, policy_src_ready);
+                       }
+               } else if (evt & EVENT_TIMER_STATE) {
+                       dev_warn(chip->dev, "Get sink cap time out\n");
+                       chip->partner_cap[0] = 0xffffffff;
+                       set_state(chip, policy_src_ready);
+               }
+       }
+}
+
+static void fusb_state_src_send_hardreset(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+               /* without break */
+       default:
+               tmp = policy_send_hardrst(chip, evt);
+               if (tmp == tx_success) {
+                       chip->hardrst_count++;
+                       set_state(chip, policy_src_transition_default);
+               } else if (tmp == tx_failed) {
+                       /* can't reach here */
+                       set_state(chip, error_recovery);
+               }
+               break;
+       }
+}
+
+static void fusb_state_src_send_softreset(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE);
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+               /* without break */
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       chip->sub_state++;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_src_send_hardrst);
+               }
+
+               if (!(evt & FLAG_EVENT))
+                       break;
+       default:
+               if (evt & EVENT_RX) {
+                       if ((!PD_HEADER_CNT(chip->rec_head)) &&
+                           (PD_HEADER_TYPE(chip->rec_head) == CMT_ACCEPT))
+                               set_state(chip, policy_src_send_caps);
+               } else if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_src_send_hardrst);
+               }
+               break;
+       }
+}
+
+static void fusb_state_snk_startup(struct fusb30x_chip *chip, int evt)
+{
+       chip->notify.is_pd_connected = 0;
+       chip->msg_id = 0;
+       chip->vdm_state = 0;
+       chip->vdm_substate = 0;
+       chip->vdm_send_state = 0;
+       chip->val_tmp = 0;
+       chip->pos_power = 0;
+
+       memset(chip->partner_cap, 0, sizeof(chip->partner_cap));
+
+       tcpm_set_msg_header(chip);
+       tcpm_set_polarity(chip, chip->cc_polarity);
+       tcpm_set_rx_enable(chip, 1);
+       set_state(chip, policy_snk_discovery);
+}
+
+static void fusb_state_snk_discovery(struct fusb30x_chip *chip, int evt)
+{
+       set_state(chip, policy_snk_wait_caps);
+       chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+       fusb_timer_start(&chip->timer_state_machine,
+                        chip->timer_state);
+}
+
+static void fusb_state_snk_wait_caps(struct fusb30x_chip *chip, int evt)
+{
+       if (evt & EVENT_RX) {
+               if (PD_HEADER_CNT(chip->rec_head) &&
+                   PD_HEADER_TYPE(chip->rec_head) == DMT_SOURCECAPABILITIES) {
+                       chip->timer_mux = T_DISABLED;
+                       set_state(chip, policy_snk_evaluate_caps);
+               }
+       } else if (evt & EVENT_TIMER_STATE) {
+               if (chip->hardrst_count <= N_HARDRESET_COUNT)
+                       set_state(chip, policy_snk_send_hardrst);
+               else
+                       set_state(chip, disabled);
+       } else if ((evt & EVENT_TIMER_MUX) &&
+                  (chip->hardrst_count > N_HARDRESET_COUNT)) {
+               if (chip->notify.is_pd_connected)
+                       set_state(chip, error_recovery);
+               else
+                       set_state(chip, disabled);
+       }
+}
+
+static void fusb_state_snk_evaluate_caps(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       chip->hardrst_count = 0;
+       chip->pos_power = 0;
+
+       for (tmp = 0; tmp < PD_HEADER_CNT(chip->rec_head); tmp++) {
+               switch (CAP_POWER_TYPE(chip->rec_load[tmp])) {
+               case 0:
+                       /* Fixed Supply */
+                       if (CAP_FPDO_VOLTAGE(chip->rec_load[tmp]) <= 100)
+                               chip->pos_power = tmp + 1;
+                       break;
+               case 1:
+                       /* Battery */
+                       if (CAP_VPDO_VOLTAGE(chip->rec_load[tmp]) <= 100)
+                               chip->pos_power = tmp + 1;
+                       break;
+               default:
+                       /* not meet battery caps */
+                       break;
+               }
+       }
+
+       if ((!chip->pos_power) || (chip->pos_power > 7)) {
+               chip->pos_power = 0;
+               set_state(chip, policy_snk_wait_caps);
+       } else {
+               set_state(chip, policy_snk_select_cap);
+       }
+}
+
+static void fusb_state_snk_select_cap(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, DMT_REQUEST, DATAMESSAGE);
+               chip->sub_state = 1;
+               chip->tx_state = tx_idle;
+               /* without break */
+       case 1:
+               tmp = policy_send_data(chip);
+
+               if (tmp == tx_success) {
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+                       chip->sub_state++;
+               } else if (tmp == tx_failed) {
+                       set_state(chip, policy_snk_discovery);
+                       break;
+               }
+
+               if (!(evt & FLAG_EVENT))
+                       break;
+       default:
+               if (evt & EVENT_RX) {
+                       if (!PD_HEADER_CNT(chip->rec_head)) {
+                               switch (PD_HEADER_TYPE(chip->rec_head)) {
+                               case CMT_ACCEPT:
+                                       set_state(chip,
+                                                 policy_snk_transition_sink);
+                                       chip->timer_state = T_PS_TRANSITION;
+                                       fusb_timer_start(&chip->timer_state_machine,
+                                                        chip->timer_state);
+                                       break;
+                               case CMT_WAIT:
+                               case CMT_REJECT:
+                                       if (chip->notify.is_pd_connected) {
+                                               dev_info(chip->dev,
+                                                        "PD connected as UFP, fetching 5V\n");
+                                               set_state(chip,
+                                                         policy_snk_ready);
+                                       } else {
+                                               set_state(chip,
+                                                         policy_snk_wait_caps);
+                                               /*
+                                                * make sure don't send
+                                                * hard reset to prevent
+                                                * infinite loop
+                                                */
+                                               chip->hardrst_count =
+                                                       N_HARDRESET_COUNT + 1;
+                                       }
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+               } else if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_snk_send_hardrst);
+               }
+               break;
+       }
+}
+
+static void fusb_state_snk_transition_sink(struct fusb30x_chip *chip, int evt)
+{
+       if (evt & EVENT_RX) {
+               if ((!PD_HEADER_CNT(chip->rec_head)) &&
+                   (PD_HEADER_TYPE(chip->rec_head) == CMT_PS_RDY)) {
+                       chip->notify.is_pd_connected = 1;
+                       dev_info(chip->dev,
+                                "PD connected as UFP, fetching 5V\n");
+                       set_state(chip, policy_snk_ready);
+               } else if ((PD_HEADER_CNT(chip->rec_head)) &&
+                          (PD_HEADER_TYPE(chip->rec_head) ==
+                           DMT_SOURCECAPABILITIES)) {
+                       set_state(chip, policy_snk_evaluate_caps);
+               }
+       } else if (evt & EVENT_TIMER_STATE) {
+               set_state(chip, policy_snk_send_hardrst);
+       }
+}
+
+static void fusb_state_snk_transition_default(struct fusb30x_chip *chip,
+                                             int evt)
+{
+       switch (chip->sub_state) {
+       case 0:
+               chip->notify.is_pd_connected = 0;
+               chip->timer_mux = T_NO_RESPONSE;
+               fusb_timer_start(&chip->timer_mux_machine,
+                                chip->timer_mux);
+               chip->timer_state = T_PS_HARD_RESET_MAX + T_SAFE_0V;
+               fusb_timer_start(&chip->timer_state_machine,
+                                chip->timer_state);
+               if (chip->notify.data_role)
+                       tcpm_set_msg_header(chip);
+
+               chip->sub_state++;
+       case 1:
+               if (!tcpm_check_vbus(chip)) {
+                       chip->sub_state++;
+                       chip->timer_state = T_SRC_RECOVER_MAX + T_SRC_TURN_ON;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_snk_startup);
+               }
+               break;
+       default:
+               if (tcpm_check_vbus(chip)) {
+                       chip->timer_state = T_DISABLED;
+                       set_state(chip, policy_snk_startup);
+               } else if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_snk_startup);
+               }
+               break;
+       }
+}
+
+static void fusb_state_snk_ready(struct fusb30x_chip *chip, int evt)
+{
+       /* TODO: snk_ready_function would be added later on*/
+       platform_fusb_notify(chip);
+}
+
+static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+       default:
+               tmp = policy_send_hardrst(chip, evt);
+               if (tmp == tx_success) {
+                       chip->hardrst_count++;
+                       set_state(chip, policy_snk_transition_default);
+               } else if (tmp == tx_failed) {
+                       set_state(chip, error_recovery);
+               }
+               break;
+       }
+}
+
+static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, int evt)
+{
+       u32 tmp;
+
+       switch (chip->sub_state) {
+       case 0:
+               set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE);
+               chip->tx_state = tx_idle;
+               chip->sub_state++;
+       case 1:
+               tmp = policy_send_data(chip);
+               if (tmp == tx_success) {
+                       chip->timer_state = T_SENDER_RESPONSE;
+                       chip->sub_state++;
+                       fusb_timer_start(&chip->timer_state_machine,
+                                        chip->timer_state);
+               } else if (tmp == tx_failed) {
+                       /* can't reach here */
+                       set_state(chip, policy_snk_send_hardrst);
+               }
+
+               if (!(evt & FLAG_EVENT))
+                       break;
+       default:
+               if (evt & EVENT_RX) {
+                       if ((!PD_HEADER_CNT(chip->rec_head)) &&
+                           (PD_HEADER_TYPE(chip->rec_head) == CMT_ACCEPT))
+                               set_state(chip, policy_snk_wait_caps);
+               } else if (evt & EVENT_TIMER_STATE) {
+                       set_state(chip, policy_snk_send_hardrst);
+               }
+               break;
+       }
+}
+
+static void state_machine_typec(struct fusb30x_chip *chip)
+{
+       int evt = 0;
+       int cc1, cc2;
+
+       tcpc_alert(chip, &evt);
+       mux_alert(chip, &evt);
+       if (!evt)
+               goto BACK;
+
+       if (chip->notify.is_cc_connected) {
+               if (evt & EVENT_CC) {
+                       if ((chip->cc_state & 0x04) &&
+                           (chip->conn_state !=
+                            policy_snk_transition_default)) {
+                               if (!tcpm_check_vbus(chip))
+                                       set_state_unattached(chip);
+                       } else if (chip->conn_state !=
+                                  policy_src_transition_default) {
+                               tcpm_get_cc(chip, &cc1, &cc2);
+                               if (!(chip->cc_state & 0x01))
+                                       cc1 = cc2;
+                               if (cc1 == TYPEC_CC_VOLT_OPEN)
+                                       set_state_unattached(chip);
+                       }
+               }
+       }
+
+       if (evt & EVENT_RX) {
+               tcpm_get_message(chip);
+               if ((!PD_HEADER_CNT(chip->rec_head)) &&
+                   (PD_HEADER_TYPE(chip->rec_head) == CMT_SOFTRESET)) {
+                       if (chip->notify.power_role)
+                               set_state(chip, policy_src_send_softrst);
+                       else
+                               set_state(chip, policy_snk_send_softrst);
+               }
+       }
+
+       if (evt & EVENT_TX) {
+               if (chip->tx_state == tx_success)
+                       chip->msg_id++;
+       }
+       switch (chip->conn_state) {
+       case disabled:
+               fusb_state_disabled(chip, evt);
+               break;
+       case error_recovery:
+               set_state_unattached(chip);
+               break;
+       case unattached:
+               fusb_state_unattached(chip, evt);
+               break;
+       case attach_wait_sink:
+               fusb_state_attach_wait_sink(chip, evt);
+               break;
+       case attach_wait_source:
+               fusb_state_attach_wait_source(chip, evt);
+               break;
+       case attached_source:
+               fusb_state_attached_source(chip, evt);
+               break;
+       case attached_sink:
+               fusb_state_attached_sink(chip, evt);
+               break;
+
+       /* POWER DELIVERY */
+       case policy_src_startup:
+               fusb_state_src_startup(chip, evt);
+               break;
+       case policy_src_discovery:
+               fusb_state_src_discovery(chip, evt);
+               break;
+       case policy_src_send_caps:
+               fusb_state_src_send_caps(chip, evt);
+               if (chip->conn_state != policy_src_negotiate_cap)
+                       break;
+       case policy_src_negotiate_cap:
+               fusb_state_src_negotiate_cap(chip, evt);
+
+       case policy_src_transition_supply:
+               fusb_state_src_transition_supply(chip, evt);
+               break;
+       case policy_src_cap_response:
+               fusb_state_src_cap_response(chip, evt);
+               break;
+       case policy_src_transition_default:
+               fusb_state_src_transition_default(chip, evt);
+               break;
+       case policy_src_ready:
+               fusb_state_src_ready(chip, evt);
+               break;
+       case policy_src_get_sink_caps:
+               fusb_state_src_get_sink_cap(chip, evt);
+               break;
+       case policy_src_send_hardrst:
+               fusb_state_src_send_hardreset(chip, evt);
+               break;
+       case policy_src_send_softrst:
+               fusb_state_src_send_softreset(chip, evt);
+               break;
+
+       /* UFP */
+       case policy_snk_startup:
+               fusb_state_snk_startup(chip, evt);
+               break;
+       case policy_snk_discovery:
+               fusb_state_snk_discovery(chip, evt);
+               break;
+       case policy_snk_wait_caps:
+               fusb_state_snk_wait_caps(chip, evt);
+               break;
+       case policy_snk_evaluate_caps:
+               fusb_state_snk_evaluate_caps(chip, evt);
+               /* without break */
+       case policy_snk_select_cap:
+               fusb_state_snk_select_cap(chip, evt);
+               break;
+       case policy_snk_transition_sink:
+               fusb_state_snk_transition_sink(chip, evt);
+               break;
+       case policy_snk_transition_default:
+               fusb_state_snk_transition_default(chip, evt);
+               break;
+       case policy_snk_ready:
+               fusb_state_snk_ready(chip, evt);
+               break;
+       case policy_snk_send_hardrst:
+               fusb_state_snk_send_hardreset(chip, evt);
+               break;
+       case policy_snk_send_softrst:
+               fusb_state_snk_send_softreset(chip, evt);
+               break;
+
+       default:
+               break;
+       }
+
+BACK:
+       if (chip->work_continue) {
+               queue_work(chip->fusb30x_wq, &chip->work);
+               return;
+       }
+
+       if (!platform_get_device_irq_state(chip))
+               fusb_irq_enable(chip);
+       else
+               queue_work(chip->fusb30x_wq, &chip->work);
+}
+
+static irqreturn_t cc_interrupt_handler(int irq, void *dev_id)
+{
+       struct fusb30x_chip *chip = dev_id;
+
+       queue_work(chip->fusb30x_wq, &chip->work);
+       fusb_irq_disable(chip);
+       return IRQ_HANDLED;
+}
+
+static int fusb_initialize_gpio(struct fusb30x_chip *chip)
+{
+       chip->gpio_int = devm_gpiod_get_optional(chip->dev, "int-n", GPIOD_IN);
+       if (IS_ERR(chip->gpio_int))
+               return PTR_ERR(chip->gpio_int);
+
+       /* some board support vbus with other ways */
+       chip->gpio_vbus_5v = devm_gpiod_get_optional(chip->dev, "vbus-5v",
+                                                    GPIOD_OUT_LOW);
+       if (IS_ERR(chip->gpio_vbus_5v))
+               dev_warn(chip->dev,
+                        "Could not get named GPIO for VBus5V!\n");
+       else
+               gpiod_set_raw_value(chip->gpio_vbus_5v, 0);
+
+       chip->gpio_vbus_other = devm_gpiod_get_optional(chip->dev,
+                                                       "vbus-other",
+                                                       GPIOD_OUT_LOW);
+       if (IS_ERR(chip->gpio_vbus_other))
+               dev_warn(chip->dev,
+                        "Could not get named GPIO for VBusOther!\n");
+       else
+               gpiod_set_raw_value(chip->gpio_vbus_other, 0);
+
+       return 0;
+}
+
+static enum hrtimer_restart fusb_timer_handler(struct hrtimer *timer)
+{
+       int i;
+
+       for (i = 0; i < fusb30x_port_used; i++) {
+               if (timer == &fusb30x_port_info[i]->timer_state_machine) {
+                       if (fusb30x_port_info[i]->timer_state != T_DISABLED)
+                               fusb30x_port_info[i]->timer_state = 0;
+                       break;
+               }
+
+               if (timer == &fusb30x_port_info[i]->timer_mux_machine) {
+                       if (fusb30x_port_info[i]->timer_mux != T_DISABLED)
+                               fusb30x_port_info[i]->timer_mux = 0;
+                       break;
+               }
+       }
+
+       if (i != fusb30x_port_used)
+               queue_work(fusb30x_port_info[i]->fusb30x_wq,
+                          &fusb30x_port_info[i]->work);
+
+       return HRTIMER_NORESTART;
+}
+
+static void fusb_initialize_timer(struct fusb30x_chip *chip)
+{
+       hrtimer_init(&chip->timer_state_machine, CLOCK_MONOTONIC,
+                    HRTIMER_MODE_REL);
+       chip->timer_state_machine.function = fusb_timer_handler;
+
+       hrtimer_init(&chip->timer_mux_machine, CLOCK_MONOTONIC,
+                    HRTIMER_MODE_REL);
+       chip->timer_mux_machine.function = fusb_timer_handler;
+
+       chip->timer_state = T_DISABLED;
+       chip->timer_mux = T_DISABLED;
+}
+
+static void fusb302_work_func(struct work_struct *work)
+{
+       struct fusb30x_chip *chip;
+
+       chip = container_of(work, struct fusb30x_chip, work);
+       state_machine_typec(chip);
+}
+
+static int fusb30x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct fusb30x_chip *chip;
+       struct PD_CAP_INFO *pd_cap_info;
+       int ret;
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       if (fusb30x_port_used == 0xff)
+               return -1;
+
+       chip->port_num = fusb30x_port_used++;
+       fusb30x_port_info[chip->port_num] = chip;
+
+       chip->dev = &client->dev;
+       chip->regmap = devm_regmap_init_i2c(client, &fusb302_regmap_config);
+       if (IS_ERR(chip->regmap)) {
+               dev_err(&client->dev, "Failed to allocate regmap!\n");
+               return PTR_ERR(chip->regmap);
+       }
+
+       ret = fusb_initialize_gpio(chip);
+       if (ret)
+               return ret;
+
+       fusb_initialize_timer(chip);
+
+       chip->fusb30x_wq = create_workqueue("fusb302_wq");
+       INIT_WORK(&chip->work, fusb302_work_func);
+
+       tcpm_init(chip);
+       tcpm_set_rx_enable(chip, 0);
+       chip->conn_state = unattached;
+       tcpm_set_cc(chip, FUSB_MODE_DRP);
+
+       chip->n_caps_used = 1;
+       chip->source_power_supply[0] = 0x64;
+       chip->source_max_current[0] = 0x96;
+
+       /*
+        * these two variable should be 1 if support DRP,
+        * but now we do not support swap,
+        * it will be blanked in future
+        */
+       pd_cap_info = &chip->pd_cap_info;
+       pd_cap_info->dual_role_power = 0;
+       pd_cap_info->data_role_swap = 0;
+
+       pd_cap_info->externally_powered = 1;
+       pd_cap_info->usb_suspend_support = 0;
+       pd_cap_info->usb_communications_cap = 0;
+       pd_cap_info->supply_type = 0;
+       pd_cap_info->peak_current = 0;
+
+       chip->extcon = devm_extcon_dev_allocate(&client->dev, fusb302_cable);
+       if (IS_ERR(chip->extcon)) {
+               dev_err(&client->dev, "allocat extcon failed\n");
+               return PTR_ERR(chip->extcon);
+       }
+
+       ret = devm_extcon_dev_register(&client->dev, chip->extcon);
+       if (ret) {
+               dev_err(&client->dev, "failed to register extcon: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
+                                            EXTCON_PROP_USB_TYPEC_POLARITY);
+       if (ret) {
+               dev_err(&client->dev,
+                       "failed to set USB property capability: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
+                                            EXTCON_PROP_USB_TYPEC_POLARITY);
+       if (ret) {
+               dev_err(&client->dev,
+                       "failed to set USB_HOST property capability: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
+                                            EXTCON_PROP_USB_TYPEC_POLARITY);
+       if (ret) {
+               dev_err(&client->dev,
+                       "failed to set DISP_DP property capability: %d\n",
+                       ret);
+               return ret;
+       }
+
+       i2c_set_clientdata(client, chip);
+
+       spin_lock_init(&chip->irq_lock);
+       chip->enable_irq = 1;
+
+       chip->gpio_int_irq = gpiod_to_irq(chip->gpio_int);
+       if (chip->gpio_int_irq < 0) {
+               dev_err(&client->dev,
+                       "Unable to request IRQ for INT_N GPIO! %d\n",
+                       ret);
+               ret = chip->gpio_int_irq;
+               goto IRQ_ERR;
+       }
+
+       ret = devm_request_threaded_irq(&client->dev,
+                                       chip->gpio_int_irq,
+                                       NULL,
+                                       cc_interrupt_handler,
+                                       IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+                                       client->name,
+                                       chip);
+       if (ret) {
+               dev_err(&client->dev, "irq request failed\n");
+               goto IRQ_ERR;
+       }
+
+       dev_info(chip->dev, "port %d probe success\n", chip->port_num);
+
+       return 0;
+
+IRQ_ERR:
+       destroy_workqueue(chip->fusb30x_wq);
+       return ret;
+}
+
+static int fusb30x_remove(struct i2c_client *client)
+{
+       struct fusb30x_chip *chip = i2c_get_clientdata(client);
+
+       destroy_workqueue(chip->fusb30x_wq);
+       return 0;
+}
+
+static const struct of_device_id fusb30x_dt_match[] = {
+       { .compatible = FUSB30X_I2C_DEVICETREE_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(of, fusb30x_dt_match);
+
+static const struct i2c_device_id fusb30x_i2c_device_id[] = {
+       { FUSB30X_I2C_DRIVER_NAME, 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, fusb30x_i2c_device_id);
+
+static struct i2c_driver fusb30x_driver = {
+       .driver = {
+               .name = FUSB30X_I2C_DRIVER_NAME,
+               .of_match_table = of_match_ptr(fusb30x_dt_match),
+       },
+       .probe = fusb30x_probe,
+       .remove = fusb30x_remove,
+       .id_table = fusb30x_i2c_device_id,
+};
+
+module_i2c_driver(fusb30x_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("zain wang <zain.wang@rock-chips.com>");
+MODULE_DESCRIPTION("fusb302 typec pd driver");
diff --git a/drivers/mfd/fusb302.h b/drivers/mfd/fusb302.h
new file mode 100644 (file)
index 0000000..a3368a5
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from chrome ec and fairchild GPL fusb302 driver.
+ */
+
+#ifndef FUSB302_H
+#define FUSB302_H
+
+#include <linux/i2c.h>
+#include <linux/hrtimer.h>
+
+#define FUSB_VCONN_SUPPORT
+/* TODO: more modes would be added here later on */
+#define FUSB_HAVE_DRP
+
+const char *FUSB_DT_INTERRUPT_INTN =   "fsc_interrupt_int_n";
+#define FUSB_DT_GPIO_INTN              "fairchild,int_n"
+#define FUSB_DT_GPIO_VBUS_5V           "fairchild,vbus5v"
+#define FUSB_DT_GPIO_VBUS_OTHER                "fairchild,vbusOther"
+
+#define FUSB30X_I2C_DRIVER_NAME                "fusb302"
+#define FUSB30X_I2C_DEVICETREE_NAME    "fairchild,fusb302"
+
+/* FUSB300 Register Addresses */
+#define FUSB_REG_DEVICEID              0x01
+#define FUSB_REG_SWITCHES0             0x02
+#define FUSB_REG_SWITCHES1             0x03
+#define FUSB_REG_MEASURE               0x04
+#define FUSB_REG_SLICE                 0x05
+#define FUSB_REG_CONTROL0              0x06
+#define FUSB_REG_CONTROL1              0x07
+#define FUSB_REG_CONTROL2              0x08
+#define FUSB_REG_CONTROL3              0x09
+#define FUSB_REG_MASK                  0x0A
+#define FUSB_REG_POWER                 0x0B
+#define FUSB_REG_RESET                 0x0C
+#define FUSB_REG_OCPREG                        0x0D
+#define FUSB_REG_MASKA                 0x0E
+#define FUSB_REG_MASKB                 0x0F
+#define FUSB_REG_CONTROL4              0x10
+#define FUSB_REG_STATUS0A              0x3C
+#define FUSB_REG_STATUS1A              0x3D
+#define FUSB_REG_INTERRUPTA            0x3E
+#define FUSB_REG_INTERRUPTB            0x3F
+#define FUSB_REG_STATUS0               0x40
+#define FUSB_REG_STATUS1               0x41
+#define FUSB_REG_INTERRUPT             0x42
+#define FUSB_REG_FIFO                  0x43
+
+enum connection_state {
+       disabled = 0,
+       error_recovery,
+       unattached,
+       attach_wait_sink,
+       attach_wait_source,
+       attached_source,
+       attached_sink,
+
+       policy_src_startup,
+       policy_src_send_caps,
+       policy_src_discovery,
+       policy_src_negotiate_cap,
+       policy_src_cap_response,
+       policy_src_transition_supply,
+       policy_src_transition_default,
+
+       policy_src_ready,
+       policy_src_get_sink_caps,
+
+       policy_src_send_softrst,
+       policy_src_send_hardrst,
+
+       policy_snk_startup,
+       policy_snk_discovery,
+       policy_snk_wait_caps,
+       policy_snk_evaluate_caps,
+       policy_snk_select_cap,
+       policy_snk_transition_sink,
+       policy_snk_ready,
+
+       policy_snk_send_softrst,
+       policy_snk_send_hardrst,
+
+       policy_snk_transition_default,
+};
+
+#define SBF(s, v)              ((s) << (v))
+#define SWITCHES0_PDWN1                SBF(1, 0)
+#define SWITCHES0_PDWN2                SBF(1, 1)
+#define SWITCHES0_MEAS_CC1     SBF(1, 2)
+#define SWITCHES0_MEAS_CC2     SBF(1, 3)
+#define SWITCHES0_VCONN_CC1    SBF(1, 4)
+#define SWITCHES0_VCONN_CC2    SBF(1, 5)
+#define SWITCHES0_PU_EN1       SBF(1, 6)
+#define SWITCHES0_PU_EN2       SBF(1, 7)
+
+#define SWITCHES1_TXCC1                SBF(1, 0)
+#define SWITCHES1_TXCC2                SBF(1, 1)
+#define SWITCHES1_AUTO_CRC     SBF(1, 2)
+#define SWITCHES1_DATAROLE     SBF(1, 4)
+#define SWITCHES1_SPECREV      SBF(3, 5)
+#define SWITCHES1_POWERROLE    SBF(1, 7)
+
+#define MEASURE_MDAC           SBF(0x3f, 0)
+#define MEASURE_VBUS           SBF(1, 6)
+
+#define SLICE_SDAC             SBF(0x3f, 0)
+#define SLICE_SDAC_HYS         SBF(3, 6)
+
+#define CONTROL0_TX_START      SBF(1, 0)
+#define CONTROL0_AUTO_PRE      SBF(1, 1)
+#define CONTROL0_HOST_CUR      SBF(3, 2)
+#define CONTROL0_INT_MASK      SBF(1, 5)
+#define CONTROL0_TX_FLUSH      SBF(1, 6)
+
+#define CONTROL1_ENSOP1                SBF(1, 0)
+#define CONTROL1_ENSOP2                SBF(1, 1)
+#define CONTROL1_RX_FLUSH      SBF(1, 2)
+#define CONTROL1_BIST_MODE2    SBF(1, 4)
+#define CONTROL1_ENSOP1DB      SBF(1, 5)
+#define CONTROL1_ENSOP2DB      SBF(1, 6)
+
+#define CONTROL2_TOGGLE                SBF(1, 0)
+#define CONTROL2_MODE          SBF(3, 1)
+#define CONTROL2_WAKE_EN       SBF(1, 3)
+#define CONTROL2_TOG_RD_ONLY   SBF(1, 5)
+#define CONTROL2_TOG_SAVE_PWR1 SBF(1, 6)
+#define CONTROL2_TOG_SAVE_PWR2 SBF(1, 7)
+
+#define CONTROL3_AUTO_RETRY    SBF(1, 0)
+#define CONTROL3_N_RETRIES     SBF(3, 1)
+#define CONTROL3_AUTO_SOFTRESET        SBF(1, 3)
+#define CONTROL3_AUTO_HARDRESET        SBF(1, 4)
+#define CONTROL3_SEND_HARDRESET        SBF(1, 6)
+
+#define MASK_M_BC_LVL          SBF(1, 0)
+#define MASK_M_COLLISION       SBF(1, 1)
+#define MASK_M_WAKE            SBF(1, 2)
+#define MASK_M_ALERT           SBF(1, 3)
+#define MASK_M_CRC_CHK         SBF(1, 4)
+#define MASK_M_COMP_CHNG       SBF(1, 5)
+#define MASK_M_ACTIVITY                SBF(1, 6)
+#define MASK_M_VBUSOK          SBF(1, 7)
+
+#define POWER_PWR              SBF(0xf, 0)
+
+#define RESET_SW_RESET         SBF(1, 0)
+#define RESET_PD_RESET         SBF(1, 1)
+
+#define MASKA_M_HARDRST                SBF(1, 0)
+#define MASKA_M_SOFTRST                SBF(1, 1)
+#define MASKA_M_TXSENT         SBF(1, 2)
+#define MASKA_M_HARDSENT       SBF(1, 3)
+#define MASKA_M_RETRYFAIL      SBF(1, 4)
+#define MASKA_M_SOFTFAIL       SBF(1, 5)
+#define MASKA_M_TOGDONE                SBF(1, 6)
+#define MASKA_M_OCP_TEMP       SBF(1, 7)
+
+#define MASKB_M_GCRCSEND       SBF(1, 0)
+
+#define CONTROL4_TOG_USRC_EXIT SBF(1, 0)
+
+#define MDAC_1P6V              0x26
+
+#define STATUS0A_HARDRST       SBF(1, 0)
+#define STATUS0A_SOFTRST       SBF(1, 1)
+#define STATUS0A_POWER23       SBF(3, 2)
+#define STATUS0A_RETRYFAIL     SBF(1, 4)
+#define STATUS0A_SOFTFAIL      SBF(1, 5)
+#define STATUS0A_TOGDONE       SBF(1, 6)
+#define STATUS0A_M_OCP_TEMP    SBF(1, 7)
+
+#define STATUS1A_RXSOP         SBF(1, 0)
+#define STATUS1A_RXSOP1DB      SBF(1, 1)
+#define STATUS1A_RXSOP2DB      SBF(1, 2)
+#define STATUS1A_TOGSS         SBF(7, 3)
+
+#define INTERRUPTA_HARDRST     SBF(1, 0)
+#define INTERRUPTA_SOFTRST     SBF(1, 1)
+#define INTERRUPTA_TXSENT      SBF(1, 2)
+#define INTERRUPTA_HARDSENT    SBF(1, 3)
+#define INTERRUPTA_RETRYFAIL   SBF(1, 4)
+#define INTERRUPTA_SOFTFAIL    SBF(1, 5)
+#define INTERRUPTA_TOGDONE     SBF(1, 6)
+#define INTERRUPTA_OCP_TEMP    SBF(1, 7)
+
+#define INTERRUPTB_GCRCSENT    SBF(1, 0)
+
+#define STATUS0_BC_LVL         SBF(3, 0)
+#define STATUS0_WAKE           SBF(1, 2)
+#define STATUS0_ALERT          SBF(1, 3)
+#define STATUS0_CRC_CHK                SBF(1, 4)
+#define STATUS0_COMP           SBF(1, 5)
+#define STATUS0_ACTIVITY       SBF(1, 6)
+#define STATUS0_VBUSOK         SBF(1, 7)
+
+#define STATUS1_OCP            SBF(1, 0)
+#define STATUS1_OVRTEMP                SBF(1, 1)
+#define STATUS1_TX_FULL                SBF(1, 2)
+#define STATUS1_TX_EMPTY       SBF(1, 3)
+#define STATUS1_RX_FULL                SBF(1, 4)
+#define STATUS1_RX_EMPTY       SBF(1, 5)
+#define STATUS1_RXSOP1         SBF(1, 6)
+#define STATUS1_RXSOP2         SBF(1, 7)
+
+#define INTERRUPT_BC_LVL       SBF(1, 0)
+#define INTERRUPT_COLLISION    SBF(1, 1)
+#define INTERRUPT_WAKE         SBF(1, 2)
+#define INTERRUPT_ALERT                SBF(1, 3)
+#define INTERRUPT_CRC_CHK      SBF(1, 4)
+#define INTERRUPT_COMP_CHNG    SBF(1, 5)
+#define INTERRUPT_ACTIVITY     SBF(1, 6)
+#define INTERRUPT_VBUSOK       SBF(1, 7)
+
+#define FUSB_TKN_TXON          0xa1
+#define FUSB_TKN_SYNC1         0x12
+#define FUSB_TKN_SYNC2         0x13
+#define FUSB_TKN_SYNC3         0x1b
+#define FUSB_TKN_RST1          0x15
+#define FUSB_TKN_RST2          0x16
+#define FUSB_TKN_PACKSYM       0x80
+#define FUSB_TKN_JAMCRC                0xff
+#define FUSB_TKN_EOP           0x14
+#define FUSB_TKN_TXOFF         0xfe
+
+/* USB PD Control Message Types */
+#define CONTROLMESSAGE         0
+#define CMT_GOODCRC            1
+#define CMT_GOTOMIN            2
+#define CMT_ACCEPT             3
+#define CMT_REJECT             4
+#define CMT_PING               5
+#define CMT_PS_RDY             6
+#define CMT_GETSOURCECAP       7
+#define CMT_GETSINKCAP         8
+#define CMT_DR_SWAP            9
+#define CMT_PR_SWAP            10
+#define CMT_VCONN_SWAP         11
+#define CMT_WAIT               12
+#define CMT_SOFTRESET          13
+
+/* USB PD Data Message Types */
+#define DATAMESSAGE            1
+#define DMT_SOURCECAPABILITIES 1
+#define DMT_REQUEST            2
+#define DMT_BIST               3
+#define DMT_SINKCAPABILITIES   4
+#define DMT_VENDERDEFINED      15
+
+/* VDM Command Types */
+#define VDM_DISCOVERY_ID       0X01
+#define VDM_DISCOVERY_SVIDS    0X02
+#define VDM_DISCOVERY_MODES    0X03
+#define VDM_ENTER_MODE         0X04
+#define VDM_EXIT_MODE          0X05
+#define VDM_ATTENTION          0X06
+#define VDM_DP_STATUS_UPDATE   0X10
+#define VDM_DP_CONFIG          0X11
+
+#define VDM_TYPE_INIT          0
+#define VDM_TYPE_ACK           1
+#define VDM_TYPE_NACK          2
+#define VDM_TYPE_BUSY          3
+
+#define N_DEBOUNCE_CNT         (10 - 1)
+#define N_CAPS_COUNT           50
+#define N_HARDRESET_COUNT      0
+
+#define T_NO_RESPONSE          5000
+#define T_SRC_RECOVER          830
+#define T_TYPEC_SEND_SOURCECAP 3
+#define T_SENDER_RESPONSE      30
+#define T_SRC_TRANSITION       30
+#define T_TYPEC_SINK_WAIT_CAP  500
+#define T_PS_TRANSITION                500
+#define T_BMC_TIMEOUT          5
+#define T_PS_HARD_RESET_MAX    35
+#define T_SAFE_0V              650
+#define T_SRC_TURN_ON          275
+#define T_SRC_RECOVER_MAX      1000
+
+#define T_NO_TRIGGER           500
+#define T_DISABLED             0xffff
+
+#define PD_HEADER_CNT(header)          (((header) >> 12) & 7)
+#define PD_HEADER_TYPE(header)         ((header) & 0xF)
+#define PD_HEADER_ID(header)           (((header) >> 9) & 7)
+
+#define VDM_HEADER_TYPE(header)                (((header) >> 6) & 3)
+#define VDMHEAD_CMD_TYPE_MASK          (3 << 6)
+#define VDMHEAD_CMD_MASK               (0x1f << 0)
+#define VDMHEAD_STRUCT_TYPE_MASK       BIT(15)
+
+#define GET_VDMHEAD_CMD_TYPE(head)     ((head & VDMHEAD_CMD_TYPE_MASK) >> 6)
+#define GET_VDMHEAD_CMD(head)          (head & VDMHEAD_CMD_MASK)
+#define GET_VDMHEAD_STRUCT_TYPE(head)  ((head & VDMHEAD_STRUCT_TYPE_MASK) >> 15)
+
+#define VDM_IDHEAD_USBVID_MASK         (0xffff << 0)
+#define VDM_IDHEAD_MODALSUPPORT_MASK   BIT(26)
+#define VDM_IDHEAD_PRODUCTTYPE         (7 << 27)
+#define VDM_IDHEAD_USBDEVICE           BIT(30)
+#define VDM_IDHEAD_USBHOST             BIT(30)
+
+#define CAP_POWER_TYPE(PDO)            ((PDO >> 30) & 3)
+#define CAP_FPDO_VOLTAGE(PDO)          ((PDO >> 10) & 0x3ff)
+#define CAP_VPDO_VOLTAGE(PDO)          ((PDO >> 20) & 0x3ff)
+#define CAP_FPDO_CURRENT(PDO)          ((PDO >> 0) & 0x3ff)
+#define CAP_VPDO_CURRENT(PDO)          ((PDO >> 0) & 0x3ff)
+
+enum CC_ORIENTATION {
+       NONE,
+       CC1,
+       CC2,
+};
+
+struct notify_info {
+       enum CC_ORIENTATION orientation;
+       /* 0 UFP : 1 DFP */
+       bool power_role;
+       bool data_role;
+
+       bool is_cc_connected;
+       bool is_pd_connected;
+
+       bool is_enter_mode;
+       int pin_assignment_support;
+       int pin_assignment_def;
+       bool attention;
+};
+
+enum tx_state {
+       tx_idle,
+       tx_busy,
+       tx_failed,
+       tx_success
+};
+
+struct PD_CAP_INFO {
+       u32 peak_current;
+       u32 specification_revision;
+       u32 externally_powered;
+       u32 usb_suspend_support;
+       u32 usb_communications_cap;
+       u32 dual_role_power;
+       u32 data_role_swap;
+       u32 supply_type;
+};
+
+struct fusb30x_chip {
+       struct i2c_client *client;
+       struct device *dev;
+       struct regmap *regmap;
+       struct work_struct work;
+       struct workqueue_struct *fusb30x_wq;
+       struct hrtimer timer_state_machine;
+       struct hrtimer timer_mux_machine;
+       struct PD_CAP_INFO pd_cap_info;
+       struct notify_info notify;
+       struct notify_info notify_cmp;
+       struct extcon_dev *extcon;
+       enum connection_state conn_state;
+       struct gpio_desc *gpio_vbus_5v;
+       struct gpio_desc *gpio_vbus_other;
+       struct gpio_desc *gpio_int;
+       int timer_state;
+       int timer_mux;
+       int port_num;
+       int work_continue;
+       spinlock_t irq_lock;
+       int gpio_int_irq;
+       int enable_irq;
+
+       /*
+        * ---------------------------------
+        * | role 0x03 << 2, | cc_use 0x03 |
+        * | src  1 << 2,    | cc1 1       |
+        * | snk  2 << 2,    | cc2 2       |
+        * ---------------------------------
+        */
+       u8 cc_state;
+       int cc1;
+       int cc2;
+       /* 0 cc1 : 1 cc2 */
+       bool cc_polarity;
+       u8 val_tmp;
+       u8 debounce_cnt;
+       int sub_state;
+       int caps_counter;
+       u32 send_load[7];
+       u32 rec_load[7];
+       u16 send_head;
+       u16 rec_head;
+       int msg_id;
+       enum tx_state tx_state;
+       int hardrst_count;
+       u32 source_power_supply[7];
+       /* 50mv unit */
+       u32 source_max_current[7];
+       /* 10ma uint*/
+       int pos_power;
+       /*
+        * if PartnerCap[0] == 0xffffffff
+        * show Partner Device do not support supply
+        */
+       u32 partner_cap[7];
+       int n_caps_used;
+       int vdm_state;
+       int vdm_substate;
+       int vdm_send_state;
+       u32 dp_status;
+       u16 vdm_svid[12];
+       int vdm_svid_num;
+       u32 vdm_id;
+       u8 chip_id;
+       bool vconn_enabled;
+       int togdone_pullup;
+};
+
+#endif /* FUSB302_H */
+