armbian-build/patch/kernel/rk3399-dev/add-fusb30x-driver.patch
5kft a9f6207a3e [rk3399-dev] bring in rockchip fusb302 driver; enable module in build
This change brings in the Rockchip version of the FUSB302 USB controller driver into
the kernel (with appropriate changes to enable it to build in kernel 4.19.y), and
configures the module build for the rk3399 kernel.  This provides proper operation
of all <&fusb0> "extcon" references for the rk3399-nanopi4 boards, thereby enabling
all board USB host ports (e.g., now including <&u2phy0>) to now function properly.
2018-10-21 18:48:38 +00:00

4048 lines
107 KiB
Diff

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 1abf76be2..7ad8b090c 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -92,6 +92,8 @@ source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/fsl-dpaa2/Kconfig"
+source "drivers/staging/fusb30x/Kconfig"
+
source "drivers/staging/wilc1000/Kconfig"
source "drivers/staging/most/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ab0cbe881..2e308d901 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/
+obj-$(CONFIG_FUSB_30X) += fusb30x/
obj-$(CONFIG_WILC1000) += wilc1000/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_KS7010) += ks7010/
diff --git a/drivers/staging/fusb30x/Kconfig b/drivers/staging/fusb30x/Kconfig
new file mode 100644
index 000000000..5bb75270f
--- /dev/null
+++ b/drivers/staging/fusb30x/Kconfig
@@ -0,0 +1,10 @@
+config FUSB_30X
+ tristate "Fairchild FUSB30X Type-C chip driver"
+ depends on I2C
+ help
+ This is a driver for the Fairchild FUSB302 Type-C chip. It supports
+ USB Type-C PD functionality controlled using I2C.
+
+ This driver supports extcon reporting not yet implemented in the
+ mainline FUSB302 driver.
+
diff --git a/drivers/staging/fusb30x/Makefile b/drivers/staging/fusb30x/Makefile
new file mode 100644
index 000000000..1c8e35df3
--- /dev/null
+++ b/drivers/staging/fusb30x/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_FUSB_30X) += fusb30x.o
diff --git a/drivers/staging/fusb30x/fusb30x.c b/drivers/staging/fusb30x/fusb30x.c
new file mode 100644
index 000000000..56d22648c
--- /dev/null
+++ b/drivers/staging/fusb30x/fusb30x.c
@@ -0,0 +1,3434 @@
+/*
+ * 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/extcon-provider.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+
+#include "fusb30x.h"
+
+#define FUSB302_MAX_REG (FUSB_REG_FIFO + 50)
+#define FUSB_MS_TO_NS(x) ((s64)x * 1000 * 1000)
+
+#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 EVENT_DELAY_CC BIT(8)
+#define FLAG_EVENT (EVENT_RX | EVENT_TIMER_MUX | \
+ EVENT_TIMER_STATE)
+
+#define PACKET_IS_CONTROL_MSG(header, type) \
+ (PD_HEADER_CNT(header) == 0 && \
+ PD_HEADER_TYPE(header) == type)
+
+#define PACKET_IS_DATA_MSG(header, type) \
+ (PD_HEADER_CNT(header) != 0 && \
+ PD_HEADER_TYPE(header) == type)
+
+/*
+ * DisplayPort modes capabilities
+ * -------------------------------
+ * <31:24> : Reserved (always 0).
+ * <23:16> : UFP_D pin assignment supported
+ * <15:8> : DFP_D pin assignment supported
+ * <7> : USB 2.0 signaling (0b=yes, 1b=no)
+ * <6> : Plug | Receptacle (0b == plug, 1b == receptacle)
+ * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling
+ * Other bits are reserved.
+ * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both )
+ */
+#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \
+ : (((x) >> 8) & 0x3f))
+#define PD_DP_SIGNAL_GEN2(x) (((x) >> 3) & 0x1)
+
+#define MODE_DP_PIN_A BIT(0)
+#define MODE_DP_PIN_B BIT(1)
+#define MODE_DP_PIN_C BIT(2)
+#define MODE_DP_PIN_D BIT(3)
+#define MODE_DP_PIN_E BIT(4)
+#define MODE_DP_PIN_F BIT(5)
+
+/* Pin configs B/D/F support multi-function */
+#define MODE_DP_PIN_MF_MASK (MODE_DP_PIN_B | MODE_DP_PIN_D | MODE_DP_PIN_F)
+/* Pin configs A/B support BR2 signaling levels */
+#define MODE_DP_PIN_BR2_MASK (MODE_DP_PIN_A | MODE_DP_PIN_B)
+/* Pin configs C/D/E/F support DP signaling levels */
+#define MODE_DP_PIN_DP_MASK (MODE_DP_PIN_C | MODE_DP_PIN_D | \
+ MODE_DP_PIN_E | MODE_DP_PIN_F)
+
+/*
+ * DisplayPort Status VDO
+ * ----------------------
+ * <31:9> : Reserved (always 0).
+ * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0.
+ * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH
+ * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit
+ * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP
+ * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred.
+ * <3> : enabled : is DPout on/off.
+ * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM
+ * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled.
+ * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both.
+ */
+#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 0x1)
+#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 0x1)
+#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 0x1)
+
+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_CHG_USB_FAST,
+ EXTCON_DISP_DP,
+ EXTCON_NONE,
+};
+
+static void fusb_set_pos_power(struct fusb30x_chip *chip, int max_vol,
+ int max_cur)
+{
+ int i;
+ int pos_find;
+ int tmp;
+
+ pos_find = 0;
+ for (i = PD_HEADER_CNT(chip->rec_head) - 1; i >= 0; i--) {
+ switch (CAP_POWER_TYPE(chip->rec_load[i])) {
+ case 0:
+ /* Fixed Supply */
+ if ((CAP_FPDO_VOLTAGE(chip->rec_load[i]) * 50) <=
+ max_vol &&
+ (CAP_FPDO_CURRENT(chip->rec_load[i]) * 10) <=
+ max_cur) {
+ chip->pos_power = i + 1;
+ tmp = CAP_FPDO_VOLTAGE(chip->rec_load[i]);
+ chip->pd_output_vol = tmp * 50;
+ tmp = CAP_FPDO_CURRENT(chip->rec_load[i]);
+ chip->pd_output_cur = tmp * 10;
+ pos_find = 1;
+ }
+ break;
+ case 1:
+ /* Battery */
+ if ((CAP_VPDO_VOLTAGE(chip->rec_load[i]) * 50) <=
+ max_vol &&
+ (CAP_VPDO_CURRENT(chip->rec_load[i]) * 10) <=
+ max_cur) {
+ chip->pos_power = i + 1;
+ tmp = CAP_VPDO_VOLTAGE(chip->rec_load[i]);
+ chip->pd_output_vol = tmp * 50;
+ tmp = CAP_VPDO_CURRENT(chip->rec_load[i]);
+ chip->pd_output_cur = tmp * 10;
+ pos_find = 1;
+ }
+ break;
+ default:
+ /* not meet battery caps */
+ break;
+ }
+ if (pos_find)
+ break;
+ }
+}
+
+static int fusb302_set_pos_power_by_charge_ic(struct fusb30x_chip *chip)
+{
+ struct power_supply *psy = NULL;
+ union power_supply_propval val;
+ enum power_supply_property psp;
+ int max_vol, max_cur;
+
+ max_vol = 0;
+ max_cur = 0;
+ psy = power_supply_get_by_phandle(chip->dev->of_node, "charge-dev");
+ if (!psy || IS_ERR(psy))
+ return -1;
+
+ psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
+ if (power_supply_get_property(psy, psp, &val) == 0)
+ max_vol = val.intval / 1000;
+
+ psp = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
+ if (power_supply_get_property(psy, psp, &val) == 0)
+ max_cur = val.intval / 1000;
+
+ if (max_vol > 0 && max_cur > 0)
+ fusb_set_pos_power(chip, max_vol, max_cur);
+
+ return 0;
+}
+
+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 = false, flip = false, dfp = false, ufp = false,
+ dp = false, usb_ss = false, hpd = false;
+ union extcon_property_value property;
+
+ if (chip->notify.is_cc_connected)
+ chip->notify.orientation =
+ (chip->cc_polarity == TYPEC_POLARITY_CC1) ?
+ CC1 : CC2;
+
+ /* avoid notify repeated */
+ if (memcmp(&chip->notify, &chip->notify_cmp,
+ sizeof(struct notify_info))) {
+ dump_notify_info(chip);
+ chip->notify.attention = false;
+ memcpy(&chip->notify_cmp, &chip->notify,
+ sizeof(struct notify_info));
+
+ plugged = chip->notify.is_cc_connected ||
+ chip->notify.is_pd_connected;
+ if (chip->notify.orientation != NONE)
+ flip = (chip->notify.orientation == CC1) ? false : true;
+ dp = chip->notify.is_enter_mode;
+
+ if (dp) {
+ dfp = true;
+ usb_ss = (chip->notify.pin_assignment_def &
+ MODE_DP_PIN_MF_MASK) ? true : false;
+ hpd = GET_DP_STATUS_HPD(chip->notify.dp_status);
+ } else if (chip->notify.data_role) {
+ dfp = true;
+ usb_ss = true;
+ } else if (plugged) {
+ ufp = true;
+ usb_ss = true;
+ }
+
+ 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);
+
+ property.intval = usb_ss;
+ extcon_set_property(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS, property);
+ extcon_set_property(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS, property);
+ extcon_set_property(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS, 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 && hpd);
+ extcon_sync(chip->extcon, EXTCON_USB);
+ extcon_sync(chip->extcon, EXTCON_USB_HOST);
+ extcon_sync(chip->extcon, EXTCON_DISP_DP);
+ if (chip->notify.power_role == POWER_ROLE_SINK &&
+ chip->notify.is_pd_connected &&
+ chip->pd_output_vol > 0 && chip->pd_output_cur > 0) {
+ extcon_set_state(chip->extcon, EXTCON_CHG_USB_FAST, true);
+ property.intval =
+ (chip->pd_output_cur << 15 |
+ chip->pd_output_vol);
+ extcon_set_property(chip->extcon, EXTCON_CHG_USB_FAST,
+ EXTCON_PROP_USB_TYPEC_POLARITY,
+ property);
+ extcon_sync(chip->extcon, EXTCON_CHG_USB_FAST);
+ }
+ }
+}
+
+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)
+{
+ bool gpio_vbus_value = false;
+
+ gpio_vbus_value = gpiod_get_value(chip->gpio_vbus_5v);
+ 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);
+
+ if (chip->gpio_discharge && !vbus_5v && gpio_vbus_value) {
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(20);
+ gpiod_set_value(chip->gpio_discharge, 0);
+ }
+}
+
+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 |= EVENT_WORK_CONTINUE;
+}
+
+static int tcpm_get_message(struct fusb30x_chip *chip)
+{
+ u8 buf[32];
+ int len;
+
+ do {
+ 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);
+ /* ignore good_crc message */
+ } while (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_GOODCRC));
+
+ memcpy(chip->rec_load, buf, len);
+
+ return 0;
+}
+
+static void fusb302_flush_rx_fifo(struct fusb30x_chip *chip)
+{
+ regmap_write(chip->regmap, FUSB_REG_CONTROL1, CONTROL1_RX_FLUSH);
+}
+
+static int tcpm_get_cc(struct fusb30x_chip *chip, int *CC1, int *CC2)
+{
+ u32 val;
+ int *CC_MEASURE;
+ u32 store;
+
+ *CC1 = TYPEC_CC_VOLT_OPEN;
+ *CC2 = TYPEC_CC_VOLT_OPEN;
+
+ if (chip->cc_state & CC_STATE_TOGSS_CC1)
+ CC_MEASURE = CC1;
+ else
+ CC_MEASURE = CC2;
+
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) {
+ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &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, &val);
+ val &= STATUS0_BC_LVL;
+ *CC1 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN;
+
+ 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, &val);
+ val &= STATUS0_BC_LVL;
+ *CC2 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN;
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2,
+ store);
+ } else {
+ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store);
+ val = store;
+ val &= ~(SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2);
+ if (chip->cc_state & CC_STATE_TOGSS_CC1) {
+ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1;
+ } else {
+ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2;
+ }
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, val);
+
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP) {
+ int retry = 3;
+ int comp_times = 0;
+
+ while (retry--) {
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high);
+ usleep_range(250, 300);
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP) {
+ comp_times++;
+ if (comp_times == 3) {
+ *CC_MEASURE = TYPEC_CC_VOLT_OPEN;
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store);
+ }
+ }
+ }
+ } else {
+ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_low);
+ regmap_read(chip->regmap, FUSB_REG_MEASURE, &val);
+ usleep_range(250, 300);
+
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+
+ if (val & STATUS0_COMP)
+ *CC_MEASURE = TYPEC_CC_VOLT_RD;
+ else
+ *CC_MEASURE = TYPEC_CC_VOLT_RA;
+ }
+ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store);
+ regmap_write(chip->regmap, FUSB_REG_MEASURE,
+ chip->cc_meas_high);
+ }
+
+ return 0;
+}
+
+static void tcpm_set_cc_pull_mode(struct fusb30x_chip *chip, enum CC_MODE mode)
+{
+ u8 val;
+
+ switch (mode) {
+ case CC_PULL_UP:
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val = SWITCHES0_PU_EN1;
+ else
+ val = SWITCHES0_PU_EN2;
+ break;
+ case CC_PULL_DOWN:
+ val = SWITCHES0_PDWN1 | SWITCHES0_PDWN2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 |
+ SWITCHES0_PDWN1 | SWITCHES0_PDWN2,
+ val);
+
+ if (chip->cc_meas_high && mode == CC_PULL_UP)
+ regmap_write(chip->regmap, FUSB_REG_MEASURE,
+ chip->cc_meas_high);
+}
+
+static int tcpm_set_cc(struct fusb30x_chip *chip, enum role_mode mode)
+{
+ switch (mode) {
+ case ROLE_MODE_DFP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_DFP | CONTROL2_TOG_RD_ONLY);
+ break;
+ case ROLE_MODE_UFP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_UFP);
+ break;
+ case ROLE_MODE_DRP:
+ tcpm_set_cc_pull_mode(chip, CC_PULL_NONE);
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2,
+ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY,
+ CONTROL2_MODE_DRP | CONTROL2_TOG_RD_ONLY);
+ break;
+ default:
+ dev_err(chip->dev, "%s: Unsupport cc mode %d\n",
+ __func__, mode);
+ return -EINVAL;
+ break;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, CONTROL2_TOGGLE,
+ CONTROL2_TOGGLE);
+
+ return 0;
+}
+
+static int tcpm_set_rx_enable(struct fusb30x_chip *chip, int enable)
+{
+ u8 val = 0;
+
+ if (enable) {
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1;
+ else
+ val |= SWITCHES0_MEAS_CC2;
+ 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 {
+ 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,
+ enum typec_cc_polarity polarity)
+{
+ u8 val = 0;
+
+ if (chip->vconn_enabled) {
+ if (polarity)
+ val |= SWITCHES0_VCONN_CC1;
+ else
+ val |= SWITCHES0_VCONN_CC2;
+ }
+
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) {
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1;
+ else
+ val |= SWITCHES0_MEAS_CC2;
+ } else {
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1;
+ else
+ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2;
+ }
+
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2 |
+ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 |
+ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2,
+ val);
+
+ val = 0;
+ if (polarity == TYPEC_POLARITY_CC1)
+ val |= SWITCHES1_TXCC1;
+ else
+ val |= SWITCHES1_TXCC2;
+ 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) {
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ val = SWITCHES0_VCONN_CC2;
+ else
+ val = SWITCHES0_VCONN_CC1;
+ }
+ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0,
+ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2,
+ val);
+ chip->vconn_enabled = (bool)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_select_rp_value(struct fusb30x_chip *chip, u32 rp)
+{
+ u32 control0_reg;
+
+ regmap_read(chip->regmap, FUSB_REG_CONTROL0, &control0_reg);
+
+ control0_reg &= ~CONTROL0_HOST_CUR;
+ /*
+ * according to the host current, the compare value is different
+ * Fusb302 datasheet Table 3
+ */
+ switch (rp) {
+ /*
+ * host pull up current is 80ua , high voltage is 1.596v,
+ * low is 0.21v
+ */
+ case TYPEC_RP_USB:
+ chip->cc_meas_high = 0x26;
+ chip->cc_meas_low = 0x5;
+ control0_reg |= CONTROL0_HOST_CUR_USB;
+ break;
+ /*
+ * host pull up current is 330ua , high voltage is 2.604v,
+ * low is 0.798v
+ */
+ case TYPEC_RP_3A0:
+ chip->cc_meas_high = 0x3e;
+ chip->cc_meas_low = 0x13;
+ control0_reg |= CONTROL0_HOST_CUR_3A0;
+ break;
+ /*
+ * host pull up current is 180ua , high voltage is 1.596v,
+ * low is 0.42v
+ */
+ case TYPEC_RP_1A5:
+ default:
+ chip->cc_meas_high = 0x26;
+ chip->cc_meas_low = 0xa;
+ control0_reg |= CONTROL0_HOST_CUR_1A5;
+ break;
+ }
+
+ regmap_write(chip->regmap, FUSB_REG_CONTROL0, control0_reg);
+}
+
+static int tcpm_check_vbus(struct fusb30x_chip *chip)
+{
+ u32 val;
+
+ /* Read status register */
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+
+ return (val & STATUS0_VBUSOK) ? 1 : 0;
+}
+
+static void tcpm_init(struct fusb30x_chip *chip)
+{
+ u8 val;
+ u32 tmp;
+
+ regmap_read(chip->regmap, FUSB_REG_DEVICEID, &tmp);
+ chip->chip_id = (u8)tmp;
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ chip->notify.is_cc_connected = false;
+ 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_COLLISION | MASK_M_ALERT | MASK_M_VBUSOK);
+ regmap_write(chip->regmap, FUSB_REG_MASK, val);
+
+ val = 0xff;
+ val &= ~(MASKA_M_RETRYFAIL | MASKA_M_HARDSENT | MASKA_M_TXSENT |
+ MASKA_M_HARDRST | MASKA_M_TOGDONE);
+ regmap_write(chip->regmap, FUSB_REG_MASKA, val);
+
+ val = ~MASKB_M_GCRCSEND;
+ regmap_write(chip->regmap, FUSB_REG_MASKB, val);
+
+ tcpm_select_rp_value(chip, TYPEC_RP_1A5);
+ /* Interrupts Enable */
+ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL0, CONTROL0_INT_MASK,
+ ~CONTROL0_INT_MASK);
+
+ 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 = VDM_STATE_DISCOVERY_ID;
+ 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, u32 *evt)
+{
+ int interrupt, interrupta, interruptb;
+ u32 val;
+ static int retry;
+
+ 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_COMP_CHNG) &&
+ (!(chip->cc_state & CC_STATE_TOGSS_IS_UFP))) {
+ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val);
+ if (val & STATUS0_COMP)
+ *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);
+ }
+
+ if (interrupta & INTERRUPTA_TXSENT) {
+ *evt |= EVENT_TX;
+ 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) {
+ /*
+ * The fusb PD should be reset once to sync adapter PD
+ * signal after fusb sent hard reset cmd.This is not PD
+ * device if reset failed.
+ */
+ if (!retry) {
+ retry = 1;
+ fusb302_pd_reset(chip);
+ pd_execute_hard_reset(chip);
+ } else {
+ retry = 0;
+ chip->tx_state = tx_success;
+ chip->timer_state = T_DISABLED;
+ *evt |= EVENT_TX;
+ }
+ }
+}
+
+static void mux_alert(struct fusb30x_chip *chip, u32 *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 |= chip->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);
+ set_state(chip, unattached);
+ tcpm_set_cc(chip, chip->role);
+
+ /* claer notify_info */
+ memset(&chip->notify, 0, sizeof(struct notify_info));
+ platform_fusb_notify(chip);
+
+ if (chip->gpio_discharge)
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(100);
+ if (chip->gpio_discharge)
+ gpiod_set_value(chip->gpio_discharge, 0);
+
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK,
+ MASK_M_COMP_CHNG, MASK_M_COMP_CHNG);
+ chip->try_role_complete = false;
+}
+
+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);
+ }
+}
+
+/*
+ * This algorithm defaults to choosing higher pin config over lower ones in
+ * order to prefer multi-function if desired.
+ *
+ * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG
+ * -------------------------------------------------------------
+ * A | USB G2 | ? | no | 00_0001
+ * B | USB G2 | ? | yes | 00_0010
+ * C | DP | CONVERTED | no | 00_0100
+ * D | PD | CONVERTED | yes | 00_1000
+ * E | DP | DP | no | 01_0000
+ * F | PD | DP | yes | 10_0000
+ *
+ * if UFP has NOT asserted multi-function preferred code masks away B/D/F
+ * leaving only A/C/E. For single-output dongles that should leave only one
+ * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP
+ * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C
+ * receptacle must always choose C/D in those cases.
+ */
+static int pd_dfp_dp_get_pin_assignment(struct fusb30x_chip *chip,
+ uint32_t caps, uint32_t status)
+{
+ uint32_t pin_caps;
+
+ /* revisit with DFP that can be a sink */
+ pin_caps = PD_DP_PIN_CAPS(caps);
+
+ /* if don't want multi-function then ignore those pin configs */
+ if (!PD_VDO_DPSTS_MF_PREF(status))
+ pin_caps &= ~MODE_DP_PIN_MF_MASK;
+
+ /* revisit if DFP drives USB Gen 2 signals */
+ if (PD_DP_SIGNAL_GEN2(caps))
+ pin_caps &= ~MODE_DP_PIN_DP_MASK;
+ else
+ pin_caps &= ~MODE_DP_PIN_BR2_MASK;
+
+ /* if C/D present they have precedence over E/F for USB-C->USB-C */
+ if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D))
+ pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F);
+
+ /* returns undefined for zero */
+ if (!pin_caps)
+ return 0;
+
+ /* choosing higher pin config over lower ones */
+ return 1 << (31 - __builtin_clz(pin_caps));
+}
+
+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->notify.pin_assignment_def =
+ pd_dfp_dp_get_pin_assignment(chip, chip->notify.dp_caps,
+ chip->notify.dp_status);
+
+ chip->send_load[1] = (chip->notify.pin_assignment_def << 8) |
+ (1 << 2) | 2;
+ dev_dbg(chip->dev, "DisplayPort Configurations: 0x%08x\n",
+ chip->send_load[1]);
+ break;
+ default:
+ break;
+ }
+}
+
+static enum tx_state policy_send_hardrst(struct fusb30x_chip *chip, u32 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)) {
+ dev_warn(chip->dev, "unknown unstructed vdm message\n");
+ return;
+ }
+
+ switch (GET_VDMHEAD_CMD_TYPE(vdm_header)) {
+ case VDM_TYPE_INIT:
+ switch (GET_VDMHEAD_CMD(vdm_header)) {
+ case VDM_ATTENTION:
+ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]);
+ dev_info(chip->dev, "attention, dp_status %x\n",
+ chip->rec_load[1]);
+ chip->notify.attention = true;
+ platform_fusb_notify(chip);
+ 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
+ */
+ tmp = chip->rec_load[1];
+
+ if ((!((tmp >> 8) & 0x3f)) &&
+ (!((tmp >> 16) & 0x3f))) {
+ chip->val_tmp |= 1;
+ break;
+ }
+ chip->notify.dp_caps = chip->rec_load[1];
+ chip->notify.pin_assignment_def = 0;
+ chip->notify.pin_assignment_support =
+ PD_DP_PIN_CAPS(tmp);
+ chip->val_tmp |= 1;
+ dev_dbg(chip->dev,
+ "DisplayPort Capabilities: 0x%08x\n",
+ chip->rec_load[1]);
+ }
+ break;
+ case VDM_ENTER_MODE:
+ chip->val_tmp = 1;
+ break;
+ case VDM_DP_STATUS_UPDATE:
+ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]);
+ dev_dbg(chip->dev, "DisplayPort Status: 0x%08x\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 = true;
+ 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 = VDM_STATE_ERR;
+ break;
+ }
+}
+
+static int vdm_send_discoveryid(struct fusb30x_chip *chip, u32 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->vdm_id) {
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_discoverysvid(struct fusb30x_chip *chip, u32 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, sizeof(chip->vdm_svid));
+ 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->vdm_svid_num) {
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_discoverymodes(struct fusb30x_chip *chip, u32 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp & 1) {
+ chip->val_tmp &= 0xfe;
+ chip->val_tmp += 2;
+ chip->vdm_send_state = 0;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev,
+ "VDM_DISCOVERY_MODES time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ } else {
+ chip->val_tmp = 0;
+ return 0;
+ }
+
+ return -EINPROGRESS;
+}
+
+static int vdm_send_entermode(struct fusb30x_chip *chip, u32 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 = false;
+ 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_ENTER_MODE time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_getdpstatus(struct fusb30x_chip *chip, u32 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+static int vdm_send_dpconfig(struct fusb30x_chip *chip, u32 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 = VDM_STATE_ERR;
+ return -EPIPE;
+ }
+
+ if (chip->vdm_send_state != 2)
+ break;
+ default:
+ if (chip->val_tmp) {
+ chip->val_tmp = 0;
+ chip->vdm_send_state = 0;
+ return 0;
+ } else if (evt & EVENT_TIMER_STATE) {
+ dev_warn(chip->dev, "vdm_send_dpconfig time out\n");
+ chip->vdm_state = VDM_STATE_ERR;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ return -ETIMEDOUT;
+ }
+ break;
+ }
+ return -EINPROGRESS;
+}
+
+/* without break if success */
+#define AUTO_VDM_HANDLE(func, chip, evt, conditions) \
+do { \
+ conditions = func(chip, evt); \
+ if (!conditions) { \
+ chip->vdm_state++; \
+ chip->work_continue |= EVENT_WORK_CONTINUE; \
+ } else { \
+ if (conditions != -EINPROGRESS) \
+ chip->vdm_state = VDM_STATE_ERR; \
+ } \
+} while (0)
+
+static void auto_vdm_machine(struct fusb30x_chip *chip, u32 evt)
+{
+ int conditions;
+
+ switch (chip->vdm_state) {
+ case VDM_STATE_DISCOVERY_ID:
+ AUTO_VDM_HANDLE(vdm_send_discoveryid, chip, evt, conditions);
+ break;
+ case VDM_STATE_DISCOVERY_SVID:
+ AUTO_VDM_HANDLE(vdm_send_discoverysvid, chip, evt, conditions);
+ break;
+ case VDM_STATE_DISCOVERY_MODES:
+ AUTO_VDM_HANDLE(vdm_send_discoverymodes, chip, evt, conditions);
+ break;
+ case VDM_STATE_ENTER_MODE:
+ AUTO_VDM_HANDLE(vdm_send_entermode, chip, evt, conditions);
+ break;
+ case VDM_STATE_UPDATE_STATUS:
+ AUTO_VDM_HANDLE(vdm_send_getdpstatus, chip, evt, conditions);
+ break;
+ case VDM_STATE_DP_CONFIG:
+ AUTO_VDM_HANDLE(vdm_send_dpconfig, chip, evt, conditions);
+ break;
+ case VDM_STATE_NOTIFY:
+ platform_fusb_notify(chip);
+ chip->vdm_state = VDM_STATE_READY;
+ break;
+ default:
+ break;
+ }
+}
+
+static void fusb_state_disabled(struct fusb30x_chip *chip, u32 evt)
+{
+ /* Do nothing */
+}
+
+static void fusb_state_unattached(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.is_cc_connected = false;
+ chip->is_pd_support = false;
+
+ if ((evt & EVENT_CC) && chip->cc_state) {
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP)
+ set_state(chip, attach_wait_sink);
+ else
+ set_state(chip, attach_wait_source);
+
+ chip->vbus_begin = tcpm_check_vbus(chip);
+
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 :
+ TYPEC_POLARITY_CC2);
+ 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_try_attach_set(struct fusb30x_chip *chip,
+ enum role_mode mode)
+{
+ if (mode == ROLE_MODE_NONE || mode == ROLE_MODE_DRP ||
+ mode == ROLE_MODE_ASS)
+ return;
+
+ tcpm_init(chip);
+ tcpm_set_cc(chip, (mode == ROLE_MODE_DFP) ?
+ ROLE_MODE_DFP : ROLE_MODE_UFP);
+ chip->timer_mux = T_PD_TRY_DRP;
+ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux);
+ set_state(chip, (mode == ROLE_MODE_DFP) ?
+ attach_try_src : attach_try_snk);
+}
+
+static void fusb_state_attach_wait_sink(struct fusb30x_chip *chip, u32 evt)
+{
+ int cc1, cc2;
+
+ if (evt & EVENT_TIMER_MUX) {
+ if (tcpm_check_vbus(chip)) {
+ chip->timer_mux = T_DISABLED;
+ if (chip->role == ROLE_MODE_DRP &&
+ chip->try_role == ROLE_MODE_DFP &&
+ !chip->try_role_complete) {
+ fusb_state_try_attach_set(chip, ROLE_MODE_DFP);
+ return;
+ } else if (chip->try_role_complete) {
+ chip->timer_mux = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ set_state(chip, attached_sink);
+ return;
+ }
+ }
+
+ 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) {
+ chip->timer_mux = T_DISABLED;
+ if ((chip->cc1 == TYPEC_CC_VOLT_RP &&
+ chip->cc2 == TYPEC_CC_VOLT_OPEN) ||
+ (chip->cc2 == TYPEC_CC_VOLT_RP &&
+ chip->cc1 == TYPEC_CC_VOLT_OPEN)) {
+ chip->timer_mux = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_mux_machine,
+ chip->timer_mux);
+ 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, u32 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 == TYPEC_CC_VOLT_RD) ||
+ (chip->cc2 == TYPEC_CC_VOLT_RD))) {
+ if (chip->role == ROLE_MODE_DRP &&
+ chip->try_role == ROLE_MODE_UFP &&
+ !chip->try_role_complete)
+ fusb_state_try_attach_set(chip,
+ ROLE_MODE_UFP);
+ else
+ 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, u32 evt)
+{
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2);
+ tcpm_set_vconn(chip, 1);
+
+ chip->notify.is_cc_connected = true;
+
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ chip->notify.data_role = DATA_ROLE_DFP;
+ chip->hardrst_count = 0;
+ set_state(chip, policy_src_startup);
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK, MASK_M_COMP_CHNG, 0);
+ dev_info(chip->dev, "CC connected in %s as DFP\n",
+ chip->cc_polarity ? "CC1" : "CC2");
+}
+
+static void fusb_state_attached_sink(struct fusb30x_chip *chip, u32 evt)
+{
+ if (tcpm_check_vbus(chip)) {
+ chip->timer_mux = T_DISABLED;
+ chip->timer_state = T_DISABLED;
+ if (!chip->try_role_complete &&
+ chip->try_role == ROLE_MODE_DFP &&
+ chip->role == ROLE_MODE_DRP) {
+ fusb_state_try_attach_set(chip, ROLE_MODE_DFP);
+ return;
+ }
+
+ chip->try_role_complete = true;
+ chip->notify.is_cc_connected = true;
+ chip->notify.power_role = POWER_ROLE_SINK;
+ chip->notify.data_role = DATA_ROLE_UFP;
+ chip->hardrst_count = 0;
+ set_state(chip, policy_snk_startup);
+ dev_info(chip->dev, "CC connected in %s as UFP\n",
+ chip->cc_polarity ? "CC1" : "CC2");
+ return;
+ } else if (evt & EVENT_TIMER_MUX) {
+ set_state_unattached(chip);
+ return;
+ }
+
+ chip->timer_state = 2;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+}
+
+static void fusb_state_try_attach(struct fusb30x_chip *chip, u32 evt,
+ enum role_mode mode)
+{
+ if ((evt & EVENT_CC) && chip->cc_state) {
+ chip->try_role_complete = true;
+ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP)
+ set_state(chip, (mode == ROLE_MODE_UFP) ?
+ attach_wait_sink : error_recovery);
+ else
+ set_state(chip, (mode == ROLE_MODE_DFP) ?
+ attach_wait_source : error_recovery);
+
+ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ?
+ TYPEC_POLARITY_CC1 :
+ TYPEC_POLARITY_CC2);
+ 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);
+ } else if (evt & EVENT_TIMER_MUX) {
+ if (!chip->try_role_complete) {
+ chip->try_role_complete = true;
+ fusb_state_try_attach_set(chip,
+ (mode == ROLE_MODE_DFP) ?
+ ROLE_MODE_UFP :
+ ROLE_MODE_DFP);
+ } else {
+ set_state(chip, error_recovery);
+ }
+ }
+}
+
+static void fusb_soft_reset_parameter(struct fusb30x_chip *chip)
+{
+ chip->caps_counter = 0;
+ chip->msg_id = 0;
+ chip->vdm_state = VDM_STATE_DISCOVERY_ID;
+ chip->vdm_substate = 0;
+ chip->vdm_send_state = 0;
+ chip->val_tmp = 0;
+ chip->pos_power = 0;
+}
+
+static void fusb_state_src_startup(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.is_pd_connected = false;
+ fusb_soft_reset_parameter(chip);
+
+ 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);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_src_discovery(struct fusb30x_chip *chip, u32 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) {
+ if (!chip->is_pd_support)
+ set_state(chip, disabled);
+ else if (chip->hardrst_count > N_HARDRESET_COUNT)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_send_caps(struct fusb30x_chip *chip, u32 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++;
+ chip->is_pd_support = true;
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_discovery);
+ break;
+ }
+
+ if (!(evt & FLAG_EVENT))
+ break;
+ default:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(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->is_pd_support)
+ set_state(chip, disabled);
+ else if (chip->hardrst_count > N_HARDRESET_COUNT)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_negotiate_cap(struct fusb30x_chip *chip, u32 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,
+ u32 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 = true;
+ 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 |= EVENT_WORK_CONTINUE;
+ }
+ 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, u32 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,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->notify.is_pd_connected = false;
+ 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_vcs_ufp_evaluate_swap(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->vconn_supported)
+ set_state(chip, policy_vcs_ufp_accept);
+ else
+ set_state(chip, policy_vcs_ufp_reject);
+}
+
+static void fusb_state_swap_msg_process(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PR_SWAP)) {
+ set_state(chip, policy_src_prs_evaluate);
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_VCONN_SWAP)) {
+ if (chip->notify.data_role)
+ set_state(chip, chip->conn_state);
+ else
+ set_state(chip, policy_vcs_ufp_evaluate_swap);
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_DR_SWAP)) {
+ if (chip->notify.data_role)
+ set_state(chip, policy_drs_dfp_evaluate);
+ else
+ set_state(chip, policy_drs_ufp_evaluate);
+ }
+ }
+}
+
+#define VDM_IS_ACTIVE(chip) \
+ (chip->notify.data_role && chip->vdm_state < VDM_STATE_READY)
+
+static void fusb_state_src_ready(struct fusb30x_chip *chip, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) {
+ process_vdm_msg(chip);
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ chip->timer_state = T_DISABLED;
+ } else if (!VDM_IS_ACTIVE(chip)) {
+ fusb_state_swap_msg_process(chip, evt);
+ }
+ }
+
+ if (!chip->partner_cap[0])
+ set_state(chip, policy_src_get_sink_caps);
+ else if (VDM_IS_ACTIVE(chip))
+ auto_vdm_machine(chip, evt);
+}
+
+static void fusb_state_prs_evaluate(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_accept);
+ else
+ set_state(chip, policy_src_prs_reject);
+}
+
+static void fusb_state_send_simple_msg(struct fusb30x_chip *chip, u32 evt,
+ int cmd, int is_DMT,
+ enum connection_state state_success,
+ enum connection_state state_failed)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, cmd, is_DMT);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success)
+ set_state(chip, state_success);
+ else if (tmp == tx_failed)
+ set_state(chip, state_failed);
+ }
+}
+
+static void fusb_state_prs_reject(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_REJECT, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_ready : policy_snk_ready,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_prs_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_prs_transition_to_off :
+ policy_snk_prs_transition_to_off,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_ufp_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ (chip->vconn_enabled) ?
+ policy_vcs_ufp_wait_for_dfp_vconn :
+ policy_vcs_ufp_turn_on_vconn,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_set_vconn(struct fusb30x_chip *chip,
+ u32 evt, bool on)
+{
+ if (on) {
+ tcpm_set_vconn(chip, 1);
+ set_state(chip, chip->notify.data_role ?
+ policy_vcs_dfp_send_ps_rdy :
+ policy_vcs_ufp_send_ps_rdy);
+ } else {
+ tcpm_set_vconn(chip, 0);
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+}
+
+static void fusb_state_vcs_send_ps_rdy(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_PS_RDY, CONTROLMESSAGE,
+ (chip->notify.power_role) ?
+ policy_src_ready : policy_snk_ready,
+ (chip->notify.power_role) ?
+ policy_src_send_softrst :
+ policy_snk_send_softrst);
+}
+
+static void fusb_state_vcs_wait_for_vconn(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_PD_VCONN_SRC_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY))
+ set_state(chip, chip->notify.data_role ?
+ policy_vcs_dfp_turn_off_vconn :
+ policy_vcs_ufp_turn_off_vconn);
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_hardrst);
+ else
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ }
+}
+
+static void fusb_state_src_prs_transition_to_off(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_SRC_TRANSITION;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ break;
+ case 1:
+ if (evt & EVENT_TIMER_STATE) {
+ platform_set_vbus_lvl_enable(chip, 0, 0);
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip, policy_src_prs_assert_rd);
+ else
+ set_state(chip, policy_src_prs_source_off);
+ }
+ }
+}
+
+static void fusb_state_src_prs_assert_rd(struct fusb30x_chip *chip, u32 evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_DOWN);
+ set_state(chip, policy_src_prs_source_off);
+}
+
+static void fusb_state_src_prs_source_off(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ chip->timer_state = T_PD_SOURCE_ON;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ if (chip->sub_state != 3)
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ chip->timer_state = T_DISABLED;
+ /* snk startup */
+ chip->notify.is_pd_connected = false;
+ chip->cc_state |= CC_STATE_TOGSS_IS_UFP;
+ tcpm_set_polarity(chip, chip->cc_polarity);
+ tcpm_set_rx_enable(chip, 1);
+ set_state(chip, policy_snk_discovery);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_src_send_hardrst);
+ }
+ }
+}
+
+static void fusb_state_drs_evaluate(struct fusb30x_chip *chip, u32 evt)
+{
+ if (chip->pd_cap_info.data_role_swap)
+ /*
+ * TODO:
+ * NOW REJECT swap when the port is DFP
+ * since we should work together with USB part
+ */
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_reject : policy_drs_ufp_accept);
+ else
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_reject : policy_drs_ufp_reject);
+}
+
+static void fusb_state_drs_send_accept(struct fusb30x_chip *chip, u32 evt)
+{
+ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE,
+ chip->notify.power_role ?
+ policy_drs_dfp_change :
+ policy_drs_ufp_change,
+ error_recovery);
+}
+
+static void fusb_state_drs_role_change(struct fusb30x_chip *chip, u32 evt)
+{
+ chip->notify.data_role = chip->notify.data_role ?
+ DATA_ROLE_UFP : DATA_ROLE_DFP;
+ tcpm_set_msg_header(chip);
+ set_state(chip, chip->notify.power_role ? policy_src_ready :
+ policy_snk_ready);
+}
+
+static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, u32 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 (PACKET_IS_DATA_MSG(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, u32 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_softreset(struct fusb30x_chip *chip)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ fusb_soft_reset_parameter(chip);
+ set_state(chip, policy_src_send_caps);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_src_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_src_send_softreset(struct fusb30x_chip *chip, u32 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 (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_ACCEPT)) {
+ fusb_soft_reset_parameter(chip);
+ 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, u32 evt)
+{
+ chip->notify.is_pd_connected = false;
+ fusb_soft_reset_parameter(chip);
+
+ 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);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_snk_discovery(struct fusb30x_chip *chip, u32 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, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head,
+ DMT_SOURCECAPABILITIES)) {
+ chip->is_pd_support = true;
+ 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) {
+ if (chip->vbus_begin) {
+ chip->vbus_begin = false;
+ set_state(chip, policy_snk_send_softrst);
+ } else {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ } else {
+ if (chip->is_pd_support)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, disabled);
+ }
+ } else if ((evt & EVENT_TIMER_MUX) &&
+ (chip->hardrst_count > N_HARDRESET_COUNT)) {
+ if (chip->is_pd_support)
+ set_state(chip, error_recovery);
+ else
+ set_state(chip, disabled);
+ }
+}
+
+static void fusb_state_snk_evaluate_caps(struct fusb30x_chip *chip, u32 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;
+ }
+ }
+ fusb302_set_pos_power_by_charge_ic(chip);
+
+ 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, u32 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, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) {
+ chip->notify.is_pd_connected = true;
+ dev_info(chip->dev,
+ "PD connected as UFP, fetching 5V\n");
+ set_state(chip, policy_snk_ready);
+ } else if (PACKET_IS_DATA_MSG(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,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->notify.is_pd_connected = false;
+ 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++;
+ /* fallthrough */
+ 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, u32 evt)
+{
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) {
+ process_vdm_msg(chip);
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ chip->timer_state = T_DISABLED;
+ } else if (!VDM_IS_ACTIVE(chip)) {
+ fusb_state_swap_msg_process(chip, evt);
+ }
+ }
+
+ if (VDM_IS_ACTIVE(chip))
+ auto_vdm_machine(chip, evt);
+
+ fusb_state_swap_msg_process(chip, evt);
+ platform_fusb_notify(chip);
+}
+
+static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, u32 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_send_swap(struct fusb30x_chip *chip, u32 evt, int cmd)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, cmd, CONTROLMESSAGE);
+ chip->sub_state = 1;
+ chip->tx_state = tx_idle;
+ /* fallthrough */
+ 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) {
+ if (cmd == CMT_DR_SWAP) {
+ set_state(chip, error_recovery);
+ return;
+ }
+
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_send_softrst);
+ else
+ set_state(chip, policy_snk_send_softrst);
+ }
+ break;
+ case 2:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_ACCEPT)) {
+ chip->timer_state = T_DISABLED;
+ if (cmd == CMT_VCONN_SWAP) {
+ set_state(chip, chip->vconn_enabled ?
+ policy_vcs_dfp_wait_for_ufp_vconn :
+ policy_vcs_dfp_turn_on_vconn);
+ } else if (cmd == CMT_PR_SWAP) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_prs_transition_to_off);
+ else
+ set_state(chip, policy_snk_prs_transition_to_off);
+ chip->notify.power_role = POWER_ROLE_SOURCE;
+ tcpm_set_msg_header(chip);
+ } else if (cmd == CMT_DR_SWAP) {
+ set_state(chip, chip->notify.data_role ?
+ policy_drs_dfp_change :
+ policy_drs_ufp_change);
+ }
+ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_REJECT) ||
+ PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_WAIT)) {
+ chip->timer_state = T_DISABLED;
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_ready);
+ else
+ set_state(chip, policy_snk_ready);
+ }
+ }
+}
+
+static void fusb_state_snk_prs_transition_to_off(struct fusb30x_chip *chip,
+ u32 evt)
+{
+ switch (chip->sub_state) {
+ case 0:
+ chip->timer_state = T_PD_SOURCE_OFF;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ /* fallthrough */
+ case 1:
+ if (evt & EVENT_RX) {
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head,
+ CMT_PS_RDY)) {
+ if (chip->role == ROLE_MODE_DRP)
+ set_state(chip,
+ policy_snk_prs_assert_rp);
+ else
+ set_state(chip,
+ policy_snk_prs_source_on);
+ } else {
+ dev_dbg(chip->dev,
+ "rec careless msg: head %x\n",
+ chip->rec_head);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_prs_assert_rp(struct fusb30x_chip *chip, u32 evt)
+{
+ tcpm_set_cc_pull_mode(chip, CC_PULL_UP);
+ set_state(chip, policy_snk_prs_source_on);
+}
+
+static void fusb_state_snk_prs_source_on(struct fusb30x_chip *chip, u32 evt)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ /* supply power in 50ms */
+ platform_set_vbus_lvl_enable(chip, 1, 0);
+ chip->sub_state++;
+ chip->work_continue |= EVENT_WORK_CONTINUE;
+ break;
+ case 1:
+ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* fallthrough */
+ case 2:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ /* PD spe 6.5.10.2 */
+ chip->timer_state = T_PD_SWAP_SOURCE_START;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ chip->sub_state++;
+ } else if (tmp == tx_failed) {
+ chip->notify.power_role = POWER_ROLE_SINK;
+ tcpm_set_msg_header(chip);
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ case 3:
+ if (evt & EVENT_TIMER_STATE) {
+ chip->cc_state &= ~CC_STATE_TOGSS_IS_UFP;
+ regmap_update_bits(chip->regmap, FUSB_REG_MASK,
+ MASK_M_COMP_CHNG, 0);
+ set_state(chip, policy_src_send_caps);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_softreset(struct fusb30x_chip *chip)
+{
+ u32 tmp;
+
+ switch (chip->sub_state) {
+ case 0:
+ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE);
+ chip->tx_state = tx_idle;
+ chip->sub_state++;
+ /* without break */
+ default:
+ tmp = policy_send_data(chip);
+ if (tmp == tx_success) {
+ fusb_soft_reset_parameter(chip);
+ chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ set_state(chip, policy_snk_wait_caps);
+ } else if (tmp == tx_failed) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, u32 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)) {
+ fusb_soft_reset_parameter(chip);
+ chip->timer_state = T_TYPEC_SINK_WAIT_CAP;
+ fusb_timer_start(&chip->timer_state_machine,
+ chip->timer_state);
+ set_state(chip, policy_snk_wait_caps);
+ }
+ } else if (evt & EVENT_TIMER_STATE) {
+ set_state(chip, policy_snk_send_hardrst);
+ }
+ break;
+ }
+}
+
+static void fusb_try_detach(struct fusb30x_chip *chip)
+{
+ int cc1, cc2;
+
+ if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) &&
+ (chip->conn_state !=
+ policy_snk_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_send_swap) &&
+ (chip->conn_state != policy_snk_prs_assert_rp) &&
+ (chip->conn_state != policy_snk_prs_source_on) &&
+ (chip->conn_state != policy_snk_prs_transition_to_off)) {
+ if (!tcpm_check_vbus(chip))
+ set_state_unattached(chip);
+ } else if ((chip->conn_state !=
+ policy_src_transition_default) &&
+ (chip->conn_state !=
+ policy_src_prs_source_off) &&
+ (chip->conn_state != policy_snk_prs_source_on)) {
+ tcpm_get_cc(chip, &cc1, &cc2);
+ if (chip->cc_state & CC_STATE_TOGSS_CC2)
+ cc1 = cc2;
+ if (cc1 == TYPEC_CC_VOLT_OPEN)
+ set_state_unattached(chip);
+ } else {
+ /*
+ * Detached may occurred at swap operations. So, DON'T ignore
+ * the EVENT_CC during swapping at all, check the connection
+ * after it.
+ */
+ chip->work_continue |= EVENT_DELAY_CC;
+ }
+}
+
+static void state_machine_typec(struct fusb30x_chip *chip)
+{
+ u32 evt = 0;
+
+ tcpc_alert(chip, &evt);
+ mux_alert(chip, &evt);
+ if (!evt)
+ goto BACK;
+
+ if (chip->notify.is_cc_connected)
+ if (evt & (EVENT_CC | EVENT_DELAY_CC))
+ fusb_try_detach(chip);
+
+ if (evt & EVENT_RX) {
+ tcpm_get_message(chip);
+ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_SOFTRESET)) {
+ if (chip->notify.power_role)
+ set_state(chip, policy_src_softrst);
+ else
+ set_state(chip, policy_snk_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;
+ case attach_try_src:
+ fusb_state_try_attach(chip, evt, ROLE_MODE_DFP);
+ break;
+ case attach_try_snk:
+ fusb_state_try_attach(chip, evt, ROLE_MODE_UFP);
+ 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;
+ case policy_src_softrst:
+ fusb_state_src_softreset(chip);
+ 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;
+ case policy_snk_softrst:
+ fusb_state_snk_softreset(chip);
+ break;
+
+ /*
+ * PD Spec 1.0: PR SWAP: chap 8.3.3.6.3.1/2
+ * VC SWAP: chap 8.3.3.7.1/2
+ */
+ case policy_src_prs_evaluate:
+ case policy_snk_prs_evaluate:
+ fusb_state_prs_evaluate(chip, evt);
+ break;
+ case policy_snk_prs_accept:
+ case policy_src_prs_accept:
+ fusb_state_prs_accept(chip, evt);
+ break;
+ case policy_snk_prs_reject:
+ case policy_src_prs_reject:
+ case policy_vcs_ufp_reject:
+ case policy_drs_dfp_reject:
+ case policy_drs_ufp_reject:
+ fusb_state_prs_reject(chip, evt);
+ break;
+ case policy_src_prs_transition_to_off:
+ fusb_state_src_prs_transition_to_off(chip, evt);
+ break;
+ case policy_src_prs_assert_rd:
+ fusb_state_src_prs_assert_rd(chip, evt);
+ break;
+ case policy_src_prs_source_off:
+ fusb_state_src_prs_source_off(chip, evt);
+ break;
+ case policy_snk_prs_send_swap:
+ case policy_src_prs_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_PR_SWAP);
+ break;
+ case policy_snk_prs_transition_to_off:
+ fusb_state_snk_prs_transition_to_off(chip, evt);
+ break;
+ case policy_snk_prs_assert_rp:
+ fusb_state_snk_prs_assert_rp(chip, evt);
+ break;
+ case policy_snk_prs_source_on:
+ fusb_state_snk_prs_source_on(chip, evt);
+ break;
+ case policy_vcs_ufp_evaluate_swap:
+ fusb_state_vcs_ufp_evaluate_swap(chip, evt);
+ break;
+ case policy_vcs_ufp_accept:
+ fusb_state_vcs_ufp_accept(chip, evt);
+ break;
+ case policy_vcs_ufp_wait_for_dfp_vconn:
+ case policy_vcs_dfp_wait_for_ufp_vconn:
+ fusb_state_vcs_wait_for_vconn(chip, evt);
+ break;
+ case policy_vcs_ufp_turn_off_vconn:
+ case policy_vcs_dfp_turn_off_vconn:
+ fusb_state_vcs_set_vconn(chip, evt, false);
+ break;
+ case policy_vcs_ufp_turn_on_vconn:
+ case policy_vcs_dfp_turn_on_vconn:
+ fusb_state_vcs_set_vconn(chip, evt, true);
+ break;
+ case policy_vcs_ufp_send_ps_rdy:
+ case policy_vcs_dfp_send_ps_rdy:
+ fusb_state_vcs_send_ps_rdy(chip, evt);
+ break;
+ case policy_vcs_dfp_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_VCONN_SWAP);
+ break;
+ case policy_drs_ufp_evaluate:
+ case policy_drs_dfp_evaluate:
+ fusb_state_drs_evaluate(chip, evt);
+ break;
+ case policy_drs_dfp_accept:
+ case policy_drs_ufp_accept:
+ fusb_state_drs_send_accept(chip, evt);
+ break;
+ case policy_drs_dfp_change:
+ case policy_drs_ufp_change:
+ fusb_state_drs_role_change(chip, evt);
+ break;
+ case policy_drs_ufp_send_swap:
+ case policy_drs_dfp_send_swap:
+ fusb_state_send_swap(chip, evt, CMT_DR_SWAP);
+ 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);
+
+ chip->gpio_discharge = devm_gpiod_get_optional(chip->dev, "discharge",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(chip->gpio_discharge)) {
+ dev_warn(chip->dev,
+ "Could not get named GPIO for discharge!\n");
+ chip->gpio_discharge = NULL;
+ }
+
+ 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;
+ char *string[2];
+
+ 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);
+
+ chip->role = ROLE_MODE_NONE;
+ chip->try_role = ROLE_MODE_NONE;
+ if (!of_property_read_string(chip->dev->of_node, "fusb302,role",
+ (const char **)&string[0])) {
+ if (!strcmp(string[0], "ROLE_MODE_DRP"))
+ chip->role = ROLE_MODE_DRP;
+ else if (!strcmp(string[0], "ROLE_MODE_DFP"))
+ chip->role = ROLE_MODE_DFP;
+ else if (!strcmp(string[0], "ROLE_MODE_UFP"))
+ chip->role = ROLE_MODE_UFP;
+ }
+
+ if (chip->role == ROLE_MODE_NONE) {
+ dev_warn(chip->dev,
+ "Can't get property of role, set role to default DRP\n");
+ chip->role = ROLE_MODE_DRP;
+ string[0] = "ROLE_MODE_DRP";
+ }
+
+ if (!of_property_read_string(chip->dev->of_node, "fusb302,try_role",
+ (const char **)&string[1])) {
+ if (!strcmp(string[1], "ROLE_MODE_DFP"))
+ chip->try_role = ROLE_MODE_DFP;
+ else if (!strcmp(string[1], "ROLE_MODE_UFP"))
+ chip->try_role = ROLE_MODE_UFP;
+ }
+
+ if (chip->try_role == ROLE_MODE_NONE)
+ string[1] = "ROLE_MODE_NONE";
+
+ chip->vconn_supported = true;
+ tcpm_init(chip);
+ tcpm_set_rx_enable(chip, 0);
+ chip->conn_state = unattached;
+ tcpm_set_cc(chip, chip->role);
+
+ chip->n_caps_used = 1;
+ chip->source_power_supply[0] = 0x64;
+ chip->source_max_current[0] = 0x96;
+
+ pd_cap_info = &chip->pd_cap_info;
+ pd_cap_info->dual_role_power = 1;
+ pd_cap_info->data_role_swap = 1;
+
+ 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;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB_HOST USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set DISP_DP USB_SS property capability: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(chip->extcon, EXTCON_CHG_USB_FAST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set USB_PD 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 with role %s, try_role %s\n",
+ chip->port_num, string[0], string[1]);
+
+ 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 void fusb30x_shutdown(struct i2c_client *client)
+{
+ struct fusb30x_chip *chip = i2c_get_clientdata(client);
+
+ if (chip->gpio_vbus_5v)
+ gpiod_set_value(chip->gpio_vbus_5v, 0);
+ if (chip->gpio_discharge) {
+ gpiod_set_value(chip->gpio_discharge, 1);
+ msleep(100);
+ gpiod_set_value(chip->gpio_discharge, 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,
+ .shutdown = fusb30x_shutdown,
+ .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/staging/fusb30x/fusb30x.h b/drivers/staging/fusb30x/fusb30x.h
new file mode 100644
index 000000000..4f5ca64f7
--- /dev/null
+++ b/drivers/staging/fusb30x/fusb30x.h
@@ -0,0 +1,552 @@
+/*
+ * 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>
+
+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_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_softrst,
+ policy_snk_send_hardrst,
+
+ policy_snk_transition_default,
+
+ /* PR SWAP */
+ policy_src_prs_evaluate,
+ policy_src_prs_accept,
+ policy_src_prs_transition_to_off,
+ policy_src_prs_source_off,
+ policy_src_prs_assert_rd,
+ policy_src_prs_reject,
+ policy_src_prs_send_swap,
+
+ policy_snk_prs_evaluate,
+ policy_snk_prs_accept,
+ policy_snk_prs_transition_to_off,
+ policy_snk_prs_source_on,
+ policy_snk_prs_assert_rp,
+ policy_snk_prs_reject,
+ policy_snk_prs_send_swap,
+
+ /* VC SWAP */
+ policy_vcs_dfp_send_swap,
+ policy_vcs_dfp_wait_for_ufp_vconn,
+ policy_vcs_dfp_turn_off_vconn,
+ policy_vcs_dfp_turn_on_vconn,
+ policy_vcs_dfp_send_ps_rdy,
+
+ policy_vcs_ufp_evaluate_swap,
+ policy_vcs_ufp_reject,
+ policy_vcs_ufp_accept,
+ policy_vcs_ufp_wait_for_dfp_vconn,
+ policy_vcs_ufp_turn_off_vconn,
+ policy_vcs_ufp_turn_on_vconn,
+ policy_vcs_ufp_send_ps_rdy,
+
+ policy_drs_ufp_evaluate,
+ policy_drs_ufp_accept,
+ policy_drs_ufp_reject,
+ policy_drs_ufp_change,
+ policy_drs_ufp_send_swap,
+
+ policy_drs_dfp_evaluate,
+ policy_drs_dfp_accept,
+ policy_drs_dfp_reject,
+ policy_drs_dfp_change,
+ policy_drs_dfp_send_swap,
+
+ attach_try_src,
+ attach_try_snk,
+};
+
+enum vdm_state {
+ VDM_STATE_DISCOVERY_ID,
+ VDM_STATE_DISCOVERY_SVID,
+ VDM_STATE_DISCOVERY_MODES,
+ VDM_STATE_ENTER_MODE,
+ VDM_STATE_UPDATE_STATUS,
+ VDM_STATE_DP_CONFIG,
+ VDM_STATE_NOTIFY,
+ VDM_STATE_READY,
+ VDM_STATE_ERR,
+};
+
+enum tcpm_rp_value {
+ TYPEC_RP_USB = 0,
+ TYPEC_RP_1A5 = 1,
+ TYPEC_RP_3A0 = 2,
+ TYPEC_RP_RESERVED = 3,
+};
+
+enum role_mode {
+ ROLE_MODE_NONE,
+ ROLE_MODE_DRP,
+ ROLE_MODE_UFP,
+ ROLE_MODE_DFP,
+ ROLE_MODE_ASS,
+};
+
+#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_HOST_CUR_USB SBF(1, 2)
+#define CONTROL0_HOST_CUR_1A5 SBF(2, 2)
+#define CONTROL0_HOST_CUR_3A0 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_MODE_NONE 0
+#define CONTROL2_MODE_DFP SBF(3, 1)
+#define CONTROL2_MODE_UFP SBF(2, 1)
+#define CONTROL2_MODE_DRP SBF(1, 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 CC_STATE_TOGSS_CC1 SBF(1, 0)
+#define CC_STATE_TOGSS_CC2 SBF(1, 1)
+#define CC_STATE_TOGSS_IS_UFP SBF(1, 2)
+
+#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
+
+/* 200ms at least, 1 cycle about 6ms */
+#define N_DEBOUNCE_CNT 33
+#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 100
+#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_PD_SOURCE_OFF 920
+#define T_PD_SOURCE_ON 480
+#define T_PD_SWAP_SOURCE_START 20
+#define T_PD_VCONN_SRC_ON 100
+#define T_PD_TRY_DRP 75
+
+#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 DP_STATUS_MASK 0x000000ff
+#define DP_STATUS_HPD_STATE BIT(7)
+
+#define GET_DP_STATUS(status) (status & DP_STATUS_MASK)
+#define GET_DP_STATUS_HPD(status) ((status & DP_STATUS_HPD_STATE) >> 7)
+
+#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,
+};
+
+enum typec_cc_polarity {
+ TYPEC_POLARITY_CC1,
+ TYPEC_POLARITY_CC2,
+};
+
+enum CC_MODE {
+ CC_PULL_UP,
+ CC_PULL_DOWN,
+ CC_PULL_NONE,
+};
+
+enum typec_power_role {
+ POWER_ROLE_SINK = 0,
+ POWER_ROLE_SOURCE,
+};
+
+enum typec_data_role {
+ DATA_ROLE_UFP = 0,
+ DATA_ROLE_DFP,
+};
+
+struct notify_info {
+ enum CC_ORIENTATION orientation;
+ /* 0 UFP : 1 DFP */
+ enum typec_power_role power_role;
+ enum typec_data_role data_role;
+
+ bool is_cc_connected;
+ bool is_pd_connected;
+
+ bool is_enter_mode;
+ int pin_assignment_support;
+ int pin_assignment_def;
+ bool attention;
+ u32 dp_status;
+ u32 dp_caps;
+};
+
+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;
+ struct gpio_desc *gpio_discharge;
+ int timer_state;
+ int timer_mux;
+ int port_num;
+ u32 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;
+ enum typec_cc_polarity 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;
+ u16 vdm_svid[12];
+ int vdm_svid_num;
+ u32 vdm_id;
+ u8 chip_id;
+ bool vconn_enabled;
+ bool is_pd_support;
+ int pd_output_vol;
+ int pd_output_cur;
+ int cc_meas_high;
+ int cc_meas_low;
+ bool vbus_begin;
+
+ enum role_mode role;
+ bool vconn_supported;
+ bool try_role_complete;
+ enum role_mode try_role;
+};
+
+#endif /* FUSB302_H */
+