usb: add pd support for hikey960
authorFan Ning <fanning4@hisilicon.com>
Tue, 6 Jun 2017 09:19:05 +0000 (17:19 +0800)
committerDouglas RAILLARD <douglas.raillard@arm.com>
Tue, 14 Aug 2018 15:32:09 +0000 (16:32 +0100)
Signed-off-by: Fan Ning <fanning4@hisilicon.com>
52 files changed:
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/pd/Kconfig [new file with mode: 0644]
drivers/usb/pd/Makefile [new file with mode: 0644]
drivers/usb/pd/hisi_pd.c [new file with mode: 0644]
drivers/usb/pd/richtek/Kconfig [new file with mode: 0644]
drivers/usb/pd/richtek/Makefile [new file with mode: 0644]
drivers/usb/pd/richtek/pd_core.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_dpm_core.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_dpm_prv.h [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_dbg.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_dfp.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_dr.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_drs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_prs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_snk.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_src.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_ufp.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_policy_engine_vcs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_dbg.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_drs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_prs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_snk.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_src.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_vcs.c [new file with mode: 0644]
drivers/usb/pd/richtek/pd_process_evt_vdm.c [new file with mode: 0644]
drivers/usb/pd/richtek/rt-regmap.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpc_rt1711h.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpci_alert.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpci_core.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpci_event.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpci_timer.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpci_typec.c [new file with mode: 0644]
drivers/usb/pd/richtek/tcpm.c [new file with mode: 0644]
include/linux/hisi/log/hisi_log.h [new file with mode: 0644]
include/linux/hisi/usb/hisi_pd_dev.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/pd_core.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/pd_dpm_core.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/pd_policy_engine.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/pd_process_evt.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/rt-regmap.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/rt1711h.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/std_tcpci_v10.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci_config.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci_core.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci_event.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci_timer.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpci_typec.h [new file with mode: 0644]
include/linux/hisi/usb/pd/richtek/tcpm.h [new file with mode: 0644]

index 987fc5ba63211bb5829a8f38629e86ab6022a094..99cc4baf96f92962dbec990d62f69106a19c748a 100644 (file)
@@ -124,6 +124,8 @@ source "drivers/usb/chipidea/Kconfig"
 
 source "drivers/usb/isp1760/Kconfig"
 
+source "drivers/usb/pd/Kconfig"
+
 comment "USB port drivers"
 
 if USB
index 7d1b8c82b208917e83ada929ef6902fed878e12e..9e7f7af9d4b87b71f66bc80529f8be3de34a7306 100644 (file)
@@ -67,3 +67,5 @@ obj-$(CONFIG_USBIP_CORE)      += usbip/
 obj-$(CONFIG_TYPEC)            += typec/
 
 obj-$(CONFIG_USB_ROLE_SWITCH)  += roles/
+
+obj-$(CONFIG_TCPC_CLASS)       += pd/
diff --git a/drivers/usb/pd/Kconfig b/drivers/usb/pd/Kconfig
new file mode 100644 (file)
index 0000000..5954dbb
--- /dev/null
@@ -0,0 +1 @@
+source "drivers/usb/pd/richtek/Kconfig"
diff --git a/drivers/usb/pd/Makefile b/drivers/usb/pd/Makefile
new file mode 100644 (file)
index 0000000..ead73b4
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y   += hisi_pd.o
+obj-y  += richtek/
diff --git a/drivers/usb/pd/hisi_pd.c b/drivers/usb/pd/hisi_pd.c
new file mode 100644 (file)
index 0000000..5fe0f46
--- /dev/null
@@ -0,0 +1,602 @@
+/************************************************************
+ *
+ * Copyright (C), Hisilicon Tech. Co., Ltd.
+ * FileName: hisi_pd.c
+ * Author: Hisilicon       Version : 0.1      Date:  2016-5-9
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *  Description:  .c file for power delivery core layer which is used to handle
+ *                  pulic logic management for different chips and to
+ *                  provide interfaces for exteranl modules.
+ *  Version:
+ *  Function List:
+ *  History:
+ *  <author>  <time>   <version >   <desc>
+ ***********************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/notifier.h>
+#include <linux/mutex.h>
+#include <linux/version.h>
+#include <linux/hisi/log/hisi_log.h>
+#include <linux/hisi/usb/hisi_pd_dev.h>
+#include <linux/hisi/usb/hisi_usb.h>
+#include <linux/hisi/usb/pd/richtek/tcpm.h>
+
+struct pd_dpm_info *g_pd_di;
+static bool g_pd_cc_orientation;
+static struct class *typec_class;
+static struct device *typec_dev;
+static int pd_dpm_typec_state;
+
+#ifndef HISILOG_TAG
+#define HISILOG_TAG hisi_pd
+HISILOG_REGIST();
+#endif
+
+static bool pd_dpm_get_cc_orientation(void)
+{
+       hisilog_info("%s cc_orientation =%d\n", __func__, g_pd_cc_orientation);
+       return g_pd_cc_orientation;
+}
+
+static void pd_dpm_set_cc_orientation(bool cc_orientation)
+{
+       hisilog_info("%s cc_orientation =%d\n", __func__, cc_orientation);
+       g_pd_cc_orientation = cc_orientation;
+}
+
+void pd_dpm_get_typec_state(int *typec_state)
+{
+       hisilog_info("%s = %d\n",
+                    __func__, pd_dpm_typec_state);
+
+       *typec_state = pd_dpm_typec_state;
+}
+
+static void pd_dpm_set_typec_state(int typec_state)
+{
+       hisilog_info("%s = %d\n",
+                    __func__, typec_state);
+
+       pd_dpm_typec_state = typec_state;
+}
+
+static ssize_t pd_dpm_cc_orientation_show(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               pd_dpm_get_cc_orientation() ? "2" : "1");
+}
+
+static ssize_t pd_dpm_pd_state_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               pd_dpm_get_pd_finish_flag() ? "0" : "1");
+}
+
+static DEVICE_ATTR(cc_orientation, 0444, pd_dpm_cc_orientation_show, NULL);
+static DEVICE_ATTR(pd_state, 0444, pd_dpm_pd_state_show, NULL);
+
+static struct attribute *pd_dpm_ctrl_attributes[] = {
+       &dev_attr_cc_orientation.attr,
+       &dev_attr_pd_state.attr,
+       NULL,
+};
+
+static const struct attribute_group pd_dpm_attr_group = {
+       .attrs = pd_dpm_ctrl_attributes,
+};
+
+int pd_dpm_wake_unlock_notifier_call(struct pd_dpm_info *di,
+                                    unsigned long event, void *data)
+{
+       return atomic_notifier_call_chain(&di->pd_wake_unlock_evt_nh,
+               event, data);
+}
+
+int pd_dpm_vbus_notifier_call(struct pd_dpm_info *di, unsigned long event,
+                             void *data)
+{
+       hisilog_err("%s: pd_dpm_vbus_notifier_call!!!,++++\n",
+                   __func__);
+       return atomic_notifier_call_chain(&di->pd_evt_nh, event, data);
+}
+
+bool pd_dpm_get_pd_finish_flag(void)
+{
+       if (g_pd_di)
+               return g_pd_di->pd_finish_flag;
+       else
+               return false;
+}
+
+bool pd_dpm_get_pd_source_vbus(void)
+{
+       if (g_pd_di)
+               return g_pd_di->pd_source_vbus;
+       else
+               return false;
+}
+
+void pd_dpm_report_pd_source_vbus(struct pd_dpm_info *di, void *data)
+{
+       struct pd_dpm_vbus_state *vbus_state = data;
+
+       mutex_lock(&di->sink_vbus_lock);
+
+       if (vbus_state->vbus_type & TCP_VBUS_CTRL_PD_DETECT)
+               di->pd_finish_flag = true;
+
+       if (vbus_state->mv == 0) {
+               hisilog_info("%s : Disable\n", __func__);
+               pd_dpm_vbus_notifier_call(g_pd_di, CHARGER_TYPE_NONE, data);
+       } else {
+               di->pd_source_vbus = true;
+               hisilog_info("%s : Source %d mV, %d mA\n",
+                            __func__, vbus_state->mv, vbus_state->ma);
+               pd_dpm_vbus_notifier_call(g_pd_di, PLEASE_PROVIDE_POWER, data);
+       }
+       mutex_unlock(&di->sink_vbus_lock);
+}
+
+void pd_dpm_report_pd_sink_vbus(struct pd_dpm_info *di, void *data)
+{
+       bool skip = false;
+       unsigned long event;
+       struct pd_dpm_vbus_state *vbus_state = data;
+
+       mutex_lock(&di->sink_vbus_lock);
+
+       if (vbus_state->vbus_type & TCP_VBUS_CTRL_PD_DETECT)
+               di->pd_finish_flag = true;
+
+       if (di->pd_finish_flag)
+               event = PD_DPM_VBUS_TYPE_PD;
+       else if (di->bc12_finish_flag)
+               skip = true;
+       else
+               event = PD_DPM_VBUS_TYPE_TYPEC;
+
+       if (!skip) {
+               vbus_state = data;
+
+               if (vbus_state->mv == 0) {
+                       if (event == PD_DPM_VBUS_TYPE_PD) {
+                               hisilog_info("%s : Disable\n", __func__);
+                               pd_dpm_vbus_notifier_call(g_pd_di,
+                                                         CHARGER_TYPE_NONE,
+                                                         data);
+                       }
+               } else {
+                       di->pd_source_vbus = false;
+                       hisilog_info("%s : Sink %d mV, %d mA\n",
+                                    __func__, vbus_state->mv, vbus_state->ma);
+                       pd_dpm_vbus_notifier_call(g_pd_di, event, data);
+               }
+       } else {
+               hisilog_info("%s : skip\n", __func__);
+       }
+
+       mutex_unlock(&di->sink_vbus_lock);
+}
+
+int pd_dpm_report_bc12(struct notifier_block *usb_nb, unsigned long event,
+                      void *data)
+{
+       struct pd_dpm_info *di = container_of(usb_nb,
+               struct pd_dpm_info, usb_nb);
+
+       if (event == CHARGER_TYPE_NONE && !di->pd_finish_flag) {
+               di->bc12_finish_flag = false;
+               hisilog_info("%s : PD_WAKE_UNLOCK\n",
+                            __func__);
+               pd_dpm_wake_unlock_notifier_call(g_pd_di, PD_WAKE_UNLOCK, NULL);
+       }
+
+       if (event == PLEASE_PROVIDE_POWER)
+               return NOTIFY_OK;
+
+       if (!di->pd_finish_flag) {
+               hisilog_info("%s : event (%lu)\n", __func__, event);
+               pd_dpm_vbus_notifier_call(di, event, data);
+       } else {
+               hisilog_info("%s : igrone\n", __func__);
+       }
+
+       return NOTIFY_OK;
+}
+
+int register_pd_wake_unlock_notifier(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       if (!nb)
+               return -EINVAL;
+
+       if (!g_pd_di)
+               return ret;
+
+       ret = atomic_notifier_chain_register(&g_pd_di->pd_wake_unlock_evt_nh,
+                                            nb);
+       if (ret != 0)
+               return ret;
+
+       return ret;
+}
+EXPORT_SYMBOL(register_pd_wake_unlock_notifier);
+
+int unregister_pd_wake_unlock_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister
+               (&g_pd_di->pd_wake_unlock_evt_nh, nb);
+}
+EXPORT_SYMBOL(unregister_pd_wake_unlock_notifier);
+
+int register_pd_dpm_notifier(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       if (!nb)
+               return -EINVAL;
+
+       if (!g_pd_di)
+               return ret;
+
+       ret = atomic_notifier_chain_register(&g_pd_di->pd_evt_nh, nb);
+       if (ret != 0)
+               return ret;
+
+       return ret;
+}
+EXPORT_SYMBOL(register_pd_dpm_notifier);
+
+int unregister_pd_dpm_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&g_pd_di->pd_evt_nh, nb);
+}
+EXPORT_SYMBOL(unregister_pd_dpm_notifier);
+
+static inline void pd_dpm_report_device_attach(void)
+{
+       hisilog_info("%s \r\n", __func__);
+       if (pd_dpm_get_pd_finish_flag()) {
+               hisilog_info("%s, in pd process, report charger connect event\n",
+                            __func__);
+               hisi_usb_otg_event(CHARGER_CONNECT_EVENT);
+       }
+}
+
+static inline void pd_dpm_report_host_attach(void)
+{
+       hisilog_info("%s \r\n", __func__);
+}
+
+static inline void pd_dpm_report_device_detach(void)
+{
+       hisilog_info("%s \r\n", __func__);
+       if (pd_dpm_get_pd_finish_flag()) {
+               hisilog_info("%s, in pd process, report charger connect event\n",
+                            __func__);
+               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+       }
+       pd_dpm_vbus_notifier_call(g_pd_di, CHARGER_TYPE_NONE, NULL);
+}
+
+static inline void pd_dpm_report_host_detach(void)
+{
+       hisilog_info("%s \r\n", __func__);
+}
+
+static void pd_dpm_report_attach(int new_state)
+{
+       switch (new_state) {
+       case PD_DPM_USB_TYPEC_DEVICE_ATTACHED:
+               pd_dpm_report_device_attach();
+               break;
+
+       case PD_DPM_USB_TYPEC_HOST_ATTACHED:
+               pd_dpm_report_host_attach();
+               break;
+       }
+}
+
+static void pd_dpm_report_detach(int last_state)
+{
+       switch (last_state) {
+       case PD_DPM_USB_TYPEC_DEVICE_ATTACHED:
+               pd_dpm_report_device_detach();
+               break;
+
+       case PD_DPM_USB_TYPEC_HOST_ATTACHED:
+               pd_dpm_report_host_detach();
+               break;
+       }
+}
+
+static void pd_dpm_usb_update_state(
+               struct work_struct *work)
+{
+       int new_ev, last_ev;
+       struct pd_dpm_info *usb_cb_data =
+               container_of(to_delayed_work(work),
+                            struct pd_dpm_info,
+                                usb_state_update_work);
+
+       mutex_lock(&usb_cb_data->usb_lock);
+       new_ev = usb_cb_data->pending_usb_event;
+       mutex_unlock(&usb_cb_data->usb_lock);
+
+       last_ev = usb_cb_data->last_usb_event;
+
+       if (last_ev == new_ev)
+               return;
+
+       switch (new_ev) {
+       case PD_DPM_USB_TYPEC_DETACHED:
+               pd_dpm_report_detach(last_ev);
+               break;
+
+       case PD_DPM_USB_TYPEC_DEVICE_ATTACHED:
+       case PD_DPM_USB_TYPEC_HOST_ATTACHED:
+               if (last_ev != PD_DPM_USB_TYPEC_DETACHED)
+                       pd_dpm_report_detach(last_ev);
+               pd_dpm_report_attach(new_ev);
+               break;
+       default:
+               return;
+       }
+
+       usb_cb_data->last_usb_event = new_ev;
+}
+
+int pd_dpm_handle_pe_event(unsigned long event, void *data)
+{
+       bool attach_event = false;
+       int usb_event = PD_DPM_USB_TYPEC_NONE;
+       struct pd_dpm_typec_state *typec_state = NULL;
+
+       hisilog_err("%s:!!!,event=%ld,+++\n",
+                   __func__, event);
+
+       switch (event) {
+       case PD_DPM_PE_EVT_TYPEC_STATE:
+               {
+                       typec_state = data;
+                       switch (typec_state->new_state) {
+                       case PD_DPM_TYPEC_ATTACHED_SNK:
+                               attach_event = true;
+                               usb_event = PD_DPM_USB_TYPEC_DEVICE_ATTACHED;
+                               break;
+
+                       case PD_DPM_TYPEC_ATTACHED_SRC:
+                               attach_event = true;
+                               usb_event = PD_DPM_USB_TYPEC_HOST_ATTACHED;
+                               break;
+
+                       case PD_DPM_TYPEC_UNATTACHED:
+                               mutex_lock(&g_pd_di->sink_vbus_lock);
+                               g_pd_di->pd_finish_flag = false;
+                               g_pd_di->bc12_finish_flag = false;
+                               g_pd_di->pd_source_vbus = false;
+                               mutex_unlock(&g_pd_di->sink_vbus_lock);
+                               usb_event = PD_DPM_USB_TYPEC_DETACHED;
+                               break;
+
+                       default:
+                               hisilog_info("%s can not detect typec state\r\n",
+                                            __func__);
+                               break;
+                       }
+                       pd_dpm_set_typec_state(usb_event);
+               }
+               break;
+
+       case PD_DPM_PE_EVT_PD_STATE:
+               {
+                       struct pd_dpm_pd_state *pd_state = data;
+
+                       switch (pd_state->connected) {
+                       case PD_CONNECT_PE_READY_SNK:
+                       case PD_CONNECT_PE_READY_SRC:
+                               break;
+                       }
+               }
+               break;
+
+       case PD_DPM_PE_EVT_DIS_VBUS_CTRL:
+               {
+                       if (!g_pd_di) {
+                               hisilog_err("%s: g_pd_di is null!!!,+++\n",
+                                           __func__);
+                               return -1;
+                       }
+
+                       if (g_pd_di->pd_finish_flag) {
+                               struct pd_dpm_vbus_state vbus_state;
+
+                               hisilog_info("%s : Disable VBUS Control\n",
+                                            __func__);
+                               vbus_state.mv = 0;
+                               vbus_state.ma = 0;
+
+                               pd_dpm_vbus_notifier_call(g_pd_di,
+                                                         CHARGER_TYPE_NONE,
+                                                         &vbus_state);
+                       }
+               }
+               break;
+
+       case PD_DPM_PE_EVT_SINK_VBUS:
+               {
+                       pd_dpm_report_pd_sink_vbus(g_pd_di, data);
+               }
+               break;
+
+       case PD_DPM_PE_EVT_SOURCE_VBUS:
+               {
+                       pd_dpm_report_pd_source_vbus(g_pd_di, data);
+               }
+               break;
+
+       case PD_DPM_PE_EVT_DR_SWAP:
+               {
+                       struct pd_dpm_swap_state *swap_state = data;
+
+                       if (swap_state->new_role == PD_ROLE_DFP)
+                               usb_event = PD_DPM_USB_TYPEC_HOST_ATTACHED;
+                       else
+                               usb_event = PD_DPM_USB_TYPEC_DEVICE_ATTACHED;
+               }
+               break;
+
+       case PD_DPM_PE_EVT_PR_SWAP:
+               break;
+
+       default:
+               hisilog_info("%s  unkonw event \r\n", __func__);
+               break;
+       };
+
+       if (attach_event)
+               pd_dpm_set_cc_orientation(typec_state->polarity);
+
+       if (usb_event != PD_DPM_USB_TYPEC_NONE) {
+               mutex_lock(&g_pd_di->usb_lock);
+               if (g_pd_di->pending_usb_event != usb_event) {
+                       cancel_delayed_work(&g_pd_di->usb_state_update_work);
+                       g_pd_di->pending_usb_event = usb_event;
+                       queue_delayed_work(g_pd_di->usb_wq,
+                                          &g_pd_di->usb_state_update_work,
+                                          msecs_to_jiffies(0));
+               } else {
+                       pr_info("Pending event is same --> ignore this event %d\n",
+                               usb_event);
+               }
+               mutex_unlock(&g_pd_di->usb_lock);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pd_dpm_handle_pe_event);
+
+static int pd_dpm_parse_dt(struct pd_dpm_info *info,
+                          struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+
+       if (!np)
+               return -EINVAL;
+       /* default name */
+       if (of_property_read_string(np, "tcp_name",
+                                   &info->tcpc_name) < 0)
+               info->tcpc_name = "type_c_port0";
+
+       return 0;
+}
+
+static int pd_dpm_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct pd_dpm_info *di;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       di->dev = &pdev->dev;
+       hisilog_info("%s : +++++++++\n", __func__);
+       g_pd_di = di;
+
+       mutex_init(&di->sink_vbus_lock);
+
+       ATOMIC_INIT_NOTIFIER_HEAD(&di->pd_evt_nh);
+       ATOMIC_INIT_NOTIFIER_HEAD(&di->pd_wake_unlock_evt_nh);
+
+       di->usb_nb.notifier_call = pd_dpm_report_bc12;
+       ret = hisi_charger_type_notifier_register(&di->usb_nb);
+       if (ret < 0)
+               hisilog_err("hisi_charger_type_notifier_register failed\n");
+
+       if (typec_class) {
+               typec_dev = device_create(typec_class, NULL, 0, NULL, "typec");
+               ret = sysfs_create_group(&typec_dev->kobj, &pd_dpm_attr_group);
+               if (ret)
+                       hisilog_err("%s: typec sysfs group create error\n",
+                                   __func__);
+       }
+
+       hisilog_info("%s ++++\r\n\r\n", __func__);
+
+       di->last_usb_event = PD_DPM_USB_TYPEC_NONE;
+       di->pending_usb_event = PD_DPM_USB_TYPEC_NONE;
+
+       mutex_init(&di->usb_lock);
+
+       di->usb_wq = create_workqueue("pd_dpm_usb_wq");
+       INIT_DELAYED_WORK(&di->usb_state_update_work,
+                         pd_dpm_usb_update_state);
+       platform_set_drvdata(pdev, di);
+
+       pd_dpm_parse_dt(di, &pdev->dev);
+       notify_tcp_dev_ready(di->tcpc_name);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pd_dpm_probe);
+
+static const struct of_device_id pd_dpm_callback_match_table[] = {
+       {.compatible = "hisilicon,pd_dpm",},
+       {},
+};
+
+static struct platform_driver pd_dpm_callback_driver = {
+       .probe          = pd_dpm_probe,
+       .remove         = NULL,
+       .driver         = {
+               .name   = "hisilicon,pd_dpm",
+               .owner  = THIS_MODULE,
+               .of_match_table = pd_dpm_callback_match_table,
+       }
+};
+
+static int __init pd_dpm_init(void)
+{
+       hisilog_info("%s\n", __func__);
+       /*adjust the original product*/
+       typec_class = class_create(THIS_MODULE, "hisi_typec");
+       if (IS_ERR(typec_class)) {
+               hisilog_err("%s: cannot create class\n", __func__);
+               return PTR_ERR(typec_class);
+       }
+
+       return platform_driver_register(&pd_dpm_callback_driver);
+}
+
+static void __exit pd_dpm_exit(void)
+{
+       platform_driver_unregister(&pd_dpm_callback_driver);
+}
+
+device_initcall(pd_dpm_init);
+module_exit(pd_dpm_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("hisilicon pd dpm");
+MODULE_AUTHOR("wangbinghui<wangbinghui@hisilicon.com>");
diff --git a/drivers/usb/pd/richtek/Kconfig b/drivers/usb/pd/richtek/Kconfig
new file mode 100644 (file)
index 0000000..a507858
--- /dev/null
@@ -0,0 +1,48 @@
+config TCPC_CLASS
+       bool "TypeC Port Controller Device Class"
+       help
+         Say Y to enable
+         Typec Port
+         Controller Device
+         Class
+
+config USB_POWER_DELIVERY
+       bool "Support USB power delivery Function"
+       default n
+       help
+         Say Y to enable
+         USB
+         Power Delivery
+         support
+
+config TCPC_RT1711H
+       bool "Richtek RT1711H TypeC port Controller Driver"
+       depends on TCPC_CLASS
+       default n
+       help
+         Say Y to enable
+         Richtek RT1711H
+         TypeC port Controller
+         Driver
+
+config USB_PD_VBUS_STABLE_TOUT
+       int "PD VBUS Stable Timeout"
+       depends on USB_POWER_DELIVERY
+       range 0 1000                       # >= 0,  <= 1000
+       default 125
+       help
+          Setup a timeout value (ms)
+         for
+         VBUS change
+         stable
+
+config USB_PD_VBUS_PRESENT_TOUT
+       int "PD VBUS Present Timeout"
+       depends on USB_POWER_DELIVERY
+       range 0 1000                       # >= 0,  <= 1000
+       default 20
+       help
+          Setup a timeout value (ms)
+         for
+         VBUS present
+         stable
diff --git a/drivers/usb/pd/richtek/Makefile b/drivers/usb/pd/richtek/Makefile
new file mode 100644 (file)
index 0000000..c8990c4
--- /dev/null
@@ -0,0 +1,12 @@
+obj-$(CONFIG_DUAL_ROLE_USB_INTF)       += tcpci_dual_role.o
+obj-$(CONFIG_TCPC_RT1711H)             += tcpc_rt1711h.o
+obj-$(CONFIG_TCPC_CLASS)               += tcpci_core.o tcpci_typec.o tcpci_alert.o tcpci_timer.o tcpm.o rt-regmap.o
+obj-$(CONFIG_USB_POWER_DELIVERY)       += tcpci_event.o \
+                                          pd_core.o pd_policy_engine.o pd_process_evt.o \
+                                          pd_dpm_core.o \
+                                          pd_process_evt_snk.o pd_process_evt_src.o pd_process_evt_vdm.o \
+                                          pd_process_evt_drs.o pd_process_evt_prs.o pd_process_evt_vcs.o \
+                                          pd_process_evt_dbg.o \
+                                          pd_policy_engine_src.o pd_policy_engine_snk.o pd_policy_engine_ufp.o pd_policy_engine_vcs.o \
+                                          pd_policy_engine_dfp.o pd_policy_engine_dr.o pd_policy_engine_drs.o pd_policy_engine_prs.o \
+                                          pd_policy_engine_dbg.o
diff --git a/drivers/usb/pd/richtek/pd_core.c b/drivers/usb/pd/richtek/pd_core.c
new file mode 100644 (file)
index 0000000..24c0f9b
--- /dev/null
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Core Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+/* From DTS */
+
+static int pd_parse_pdata(pd_port_t *pd_port)
+{
+       struct device_node *np;
+       int ret = 0, i;
+
+       pr_info("%s\n", __func__);
+       np = of_find_node_by_name(pd_port->tcpc_dev->dev.of_node, "pd-data");
+
+       if (np) {
+               ret = of_property_read_u32(
+                               np, "pd,source-pdo-size",
+                               (u32 *)&pd_port->local_src_cap_default.nr);
+               if (ret < 0)
+                       pr_err("%s get source pdo size fail\n", __func__);
+
+               ret = of_property_read_u32_array(
+                               np, "pd,source-pdo-data",
+                               (u32 *)pd_port->local_src_cap_default.pdos,
+                               pd_port->local_src_cap_default.nr);
+               if (ret < 0)
+                       pr_err("%s get source pdo data fail\n", __func__);
+
+               pr_info("%s src pdo data =\n", __func__);
+               for (i = 0; i < pd_port->local_src_cap_default.nr; i++) {
+                       pr_info("%s %d: 0x%08x\n", __func__, i,
+                               pd_port->local_src_cap_default.pdos[i]);
+               }
+
+               ret = of_property_read_u32(np, "pd,sink-pdo-size",
+                                          (u32 *)&pd_port->local_snk_cap.nr);
+               if (ret < 0)
+                       pr_err("%s get sink pdo size fail\n", __func__);
+
+               ret = of_property_read_u32_array(
+                               np, "pd,sink-pdo-data",
+                               (u32 *)pd_port->local_snk_cap.pdos,
+                               pd_port->local_snk_cap.nr);
+               if (ret < 0)
+                       pr_err("%s get sink pdo data fail\n", __func__);
+
+               pr_info("%s snk pdo data =\n", __func__);
+               for (i = 0; i < pd_port->local_snk_cap.nr; i++) {
+                       pr_info("%s %d: 0x%08x\n", __func__, i,
+                               pd_port->local_snk_cap.pdos[i]);
+               }
+
+               ret = of_property_read_u32(np, "pd,id-vdo-size",
+                                          (u32 *)&pd_port->id_vdo_nr);
+               if (ret < 0)
+                       pr_err("%s get id vdo size fail\n", __func__);
+               ret = of_property_read_u32_array(
+                               np, "pd,id-vdo-data",
+                               (u32 *)pd_port->id_vdos, pd_port->id_vdo_nr);
+               if (ret < 0)
+                       pr_err("%s get id vdo data fail\n", __func__);
+
+               pr_info("%s id vdos data =\n", __func__);
+               for (i = 0; i < pd_port->id_vdo_nr; i++)
+                       pr_info("%s %d: 0x%08x\n", __func__, i,
+                               pd_port->id_vdos[i]);
+       }
+
+       return 0;
+}
+
+#define DEFAULT_DP_ROLE_CAP                            (MODE_DP_SRC)
+#define DEFAULT_DP_FIRST_CONNECTED             (DPSTS_DFP_D_CONNECTED)
+#define DEFAULT_DP_SECOND_CONNECTED            (DPSTS_DFP_D_CONNECTED)
+
+static const struct {
+       const char *prop_name;
+       u32 val;
+} supported_dpm_caps[] = {
+       {"local_dr_power", DPM_CAP_LOCAL_DR_POWER},
+       {"local_dr_data", DPM_CAP_LOCAL_DR_DATA},
+       {"local_ext_power", DPM_CAP_LOCAL_EXT_POWER},
+       {"local_usb_comm", DPM_CAP_LOCAL_USB_COMM},
+       {"local_usb_suspend", DPM_CAP_LOCAL_USB_SUSPEND},
+       {"local_high_cap", DPM_CAP_LOCAL_HIGH_CAP},
+       {"local_give_back", DPM_CAP_LOCAL_GIVE_BACK},
+       {"local_no_suspend", DPM_CAP_LOCAL_NO_SUSPEND},
+       {"local_vconn_supply", DPM_CAP_LOCAL_VCONN_SUPPLY},
+
+       {"attemp_discover_cable_dfp", DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP},
+       {"attemp_enter_dp_mode", DPM_CAP_ATTEMP_ENTER_DP_MODE},
+       {"attemp_discover_cable", DPM_CAP_ATTEMP_DISCOVER_CABLE},
+       {"attemp_discover_id", DPM_CAP_ATTEMP_DISCOVER_ID},
+
+       {"pr_reject_as_source", DPM_CAP_PR_SWAP_REJECT_AS_SRC},
+       {"pr_reject_as_sink", DPM_CAP_PR_SWAP_REJECT_AS_SNK},
+       {"pr_check_gp_source", DPM_CAP_PR_SWAP_CHECK_GP_SRC},
+       {"pr_check_gp_sink", DPM_CAP_PR_SWAP_CHECK_GP_SNK},
+
+       {"dr_reject_as_dfp", DPM_CAP_DR_SWAP_REJECT_AS_DFP},
+       {"dr_reject_as_ufp", DPM_CAP_DR_SWAP_REJECT_AS_UFP},
+
+       {"snk_prefer_low_voltage", DPM_CAP_SNK_PREFER_LOW_VOLTAGE},
+       {"snk_ignore_mismatch_current", DPM_CAP_SNK_IGNORE_MISMATCH_CURRENT},
+};
+
+static void pd_core_power_flags_init(pd_port_t *pd_port)
+{
+       u32 src_flag, snk_flag, val;
+       struct device_node *np;
+       int i;
+       pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
+       pd_port_power_caps *src_cap = &pd_port->local_src_cap_default;
+
+       np = of_find_node_by_name(pd_port->tcpc_dev->dev.of_node, "dpm_caps");
+
+       for (i = 0; i < ARRAY_SIZE(supported_dpm_caps); i++) {
+               if (of_property_read_bool(np,
+                                         supported_dpm_caps[i].prop_name))
+                       pd_port->dpm_caps |=
+                               supported_dpm_caps[i].val;
+               pr_info("dpm_caps: %s\n",
+                       supported_dpm_caps[i].prop_name);
+       }
+
+       if (of_property_read_u32(np, "pr_check", &val) == 0)
+               pd_port->dpm_caps |= DPM_CAP_PR_CHECK_PROP(val);
+       else
+               pr_err("%s get pr_check data fail\n", __func__);
+
+       if (of_property_read_u32(np, "dr_check", &val) == 0)
+               pd_port->dpm_caps |= DPM_CAP_DR_CHECK_PROP(val);
+       else
+               pr_err("%s get dr_check data fail\n", __func__);
+
+       pr_info("dpm_caps = 0x%08x\n", pd_port->dpm_caps);
+
+       src_flag = 0;
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER)
+               src_flag |= PDO_FIXED_DUAL_ROLE;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA)
+               src_flag |= PDO_FIXED_DATA_SWAP;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER)
+               src_flag |= PDO_FIXED_EXTERNAL;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_COMM)
+               src_flag |= PDO_FIXED_COMM_CAP;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_SUSPEND)
+               src_flag |= PDO_FIXED_SUSPEND;
+
+       snk_flag = src_flag;
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_HIGH_CAP)
+               snk_flag |= PDO_FIXED_HIGH_CAP;
+
+       snk_cap->pdos[0] |= snk_flag;
+       src_cap->pdos[0] |= src_flag;
+}
+
+int pd_core_init(struct tcpc_device *tcpc_dev)
+{
+       int ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       mutex_init(&pd_port->pd_lock);
+       pd_port->tcpc_dev = tcpc_dev;
+
+       pd_port->pe_pd_state = PE_IDLE2;
+       pd_port->pe_vdm_state = PE_IDLE2;
+
+       pd_port->pd_connect_state = PD_CONNECT_NONE;
+
+       ret = pd_parse_pdata(pd_port);
+       if (ret)
+               return ret;
+
+       pd_port->svid_data_cnt = 0;
+       pd_core_power_flags_init(pd_port);
+
+       PE_INFO("%s\r\n", __func__);
+       return 0;
+}
+
+void pd_extract_rdo_power(u32 rdo, u32 pdo,
+                         u32 *op_curr, u32 *max_curr)
+{
+       u32 op_power, max_power, vmin;
+
+       switch (pdo & PDO_TYPE_MASK) {
+       case PDO_TYPE_FIXED:
+       case PDO_TYPE_VARIABLE:
+               *op_curr = RDO_FIXED_VAR_EXTRACT_OP_CURR(rdo);
+               *max_curr = RDO_FIXED_VAR_EXTRACT_MAX_CURR(rdo);
+               break;
+
+       case PDO_TYPE_BATTERY: /* TODO:check it later !! */
+               vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
+               op_power = RDO_BATT_EXTRACT_OP_POWER(rdo);
+               max_power = RDO_BATT_EXTRACT_MAX_POWER(rdo);
+
+               *op_curr = op_power / vmin;
+               *max_curr = max_power / vmin;
+               break;
+
+       default:
+               *op_curr = *max_curr = 0;
+               break;
+       }
+}
+
+u32 pd_reset_pdo_power(u32 pdo, u32 imax)
+{
+       u32 ioper;
+
+       switch (pdo & PDO_TYPE_MASK) {
+       case PDO_TYPE_FIXED:
+               ioper = PDO_FIXED_EXTRACT_CURR(pdo);
+               if (ioper > imax)
+                       return PDO_FIXED_RESET_CURR(pdo, imax);
+               break;
+
+       case PDO_TYPE_VARIABLE:
+               ioper = PDO_VAR_EXTRACT_CURR(pdo);
+               if (ioper > imax)
+                       return PDO_VAR_RESET_CURR(pdo, imax);
+               break;
+
+       case PDO_TYPE_BATTERY:
+               /* TODO:check it later !! */
+               PD_ERR("No Support\r\n");
+               break;
+       }
+       return pdo;
+}
+
+void pd_extract_pdo_power(u32 pdo,
+                         u32 *vmin, u32 *vmax, u32 *ioper)
+{
+       u32 pwatt;
+
+       switch (pdo & PDO_TYPE_MASK) {
+       case PDO_TYPE_FIXED:
+               *ioper = PDO_FIXED_EXTRACT_CURR(pdo);
+               *vmin = *vmax = PDO_FIXED_EXTRACT_VOLT(pdo);
+               break;
+
+       case PDO_TYPE_VARIABLE:
+               *ioper = PDO_VAR_EXTRACT_CURR(pdo);
+               *vmin = PDO_VAR_EXTRACT_MIN_VOLT(pdo);
+               *vmax = PDO_VAR_EXTRACT_MAX_VOLT(pdo);
+               break;
+
+       case PDO_TYPE_BATTERY:  /* TODO:check it later !! */
+               *vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
+               *vmax = PDO_BATT_EXTRACT_MAX_VOLT(pdo);
+               pwatt = PDO_BATT_EXTRACT_OP_POWER(pdo);
+               *ioper = pwatt / *vmin;
+               break;
+
+       default:
+               *vmin = *vmax = *ioper = 0;
+       }
+}
+
+u32 pd_extract_cable_curr(u32 vdo)
+{
+       u32 cable_curr;
+
+       switch (PD_VDO_CABLE_CURR(vdo)) {
+       case CABLE_CURR_1A5:
+               cable_curr = 1500;
+               break;
+       case CABLE_CURR_5A:
+               cable_curr = 5000;
+               break;
+       default:
+       case CABLE_CURR_3A:
+               cable_curr = 3000;
+               break;
+       }
+
+       return cable_curr;
+}
+
+void pd_reset_svid_data(pd_port_t *pd_port)
+{
+       u8 i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               svid_data->exist = false;
+               svid_data->remote_mode.mode_cnt = 0;
+               svid_data->active_mode = 0;
+       }
+}
+
+int pd_reset_protocol_layer(pd_port_t *pd_port)
+{
+       int i = 0;
+
+       pd_notify_pe_reset_protocol(pd_port);
+
+       pd_port->explicit_contract = 0;
+       pd_port->local_selected_cap = 0;
+       pd_port->remote_selected_cap = 0;
+       pd_port->during_swap = 0;
+       pd_port->dpm_ack_immediately = 0;
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       pd_port->vconn_return = false;
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+
+       for (i = 0; i < PD_SOP_NR; i++) {
+               pd_port->msg_id_tx[i] = 0;
+               pd_port->msg_id_rx[i] = 0;
+               pd_port->msg_id_rx_init[i] = false;
+       }
+
+       return 0;
+}
+
+int pd_set_rx_enable(pd_port_t *pd_port, u8 enable)
+{
+       return tcpci_set_rx_enable(pd_port->tcpc_dev, enable);
+}
+
+int pd_enable_vbus_valid_detection(pd_port_t *pd_port, bool wait_valid)
+{
+       PE_DBG("WaitVBUS=%d\r\n", wait_valid);
+       pd_notify_pe_wait_vbus_once(pd_port,
+                                   wait_valid ? PD_WAIT_VBUS_VALID_ONCE :
+                                       PD_WAIT_VBUS_INVALID_ONCE);
+       return 0;
+}
+
+int pd_enable_vbus_safe0v_detection(pd_port_t *pd_port)
+{
+       PE_DBG("WaitVSafe0V\r\n");
+       pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_SAFE0V_ONCE);
+       return 0;
+}
+
+int pd_enable_vbus_stable_detection(pd_port_t *pd_port)
+{
+       PE_DBG("WaitVStable\r\n");
+       pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_STABLE_ONCE);
+       return 0;
+}
+
+int pd_set_data_role(pd_port_t *pd_port, u8 dr)
+{
+       pd_port->data_role = dr;
+
+       tcpci_notify_role_swap(pd_port->tcpc_dev, TCP_NOTIFY_DR_SWAP, dr);
+       return tcpci_set_msg_header(pd_port->tcpc_dev,
+                       pd_port->power_role, pd_port->data_role);
+}
+
+int pd_set_power_role(pd_port_t *pd_port, u8 pr)
+{
+       int ret;
+
+       pd_port->power_role = pr;
+       ret = tcpci_set_msg_header(pd_port->tcpc_dev,
+                                  pd_port->power_role, pd_port->data_role);
+       if (ret)
+               return ret;
+
+       pd_notify_pe_pr_changed(pd_port);
+
+       tcpci_notify_role_swap(pd_port->tcpc_dev, TCP_NOTIFY_PR_SWAP, pr);
+       return ret;
+}
+
+int pd_init_role(pd_port_t *pd_port, u8 pr, u8 dr, bool vr)
+{
+       pd_port->power_role = pr;
+       pd_port->data_role = dr;
+       pd_port->vconn_source = vr;
+
+       return tcpci_set_msg_header(pd_port->tcpc_dev,
+                       pd_port->power_role, pd_port->data_role);
+}
+
+int pd_set_vconn(pd_port_t *pd_port, int enable)
+{
+       pd_port->vconn_source = enable;
+
+       tcpci_notify_role_swap(pd_port->tcpc_dev,
+                              TCP_NOTIFY_VCONN_SWAP, enable);
+       return tcpci_set_vconn(pd_port->tcpc_dev, enable);
+}
+
+static inline int pd_reset_modal_operation(pd_port_t *pd_port)
+{
+       u8 i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+
+               if (svid_data->active_mode) {
+                       svid_data->active_mode = 0;
+                       tcpci_exit_mode(pd_port->tcpc_dev, svid_data->svid);
+               }
+       }
+
+       pd_port->modal_operation = false;
+       return 0;
+}
+
+int pd_reset_local_hw(pd_port_t *pd_port)
+{
+       pd_notify_pe_transit_to_default(pd_port);
+       pd_unlock_msg_output(pd_port);
+
+       pd_reset_pe_timer(pd_port);
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_HARDRESET);
+
+       pd_port->explicit_contract = false;
+       pd_port->pd_connected  = false;
+       pd_port->pe_ready = false;
+       pd_port->dpm_ack_immediately = false;
+
+       pd_reset_modal_operation(pd_port);
+
+       pd_set_vconn(pd_port, false);
+
+       if (pd_port->power_role == PD_ROLE_SINK) {
+               pd_port->state_machine = PE_STATE_MACHINE_SINK;
+               pd_set_data_role(pd_port, PD_ROLE_UFP);
+       } else {
+               pd_port->state_machine = PE_STATE_MACHINE_SOURCE;
+               pd_set_data_role(pd_port, PD_ROLE_DFP);
+       }
+
+       pd_dpm_notify_pe_hardreset(pd_port);
+       PE_DBG("reset_local_hw\r\n");
+
+       return 0;
+}
+
+int pd_enable_bist_test_mode(pd_port_t *pd_port, bool en)
+{
+       PE_DBG("bist_test_mode=%d\r\n", en);
+       return tcpci_set_bist_test_mode(pd_port->tcpc_dev, en);
+}
+
+/* ---- Handle PD Message ----*/
+
+int pd_handle_soft_reset(pd_port_t *pd_port, u8 state_machine)
+{
+       pd_port->state_machine = state_machine;
+
+       pd_reset_protocol_layer(pd_port);
+       pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_RECV_SRESET);
+       return pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+/* ---- Send PD Message ----*/
+
+static int pd_send_message(pd_port_t *pd_port, u8 sop_type,
+                          u8 msg, u16 count, const u32 *data)
+{
+       int ret;
+       u16 msg_hdr;
+       u8 type = PD_TX_STATE_WAIT_CRC_PD;
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       if (tcpc_dev->typec_attach_old == 0) {
+               PE_DBG("[SendMsg] Unattached\r\n");
+               return 0;
+       }
+
+       if (tcpc_dev->pd_hard_reset_event_pending) {
+               PE_DBG("[SendMsg] HardReset Pending");
+               return 0;
+       }
+
+       if (sop_type == TCPC_TX_SOP) {
+               msg_hdr = PD_HEADER_SOP(msg, pd_port->power_role,
+                                       pd_port->data_role,
+                       pd_port->msg_id_tx[sop_type], count);
+       } else {
+               msg_hdr = PD_HEADER_SOP_PRIME(
+                               msg, 0,
+                               pd_port->msg_id_tx[sop_type], count);
+       }
+
+       if ((count > 0) && (msg == PD_DATA_VENDOR_DEF))
+               type = PD_TX_STATE_WAIT_CRC_VDM;
+
+       pd_port->msg_id_tx[sop_type] = (pd_port->msg_id_tx[sop_type] + 1) % 8;
+
+       pd_notify_pe_transmit_msg(pd_port, type);
+       ret = tcpci_transmit(pd_port->tcpc_dev, sop_type, msg_hdr, data);
+       if (ret < 0)
+               PD_ERR("[SendMsg] Failed, %d\r\n", ret);
+
+       return ret;
+}
+
+int pd_send_ctrl_msg(pd_port_t *pd_port, u8 sop_type, u8 msg)
+{
+       return pd_send_message(pd_port, sop_type, msg, 0, NULL);
+}
+
+int pd_send_data_msg(pd_port_t *pd_port,
+                    u8 sop_type, u8 msg,
+                        u8 cnt, u32 *payload)
+{
+       return pd_send_message(pd_port, sop_type, msg, cnt, payload);
+}
+
+int pd_send_soft_reset(pd_port_t *pd_port, u8 state_machine)
+{
+       pd_port->state_machine = state_machine;
+
+       pd_reset_protocol_layer(pd_port);
+       pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_SEND_SRESET);
+       return pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_SOFT_RESET);
+}
+
+int pd_send_hard_reset(pd_port_t *pd_port)
+{
+       int ret;
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       PE_DBG("Send HARD Reset\r\n");
+
+       pd_port->hard_reset_counter++;
+       pd_notify_pe_send_hard_reset(pd_port);
+       pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_SEND_HRESET);
+       ret = tcpci_transmit(tcpc_dev, TCPC_TX_HARD_RESET, 0, NULL);
+       if (ret)
+               return ret;
+
+#ifdef CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER
+       if (!(tcpc_dev->tcpc_flags & TCPC_FLAGS_WAIT_HRESET_COMPLETE)) {
+               pd_put_sent_hard_reset_event(tcpc_dev);
+               return 0;
+       }
+#endif
+       return 0;
+}
+
+int pd_send_bist_mode2(pd_port_t *pd_port)
+{
+       int ret = 0;
+
+       pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_SEND_BIST);
+
+#ifdef CONFIG_USB_PD_TRANSMIT_BIST2
+       TCPC_DBG("BIST_MODE_2\r\n");
+       ret = tcpci_transmit(
+               pd_port->tcpc_dev, TCPC_TX_BIST_MODE_2, 0, NULL);
+#else
+       ret = tcpci_set_bist_carrier_mode(
+               pd_port->tcpc_dev, 1 << 2);
+#endif
+
+       return ret;
+}
+
+int pd_disable_bist_mode2(pd_port_t *pd_port)
+{
+#ifndef CONFIG_USB_PD_TRANSMIT_BIST2
+       return tcpci_set_bist_carrier_mode(
+               pd_port->tcpc_dev, 0);
+#else
+       return 0;
+#endif
+}
+
+/* ---- Send / Reply VDM Command ----*/
+
+int pd_send_svdm_request(pd_port_t *pd_port,
+                        u8 sop_type, u16 svid, u8 vdm_cmd,
+                        u8 obj_pos, u8 cnt, u32 *data_obj)
+{
+       int ret;
+       u32 payload[VDO_MAX_SIZE];
+       char buf[1024] = { 0 };
+
+       if (cnt >= (VDO_MAX_SIZE - 1))
+               snprintf(buf, sizeof(buf), "%d over the vdo max size\n", cnt);
+
+       payload[0] = VDO_S(svid, CMDT_INIT, vdm_cmd, obj_pos);
+       memcpy(&payload[1], data_obj, sizeof(u32) * cnt);
+
+       ret = pd_send_data_msg(
+                       pd_port, sop_type,
+                       PD_DATA_VENDOR_DEF, 1 + cnt, payload);
+
+       if (ret == 0 && (vdm_cmd != CMD_ATTENTION))
+               pd_enable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+
+       return ret;
+}
+
+int pd_reply_svdm_request(pd_port_t *pd_port, pd_event_t *pd_event,
+                         u8 reply, u8 cnt, u32 *data_obj)
+{
+       u32 vdo;
+       u32 payload[VDO_MAX_SIZE];
+       char buf[1024] = { 0 };
+
+       if (cnt >= (VDO_MAX_SIZE - 1))
+               snprintf(buf, sizeof(buf), "%d over the vdo max size\n", cnt);
+
+       if (!pd_event->pd_msg)
+               snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+
+       vdo = pd_event->pd_msg->payload[0];
+       payload[0] = VDO_S(
+               PD_VDO_VID(vdo), reply, PD_VDO_CMD(vdo), PD_VDO_OPOS(vdo));
+
+       if (cnt > 0) {
+               if (!data_obj)
+                       snprintf(buf, sizeof(buf), "the data_obj is NULL\n");
+
+               memcpy(&payload[1], data_obj, sizeof(u32) * cnt);
+       }
+
+       return pd_send_data_msg(pd_port,
+                       TCPC_TX_SOP, PD_DATA_VENDOR_DEF, 1 + cnt, payload);
+}
+
+void pd_lock_msg_output(pd_port_t *pd_port)
+{
+       if (pd_port->msg_output_lock)
+               return;
+       pd_port->msg_output_lock = true;
+}
+
+void pd_unlock_msg_output(pd_port_t *pd_port)
+{
+       if (!pd_port->msg_output_lock)
+               return;
+       pd_port->msg_output_lock = false;
+}
+
+int pd_update_connect_state(pd_port_t *pd_port, u8 state)
+{
+       if (pd_port->pd_connect_state == state)
+               return 0;
+
+       switch (state) {
+       case PD_CONNECT_TYPEC_ONLY:
+               if (pd_port->power_role == PD_ROLE_SOURCE) {
+                       state = PD_CONNECT_TYPEC_ONLY_SRC;
+               } else {
+                       switch (pd_port->tcpc_dev->typec_remote_rp_level) {
+                       case TYPEC_CC_VOLT_SNK_DFT:
+                               state = PD_CONNECT_TYPEC_ONLY_SNK_DFT;
+                               break;
+
+                       case TYPEC_CC_VOLT_SNK_1_5:
+                       case TYPEC_CC_VOLT_SNK_3_0:
+                               state = PD_CONNECT_TYPEC_ONLY_SNK;
+                               break;
+                       }
+               }
+               break;
+
+       case PD_CONNECT_PE_READY:
+               state = pd_port->power_role == PD_ROLE_SOURCE ?
+                       PD_CONNECT_PE_READY_SRC : PD_CONNECT_PE_READY_SNK;
+               break;
+
+       case PD_CONNECT_NONE:
+               break;
+       }
+
+       pd_port->pd_connect_state = state;
+       return tcpci_notify_pd_state(pd_port->tcpc_dev, state);
+}
+
+void pd_update_dpm_request_state(pd_port_t *pd_port, u8 state)
+{
+       /* TODO */
+}
diff --git a/drivers/usb/pd/richtek/pd_dpm_core.c b/drivers/usb/pd/richtek/pd_dpm_core.c
new file mode 100644 (file)
index 0000000..949ad11
--- /dev/null
@@ -0,0 +1,1450 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * PD Device Policy Manager Core Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+#include "pd_dpm_prv.h"
+
+/* DPM Init */
+
+static void pd_dpm_update_pdos_flags(pd_port_t *pd_port, u32 pdo)
+{
+       pd_port->dpm_flags &= ~DPM_FLAGS_RESET_PARTNER_MASK;
+
+       /* Only update PDO flags if pdo's type is fixed */
+       if ((pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED)
+               return;
+
+       if (pdo & PDO_FIXED_DUAL_ROLE)
+               pd_port->dpm_flags |= DPM_FLAGS_PARTNER_DR_POWER;
+
+       if (pdo & PDO_FIXED_DATA_SWAP)
+               pd_port->dpm_flags |= DPM_FLAGS_PARTNER_DR_DATA;
+
+       if (pdo & PDO_FIXED_EXTERNAL)
+               pd_port->dpm_flags |= DPM_FLAGS_PARTNER_EXTPOWER;
+
+       if (pdo & PDO_FIXED_COMM_CAP)
+               pd_port->dpm_flags |= DPM_FLAGS_PARTNER_USB_COMM;
+}
+
+int pd_dpm_enable_vconn(pd_port_t *pd_port, bool en)
+{
+       return pd_set_vconn(pd_port, en);
+}
+
+int pd_dpm_send_sink_caps(pd_port_t *pd_port)
+{
+       pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
+
+       return pd_send_data_msg(pd_port, TCPC_TX_SOP, PD_DATA_SINK_CAP,
+               snk_cap->nr, snk_cap->pdos);
+}
+
+int pd_dpm_send_source_caps(pd_port_t *pd_port)
+{
+       u8 i;
+       u32 cable_curr = 3000;
+
+       pd_port_power_caps *src_cap0 = &pd_port->local_src_cap_default;
+       pd_port_power_caps *src_cap1 = &pd_port->local_src_cap;
+
+       if (pd_port->power_cable_present) {
+               cable_curr =
+                       pd_extract_cable_curr(
+                               pd_port->cable_vdos[VDO_INDEX_CABLE]);
+               DPM_DBG("cable_limit: %dmA\r\n", cable_curr);
+       }
+
+       src_cap1->nr = src_cap0->nr;
+       for (i = 0; i < src_cap0->nr; i++) {
+               src_cap1->pdos[i] =
+                       pd_reset_pdo_power(src_cap0->pdos[i], cable_curr);
+       }
+
+       return pd_send_data_msg(pd_port, TCPC_TX_SOP, PD_DATA_SOURCE_CAP,
+               src_cap1->nr, src_cap1->pdos);
+}
+
+enum {
+       GOOD_PW_NONE = 0,       /* both no GP */
+       GOOD_PW_PARTNER,        /* partner has GP */
+       GOOD_PW_LOCAL,          /* local has GP */
+       GOOD_PW_BOTH,           /* both have GPs */
+};
+
+static inline int dpm_check_good_power(pd_port_t *pd_port)
+{
+       bool local_ex, partner_ex;
+
+       local_ex =
+               (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER) != 0;
+
+       partner_ex =
+               (pd_port->dpm_flags & DPM_FLAGS_PARTNER_EXTPOWER) != 0;
+
+       if (local_ex != partner_ex) {
+               if (partner_ex)
+                       return GOOD_PW_PARTNER;
+               return GOOD_PW_LOCAL;
+       }
+
+       if (local_ex)
+               return GOOD_PW_BOTH;
+
+       return GOOD_PW_NONE;
+}
+
+static inline bool dpm_response_request(pd_port_t *pd_port, bool accept)
+{
+       if (accept)
+               return pd_put_dpm_ack_event(pd_port);
+       return pd_put_dpm_nak_event(pd_port, PD_DPM_NAK_REJECT);
+}
+
+/* ---- SNK ---- */
+
+struct dpm_pdo_info_t {
+       u8 type;
+       int vmin;
+       int vmax;
+       int uw;
+       int ma;
+};
+
+struct dpm_rdo_info_t {
+       u8 pos;
+       u8 type;
+       bool mismatch;
+
+       int vmin;
+       int vmax;
+
+       union {
+               u32 max_uw;
+               u32 max_ma;
+       };
+
+       union {
+               u32 oper_uw;
+               u32 oper_ma;
+       };
+};
+
+#define DPM_PDO_TYPE_FIXED     0
+#define DPM_PDO_TYPE_BAT       1
+#define DPM_PDO_TYPE_VAR       2
+#define DPM_PDO_TYPE(pdo)      (((pdo) & PDO_TYPE_MASK) >> 30)
+
+static inline bool dpm_is_valid_pdo_pair(
+               struct dpm_pdo_info_t *sink,
+               struct dpm_pdo_info_t *source, u32 caps)
+{
+       if (sink->vmax < source->vmax)
+               return false;
+
+       if (sink->vmin > source->vmin)
+               return false;
+
+       if (caps & DPM_CAP_SNK_IGNORE_MISMATCH_CURRENT)
+               return (sink->ma <= source->ma);
+
+       return true;
+}
+
+static inline void dpm_extract_pdo_info(
+                       u32 pdo, struct dpm_pdo_info_t *info)
+{
+       memset(info, 0, sizeof(struct dpm_pdo_info_t));
+
+       info->type = DPM_PDO_TYPE(pdo);
+
+       switch (info->type) {
+       case DPM_PDO_TYPE_FIXED:
+               info->ma = PDO_FIXED_EXTRACT_CURR(pdo);
+               info->vmin = PDO_FIXED_EXTRACT_VOLT(pdo);
+               info->vmax = info->vmin;
+               info->uw = info->ma * info->vmax;
+               break;
+
+       case DPM_PDO_TYPE_VAR:
+               info->ma = PDO_VAR_OP_CURR(pdo);
+               info->vmin = PDO_VAR_EXTRACT_MIN_VOLT(pdo);
+               info->vmax = PDO_VAR_EXTRACT_MAX_VOLT(pdo);
+               info->uw = info->ma * info->vmax;
+               break;
+
+       case DPM_PDO_TYPE_BAT:
+               info->uw = PDO_BATT_EXTRACT_OP_POWER(pdo) * 1000;
+               info->vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
+               info->vmax = PDO_BATT_EXTRACT_MAX_VOLT(pdo);
+               info->ma = info->uw / info->vmin;
+               break;
+       }
+}
+
+#ifndef MIN
+#define MIN(a, b)      (((a) < (b)) ? (a) : (b))
+#endif
+
+static inline int dpm_calc_src_cap_power_uw(
+       struct dpm_pdo_info_t *source, struct dpm_pdo_info_t *sink)
+{
+       int uw, ma;
+
+       if (source->type == DPM_PDO_TYPE_BAT) {
+               uw = source->uw;
+
+               if (sink->type == DPM_PDO_TYPE_BAT)
+                       uw = MIN(uw, sink->uw);
+       } else {
+               ma = source->ma;
+
+               if (sink->type != DPM_PDO_TYPE_BAT)
+                       ma = MIN(ma, sink->ma);
+
+               uw = ma * source->vmax;
+       }
+
+       return uw;
+}
+
+static bool dpm_find_match_req_info(
+               struct dpm_rdo_info_t *req_info,
+               u32 snk_pdo, int cnt, u32 *src_pdos,
+               int min_uw, u32 caps)
+{
+       bool overload;
+       int ret = -1;
+       int i;
+       int uw, max_uw = min_uw, cur_mv = 0;
+       struct dpm_pdo_info_t sink, source;
+
+       dpm_extract_pdo_info(snk_pdo, &sink);
+
+       for (i = 0; i < cnt; i++) {
+               dpm_extract_pdo_info(src_pdos[i], &source);
+               if (!dpm_is_valid_pdo_pair(&sink, &source, caps))
+                       continue;
+
+               uw = dpm_calc_src_cap_power_uw(&source, &sink);
+
+               overload = uw > max_uw;
+
+               if (caps & DPM_CAP_SNK_PREFER_LOW_VOLTAGE)
+                       overload |= (uw == max_uw) && (source.vmax < cur_mv);
+
+               if (overload) {
+                       ret = i;
+                       max_uw = uw;
+                       cur_mv = source.vmax;
+               }
+       }
+
+       if (ret >= 0) {
+               req_info->pos = ret + 1;
+               req_info->type = source.type;
+
+               dpm_extract_pdo_info(src_pdos[ret], &source);
+
+               req_info->vmax = source.vmax;
+               req_info->vmin = source.vmin;
+
+               if (sink.type == DPM_PDO_TYPE_BAT)
+                       req_info->mismatch = max_uw < sink.uw;
+               else
+                       req_info->mismatch = source.ma < sink.ma;
+
+               if (source.type == DPM_PDO_TYPE_BAT) {
+                       req_info->max_uw = sink.uw;
+                       req_info->oper_uw = max_uw;
+               } else {
+                       req_info->max_ma = sink.ma;
+                       req_info->oper_ma = MIN(sink.ma, source.ma);
+               }
+       }
+
+       return (ret >= 0);
+}
+
+static bool dpm_build_request_info(
+       pd_port_t *pd_port, struct dpm_rdo_info_t *req_info)
+{
+       bool find_cap = false;
+       int i, max_uw = 0;
+       pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
+       pd_port_power_caps *src_cap = &pd_port->remote_src_cap;
+
+       memset(req_info, 0, sizeof(struct dpm_rdo_info_t));
+
+       for (i = 0; i < src_cap->nr; i++)
+               DPM_DBG("SrcCap%d: 0x%08x\r\n", i + 1, src_cap->pdos[i]);
+
+       for (i = 0; i < snk_cap->nr; i++) {
+               DPM_DBG("EvaSinkCap%d\r\n", i + 1);
+
+               find_cap = dpm_find_match_req_info(
+                               req_info, snk_cap->pdos[i],
+                               src_cap->nr, src_cap->pdos,
+                               max_uw, pd_port->dpm_caps);
+
+               if (find_cap) {
+                       if (req_info->type == DPM_PDO_TYPE_BAT)
+                               max_uw = req_info->oper_uw;
+                       else
+                               max_uw = req_info->vmax * req_info->oper_ma;
+
+                       DPM_DBG("Find SrcCap%d(%s):%d mw\r\n",
+                               req_info->pos, req_info->mismatch ?
+                                       "Mismatch" : "Match", max_uw / 1000);
+                       pd_port->local_selected_cap = i + 1;
+               }
+       }
+
+       return max_uw != 0;
+}
+
+static bool dpm_build_default_request_info(
+       pd_port_t *pd_port, struct dpm_rdo_info_t *req_info)
+{
+       struct dpm_pdo_info_t sink, source;
+       pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
+       pd_port_power_caps *src_cap = &pd_port->remote_src_cap;
+
+       pd_port->local_selected_cap = 1;
+
+       dpm_extract_pdo_info(snk_cap->pdos[0], &sink);
+       dpm_extract_pdo_info(src_cap->pdos[0], &source);
+
+       req_info->pos = 1;
+       req_info->type = source.type;
+       req_info->mismatch = true;
+       req_info->vmax = 5000;
+       req_info->vmin = 5000;
+
+       if (req_info->type == DPM_PDO_TYPE_BAT) {
+               req_info->max_uw = sink.uw;
+               req_info->oper_uw = source.uw;
+
+       } else {
+               req_info->max_ma = sink.ma;
+               req_info->oper_ma = source.ma;
+       }
+
+       return true;
+}
+
+static inline void dpm_update_request(
+       pd_port_t *pd_port, struct dpm_rdo_info_t *req_info)
+{
+       u32 mw_op, mw_max;
+
+       u32 flags = 0;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK)
+               flags |= RDO_GIVE_BACK;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_NO_SUSPEND)
+               flags |= RDO_NO_SUSPEND;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_COMM)
+               flags |= RDO_COMM_CAP;
+
+       if (req_info->mismatch)
+               flags |= RDO_CAP_MISMATCH;
+
+       pd_port->request_v_new = req_info->vmax;
+
+       if (req_info->type == DPM_PDO_TYPE_BAT) {
+               mw_op = req_info->oper_uw / 1000;
+               mw_max = req_info->max_uw / 1000;
+
+               pd_port->request_i_op = req_info->oper_uw / req_info->vmin;
+               pd_port->request_i_max = req_info->max_uw / req_info->vmin;
+
+               if (req_info->mismatch)
+                       pd_port->request_i_new = pd_port->request_i_op;
+               else
+                       pd_port->request_i_new = pd_port->request_i_max;
+
+               pd_port->last_rdo = RDO_BATT(
+                               req_info->pos, mw_op, mw_max, flags);
+       } else {
+               pd_port->request_i_op = req_info->oper_ma;
+               pd_port->request_i_max = req_info->max_ma;
+
+               if (req_info->mismatch)
+                       pd_port->request_i_new = pd_port->request_i_op;
+               else
+                       pd_port->request_i_new = pd_port->request_i_max;
+
+               pd_port->last_rdo = RDO_FIXED(
+                       req_info->pos, req_info->oper_ma,
+                       req_info->max_ma, flags);
+       }
+}
+
+bool pd_dpm_send_request(pd_port_t *pd_port, int mv, int ma)
+{
+       bool find_cap = false;
+       struct dpm_rdo_info_t req_info;
+       pd_port_power_caps *src_cap = &pd_port->remote_src_cap;
+       u32 snk_pdo = PDO_FIXED(mv, ma, 0);
+
+       memset(&req_info, 0, sizeof(struct dpm_rdo_info_t));
+
+       find_cap = dpm_find_match_req_info(
+                       &req_info, snk_pdo,
+                       src_cap->nr, src_cap->pdos,
+                       0, pd_port->dpm_caps);
+
+       if (!find_cap)
+               return false;
+
+       dpm_update_request(pd_port, &req_info);
+       return pd_put_dpm_pd_request_event(pd_port,
+                       PD_DPM_PD_REQUEST_PW_REQUEST);
+}
+
+void pd_dpm_snk_evaluate_caps(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool find_cap = false;
+       int sink_nr, source_nr;
+       char buf[1024] = { 0 };
+
+       struct dpm_rdo_info_t req_info;
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+       pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
+       pd_port_power_caps *src_cap = &pd_port->remote_src_cap;
+
+       if (!pd_msg)
+               snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+
+       sink_nr = snk_cap->nr;
+       source_nr = PD_HEADER_CNT(pd_msg->msg_hdr);
+
+       if ((source_nr <= 0) || (sink_nr <= 0)) {
+               DPM_DBG("SrcNR or SnkNR = 0\r\n");
+               return;
+       }
+
+       src_cap->nr = source_nr;
+       memcpy(src_cap->pdos, pd_msg->payload, sizeof(u32) * source_nr);
+       pd_dpm_update_pdos_flags(pd_port, src_cap->pdos[0]);
+
+       find_cap = dpm_build_request_info(pd_port, &req_info);
+
+       /* If we can't find any cap to use, choose default setting */
+       if (!find_cap) {
+               DPM_DBG("Can't find any SrcCap\r\n");
+               dpm_build_default_request_info(pd_port, &req_info);
+       }
+
+       dpm_update_request(pd_port, &req_info);
+
+       pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SOURCE_CAP;
+       if (!(pd_port->dpm_flags & DPM_FLAGS_PARTNER_DR_POWER))
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SINK_CAP;
+
+       if (req_info.pos > 0)
+               pd_put_dpm_notify_event(pd_port, req_info.pos);
+}
+
+void pd_dpm_snk_transition_power(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       tcpci_sink_vbus(
+                       pd_port->tcpc_dev, TCP_VBUS_CTRL_REQUEST,
+                       pd_port->request_v_new,
+                       pd_port->request_i_new);
+
+       pd_port->request_v = pd_port->request_v_new;
+       pd_port->request_i = pd_port->request_i_new;
+}
+
+void pd_dpm_snk_hard_reset(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       tcpci_sink_vbus(
+                       pd_port->tcpc_dev,
+                       TCP_VBUS_CTRL_HRESET,
+                       TCPC_VBUS_SINK_0V, 0);
+       pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT);
+}
+
+/* ---- SRC ---- */
+
+void pd_dpm_src_evaluate_request(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u8 rdo_pos;
+       u32 rdo, pdo;
+       u32 op_curr, max_curr;
+       u32 source_vmin, source_vmax, source_i;
+       bool accept_request = true;
+       char buf[1024] = { 0 };
+
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+       pd_port_power_caps *src_cap = &pd_port->local_src_cap;
+
+       if (!pd_msg)
+               snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+
+       rdo = pd_msg->payload[0];
+       rdo_pos = RDO_POS(rdo);
+
+       DPM_DBG("RequestCap%d\r\n", rdo_pos);
+
+       pd_port->dpm_flags &= (~DPM_FLAGS_PARTNER_MISMATCH);
+       if ((rdo_pos > 0) && (rdo_pos <= src_cap->nr)) {
+               pdo = src_cap->pdos[rdo_pos - 1];
+
+               pd_extract_rdo_power(rdo, pdo, &op_curr, &max_curr);
+               pd_extract_pdo_power(
+                               pdo, &source_vmin,
+                               &source_vmax, &source_i);
+
+               if (source_i < op_curr) {
+                       DPM_DBG("src_i (%d) < op_i (%d)\r\n",
+                               source_i, op_curr);
+                       accept_request = false;
+               }
+
+               if (rdo & RDO_CAP_MISMATCH) {
+                       /* TODO: handle it later */
+                       DPM_DBG("CAP_MISMATCH\r\n");
+                       pd_port->dpm_flags |= DPM_FLAGS_PARTNER_MISMATCH;
+               } else if (source_i < max_curr) {
+                       DPM_DBG("src_i (%d) < max_i (%d)\r\n",
+                               source_i, max_curr);
+                       accept_request = false;
+               }
+       } else {
+               accept_request = false;
+               DPM_DBG("RequestPos Wrong (%d)\r\n", rdo_pos);
+       }
+
+       if (accept_request) {
+               pd_port->local_selected_cap = rdo_pos;
+
+               pd_port->request_i_op = op_curr;
+               pd_port->request_i_max = max_curr;
+
+               if (rdo & RDO_CAP_MISMATCH)
+                       pd_port->request_i_new = op_curr;
+               else
+                       pd_port->request_i_new = max_curr;
+
+               pd_port->request_v_new = source_vmin;
+               pd_put_dpm_notify_event(pd_port, rdo_pos);
+       } else {
+               /*
+                * "Contract Invalid" means that the previously
+                * negotiated Voltage and Current values
+                * are no longer included in the Sources new Capabilities.
+                * If the Sink fails to make a valid Request in this case
+                * then Power Delivery operation is no longer possible
+                * and Power Delivery mode is exited with a Hard Reset.
+                */
+
+               pd_port->local_selected_cap = 0;
+               pd_put_dpm_nak_event(pd_port, PD_DPM_NAK_REJECT_INVALID);
+       }
+}
+
+void pd_dpm_src_transition_power(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_vbus_stable_detection(pd_port);
+
+       tcpci_source_vbus(
+                       pd_port->tcpc_dev, TCP_VBUS_CTRL_REQUEST,
+                       pd_port->request_v_new, pd_port->request_i_new);
+
+       if (pd_port->request_v == pd_port->request_v_new)
+               pd_put_vbus_stable_event(pd_port->tcpc_dev);
+#if CONFIG_USB_PD_VBUS_STABLE_TOUT
+       else
+               pd_enable_timer(pd_port, PD_TIMER_VBUS_STABLE);
+#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */
+
+       pd_port->request_v = pd_port->request_v_new;
+       pd_port->request_i = pd_port->request_i_new;
+}
+
+void pd_dpm_src_inform_cable_vdo(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       const int size = sizeof(u32) * VDO_MAX_SIZE;
+
+       if (pd_event->pd_msg)
+               memcpy(pd_port->cable_vdos, pd_event->pd_msg->payload, size);
+
+       pd_put_dpm_ack_event(pd_port);
+}
+
+void pd_dpm_src_hard_reset(pd_port_t *pd_port)
+{
+       tcpci_source_vbus(
+                       pd_port->tcpc_dev,
+                       TCP_VBUS_CTRL_HRESET,
+                       TCPC_VBUS_SOURCE_0V, 0);
+       pd_enable_vbus_safe0v_detection(pd_port);
+}
+
+/* ---- UFP : update_svid_data ---- */
+
+static inline bool dpm_ufp_update_svid_data_enter_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       svdm_svid_data_t *svid_data;
+
+       DPM_DBG("EnterMode (svid0x%04x, ops:%d)\r\n", svid, ops);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+
+       if (!svid_data)
+               return false;
+
+       /* Only accept 1 mode active at the same time */
+       if (svid_data->active_mode)
+               return false;
+
+       if ((ops == 0) || (ops > svid_data->local_mode.mode_cnt))
+               return false;
+
+       svid_data->active_mode = ops;
+       pd_port->modal_operation = true;
+
+       svdm_ufp_request_enter_mode(pd_port, svid, ops);
+
+       tcpci_enter_mode(
+                       pd_port->tcpc_dev,
+                       svid, ops,
+                       svid_data->local_mode.mode_vdo[ops]);
+       return true;
+}
+
+static inline bool dpm_ufp_update_svid_data_exit_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       u8 i;
+       bool modal_operation;
+       svdm_svid_data_t *svid_data;
+
+       DPM_DBG("ExitMode (svid0x%04x, mode:%d)\r\n", svid, ops);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+
+       if (!svid_data)
+               return false;
+
+       if (svid_data->active_mode == 0)
+               return false;
+
+       if ((ops == 0) || (ops == svid_data->active_mode)) {
+               svid_data->active_mode = 0;
+
+               modal_operation = false;
+               for (i = 0; i < pd_port->svid_data_cnt; i++) {
+                       svid_data = &pd_port->svid_data[i];
+
+                       if (svid_data->active_mode) {
+                               modal_operation = true;
+                               break;
+                       }
+               }
+
+               pd_port->modal_operation = modal_operation;
+
+               svdm_ufp_request_exit_mode(pd_port, svid, ops);
+               tcpci_exit_mode(pd_port->tcpc_dev, svid);
+               return true;
+       }
+
+       return false;
+}
+
+/* ---- UFP : Evaluate VDM Request ---- */
+
+static inline bool pd_dpm_ufp_reply_request(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool ack)
+{
+       return vdm_put_dpm_event(
+               pd_port, ack ? PD_DPM_ACK : PD_DPM_NAK, pd_event->pd_msg);
+}
+
+static inline u32 dpm_vdm_get_svid(pd_event_t *pd_event)
+{
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+       char buf[1024] = { 0 };
+
+       if (!pd_msg)
+               snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+       return PD_VDO_VID(pd_msg->payload[0]);
+}
+
+void pd_dpm_ufp_request_id_info(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_reply_request(
+                       pd_port, pd_event,
+                       dpm_vdm_get_svid(pd_event) == USB_SID_PD);
+}
+
+void pd_dpm_ufp_request_svid_info(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ack = false;
+
+       if (pd_is_support_modal_operation(pd_port))
+               ack = (dpm_vdm_get_svid(pd_event) == USB_SID_PD);
+
+       pd_dpm_ufp_reply_request(pd_port, pd_event, ack);
+}
+
+void pd_dpm_ufp_request_mode_info(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u16 svid = dpm_vdm_get_svid(pd_event);
+       bool ack = 0;
+
+       if (dpm_get_svdm_svid_data(pd_port, svid))
+               ack = 1;
+       pd_dpm_ufp_reply_request(pd_port, pd_event, ack);
+}
+
+void pd_dpm_ufp_request_enter_mode(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ack = false;
+       u16 svid;
+       u8 ops;
+       char buf[1024] = { 0 };
+
+       if (!pd_event->pd_msg)
+               snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+       dpm_vdm_get_svid_ops(pd_event, &svid, &ops);
+       ack = dpm_ufp_update_svid_data_enter_mode(pd_port, svid, ops);
+
+       pd_dpm_ufp_reply_request(pd_port, pd_event, ack);
+}
+
+void pd_dpm_ufp_request_exit_mode(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ack;
+       u16 svid;
+       u8 ops;
+
+       dpm_vdm_get_svid_ops(pd_event, &svid, &ops);
+       ack = dpm_ufp_update_svid_data_exit_mode(pd_port, svid, ops);
+       pd_dpm_ufp_reply_request(pd_port, pd_event, ack);
+}
+
+/* ---- UFP : Response VDM Request ---- */
+
+int pd_dpm_ufp_response_id(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       return pd_reply_svdm_request(pd_port, pd_event,
+               CMDT_RSP_ACK, pd_port->id_vdo_nr, pd_port->id_vdos);
+}
+
+int pd_dpm_ufp_response_svids(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       svdm_svid_data_t *svid_data;
+       u16 svid_list[2];
+       u32 svids[VDO_MAX_DATA_SIZE];
+       u8 i = 0, j = 0, cnt = pd_port->svid_data_cnt;
+       char buf[1024] = { 0 };
+
+       if (pd_port->svid_data_cnt >= VDO_MAX_SVID_SIZE) {
+               snprintf(buf, sizeof(buf),
+                        "the %d is over vdo max svid size\n",
+                       pd_port->svid_data_cnt);
+       }
+
+       if (unlikely(cnt >= VDO_MAX_SVID_SIZE))
+               cnt = VDO_MAX_SVID_SIZE;
+
+       while (i < cnt) {
+               svid_data = &pd_port->svid_data[i++];
+               svid_list[0] = svid_data->svid;
+
+               if (i < cnt) {
+                       svid_data = &pd_port->svid_data[i++];
+                       svid_list[1] = svid_data->svid;
+               } else {
+                       svid_list[1] = 0;
+               }
+               svids[j++] = VDO_SVID(svid_list[0], svid_list[1]);
+       }
+
+       if ((cnt % 2) == 0)
+               svids[j++] = VDO_SVID(0, 0);
+
+       return pd_reply_svdm_request(
+               pd_port, pd_event, CMDT_RSP_ACK, j, svids);
+}
+
+int pd_dpm_ufp_response_modes(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       svdm_svid_data_t *svid_data;
+       u16 svid = dpm_vdm_get_svid(pd_event);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (svid_data) {
+               return pd_reply_svdm_request(
+                       pd_port, pd_event, CMDT_RSP_ACK,
+                       svid_data->local_mode.mode_cnt,
+                       svid_data->local_mode.mode_vdo);
+       } else {
+               PE_DBG("ERROR-4965\r\n");
+               return pd_reply_svdm_request_simply(
+                       pd_port, pd_event, CMDT_RSP_NAK);
+       }
+}
+
+/* ---- DFP : update_svid_data ---- */
+
+static inline void dpm_dfp_update_svid_data_exist(
+                       pd_port_t *pd_port, u16 svid)
+{
+       u8 k;
+       svdm_svid_data_t *svid_data;
+
+#ifdef CONFIG_USB_PD_KEEP_SVIDS
+       svdm_svid_list_t *list = &pd_port->remote_svid_list;
+
+       if (list->cnt < VDO_MAX_SVID_SIZE)
+               list->svids[list->cnt++] = svid;
+       else
+               DPM_DBG("ERR:SVIDCNT\r\n");
+#endif
+
+       for (k = 0; k < pd_port->svid_data_cnt; k++) {
+               svid_data = &pd_port->svid_data[k];
+
+               if (svid_data->svid == svid)
+                       svid_data->exist = 1;
+       }
+}
+
+static inline void dpm_dfp_update_svid_data_modes(
+       pd_port_t *pd_port, u16 svid, u32 *mode_list, u8 count)
+{
+       u8 i;
+       svdm_svid_data_t *svid_data;
+
+       DPM_DBG("InformMode (0x%04x:%d): \r\n", svid, count);
+       for (i = 0; i < count; i++)
+               DPM_DBG("Mode[%d]: 0x%08x\r\n", i, mode_list[i]);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return;
+
+       svid_data->remote_mode.mode_cnt = count;
+
+       if (count != 0) {
+               memcpy(svid_data->remote_mode.mode_vdo,
+                      mode_list, sizeof(u32) * count);
+       }
+}
+
+static inline void dpm_dfp_update_svid_enter_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       svdm_svid_data_t *svid_data;
+
+       DPM_DBG("EnterMode (svid0x%04x, mode:%d)\r\n", svid, ops);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return;
+
+       svid_data->active_mode = ops;
+       pd_port->modal_operation = true;
+
+       tcpci_enter_mode(
+                       pd_port->tcpc_dev,
+                       svid_data->svid, ops,
+                       svid_data->remote_mode.mode_vdo[ops]);
+}
+
+static inline void dpm_dfp_update_svid_data_exit_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       u8 i;
+       bool modal_operation;
+       svdm_svid_data_t *svid_data;
+
+       DPM_DBG("ExitMode (svid0x%04x, mode:%d)\r\n", svid, ops);
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return;
+
+       if ((ops == 0) || (ops == svid_data->active_mode)) {
+               svid_data->active_mode = 0;
+
+               modal_operation = false;
+               for (i = 0; i < pd_port->svid_data_cnt; i++) {
+                       svid_data = &pd_port->svid_data[i];
+
+                       if (svid_data->active_mode) {
+                               modal_operation = true;
+                               break;
+                       }
+               }
+
+               pd_port->modal_operation = modal_operation;
+               tcpci_exit_mode(pd_port->tcpc_dev, svid);
+       }
+}
+
+/* ---- DFP : Inform VDM Result ---- */
+
+void pd_dpm_dfp_inform_id(pd_port_t *pd_port, pd_event_t *pd_event, bool ack)
+{
+#if DPM_DBG_ENABLE
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+#endif /* DPM_DBG_ENABLE */
+
+       if (ack) {
+               DPM_DBG("InformID, 0x%02x, 0x%02x, 0x%02x, 0x%02x\r\n",
+                       pd_msg->payload[0], pd_msg->payload[1],
+                       pd_msg->payload[2], pd_msg->payload[3]);
+       }
+
+       svdm_dfp_inform_id(pd_port, pd_event, ack);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+static inline int dpm_dfp_consume_svids(
+       pd_port_t *pd_port, u32 *svid_list, u8 count)
+{
+       bool discover_again = true;
+
+       u8 i, j;
+       u16 svid[2];
+
+       DPM_DBG("InformSVID (%d): \r\n", count);
+
+       if (count < 6)
+               discover_again = false;
+
+       for (i = 0; i < count; i++) {
+               svid[0] = PD_VDO_SVID_SVID0(svid_list[i]);
+               svid[1] = PD_VDO_SVID_SVID1(svid_list[i]);
+
+               DPM_DBG("svid[%d]: 0x%04x 0x%04x\r\n", i, svid[0], svid[1]);
+
+               for (j = 0; j < 2; j++) {
+                       if (svid[j] == 0) {
+                               discover_again = false;
+                               break;
+                       }
+
+                       dpm_dfp_update_svid_data_exist(pd_port, svid[j]);
+               }
+       }
+
+       if (discover_again) {
+               DPM_DBG("DiscoverSVID Again\r\n");
+               vdm_put_dpm_vdm_request_event(
+                       pd_port, PD_DPM_VDM_REQUEST_DISCOVER_SVIDS);
+               return 1;
+       }
+
+       return 0;
+}
+
+void pd_dpm_dfp_inform_svids(pd_port_t *pd_port, pd_event_t *pd_event, bool ack)
+{
+       u8 count;
+       u32 *svid_list;
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       if (ack) {
+               svid_list = &pd_msg->payload[1];
+               count = (PD_HEADER_CNT(pd_msg->msg_hdr) - 1);
+
+               if (dpm_dfp_consume_svids(pd_port, svid_list, count))
+                       return;
+       }
+
+       svdm_dfp_inform_svids(pd_port, ack);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+void pd_dpm_dfp_inform_modes(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool ack)
+{
+       u8 count;
+       u16 svid = 0;
+       u16 expected_svid = pd_port->mode_svid;
+
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       if (ack) {
+               count = (PD_HEADER_CNT(pd_msg->msg_hdr));
+               svid = PD_VDO_VID(pd_msg->payload[VDO_INDEX_HDR]);
+
+               if (svid != expected_svid) {
+                       ack = false;
+                       DPM_DBG("Not expected SVID (0x%04x, 0x%04x)\r\n",
+                               svid, expected_svid);
+               } else {
+                       dpm_dfp_update_svid_data_modes(
+                               pd_port, svid, &pd_msg->payload[1], count - 1);
+               }
+       }
+
+       svdm_dfp_inform_modes(pd_port, expected_svid, ack);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+void pd_dpm_dfp_inform_enter_mode(
+               pd_port_t *pd_port,
+               pd_event_t *pd_event, bool ack)
+{
+       u16 svid = 0;
+       u16 expected_svid = pd_port->mode_svid;
+       u8 ops = 0;
+
+       if (ack) {
+               dpm_vdm_get_svid_ops(pd_event, &svid, &ops);
+
+               /* TODO: check ops later ?! */
+               if (svid != expected_svid) {
+                       ack = false;
+                       DPM_DBG("Not expected SVID (0x%04x, 0x%04x)\r\n",
+                               svid, expected_svid);
+               } else {
+                       dpm_dfp_update_svid_enter_mode(pd_port, svid, ops);
+               }
+       }
+
+       svdm_dfp_inform_enter_mode(pd_port, expected_svid, ops, ack);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+void pd_dpm_dfp_inform_exit_mode(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u16 svid = 0;
+       u16 expected_svid = pd_port->mode_svid;
+       u8 ops;
+
+       if (pd_event->event_type != PD_EVT_TIMER_MSG) {
+               dpm_vdm_get_svid_ops(pd_event, &svid, &ops);
+       } else {
+               svid = pd_port->mode_svid;
+               ops = pd_port->mode_obj_pos;
+       }
+
+       dpm_dfp_update_svid_data_exit_mode(pd_port, expected_svid, ops);
+
+       svdm_dfp_inform_exit_mode(pd_port, expected_svid, ops);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+void pd_dpm_dfp_inform_attention(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u16 svid = 0;
+       u8 ops;
+
+       dpm_vdm_get_svid_ops(pd_event, &svid, &ops);
+       DPM_DBG("Attention (svid0x%04x, mode:%d)\r\n", svid, ops);
+
+       svdm_dfp_inform_attention(pd_port, svid, pd_event);
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+void pd_dpm_dfp_inform_cable_vdo(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       const int size = sizeof(u32) * VDO_MAX_SIZE;
+
+       if (pd_event->pd_msg)
+               memcpy(pd_port->cable_vdos, pd_event->pd_msg->payload, size);
+
+       vdm_put_dpm_notified_event(pd_port);
+}
+
+/*
+ * DRP : Inform Source/Sink Cap
+ */
+
+void pd_dpm_dr_inform_sink_cap(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+       pd_port_power_caps *snk_cap = &pd_port->remote_snk_cap;
+       char buf[1024] = { 0 };
+
+       if (pd_event_msg_match(pd_event, PD_EVT_DATA_MSG, PD_DATA_SINK_CAP)) {
+               if (!pd_msg)
+                       snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+               snk_cap->nr = PD_HEADER_CNT(pd_msg->msg_hdr);
+               memcpy(snk_cap->pdos, pd_msg->payload,
+                      sizeof(u32) * snk_cap->nr);
+
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SINK_CAP;
+       } else {
+               if (pd_event_msg_match(
+                                       pd_event,
+                                       PD_EVT_CTRL_MSG,
+                                       PD_CTRL_REJECT))
+                       pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SINK_CAP;
+
+               snk_cap->nr = 0;
+               snk_cap->pdos[0] = 0;
+       }
+
+       pd_dpm_update_pdos_flags(pd_port, snk_cap->pdos[0]);
+}
+
+void pd_dpm_dr_inform_source_cap(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+       pd_port_power_caps *src_cap = &pd_port->remote_src_cap;
+       char buf[1024] = { 0 };
+
+       if (pd_event_msg_match(pd_event, PD_EVT_DATA_MSG, PD_DATA_SOURCE_CAP)) {
+               if (!pd_msg)
+                       snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+               src_cap->nr = PD_HEADER_CNT(pd_msg->msg_hdr);
+               memcpy(src_cap->pdos, pd_msg->payload,
+                      sizeof(u32) * src_cap->nr);
+
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SOURCE_CAP;
+       } else {
+               if (pd_event_msg_match(
+                                       pd_event,
+                                       PD_EVT_CTRL_MSG, PD_CTRL_REJECT))
+                       pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_SOURCE_CAP;
+
+               src_cap->nr = 0;
+               src_cap->pdos[0] = 0;
+       }
+
+       pd_dpm_update_pdos_flags(pd_port, src_cap->pdos[0]);
+}
+
+/*
+ * DRP : Data Role Swap
+ */
+
+void pd_dpm_drs_evaluate_swap(pd_port_t *pd_port, u8 role)
+{
+       /* TODO : Check it later */
+       pd_put_dpm_ack_event(pd_port);
+}
+
+void pd_dpm_drs_change_role(pd_port_t *pd_port, u8 role)
+{
+       pd_set_data_role(pd_port, role);
+
+       /* pd_put_dpm_ack_event(pd_port); */
+       pd_port->dpm_ack_immediately = true;
+}
+
+/* Rules: */
+       /* External Sources -> EXS */
+       /* Provider/Consumers -> PC */
+       /* Consumers/Provider -> CP */
+
+       /* 1. PC (with EXS) shall always deny PR_SWAP from CP (without EXS) */
+
+       /* 2. PC (without EXS) shall always acppet PR_SWAP from CP (with EXS) */
+       /* unless the requester isn't able to provide PDOs. */
+
+void pd_dpm_prs_evaluate_swap(pd_port_t *pd_port, u8 role)
+{
+       int good_power;
+       bool accept = true;
+       bool sink, check_src, check_snk, check_ext;
+
+       check_src = (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SRC) ? 1 : 0;
+       check_snk = (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SNK) ? 1 : 0;
+       check_ext = (pd_port->dpm_flags & DPM_FLAGS_CHECK_EXT_POWER) ? 1 : 0;
+
+       if (check_src | check_snk | check_ext) {
+               sink = pd_port->power_role == PD_ROLE_SINK;
+               good_power = dpm_check_good_power(pd_port);
+
+               switch (good_power) {
+               case GOOD_PW_PARTNER:
+                       if (sink && check_snk)
+                               accept = false;
+                       break;
+
+               case GOOD_PW_LOCAL:
+                       if ((!sink) && (check_src || check_ext))
+                               accept = false;
+                       break;
+
+               case GOOD_PW_NONE:
+                       accept = true;
+                       break;
+
+               default:
+                       accept = true;
+                       break;
+               }
+       }
+
+       dpm_response_request(pd_port, accept);
+}
+
+void pd_dpm_prs_turn_off_power_sink(pd_port_t *pd_port)
+{
+       tcpci_sink_vbus(
+                       pd_port->tcpc_dev,
+                       TCP_VBUS_CTRL_PR_SWAP, TCPC_VBUS_SINK_0V, 0);
+}
+
+void pd_dpm_prs_enable_power_source(pd_port_t *pd_port, bool en)
+{
+       int vbus_level = en ? TCPC_VBUS_SOURCE_5V : TCPC_VBUS_SOURCE_0V;
+
+       tcpci_source_vbus(
+                       pd_port->tcpc_dev,
+                       TCP_VBUS_CTRL_PR_SWAP, vbus_level, -1);
+
+       if (en)
+               pd_enable_vbus_valid_detection(pd_port, en);
+       else
+               pd_enable_vbus_safe0v_detection(pd_port);
+}
+
+void pd_dpm_prs_change_role(pd_port_t *pd_port, u8 role)
+{
+       pd_set_power_role(pd_port, role);
+       pd_put_dpm_ack_event(pd_port);
+}
+
+/*
+ * DRP : Vconn Swap
+ */
+
+void pd_dpm_vcs_evaluate_swap(pd_port_t *pd_port)
+{
+       bool accept = true;
+
+       dpm_response_request(pd_port, accept);
+}
+
+void pd_dpm_vcs_enable_vconn(pd_port_t *pd_port, bool en)
+{
+       pd_dpm_enable_vconn(pd_port, en);
+
+       /* TODO: If we can't enable vconn immediately, */
+       /* then after vconn_on, Vconn Controller */
+       /*should pd_put_dpm_ack_event() */
+
+       pd_port->dpm_ack_immediately = true;
+}
+
+/*
+ * PE : Notify DPM
+ */
+
+static inline int pd_dpm_ready_get_sink_cap(pd_port_t *pd_port)
+{
+       if (!(pd_port->dpm_flags & DPM_FLAGS_CHECK_SINK_CAP))
+               return 0;
+
+       if (pd_port->get_snk_cap_count >= PD_GET_SNK_CAP_RETRIES)
+               return 0;
+
+       pd_port->get_snk_cap_count++;
+       pd_put_dpm_pd_request_event(
+               pd_port, PD_DPM_PD_REQUEST_GET_SINK_CAP);
+
+       return 1;
+}
+
+static inline int pd_dpm_ready_get_source_cap(pd_port_t *pd_port)
+{
+       if (!(pd_port->dpm_flags & DPM_FLAGS_CHECK_SOURCE_CAP))
+               return 0;
+
+       if (pd_port->get_src_cap_count >= PD_GET_SRC_CAP_RETRIES)
+               return 0;
+
+       pd_port->get_src_cap_count++;
+       pd_put_dpm_pd_request_event(
+               pd_port, PD_DPM_PD_REQUEST_GET_SOURCE_CAP);
+
+       return 1;
+}
+
+static inline int pd_dpm_ready_attempt_get_extbit(pd_port_t *pd_port)
+{
+       if (pd_port->remote_src_cap.nr >= 1)
+               return 0;
+
+       if (pd_port->remote_snk_cap.nr >= 1)
+               return 0;
+
+       if (!(pd_port->dpm_flags & DPM_FLAGS_CHECK_EXT_POWER))
+               return 0;
+
+       if (pd_port->get_snk_cap_count >= PD_GET_SNK_CAP_RETRIES)
+               return 0;
+
+       pd_port->get_snk_cap_count++;
+       pd_put_dpm_pd_request_event(
+                       pd_port, PD_DPM_PD_REQUEST_GET_SINK_CAP);
+       return 1;
+}
+
+static inline int pd_dpm_notify_pe_src_ready(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       return pd_dpm_ready_attempt_get_extbit(pd_port);
+}
+
+static inline int pd_dpm_notify_pe_dfp_ready(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       if (pd_port->dpm_flags & DPM_FLAGS_CHECK_CABLE_ID_DFP) {
+               if (pd_is_auto_discover_cable_id(pd_port)) {
+                       if (!pd_port->vconn_source) {
+                               pd_port->vconn_return = true;
+                               pd_put_dpm_pd_request_event(
+                                               pd_port,
+                                               PD_DPM_PD_REQUEST_VCONN_SWAP);
+                               return 1;
+                       }
+
+                       pd_restart_timer(pd_port, PD_TIMER_DISCOVER_ID);
+                       return 1;
+               }
+       }
+
+       if (pd_port->vconn_return) {
+               DPM_DBG("VconnReturn\r\n");
+               pd_port->vconn_return = false;
+               if (pd_port->vconn_source) {
+                       pd_put_dpm_pd_request_event(
+                                       pd_port,
+                                       PD_DPM_PD_REQUEST_VCONN_SWAP);
+                       return 1;
+               }
+       }
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+
+#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID
+       if (pd_port->dpm_flags & DPM_FLAGS_CHECK_UFP_ID) {
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_UFP_ID;
+               if (vdm_put_dpm_vdm_request_event(
+                       pd_port, PD_DPM_VDM_REQUEST_DISCOVER_ID))
+                       return 1;
+       }
+#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */
+
+#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
+       if (pd_port->dpm_flags & DPM_FLAGS_CHECK_UFP_SVID) {
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_UFP_SVID;
+               if (vdm_put_dpm_vdm_request_event(
+                       pd_port, PD_DPM_VDM_REQUEST_DISCOVER_SVIDS))
+                       return 1;
+       }
+#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */
+
+#ifdef CONFIG_USB_PD_MODE_OPERATION
+       if (svdm_notify_pe_ready(pd_port, pd_event))
+               return 1;
+#endif /* CONFIG_USB_PD_MODE_OPERATION */
+
+       return 0;
+}
+
+int pd_dpm_notify_pe_startup(pd_port_t *pd_port)
+{
+       u32 caps, flags = 0;
+
+       caps = DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps);
+       if (caps != DPM_CAP_PR_CHECK_DISABLE)
+               flags |= DPM_FLAGS_CHECK_PR_ROLE;
+
+       caps = DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps);
+       if (caps != DPM_CAP_DR_CHECK_DISABLE)
+               flags |= DPM_FLAGS_CHECK_DR_ROLE;
+
+       if (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SRC)
+               flags |= DPM_FLAGS_CHECK_EXT_POWER;
+
+       if (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SNK)
+               flags |= DPM_FLAGS_CHECK_EXT_POWER;
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER)
+               flags |= DPM_FLAGS_CHECK_EXT_POWER;
+
+       if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_CABLE)
+               flags |= DPM_FLAGS_CHECK_CABLE_ID;
+
+       if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP)
+               flags |= DPM_FLAGS_CHECK_CABLE_ID_DFP;
+
+       if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_ID)
+               flags |= DPM_FLAGS_CHECK_UFP_ID;
+
+       pd_port->dpm_flags = flags;
+       pd_port->dpm_dfp_retry_cnt = 2;
+
+       svdm_notify_pe_startup(pd_port);
+       return 0;
+}
+
+int pd_dpm_notify_pe_hardreset(pd_port_t *pd_port)
+{
+       u32 flags = 0;
+
+       if (pd_port->dpm_dfp_retry_cnt) {
+               pd_port->dpm_dfp_retry_cnt--;
+               pd_port->dpm_flags |= flags;
+               svdm_notify_pe_startup(pd_port);
+       }
+
+       return 0;
+}
+
+int pd_dpm_notify_pe_ready(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       int ret = 0;
+
+       if (pd_dpm_ready_get_source_cap(pd_port))
+               return 1;
+
+       if (pd_dpm_ready_get_sink_cap(pd_port))
+               return 1;
+
+       if (pd_port->power_role == PD_ROLE_SOURCE)
+               ret = pd_dpm_notify_pe_src_ready(pd_port, pd_event);
+
+       if (ret != 0)
+               return ret;
+
+       if (pd_port->data_role == PD_ROLE_DFP)
+               ret = pd_dpm_notify_pe_dfp_ready(pd_port, pd_event);
+
+       if (ret != 0)
+               return ret;
+
+       if (!pd_port->pe_ready) {
+               pd_port->pe_ready = true;
+               pd_update_connect_state(pd_port, PD_CONNECT_PE_READY);
+       }
+
+       return 0;
+}
diff --git a/drivers/usb/pd/richtek/pd_dpm_prv.h b/drivers/usb/pd/richtek/pd_dpm_prv.h
new file mode 100644 (file)
index 0000000..67976c4
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PD_DPM_PRV_H_INCLUDED
+#define PD_DPM_PRV_H_INCLUDED
+
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+typedef struct __eval_snk_request_result {
+       int src_sel;
+       int snk_sel;
+} eval_snk_request_result_t;
+
+#define SVID_DATA_LOCAL_MODE(svid_data, n)     \
+               ((svid_data)->local_mode.mode_vdo[n])
+
+#define SVID_DATA_REMOTE_MODE(svid_data, n) \
+               ((svid_data)->remote_mode.mode_vdo[n])
+
+#define SVID_DATA_DFP_GET_ACTIVE_MODE(svid_data)\
+       SVID_DATA_REMOTE_MODE(svid_data, svid_data->active_mode - 1)
+
+#define SVID_DATA_UFP_GET_ACTIVE_MODE(svid_data)\
+       SVID_DATA_LOCAL_MODE(svid_data, svid_data->active_mode - 1)
+
+bool eval_snk_cap_request(
+       const pd_port_power_caps *snk_caps,
+       const pd_port_power_caps *src_caps,
+       int strategy,
+       eval_snk_request_result_t *result);
+
+enum pd_ufp_u_state {
+       DP_UFP_U_NONE = 0,
+       DP_UFP_U_STARTUP,
+       DP_UFP_U_WAIT,
+       DP_UFP_U_OPERATION,
+       DP_UFP_U_STATE_NR,
+
+       DP_UFP_U_ERR = 0X10,
+
+       DP_DFP_U_ERR_DP_CONNECTED,
+};
+
+typedef struct __pd_mode_prop {
+       const char *name;
+       u32 svid;
+       void (*request_enter_mode)(pd_port_t *pd_port);
+       void (*request_exit_mode)(pd_port_t *pd_port);
+       bool (*dfp_inform_id)(
+                       pd_port_t *pd_port,
+                       pd_event_t *pd_event, bool ack);
+       bool (*dfp_inform_svids)(pd_port_t *pd_port, bool ack);
+       bool (*dfp_inform_modes)(pd_port_t *pd_port, bool ack);
+       bool (*dfp_inform_enter_mode)(pd_port_t *pd_port, bool ack);
+       bool (*dfp_inform_exit_mode)(pd_port_t *pd_port, u16 svid);
+       bool (*dfp_inform_attention)(pd_port_t *pd_port, pd_event_t *pd_event);
+       bool (*notify_pe_dfp_ready)(pd_port_t *pd_port, pd_event_t *pd_event);
+       void (*reset_state)(pd_port_t *pd_port);
+} pd_mode_prop_t;
+
+typedef struct __svdm_svid_ops {
+       const char *name;
+       u16 svid;
+
+       bool (*dfp_inform_id)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data,
+                       pd_event_t *pd_event, bool ack);
+       bool (*dfp_inform_svids)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, bool ack);
+       bool (*dfp_inform_modes)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, bool ack);
+
+       bool (*dfp_inform_enter_mode)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, u8 ops, bool ack);
+       bool (*dfp_inform_exit_mode)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, u8 ops);
+
+       bool (*dfp_inform_attention)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, pd_event_t *pd_event);
+
+       void (*ufp_request_enter_mode)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, u8 ops);
+       void (*ufp_request_exit_mode)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, u8 ops);
+
+       bool (*notify_pe_startup)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data);
+       int (*notify_pe_ready)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data, pd_event_t *pd_event);
+
+       bool (*reset_state)(
+                       pd_port_t *pd_port,
+                       svdm_svid_data_t *svid_data);
+} svdm_svid_ops_t;
+
+static inline svdm_svid_data_t *
+       dpm_get_svdm_svid_data(pd_port_t *pd_port, u16 svid)
+{
+       u8 i;
+       svdm_svid_data_t *svid_data;
+
+       if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT))
+               return NULL;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->svid == svid)
+                       return svid_data;
+       }
+
+       return NULL;
+}
+
+static inline void dpm_vdm_get_svid_ops(
+       pd_event_t *pd_event, u16 *svid, u8 *ops)
+{
+       u32 vdm_hdr;
+       char buf[1024] = { 0 };
+
+       if (!pd_event->pd_msg)
+               snprintf(buf, sizeof(buf), "the pd msg is NULL\n");
+       vdm_hdr = pd_event->pd_msg->payload[0];
+       if (svid)
+               *svid = PD_VDO_VID(vdm_hdr);
+       if (ops)
+               *ops = PD_VDO_OPOS(vdm_hdr);
+}
+
+static inline bool dpm_register_svdm_ops(
+       pd_port_t *pd_port, const svdm_svid_ops_t *ops)
+{
+       svdm_svid_data_t *svid_data =
+               dpm_get_svdm_svid_data(pd_port, ops->svid);
+       if (!svid_data)
+               return false;
+
+       svid_data->ops = ops;
+       return true;
+}
+
+static inline bool svdm_notify_pe_startup(pd_port_t *pd_port)
+{
+       int i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->ops && svid_data->ops->notify_pe_startup)
+                       svid_data->ops->notify_pe_startup(pd_port, svid_data);
+       }
+
+       return true;
+}
+
+static inline int svdm_notify_pe_ready(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       int i, ret;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->ops && svid_data->ops->notify_pe_ready) {
+                       ret = svid_data->ops->notify_pe_ready(
+                               pd_port, svid_data, pd_event);
+
+                       if (ret != 0)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static inline bool svdm_reset_state(pd_port_t *pd_port)
+{
+       int i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->ops && svid_data->ops->reset_state)
+                       svid_data->ops->reset_state(pd_port, svid_data);
+       }
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_id(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool ack)
+{
+       int i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->ops && svid_data->ops->dfp_inform_id)
+                       svid_data->ops->dfp_inform_id(
+                                       pd_port, svid_data, pd_event, ack);
+       }
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_svids(pd_port_t *pd_port, bool ack)
+{
+       int i;
+       svdm_svid_data_t *svid_data;
+
+       for (i = 0; i < pd_port->svid_data_cnt; i++) {
+               svid_data = &pd_port->svid_data[i];
+               if (svid_data->ops && svid_data->ops->dfp_inform_svids)
+                       svid_data->ops->dfp_inform_svids(
+                                               pd_port, svid_data, ack);
+       }
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_modes(
+               pd_port_t *pd_port, u16 svid, bool ack)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->dfp_inform_modes)
+               svid_data->ops->dfp_inform_modes(pd_port, svid_data, ack);
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_enter_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops, bool ack)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->dfp_inform_enter_mode)
+               svid_data->ops->dfp_inform_enter_mode(
+                                               pd_port, svid_data, ops, ack);
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_exit_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->dfp_inform_exit_mode)
+               svid_data->ops->dfp_inform_exit_mode(pd_port, svid_data, ops);
+
+       return true;
+}
+
+static inline bool svdm_dfp_inform_attention(
+       pd_port_t *pd_port, u16 svid, pd_event_t *pd_event)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->dfp_inform_attention)
+               svid_data->ops->dfp_inform_attention(
+                                       pd_port, svid_data, pd_event);
+
+       return true;
+}
+
+static inline bool svdm_ufp_request_enter_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->ufp_request_enter_mode)
+               svid_data->ops->ufp_request_enter_mode(pd_port, svid_data, ops);
+
+       return true;
+}
+
+static inline bool svdm_ufp_request_exit_mode(
+       pd_port_t *pd_port, u16 svid, u8 ops)
+{
+       svdm_svid_data_t *svid_data;
+
+       svid_data = dpm_get_svdm_svid_data(pd_port, svid);
+       if (!svid_data)
+               return false;
+
+       if (svid_data->ops && svid_data->ops->ufp_request_exit_mode)
+               svid_data->ops->ufp_request_exit_mode(pd_port, svid_data, ops);
+
+       return true;
+}
+
+#endif /* PD_DPM_PRV_H_INCLUDED */
diff --git a/drivers/usb/pd/richtek/pd_policy_engine.c b/drivers/usb/pd/richtek/pd_policy_engine.c
new file mode 100644 (file)
index 0000000..ceded2c
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+/* ---- Policy Engine State ---- */
+
+#if PE_STATE_FULL_NAME
+
+static const char *const pe_state_name[] = {
+       "PE_SRC_STARTUP",
+       "PE_SRC_DISCOVERY",
+       "PE_SRC_SEND_CAPABILITIES",
+       "PE_SRC_NEGOTIATE_CAPABILITIES",
+       "PE_SRC_TRANSITION_SUPPLY",
+       "PE_SRC_TRANSITION_SUPPLY2",
+       "PE_SRC_READY",
+       "PE_SRC_DISABLED",
+       "PE_SRC_CAPABILITY_RESPONSE",
+       "PE_SRC_HARD_RESET",
+       "PE_SRC_HARD_RESET_RECEIVED",
+       "PE_SRC_TRANSITION_TO_DEFAULT",
+       "PE_SRC_GIVE_SOURCE_CAP",
+       "PE_SRC_GET_SINK_CAP",
+       "PE_SRC_WAIT_NEW_CAPABILITIES",
+
+       "PE_SRC_SEND_SOFT_RESET",
+       "PE_SRC_SOFT_RESET",
+       "PE_SRC_PING",
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       "PE_SRC_VDM_IDENTITY_REQUEST",
+       "PE_SRC_VDM_IDENTITY_ACKED",
+       "PE_SRC_VDM_IDENTITY_NAKED",
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+
+       "PE_SNK_STARTUP",
+       "PE_SNK_DISCOVERY",
+       "PE_SNK_WAIT_FOR_CAPABILITIES",
+       "PE_SNK_EVALUATE_CAPABILITY",
+       "PE_SNK_SELECT_CAPABILITY",
+       "PE_SNK_TRANSITION_SINK",
+       "PE_SNK_READY",
+       "PE_SNK_HARD_RESET",
+       "PE_SNK_TRANSITION_TO_DEFAULT",
+       "PE_SNK_GIVE_SINK_CAP",
+       "PE_SNK_GET_SOURCE_CAP",
+
+       "PE_SNK_SEND_SOFT_RESET",
+       "PE_SNK_SOFT_RESET",
+
+       "PE_DRS_DFP_UFP_EVALUATE_DR_SWAP",
+       "PE_DRS_DFP_UFP_ACCEPT_DR_SWAP",
+       "PE_DRS_DFP_UFP_CHANGE_TO_UFP",
+       "PE_DRS_DFP_UFP_SEND_DR_SWAP",
+       "PE_DRS_DFP_UFP_REJECT_DR_SWAP",
+
+       "PE_DRS_UFP_DFP_EVALUATE_DR_SWAP",
+       "PE_DRS_UFP_DFP_ACCEPT_DR_SWAP",
+       "PE_DRS_UFP_DFP_CHANGE_TO_DFP",
+       "PE_DRS_UFP_DFP_SEND_SWAP",
+       "PE_DRS_UFP_DFP_REJECT_DR_SWAP",
+
+       "PE_PRS_SRC_SNK_EVALUATE_PR_SWAP",
+       "PE_PRS_SRC_SNK_ACCEPT_PR_SWAP",
+       "PE_PRS_SRC_SNK_TRANSITION_TO_OFF",
+       "PE_PRS_SRC_SNK_ASSERT_RD",
+       "PE_PRS_SRC_SNK_WAIT_SOURCE_ON",
+       "PE_PRS_SRC_SNK_SEND_SWAP",
+       "PE_PRS_SRC_SNK_REJECT_PR_SWAP",
+
+       "PE_PRS_SNK_SRC_EVALUATE_PR_SWAP",
+       "PE_PRS_SNK_SRC_ACCEPT_PR_SWAP",
+       "PE_PRS_SNK_SRC_TRANSITION_TO_OFF",
+       "PE_PRS_SNK_SRC_ASSERT_RP",
+       "PE_PRS_SNK_SRC_SOURCE_ON",
+       "PE_PRS_SNK_SRC_SEND_PR_SWAP",
+       "PE_PRS_SNK_SRC_REJECT_SWAP",
+
+       "PE_DR_SRC_GET_SOURCE_CAP",
+
+       "PE_DR_SRC_GIVE_SINK_CAP",
+
+       "PE_DR_SNK_GET_SINK_CAP",
+
+       "PE_DR_SNK_GIVE_SOURCE_CAP",
+
+       "PE_VCS_SEND_SWAP",
+       "PE_VCS_EVALUATE_SWAP",
+       "PE_VCS_ACCEPT_SWAP",
+       "PE_VCS_REJECT_SWAP",
+       "PE_VCS_WAIT_FOR_VCONN",
+       "PE_VCS_TURN_OFF_VCONN",
+       "PE_VCS_TURN_ON_VCONN",
+       "PE_VCS_SEND_PS_RDY",
+
+       "PE_UFP_VDM_GET_IDENTITY",
+       "PE_UFP_VDM_SEND_IDENTITY",
+       "PE_UFP_VDM_GET_IDENTITY_NAK",
+
+       "PE_UFP_VDM_GET_SVIDS",
+       "PE_UFP_VDM_SEND_SVIDS",
+       "PE_UFP_VDM_GET_SVIDS_NAK",
+
+       "PE_UFP_VDM_GET_MODES",
+       "PE_UFP_VDM_SEND_MODES",
+       "PE_UFP_VDM_GET_MODES_NAK",
+
+       "PE_UFP_VDM_EVALUATE_MODE_ENTRY",
+       "PE_UFP_VDM_MODE_ENTRY_ACK",
+       "PE_UFP_VDM_MODE_ENTRY_NAK",
+
+       "PE_UFP_VDM_MODE_EXIT",
+       "PE_UFP_VDM_MODE_EXIT_ACK",
+       "PE_UFP_VDM_MODE_EXIT_NAK",
+
+       "PE_UFP_VDM_ATTENTION_REQUEST",
+
+       "PE_DFP_UFP_VDM_IDENTITY_REQUEST",
+       "PE_DFP_UFP_VDM_IDENTITY_ACKED",
+       "PE_DFP_UFP_VDM_IDENTITY_NAKED",
+
+       "PE_DFP_CBL_VDM_IDENTITY_REQUEST",
+       "PE_DFP_CBL_VDM_IDENTITY_ACKED",
+       "PE_DFP_CBL_VDM_IDENTITY_NAKED",
+
+       "PE_DFP_VDM_SVIDS_REQUEST",
+       "PE_DFP_VDM_SVIDS_ACKED",
+       "PE_DFP_VDM_SVIDS_NAKED",
+
+       "PE_DFP_VDM_MODES_REQUEST",
+       "PE_DFP_VDM_MODES_ACKED",
+       "PE_DFP_VDM_MODES_NAKED",
+
+       "PE_DFP_VDM_MODE_ENTRY_REQUEST",
+       "PE_DFP_VDM_MODE_ENTRY_ACKED",
+       "PE_DFP_VDM_MODE_ENTRY_NAKED",
+
+       "PE_DFP_VDM_MODE_EXIT_REQUEST",
+       "PE_DFP_VDM_MODE_EXIT_ACKED",
+
+       "PE_DFP_VDM_ATTENTION_REQUEST",
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       "PE_DBG_READY",
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       "PE_BIST_TEST_DATA",
+       "PE_BIST_CARRIER_MODE_2",
+
+       "PE_IDLE1",
+       "PE_IDLE2",
+
+       "PE_VIRT_HARD_RESET",
+       "PE_VIRT_READY",
+};
+#else
+
+static const char *const pe_state_name[] = {
+       "SRC_START",
+       "SRC_DISCOVERY",
+       "SRC_SEND_CAP",
+       "SRC_NEG_CAP",
+       "SRC_TRANS_SUPPLY",
+       "SRC_TRANS_SUPPLY2",
+       "SRC_READY",
+       "SRC_DISABLED",
+       "SRC_CAP_RESP",
+       "SRC_HRESET",
+       "SRC_HRESET_RECV",
+       "SRC_TRANS_DFT",
+       "SRC_GIVE_CAP",
+       "SRC_GET_CAP",
+       "SRC_WAIT_CAP",
+
+       "SRC_SEND_SRESET",
+       "SRC_SRESET",
+       "SRC_PING",
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       "SRC_VDM_ID_REQ",
+       "SRC_VDM_ID_ACK",
+       "SRC_VDM_ID_NAK",
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+
+       "SNK_START",
+       "SNK_DISCOVERY",
+       "SNK_WAIT_CAP",
+       "SNK_EVA_CAP",
+       "SNK_SEL_CAP",
+       "SNK_TRANS_SINK",
+       "SNK_READY",
+       "SNK_HRESET",
+       "SNK_TRANS_DFT",
+       "SNK_GIVE_CAP",
+       "SNK_GET_CAP",
+
+       "SNK_SEND_SRESET",
+       "SNK_SRESET",
+
+       "D_DFP_EVALUATE",
+       "D_DFP_ACCEPT",
+       "D_DFP_CHANGE",
+       "D_DFP_SEND",
+       "D_DFP_REJECT",
+
+       "D_UFP_EVALUATE",
+       "D_UFP_ACCEPT",
+       "D_UFP_CHANGE",
+       "D_UFP_SEND",
+       "D_UFP_REJECT",
+
+       "P_SRC_EVALUATE",
+       "P_SRC_ACCEPT",
+       "P_SRC_TRANS_OFF",
+       "P_SRC_ASSERT",
+       "P_SRC_WAIT_ON",
+       "P_SRC_SEND",
+       "P_SRC_REJECT",
+
+       "P_SNK_EVALUATE",
+       "P_SNK_ACCEPT",
+       "P_SNK_TRANS_OFF",
+       "P_SNK_ASSERT",
+       "P_SNK_SOURCE_ON",
+       "P_SNK_SEND",
+       "P_SNK_REJECT",
+
+       "DR_SRC_GET_CAP",       /* get source cap */
+       "DR_SRC_GIVE_CAP",      /* give sink cap */
+       "DR_SNK_GET_CAP",       /* get sink cap */
+       "DR_SNK_GIVE_CAP", /* give source cap */
+
+       "V_SEND",
+       "V_EVALUATE",
+       "V_ACCEPT",
+       "V_REJECT",
+       "V_WAIT_VCONN",
+       "V_TURN_OFF",
+       "V_TURN_ON",
+       "V_PS_RDY",
+
+       "U_GET_ID",
+       "U_SEND_ID",
+       "U_GET_ID_N",
+
+       "U_GET_SVID",
+       "U_SEND_SVID",
+       "U_GET_SVID_N",
+
+       "U_GET_MODE",
+       "U_SEND_MODE",
+       "U_GET_MODE_N",
+
+       "U_EVA_MODE",
+       "U_MODE_EN_A",
+       "U_MODE_EN_N",
+
+       "U_MODE_EX",
+       "U_MODE_EX_A",
+       "U_MODE_EX_N",
+
+       "U_ATTENTION",
+
+       "D_UID_REQ",
+       "D_UID_A",
+       "D_UID_N",
+
+       "D_CID_REQ",
+       "D_CID_ACK",
+       "D_CID_NAK",
+
+       "D_SVID_REQ",
+       "D_SVID_ACK",
+       "D_SVID_NAK",
+
+       "D_MODE_REQ",
+       "D_MODE_ACK",
+       "D_MODE_NAK",
+
+       "D_MODE_EN_REQ",
+       "D_MODE_EN_ACK",
+       "D_MODE_EN_NAK",
+
+       "D_MODE_EX_REQ",
+       "D_MODE_EX_ACK",
+
+       "D_ATTENTION",
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       "DBG_READY",
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       "ERR_RECOVERY",
+
+       "BIST_TD",
+       "BIST_C2",
+
+       "IDLE1",
+       "IDLE2",
+
+       "VIRT_HARD_RESET",
+       "VIRT_READY",
+};
+
+#endif
+
+typedef void (*pe_state_action_fcn_t)
+       (pd_port_t *pd_port, pd_event_t *pd_event);
+
+typedef struct __pe_state_actions {
+       const pe_state_action_fcn_t entry_action;
+       /* const pd_pe_state_action_fcn_t exit_action; */
+} pe_state_actions_t;
+
+#define PE_STATE_ACTIONS(state) { .entry_action = state##_entry, }
+
+/*
+ * Policy Engine General State Activity
+ */
+
+/* extern int rt1711_set_bist_carrier_mode( */
+/* struct tcpc_device *tcpc_dev, uint8_t pattern); */
+static void pe_idle_reset_data(pd_port_t *pd_port)
+{
+       pd_reset_pe_timer(pd_port);
+       pd_reset_svid_data(pd_port);
+
+       pd_port->pd_prev_connected = false;
+       pd_port->state_machine = PE_STATE_MACHINE_IDLE;
+
+       switch (pd_port->pe_state_curr) {
+       case PE_BIST_TEST_DATA:
+               pd_enable_bist_test_mode(pd_port, false);
+               break;
+
+       case PE_BIST_CARRIER_MODE_2:
+               pd_disable_bist_mode2(pd_port);
+               break;
+       }
+
+       pd_unlock_msg_output(pd_port);
+}
+
+static void pe_idle1_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pe_idle_reset_data(pd_port);
+
+       pd_try_put_pe_idle_event(pd_port);
+}
+
+static void pe_idle2_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
+       pd_notify_pe_idle(pd_port);
+}
+
+void pe_error_recovery_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pe_idle_reset_data(pd_port);
+
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
+       pd_notify_pe_error_recovery(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_bist_test_data_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_bist_test_mode(pd_port, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_bist_test_data_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_bist_test_mode(pd_port, false);
+}
+
+void pe_bist_carrier_mode_2_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_bist_mode2(pd_port);
+       pd_enable_timer(pd_port, PD_TIMER_BIST_CONT_MODE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_bist_carrier_mode_2_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_BIST_CONT_MODE);
+       pd_disable_bist_mode2(pd_port);
+}
+
+/*
+ * Policy Engine Share State Activity
+ */
+
+void pe_power_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->during_swap = false;
+       pd_port->explicit_contract = true;
+
+       if (pd_port->data_role == PD_ROLE_UFP)
+               pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_UFP);
+       else
+               pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_DFP);
+
+       pd_dpm_notify_pe_ready(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+static const pe_state_actions_t pe_state_actions[] = {
+       /* src activity */
+       PE_STATE_ACTIONS(pe_src_startup),
+       PE_STATE_ACTIONS(pe_src_discovery),
+       PE_STATE_ACTIONS(pe_src_send_capabilities),
+       PE_STATE_ACTIONS(pe_src_negotiate_capabilities),
+       PE_STATE_ACTIONS(pe_src_transition_supply),
+       PE_STATE_ACTIONS(pe_src_transition_supply2),
+       PE_STATE_ACTIONS(pe_src_ready),
+       PE_STATE_ACTIONS(pe_src_disabled),
+       PE_STATE_ACTIONS(pe_src_capability_response),
+       PE_STATE_ACTIONS(pe_src_hard_reset),
+       PE_STATE_ACTIONS(pe_src_hard_reset_received),
+       PE_STATE_ACTIONS(pe_src_transition_to_default),
+       PE_STATE_ACTIONS(pe_src_give_source_cap),
+       PE_STATE_ACTIONS(pe_src_get_sink_cap),
+       PE_STATE_ACTIONS(pe_src_wait_new_capabilities),
+
+       PE_STATE_ACTIONS(pe_src_send_soft_reset),
+       PE_STATE_ACTIONS(pe_src_soft_reset),
+       PE_STATE_ACTIONS(pe_src_ping),
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       PE_STATE_ACTIONS(pe_src_vdm_identity_request),
+       PE_STATE_ACTIONS(pe_src_vdm_identity_acked),
+       PE_STATE_ACTIONS(pe_src_vdm_identity_naked),
+#endif
+
+       /* snk activity */
+       PE_STATE_ACTIONS(pe_snk_startup),
+       PE_STATE_ACTIONS(pe_snk_discovery),
+       PE_STATE_ACTIONS(pe_snk_wait_for_capabilities),
+       PE_STATE_ACTIONS(pe_snk_evaluate_capability),
+       PE_STATE_ACTIONS(pe_snk_select_capability),
+       PE_STATE_ACTIONS(pe_snk_transition_sink),
+       PE_STATE_ACTIONS(pe_snk_ready),
+       PE_STATE_ACTIONS(pe_snk_hard_reset),
+       PE_STATE_ACTIONS(pe_snk_transition_to_default),
+       PE_STATE_ACTIONS(pe_snk_give_sink_cap),
+       PE_STATE_ACTIONS(pe_snk_get_source_cap),
+
+       PE_STATE_ACTIONS(pe_snk_send_soft_reset),
+       PE_STATE_ACTIONS(pe_snk_soft_reset),
+
+       /* drs dfp activity */
+       PE_STATE_ACTIONS(pe_drs_dfp_ufp_evaluate_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_dfp_ufp_accept_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_dfp_ufp_change_to_ufp),
+       PE_STATE_ACTIONS(pe_drs_dfp_ufp_send_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_dfp_ufp_reject_dr_swap),
+
+       /* drs ufp activity */
+       PE_STATE_ACTIONS(pe_drs_ufp_dfp_evaluate_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_ufp_dfp_accept_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_ufp_dfp_change_to_dfp),
+       PE_STATE_ACTIONS(pe_drs_ufp_dfp_send_dr_swap),
+       PE_STATE_ACTIONS(pe_drs_ufp_dfp_reject_dr_swap),
+
+       /* prs src activity */
+       PE_STATE_ACTIONS(pe_prs_src_snk_evaluate_pr_swap),
+       PE_STATE_ACTIONS(pe_prs_src_snk_accept_pr_swap),
+       PE_STATE_ACTIONS(pe_prs_src_snk_transition_to_off),
+       PE_STATE_ACTIONS(pe_prs_src_snk_assert_rd),
+       PE_STATE_ACTIONS(pe_prs_src_snk_wait_source_on),
+       PE_STATE_ACTIONS(pe_prs_src_snk_send_swap),
+       PE_STATE_ACTIONS(pe_prs_src_snk_reject_pr_swap),
+
+       /* prs snk activity */
+       PE_STATE_ACTIONS(pe_prs_snk_src_evaluate_pr_swap),
+       PE_STATE_ACTIONS(pe_prs_snk_src_accept_pr_swap),
+       PE_STATE_ACTIONS(pe_prs_snk_src_transition_to_off),
+       PE_STATE_ACTIONS(pe_prs_snk_src_assert_rp),
+       PE_STATE_ACTIONS(pe_prs_snk_src_source_on),
+       PE_STATE_ACTIONS(pe_prs_snk_src_send_swap),
+       PE_STATE_ACTIONS(pe_prs_snk_src_reject_swap),
+
+       /* dr src activity */
+       PE_STATE_ACTIONS(pe_dr_src_get_source_cap),
+       PE_STATE_ACTIONS(pe_dr_src_give_sink_cap),
+
+       /* dr snk activity */
+       PE_STATE_ACTIONS(pe_dr_snk_get_sink_cap),
+       PE_STATE_ACTIONS(pe_dr_snk_give_source_cap),
+
+       /* vcs activity */
+       PE_STATE_ACTIONS(pe_vcs_send_swap),
+       PE_STATE_ACTIONS(pe_vcs_evaluate_swap),
+       PE_STATE_ACTIONS(pe_vcs_accept_swap),
+       PE_STATE_ACTIONS(pe_vcs_reject_vconn_swap),
+       PE_STATE_ACTIONS(pe_vcs_wait_for_vconn),
+       PE_STATE_ACTIONS(pe_vcs_turn_off_vconn),
+       PE_STATE_ACTIONS(pe_vcs_turn_on_vconn),
+       PE_STATE_ACTIONS(pe_vcs_send_ps_rdy),
+
+       /* ufp structured vdm activity */
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_identity),
+       PE_STATE_ACTIONS(pe_ufp_vdm_send_identity),
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_identity_nak),
+
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_svids),
+       PE_STATE_ACTIONS(pe_ufp_vdm_send_svids),
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_svids_nak),
+
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_modes),
+       PE_STATE_ACTIONS(pe_ufp_vdm_send_modes),
+       PE_STATE_ACTIONS(pe_ufp_vdm_get_modes_nak),
+
+       PE_STATE_ACTIONS(pe_ufp_vdm_evaluate_mode_entry),
+       PE_STATE_ACTIONS(pe_ufp_vdm_mode_entry_ack),
+       PE_STATE_ACTIONS(pe_ufp_vdm_mode_entry_nak),
+
+       PE_STATE_ACTIONS(pe_ufp_vdm_mode_exit),
+       PE_STATE_ACTIONS(pe_ufp_vdm_mode_exit_ack),
+       PE_STATE_ACTIONS(pe_ufp_vdm_mode_exit_nak),
+
+       PE_STATE_ACTIONS(pe_ufp_vdm_attention_request),
+
+       /* dfp structured vdm */
+       PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_request),
+       PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_acked),
+       PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_naked),
+
+       PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_request),
+       PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_acked),
+       PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_naked),
+
+       PE_STATE_ACTIONS(pe_dfp_vdm_svids_request),
+       PE_STATE_ACTIONS(pe_dfp_vdm_svids_acked),
+       PE_STATE_ACTIONS(pe_dfp_vdm_svids_naked),
+
+       PE_STATE_ACTIONS(pe_dfp_vdm_modes_request),
+       PE_STATE_ACTIONS(pe_dfp_vdm_modes_acked),
+       PE_STATE_ACTIONS(pe_dfp_vdm_modes_naked),
+
+       PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_request),
+       PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_acked),
+       PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_naked),
+
+       PE_STATE_ACTIONS(pe_dfp_vdm_mode_exit_request),
+       PE_STATE_ACTIONS(pe_dfp_vdm_mode_exit_acked),
+
+       PE_STATE_ACTIONS(pe_dfp_vdm_attention_request),
+
+       /* general activity */
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       PE_STATE_ACTIONS(pe_dbg_ready),
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+       PE_STATE_ACTIONS(pe_error_recovery),
+
+       PE_STATE_ACTIONS(pe_bist_test_data),
+       PE_STATE_ACTIONS(pe_bist_carrier_mode_2),
+
+       PE_STATE_ACTIONS(pe_idle1),
+       PE_STATE_ACTIONS(pe_idle2),
+};
+
+static void pe_exit_action_disable_sender_response(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+}
+
+pe_state_action_fcn_t pe_get_exit_action(uint8_t pe_state)
+{
+       pe_state_action_fcn_t retval = NULL;
+
+       switch (pe_state) {
+       /* Source */
+       case PE_SRC_SEND_CAPABILITIES:
+               retval = pe_src_send_capabilities_exit;
+               break;
+       case PE_SRC_TRANSITION_SUPPLY:
+               retval = pe_src_transition_supply_exit;
+               break;
+       case PE_SRC_TRANSITION_TO_DEFAULT:
+               retval = pe_src_transition_to_default_exit;
+               break;
+       case PE_SRC_GET_SINK_CAP:
+               retval = pe_src_get_sink_cap_exit;
+               break;
+
+       /* Sink */
+       case PE_SNK_WAIT_FOR_CAPABILITIES:
+               retval = pe_snk_wait_for_capabilities_exit;
+               break;
+       case PE_SNK_SELECT_CAPABILITY:
+               retval = pe_snk_select_capability_exit;
+               break;
+       case PE_SNK_TRANSITION_SINK:
+               retval = pe_snk_transition_sink_exit;
+               break;
+       case PE_SNK_TRANSITION_TO_DEFAULT:
+               retval = pe_snk_transition_to_default_exit;
+               break;
+
+       case PE_DR_SRC_GET_SOURCE_CAP:
+               retval = pe_dr_src_get_source_cap_exit;
+               break;
+       case PE_DR_SNK_GET_SINK_CAP:
+               retval = pe_dr_snk_get_sink_cap_exit;
+               break;
+
+       case PE_BIST_TEST_DATA:
+               retval = pe_bist_test_data_exit;
+               break;
+
+       case PE_BIST_CARRIER_MODE_2:
+               retval = pe_bist_carrier_mode_2_exit;
+               break;
+
+       case PE_VCS_SEND_SWAP:
+       case PE_PRS_SRC_SNK_SEND_SWAP:
+       case PE_PRS_SNK_SRC_SEND_SWAP:
+       case PE_DRS_DFP_UFP_SEND_DR_SWAP:
+       case PE_DRS_UFP_DFP_SEND_DR_SWAP:
+               retval = pe_exit_action_disable_sender_response;
+               break;
+
+       case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
+               retval = pe_prs_src_snk_wait_source_on_exit;
+               break;
+
+       case PE_PRS_SNK_SRC_SOURCE_ON:
+               retval = pe_prs_snk_src_source_on_exit;
+               break;
+
+       case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
+               retval = pe_prs_snk_src_transition_to_off_exit;
+               break;
+
+       case PE_VCS_WAIT_FOR_VCONN:
+               retval = pe_vcs_wait_for_vconn_exit;
+               break;
+       }
+
+       return retval;
+}
+
+static void pd_pe_state_change(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool vdm_evt)
+{
+       pe_state_action_fcn_t prev_exit_action;
+       pe_state_action_fcn_t next_entry_action;
+
+       u8 old_state = pd_port->pe_state_curr;
+       u8 new_state = pd_port->pe_state_next;
+       char buf[1024] = { 0 };
+
+       if ((old_state >= PD_NR_PE_STATES) || (new_state >= PD_NR_PE_STATES))
+               snprintf(buf, sizeof(buf), "the pd nr pe states\n");
+       if ((new_state == PE_IDLE1) || (new_state == PE_IDLE2))
+               prev_exit_action = NULL;
+       else
+               prev_exit_action = pe_get_exit_action(old_state);
+
+       next_entry_action = pe_state_actions[new_state].entry_action;
+
+       /*
+        * Source (P, Provider), Sink (C, Consumer)
+        * DFP (D), UFP (U)
+        * Vconn Source (Y/N)
+        */
+
+#if PE_DBG_ENABLE
+       PE_DBG("%s -> %s (%c%c%c)\r\n",
+              vdm_evt ? "VDM" : "PD", pe_state_name[new_state],
+               pd_port->power_role ? 'P' : 'C',
+               pd_port->data_role ? 'D' : 'U',
+               pd_port->vconn_source ? 'Y' : 'N');
+#else
+       if (!vdm_evt) {
+               PE_STATE_INFO("%s-> %s\r\n",
+                             vdm_evt ? "VDM" : "PD", pe_state_name[new_state]);
+       }
+#endif
+
+       if (prev_exit_action)
+               prev_exit_action(pd_port, pd_event);
+
+       if (next_entry_action)
+               next_entry_action(pd_port, pd_event);
+
+       if (vdm_evt)
+               pd_port->pe_vdm_state = new_state;
+       else
+               pd_port->pe_pd_state = new_state;
+}
+
+static int pd_handle_event(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool vdm_evt)
+{
+       if (vdm_evt) {
+               if (pd_port->reset_vdm_state) {
+                       pd_port->reset_vdm_state = false;
+                       pd_port->pe_vdm_state = pd_port->pe_pd_state;
+               }
+
+               pd_port->pe_state_curr = pd_port->pe_vdm_state;
+       } else {
+               pd_port->pe_state_curr = pd_port->pe_pd_state;
+       }
+
+       if (pd_process_event(pd_port, pd_event, vdm_evt))
+               pd_pe_state_change(pd_port, pd_event, vdm_evt);
+       else
+               pd_free_pd_event(pd_port, pd_event);
+
+       return 1;
+}
+
+static inline int pd_put_dpm_ack_immediately(
+       pd_port_t *pd_port, bool vdm_evt)
+{
+       pd_event_t pd_event = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_ACK,
+               .pd_msg = NULL,
+       };
+
+       pd_handle_event(pd_port, &pd_event, vdm_evt);
+
+       PE_DBG("ACK_Immediately\r\n");
+       pd_port->dpm_ack_immediately = false;
+       return 1;
+}
+
+int pd_policy_engine_run(struct tcpc_device *tcpc_dev)
+{
+       bool vdm_evt = false;
+       pd_event_t pd_event;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       if (!pd_get_event(tcpc_dev, &pd_event)) {
+               switch (pd_port->pe_pd_state) {
+               case PE_SNK_READY:
+               case PE_SRC_READY:
+               case PE_SRC_STARTUP:
+               case PE_SRC_DISCOVERY:
+                       vdm_evt = pd_get_vdm_event(tcpc_dev, &pd_event);
+                       break;
+               }
+
+               if (!vdm_evt)
+                       return 0;
+       }
+       mutex_lock(&pd_port->pd_lock);
+
+       pd_handle_event(pd_port, &pd_event, vdm_evt);
+
+       if (pd_port->dpm_ack_immediately)
+               pd_put_dpm_ack_immediately(pd_port, vdm_evt);
+
+       mutex_unlock(&pd_port->pd_lock);
+
+       return 1;
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_dbg.c b/drivers/usb/pd/richtek/pd_policy_engine_dbg.c
new file mode 100644 (file)
index 0000000..0efc83d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for DBGACC
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+
+void pe_dbg_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u8 state;
+
+       if (pd_port->pe_ready)
+               return;
+
+       pd_port->pe_ready = true;
+       pd_port->state_machine = PE_STATE_MACHINE_DBGACC;
+
+       if (pd_port->data_role == PD_ROLE_UFP) {
+               PE_INFO("Custom_DBGACC : UFP\r\n");
+               state = PD_CONNECT_PE_READY_DBGACC_UFP;
+               pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_UFP);
+       } else {
+               PE_INFO("Custom_DBGACC : DFP\r\n");
+               state = PD_CONNECT_PE_READY_DBGACC_DFP;
+               pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_DFP);
+       }
+
+       pd_reset_protocol_layer(pd_port);
+       pd_update_connect_state(pd_port, state);
+}
+
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_dfp.c b/drivers/usb/pd/richtek/pd_policy_engine_dfp.c
new file mode 100644 (file)
index 0000000..e255cc6
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for DFP
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-64 DFP to UFP VDM Discover Identity State Diagram
+ */
+
+void pe_dfp_ufp_vdm_identity_request_entry(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP);
+}
+
+void pe_dfp_ufp_vdm_identity_acked_entry(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_id(pd_port, pd_event, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_ufp_vdm_identity_naked_entry(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_id(pd_port, pd_event, false);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-65 DFP VDM Discover Identity State Diagram
+ */
+
+void pe_dfp_cbl_vdm_identity_request_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
+       pd_port->discover_id_counter++;
+
+       pd_enable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_cbl_vdm_identity_acked_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->dpm_flags &=
+               ~(DPM_FLAGS_CHECK_CABLE_ID | DPM_FLAGS_CHECK_CABLE_ID_DFP);
+
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_cable_vdo(pd_port, pd_event);
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_cbl_vdm_identity_naked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_cable_vdo(pd_port, pd_event);
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-66 DFP VDM Discover SVIDs State Diagram
+ */
+
+void pe_dfp_vdm_svids_request_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_discover_svids(pd_port, TCPC_TX_SOP);
+}
+
+void pe_dfp_vdm_svids_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_svids(pd_port, pd_event, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_vdm_svids_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_svids(pd_port, pd_event, false);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-67 DFP VDM Discover Modes State Diagram
+ */
+
+void pe_dfp_vdm_modes_request_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_discover_modes(pd_port, TCPC_TX_SOP, pd_port->mode_svid);
+}
+
+void pe_dfp_vdm_modes_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_modes(pd_port, pd_event, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_vdm_modes_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_modes(pd_port, pd_event, false);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-68 DFP VDM Mode Entry State Diagram
+ */
+
+void pe_dfp_vdm_mode_entry_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_enter_mode(pd_port, TCPC_TX_SOP,
+                              pd_port->mode_svid, pd_port->mode_obj_pos);
+}
+
+void pe_dfp_vdm_mode_entry_acked_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_enter_mode(pd_port, pd_event, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dfp_vdm_mode_entry_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_enter_mode(pd_port, pd_event, false);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-69 DFP VDM Mode Exit State Diagram
+ */
+
+void pe_dfp_vdm_mode_exit_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_vdm_exit_mode(pd_port, TCPC_TX_SOP,
+                             pd_port->mode_svid, pd_port->mode_obj_pos);
+}
+
+void pe_dfp_vdm_mode_exit_acked_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_dfp_inform_exit_mode(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-70 DFP VDM Attention State Diagram
+ */
+
+void pe_dfp_vdm_attention_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_dfp_inform_attention(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_dr.c b/drivers/usb/pd/richtek/pd_policy_engine_dr.c
new file mode 100644 (file)
index 0000000..6efdeb4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for DR
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0]
+ * Figure 8-53 Dual-Role (Source) Get Source Capabilities diagram
+ * Figure 8-54 Dual-Role (Source) Give Sink Capabilities diagram
+ * Figure 8-55 Dual-Role (Sink) Get Sink Capabilities State Diagram
+ * Figure 8-56 Dual-Role (Sink) Give Source Capabilities State Diagram
+ */
+
+void pe_dr_src_get_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GET_SOURCE_CAP);
+}
+
+void pe_dr_src_get_source_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+       pd_dpm_dr_inform_source_cap(pd_port, pd_event);
+}
+
+void pe_dr_src_give_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_send_sink_caps(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_dr_snk_get_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GET_SINK_CAP);
+}
+
+void pe_dr_snk_get_sink_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+       pd_dpm_dr_inform_sink_cap(pd_port, pd_event);
+}
+
+void pe_dr_snk_give_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_send_source_caps(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_drs.c b/drivers/usb/pd/richtek/pd_policy_engine_drs.c
new file mode 100644 (file)
index 0000000..be01eb3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for DRS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-49: Type-C DFP to UFP Data Role Swap State Diagram
+ */
+
+void pe_drs_dfp_ufp_evaluate_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_UFP);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_drs_dfp_ufp_accept_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+void pe_drs_dfp_ufp_change_to_ufp_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_drs_change_role(pd_port, PD_ROLE_UFP);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_drs_dfp_ufp_send_dr_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_DR_SWAP);
+}
+
+void pe_drs_dfp_ufp_reject_dr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg_sec == PD_DPM_NAK_REJECT)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       else
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+}
+
+/*
+ * [PD2.0] Figure 8-50: Type-C UFP to DFP Data Role Swap State Diagram
+ */
+
+void pe_drs_ufp_dfp_evaluate_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_DFP);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_drs_ufp_dfp_accept_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+void pe_drs_ufp_dfp_change_to_dfp_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_drs_change_role(pd_port, PD_ROLE_DFP);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_drs_ufp_dfp_send_dr_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_DR_SWAP);
+}
+
+void pe_drs_ufp_dfp_reject_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg_sec == PD_DPM_NAK_REJECT)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       else
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_prs.c b/drivers/usb/pd/richtek/pd_policy_engine_prs.c
new file mode 100644 (file)
index 0000000..fad45a3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for PRS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-51:
+ *      Dual-Role Port in Source to Sink Power Role Swap State Diagram
+ */
+
+void pe_prs_src_snk_evaluate_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SINK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_prs_src_snk_accept_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_notify_pe_execute_pr_swap(pd_port, true);
+
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+void pe_prs_src_snk_transition_to_off_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_lock_msg_output(pd_port);    /* for tSRCTransition */
+       pd_notify_pe_execute_pr_swap(pd_port, true);
+
+       pd_enable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_prs_src_snk_assert_rd_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_prs_change_role(pd_port, PD_ROLE_SINK);
+}
+
+void pe_prs_src_snk_wait_source_on_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PS_RDY);
+}
+
+void pe_prs_src_snk_wait_source_on_exit(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_PS_SOURCE_ON);
+}
+
+void pe_prs_src_snk_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PR_SWAP);
+}
+
+void pe_prs_src_snk_reject_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg_sec == PD_DPM_NAK_REJECT)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       else
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+}
+
+/*
+ * [PD2.0] Figure 8-52:
+ *      Dual-role Port in Sink to Source Power Role Swap State Diagram
+ */
+
+void pe_prs_snk_src_evaluate_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SOURCE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_prs_snk_src_accept_pr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_notify_pe_execute_pr_swap(pd_port, true);
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+void pe_prs_snk_src_transition_to_off_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       /*
+        * Sink should call pd_notify_pe_execute_pr_swap before this state,
+        * because source may turn off power & change CC before we got
+        * GoodCRC or Accept.
+        */
+
+       pd_port->during_swap = true;
+       pd_enable_timer(pd_port, PD_TIMER_PS_SOURCE_OFF);
+       pd_dpm_prs_turn_off_power_sink(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_prs_snk_src_transition_to_off_exit(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_PS_SOURCE_OFF);
+}
+
+void pe_prs_snk_src_assert_rp_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_prs_change_role(pd_port, PD_ROLE_SOURCE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_prs_snk_src_source_on_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_prs_enable_power_source(pd_port, true);
+}
+
+void pe_prs_snk_src_source_on_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+/* Do it in process_event after source_on */
+/* pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PS_RDY); */
+}
+
+void pe_prs_snk_src_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_notify_pe_execute_pr_swap(pd_port, false);
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PR_SWAP);
+}
+
+void pe_prs_snk_src_reject_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg_sec == PD_DPM_NAK_REJECT)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       else
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_snk.c b/drivers/usb/pd/richtek/pd_policy_engine_snk.c
new file mode 100644 (file)
index 0000000..27f6034
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for SNK
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-39 Sink Port state diagram
+ */
+
+void pe_snk_startup_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u8 rx_cap = PD_RX_CAP_PE_STARTUP;
+
+       pd_port->state_machine = PE_STATE_MACHINE_SINK;
+       pd_reset_protocol_layer(pd_port);
+
+       switch (pd_event->event_type) {
+       case PD_EVT_HW_MSG:     /* CC attached */
+               pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
+               break;
+
+       case PD_EVT_PE_MSG: /* From Hard-Reset */
+               pd_enable_vbus_valid_detection(pd_port, false);
+               break;
+
+       case PD_EVT_CTRL_MSG: /* From PR-SWAP (Received PS_RDY) */
+               /* If we reset rx_cap in here, */
+               /* maybe can't meet tSwapSink (Check it later) */
+               if (!pd_dpm_check_vbus_valid(pd_port)) {
+                       PE_INFO("rx_cap_on\r\n");
+                       rx_cap = PD_RX_CAP_PE_SEND_WAIT_CAP;
+               }
+
+               pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
+               pd_free_pd_event(pd_port, pd_event);
+               break;
+       }
+
+       pd_set_rx_enable(pd_port, rx_cap);
+}
+
+void pe_snk_discovery_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#ifdef CONFIG_USB_PD_FAST_RESP_TYPEC_SRC
+               pd_disable_timer(pd_port, PD_TIMER_SRC_RECOVER);
+#endif /* CONFIG_USB_PD_FAST_RESP_TYPEC_SRC */
+       pd_enable_vbus_valid_detection(pd_port, true);
+}
+
+void pe_snk_wait_for_capabilities_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_notify_pe_hard_reset_completed(pd_port);
+
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
+       pd_enable_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_wait_for_capabilities_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
+}
+
+void pe_snk_evaluate_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       /* Stop NoResponseTimer and reset HardResetCounter to zero */
+
+       pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+
+       pd_port->hard_reset_counter = 0;
+       pd_port->pd_connected = 1;
+       pd_port->pd_prev_connected = 1;
+       pd_port->explicit_contract = false;
+
+       pd_dpm_snk_evaluate_caps(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_select_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg == PD_DPM_NOTIFIED) {
+               PE_DBG("SelectCap%d, rdo:0x%08x\r\n",
+                      pd_event->msg_sec, pd_port->last_rdo);
+       } else {
+               /* new request, for debug only */
+               /* pd_dpm_sink_vbus(pd_port, false); */
+               PE_DBG("NewReq, rdo:0x%08x\r\n", pd_port->last_rdo);
+       }
+
+       pd_lock_msg_output(pd_port);    /* SenderResponse */
+       pd_send_data_msg(pd_port,
+                        TCPC_TX_SOP, PD_DATA_REQUEST, 1, &pd_port->last_rdo);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_select_capability_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+
+       if (pd_event_msg_match(pd_event,
+                              PD_EVT_CTRL_MSG, PD_CTRL_ACCEPT))
+               pd_port->remote_selected_cap = RDO_POS(pd_port->last_rdo);
+
+       /* Waiting for Hard-Reset Done */
+       if (!pd_event_msg_match(pd_event,
+                               PD_EVT_TIMER_MSG, PD_TIMER_SENDER_RESPONSE))
+               pd_unlock_msg_output(pd_port);
+}
+
+void pe_snk_transition_sink_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_timer(pd_port, PD_TIMER_PS_TRANSITION);
+
+       if (pd_event->msg == PD_CTRL_GOTO_MIN) {
+               if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK) {
+                       pd_port->request_i_new = pd_port->request_i_op;
+                       pd_dpm_snk_transition_power(pd_port, pd_event);
+               }
+       }
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_transition_sink_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, PD_CTRL_PS_RDY))
+               pd_dpm_snk_transition_power(pd_port, pd_event);
+
+       pd_disable_timer(pd_port, PD_TIMER_PS_TRANSITION);
+}
+
+void pe_snk_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, PD_CTRL_WAIT))
+               pd_enable_timer(pd_port, PD_TIMER_SINK_REQUEST);
+
+       pd_port->state_machine = PE_STATE_MACHINE_SINK;
+       pe_power_ready_entry(pd_port, pd_event);
+}
+
+void pe_snk_hard_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_hard_reset(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_transition_to_default_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reset_local_hw(pd_port);
+       pd_dpm_snk_hard_reset(pd_port, pd_event);
+}
+
+void pe_snk_transition_to_default_exit(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+
+#ifdef CONFIG_USB_PD_FAST_RESP_TYPEC_SRC
+       if (!pd_port->pd_prev_connected)
+               pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER);
+#endif /* CONFIG_USB_PD_FAST_RESP_TYPEC_SRC */
+}
+
+void pe_snk_give_sink_cap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_send_sink_caps(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_get_source_cap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GET_SOURCE_CAP);
+}
+
+void pe_snk_send_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_soft_reset(pd_port, PE_STATE_MACHINE_SINK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_snk_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_handle_soft_reset(pd_port, PE_STATE_MACHINE_SINK);
+       pd_free_pd_event(pd_port, pd_event);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_src.c b/drivers/usb/pd/richtek/pd_policy_engine_src.c
new file mode 100644 (file)
index 0000000..7b846dc
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for SRC
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-38 Source Port Policy Engine state diagram
+ */
+
+void pe_src_startup_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->state_machine = PE_STATE_MACHINE_SOURCE;
+
+       pd_port->cap_counter = 0;
+       pd_port->request_i = -1;
+       pd_port->request_v = TCPC_VBUS_SOURCE_5V;
+
+       pd_reset_protocol_layer(pd_port);
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_STARTUP);
+
+       switch (pd_event->event_type) {
+       case PD_EVT_HW_MSG:     /* CC attached */
+               pd_enable_vbus_valid_detection(pd_port, true);
+               break;
+
+       case PD_EVT_PE_MSG: /* From Hard-Reset */
+               pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
+               break;
+
+       case PD_EVT_CTRL_MSG: /* From PR-SWAP (Received PS_RDY) */
+               pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
+               break;
+       }
+}
+
+void pe_src_discovery_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       /* MessageID Should be 0 for First SourceCap (Ellisys)... */
+
+       /* The SourceCapabilitiesTimer continues to run during the states
+        * defined in Source Startup Structured VDM Discover Identity State
+        * Diagram
+        */
+
+       pd_port->msg_id_tx[TCPC_TX_SOP] = 0;
+       pd_port->pd_connected = false;
+
+       pd_enable_timer(pd_port, PD_TIMER_SOURCE_CAPABILITY);
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       if (pd_is_auto_discover_cable_id(pd_port)) {
+               pd_port->msg_id_tx[TCPC_TX_SOP_PRIME] = 0;
+               pd_enable_timer(pd_port, PD_TIMER_DISCOVER_ID);
+       }
+#endif
+}
+
+void pe_src_send_capabilities_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
+
+       pd_dpm_send_source_caps(pd_port);
+       pd_port->cap_counter++;
+
+       pd_free_pd_event(pd_port, pd_event);    /* soft-reset */
+}
+
+void pe_src_send_capabilities_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+}
+
+void pe_src_negotiate_capabilities_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->pd_connected = true;
+       pd_port->pd_prev_connected = true;
+
+       pd_dpm_src_evaluate_request(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_transition_supply_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg == PD_DPM_PD_REQUEST) {
+               pd_port->request_i_new = pd_port->request_i_op;
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GOTO_MIN);
+       } else {
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+       }
+       pd_enable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
+}
+
+void pe_src_transition_supply_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
+}
+
+void pe_src_transition_supply2_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PS_RDY);
+}
+
+void pe_src_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->state_machine = PE_STATE_MACHINE_SOURCE;
+       pd_notify_pe_src_explicit_contract(pd_port);
+       pe_power_ready_entry(pd_port, pd_event);
+}
+
+void pe_src_disabled_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISABLE);
+       pd_update_connect_state(pd_port, PD_CONNECT_TYPEC_ONLY);
+}
+
+void pe_src_capability_response_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg_sec) {
+       case PD_DPM_NAK_REJECT_INVALID:
+               pd_port->invalid_contract = true;
+       case PD_DPM_NAK_REJECT:
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+               break;
+
+       case PD_DPM_NAK_WAIT:
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+               break;
+       }
+}
+
+void pe_src_hard_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_hard_reset(pd_port);
+
+       pd_free_pd_event(pd_port, pd_event);
+       pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
+}
+
+void pe_src_hard_reset_received_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
+}
+
+void pe_src_transition_to_default_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reset_local_hw(pd_port);
+       pd_dpm_src_hard_reset(pd_port);
+}
+
+void pe_src_transition_to_default_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_enable_vconn(pd_port, true);
+       pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+}
+
+void pe_src_give_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_send_source_caps(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_get_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_GET_SINK_CAP);
+}
+
+void pe_src_get_sink_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+       pd_dpm_dr_inform_sink_cap(pd_port, pd_event);
+}
+
+void pe_src_wait_new_capabilities_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       /* Wait for new Source Capabilities */
+}
+
+void pe_src_send_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_soft_reset(pd_port, PE_STATE_MACHINE_SOURCE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_handle_soft_reset(pd_port, PE_STATE_MACHINE_SOURCE);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_ping_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       /* TODO: Send Ping Message */
+}
+
+/*
+ * [PD2.0] Figure 8-81
+ Source Startup Structured VDM Discover Identity State Diagram (TODO)
+ */
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+
+void pe_src_vdm_identity_request_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE);
+
+       pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
+
+       pd_port->discover_id_counter++;
+       pd_enable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_vdm_identity_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_CABLE_ID;
+
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_src_inform_cable_vdo(pd_port, pd_event);
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_src_vdm_identity_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VDM_RESPONSE);
+       pd_dpm_src_inform_cable_vdo(pd_port, pd_event);
+
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_ufp.c b/drivers/usb/pd/richtek/pd_policy_engine_ufp.c
new file mode 100644 (file)
index 0000000..3741c38
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for UFP
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-58 UFP Structured VDM Discover Identity State Diagram
+ */
+
+void pe_ufp_vdm_get_identity_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_request_id_info(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_send_identity_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_response_id(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_get_identity_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_NAK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-59 UFP Structured VDM Discover SVIDs State Diagram
+ */
+
+void pe_ufp_vdm_get_svids_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_request_svid_info(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_send_svids_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_response_svids(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_get_svids_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_NAK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-60 UFP Structured VDM Discover Modes State Diagram
+ */
+
+void pe_ufp_vdm_get_modes_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_request_mode_info(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_send_modes_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_response_modes(pd_port, pd_event);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_get_modes_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_NAK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-61 UFP Structured VDM Enter Mode State Diagram
+ */
+
+void pe_ufp_vdm_evaluate_mode_entry_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_request_enter_mode(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_mode_entry_ack_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_ACK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_mode_entry_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_NAK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-62 UFP Structured VDM Exit Mode State Diagram
+ */
+
+void pe_ufp_vdm_mode_exit_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_ufp_request_exit_mode(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_mode_exit_ack_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_ACK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_ufp_vdm_mode_exit_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_reply_svdm_request_simply(pd_port, pd_event, CMDT_RSP_NAK);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+/*
+ * [PD2.0] Figure 8-63 UFP VDM Attention State Diagram
+ */
+
+void pe_ufp_vdm_attention_request_entry(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->mode_svid) {
+       default:
+               pd_send_vdm_attention(pd_port,
+                                     TCPC_TX_SOP, pd_port->mode_svid,
+                                         pd_port->mode_obj_pos);
+               break;
+       }
+
+       pd_free_pd_event(pd_port, pd_event);
+}
diff --git a/drivers/usb/pd/richtek/pd_policy_engine_vcs.c b/drivers/usb/pd/richtek/pd_policy_engine_vcs.c
new file mode 100644 (file)
index 0000000..d3667ca
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Policy Engine for VCS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+/*
+ * [PD2.0] Figure 8-57 VCONN Swap State Diagram
+ */
+
+void pe_vcs_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_VCONN_SWAP);
+}
+
+void pe_vcs_evaluate_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_vcs_evaluate_swap(pd_port);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_vcs_accept_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+}
+
+void pe_vcs_reject_vconn_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->msg_sec == PD_DPM_NAK_REJECT)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       else
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_WAIT);
+}
+
+void pe_vcs_wait_for_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_enable_timer(pd_port, PD_TIMER_VCONN_ON);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_vcs_wait_for_vconn_exit(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_disable_timer(pd_port, PD_TIMER_VCONN_ON);
+}
+
+void pe_vcs_turn_off_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_vcs_enable_vconn(pd_port, false);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_vcs_turn_on_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_dpm_vcs_enable_vconn(pd_port, true);
+       pd_free_pd_event(pd_port, pd_event);
+}
+
+void pe_vcs_send_ps_rdy_entry(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PS_RDY);
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt.c b/drivers/usb/pd/richtek/pd_process_evt.c
new file mode 100644 (file)
index 0000000..e5a2fe0
--- /dev/null
@@ -0,0 +1,883 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+
+/*
+ * [BLOCK] print event
+ */
+
+#if PE_EVENT_DBG_ENABLE
+static const char * const pd_ctrl_msg_name[] = {
+       "ctrl0",
+       "good_crc",
+       "goto_min",
+       "accept",
+       "reject",
+       "ping",
+       "ps_rdy",
+       "get_src_cap",
+       "get_snk_cap",
+       "dr_swap",
+       "pr_swap",
+       "vs_swap",
+       "wait",
+       "soft_reset",
+       "ctrlE",
+       "ctrlF",
+};
+
+static inline void print_ctrl_msg_event(u8 msg)
+{
+       if (msg < PD_CTRL_MSG_NR)
+               PE_EVT_INFO("%s\r\n", pd_ctrl_msg_name[msg]);
+}
+
+static const char * const pd_data_msg_name[] = {
+       "data0",
+       "src_cap",
+       "request",
+       "bist",
+       "sink_cap",
+       "data5",
+       "data6",
+       "data7",
+       "data8",
+       "data9",
+       "dataA",
+       "dataB",
+       "dataC",
+       "dataD",
+       "dataE",
+       "vdm",
+};
+
+static inline void print_data_msg_event(u8 msg)
+{
+       if (msg < PD_DATA_MSG_NR)
+               PE_EVT_INFO("%s\r\n", pd_data_msg_name[msg]);
+}
+
+static const char *const pd_hw_msg_name[] = {
+       "Detached",
+       "Attached",
+       "hard_reset",
+       "vbus_high",
+       "vbus_low",
+       "vbus_0v",
+       "vbus_stable",
+       "tx_err",
+       "retry_vdm",
+};
+
+static inline void print_hw_msg_event(u8 msg)
+{
+       if (msg < PD_HW_MSG_NR)
+               PE_EVT_INFO("%s\r\n", pd_hw_msg_name[msg]);
+}
+
+static const char *const pd_pe_msg_name[] = {
+       "reset_prl_done",
+       "pr_at_dft",
+       "hard_reset_done",
+       "pe_idle",
+};
+
+static inline void print_pe_msg_event(u8 msg)
+{
+       if (msg < PD_PE_MSG_NR)
+               PE_EVT_INFO("%s\r\n", pd_pe_msg_name[msg]);
+}
+
+static const char * const pd_dpm_msg_name[] = {
+       "ack",
+       "nak",
+
+       "pd_req",
+       "vdm_req",
+       "cable_req",
+
+       "cap_change",
+       "recover",
+};
+
+static inline void print_dpm_msg_event(u8 msg)
+{
+       if (msg < PD_DPM_MSG_NR)
+               PE_EVT_INFO("dpm_%s\r\n", pd_dpm_msg_name[msg]);
+}
+
+const char *const pd_dpm_pd_request_name[] = {
+       "pr_swap",
+       "dr_swap",
+       "vs_swap",
+       "gotomin",
+       "softreset",
+       "hardreset",
+       "get_src_cap",
+       "get_snk_cap",
+       "request",
+       "bist_cm2",
+};
+
+static inline void print_dpm_pd_request(u8 msg)
+{
+       if (msg < PD_DPM_PD_REQUEST_NR)
+               PE_EVT_INFO("dpm_pd_req(%s)\r\n", pd_dpm_pd_request_name[msg]);
+}
+#endif
+
+static inline void print_event(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#if PE_EVENT_DBG_ENABLE
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               print_ctrl_msg_event(pd_event->msg);
+               break;
+
+       case PD_EVT_DATA_MSG:
+               print_data_msg_event(pd_event->msg);
+               break;
+
+       case PD_EVT_DPM_MSG:
+               if (pd_event->msg == PD_DPM_PD_REQUEST)
+                       print_dpm_pd_request(pd_event->msg_sec);
+               else
+                       print_dpm_msg_event(pd_event->msg);
+               break;
+
+       case PD_EVT_HW_MSG:
+               print_hw_msg_event(pd_event->msg);
+               break;
+
+       case PD_EVT_PE_MSG:
+               print_pe_msg_event(pd_event->msg);
+               break;
+
+       case PD_EVT_TIMER_MSG:
+               PE_EVT_INFO("timer\r\n");
+               break;
+       }
+#endif
+}
+
+bool pd_make_pe_state_transit(pd_port_t *pd_port,
+                             u8 curr_state,
+                                 const pe_state_reaction_t *state_reaction)
+{
+       int i;
+       const pe_state_transition_t *state_transition =
+               state_reaction->state_transition;
+
+       for (i = 0; i < state_reaction->nr_transition; i++) {
+               if (state_transition[i].curr_state == curr_state) {
+                       PE_TRANSIT_STATE(pd_port,
+                                        state_transition[i].next_state);
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+bool pd_make_pe_state_transit_virt(pd_port_t *pd_port,
+                                  u8 curr_state,
+                                  const pe_state_reaction_t *state_reaction)
+{
+       bool ret = pd_make_pe_state_transit(
+                       pd_port, curr_state, state_reaction);
+
+       if (ret) {
+               switch (pd_port->pe_state_next) {
+               case PE_VIRT_READY:
+                       PE_TRANSIT_READY_STATE(pd_port);
+                       break;
+
+               case PE_VIRT_HARD_RESET:
+                       PE_TRANSIT_HARD_RESET_STATE(pd_port);
+                       break;
+               }
+       }
+       return ret;
+}
+
+bool pd_make_pe_state_transit_force(pd_port_t *pd_port,
+                                   u8 curr_state, u8 force_state,
+       const pe_state_reaction_t *state_reaction)
+{
+       bool ret = pd_make_pe_state_transit(
+                       pd_port, curr_state, state_reaction);
+
+       if (ret)
+               return ret;
+
+       PE_TRANSIT_STATE(pd_port, force_state);
+       return true;
+}
+
+bool pd_process_protocol_error(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool power_change = false;
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       u8 event_type = pd_event->event_type;
+       u8 msg_id = PD_HEADER_ID(pd_msg->msg_hdr);
+       u8 msg_type = PD_HEADER_TYPE(pd_msg->msg_hdr);
+
+       switch (pd_port->pe_state_curr) {
+       case PE_SNK_TRANSITION_SINK:
+       case PE_SRC_TRANSITION_SUPPLY:
+       case PE_SRC_TRANSITION_SUPPLY2:
+               power_change = true;
+       case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
+               if (pd_event_msg_match(pd_event,
+                                      PD_EVT_CTRL_MSG, PD_CTRL_PING)) {
+                       PE_DBG("Igrone Ping\r\n");
+                       return false;
+               }
+               break;
+
+       case PE_SRC_SOFT_RESET:
+       case PE_SRC_SEND_SOFT_RESET:
+       case PE_SNK_SOFT_RESET:
+       case PE_SNK_SEND_SOFT_RESET:
+       case PE_SNK_READY:
+       case PE_SRC_READY:
+       case PE_BIST_TEST_DATA:
+               PE_DBG("Igrone Unknown Event\r\n");
+               return false;
+       };
+
+       PE_INFO("PRL_ERR: %d-%d-%d\r\n", event_type, msg_type, msg_id);
+
+       if (pd_port->during_swap)
+               PE_TRANSIT_HARD_RESET_STATE(pd_port);
+       else if (power_change)
+               PE_TRANSIT_HARD_RESET_STATE(pd_port);
+       else
+               PE_TRANSIT_SEND_SOFT_RESET_STATE(pd_port);
+       return true;
+}
+
+bool pd_process_data_msg_bist(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       if (pd_port->request_v > 5000) {
+               PE_INFO("bist_not_vsafe5v\r\n");
+               return false;
+       }
+
+       switch (BDO_MODE(pd_event->pd_msg->payload[0])) {
+       case BDO_MODE_TEST_DATA:
+               PE_DBG("bist_test\r\n");
+               PE_TRANSIT_STATE(pd_port, PE_BIST_TEST_DATA);
+               pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_TEST_DATA);
+               return true;
+
+       case BDO_MODE_CARRIER2:
+               PE_DBG("bist_cm2\r\n");
+               PE_TRANSIT_STATE(pd_port, PE_BIST_CARRIER_MODE_2);
+               pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
+               return true;
+
+       default:
+       case BDO_MODE_RECV:
+       case BDO_MODE_TRANSMIT:
+       case BDO_MODE_COUNTERS:
+       case BDO_MODE_CARRIER0:
+       case BDO_MODE_CARRIER1:
+       case BDO_MODE_CARRIER3:
+       case BDO_MODE_EYE:
+               PE_DBG("Unsupport BIST\r\n");
+               pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
+               return false;
+       }
+
+       return false;
+}
+
+/* DRP (Data Role Swap) */
+
+bool pd_process_ctrl_msg_dr_swap(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool reject;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       reject = !(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA);
+
+       if (!reject) {
+               if (pd_port->data_role == PD_ROLE_DFP)
+                       reject = pd_port->dpm_caps &
+                                       DPM_CAP_DR_SWAP_REJECT_AS_UFP;
+               else
+                       reject = pd_port->dpm_caps &
+                                       DPM_CAP_DR_SWAP_REJECT_AS_DFP;
+       }
+
+       if (reject) {
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+               return false;
+       }
+       if (pd_port->modal_operation) {
+               PE_TRANSIT_HARD_RESET_STATE(pd_port);
+       } else {
+               pd_port->during_swap = false;
+               pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
+
+               PE_TRANSIT_DATA_STATE(pd_port,
+                                     PE_DRS_UFP_DFP_EVALUATE_DR_SWAP,
+                                         PE_DRS_DFP_UFP_EVALUATE_DR_SWAP);
+       }
+       return true;
+}
+
+bool pd_process_dpm_msg_dr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA))
+               return false;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       pd_port->during_swap = false;
+       pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
+
+       PE_TRANSIT_DATA_STATE(pd_port,
+                             PE_DRS_UFP_DFP_SEND_DR_SWAP,
+                                 PE_DRS_DFP_UFP_SEND_DR_SWAP);
+
+       return true;
+}
+
+/* DRP (Power Role Swap) */
+
+bool pd_process_ctrl_msg_pr_swap(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool reject;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       reject = !(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER);
+
+       if (!reject) {
+               if (pd_port->power_role == PD_ROLE_SOURCE)
+                       reject = pd_port->dpm_caps &
+                                       DPM_CAP_PR_SWAP_REJECT_AS_SNK;
+               else
+                       reject = pd_port->dpm_caps &
+                                       DPM_CAP_PR_SWAP_REJECT_AS_SRC;
+       }
+
+       if (reject) {
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+               return false;
+       }
+       pd_port->during_swap = false;
+       pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
+
+       PE_TRANSIT_POWER_STATE(pd_port,
+                              PE_PRS_SNK_SRC_EVALUATE_PR_SWAP,
+                                       PE_PRS_SRC_SNK_EVALUATE_PR_SWAP);
+
+       return true;
+}
+
+bool pd_process_dpm_msg_pr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER))
+               return false;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       pd_port->during_swap = false;
+       pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
+
+       PE_TRANSIT_POWER_STATE(pd_port,
+                              PE_PRS_SNK_SRC_SEND_SWAP,
+                                       PE_PRS_SRC_SNK_SEND_SWAP);
+       return true;
+}
+
+/* DRP (Vconn Swap) */
+
+bool pd_process_ctrl_msg_vconn_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)) {
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+               return false;
+       }
+       pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
+       PE_TRANSIT_STATE(pd_port, PE_VCS_EVALUATE_SWAP);
+       return true;
+}
+
+bool pd_process_dpm_msg_vconn_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY))
+               return false;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
+       PE_TRANSIT_STATE(pd_port, PE_VCS_SEND_SWAP);
+       return true;
+}
+
+bool pd_process_recv_hard_reset(
+               pd_port_t *pd_port, pd_event_t *pd_event, u8 hreset_state)
+{
+       PE_TRANSIT_STATE(pd_port, hreset_state);
+       return true;
+}
+
+bool pd_process_dpm_msg_pw_request(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr != PE_SNK_READY)
+               return false;
+
+       PE_TRANSIT_STATE(pd_port, PE_SNK_SELECT_CAPABILITY);
+       return true;
+}
+
+bool pd_process_dpm_msg_bist_cm2(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u32 bist = BDO_MODE_CARRIER2;
+
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       pd_send_data_msg(pd_port, TCPC_TX_SOP, PD_DATA_BIST, 1, &bist);
+       return false;
+}
+
+bool pd_process_dpm_msg_gotomin(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr != PE_SRC_READY)
+               return false;
+
+       if (!(pd_port->dpm_flags & DPM_CAP_LOCAL_GIVE_BACK))
+               return false;
+
+       PE_TRANSIT_STATE(pd_port, PE_SRC_TRANSITION_SUPPLY);
+       return true;
+}
+
+bool pd_process_dpm_msg_softreset(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       PE_TRANSIT_SEND_SOFT_RESET_STATE(pd_port);
+       return true;
+}
+
+bool pd_process_dpm_msg_hardreset(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!pd_check_pe_state_ready(pd_port))
+               return false;
+
+       PE_TRANSIT_HARD_RESET_STATE(pd_port);
+       return true;
+}
+
+bool pd_process_dpm_msg_get_source_cap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SNK_READY:
+               PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP);
+               return true;
+
+       case PE_SRC_READY:
+               if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
+                       PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GET_SOURCE_CAP);
+                       return true;
+               }
+               break;
+       }
+
+       return false;
+}
+
+bool pd_process_dpm_msg_get_sink_cap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SRC_READY:
+               PE_TRANSIT_STATE(pd_port, PE_SRC_GET_SINK_CAP);
+               return true;
+
+       case PE_SNK_READY:
+               if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
+                       PE_TRANSIT_STATE(pd_port, PE_DR_SNK_GET_SINK_CAP);
+                       return true;
+               }
+               break;
+       }
+
+       return false;
+}
+
+bool pd_process_event_dpm_pd_request(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg_sec) {
+       case PD_DPM_PD_REQUEST_PR_SWAP:
+               ret = pd_process_dpm_msg_pr_swap(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_DR_SWAP:
+               ret = pd_process_dpm_msg_dr_swap(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_VCONN_SWAP:
+               ret = pd_process_dpm_msg_vconn_swap(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_GOTOMIN:
+               ret = pd_process_dpm_msg_gotomin(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_SOFTRESET:
+               ret = pd_process_dpm_msg_softreset(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_HARDRESET:
+               ret = pd_process_dpm_msg_hardreset(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_GET_SOURCE_CAP:
+               ret = pd_process_dpm_msg_get_source_cap(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_GET_SINK_CAP:
+               ret = pd_process_dpm_msg_get_sink_cap(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_PW_REQUEST:
+               ret = pd_process_dpm_msg_pw_request(pd_port, pd_event);
+               break;
+
+       case PD_DPM_PD_REQUEST_BIST_CM2:
+               ret = pd_process_dpm_msg_bist_cm2(pd_port, pd_event);
+               break;
+
+       default:
+               PE_DBG("Unknown PD_Request\r\n");
+               return false;
+       }
+
+       if (!ret)
+               /* TODO: Notify DPM, Policy Engine Reject this request ...  */
+               PE_DBG("Reject DPM PD Request\r\n");
+       return ret;
+}
+
+/* @ true : valid message */
+/* @ false : invalid message, pe should drop the message */
+
+static inline bool pe_is_valid_pd_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       u8 event_type = pd_event->event_type;
+       u8 sop_type = pd_msg->frame_type;
+       u8 msg_id = PD_HEADER_ID(pd_msg->msg_hdr);
+       u8 msg_type = PD_HEADER_TYPE(pd_msg->msg_hdr);
+
+       if (pd_port->pe_state_curr == PE_BIST_TEST_DATA)
+               return false;
+
+       if (event_type == PD_EVT_CTRL_MSG) {
+               switch (msg_type) {
+               /* SofReset always has a MessageID value of zero */
+               case PD_CTRL_SOFT_RESET:
+                       if (msg_id != 0) {
+                               PE_INFO("Repeat soft_reset\r\n");
+                               return false;
+                       }
+
+                       return true;
+               case PD_CTRL_GOOD_CRC:
+                       PE_DBG("Discard_CRC\r\n");
+                       return true;
+               }
+       }
+
+       if ((pd_port->msg_id_rx_init[sop_type]) &&
+           (pd_port->msg_id_rx[sop_type] == msg_id)) {
+               PE_INFO("Repeat msg: %c:%d:%d\r\n",
+                       (pd_event->event_type == PD_EVT_CTRL_MSG) ? 'C' : 'D',
+                       pd_event->msg, msg_id);
+               return false;
+       }
+
+       pd_port->msg_id_rx[sop_type] = msg_id;
+       pd_port->msg_id_rx_init[sop_type] = true;
+
+       return true;
+}
+
+static inline bool pe_is_valid_pd_msg_role(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = true;
+       u8 msg_pr, msg_dr;
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       if (!pd_msg)    /* Good-CRC */
+               return true;
+
+       if (pd_msg->frame_type != TCPC_TX_SOP)
+               return true;
+
+       msg_pr = PD_HEADER_PR(pd_msg->msg_hdr);
+       msg_dr = PD_HEADER_DR(pd_msg->msg_hdr);
+
+       /*
+        * The Port Power Role field of a received Message shall not be verified
+        * by the receiver and no error recovery action shall be
+        * taken if it is incorrect.
+        */
+
+       if (msg_pr == pd_port->power_role)
+               PE_DBG("Wrong PR:%d\r\n", msg_pr);
+
+       /*
+        * Should a Type-C Port receive a Message with the Port Data Role field
+        * set to the same Data Role as its current Data Role,
+        * except for the GoodCRC Message,
+        * Type-C error recovery actions as defined
+        * in [USBType-C 1.0] shall be performed.
+        */
+
+       if (msg_dr == pd_port->data_role)
+               PE_INFO("Wrong DR:%d\r\n", msg_dr);
+
+       return ret;
+}
+
+static inline bool pe_translate_pd_msg_event(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_msg_t *pd_msg;
+
+       if (pd_event->event_type != PD_EVT_PD_MSG)
+               return true;
+
+       pd_msg = pd_event->pd_msg;
+
+       if (PD_HEADER_CNT(pd_msg->msg_hdr))
+               pd_event->event_type = PD_EVT_DATA_MSG;
+       else
+               pd_event->event_type = PD_EVT_CTRL_MSG;
+
+       pd_event->msg = PD_HEADER_TYPE(pd_msg->msg_hdr);
+
+       return pe_is_valid_pd_msg(pd_port, pd_event);
+}
+
+static inline bool pe_exit_idle_state(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       pd_port->custom_dbgacc = false;
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       switch (pd_event->msg_sec) {
+       case TYPEC_ATTACHED_SNK:
+               pd_init_role(pd_port,
+                            PD_ROLE_SINK, PD_ROLE_UFP, PD_ROLE_VCONN_OFF);
+               break;
+
+       case TYPEC_ATTACHED_SRC:
+               pd_init_role(pd_port,
+                            PD_ROLE_SOURCE, PD_ROLE_DFP, PD_ROLE_VCONN_ON);
+               break;
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       case TYPEC_ATTACHED_DBGACC_SNK:
+               pd_port->custom_dbgacc = true;
+               pd_init_role(pd_port,
+                            PD_ROLE_SINK, PD_ROLE_UFP, PD_ROLE_VCONN_OFF);
+               break;
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       default:
+               return false;
+       }
+
+       pd_port->cap_counter = 0;
+       pd_port->discover_id_counter = 0;
+       pd_port->hard_reset_counter = 0;
+       pd_port->get_snk_cap_count = 0;
+       pd_port->get_src_cap_count = 0;
+
+       pd_port->pe_ready = 0;
+       pd_port->pd_connected = 0;
+       pd_port->pd_prev_connected = 0;
+       pd_port->reset_vdm_state = 0;
+       pd_port->power_cable_present = 0;
+
+       pd_port->explicit_contract = false;
+       pd_port->invalid_contract = false;
+
+       pd_port->modal_operation = false;
+       pd_port->during_swap = false;
+       pd_port->dpm_ack_immediately = false;
+
+       pd_port->remote_src_cap.nr = 0;
+       pd_port->remote_snk_cap.nr = 0;
+
+       memset(pd_port->cable_vdos, 0, sizeof(uint32_t) * VDO_MAX_SIZE);
+
+       pd_dpm_notify_pe_startup(pd_port);
+       return true;
+}
+
+static inline bool pe_is_trap_in_idle_state(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool trap = true;
+
+       switch (pd_port->pe_state_curr) {
+       case PE_IDLE1:
+               if (pd_event_msg_match(pd_event, PD_EVT_PE_MSG, PD_PE_IDLE))
+                       return false;
+               pd_try_put_pe_idle_event(pd_port);
+       case PE_IDLE2:
+               break;
+
+       default:
+               return false;
+       }
+
+       if (pd_event->event_type == PD_EVT_HW_MSG) {
+               switch (pd_event->msg) {
+               case PD_HW_CC_ATTACHED:
+                       trap = false;
+                       break;
+
+               case PD_HW_CC_DETACHED:
+                       pd_notify_pe_idle(pd_port);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (!trap)
+               trap = !pe_exit_idle_state(pd_port, pd_event);
+       return trap;
+}
+
+bool pd_process_event(pd_port_t *pd_port, pd_event_t *pd_event, bool vdm_evt)
+{
+       bool ret = false;
+
+       if (pe_is_trap_in_idle_state(pd_port, pd_event)) {
+               PE_DBG("Trap in idle state, Igrone All MSG\r\n");
+               return false;
+       }
+
+       if (!pe_translate_pd_msg_event(pd_port, pd_event))
+               return false;
+
+#if PE_EVT_INFO_VDM_DIS
+       if (!vdm_evt)
+#endif
+               print_event(pd_port, pd_event);
+
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+       case PD_EVT_DATA_MSG:
+               if (!pe_is_valid_pd_msg_role(pd_port, pd_event)) {
+                       PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+                       return true;
+               }
+               break;
+       }
+
+       if (vdm_evt)
+               return pd_process_event_vdm(pd_port, pd_event);
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       if (pd_port->custom_dbgacc)
+               return pd_process_event_dbg(pd_port, pd_event);
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       if ((pd_event->event_type == PD_EVT_CTRL_MSG) &&
+           (pd_event->msg != PD_CTRL_GOOD_CRC) &&
+               (pd_event->pd_msg->frame_type != TCPC_TX_SOP)) {
+               PE_DBG("Igrone not SOP Ctrl Msg\r\n");
+               return false;
+       }
+
+       if (pd_event_msg_match(pd_event, PD_EVT_DPM_MSG, PD_DPM_PD_REQUEST))
+               return pd_process_event_dpm_pd_request(pd_port, pd_event);
+
+       switch (pd_port->state_machine) {
+       case PE_STATE_MACHINE_DR_SWAP:
+               ret = pd_process_event_drs(pd_port, pd_event);
+               break;
+       case PE_STATE_MACHINE_PR_SWAP:
+               ret = pd_process_event_prs(pd_port, pd_event);
+               break;
+       case PE_STATE_MACHINE_VCONN_SWAP:
+               ret = pd_process_event_vcs(pd_port, pd_event);
+               break;
+       }
+
+       if (ret)
+               return true;
+
+       if (pd_port->power_role == PD_ROLE_SINK)
+               ret = pd_process_event_snk(pd_port, pd_event);
+       else
+               ret = pd_process_event_src(pd_port, pd_event);
+
+       return ret;
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_dbg.c b/drivers/usb/pd/richtek/pd_process_evt_dbg.c
new file mode 100644 (file)
index 0000000..162a597
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For DBGACC
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_IDLE) = {
+       { PE_IDLE1, PE_IDLE2 },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_IDLE);
+
+bool pd_process_event_dbg(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_event->event_type == PD_EVT_HW_MSG) {
+               switch (pd_event->msg) {
+               case PD_HW_CC_DETACHED:
+                       PE_TRANSIT_STATE(pd_port, PE_IDLE1);
+                       return true;
+
+               case PD_HW_CC_ATTACHED:
+                       PE_TRANSIT_STATE(pd_port, PE_DBG_READY);
+                       return true;
+               }
+       }
+
+       if (pd_event_msg_match(pd_event, PD_EVT_PE_MSG, PD_PE_IDLE))
+               return PE_MAKE_STATE_TRANSIT(PD_PE_MSG_IDLE);
+
+       return false;
+}
+
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
diff --git a/drivers/usb/pd/richtek/pd_process_evt_drs.c b/drivers/usb/pd/richtek/pd_process_evt_drs.c
new file mode 100644 (file)
index 0000000..c5e6833
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For DRS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+
+/* PD Control MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
+       { PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP },
+       { PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_REJECT_WAIT) = {
+       { PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_VIRT_READY },
+       { PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_REJECT_WAIT);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_ACCEPT_DR_SWAP },
+       { PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_ACCEPT_DR_SWAP },
+       { PE_DRS_DFP_UFP_CHANGE_TO_UFP, PE_VIRT_READY },
+       { PE_DRS_UFP_DFP_CHANGE_TO_DFP, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+       { PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_REJECT_DR_SWAP },
+       { PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_REJECT_DR_SWAP },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SENDER_RESPONSE) = {
+       { PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_VIRT_READY },
+       { PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SENDER_RESPONSE);
+
+/*
+ * [BLOCK] Porcess PD Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_DRS_DFP_UFP_REJECT_DR_SWAP:
+       case PE_DRS_UFP_DFP_REJECT_DR_SWAP:
+               PE_TRANSIT_READY_STATE(pd_port);
+               return true;
+
+       case PE_DRS_DFP_UFP_ACCEPT_DR_SWAP:
+               PE_TRANSIT_STATE(pd_port, PE_DRS_DFP_UFP_CHANGE_TO_UFP);
+               return true;
+
+       case PE_DRS_UFP_DFP_ACCEPT_DR_SWAP:
+               PE_TRANSIT_STATE(pd_port, PE_DRS_UFP_DFP_CHANGE_TO_DFP);
+               return true;
+
+       case PE_DRS_DFP_UFP_SEND_DR_SWAP:
+       case PE_DRS_UFP_DFP_SEND_DR_SWAP:
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               return false;
+
+       default:
+               return false;
+       }
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       case PD_CTRL_ACCEPT:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
+
+       case PD_CTRL_WAIT:
+       case PD_CTRL_REJECT:
+               return PE_MAKE_STATE_TRANSIT_VIRT(PD_CTRL_MSG_REJECT_WAIT);
+
+       default:
+               return false;
+       }
+}
+
+/*
+ * [BLOCK] Porcess DPM MSG
+ */
+
+static inline bool pd_process_dpm_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_DPM_MSG_ACK);
+               break;
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+
+static inline bool pd_process_timer_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_TIMER_SENDER_RESPONSE:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_TIMER_SENDER_RESPONSE);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Process Policy Engine's DRS Message
+ */
+
+bool pd_process_event_drs(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+
+       default:
+               return false;
+       }
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_prs.c b/drivers/usb/pd/richtek/pd_process_evt_prs.c
new file mode 100644 (file)
index 0000000..ed05f66
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For PRS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+
+/* PD Control MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
+       { PE_PRS_SRC_SNK_ACCEPT_PR_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
+       { PE_PRS_SRC_SNK_REJECT_PR_SWAP, PE_SRC_READY },
+
+       { PE_PRS_SNK_SRC_ACCEPT_PR_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
+       { PE_PRS_SNK_SRC_REJECT_SWAP, PE_SNK_READY },
+
+       /* VBUS-ON & PS_RDY SENT */
+       { PE_PRS_SNK_SRC_SOURCE_ON, PE_SRC_STARTUP },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
+       { PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
+       { PE_PRS_SNK_SRC_SEND_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_REJECT_WAIT) = {
+       { PE_PRS_SRC_SNK_SEND_SWAP, PE_SRC_READY },
+       { PE_PRS_SNK_SRC_SEND_SWAP, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_REJECT_WAIT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = {
+       { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP },
+       { PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_ACCEPT_PR_SWAP },
+       { PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_ACCEPT_PR_SWAP },
+
+       { PE_PRS_SRC_SNK_ASSERT_RD, PE_PRS_SRC_SNK_WAIT_SOURCE_ON },
+       { PE_PRS_SNK_SRC_ASSERT_RP, PE_PRS_SNK_SRC_SOURCE_ON },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+       { PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_REJECT_PR_SWAP },
+       { PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_REJECT_SWAP },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+/* HW Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_HW_TX_FAILED) = {
+       { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_HARD_RESET },
+       { PE_PRS_SNK_SRC_SOURCE_ON, PE_SRC_HARD_RESET },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_TX_FAILED);
+
+DECL_PE_STATE_TRANSITION(PD_HW_VBUS_SAFE0V) = {
+       { PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_ASSERT_RD },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_VBUS_SAFE0V);
+
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SENDER_RESPONSE) = {
+       { PE_PRS_SRC_SNK_SEND_SWAP, PE_SRC_READY },
+       { PE_PRS_SNK_SRC_SEND_SWAP, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SENDER_RESPONSE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_PS_SOURCE_ON) = {
+       { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_HARD_RESET },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_PS_SOURCE_ON);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_PS_SOURCE_OFF) = {
+       { PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_SNK_HARD_RESET },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_PS_SOURCE_OFF);
+
+/*
+ * [BLOCK] Porcess PD Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
+               pd_enable_timer(pd_port, PD_TIMER_PS_SOURCE_ON);
+               pd_unlock_msg_output(pd_port);  /* for tSRCTransition */
+               return false;
+
+       case PE_PRS_SRC_SNK_SEND_SWAP:
+       case PE_PRS_SNK_SRC_SEND_SWAP:
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               return false;
+
+       default:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
+       }
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       case PD_CTRL_ACCEPT:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
+
+       case PD_CTRL_WAIT:
+       case PD_CTRL_REJECT:
+               pd_notify_pe_cancel_pr_swap(pd_port);
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_REJECT_WAIT);
+
+       case PD_CTRL_PS_RDY:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY);
+
+       default:
+               return false;
+       }
+}
+
+/*
+ * [BLOCK] Porcess DPM MSG
+ */
+
+static inline bool pd_process_dpm_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_DPM_MSG_ACK);
+               break;
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess HW MSG
+ */
+
+static inline bool pd_process_hw_msg_vbus_present(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr == PE_PRS_SNK_SRC_SOURCE_ON)
+               pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_PS_RDY);
+
+       return false;
+}
+
+static inline bool pd_process_hw_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_HW_VBUS_PRESENT:
+               return pd_process_hw_msg_vbus_present(pd_port, pd_event);
+
+       case PD_HW_TX_FAILED:
+               return PE_MAKE_STATE_TRANSIT(PD_HW_TX_FAILED);
+
+       case PD_HW_VBUS_SAFE0V:
+               return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_SAFE0V);
+
+       default:
+               return false;
+       }
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+
+static inline bool pd_process_timer_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_TIMER_SENDER_RESPONSE:
+               pd_notify_pe_cancel_pr_swap(pd_port);
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_SENDER_RESPONSE);
+
+       case PD_TIMER_PS_SOURCE_ON:
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_SOURCE_ON);
+
+       case PD_TIMER_PS_SOURCE_OFF:
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_SOURCE_OFF);
+
+       case PD_TIMER_SOURCE_TRANSITION:
+               pd_dpm_prs_enable_power_source(pd_port, false);
+               return false;
+       default:
+               return false;
+       }
+}
+
+/*
+ * [BLOCK] Process Policy Engine's PRS Message
+ */
+
+bool pd_process_event_prs(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_HW_MSG:
+               return pd_process_hw_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+
+       default:
+               return false;
+       }
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_snk.c b/drivers/usb/pd/richtek/pd_process_evt_snk.c
new file mode 100644 (file)
index 0000000..a9102f3
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For SNK
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+
+/* PD Control MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
+       /* sink */
+       { PE_SNK_GIVE_SINK_CAP, PE_SNK_READY },
+       { PE_SNK_GET_SOURCE_CAP, PE_SNK_READY },
+
+       { PE_SNK_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES },
+
+       /* dual */
+       { PE_DR_SNK_GIVE_SOURCE_CAP, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOTO_MIN) = {
+       { PE_SNK_READY, PE_SNK_TRANSITION_SINK },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOTO_MIN);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
+       { PE_SNK_SELECT_CAPABILITY, PE_SNK_TRANSITION_SINK },
+       { PE_SNK_SEND_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = {
+       { PE_SNK_TRANSITION_SINK, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GET_SINK_CAP) = {
+       { PE_SNK_READY, PE_SNK_GIVE_SINK_CAP },
+
+       { PE_SNK_GET_SOURCE_CAP, PE_SNK_GIVE_SINK_CAP },
+       { PE_DR_SNK_GET_SINK_CAP, PE_SNK_GIVE_SINK_CAP },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GET_SINK_CAP);
+
+/* PD Data MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SOURCE_CAP) = {
+       { PE_SNK_WAIT_FOR_CAPABILITIES, PE_SNK_EVALUATE_CAPABILITY },
+       { PE_SNK_READY, PE_SNK_EVALUATE_CAPABILITY },
+
+       /* PR-Swap issue (Check it later) */
+       { PE_SNK_STARTUP, PE_SNK_EVALUATE_CAPABILITY },
+       { PE_SNK_DISCOVERY, PE_SNK_EVALUATE_CAPABILITY },
+};
+
+DECL_PE_STATE_REACTION(PD_DATA_MSG_SOURCE_CAP);
+
+DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SINK_CAP) = {
+       { PE_DR_SNK_GET_SINK_CAP, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_DATA_MSG_SINK_CAP);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_SNK_EVALUATE_CAPABILITY, PE_SNK_SELECT_CAPABILITY },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+/* HW Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_HW_MSG_VBUS_PRESENT) = {
+       { PE_SNK_DISCOVERY, PE_SNK_WAIT_FOR_CAPABILITIES},
+};
+
+DECL_PE_STATE_REACTION(PD_HW_MSG_VBUS_PRESENT);
+
+DECL_PE_STATE_TRANSITION(PD_HW_MSG_VBUS_ABSENT) = {
+       { PE_SNK_STARTUP, PE_SNK_DISCOVERY },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_MSG_VBUS_ABSENT);
+
+DECL_PE_STATE_TRANSITION(PD_HW_MSG_TX_FAILED) = {
+       { PE_SNK_SOFT_RESET, PE_SNK_HARD_RESET },
+       { PE_SNK_SEND_SOFT_RESET, PE_SNK_HARD_RESET },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_MSG_TX_FAILED);
+
+/* PE Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_HARD_RESET_COMPLETED) = {
+       { PE_SNK_HARD_RESET, PE_SNK_TRANSITION_TO_DEFAULT },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_HARD_RESET_COMPLETED);
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_RESET_PRL_COMPLETED) = {
+       { PE_SNK_STARTUP, PE_SNK_DISCOVERY },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_RESET_PRL_COMPLETED);
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_POWER_ROLE_AT_DEFAULT) = {
+       { PE_SNK_TRANSITION_TO_DEFAULT, PE_SNK_STARTUP },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_POWER_ROLE_AT_DEFAULT);
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_IDLE) = {
+       { PE_IDLE1, PE_IDLE2 },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_IDLE);
+
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_BIST_CONT_MODE) = {
+       { PE_BIST_CARRIER_MODE_2, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_BIST_CONT_MODE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SENDER_RESPONSE) = {
+       { PE_SNK_SELECT_CAPABILITY, PE_SNK_HARD_RESET },
+       { PE_SNK_SEND_SOFT_RESET, PE_SNK_HARD_RESET },
+
+       { PE_DR_SNK_GET_SINK_CAP, PE_SNK_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SENDER_RESPONSE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SINK_REQUEST) = {
+       { PE_SNK_READY, PE_SNK_SELECT_CAPABILITY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SINK_REQUEST);
+
+/*
+ * [BLOCK] Porcess Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SNK_SELECT_CAPABILITY:
+       case PE_SNK_SEND_SOFT_RESET:
+       case PE_DR_SNK_GET_SINK_CAP:
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               return false;
+
+       default:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
+       }
+}
+
+static inline bool pd_process_ctrl_msg_get_source_cap(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SNK_READY:
+       case PE_DR_SNK_GET_SINK_CAP:
+       case PE_SNK_GET_SOURCE_CAP:
+               break;
+
+       default:
+               return false;
+       }
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
+               PE_TRANSIT_STATE(pd_port, PE_DR_SNK_GIVE_SOURCE_CAP);
+               return true;
+       }
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       return false;
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       case PD_CTRL_GOTO_MIN:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOTO_MIN);
+               break;
+
+       case PD_CTRL_ACCEPT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
+               break;
+
+       case PD_CTRL_PING:
+               pd_notify_pe_recv_ping_event(pd_port);
+               break;
+
+       case PD_CTRL_PS_RDY:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY);
+               break;
+
+       case PD_CTRL_GET_SOURCE_CAP:
+               ret = pd_process_ctrl_msg_get_source_cap(pd_port, pd_event);
+               break;
+
+       case PD_CTRL_GET_SINK_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GET_SINK_CAP);
+               break;
+
+       case PD_CTRL_REJECT:
+               if (pd_port->pe_state_curr == PE_DR_SNK_GET_SINK_CAP) {
+                       PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
+                       return true;
+               }
+               /* no break!*/
+
+       case PD_CTRL_WAIT:
+               if (pd_port->pe_state_curr == PE_SNK_SELECT_CAPABILITY) {
+                       if (pd_port->explicit_contract)
+                               PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
+                       else
+                               PE_TRANSIT_STATE(pd_port,
+                                                PE_SNK_WAIT_FOR_CAPABILITIES);
+
+                       return true;
+               }
+               break;
+
+       /* Swap */
+       case PD_CTRL_DR_SWAP:
+               ret = pd_process_ctrl_msg_dr_swap(pd_port, pd_event);
+               break;
+
+       case PD_CTRL_PR_SWAP:
+               ret = pd_process_ctrl_msg_pr_swap(pd_port, pd_event);
+               break;
+
+       case PD_CTRL_VCONN_SWAP:
+               ret = pd_process_ctrl_msg_vconn_swap(pd_port, pd_event);
+               break;
+
+       /* SoftReset */
+       case PD_CTRL_SOFT_RESET:
+               if (!pd_port->during_swap) {
+                       PE_TRANSIT_STATE(pd_port, PE_SNK_SOFT_RESET);
+                       return true;
+               }
+               break;
+       }
+
+       if (!ret)
+               ret = pd_process_protocol_error(pd_port, pd_event);
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Data MSG
+ */
+
+static inline bool pd_process_data_msg(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DATA_SOURCE_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SOURCE_CAP);
+               break;
+
+       case PD_DATA_SINK_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SINK_CAP);
+               break;
+
+       case PD_DATA_BIST:
+               ret = pd_process_data_msg_bist(pd_port, pd_event);
+               break;
+
+       case PD_DATA_REQUEST:
+       case PD_DATA_VENDOR_DEF:
+               break;
+       }
+
+       if (!ret)
+               ret = pd_process_protocol_error(pd_port, pd_event);
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess DPM MSG
+ */
+
+static inline bool pd_process_dpm_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
+               break;
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+       case PD_DPM_ERROR_RECOVERY:
+               PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+               return true;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess HW MSG
+ */
+
+static inline bool pd_process_hw_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_HW_CC_DETACHED:
+               PE_TRANSIT_STATE(pd_port, PE_IDLE1);
+               return true;
+
+       case PD_HW_CC_ATTACHED:
+               PE_TRANSIT_STATE(pd_port, PE_SNK_STARTUP);
+               return true;
+
+       case PD_HW_RECV_HARD_RESET:
+               ret = pd_process_recv_hard_reset(
+                       pd_port, pd_event, PE_SNK_TRANSITION_TO_DEFAULT);
+               break;
+
+       case PD_HW_VBUS_PRESENT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_HW_MSG_VBUS_PRESENT);
+               break;
+
+       case PD_HW_VBUS_ABSENT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_HW_MSG_VBUS_ABSENT);
+               break;
+
+       case PD_HW_TX_FAILED:
+               ret = PE_MAKE_STATE_TRANSIT_FORCE(
+                       PD_HW_MSG_TX_FAILED, PE_SNK_SEND_SOFT_RESET);
+               break;
+       };
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess PE MSG
+ */
+
+static inline bool pd_process_pe_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_PE_RESET_PRL_COMPLETED:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_RESET_PRL_COMPLETED);
+               break;
+
+       case PD_PE_HARD_RESET_COMPLETED:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_HARD_RESET_COMPLETED);
+               break;
+
+       case PD_PE_POWER_ROLE_AT_DEFAULT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_POWER_ROLE_AT_DEFAULT);
+               break;
+
+       case PD_PE_IDLE:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_IDLE);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+
+static inline void pd_report_typec_only_charger(pd_port_t *pd_port)
+{
+       /* TODO: pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISABLE);*/
+       PE_INFO("TYPE-C Only Charger!\r\n");
+       pd_dpm_sink_vbus(pd_port, true);
+       pd_update_connect_state(pd_port, PD_CONNECT_TYPEC_ONLY);
+}
+
+static inline bool pd_process_timer_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_TIMER_BIST_CONT_MODE:
+               ret = PE_MAKE_STATE_TRANSIT(PD_TIMER_BIST_CONT_MODE);
+               break;
+
+       case PD_TIMER_SINK_REQUEST:
+               ret = PE_MAKE_STATE_TRANSIT(PD_TIMER_SINK_REQUEST);
+               break;
+
+#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
+       case PD_TIMER_SENDER_RESPONSE:
+               ret = PE_MAKE_STATE_TRANSIT(PD_TIMER_SENDER_RESPONSE);
+               break;
+
+       case PD_TIMER_SINK_WAIT_CAP:
+       case PD_TIMER_PS_TRANSITION:
+               if (pd_port->hard_reset_counter <= PD_HARD_RESET_COUNT) {
+                       PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET);
+                       return true;
+               }
+               break;
+
+#ifdef CONFIG_USB_PD_FAST_RESP_TYPEC_SRC
+       case PD_TIMER_SRC_RECOVER:
+               if (pd_port->pe_state_curr == PE_SNK_STARTUP) {
+                       pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+                       pd_report_typec_only_charger(pd_port);
+               }
+               break;
+#endif /* CONFIG_USB_PD_FAST_RESP_TYPEC_SRC */
+
+       case PD_TIMER_NO_RESPONSE:
+               if (!pd_dpm_check_vbus_valid(pd_port)) {
+                       PE_DBG("NoResp&VBUS=0\r\n");
+                       PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+                       ret = true;
+               } else if (pd_port->hard_reset_counter <= PD_HARD_RESET_COUNT) {
+                       PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET);
+                       ret = true;
+               } else if (pd_port->pd_prev_connected) {
+                       PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+                       ret = true;
+               } else {
+                       pd_report_typec_only_charger(pd_port);
+               }
+               break;
+#endif
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       case PD_TIMER_DISCOVER_ID:
+               vdm_put_dpm_discover_cable_event(pd_port);
+               break;
+#endif
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Process Policy Engine's SNK Message
+ */
+
+bool pd_process_event_snk(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DATA_MSG:
+               return pd_process_data_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_HW_MSG:
+               return pd_process_hw_msg(pd_port, pd_event);
+
+       case PD_EVT_PE_MSG:
+               return pd_process_pe_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+
+       default:
+               return false;
+       }
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_src.c b/drivers/usb/pd/richtek/pd_process_evt_src.c
new file mode 100644 (file)
index 0000000..8c307ee
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For SRC
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+
+/* PD Control MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
+       { PE_SRC_TRANSITION_SUPPLY2, PE_SRC_READY },
+       { PE_SRC_GIVE_SOURCE_CAP, PE_SRC_READY },
+       { PE_SRC_SOFT_RESET, PE_SRC_SEND_CAPABILITIES },
+
+       { PE_DR_SRC_GIVE_SINK_CAP, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GET_SOURCE_CAP) = {
+       { PE_SRC_READY, PE_SRC_GIVE_SOURCE_CAP },
+
+/* Handler Port Partner Request first  */
+       { PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_GIVE_SOURCE_CAP},
+       { PE_SRC_GET_SINK_CAP, PE_SRC_GIVE_SOURCE_CAP},
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_GET_SOURCE_CAP);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
+       {PE_SRC_SEND_SOFT_RESET, PE_SRC_SEND_CAPABILITIES },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_REJECT) = {
+       { PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_REJECT);
+
+/* PD Data MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DATA_MSG_REQUEST) = {
+       { PE_SRC_SEND_CAPABILITIES, PE_SRC_NEGOTIATE_CAPABILITIES },
+       { PE_SRC_READY, PE_SRC_NEGOTIATE_CAPABILITIES },
+
+/* Handler Port Partner Request first */
+       { PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_GIVE_SOURCE_CAP},
+       { PE_SRC_GET_SINK_CAP, PE_SRC_GIVE_SOURCE_CAP},
+};
+
+DECL_PE_STATE_REACTION(PD_DATA_MSG_REQUEST);
+
+DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SOURCE_CAP) = {
+       { PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_DATA_MSG_SOURCE_CAP);
+
+DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SINK_CAP) = {
+       { PE_SRC_GET_SINK_CAP, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_DATA_MSG_SINK_CAP);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_SRC_NEGOTIATE_CAPABILITIES, PE_SRC_TRANSITION_SUPPLY },
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       { PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES },
+#endif /*  CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+       { PE_SRC_NEGOTIATE_CAPABILITIES, PE_SRC_CAPABILITY_RESPONSE },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_CAP_CHANGED) = {
+       { PE_SRC_READY, PE_SRC_SEND_CAPABILITIES },
+       { PE_SRC_WAIT_NEW_CAPABILITIES, PE_SRC_SEND_CAPABILITIES },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_CAP_CHANGED);
+
+/* HW Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_HW_MSG_TX_FAILED) = {
+       { PE_SRC_SOFT_RESET, PE_SRC_HARD_RESET },
+       { PE_SRC_SEND_SOFT_RESET, PE_SRC_HARD_RESET },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_MSG_TX_FAILED);
+
+DECL_PE_STATE_TRANSITION(PD_HW_VBUS_STABLE) = {
+       { PE_SRC_TRANSITION_SUPPLY, PE_SRC_TRANSITION_SUPPLY2 },
+};
+
+DECL_PE_STATE_REACTION(PD_HW_VBUS_STABLE);
+
+/* PE Event reactions */
+
+/* TODO: Remove it later, always trigger by pd_evt_source_start_timeout */
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_RESET_PRL_COMPLETED) = {
+       { PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_RESET_PRL_COMPLETED);
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_POWER_ROLE_AT_DEFAULT) = {
+       { PE_SRC_TRANSITION_TO_DEFAULT, PE_SRC_STARTUP },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_POWER_ROLE_AT_DEFAULT);
+
+DECL_PE_STATE_TRANSITION(PD_PE_MSG_IDLE) = {
+       { PE_IDLE1, PE_IDLE2 },
+};
+
+DECL_PE_STATE_REACTION(PD_PE_MSG_IDLE);
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SENDER_RESPONSE) = {
+       { PE_SRC_SEND_CAPABILITIES, PE_SRC_HARD_RESET },
+       { PE_SRC_SEND_SOFT_RESET, PE_SRC_HARD_RESET },
+
+       { PE_SRC_GET_SINK_CAP, PE_SRC_READY },
+       { PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SENDER_RESPONSE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_PS_HARD_RESET) = {
+       { PE_SRC_HARD_RESET, PE_SRC_TRANSITION_TO_DEFAULT },
+       { PE_SRC_HARD_RESET_RECEIVED, PE_SRC_TRANSITION_TO_DEFAULT },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_PS_HARD_RESET);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_BIST_CONT_MODE) = {
+       { PE_BIST_CARRIER_MODE_2, PE_SRC_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_BIST_CONT_MODE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SOURCE_START) = {
+       { PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SOURCE_START);
+
+/*
+ * [BLOCK] Porcess Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SRC_SEND_SOFT_RESET:
+       case PE_SRC_GET_SINK_CAP:
+       case PE_DR_SRC_GET_SOURCE_CAP:
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               return false;
+
+       case PE_SRC_SEND_CAPABILITIES:
+               pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE);
+               pd_port->cap_counter = 0;
+               pd_port->hard_reset_counter = 0;
+               pd_notify_pe_hard_reset_completed(pd_port);
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               /* pd_set_cc_res(pd_port, TYPEC_CC_RP_1_5); */
+               return false;
+
+       case PE_SRC_CAPABILITY_RESPONSE:
+               if (!pd_port->explicit_contract)
+                       PE_TRANSIT_STATE(pd_port, PE_SRC_WAIT_NEW_CAPABILITIES);
+               else if (pd_port->invalid_contract)
+                       PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
+               else
+                       PE_TRANSIT_STATE(pd_port, PE_SRC_READY);
+               return true;
+       default:
+               return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
+       }
+}
+
+static inline bool pd_process_ctrl_msg_get_sink_cap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SRC_READY:
+       case PE_DR_SRC_GET_SOURCE_CAP:
+       case PE_SRC_GET_SINK_CAP:
+               break;
+
+       default:
+               return false;
+       }
+
+       if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
+               PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GIVE_SINK_CAP);
+               return true;
+       }
+       pd_send_ctrl_msg(pd_port, TCPC_TX_SOP, PD_CTRL_REJECT);
+       return false;
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       case PD_CTRL_ACCEPT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
+               break;
+
+       case PD_CTRL_REJECT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_REJECT);
+               break;
+
+       case PD_CTRL_GET_SOURCE_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GET_SOURCE_CAP);
+               break;
+
+       case PD_CTRL_GET_SINK_CAP:
+               ret = pd_process_ctrl_msg_get_sink_cap(pd_port, pd_event);
+               break;
+
+       /* Swap */
+       case PD_CTRL_DR_SWAP:
+               ret = pd_process_ctrl_msg_dr_swap(pd_port, pd_event);
+               break;
+
+       case PD_CTRL_PR_SWAP:
+               ret = pd_process_ctrl_msg_pr_swap(pd_port, pd_event);
+               break;
+
+       case PD_CTRL_VCONN_SWAP:
+               ret = pd_process_ctrl_msg_vconn_swap(pd_port, pd_event);
+               break;
+
+       /* SoftReset */
+       case PD_CTRL_SOFT_RESET:
+               if (!pd_port->during_swap) {
+                       PE_TRANSIT_STATE(pd_port, PE_SRC_SOFT_RESET);
+                       return true;
+               }
+               break;
+
+       /* Ignore */
+       case PD_CTRL_PING:
+               pd_notify_pe_recv_ping_event(pd_port);
+       case PD_CTRL_PS_RDY:
+       case PD_CTRL_GOTO_MIN:
+       case PD_CTRL_WAIT:
+               break;
+       }
+
+       if (!ret)
+               ret = pd_process_protocol_error(pd_port, pd_event);
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Data MSG
+ */
+
+static inline bool pd_process_data_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DATA_SOURCE_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SOURCE_CAP);
+               break;
+
+       case PD_DATA_SINK_CAP:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SINK_CAP);
+               break;
+
+       case PD_DATA_BIST:
+               ret = pd_process_data_msg_bist(pd_port, pd_event);
+               break;
+
+       case PD_DATA_REQUEST:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_REQUEST);
+               break;
+
+       case PD_DATA_VENDOR_DEF:
+               return false;
+       }
+
+       if (!ret)
+               ret = pd_process_protocol_error(pd_port, pd_event);
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess DPM MSG
+ */
+
+static inline bool pd_process_dpm_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
+               break;
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+       case PD_DPM_CAP_CHANGED:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_CAP_CHANGED);
+               break;
+
+       case PD_DPM_ERROR_RECOVERY:
+               PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+               return true;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess HW MSG
+ */
+
+static inline bool pd_process_hw_msg_vbus_present(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_SRC_STARTUP:
+               pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
+               break;
+
+       case PE_SRC_TRANSITION_TO_DEFAULT:
+               pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT);
+               break;
+       }
+
+       return false;
+}
+
+static inline bool pd_process_hw_msg_tx_failed(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr == PE_SRC_SEND_CAPABILITIES) {
+               if (pd_port->pd_connected) {
+                       PE_DBG("PR_SWAP NoResp\r\n");
+                       return false;
+               }
+
+               PE_TRANSIT_STATE(pd_port, PE_SRC_DISCOVERY);
+               return true;
+       }
+
+       return PE_MAKE_STATE_TRANSIT_FORCE(
+               PD_HW_MSG_TX_FAILED, PE_SRC_SEND_SOFT_RESET);
+}
+
+static inline bool pd_process_hw_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_HW_CC_DETACHED:
+               PE_TRANSIT_STATE(pd_port, PE_IDLE1);
+               return true;
+
+       case PD_HW_CC_ATTACHED:
+               PE_TRANSIT_STATE(pd_port, PE_SRC_STARTUP);
+               return true;
+
+       case PD_HW_RECV_HARD_RESET:
+               ret = pd_process_recv_hard_reset(
+                               pd_port, pd_event, PE_SRC_HARD_RESET_RECEIVED);
+               break;
+
+       case PD_HW_VBUS_PRESENT:
+               ret = pd_process_hw_msg_vbus_present(pd_port, pd_event);
+               break;
+
+       case PD_HW_VBUS_SAFE0V:
+               pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER);
+               break;
+
+       case PD_HW_VBUS_STABLE:
+               ret = PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_STABLE);
+               break;
+
+       case PD_HW_TX_FAILED:
+               ret = pd_process_hw_msg_tx_failed(pd_port, pd_event);
+               break;
+
+       case PD_HW_VBUS_ABSENT:
+               break;
+       };
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess PE MSG
+ */
+
+static inline bool pd_process_pe_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_PE_RESET_PRL_COMPLETED:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_RESET_PRL_COMPLETED);
+               break;
+
+       case PD_PE_POWER_ROLE_AT_DEFAULT:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_POWER_ROLE_AT_DEFAULT);
+               break;
+
+       case PD_PE_IDLE:
+               ret = PE_MAKE_STATE_TRANSIT(PD_PE_MSG_IDLE);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+static inline bool pd_process_timer_msg_source_start(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       if (pd_is_auto_discover_cable_id(pd_port)) {
+               if (vdm_put_dpm_discover_cable_event(pd_port)) {
+                       /* waiting for dpm_ack event */
+                       return false;
+               }
+       }
+#endif
+
+       return PE_MAKE_STATE_TRANSIT(PD_TIMER_SOURCE_START);
+}
+
+static inline bool pd_process_timer_msg_source_cap(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr != PE_SRC_DISCOVERY)
+               return false;
+
+       if (pd_port->cap_counter <= PD_CAPS_COUNT)
+               PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
+       else    /* in this state, PD always not connected */
+               PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
+
+       return true;
+}
+
+static inline bool pd_process_timer_msg_no_response(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->hard_reset_counter <= PD_HARD_RESET_COUNT)
+               PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
+       else if (pd_port->pd_prev_connected)
+               PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
+       else
+               PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
+
+       return true;
+}
+
+static inline bool pd_process_timer_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_TIMER_BIST_CONT_MODE:
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_BIST_CONT_MODE);
+
+       case PD_TIMER_SOURCE_CAPABILITY:
+               return pd_process_timer_msg_source_cap(pd_port, pd_event);
+
+#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
+       case PD_TIMER_SENDER_RESPONSE:
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_SENDER_RESPONSE);
+#endif
+
+       case PD_TIMER_PS_HARD_RESET:
+               return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_HARD_RESET);
+
+       case PD_TIMER_SOURCE_START:
+               return pd_process_timer_msg_source_start(pd_port, pd_event);
+
+#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
+       case PD_TIMER_NO_RESPONSE:
+               return pd_process_timer_msg_no_response(pd_port, pd_event);
+#endif
+
+       case PD_TIMER_SOURCE_TRANSITION:
+               if (pd_port->state_machine != PE_STATE_MACHINE_PR_SWAP)
+                       pd_dpm_src_transition_power(pd_port, pd_event);
+               break;
+
+#ifdef CONFIG_PD_DISCOVER_CABLE_ID
+       case PD_TIMER_DISCOVER_ID:
+               vdm_put_dpm_discover_cable_event(pd_port);
+               break;
+#endif
+
+       case PD_TIMER_SRC_RECOVER:
+               pd_dpm_source_vbus(pd_port, true);
+               pd_enable_vbus_valid_detection(pd_port, true);
+               break;
+       }
+
+       return false;
+}
+
+/*
+ * [BLOCK] Process Policy Engine's SRC Message
+ */
+
+bool pd_process_event_src(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DATA_MSG:
+               return pd_process_data_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_HW_MSG:
+               return pd_process_hw_msg(pd_port, pd_event);
+
+       case PD_EVT_PE_MSG:
+               return pd_process_pe_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+
+       default:
+               return false;
+       }
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_vcs.c b/drivers/usb/pd/richtek/pd_process_evt_vcs.c
new file mode 100644 (file)
index 0000000..a9a91ad
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For VCS
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+
+/* PD Control MSG reactions */
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_REJECT_WAIT) = {
+       { PE_VCS_SEND_SWAP, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_REJECT_WAIT);
+
+DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = {
+       { PE_VCS_WAIT_FOR_VCONN, PE_VCS_TURN_OFF_VCONN },
+};
+
+DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_VCS_EVALUATE_SWAP, PE_VCS_ACCEPT_SWAP },
+       { PE_VCS_TURN_ON_VCONN, PE_VCS_SEND_PS_RDY },
+       { PE_VCS_TURN_OFF_VCONN, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+       { PE_VCS_EVALUATE_SWAP, PE_VCS_REJECT_VCONN_SWAP },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_SENDER_RESPONSE) = {
+       { PE_VCS_SEND_SWAP, PE_VIRT_READY },
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_SENDER_RESPONSE);
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_VCONN_ON) = {
+       { PE_VCS_WAIT_FOR_VCONN, PE_VIRT_HARD_RESET},
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_VCONN_ON);
+
+/*
+ * [BLOCK] Porcess PD Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_VCS_REJECT_VCONN_SWAP:
+       case PE_VCS_SEND_PS_RDY:
+               PE_TRANSIT_READY_STATE(pd_port);
+               return true;
+
+       case PE_VCS_ACCEPT_SWAP:
+               PE_TRANSIT_VCS_SWAP_STATE(pd_port);
+               return true;
+
+       case PE_VCS_SEND_SWAP:
+               pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
+               return false;
+
+       default:
+               return false;
+       }
+}
+
+static inline bool pd_process_ctrl_msg_accept(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->pe_state_curr == PE_VCS_SEND_SWAP) {
+               PE_TRANSIT_VCS_SWAP_STATE(pd_port);
+               return true;
+       }
+
+       return false;
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       case PD_CTRL_ACCEPT:
+               return pd_process_ctrl_msg_accept(pd_port, pd_event);
+
+       case PD_CTRL_WAIT:
+       case PD_CTRL_REJECT:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_CTRL_MSG_REJECT_WAIT);
+               break;
+
+       case PD_CTRL_PS_RDY:
+               ret = PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess DPM MSG
+ */
+
+static inline bool pd_process_dpm_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_DPM_MSG_ACK);
+               break;
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+
+static inline bool pd_process_timer_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_TIMER_SENDER_RESPONSE:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_TIMER_SENDER_RESPONSE);
+               break;
+
+       case PD_TIMER_VCONN_ON:
+               ret = PE_MAKE_STATE_TRANSIT_VIRT(PD_TIMER_VCONN_ON);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Process Policy Engine's VCS Message
+ */
+
+bool pd_process_event_vcs(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+
+       default:
+               return false;
+       }
+}
diff --git a/drivers/usb/pd/richtek/pd_process_evt_vdm.c b/drivers/usb/pd/richtek/pd_process_evt_vdm.c
new file mode 100644 (file)
index 0000000..e3216fc
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Process Event For VDM
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/pd_process_evt.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+
+#define VDM_CMD_STATE(cmd, cmd_type)   \
+       (((cmd) & 0x1f) | (((cmd_type) & 0x03) << 6))
+
+#define VDM_CMD_INIT_STATE(cmd, next_state)    \
+       { VDM_CMD_STATE(cmd, CMDT_INIT), next_state }
+
+#define VDM_CMD_ACK_STATE(cmd, next_state)     \
+       { VDM_CMD_STATE(cmd, CMDT_RSP_ACK), next_state }
+
+#define VDM_CMD_NACK_STATE(cmd, next_state)    \
+       { VDM_CMD_STATE(cmd, CMDT_RSP_NAK), next_state }
+
+#define VDM_CMD_BUSY_STATE(cmd, next_state)    \
+       { VDM_CMD_STATE(cmd, CMDT_RSP_BUSY), next_state }
+
+/* UFP PD VDM Command's reactions */
+
+DECL_PE_STATE_TRANSITION(PD_UFP_VDM_CMD) = {
+       VDM_CMD_INIT_STATE(CMD_DISCOVER_IDENT, PE_UFP_VDM_GET_IDENTITY),
+       VDM_CMD_INIT_STATE(CMD_DISCOVER_SVID, PE_UFP_VDM_GET_SVIDS),
+       VDM_CMD_INIT_STATE(CMD_DISCOVER_MODES, PE_UFP_VDM_GET_MODES),
+       VDM_CMD_INIT_STATE(CMD_ENTER_MODE, PE_UFP_VDM_EVALUATE_MODE_ENTRY),
+       VDM_CMD_INIT_STATE(CMD_EXIT_MODE, PE_UFP_VDM_MODE_EXIT),
+       /* CHECK IT LATER */
+       VDM_CMD_INIT_STATE(CMD_ATTENTION, PE_UFP_VDM_ATTENTION_REQUEST),
+};
+
+DECL_PE_STATE_REACTION(PD_UFP_VDM_CMD);
+
+/* DFP PD VDM Command's reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_DISCOVER_ID) = {
+       VDM_CMD_ACK_STATE(CMD_DISCOVER_IDENT,
+                         PE_DFP_UFP_VDM_IDENTITY_ACKED),
+       VDM_CMD_NACK_STATE(CMD_DISCOVER_IDENT, PE_DFP_UFP_VDM_IDENTITY_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_DISCOVER_IDENT, PE_DFP_UFP_VDM_IDENTITY_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_DISCOVER_ID);
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_DISCOVER_SVID) = {
+       VDM_CMD_ACK_STATE(CMD_DISCOVER_SVID,
+                         PE_DFP_VDM_SVIDS_ACKED),
+       VDM_CMD_NACK_STATE(CMD_DISCOVER_SVID, PE_DFP_VDM_SVIDS_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_DISCOVER_SVID, PE_DFP_VDM_SVIDS_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_DISCOVER_SVID);
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_DISCOVER_MODES) = {
+       VDM_CMD_ACK_STATE(CMD_DISCOVER_MODES,
+                         PE_DFP_VDM_MODES_ACKED),
+       VDM_CMD_NACK_STATE(CMD_DISCOVER_MODES, PE_DFP_VDM_MODES_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_DISCOVER_MODES, PE_DFP_VDM_MODES_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_DISCOVER_MODES);
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_ENTER_MODE) = {
+       VDM_CMD_ACK_STATE(CMD_ENTER_MODE,
+                         PE_DFP_VDM_MODE_ENTRY_ACKED),
+       VDM_CMD_NACK_STATE(CMD_ENTER_MODE, PE_DFP_VDM_MODE_ENTRY_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_ENTER_MODE, PE_DFP_VDM_MODE_ENTRY_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_ENTER_MODE);
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_EXIT_MODE) = {
+       VDM_CMD_ACK_STATE(CMD_EXIT_MODE,
+                         PE_DFP_VDM_MODE_ENTRY_ACKED),
+       VDM_CMD_NACK_STATE(CMD_EXIT_MODE,
+                          PE_DFP_VDM_MODE_ENTRY_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_EXIT_MODE,
+                          PE_VIRT_HARD_RESET),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_EXIT_MODE);
+
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_ATTENTION) = {
+       VDM_CMD_INIT_STATE(CMD_ATTENTION,
+                          PE_DFP_VDM_ATTENTION_REQUEST),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_ATTENTION);
+/* HW Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_HW_MSG_TX_FAILED) = {
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       {PE_SRC_VDM_IDENTITY_REQUEST, PE_SRC_VDM_IDENTITY_NAKED},
+#endif /* PD_CAP_SRC_STARTUP_DISCOVERY_ID */
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       {PE_DFP_CBL_VDM_IDENTITY_REQUEST, PE_DFP_CBL_VDM_IDENTITY_NAKED},
+#endif
+};
+
+DECL_PE_STATE_REACTION(PD_HW_MSG_TX_FAILED);
+
+/* DPM Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
+       { PE_UFP_VDM_GET_IDENTITY, PE_UFP_VDM_SEND_IDENTITY },
+       { PE_UFP_VDM_GET_SVIDS, PE_UFP_VDM_SEND_SVIDS },
+       { PE_UFP_VDM_GET_MODES, PE_UFP_VDM_SEND_MODES },
+       { PE_UFP_VDM_MODE_EXIT, PE_UFP_VDM_MODE_EXIT_ACK},
+       { PE_UFP_VDM_EVALUATE_MODE_ENTRY, PE_UFP_VDM_MODE_ENTRY_ACK },
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
+
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
+       {PE_UFP_VDM_GET_IDENTITY, PE_UFP_VDM_GET_IDENTITY_NAK},
+       {PE_UFP_VDM_GET_SVIDS, PE_UFP_VDM_GET_SVIDS_NAK},
+       {PE_UFP_VDM_GET_MODES, PE_UFP_VDM_GET_MODES_NAK},
+       {PE_UFP_VDM_MODE_EXIT, PE_UFP_VDM_MODE_EXIT_NAK},
+       {PE_UFP_VDM_EVALUATE_MODE_ENTRY, PE_UFP_VDM_MODE_ENTRY_NAK},
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
+
+/* Discover Cable ID */
+
+#ifdef CONFIG_PD_DISCOVER_CABLE_ID
+DECL_PE_STATE_TRANSITION(PD_DPM_MSG_DISCOVER_CABLE) = {
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       { PE_SRC_STARTUP, PE_SRC_VDM_IDENTITY_REQUEST},
+       { PE_SRC_DISCOVERY, PE_SRC_VDM_IDENTITY_REQUEST},
+#endif
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       { PE_SRC_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST},
+       { PE_SNK_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST},
+#endif
+};
+
+DECL_PE_STATE_REACTION(PD_DPM_MSG_DISCOVER_CABLE);
+#endif
+
+/* Source Startup Discover Cable ID */
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+DECL_PE_STATE_TRANSITION(PD_SRC_VDM_DISCOVER_CABLE) = {
+       VDM_CMD_ACK_STATE(CMD_DISCOVER_IDENT, PE_SRC_VDM_IDENTITY_ACKED),
+       VDM_CMD_NACK_STATE(CMD_DISCOVER_IDENT, PE_SRC_VDM_IDENTITY_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_DISCOVER_IDENT, PE_SRC_VDM_IDENTITY_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_SRC_VDM_DISCOVER_CABLE);
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+DECL_PE_STATE_TRANSITION(PD_DFP_VDM_DISCOVER_CABLE) = {
+       VDM_CMD_ACK_STATE(CMD_DISCOVER_IDENT, PE_DFP_CBL_VDM_IDENTITY_ACKED),
+       VDM_CMD_NACK_STATE(CMD_DISCOVER_IDENT, PE_DFP_CBL_VDM_IDENTITY_NAKED),
+       VDM_CMD_BUSY_STATE(CMD_DISCOVER_IDENT, PE_DFP_CBL_VDM_IDENTITY_NAKED),
+};
+
+DECL_PE_STATE_REACTION(PD_DFP_VDM_DISCOVER_CABLE);
+
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+
+/* Timer Event reactions */
+
+DECL_PE_STATE_TRANSITION(PD_TIMER_VDM_RESPONSE) = {
+       { PE_DFP_UFP_VDM_IDENTITY_REQUEST, PE_DFP_UFP_VDM_IDENTITY_NAKED },
+       { PE_DFP_VDM_SVIDS_REQUEST, PE_DFP_VDM_SVIDS_NAKED },
+       { PE_DFP_VDM_MODES_REQUEST, PE_DFP_VDM_MODES_NAKED },
+       { PE_DFP_VDM_MODE_EXIT_REQUEST, PE_VIRT_HARD_RESET },
+       { PE_DFP_VDM_MODE_ENTRY_REQUEST, PE_DFP_VDM_MODE_ENTRY_NAKED },
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       { PE_SRC_VDM_IDENTITY_REQUEST, PE_SRC_VDM_IDENTITY_NAKED },
+#endif
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       { PE_DFP_CBL_VDM_IDENTITY_REQUEST, PE_DFP_CBL_VDM_IDENTITY_NAKED },
+#endif
+};
+
+DECL_PE_STATE_REACTION(PD_TIMER_VDM_RESPONSE);
+
+/*
+ * [BLOCK] Porcess Ctrl MSG
+ */
+
+static inline bool pd_process_ctrl_msg_good_crc(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+       case PE_UFP_VDM_SEND_IDENTITY:
+       case PE_UFP_VDM_GET_IDENTITY_NAK:
+       case PE_UFP_VDM_SEND_SVIDS:
+       case PE_UFP_VDM_GET_SVIDS_NAK:
+
+       case PE_UFP_VDM_SEND_MODES:
+       case PE_UFP_VDM_GET_MODES_NAK:
+       case PE_UFP_VDM_MODE_ENTRY_ACK:
+       case PE_UFP_VDM_MODE_ENTRY_NAK:
+       case PE_UFP_VDM_MODE_EXIT_ACK:
+       case PE_UFP_VDM_MODE_EXIT_NAK:
+
+               PE_TRANSIT_READY_STATE(pd_port);
+               return true;
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       case PE_SRC_VDM_IDENTITY_REQUEST:
+               pd_port->power_cable_present = true;
+               return false;
+#endif
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       case PE_DFP_CBL_VDM_IDENTITY_REQUEST:
+               pd_port->power_cable_present = true;
+               return false;
+#endif
+       }
+
+       return false;
+}
+
+static inline bool pd_process_ctrl_msg(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_CTRL_GOOD_CRC:
+               return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
+
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static inline bool pd_process_uvdm(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       return false;
+}
+
+/*
+ * [BLOCK] Porcess Data MSG (VDM)
+ */
+
+#if (PE_EVT_INFO_VDM_DIS == 0)
+static const char * const pe_vdm_cmd_name[] = {
+       "DiscoverID",
+       "DiscoverSVID",
+       "DiscoverMode",
+       "EnterMode",
+       "ExitMode",
+       "Attention",
+};
+
+static const char *const pe_vdm_dp_cmd_name[] = {
+       "DPStatus",
+       "DPConfig",
+};
+
+static const char * const pe_vdm_cmd_type_name[] = {
+       "INIT",
+       "ACK",
+       "NACK",
+       "BUSY",
+};
+#endif /* if (PE_EVT_INFO_VDM_DIS == 0) */
+
+static inline void print_vdm_msg(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+#if (PE_EVT_INFO_VDM_DIS == 0)
+       u8 cmd;
+       u8 cmd_type;
+       const char *name = NULL;
+       u32 vdm_hdr = pd_event->pd_msg->payload[0];
+
+       cmd = PD_VDO_CMD(vdm_hdr);
+       cmd_type = PD_VDO_CMDT(vdm_hdr);
+
+       if (cmd <= ARRAY_SIZE(pe_vdm_cmd_name))
+               name = pe_vdm_cmd_name[cmd - 1];
+       if (!name)
+               return;
+
+       if (cmd_type >= ARRAY_SIZE(pe_vdm_cmd_type_name))
+               return;
+
+       PE_DBG("%s:%s\r\n", name, pe_vdm_cmd_type_name[cmd_type]);
+
+#endif /* PE_EVT_INFO_VDM_DIS */
+}
+
+static inline bool pd_process_ufp_vdm(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (!pd_check_pe_state_ready(pd_port)) {
+               PE_DBG("659 : invalid, current status\r\n");
+               return false;
+       }
+
+       if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_UFP_VDM_CMD))
+               return true;
+
+       return false;
+}
+
+static inline bool pd_process_dfp_vdm(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       u32 vdm_hdr = pd_event->pd_msg->payload[0];
+
+       if ((PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) &&
+           PD_VDO_CMD(vdm_hdr) == CMD_ATTENTION) {
+               if (!pd_check_pe_state_ready(pd_port)) {
+                       PE_DBG("670 : invalid, current status\r\n");
+                       return false;
+               }
+
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_ATTENTION))
+                       return true;
+       }
+
+       switch (pd_port->pe_state_curr) {
+       case PE_DFP_UFP_VDM_IDENTITY_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_DISCOVER_ID))
+                       return true;
+
+       case PE_DFP_VDM_SVIDS_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_DISCOVER_SVID))
+                       return true;
+
+       case PE_DFP_VDM_MODES_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_DISCOVER_MODES))
+                       return true;
+
+       case PE_DFP_VDM_MODE_ENTRY_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_ENTER_MODE))
+                       return true;
+
+       case PE_DFP_VDM_MODE_EXIT_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT_VIRT(PD_DFP_VDM_EXIT_MODE))
+                       return true;
+       }
+       return false;
+}
+
+static inline bool pd_process_sop_vdm(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       if (pd_port->data_role == PD_ROLE_UFP)
+               ret = pd_process_ufp_vdm(pd_port, pd_event);
+       else
+               ret = pd_process_dfp_vdm(pd_port, pd_event);
+
+       if (!ret)
+               PE_DBG("Unknown VDM\r\n");
+       return ret;
+}
+
+static inline bool pd_process_sop_prime_vdm(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_port->pe_state_curr) {
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       case PE_SRC_VDM_IDENTITY_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_SRC_VDM_DISCOVER_CABLE))
+                       return true;
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       case PE_DFP_CBL_VDM_IDENTITY_REQUEST:
+               if (PE_MAKE_VDM_CMD_STATE_TRANSIT(PD_DFP_VDM_DISCOVER_CABLE))
+                       return true;
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+       }
+       return false;
+}
+
+static inline bool pd_process_data_msg(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+       u32 vdm_hdr;
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       if (pd_event->msg != PD_DATA_VENDOR_DEF)
+               return ret;
+
+       vdm_hdr = pd_msg->payload[0];
+       if (!PD_VDO_SVDM(vdm_hdr))
+               return pd_process_uvdm(pd_port, pd_event);
+
+       /* From Port Partner, copy curr_state from pd_state */
+       if (PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) {
+               pd_port->pe_vdm_state = pd_port->pe_pd_state;
+               pd_port->pe_state_curr = pd_port->pe_pd_state;
+#if PE_DBG_RESET_VDM_DIS == 0
+               PE_DBG("reset vdm_state\r\n");
+#endif /* if PE_DBG_RESET_VDM_DIS == 0 */
+       }
+
+       print_vdm_msg(pd_port, pd_event);
+
+       if (pd_msg->frame_type == TCPC_TX_SOP_PRIME)
+               ret = pd_process_sop_prime_vdm(pd_port, pd_event);
+       else
+               ret = pd_process_sop_vdm(pd_port, pd_event);
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess PDM MSG
+ */
+
+static inline bool pd_process_dpm_msg_ack(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       if (pd_port->data_role == PD_ROLE_DFP) {
+               switch (pd_port->pe_state_curr) {
+               case PE_DFP_UFP_VDM_IDENTITY_ACKED:
+               case PE_DFP_UFP_VDM_IDENTITY_NAKED:
+               case PE_DFP_CBL_VDM_IDENTITY_ACKED:
+               case PE_DFP_CBL_VDM_IDENTITY_NAKED:
+               case PE_DFP_VDM_SVIDS_ACKED:
+               case PE_DFP_VDM_SVIDS_NAKED:
+               case PE_DFP_VDM_MODES_ACKED:
+               case PE_DFP_VDM_MODES_NAKED:
+               case PE_DFP_VDM_MODE_ENTRY_ACKED:
+               case PE_DFP_VDM_MODE_EXIT_REQUEST:
+               case PE_DFP_VDM_MODE_EXIT_ACKED:
+               case PE_DFP_VDM_ATTENTION_REQUEST:
+                       PE_TRANSIT_READY_STATE(pd_port);
+                       return true;
+               default:
+                       return false;
+               }
+       } else {
+               return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
+       }
+}
+
+static inline bool pd_process_dpm_msg_vdm_request(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool is_dfp;
+       bool is_attention;
+
+       if (!pd_check_pe_state_ready(pd_port)) {
+               pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_NOT_READY);
+               PE_DBG("skip vdm_request, not ready_state (%d)\r\n",
+                      pd_port->pe_state_curr);
+               return false;
+       }
+
+       is_dfp = pd_port->data_role == PD_ROLE_DFP;
+       is_attention = pd_event->msg_sec == PD_DPM_VDM_REQUEST_ATTENTION;
+
+       if ((is_dfp && is_attention) || (!is_dfp && !is_attention)) {
+               pd_update_dpm_request_state(pd_port, DPM_REQ_ERR_WRONG_ROLE);
+               PE_DBG("skip vdm_request, not dfp\r\n");
+               return false;
+       }
+
+       PE_TRANSIT_STATE(pd_port, pd_event->msg_sec);
+       return true;
+}
+
+static inline bool pd_process_dpm_msg(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_DPM_ACK:
+               ret = pd_process_dpm_msg_ack(pd_port, pd_event);
+               break;
+
+       case PD_DPM_NAK:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
+               break;
+
+       case PD_DPM_VDM_REQUEST:
+               ret = pd_process_dpm_msg_vdm_request(pd_port, pd_event);
+               break;
+
+#ifdef CONFIG_PD_DISCOVER_CABLE_ID
+       case PD_DPM_DISCOVER_CABLE_ID:
+               ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_DISCOVER_CABLE);
+               break;
+#endif
+       }
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess HW MSG
+ */
+
+static inline bool pd_process_hw_msg_retry_vdm(
+       pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       PE_DBG("RetryVDM\r\n");
+       return pd_process_sop_vdm(pd_port, pd_event);
+}
+
+static inline bool pd_process_hw_msg(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       bool ret = false;
+
+       switch (pd_event->msg) {
+       case PD_HW_TX_FAILED:
+               ret = PE_MAKE_STATE_TRANSIT(PD_HW_MSG_TX_FAILED);
+               break;
+
+       case PD_HW_RETRY_VDM:
+               ret = pd_process_hw_msg_retry_vdm(pd_port, pd_event);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Porcess Timer MSG
+ */
+
+static inline bool pd_process_timer_msg(
+               pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->msg) {
+       case PD_TIMER_VDM_RESPONSE:
+               return PE_MAKE_STATE_TRANSIT_VIRT(PD_TIMER_VDM_RESPONSE);
+
+       default:
+               return false;
+       }
+}
+
+/*
+ * [BLOCK] Process Policy Engine's VDM Message
+ */
+
+bool pd_process_event_vdm(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       switch (pd_event->event_type) {
+       case PD_EVT_CTRL_MSG:
+               return pd_process_ctrl_msg(pd_port, pd_event);
+
+       case PD_EVT_DATA_MSG:
+               return pd_process_data_msg(pd_port, pd_event);
+
+       case PD_EVT_DPM_MSG:
+               return pd_process_dpm_msg(pd_port, pd_event);
+
+       case PD_EVT_HW_MSG:
+               return pd_process_hw_msg(pd_port, pd_event);
+
+       case PD_EVT_TIMER_MSG:
+               return pd_process_timer_msg(pd_port, pd_event);
+       }
+
+       return false;
+}
diff --git a/drivers/usb/pd/richtek/rt-regmap.c b/drivers/usb/pd/richtek/rt-regmap.c
new file mode 100644 (file)
index 0000000..413ccdf
--- /dev/null
@@ -0,0 +1,2129 @@
+/* drivers/misc/rt-regmap.c
+ * Richtek regmap with debugfs Driver
+ *
+ * Copyright (C) 2014 Richtek Technology Corp.
+ * Author: Jeff Chang <jeff_chang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/semaphore.h>
+
+#include <linux/hisi/usb/pd/richtek/rt-regmap.h>
+
+struct rt_regmap_ops {
+       int (*regmap_block_write)(struct rt_regmap_device *rd, u32 reg,
+                                 int bytes, const void *data);
+       int (*regmap_block_read)(struct rt_regmap_device *rd, u32 reg,
+                                int bytes, void *dest);
+};
+
+enum {
+       RT_DBG_REG,
+       RT_DBG_DATA,
+       RT_DBG_REGS,
+       RT_DBG_SYNC,
+       RT_DBG_ERROR,
+       RT_DBG_NAME,
+       RT_DBG_BLOCK,
+       RT_DBG_SIZE,
+       RT_DBG_SLAVE_ADDR,
+       RT_SUPPORT_MODE,
+       RT_DBG_IO_LOG,
+       RT_DBG_CACHE_MODE,
+       RT_DBG_REG_SIZE,
+};
+
+struct reg_index_offset {
+       int index;
+       int offset;
+};
+
+struct rt_debug_data {
+       struct reg_index_offset rio;
+       unsigned int reg_addr;
+       unsigned int reg_size;
+       unsigned char part_id;
+};
+
+/* rt_regmap_device
+ *
+ * Richtek regmap device. One for each rt_regmap.
+ *
+ */
+struct rt_regmap_device {
+       struct rt_regmap_properties props;
+       struct rt_regmap_fops *rops;
+       struct rt_regmap_ops regmap_ops;
+       struct device dev;
+       void *client;
+       struct semaphore semaphore;
+       struct dentry *rt_den;
+       struct dentry *rt_debug_file[13];
+       struct rt_debug_st rtdbg_st[13];
+       struct dentry **rt_reg_file;
+       struct rt_debug_st **reg_st;
+       struct rt_debug_data dbg_data;
+       struct delayed_work rt_work;
+       unsigned char *cache_flag;
+       unsigned char part_size_limit;
+       unsigned char *alloc_data;
+       char *err_msg;
+
+       int (*rt_block_write[4])(struct rt_regmap_device *rd,
+                                struct rt_register *rm, int size,
+                               const struct reg_index_offset *rio,
+                               unsigned char *wdata, int *count);
+       unsigned char cache_inited:1;
+       unsigned char error_occurred:1;
+       unsigned char pending_event:1;
+};
+
+struct dentry *rt_regmap_dir;
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+       char *token;
+       int base, cnt;
+
+       token = strsep(&buf, " ");
+
+       for (cnt = 0; cnt < num_of_par; cnt++) {
+               if (token) {
+                       if ((token[1] == 'x') || (token[1] == 'X'))
+                               base = 16;
+                       else
+                               base = 10;
+
+                       if (kstrtoul(token, base, &param1[cnt]) != 0)
+                               return -EINVAL;
+
+                       token = strsep(&buf, " ");
+               } else {
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int get_datas(const char *buf, const int length,
+                    unsigned char *data_buffer, unsigned char data_length)
+{
+       int i, ptr;
+       long int value;
+       char token[5];
+
+       token[0] = '0';
+       token[1] = 'x';
+       token[4] = 0;
+       if (buf[0] != '0' || buf[1] != 'x')
+               return -EINVAL;
+
+       ptr = 2;
+       for (i = 0; (i < data_length) && (ptr + 2 <= length); i++) {
+               token[2] = buf[ptr++];
+               token[3] = buf[ptr++];
+               ptr++;
+               if (kstrtoul(token, 16, &value) != 0)
+                       return -EINVAL;
+               data_buffer[i] = value;
+       }
+       return 0;
+}
+
+static struct reg_index_offset find_register_index(
+               const struct rt_regmap_device *rd, u32 reg)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       int register_num = rd->props.register_num;
+       struct reg_index_offset rio = {0, 0};
+       int index = 0, i = 0, unit = RT_1BYTE_MODE;
+
+       for (index = 0; index < register_num; index++) {
+               if (reg == rm[index]->addr) {
+                       rio.index = index;
+                       rio.offset = 0;
+                       break;
+               } else if (reg > rm[index]->addr) {
+                       if ((reg - rm[index]->addr) < rm[index]->size) {
+                               rio.index = index;
+                               while (&rd->props.group[i]) {
+                                       if (reg >= rd->props.group[i].start &&
+                                           reg <= rd->props.group[i].end) {
+                                               unit =
+                                                       rd->props.group[i].mode;
+                                               break;
+                                       }
+                                       i++;
+                                       unit = RT_1BYTE_MODE;
+                               }
+                               rio.offset =
+                                       (reg - rm[index]->addr) * unit;
+                       } else {
+                               rio.index = -1;
+                               rio.offset = rio.index;
+                       }
+               }
+       }
+       return rio;
+}
+
+static int rt_chip_block_write(struct rt_regmap_device *rd, u32 reg,
+                              int bytes, const void *src);
+
+/* rt_regmap_cache_sync - sync all cache data to real chip*/
+void rt_regmap_cache_sync(struct rt_regmap_device *rd)
+{
+       int i, rc, num;
+       const rt_register_map_t *rm = rd->props.rm;
+
+       down(&rd->semaphore);
+       if (!rd->pending_event)
+               goto err_cache_sync;
+
+       num = rd->props.register_num;
+       for (i = 0; i < num; i++) {
+               if (*(rd->cache_flag + i) == 1) {
+                       rc = rt_chip_block_write(rd, rm[i]->addr,
+                                                rm[i]->size,
+                                                rm[i]->cache_data);
+                       if (rc < 0) {
+                               dev_err(&rd->dev, "rt-regmap sync error\n");
+                               goto err_cache_sync;
+                       }
+                       *(rd->cache_flag + i) = 0;
+               }
+       }
+       rd->pending_event = 0;
+       dev_info(&rd->dev, "regmap sync successfully\n");
+err_cache_sync:
+       up(&rd->semaphore);
+}
+EXPORT_SYMBOL(rt_regmap_cache_sync);
+
+/* rt_regmap_cache_write_back - write current cache data to chip
+ * @rd: rt_regmap_device pointer.
+ * @reg: register map address
+ */
+void rt_regmap_cache_write_back(struct rt_regmap_device *rd, u32 reg)
+{
+       struct reg_index_offset rio;
+       const rt_register_map_t *rm = rd->props.rm;
+       int rc;
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of range\n", reg);
+               return;
+       }
+
+       down(&rd->semaphore);
+       if ((rm[rio.index]->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE) {
+               rc = rt_chip_block_write(rd, rm[rio.index]->addr,
+                                        rm[rio.index]->size,
+                                        rm[rio.index]->cache_data);
+               if (rc < 0) {
+                       dev_err(&rd->dev, "rt-regmap sync error\n");
+                       goto err_cache_chip_write;
+               }
+               *(rd->cache_flag + rio.index) = 0;
+       }
+       dev_info(&rd->dev, "regmap sync successfully\n");
+err_cache_chip_write:
+       up(&rd->semaphore);
+}
+EXPORT_SYMBOL(rt_regmap_cache_write_back);
+
+/* rt_is_reg_volatile - check register map is volatile or not
+ * @rd: rt_regmap_device pointer.
+ * reg: register map address.
+ */
+int rt_is_reg_volatile(struct rt_regmap_device *rd, u32 reg)
+{
+       struct reg_index_offset rio;
+       rt_register_map_t rm;
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of range\n", reg);
+               return -EINVAL;
+       }
+       rm = rd->props.rm[rio.index];
+
+       return (rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE ? 1 : 0;
+}
+EXPORT_SYMBOL(rt_is_reg_volatile);
+
+/* rt_reg_regsize - get register map size for specific register
+ * @rd: rt_regmap_device pointer.
+ * reg: register map address
+ */
+int rt_get_regsize(struct rt_regmap_device *rd, u32 reg)
+{
+       struct reg_index_offset rio;
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0 || rio.offset != 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of map\n", reg);
+               return -EINVAL;
+       }
+       return rd->props.rm[rio.index]->size;
+}
+EXPORT_SYMBOL(rt_get_regsize);
+
+static void rt_work_func(struct work_struct *work)
+{
+       struct rt_regmap_device *rd;
+
+       pr_info(" %s\n", __func__);
+       rd = container_of(work, struct rt_regmap_device, rt_work.work);
+       rt_regmap_cache_sync(rd);
+}
+
+static int rt_chip_block_write(struct rt_regmap_device *rd, u32 reg,
+                              int bytes, const void *src)
+{
+       int ret;
+
+       if ((rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK) == RT_IO_BLK_ALL ||
+           (rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK) == RT_IO_BLK_CHIP)
+               return 0;
+
+       ret = rd->rops->write_device(rd->client, reg, bytes, src);
+
+       return ret;
+}
+
+static int rt_chip_block_read(struct rt_regmap_device *rd, u32 reg,
+                             int bytes, void *dst)
+{
+       int ret;
+
+       ret = rd->rops->read_device(rd->client, reg, bytes, dst);
+       return ret;
+}
+
+static int rt_cache_block_write(struct rt_regmap_device *rd, u32 reg,
+                               int bytes, const void *data)
+{
+       int i, j, reg_base = 0, count = 0, ret = 0, size = 0;
+       struct reg_index_offset rio;
+       unsigned char wdata[64];
+       unsigned char wri_data[128];
+       unsigned char blk_index;
+       rt_register_map_t rm;
+
+       memcpy(wdata, data, bytes);
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of range\n", reg);
+               return -EINVAL;
+       }
+
+       reg_base = 0;
+       rm = rd->props.rm[rio.index + reg_base];
+       while (bytes > 0) {
+               size = ((bytes <= (rm->size - rio.offset)) ?
+                                       bytes : rm->size - rio.offset);
+               if ((rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE) {
+                       ret = rt_chip_block_write(rd,
+                                                 rm->addr + rio.offset,
+                                       size,
+                                       &wdata[count]);
+                       count += size;
+               } else {
+                       blk_index = (rd->props.rt_regmap_mode &
+                                       RT_IO_BLK_MODE_MASK) >> 3;
+
+                       ret = rd->rt_block_write[blk_index]
+                                       (rd, rm, size, &rio, wdata, &count);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "rd->rt_block_write fail\n");
+                               goto ERR;
+                       }
+               }
+
+               if ((rm->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE)
+                       *(rd->cache_flag + rio.index + reg_base) = 1;
+
+               bytes -= size;
+               if (bytes <= 0)
+                       goto finished;
+               reg_base++;
+               rio.offset = 0;
+               rm = rd->props.rm[rio.index + reg_base];
+               if ((rio.index + reg_base) >= rd->props.register_num) {
+                       dev_err(&rd->dev, "over regmap size\n");
+                       goto ERR;
+               }
+       }
+finished:
+       if (rd->props.io_log_en) {
+               j = 0;
+               for (i = 0; i < count; i++)
+                       j += sprintf(wri_data + j, "%02x,", wdata[i]);
+               pr_info("RT_REGMAP [WRITE] reg0x%04x  [Data] 0x%s\n",
+                       reg, wri_data);
+       }
+       return 0;
+ERR:
+       return -EIO;
+}
+
+static int rt_asyn_cache_block_write(struct rt_regmap_device *rd, u32 reg,
+                                    int bytes, const void *data)
+{
+       int i, j, reg_base, count = 0, ret = 0, size = 0;
+       struct reg_index_offset rio;
+       unsigned char wdata[64];
+       unsigned char wri_data[128];
+       unsigned char blk_index;
+       rt_register_map_t rm;
+
+       memcpy(wdata, data, bytes);
+
+       cancel_delayed_work_sync(&rd->rt_work);
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of range\n", reg);
+               return -EINVAL;
+       }
+
+       reg_base = 0;
+       rm = rd->props.rm[rio.index + reg_base];
+       while (bytes > 0) {
+               size = ((bytes <= (rm->size - rio.offset)) ?
+                                       bytes : rm->size - rio.offset);
+               if ((rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE) {
+                       ret = rt_chip_block_write(rd,
+                                                 rm->addr + rio.offset,
+                                                 size, &wdata[count]);
+                       count += size;
+               } else {
+                       blk_index = (rd->props.rt_regmap_mode &
+                                       RT_IO_BLK_MODE_MASK) >> 3;
+                       ret = rd->rt_block_write[blk_index]
+                               (rd, rm, size, &rio, wdata, &count);
+               }
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rd->rt_block_write fail\n");
+                       goto ERR;
+               }
+
+               if ((rm->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE) {
+                       *(rd->cache_flag + rio.index + reg_base) = 1;
+                       rd->pending_event = 1;
+               }
+
+               bytes -= size;
+               if (bytes <= 0)
+                       goto finished;
+               reg_base++;
+               rm = rd->props.rm[rio.index + reg_base];
+               rio.offset = 0;
+               if ((rio.index + reg_base) >= rd->props.register_num) {
+                       dev_err(&rd->dev, "over regmap size\n");
+                       goto ERR;
+               }
+       }
+finished:
+       if (rd->props.io_log_en) {
+               j = 0;
+               for (i = 0; i < count; i++)
+                       j += sprintf(wri_data + j, "%02x,", wdata[i]);
+               pr_info("RT_REGMAP [WRITE] reg0x%04x  [Data] 0x%s\n",
+                       reg, wri_data);
+       }
+
+       schedule_delayed_work(&rd->rt_work, msecs_to_jiffies(1));
+       return 0;
+ERR:
+       return -EIO;
+}
+
+static int rt_block_write_blk_all(struct rt_regmap_device *rd,
+                                 struct rt_register *rm, int size,
+                                 const struct reg_index_offset *rio,
+                                 unsigned char *wdata, int *count)
+{
+       int cnt;
+
+       cnt = *count;
+       cnt += size;
+       *count = cnt;
+       return 0;
+}
+
+static int rt_block_write_blk_chip(struct rt_regmap_device *rd,
+                                  struct rt_register *rm, int size,
+                                  const struct reg_index_offset *rio,
+                                  unsigned char *wdata, int *count)
+{
+       int i, cnt;
+
+       cnt = *count;
+       for (i = rio->offset; i < rio->offset + size; i++) {
+               if ((rm->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE)
+                       rm->cache_data[i] =
+                               wdata[cnt] & rm->wbit_mask[i];
+               cnt++;
+       }
+       *count = cnt;
+       return 0;
+}
+
+static int rt_block_write_blk_cache(struct rt_regmap_device *rd,
+                                   struct rt_register *rm, int size,
+                                   const struct reg_index_offset *rio,
+                                   unsigned char *wdata, int *count)
+{
+       int ret, cnt;
+
+       cnt = *count;
+
+       ret = rt_chip_block_write(rd, rm->addr + rio->offset,
+                                 size, &wdata[cnt]);
+       if (ret < 0) {
+               dev_err(&rd->dev,
+                       "rt block write fail at 0x%02x\n",
+                       rm->addr + rio->offset);
+               return -EIO;
+       }
+       cnt += size;
+       *count = cnt;
+       return 0;
+}
+
+static int rt_block_write(struct rt_regmap_device *rd,
+                         struct rt_register *rm, int size,
+                         const struct reg_index_offset *rio,
+                         unsigned char *wdata, int *count)
+{
+       int i, ret, cnt, change = 0;
+
+       cnt = *count;
+
+       for (i = rio->offset; i < size + rio->offset; i++) {
+               if ((rm->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE) {
+                       if (rm->reg_type & RT_WR_ONCE) {
+                               if (rm->cache_data[i] !=
+                                       (wdata[cnt] & rm->wbit_mask[i]))
+                                       change++;
+                       }
+                       rm->cache_data[i] = wdata[cnt] & rm->wbit_mask[i];
+               }
+               cnt++;
+       }
+
+       if (!change && (rm->reg_type & RT_WR_ONCE))
+               goto finish;
+
+       if ((rd->props.rt_regmap_mode & RT_CACHE_MODE_MASK) ==
+                                               RT_CACHE_WR_THROUGH) {
+               ret = rt_chip_block_write(rd,
+                                         rm->addr + rio->offset,
+                                         size, rm->cache_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev,
+                               "rt block write fail at 0x%02x\n",
+                       rm->addr + rio->offset);
+                       return -EIO;
+               }
+       }
+
+finish:
+       *count = cnt;
+       return 0;
+}
+
+static int (*rt_block_map[])(struct rt_regmap_device *rd,
+                            struct rt_register *rm, int size,
+                            const struct reg_index_offset *rio,
+                            unsigned char *wdata, int *count) = {
+       &rt_block_write,
+       &rt_block_write_blk_all,
+       &rt_block_write_blk_cache,
+       &rt_block_write_blk_chip,
+};
+
+static int rt_cache_block_read(struct rt_regmap_device *rd, u32 reg,
+                              int bytes, void *dest)
+{
+       int i, ret, count = 0, reg_base = 0, total_bytes = 0;
+       struct reg_index_offset rio;
+       rt_register_map_t rm;
+       unsigned char data[100];
+       unsigned char tmp_data[32];
+
+       rio = find_register_index(rd, reg);
+       if (rio.index < 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of range\n", reg);
+               return -EINVAL;
+       }
+
+       rm = rd->props.rm[rio.index];
+
+       total_bytes += (rm->size - rio.offset);
+
+       for (i = rio.index + 1; i < rd->props.register_num; i++)
+               total_bytes += rd->props.rm[i]->size;
+
+       if (bytes > total_bytes) {
+               dev_err(&rd->dev, "out of cache map range\n");
+               return -EINVAL;
+       }
+
+       memcpy(data, &rm->cache_data[rio.offset], bytes);
+
+       if ((rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE) {
+               ret = rd->rops->read_device(rd->client,
+                               rm->addr, rm->size, tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev,
+                               "rt_regmap Error at 0x%02x\n",
+                       rm->addr);
+                       return -EIO;
+               }
+               for (i = rio.offset; i < rm->size; i++) {
+                       data[count] = tmp_data[i];
+                       count++;
+               }
+       } else {
+               count += (rm->size - rio.offset);
+       }
+
+       while (count < bytes) {
+               reg_base++;
+               rm = rd->props.rm[rio.index + reg_base];
+               if ((rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE) {
+                       ret = rd->rops->read_device(rd->client,
+                                       rm->addr, rm->size, &data[count]);
+                       if (ret < 0) {
+                               dev_err(&rd->dev,
+                                       "rt_regmap Error at 0x%02x\n",
+                                       rm->addr);
+                               return -EIO;
+                       }
+               }
+               count += rm->size;
+       }
+
+       if (rd->props.io_log_en)
+               pr_info("RT_REGMAP [READ] reg0x%04x\n", reg);
+
+       memcpy(dest, data, bytes);
+
+       return 0;
+}
+
+/* rt_regmap_cache_backup - back up all cache register value*/
+void rt_regmap_cache_backup(struct rt_regmap_device *rd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       int i;
+
+       down(&rd->semaphore);
+       for (i = 0; i < rd->props.register_num; i++)
+               if ((rm[i]->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE)
+                       *(rd->cache_flag + i) = 1;
+       rd->pending_event = 1;
+       up(&rd->semaphore);
+}
+EXPORT_SYMBOL(rt_regmap_cache_backup);
+
+/* _rt_regmap_reg_write - write data to specific register map
+ * only support 1, 2, 4 bytes regisetr map
+ * @rd: rt_regmap_device pointer.
+ * @rrd: rt_reg_data pointer.
+ */
+int _rt_regmap_reg_write(struct rt_regmap_device *rd,
+                        struct rt_reg_data *rrd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       struct reg_index_offset rio;
+       int ret, tmp_data;
+
+       rio = find_register_index(rd, rrd->reg);
+       if (rio.index < 0 || rio.offset != 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of regmap\n", rrd->reg);
+               return -EINVAL;
+       }
+
+       down(&rd->semaphore);
+       switch (rm[rio.index]->size) {
+       case 1:
+               ret = rd->regmap_ops.regmap_block_write(rd,
+                               rrd->reg, 1, &rrd->rt_data.data_u8);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       up(&rd->semaphore);
+                       return -EIO;
+               }
+               break;
+       case 2:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be16_to_cpu(rrd->rt_data.data_u32);
+               ret = rd->regmap_ops.regmap_block_write(rd,
+                               rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       up(&rd->semaphore);
+                       return -EIO;
+               }
+               break;
+       case 3:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN) {
+                       tmp_data = be32_to_cpu(rrd->rt_data.data_u32);
+                       tmp_data >>= 8;
+               }
+               ret = rd->regmap_ops.regmap_block_write(rd,
+                       rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       up(&rd->semaphore);
+                       return -EIO;
+               }
+               break;
+       case 4:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be32_to_cpu(rrd->rt_data.data_u32);
+               ret = rd->regmap_ops.regmap_block_write(rd,
+                       rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       up(&rd->semaphore);
+                       return -EIO;
+               }
+               break;
+       default:
+               dev_err(&rd->dev,
+                       "Failed: only support 1~4 bytes regmap write\n");
+               break;
+       }
+       up(&rd->semaphore);
+       return 0;
+}
+EXPORT_SYMBOL(_rt_regmap_reg_write);
+
+/* _rt_asyn_regmap_reg_write - asyn write data to specific register map*/
+int _rt_asyn_regmap_reg_write(struct rt_regmap_device *rd,
+                             struct rt_reg_data *rrd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       struct reg_index_offset rio;
+       int ret, tmp_data;
+
+       rio = find_register_index(rd, rrd->reg);
+       if (rio.index < 0 || rio.offset != 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of regmap\n", rrd->reg);
+               return -EINVAL;
+       }
+
+       down(&rd->semaphore);
+       switch (rm[rio.index]->size) {
+       case 1:
+               ret = rt_asyn_cache_block_write(rd,
+                                               rrd->reg, 1,
+                                               &rrd->rt_data.data_u8);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       ret = -EIO;
+                       goto err_regmap_write;
+               }
+               break;
+       case 2:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be16_to_cpu(rrd->rt_data.data_u32);
+               ret = rt_asyn_cache_block_write(rd,
+                                               rrd->reg,
+                                               rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       ret = -EIO;
+                       goto err_regmap_write;
+               }
+               break;
+       case 3:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN) {
+                       tmp_data = be32_to_cpu(rrd->rt_data.data_u32);
+                       tmp_data >>= 8;
+               }
+               ret = rt_asyn_cache_block_write(rd,
+                                               rrd->reg,
+                                               rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       ret = -EIO;
+                       goto err_regmap_write;
+               }
+               break;
+       case 4:
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be32_to_cpu(rrd->rt_data.data_u32);
+               ret = rt_asyn_cache_block_write(rd,
+                                               rrd->reg,
+                                               rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block write fail\n");
+                       ret = -EIO;
+                       goto err_regmap_write;
+               }
+               break;
+       default:
+               dev_err(&rd->dev,
+                       "Failed: only support 1~4 bytes regmap write\n");
+               break;
+       }
+       up(&rd->semaphore);
+       return 0;
+err_regmap_write:
+       up(&rd->semaphore);
+       return ret;
+}
+EXPORT_SYMBOL(_rt_asyn_regmap_reg_write);
+
+/* _rt_regmap_update_bits - assign bits specific register map */
+int _rt_regmap_update_bits(struct rt_regmap_device *rd,
+                          struct rt_reg_data *rrd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       struct reg_index_offset rio;
+       int ret, new, old;
+       bool change = false;
+
+       rio = find_register_index(rd, rrd->reg);
+       if (rio.index < 0 || rio.offset != 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of regmap\n", rrd->reg);
+               return -EINVAL;
+       }
+
+       down(&rd->semaphore);
+       switch (rm[rio.index]->size) {
+       case 1:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                                       rrd->reg, 1, &old);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_update_bits;
+               }
+               new = (old & ~(rrd->mask)) | (rrd->rt_data.data_u8 & rrd->mask);
+               change = old != new;
+
+               if (((rm[rio.index]->reg_type & RT_WR_ONCE) && change) ||
+                   !(rm[rio.index]->reg_type & RT_WR_ONCE)) {
+                       ret = rd->regmap_ops.regmap_block_write(rd,
+                                                       rrd->reg, 1, &new);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "rt regmap block write fail\n");
+                               goto err_update_bits;
+                       }
+               }
+               break;
+       case 2:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                               rrd->reg, rm[rio.index]->size, &old);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_update_bits;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       old = be16_to_cpu(old);
+
+               new = (old & ~(rrd->mask)) |
+                               (rrd->rt_data.data_u16 & rrd->mask);
+
+               change = old != new;
+               if (((rm[rio.index]->reg_type & RT_WR_ONCE) && change) ||
+                   !(rm[rio.index]->reg_type & RT_WR_ONCE)) {
+                       if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                               new = be16_to_cpu(new);
+                       ret = rd->regmap_ops.regmap_block_write(rd,
+                               rrd->reg, rm[rio.index]->size, &new);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "rt regmap block write fail\n");
+                               goto err_update_bits;
+                       }
+               }
+               break;
+       case 3:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                               rrd->reg, rm[rio.index]->size, &old);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_update_bits;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN) {
+                       old = be32_to_cpu(old);
+                       old >>= 8;
+               }
+
+               new = (old & ~(rrd->mask)) |
+                               (rrd->rt_data.data_u32 & rrd->mask);
+               change = old != new;
+               if (((rm[rio.index]->reg_type & RT_WR_ONCE) && change) ||
+                   !(rm[rio.index]->reg_type & RT_WR_ONCE)) {
+                       if (rd->props.rt_format == RT_LITTLE_ENDIAN) {
+                               new <<= 8;
+                               new = be32_to_cpu(new);
+                       }
+                       ret = rd->regmap_ops.regmap_block_write(rd,
+                               rrd->reg, rm[rio.index]->size, &new);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "rt regmap block write fail\n");
+                               goto err_update_bits;
+                       }
+               }
+               break;
+       case 4:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                               rrd->reg, rm[rio.index]->size, &old);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_update_bits;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       old = be32_to_cpu(old);
+
+               new = (old & ~(rrd->mask)) |
+                               (rrd->rt_data.data_u32 & rrd->mask);
+               change = old != new;
+               if (((rm[rio.index]->reg_type & RT_WR_ONCE) && change) ||
+                   !(rm[rio.index]->reg_type & RT_WR_ONCE)) {
+                       if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                               new = be32_to_cpu(new);
+                       ret = rd->regmap_ops.regmap_block_write(rd,
+                               rrd->reg, rm[rio.index]->size, &new);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "rt regmap block write fail\n");
+                               goto err_update_bits;
+                       }
+               }
+               break;
+       default:
+               dev_err(&rd->dev,
+                       "Failed: only support 1~4 bytes regmap write\n");
+               break;
+       }
+       up(&rd->semaphore);
+       return change;
+err_update_bits:
+       up(&rd->semaphore);
+       return ret;
+}
+EXPORT_SYMBOL(_rt_regmap_update_bits);
+
+/* rt_regmap_block_write - block write data to register
+ * @rd: rt_regmap_device pointer
+ * @reg: register address
+ * bytes: leng for write
+ * src: source data
+ */
+int rt_regmap_block_write(struct rt_regmap_device *rd, u32 reg,
+                         int bytes, const void *src)
+{
+       int ret;
+
+       down(&rd->semaphore);
+       ret = rd->regmap_ops.regmap_block_write(rd, reg, bytes, src);
+       up(&rd->semaphore);
+       return ret;
+};
+EXPORT_SYMBOL(rt_regmap_block_write);
+
+/* rt_asyn_regmap_block_write - asyn block write*/
+int rt_asyn_regmap_block_write(struct rt_regmap_device *rd, u32 reg,
+                              int bytes, const void *src)
+{
+       int ret;
+
+       down(&rd->semaphore);
+       ret = rt_asyn_cache_block_write(rd, reg, bytes, src);
+       up(&rd->semaphore);
+       return ret;
+};
+EXPORT_SYMBOL(rt_asyn_regmap_block_write);
+
+/* rt_regmap_block_read - block read data form register
+ * @rd: rt_regmap_device pointer
+ * @reg: register address
+ * @bytes: read length
+ * @dst: destination for read data
+ */
+int rt_regmap_block_read(struct rt_regmap_device *rd, u32 reg,
+                        int bytes, void *dst)
+{
+       int ret;
+
+       down(&rd->semaphore);
+       ret = rd->regmap_ops.regmap_block_read(rd, reg, bytes, dst);
+       up(&rd->semaphore);
+       return ret;
+};
+EXPORT_SYMBOL(rt_regmap_block_read);
+
+/* _rt_regmap_reg_read - register read for specific register map
+ * only support 1, 2, 4 bytes register map.
+ * @rd: rt_regmap_device pointer.
+ * @rrd: rt_reg_data pointer.
+ */
+int _rt_regmap_reg_read(struct rt_regmap_device *rd, struct rt_reg_data *rrd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       struct reg_index_offset rio;
+       int ret, tmp_data = 0;
+
+       rio = find_register_index(rd, rrd->reg);
+       if (rio.index < 0 || rio.offset != 0) {
+               dev_err(&rd->dev, "reg 0x%02x is out of regmap\n", rrd->reg);
+               return -EINVAL;
+       }
+
+       down(&rd->semaphore);
+       switch (rm[rio.index]->size) {
+       case 1:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                       rrd->reg, 1, &rrd->rt_data.data_u8);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_regmap_reg_read;
+               }
+               break;
+       case 2:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                       rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_regmap_reg_read;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be16_to_cpu(tmp_data);
+               rrd->rt_data.data_u16 = tmp_data;
+               break;
+       case 3:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                       rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_regmap_reg_read;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be32_to_cpu(tmp_data);
+               rrd->rt_data.data_u32 = (tmp_data >> 8);
+               break;
+       case 4:
+               ret = rd->regmap_ops.regmap_block_read(rd,
+                       rrd->reg, rm[rio.index]->size, &tmp_data);
+               if (ret < 0) {
+                       dev_err(&rd->dev, "rt regmap block read fail\n");
+                       goto err_regmap_reg_read;
+               }
+               if (rd->props.rt_format == RT_LITTLE_ENDIAN)
+                       tmp_data = be32_to_cpu(tmp_data);
+               rrd->rt_data.data_u32 = tmp_data;
+               break;
+       default:
+               dev_err(&rd->dev,
+                       "Failed: only support 1~4 bytes regmap read\n");
+               break;
+       }
+       up(&rd->semaphore);
+       return 0;
+err_regmap_reg_read:
+       up(&rd->semaphore);
+       return ret;
+}
+EXPORT_SYMBOL(_rt_regmap_reg_read);
+
+void rt_cache_getlasterror(struct rt_regmap_device *rd, char *buf)
+{
+       down(&rd->semaphore);
+       sprintf(buf, "%s\n", rd->err_msg);
+       up(&rd->semaphore);
+}
+EXPORT_SYMBOL(rt_cache_getlasterror);
+
+void rt_cache_clrlasterror(struct rt_regmap_device *rd)
+{
+       down(&rd->semaphore);
+       rd->error_occurred = 0;
+       sprintf(rd->err_msg, "%s", "No Error");
+       up(&rd->semaphore);
+}
+EXPORT_SYMBOL(rt_cache_clrlasterror);
+
+/* initialize cache data from rt_register */
+int rt_regmap_cache_init(struct rt_regmap_device *rd)
+{
+       int i, j, ret, bytes_num = 0, count = 0;
+       const rt_register_map_t *rm = rd->props.rm;
+
+       dev_info(&rd->dev, "rt register cache data init\n");
+
+       down(&rd->semaphore);
+       rd->cache_flag = devm_kzalloc(&rd->dev,
+               rd->props.register_num * sizeof(int), GFP_KERNEL);
+
+       if (!rd->props.group) {
+               rd->props.group = devm_kzalloc(&rd->dev,
+                               sizeof(rd->props.group), GFP_KERNEL);
+               rd->props.group[0].start = 0x00;
+               rd->props.group[0].end = 0xffff;
+               rd->props.group[0].mode = RT_1BYTE_MODE;
+       }
+
+       /* calculate maxima size for showing on regs debugfs node*/
+       rd->part_size_limit = 0;
+       for (i = 0; i < rd->props.register_num; i++) {
+               if (!rm[i]->cache_data)
+                       bytes_num += rm[i]->size;
+               if (rm[i]->size > rd->part_size_limit &&
+                   (rm[i]->reg_type & RT_REG_TYPE_MASK) != RT_RESERVE)
+                       rd->part_size_limit = rm[i]->size;
+       }
+       rd->part_size_limit = 400 / ((rd->part_size_limit - 1) * 3 + 5);
+
+       rd->alloc_data =
+           devm_kzalloc(&rd->dev,
+                        bytes_num * sizeof(unsigned char), GFP_KERNEL);
+       if (!rd->alloc_data) {
+               pr_info("tmp data memory allocate fail\n");
+               goto mem_err;
+       }
+
+       /* reload cache data from real chip */
+       for (i = 0; i < rd->props.register_num; i++) {
+               if (!rm[i]->cache_data) {
+                       rm[i]->cache_data = rd->alloc_data + count;
+                       count += rm[i]->size;
+                       if ((rm[i]->reg_type & RT_REG_TYPE_MASK) !=
+                           RT_VOLATILE) {
+                               ret = rd->rops->read_device(rd->client,
+                                               rm[i]->addr, rm[i]->size,
+                                                       rm[i]->cache_data);
+                               if (ret < 0) {
+                                       dev_err(&rd->dev, "chip read fail\n");
+                                       goto io_err;
+                               }
+                       } else {
+                               memset(rm[i]->cache_data, 0x00, rm[i]->size);
+                       }
+               }
+               *(rd->cache_flag + i) = 0;
+       }
+
+       /* set 0xff writeable mask for NORMAL and RESERVE type */
+       for (i = 0; i < rd->props.register_num; i++) {
+               if ((rm[i]->reg_type & RT_REG_TYPE_MASK) == RT_NORMAL ||
+                   (rm[i]->reg_type & RT_REG_TYPE_MASK) == RT_RESERVE) {
+                       for (j = 0; j < rm[i]->size; j++)
+                               rm[i]->wbit_mask[j] = 0xff;
+               }
+       }
+
+       rd->cache_inited = 1;
+       dev_info(&rd->dev, "cache cata init successfully\n");
+       up(&rd->semaphore);
+       return 0;
+mem_err:
+       up(&rd->semaphore);
+       return -ENOMEM;
+io_err:
+       up(&rd->semaphore);
+       return -EIO;
+}
+EXPORT_SYMBOL(rt_regmap_cache_init);
+
+/* rt_regmap_cache_reload - reload cache valuew from real chip*/
+int rt_regmap_cache_reload(struct rt_regmap_device *rd)
+{
+       int i, ret;
+       const rt_register_map_t *rm = rd->props.rm;
+
+       down(&rd->semaphore);
+       for (i = 0; i < rd->props.register_num; i++) {
+               if ((rm[i]->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE) {
+                       ret = rd->rops->read_device(rd->client, rm[i]->addr,
+                                               rm[i]->size, rm[i]->cache_data);
+                       if (ret < 0) {
+                               dev_err(&rd->dev, "i2c read fail\n");
+                               goto io_err;
+                       }
+                       *(rd->cache_flag + i) = 0;
+               }
+       }
+       rd->pending_event = 0;
+       up(&rd->semaphore);
+       dev_info(&rd->dev, "cache data reload\n");
+       return 0;
+
+io_err:
+       up(&rd->semaphore);
+       return -EIO;
+}
+EXPORT_SYMBOL(rt_regmap_cache_reload);
+
+/* rt_regmap_add_debubfs - add user own debugfs node
+ * @rd: rt_regmap_devcie pointer.
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have.
+ * @data: a pointer to something that the caller will want to get to later on.
+ *     The inode.i_private pointer will point this value on the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ *     this file.
+ */
+int rt_regmap_add_debugfs(struct rt_regmap_device *rd, const char *name,
+                         umode_t mode, void *data,
+                         const struct file_operations *fops)
+{
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *den;
+
+       den = debugfs_create_file(name, mode, rd->rt_den, data, fops);
+       if (!den)
+               return -EINVAL;
+#endif /*CONFIG_DEBUG_FS*/
+       return 0;
+}
+EXPORT_SYMBOL(rt_regmap_add_debugfs);
+
+/* release cache data*/
+static void rt_regmap_cache_release(struct rt_regmap_device *rd)
+{
+       int i;
+       const rt_register_map_t *rm = rd->props.rm;
+
+       dev_info(&rd->dev, "cache data release\n");
+       for (i = 0; i < rd->props.register_num; i++)
+               rm[i]->cache_data = NULL;
+       devm_kfree(&rd->dev, rd->alloc_data);
+       if (rd->cache_flag)
+               devm_kfree(&rd->dev, rd->cache_flag);
+       rd->cache_inited = 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rt_check_dump_config_file(struct rt_regmap_device *rd,
+                                     long int *reg_dump, int *cnt, char *type)
+{
+       char *token, *buf, *tmp_type;
+       char PATH[64];
+       mm_segment_t fs;
+       struct file *fp;
+       int ret, tmp_cnt = 0;
+
+       buf = devm_kzalloc(&rd->dev, 64 * sizeof(char), GFP_KERNEL);
+       sprintf(PATH, "/sdcard/%s_dump_config.txt", rd->props.name);
+       fp = filp_open(PATH, O_RDONLY, 0);
+       if (IS_ERR(fp)) {
+               pr_info("There is no Dump config file in sdcard\n");
+               devm_kfree(&rd->dev, buf);
+       } else {
+               fs = get_fs();
+               set_fs(get_ds());
+               fp->f_op->read(fp, buf, 64, &fp->f_pos);
+               set_fs(fs);
+
+               token = strsep(&buf, " ");
+               tmp_type = token;
+               while (token) {
+                       ret = kstrtoul(token, 16, &reg_dump[tmp_cnt]);
+                       if (ret == 0)
+                               tmp_cnt++;
+                       token = strsep(&buf, " ");
+               }
+               filp_close(fp, NULL);
+               *cnt = tmp_cnt;
+               memcpy(type, tmp_type, 16);
+               devm_kfree(&rd->dev, buf);
+       }
+}
+
+static void rt_show_regs(struct rt_regmap_device *rd, struct seq_file *seq_file)
+{
+       int i = 0, k = 0, ret, count = 0, cnt = 0;
+       unsigned char regval[512];
+       long int reg_dump[64] = {0};
+       const rt_register_map_t *rm = rd->props.rm;
+       char type[16];
+
+       rt_check_dump_config_file(rd, reg_dump, &cnt, type);
+       down(&rd->semaphore);
+       for (i = 0; i < rd->props.register_num; i++) {
+               ret = rd->regmap_ops.regmap_block_read(rd, rm[i]->addr,
+                                               rm[i]->size, &regval[count]);
+               count += rm[i]->size;
+               if (ret < 0) {
+                       dev_err(&rd->dev, "regmap block read fail\n");
+                       if (rd->error_occurred) {
+                               sprintf(rd->err_msg + strlen(rd->err_msg),
+                                       "Error block read fail at 0x%02x\n",
+                               rm[i]->addr);
+                       } else {
+                               sprintf(rd->err_msg,
+                                       "Error block read fail at 0x%02x\n",
+                               rm[i]->addr);
+                               rd->error_occurred = 1;
+                       }
+                       goto err_show_regs;
+               }
+
+               if ((rm[i]->reg_type & RT_REG_TYPE_MASK) != RT_RESERVE) {
+                       seq_printf(seq_file, "reg0x%02x:0x", rm[i]->addr);
+                       for (k = 0; k < rm[i]->size; k++)
+                               seq_printf(seq_file, "%02x,",
+                                          regval[count - rm[i]->size + k]);
+                       seq_puts(seq_file, "\n");
+               } else {
+                       seq_printf(seq_file,
+                                  "reg0x%02x:reserve\n", rm[i]->addr);
+               }
+       }
+err_show_regs:
+       up(&rd->semaphore);
+}
+
+static int general_read(struct seq_file *seq_file, void *_data)
+{
+       struct rt_debug_st *st = (struct rt_debug_st *)seq_file->private;
+       struct rt_regmap_device *rd = st->info;
+       rt_register_map_t rm;
+       char lbuf[900];
+       unsigned char reg_data[24] = { 0 };
+       unsigned char data;
+       int i = 0, rc = 0, size = 0;
+
+       lbuf[0] = '\0';
+       switch (st->id) {
+       case RT_DBG_REG:
+               seq_printf(seq_file, "0x%04x\n", rd->dbg_data.reg_addr);
+               break;
+       case RT_DBG_DATA:
+               if (rd->dbg_data.reg_size == 0)
+                       rd->dbg_data.reg_size = 1;
+
+               size = rd->dbg_data.reg_size;
+
+               if (rd->dbg_data.rio.index == -1) {
+                       down(&rd->semaphore);
+                       rc = rt_chip_block_read(rd, rd->dbg_data.reg_addr,
+                                               size, reg_data);
+                       up(&rd->semaphore);
+                       if (rc < 0) {
+                               seq_puts(seq_file, "invalid read\n");
+                               break;
+                       }
+                       goto hiden_read;
+               }
+
+               rm = rd->props.rm[rd->dbg_data.rio.index];
+
+               down(&rd->semaphore);
+               rc = rd->regmap_ops.regmap_block_read(rd,
+                       rd->dbg_data.reg_addr, size, reg_data);
+               up(&rd->semaphore);
+               if (rc < 0) {
+                       seq_puts(seq_file, "invalid read\n");
+                       break;
+               }
+
+hiden_read:
+               if (&reg_data[i]) {
+                       seq_puts(seq_file, "0x");
+                       for (i = 0; i < size; i++)
+                               seq_printf(seq_file, "%02x,", reg_data[i]);
+                       seq_puts(seq_file, "\n");
+               }
+               break;
+       case RT_DBG_ERROR:
+               seq_puts(seq_file, "======== Error Message ========\n");
+               if (!rd->error_occurred)
+                       seq_puts(seq_file, "No Error\n");
+               else
+                       seq_printf(seq_file, rd->err_msg);
+               break;
+       case RT_DBG_REGS:
+               rt_show_regs(rd, seq_file);
+               break;
+       case RT_DBG_NAME:
+               seq_printf(seq_file, "%s\n", rd->props.aliases);
+               break;
+       case RT_DBG_SIZE:
+               seq_printf(seq_file, "%d\n", rd->dbg_data.reg_size);
+               break;
+       case RT_DBG_BLOCK:
+               data = rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK;
+               if (data == RT_IO_PASS_THROUGH)
+                       seq_puts(seq_file, "0 => IO_PASS_THROUGH\n");
+               else if (data == RT_IO_BLK_ALL)
+                       seq_puts(seq_file, "1 => IO_BLK_ALL\n");
+               else if (data == RT_IO_BLK_CACHE)
+                       seq_puts(seq_file, "2 => IO_BLK_CACHE\n");
+               else if (data == RT_IO_BLK_CHIP)
+                       seq_puts(seq_file, "3 => IO_BLK_CHIP\n");
+               break;
+       case RT_DBG_SLAVE_ADDR:
+               {
+                       struct i2c_client *i2c = rd->client;
+
+                       seq_printf(seq_file, "0x%02x\n", i2c->addr);
+               }
+               break;
+       case RT_SUPPORT_MODE:
+               seq_puts(seq_file, " == BLOCK MODE ==\n");
+               seq_puts(seq_file, "0 => IO_PASS_THROUGH\n");
+               seq_puts(seq_file, "1 => IO_BLK_ALL\n");
+               seq_puts(seq_file, "2 => IO_BLK_CHIP\n");
+               seq_puts(seq_file, "3 => IO_BLK_CACHE\n");
+               seq_puts(seq_file, " == CACHE MODE ==\n");
+               seq_puts(seq_file, "0 => CACHE_WR_THROUGH\n");
+               seq_puts(seq_file, "1 => CACHE_WR_BACK\n");
+               seq_puts(seq_file, "2 => CACHE_DISABLE\n");
+
+               break;
+       case RT_DBG_IO_LOG:
+               seq_printf(seq_file, "%d\n", rd->props.io_log_en);
+               break;
+       case RT_DBG_CACHE_MODE:
+               data = rd->props.rt_regmap_mode & RT_CACHE_MODE_MASK;
+               if (data == RT_CACHE_WR_THROUGH)
+                       seq_printf(seq_file, "%s",
+                                  "0 => Cache Write Through\n");
+               else if (data == RT_CACHE_WR_BACK)
+                       seq_printf(seq_file, "%s", "1 => Cache Write Back\n");
+               else if (data == RT_CACHE_DISABLE)
+                       seq_printf(seq_file, "%s", "2 => Cache Disable\n");
+               break;
+       case RT_DBG_REG_SIZE:
+               size = rt_get_regsize(rd, rd->dbg_data.reg_addr);
+               if (size < 0)
+                       seq_printf(seq_file, "%d\n", 0);
+               else
+                       seq_printf(seq_file, "%d\n", size);
+               break;
+       }
+       return 0;
+}
+
+static int general_open(struct inode *inode, struct file *file)
+{
+       if (file->f_mode & FMODE_READ)
+               return single_open(file, general_read, inode->i_private);
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t general_write(struct file *file, const char __user *ubuf,
+                            size_t count, loff_t *ppos)
+{
+       struct rt_debug_st *st = file->private_data;
+       struct rt_regmap_device *rd = st->info;
+       struct reg_index_offset rio;
+       long int param[5];
+       unsigned char reg_data[24] = { 0 };
+       int rc, size = 0;
+       char lbuf[128];
+
+       if (count > sizeof(lbuf) - 1)
+               return -EFAULT;
+
+       rc = copy_from_user(lbuf, ubuf, count);
+       if (rc)
+               return -EFAULT;
+
+       lbuf[count] = '\0';
+
+       switch (st->id) {
+       case RT_DBG_REG:
+               rc = get_parameters(lbuf, param, 1);
+               rio = find_register_index(rd, param[0]);
+               down(&rd->semaphore);
+               if (rio.index < 0) {
+                       pr_info("this is an invalid or hiden register\n");
+                       rd->dbg_data.reg_addr = param[0];
+                       rd->dbg_data.rio.index = -1;
+               } else {
+                       rd->dbg_data.rio = rio;
+                       rd->dbg_data.reg_addr = param[0];
+               }
+               up(&rd->semaphore);
+               break;
+       case RT_DBG_DATA:
+               if (rd->dbg_data.reg_size == 0)
+                       rd->dbg_data.reg_size = 1;
+
+               if (rd->dbg_data.rio.index == -1) {
+                       size = rd->dbg_data.reg_size;
+                       if ((size - 1) * 3 + 5 != count) {
+                               dev_err(&rd->dev, "wrong input length\n");
+                               if (rd->error_occurred) {
+                                       sprintf(rd->err_msg +
+                                               strlen(rd->err_msg),
+                                               "Error, wrong input length\n");
+                               } else {
+                                       sprintf(rd->err_msg,
+                                               "Error, wrong input length\n");
+                                       rd->error_occurred = 1;
+                               }
+                               return -EINVAL;
+                       }
+
+                       rc = get_datas((char *)ubuf, count, reg_data, size);
+                       if (rc < 0) {
+                               dev_err(&rd->dev, "get datas fail\n");
+                               if (rd->error_occurred) {
+                                       sprintf(rd->err_msg +
+                                       strlen(rd->err_msg),
+                                       "Error, get datas fail\n");
+                               } else {
+                                       sprintf(rd->err_msg,
+                                               "Error, get datas fail\n");
+                                       rd->error_occurred = 1;
+                               }
+                               return -EINVAL;
+                       }
+                       down(&rd->semaphore);
+                       rc = rt_chip_block_write(rd, rd->dbg_data.reg_addr,
+                                                size, reg_data);
+                       up(&rd->semaphore);
+                       if (rc < 0) {
+                               dev_err(&rd->dev, "chip block write fail\n");
+                               if (rd->error_occurred) {
+                                       sprintf(rd->err_msg +
+                                       strlen(rd->err_msg),
+                                               "Error chip block write fail at 0x%02x\n",
+                                       rd->dbg_data.reg_addr);
+                               } else {
+                                       sprintf(rd->err_msg,
+                                               "Error chip block write fail at 0x%02x\n",
+                                       rd->dbg_data.reg_addr);
+                                       rd->error_occurred = 1;
+                               }
+                               return -EIO;
+                       }
+                       break;
+               }
+
+               size = rd->dbg_data.reg_size;
+
+               if ((size - 1) * 3 + 5 != count) {
+                       dev_err(&rd->dev, "wrong input length\n");
+                       if (rd->error_occurred) {
+                               sprintf(rd->err_msg + strlen(rd->err_msg),
+                                       "Error, wrong input length\n");
+                       } else {
+                               sprintf(rd->err_msg,
+                                       "Error, wrong input length\n");
+                               rd->error_occurred = 1;
+                       }
+                       return -EINVAL;
+               }
+
+               rc = get_datas((char *)ubuf, count, reg_data, size);
+               if (rc < 0) {
+                       dev_err(&rd->dev, "get datas fail\n");
+                       if (rd->error_occurred) {
+                               sprintf(rd->err_msg + strlen(rd->err_msg),
+                                       "Error, get datas fail\n");
+                       } else {
+                               sprintf(rd->err_msg,
+                                       "Error, get datas fail\n");
+                               rd->error_occurred = 1;
+                       }
+                       return -EINVAL;
+               }
+
+               down(&rd->semaphore);
+               rc = rd->regmap_ops.regmap_block_write(rd,
+                               rd->dbg_data.reg_addr, size, reg_data);
+               up(&rd->semaphore);
+               if (rc < 0) {
+                       dev_err(&rd->dev, "regmap block write fail\n");
+                       if (rd->error_occurred) {
+                               sprintf(rd->err_msg + strlen(rd->err_msg),
+                                       "Error regmap block write fail at 0x%02x\n",
+                               rd->dbg_data.reg_addr);
+                       } else {
+                               sprintf(rd->err_msg,
+                                       "Error regmap block write fail at 0x%02x\n",
+                               rd->dbg_data.reg_addr);
+                               rd->error_occurred = 1;
+                       }
+                       return -EIO;
+               }
+
+               break;
+       case RT_DBG_SYNC:
+               rc = get_parameters(lbuf, param, 1);
+               if (param[0])
+                       rt_regmap_cache_sync(rd);
+               break;
+       case RT_DBG_ERROR:
+               rc = get_parameters(lbuf, param, 1);
+               if (param[0])
+                       rt_cache_clrlasterror(rd);
+               break;
+       case RT_DBG_SIZE:
+               rc = get_parameters(lbuf, param, 1);
+               if (param[0] >= 0) {
+                       down(&rd->semaphore);
+                       rd->dbg_data.reg_size = param[0];
+                       up(&rd->semaphore);
+               } else {
+                       if (rd->error_occurred) {
+                               sprintf(rd->err_msg + strlen(rd->err_msg),
+                                       "Error, size must > 0\n");
+                       } else {
+                               sprintf(rd->err_msg,
+                                       "Error, size must > 0\n");
+                               rd->error_occurred = 1;
+                       }
+                       return -EINVAL;
+               }
+               break;
+       case RT_DBG_BLOCK:
+               rc = get_parameters(lbuf, param, 1);
+               if (param[0] < 0)
+                       param[0] = 0;
+               else if (param[0] > 3)
+                       param[0] = 3;
+
+               param[0] <<= 3;
+
+               down(&rd->semaphore);
+               rd->props.rt_regmap_mode &= ~RT_IO_BLK_MODE_MASK;
+               rd->props.rt_regmap_mode |= param[0];
+               up(&rd->semaphore);
+               if (param[0] == RT_IO_PASS_THROUGH)
+                       rt_regmap_cache_sync(rd);
+               break;
+       case RT_DBG_IO_LOG:
+               rc = get_parameters(lbuf, param, 1);
+               down(&rd->semaphore);
+               if (!param[0])
+                       rd->props.io_log_en = 0;
+               else
+                       rd->props.io_log_en = 1;
+               up(&rd->semaphore);
+               break;
+       case RT_DBG_CACHE_MODE:
+               rc = get_parameters(lbuf, param, 1);
+               if (param[0] < 0)
+                       param[0] = 0;
+               else if (param[0] > 2)
+                       param[0] = 2;
+               param[0] <<= 1;
+
+               if (param[0] == RT_CACHE_WR_THROUGH) {
+                       rt_regmap_cache_reload(rd);
+                       rd->regmap_ops.regmap_block_write =
+                                               rt_cache_block_write;
+                       rd->regmap_ops.regmap_block_read = &rt_cache_block_read;
+               } else if (param[0] == RT_CACHE_WR_BACK) {
+                       rt_regmap_cache_reload(rd);
+                       rd->regmap_ops.regmap_block_write =
+                                               rt_asyn_cache_block_write;
+                       rd->regmap_ops.regmap_block_read = &rt_cache_block_read;
+               } else if (param[0] == RT_CACHE_DISABLE) {
+                       rd->regmap_ops.regmap_block_write =
+                                                       rt_chip_block_write;
+                       rd->regmap_ops.regmap_block_read = rt_chip_block_read;
+               }
+
+               rd->props.rt_regmap_mode &= ~RT_CACHE_MODE_MASK;
+               rd->props.rt_regmap_mode |= param[0];
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static int general_release(struct inode *inode, struct file *file)
+{
+       if (file->f_mode & FMODE_READ)
+               return single_release(inode, file);
+       return 0;
+}
+
+static const struct file_operations general_ops = {
+       .owner = THIS_MODULE,
+       .open = general_open,
+       .write = general_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = general_release,
+};
+
+/* create general debugfs node */
+static void rt_create_general_debug(struct rt_regmap_device *rd,
+                                   struct dentry *dir)
+{
+       rd->rtdbg_st[0].info = rd;
+       rd->rtdbg_st[0].id = RT_DBG_REG;
+       rd->rt_debug_file[0] = debugfs_create_file("reg_addr",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[0],
+                                                  &general_ops);
+       rd->rtdbg_st[1].info = rd;
+       rd->rtdbg_st[1].id = RT_DBG_DATA;
+       rd->rt_debug_file[1] = debugfs_create_file("data",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[1],
+                                                  &general_ops);
+
+       rd->rtdbg_st[2].info = rd;
+       rd->rtdbg_st[2].id = RT_DBG_REGS;
+       rd->rt_debug_file[2] = debugfs_create_file("regs",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[2],
+                                                  &general_ops);
+
+       rd->rtdbg_st[3].info = rd;
+       rd->rtdbg_st[3].id = RT_DBG_SYNC;
+       rd->rt_debug_file[3] = debugfs_create_file("sync",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[3],
+                                                  &general_ops);
+
+       rd->rtdbg_st[4].info = rd;
+       rd->rtdbg_st[4].id = RT_DBG_ERROR;
+       rd->rt_debug_file[4] = debugfs_create_file("Error",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[4],
+                                                  &general_ops);
+
+       rd->rtdbg_st[5].info = rd;
+       rd->rtdbg_st[5].id = RT_DBG_NAME;
+       rd->rt_debug_file[5] = debugfs_create_file("name",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[5],
+                                                  &general_ops);
+
+       rd->rtdbg_st[6].info = rd;
+       rd->rtdbg_st[6].id = RT_DBG_BLOCK;
+       rd->rt_debug_file[6] = debugfs_create_file("block",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[6],
+                                                  &general_ops);
+
+       rd->rtdbg_st[7].info = rd;
+       rd->rtdbg_st[7].id = RT_DBG_SIZE;
+       rd->rt_debug_file[7] = debugfs_create_file("size",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[7],
+                                                  &general_ops);
+
+       rd->rtdbg_st[8].info = rd;
+       rd->rtdbg_st[8].id = RT_DBG_SLAVE_ADDR;
+       rd->rt_debug_file[8] = debugfs_create_file("slave_addr",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)
+                                                  &rd->rtdbg_st[8],
+                                                  &general_ops);
+
+       rd->rtdbg_st[9].info = rd;
+       rd->rtdbg_st[9].id = RT_SUPPORT_MODE;
+       rd->rt_debug_file[9] = debugfs_create_file("support_mode",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[9],
+                                                  &general_ops);
+
+       rd->rtdbg_st[10].info = rd;
+       rd->rtdbg_st[10].id = RT_DBG_IO_LOG;
+       rd->rt_debug_file[10] = debugfs_create_file("io_log",
+                                                   S_IFREG | 0444, dir,
+                                                   (void *)&rd->rtdbg_st[10],
+                                                   &general_ops);
+
+       rd->rtdbg_st[11].info = rd;
+       rd->rtdbg_st[11].id = RT_DBG_CACHE_MODE;
+       rd->rt_debug_file[11] = debugfs_create_file("cache_mode",
+                                                   S_IFREG | 0444, dir,
+                                                   (void *)&rd->rtdbg_st[11],
+                                                   &general_ops);
+       rd->rtdbg_st[12].info = rd;
+       rd->rtdbg_st[12].id = RT_DBG_REG_SIZE;
+       rd->rt_debug_file[12] = debugfs_create_file("reg_size",
+                                                  S_IFREG | 0444, dir,
+                                                  (void *)&rd->rtdbg_st[12],
+                                                  &general_ops);
+}
+
+static int eachreg_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t eachreg_write(struct file *file, const char __user *ubuf,
+                            size_t count, loff_t *ppos)
+{
+       struct rt_debug_st *st = file->private_data;
+       struct rt_regmap_device *rd = st->info;
+       rt_register_map_t rm = rd->props.rm[st->id];
+       int rc;
+       unsigned char pars[20];
+
+       if ((rm->size - 1) * 3 + 5 != count) {
+               dev_err(&rd->dev, "wrong input length\n");
+               return -EINVAL;
+       }
+       rc = get_datas((char *)ubuf, count, pars, rm->size);
+       if (rc < 0) {
+               dev_err(&rd->dev, "get datas fail\n");
+               return -EINVAL;
+       }
+
+       down(&rd->semaphore);
+       rc = rd->regmap_ops.regmap_block_write(rd, rm->addr,
+                                       rm->size, &pars[0]);
+       up(&rd->semaphore);
+       if (rc < 0) {
+               dev_err(&rd->dev, "regmap block read fail\n");
+               return -EIO;
+       }
+
+       return count;
+}
+
+static ssize_t eachreg_read(struct file *file, char __user *ubuf,
+                           size_t count, loff_t *ppos)
+{
+       struct rt_debug_st *st = file->private_data;
+       struct rt_regmap_device *rd = st->info;
+       char lbuf[80];
+       unsigned char regval[32];
+       rt_register_map_t rm = rd->props.rm[st->id];
+       int i, j = 0, rc;
+
+       lbuf[0] = '\0';
+
+       down(&rd->semaphore);
+       rc = rd->regmap_ops.regmap_block_read(rd, rm->addr, rm->size, regval);
+       up(&rd->semaphore);
+       if (rc < 0) {
+               dev_err(&rd->dev, "regmap block read fail\n");
+               return -EIO;
+       }
+
+       j += sprintf(lbuf + j, "reg0x%02x:0x", rm->addr);
+       for (i = 0; i < rm->size; i++)
+               j += sprintf(lbuf + j, "%02x,", regval[i]);
+       j += sprintf(lbuf + j, "\n");
+
+       return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static const struct file_operations eachreg_ops = {
+       .open = eachreg_open,
+       .read = eachreg_read,
+       .write = eachreg_write,
+};
+
+/* create every register node at debugfs */
+static void rt_create_every_debug(struct rt_regmap_device *rd,
+                                 struct dentry *dir)
+{
+       int i;
+       char buf[10];
+
+       rd->rt_reg_file = devm_kzalloc(&rd->dev,
+               rd->props.register_num * sizeof(struct dentry *), GFP_KERNEL);
+       rd->reg_st = devm_kzalloc(&rd->dev,
+               rd->props.register_num * sizeof(struct rt_debug_st *),
+                                                               GFP_KERNEL);
+       for (i = 0; i < rd->props.register_num; i++) {
+               sprintf(buf, "reg0x%02x", (rd->props.rm[i])->addr);
+               rd->rt_reg_file[i] = devm_kzalloc(&rd->dev,
+                                                 sizeof(rd->rt_reg_file[i]),
+                                                 GFP_KERNEL);
+               rd->reg_st[i] =
+                   devm_kzalloc(&rd->dev, sizeof(rd->reg_st[i]), GFP_KERNEL);
+
+               rd->reg_st[i]->info = rd;
+               rd->reg_st[i]->id = i;
+               rd->rt_reg_file[i] = debugfs_create_file(buf,
+                                                        S_IFREG | 0444, dir,
+                                                        (void *)rd->reg_st[i],
+                                                        &eachreg_ops);
+       }
+}
+
+static void rt_release_every_debug(struct rt_regmap_device *rd)
+{
+       int num = rd->props.register_num;
+       int i;
+
+       for (i = 0; i < num; i++) {
+               devm_kfree(&rd->dev, rd->rt_reg_file[i]);
+               devm_kfree(&rd->dev, rd->reg_st[i]);
+       }
+       devm_kfree(&rd->dev, rd->rt_reg_file);
+       devm_kfree(&rd->dev, rd->reg_st);
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void rt_regmap_device_release(struct device *dev)
+{
+       struct rt_regmap_device *rd = to_rt_regmap_device(dev);
+
+       devm_kfree(dev, rd);
+}
+
+/* check the rt_register format is correct */
+static int rt_regmap_check(struct rt_regmap_device *rd)
+{
+       const rt_register_map_t *rm = rd->props.rm;
+       int num = rd->props.register_num;
+       int i;
+
+       /* check name property */
+       if (!rd->props.name) {
+               pr_info("there is no node name for rt-regmap\n");
+               return -EINVAL;
+       }
+
+       if (!(rd->props.rt_regmap_mode & RT_BYTE_MODE_MASK))
+               goto single_byte;
+
+       for (i = 0; i < num; i++) {
+               /* check byte size, 1 byte ~ 24 bytes is valid */
+               if (rm[i]->size < 1 || rm[i]->size > 24) {
+                       pr_info("rt register size error at reg 0x%02x\n",
+                               rm[i]->addr);
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < num - 1; i++) {
+               /* check register sequence */
+               if (rm[i]->addr >= rm[i + 1]->addr) {
+                       pr_info("sequence format error at reg 0x%02x\n",
+                               rm[i]->addr);
+                       return -EINVAL;
+               }
+       }
+
+single_byte:
+       /* no default reg_addr and reister_map first addr is not 0x00 */
+       if (!rd->dbg_data.reg_addr && rm[0]->addr) {
+               rd->dbg_data.reg_addr = rm[0]->addr;
+               rd->dbg_data.rio.index = 0;
+               rd->dbg_data.rio.offset = 0;
+       }
+       return 0;
+}
+
+static int rt_create_simple_map(struct rt_regmap_device *rd)
+{
+       int i, j, count = 0, num = 0;
+       rt_register_map_t *rm;
+
+       pr_info("%s\n", __func__);
+       for (i = 0; i < rd->props.register_num; i++)
+               num += rd->props.rm[i]->size;
+
+       rm = devm_kzalloc(&rd->dev, num * sizeof(*rm), GFP_KERNEL);
+
+       for (i = 0; i < rd->props.register_num; i++) {
+               for (j = 0; j < rd->props.rm[i]->size; j++) {
+                       rm[count] = devm_kzalloc(&rd->dev,
+                                                sizeof(struct rt_register),
+                                                GFP_KERNEL);
+                       rm[count]->wbit_mask = devm_kzalloc(&rd->dev,
+                               sizeof(unsigned char), GFP_KERNEL);
+
+                       rm[count]->addr = rd->props.rm[i]->addr + j;
+                       rm[count]->size = 1;
+                       rm[count]->reg_type = rd->props.rm[i]->reg_type;
+                       if ((rd->props.rm[i]->reg_type & RT_REG_TYPE_MASK) !=
+                                                               RT_WBITS)
+                               rm[count]->wbit_mask[0] = 0xff;
+                       else
+                               rm[count]->wbit_mask[0] =
+                                       rd->props.rm[i]->wbit_mask[0];
+                       count++;
+               }
+               if (count > num)
+                       break;
+       }
+
+       rd->props.register_num = num;
+       rd->props.rm = rm;
+
+       return 0;
+}
+
+/* rt_regmap_device_register
+ * @props: a pointer to rt_regmap_properties for rt_regmap_device
+ * @rops: a pointer to rt_regmap_fops for rt_regmap_device
+ * @parent: a pinter to parent device
+ * @client: a pointer to the slave client of this device
+ * @drvdata: a pointer to the driver data
+ */
+struct rt_regmap_device *rt_regmap_device_register
+                       (struct rt_regmap_properties *props,
+                       struct rt_regmap_fops *rops,
+                       struct device *parent,
+                       void *client, void *drvdata)
+{
+       struct rt_regmap_device *rd;
+       int ret = 0, i;
+       char device_name[32];
+       unsigned char data;
+
+       pr_info("regmap_device_register: name = %s\n", props->name);
+       rd = devm_kzalloc(parent, sizeof(*rd), GFP_KERNEL);
+       if (!rd) {
+               pr_info("rt_regmap_device memory allocate fail\n");
+               return NULL;
+       }
+
+       /* create a binary semaphore */
+       sema_init(&rd->semaphore, 1);
+       rd->dev.parent = parent;
+       rd->client = client;
+       rd->dev.release = rt_regmap_device_release;
+       dev_set_drvdata(&rd->dev, drvdata);
+       sprintf(device_name, "rt_regmap_%s", props->name);
+       dev_set_name(&rd->dev, device_name);
+       if (props)
+               memcpy(&rd->props, props, sizeof(struct rt_regmap_properties));
+
+       /* check rt_registe_map format */
+       ret = rt_regmap_check(rd);
+       if (ret) {
+               pr_info("rt register map format error\n");
+               devm_kfree(parent, rd);
+               return NULL;
+       }
+
+       ret = device_register(&rd->dev);
+       if (ret) {
+               pr_info("rt-regmap dev register fail\n");
+               devm_kfree(parent, rd);
+               return NULL;
+       }
+
+       rd->rops = rops;
+       rd->err_msg = devm_kzalloc(parent, 128 * sizeof(char), GFP_KERNEL);
+
+       if (!(rd->props.rt_regmap_mode &  RT_BYTE_MODE_MASK)) {
+               ret = rt_create_simple_map(rd);
+               if (ret < 0) {
+                       pr_info(" rt create simple register map fail\n");
+                       goto err_cacheinit;
+               }
+       }
+
+       /* init cache data */
+       ret = rt_regmap_cache_init(rd);
+       if (ret < 0) {
+               pr_info(" rt cache data init fail\n");
+               goto err_cacheinit;
+       }
+
+       INIT_DELAYED_WORK(&rd->rt_work, rt_work_func);
+
+       for (i = 0; i <= 3; i++)
+               rd->rt_block_write[i] = rt_block_map[i];
+
+       data = rd->props.rt_regmap_mode & RT_CACHE_MODE_MASK;
+       if (data == RT_CACHE_WR_THROUGH) {
+               rd->regmap_ops.regmap_block_write = &rt_cache_block_write;
+               rd->regmap_ops.regmap_block_read = &rt_cache_block_read;
+       } else if (data == RT_CACHE_WR_BACK) {
+               rd->regmap_ops.regmap_block_write = &rt_asyn_cache_block_write;
+               rd->regmap_ops.regmap_block_read = &rt_cache_block_read;
+       } else if (data == RT_CACHE_DISABLE) {
+               rd->regmap_ops.regmap_block_write = &rt_chip_block_write;
+               rd->regmap_ops.regmap_block_read = &rt_chip_block_read;
+       }
+
+#ifdef CONFIG_DEBUG_FS
+       rd->rt_den = debugfs_create_dir(props->name, rt_regmap_dir);
+       if (!IS_ERR(rd->rt_den)) {
+               rt_create_general_debug(rd, rd->rt_den);
+               if (rd->props.rt_regmap_mode & DBG_MODE_MASK)
+                       rt_create_every_debug(rd, rd->rt_den);
+       } else {
+               goto err_debug;
+       }
+#endif /* CONFIG_DEBUG_FS */
+
+       return rd;
+
+#ifdef CONFIG_DEBUG_FS
+err_debug:
+       rt_regmap_cache_release(rd);
+#endif /* CONFIG_DEBUG_FS */
+err_cacheinit:
+       device_unregister(&rd->dev);
+       return NULL;
+}
+EXPORT_SYMBOL(rt_regmap_device_register);
+
+/* rt_regmap_device_unregister - unregister rt_regmap_device*/
+void rt_regmap_device_unregister(struct rt_regmap_device *rd)
+{
+       if (!rd)
+               return;
+       down(&rd->semaphore);
+       rd->rops = NULL;
+       up(&rd->semaphore);
+       if (rd->cache_inited)
+               rt_regmap_cache_release(rd);
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(rd->rt_den);
+       if (rd->props.rt_regmap_mode & DBG_MODE_MASK)
+               rt_release_every_debug(rd);
+#endif /* CONFIG_DEBUG_FS */
+       device_unregister(&rd->dev);
+}
+EXPORT_SYMBOL(rt_regmap_device_unregister);
+
+static int __init regmap_plat_init(void)
+{
+       rt_regmap_dir = debugfs_create_dir("rt-regmap", 0);
+       pr_info("Init Richtek RegMap\n");
+       if (IS_ERR(rt_regmap_dir)) {
+               pr_err("rt-regmap debugfs node create fail\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+subsys_initcall(regmap_plat_init);
+
+static void __exit regmap_plat_exit(void)
+{
+       debugfs_remove(rt_regmap_dir);
+}
+
+module_exit(regmap_plat_exit);
+
+MODULE_DESCRIPTION("Richtek regmap Driver");
+MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
+MODULE_VERSION(RT_REGMAP_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/pd/richtek/tcpc_rt1711h.c b/drivers/usb/pd/richtek/tcpc_rt1711h.c
new file mode 100644 (file)
index 0000000..1d1eb38
--- /dev/null
@@ -0,0 +1,1417 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Richtek RT1711H Type-C Port Control Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/cpu.h>
+#include <linux/version.h>
+#include <linux/sched/clock.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+#include <linux/hisi/log/hisi_log.h>
+
+#ifdef CONFIG_RT_REGMAP
+#include <linux/hisi/usb/pd/richtek/rt-regmap.h>
+#endif /* CONFIG_RT_REGMAP */
+#include <linux/sched/rt.h>
+
+/* #define DEBUG_GPIO  66 */
+
+#define RT1711H_DRV_VERSION    "1.1.8_G"
+
+struct rt1711_chip {
+       struct i2c_client *client;
+       struct device *dev;
+#ifdef CONFIG_RT_REGMAP
+       struct rt_regmap_device *m_dev;
+#endif /* CONFIG_RT_REGMAP */
+       struct semaphore io_lock;
+       struct semaphore suspend_lock;
+       struct tcpc_desc *tcpc_desc;
+       struct tcpc_device *tcpc;
+       struct kthread_worker irq_worker;
+       struct kthread_work irq_work;
+       struct task_struct *irq_worker_task;
+
+       atomic_t poll_count;
+       struct delayed_work     poll_work;
+
+       int irq_gpio;
+       int irq;
+       int chip_id;
+};
+
+#ifdef CONFIG_RT_REGMAP
+RT_REG_DECL(TCPC_V10_REG_VID, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_PID, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_DID, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TYPEC_REV, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_PD_REV, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_PDIF_REV, 2, RT_VOLATILE, {});
+
+RT_REG_DECL(TCPC_V10_REG_ALERT, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_ALERT_MASK, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_POWER_STATUS_MASK, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_FAULT_STATUS_MASK, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TCPC_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_ROLE_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_FAULT_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_POWER_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_CC_STATUS, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_POWER_STATUS, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_FAULT_STATUS, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_COMMAND, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_MSG_HDR_INFO, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_RX_DETECT, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_RX_BYTE_CNT, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_RX_BUF_FRAME_TYPE, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_RX_HDR, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_RX_DATA, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TRANSMIT, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TX_BYTE_CNT, 1, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TX_HDR, 2, RT_VOLATILE, {});
+RT_REG_DECL(TCPC_V10_REG_TX_DATA, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_CLK_CTRL2, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_CLK_CTRL3, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_BMC_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_BMCIO_RXDZSEL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_RT_STATUS, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_RT_INT, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_RT_MASK, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_IDLE_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_INTRST_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_WATCHDOG_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_I2CRST_CTRL, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_SWRESET, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_TTCPC_FILTER, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_DRP_TOGGLE_CYCLE, 1, RT_VOLATILE, {});
+RT_REG_DECL(RT1711H_REG_DRP_DUTY_CTRL, 1, RT_VOLATILE, {});
+
+static const rt_register_map_t rt1711_chip_regmap[] = {
+       RT_REG(TCPC_V10_REG_VID),
+       RT_REG(TCPC_V10_REG_PID),
+       RT_REG(TCPC_V10_REG_DID),
+       RT_REG(TCPC_V10_REG_TYPEC_REV),
+       RT_REG(TCPC_V10_REG_PD_REV),
+       RT_REG(TCPC_V10_REG_PDIF_REV),
+       RT_REG(TCPC_V10_REG_ALERT),
+       RT_REG(TCPC_V10_REG_ALERT_MASK),
+       RT_REG(TCPC_V10_REG_POWER_STATUS_MASK),
+       RT_REG(TCPC_V10_REG_FAULT_STATUS_MASK),
+       RT_REG(TCPC_V10_REG_TCPC_CTRL),
+       RT_REG(TCPC_V10_REG_ROLE_CTRL),
+       RT_REG(TCPC_V10_REG_FAULT_CTRL),
+       RT_REG(TCPC_V10_REG_POWER_CTRL),
+       RT_REG(TCPC_V10_REG_CC_STATUS),
+       RT_REG(TCPC_V10_REG_POWER_STATUS),
+       RT_REG(TCPC_V10_REG_FAULT_STATUS),
+       RT_REG(TCPC_V10_REG_COMMAND),
+       RT_REG(TCPC_V10_REG_MSG_HDR_INFO),
+       RT_REG(TCPC_V10_REG_RX_DETECT),
+       RT_REG(TCPC_V10_REG_RX_BYTE_CNT),
+       RT_REG(TCPC_V10_REG_RX_BUF_FRAME_TYPE),
+       RT_REG(TCPC_V10_REG_RX_HDR),
+       RT_REG(TCPC_V10_REG_RX_DATA),
+       RT_REG(TCPC_V10_REG_TRANSMIT),
+       RT_REG(TCPC_V10_REG_TX_BYTE_CNT),
+       RT_REG(TCPC_V10_REG_TX_HDR),
+       RT_REG(TCPC_V10_REG_TX_DATA),
+       RT_REG(RT1711H_REG_CLK_CTRL2),
+       RT_REG(RT1711H_REG_CLK_CTRL3),
+       RT_REG(RT1711H_REG_BMC_CTRL),
+       RT_REG(RT1711H_REG_BMCIO_RXDZSEL),
+       RT_REG(RT1711H_REG_RT_STATUS),
+       RT_REG(RT1711H_REG_RT_INT),
+       RT_REG(RT1711H_REG_RT_MASK),
+       RT_REG(RT1711H_REG_IDLE_CTRL),
+       RT_REG(RT1711H_REG_INTRST_CTRL),
+       RT_REG(RT1711H_REG_WATCHDOG_CTRL),
+       RT_REG(RT1711H_REG_I2CRST_CTRL),
+       RT_REG(RT1711H_REG_SWRESET),
+       RT_REG(RT1711H_REG_TTCPC_FILTER),
+       RT_REG(RT1711H_REG_DRP_TOGGLE_CYCLE),
+       RT_REG(RT1711H_REG_DRP_DUTY_CTRL),
+};
+
+#define RT1711_CHIP_REGMAP_SIZE ARRAY_SIZE(rt1711_chip_regmap)
+
+#endif /* CONFIG_RT_REGMAP */
+
+static int rt1711_read_device(void *client, u32 reg, int len, void *dst)
+{
+       struct i2c_client *i2c = (struct i2c_client *)client;
+       int ret = 0, count = 5;
+
+       while (count) {
+               if (len > 1) {
+                       ret = i2c_smbus_read_i2c_block_data(i2c, reg, len, dst);
+                       if (ret < 0)
+                               count--;
+                       else
+                               return ret;
+               } else {
+                       ret = i2c_smbus_read_byte_data(i2c, reg);
+                       if (ret < 0) {
+                               count--;
+                       } else {
+                               *(u8 *)dst = (u8)ret;
+                               return ret;
+                       }
+               }
+               usleep_range(100, 120);
+       }
+       return ret;
+}
+
+static int rt1711_write_device(void *client, u32 reg, int len, const void *src)
+{
+       const u8 *data;
+       struct i2c_client *i2c = (struct i2c_client *)client;
+       int ret = 0, count = 5;
+
+       while (count) {
+               if (len > 1) {
+                       ret = i2c_smbus_write_i2c_block_data(
+                                       i2c, reg, len, src);
+                       if (ret < 0)
+                               count--;
+                       else
+                               return ret;
+               } else {
+                       data = src;
+                       ret = i2c_smbus_write_byte_data(i2c, reg, *data);
+                       if (ret < 0)
+                               count--;
+                       else
+                               return ret;
+               }
+               usleep_range(100, 120);
+       }
+       return ret;
+}
+
+static int rt1711_reg_read(struct i2c_client *i2c, u8 reg)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(i2c);
+       u8 val = 0;
+       int ret = 0;
+
+#ifdef CONFIG_RT_REGMAP
+       ret = rt_regmap_block_read(chip->m_dev, reg, 1, &val);
+#else
+       ret = rt1711_read_device(chip->client, reg, 1, &val);
+#endif /* CONFIG_RT_REGMAP */
+       if (ret < 0) {
+               dev_err(chip->dev, "rt1711 reg read fail\n");
+               return ret;
+       }
+       return val;
+}
+
+static int rt1711_reg_write(struct i2c_client *i2c, u8 reg, const u8 data)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(i2c);
+       int ret = 0;
+
+#ifdef CONFIG_RT_REGMAP
+       ret = rt_regmap_block_write(chip->m_dev, reg, 1, &data);
+#else
+       ret = rt1711_write_device(chip->client, reg, 1, &data);
+#endif /* CONFIG_RT_REGMAP */
+       if (ret < 0)
+               dev_err(chip->dev, "rt1711 reg write fail\n");
+       return ret;
+}
+
+static int rt1711_block_read(struct i2c_client *i2c,
+                            u8 reg, int len, void *dst)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(i2c);
+       int ret = 0;
+#ifdef CONFIG_RT_REGMAP
+       ret = rt_regmap_block_read(chip->m_dev, reg, len, dst);
+#else
+       ret = rt1711_read_device(chip->client, reg, len, dst);
+#endif /* #ifdef CONFIG_RT_REGMAP */
+       if (ret < 0)
+               dev_err(chip->dev, "rt1711 block read fail\n");
+       return ret;
+}
+
+static int rt1711_block_write(struct i2c_client *i2c,
+                             u8 reg, int len, const void *src)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(i2c);
+       int ret = 0;
+#ifdef CONFIG_RT_REGMAP
+       ret = rt_regmap_block_write(chip->m_dev, reg, len, src);
+#else
+       ret = rt1711_write_device(chip->client, reg, len, src);
+#endif /* #ifdef CONFIG_RT_REGMAP */
+       if (ret < 0)
+               dev_err(chip->dev, "rt1711 block write fail\n");
+       return ret;
+}
+
+static int32_t rt1711_write_word(struct i2c_client *client,
+                                u8 reg_addr, u16 data)
+{
+       int ret;
+
+       /* don't need swap */
+       ret = rt1711_block_write(client, reg_addr, 2, (u8 *)&data);
+       return ret;
+}
+
+static int32_t rt1711_read_word(struct i2c_client *client,
+                               u8 reg_addr, u16 *data)
+{
+       int ret;
+
+       /* don't need swap */
+       ret = rt1711_block_read(client, reg_addr, 2, (u8 *)data);
+       return ret;
+}
+
+static inline int rt1711_i2c_write8(
+       struct tcpc_device *tcpc, u8 reg, const u8 data)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+
+       return rt1711_reg_write(chip->client, reg, data);
+}
+
+static inline int rt1711_i2c_write16(
+               struct tcpc_device *tcpc, u8 reg, const u16 data)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+
+       return rt1711_write_word(chip->client, reg, data);
+}
+
+static inline int rt1711_i2c_read8(struct tcpc_device *tcpc, u8 reg)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+
+       return rt1711_reg_read(chip->client, reg);
+}
+
+static inline int rt1711_i2c_read16(
+       struct tcpc_device *tcpc, u8 reg)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+       u16 data;
+       int ret;
+
+       ret = rt1711_read_word(chip->client, reg, &data);
+       if (ret < 0)
+               return ret;
+       return data;
+}
+
+#ifdef CONFIG_RT_REGMAP
+static struct rt_regmap_fops rt1711_regmap_fops = {
+       .read_device = rt1711_read_device,
+       .write_device = rt1711_write_device,
+};
+#endif /* CONFIG_RT_REGMAP */
+
+static int rt1711_regmap_init(struct rt1711_chip *chip)
+{
+#ifdef CONFIG_RT_REGMAP
+       struct rt_regmap_properties *props;
+       char name[32];
+       int len;
+
+       props = devm_kzalloc(chip->dev, sizeof(*props), GFP_KERNEL);
+       if (!props)
+               return -ENOMEM;
+
+       props->register_num = RT1711_CHIP_REGMAP_SIZE;
+       props->rm = rt1711_chip_regmap;
+
+       props->rt_regmap_mode = RT_MULTI_BYTE | RT_CACHE_DISABLE |
+                               RT_IO_PASS_THROUGH | RT_DBG_GENERAL;
+       snprintf(name, 32, "rt1711-%02x", chip->client->addr);
+
+       len = strlen(name);
+       props->name = kzalloc(len + 1, GFP_KERNEL);
+       props->aliases = kzalloc(len + 1, GFP_KERNEL);
+       strcpy((char *)props->name, name);
+       strcpy((char *)props->aliases, name);
+       props->io_log_en = 0;
+
+       chip->m_dev = rt_regmap_device_register(props,
+                       &rt1711_regmap_fops, chip->dev, chip->client, chip);
+       if (!chip->m_dev) {
+               dev_err(chip->dev, "rt1711 chip rt_regmap register fail\n");
+               return -EINVAL;
+       }
+#endif
+       return 0;
+}
+
+static int rt1711_regmap_deinit(struct rt1711_chip *chip)
+{
+#ifdef CONFIG_RT_REGMAP
+       rt_regmap_device_unregister(chip->m_dev);
+#endif
+       return 0;
+}
+
+static inline int rt1711_software_reset(struct tcpc_device *tcpc)
+{
+       int ret = rt1711_i2c_write8(tcpc, RT1711H_REG_SWRESET, 1);
+
+       if (ret < 0)
+               return ret;
+
+       mdelay(1);
+       return 0;
+}
+
+static inline int rt1711_command(struct tcpc_device *tcpc, u8 cmd)
+{
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_COMMAND, cmd);
+}
+
+static int rt1711_init_alert_mask(struct tcpc_device *tcpc)
+{
+       u16 mask;
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+
+       mask = TCPC_V10_REG_ALERT_CC_STATUS | TCPC_V10_REG_ALERT_POWER_STATUS;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       /* Need to handle RX overflow */
+       mask |= TCPC_V10_REG_ALERT_TX_SUCCESS | TCPC_V10_REG_ALERT_TX_DISCARDED
+                       | TCPC_V10_REG_ALERT_TX_FAILED
+                       | TCPC_V10_REG_ALERT_RX_HARD_RST
+                       | TCPC_V10_REG_ALERT_RX_STATUS
+                       | TCPC_V10_REG_RX_OVERFLOW;
+#endif
+
+       mask |= TCPC_REG_ALERT_FAULT;
+
+       return rt1711_write_word(chip->client, TCPC_V10_REG_ALERT_MASK, mask);
+}
+
+static int rt1711_init_power_status_mask(struct tcpc_device *tcpc)
+{
+       const u8 mask = TCPC_V10_REG_POWER_STATUS_VBUS_PRES;
+
+       return rt1711_i2c_write8(tcpc,
+                       TCPC_V10_REG_POWER_STATUS_MASK, mask);
+}
+
+static int rt1711_init_fault_mask(struct tcpc_device *tcpc)
+{
+       const u8 mask =
+               TCPC_V10_REG_FAULT_STATUS_VCONN_OV |
+               TCPC_V10_REG_FAULT_STATUS_VCONN_OC;
+
+       return rt1711_i2c_write8(tcpc,
+                       TCPC_V10_REG_FAULT_STATUS_MASK, mask);
+}
+
+static int rt1711_init_rt_mask(struct tcpc_device *tcpc)
+{
+       u8 rt_mask = 0;
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       rt_mask |= RT1711H_REG_M_VBUS_80;
+#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
+
+#ifdef CONFIG_TYPEC_CAP_RA_DETACH
+       if (tcpc->tcpc_flags & TCPC_FLAGS_CHECK_RA_DETACHE)
+               rt_mask |= RT1711H_REG_M_RA_DETACH;
+#endif /* CONFIG_TYPEC_CAP_RA_DETACH */
+
+#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
+       if (tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG)
+               rt_mask |= RT1711H_REG_M_WAKEUP;
+#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
+
+       return rt1711_i2c_write8(tcpc, RT1711H_REG_RT_MASK, rt_mask);
+}
+
+static inline void rt1711_poll_ctrl(struct rt1711_chip *chip)
+{
+       cancel_delayed_work_sync(&chip->poll_work);
+
+       if (atomic_read(&chip->poll_count) == 0) {
+               atomic_inc(&chip->poll_count);
+               cpu_idle_poll_ctrl(true);
+       }
+
+       schedule_delayed_work(
+               &chip->poll_work, msecs_to_jiffies(40));
+}
+
+static void rt1711_irq_work_handler(struct kthread_work *work)
+{
+       struct rt1711_chip *chip =
+                       container_of(work, struct rt1711_chip, irq_work);
+       int regval = 0;
+       int gpio_val;
+
+       rt1711_poll_ctrl(chip);
+       /* make sure I2C bus had resumed */
+       down(&chip->suspend_lock);
+       tcpci_lock_typec(chip->tcpc);
+
+#ifdef DEBUG_GPIO
+       gpio_set_value(DEBUG_GPIO, 1);
+#endif
+
+       do {
+               regval = tcpci_alert(chip->tcpc);
+               if (regval)
+                       break;
+               gpio_val = gpio_get_value(chip->irq_gpio);
+       } while (gpio_val == 0);
+
+       tcpci_unlock_typec(chip->tcpc);
+       up(&chip->suspend_lock);
+
+#ifdef DEBUG_GPIO
+       gpio_set_value(DEBUG_GPIO, 1);
+#endif
+}
+
+static void rt1711_poll_work(struct work_struct *work)
+{
+       struct rt1711_chip *chip = container_of(
+               work, struct rt1711_chip, poll_work.work);
+
+       if (atomic_dec_and_test(&chip->poll_count))
+               cpu_idle_poll_ctrl(false);
+}
+
+static irqreturn_t rt1711_intr_handler(int irq, void *data)
+{
+       struct rt1711_chip *chip = data;
+
+#ifdef DEBUG_GPIO
+       gpio_set_value(DEBUG_GPIO, 0);
+#endif
+       kthread_queue_work(&chip->irq_worker, &chip->irq_work);
+       return IRQ_HANDLED;
+}
+
+static int rt1711_init_alert(struct tcpc_device *tcpc)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+       struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+       int ret;
+       char *name;
+       int len;
+
+       /* Clear Alert Mask & Status */
+       rt1711_write_word(chip->client, TCPC_V10_REG_ALERT_MASK, 0);
+       rt1711_write_word(chip->client, TCPC_V10_REG_ALERT, 0xffff);
+
+       len = strlen(chip->tcpc_desc->name);
+       name = kzalloc(sizeof(len + 5), GFP_KERNEL);
+       sprintf(name, "%s-IRQ", chip->tcpc_desc->name);
+
+       pr_info("%s name = %s\n", __func__, chip->tcpc_desc->name);
+       pr_info("%s gpio # = %d\n", __func__, chip->irq_gpio);
+
+       ret = gpio_request(chip->irq_gpio, name);
+#ifdef DEBUG_GPIO
+       gpio_request(DEBUG_GPIO, "debug_latency_pin");
+       gpio_direction_output(DEBUG_GPIO, 1);
+#endif
+       if (ret < 0) {
+               pr_err("Error: failed to request GPIO%d (ret = %d)\n",
+                      chip->irq_gpio, ret);
+               return ret;
+       }
+       pr_info("GPIO requested...\n");
+
+       ret = gpio_direction_input(chip->irq_gpio);
+       if (ret < 0) {
+               pr_err("Error: failed to set GPIO%d as input pin(ret = %d)\n",
+                      chip->irq_gpio, ret);
+               return ret;
+       }
+
+       chip->irq = gpio_to_irq(chip->irq_gpio);
+       pr_info("%s : IRQ number = %d\n", __func__, chip->irq);
+
+       /*
+        * ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
+        * rt1711_intr_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+        * name, chip);
+        */
+
+       if (ret < 0) {
+               pr_err("Error: failed to request irq%d (gpio = %d, ret = %d)\n",
+                      chip->irq, chip->irq_gpio, ret);
+               return ret;
+       }
+       pr_info("%s : irq initialized...\n", __func__);
+
+       kthread_init_worker(&chip->irq_worker);
+       chip->irq_worker_task = kthread_run(kthread_worker_fn,
+                       &chip->irq_worker, chip->tcpc_desc->name);
+       if (IS_ERR(chip->irq_worker_task)) {
+               pr_err("Error: Could not create tcpc task\n");
+               return -EINVAL;
+       }
+
+       sched_setscheduler(chip->irq_worker_task, SCHED_FIFO, &param);
+       kthread_init_work(&chip->irq_work, rt1711_irq_work_handler);
+
+       pr_info("IRQF_NO_THREAD Test\r\n");
+       ret = request_irq(
+                       chip->irq, rt1711_intr_handler,
+                       IRQF_TRIGGER_FALLING | IRQF_NO_THREAD |
+                       IRQF_NO_SUSPEND, name, chip);
+
+       enable_irq_wake(chip->irq);
+
+       return 0;
+}
+
+static inline int rt1711h_set_clock_gating(
+               struct tcpc_device *tcpc_dev, bool en)
+{
+       int ret = 0;
+
+#ifdef CONFIG_TCPC_CLOCK_GATING
+       u8 clk2 = RT1711H_REG_CLK_DIV_600K_EN
+               | RT1711H_REG_CLK_DIV_300K_EN | RT1711H_REG_CLK_CK_300K_EN;
+
+       u8 clk3 = RT1711H_REG_CLK_DIV_2P4M_EN;
+
+       if (!en) {
+               clk2 |=
+                       RT1711H_REG_CLK_BCLK2_EN | RT1711H_REG_CLK_BCLK_EN;
+               clk3 |=
+                       RT1711H_REG_CLK_CK_24M_EN | RT1711H_REG_CLK_PCLK_EN;
+       }
+
+       ret = rt1711_i2c_write8(tcpc_dev, RT1711H_REG_CLK_CTRL2, clk2);
+       if (ret == 0)
+               ret = rt1711_i2c_write8(tcpc_dev, RT1711H_REG_CLK_CTRL3, clk3);
+#endif /* CONFIG_TCPC_CLOCK_GATING */
+
+       return ret;
+}
+
+static inline int rt1711h_init_cc_params(
+                       struct tcpc_device *tcpc, u8 cc_res)
+{
+       int rv = 0;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+#ifdef CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC
+       if (cc_res == TYPEC_CC_VOLT_SNK_DFT)
+               rv = rt1711_i2c_write8(tcpc, RT1711H_REG_BMCIO_RXDZSEL, 0x81);
+       else
+               rv = rt1711_i2c_write8(tcpc, RT1711H_REG_BMCIO_RXDZSEL, 0x80);
+#endif /* CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC */
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       return rv;
+}
+
+static int rt1711_tcpc_init(struct tcpc_device *tcpc, bool sw_reset)
+{
+       int ret;
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+
+       RT1711_INFO("\n");
+
+       if (sw_reset) {
+               ret = rt1711_software_reset(tcpc);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* CK_300K from 320K, SHIPPING off, AUTOIDLE enable, TIMEOUT = 32ms */
+       rt1711_i2c_write8(
+                       tcpc, RT1711H_REG_IDLE_CTRL,
+                       RT1711H_REG_IDLE_SET(0, 1, 1, 2));
+
+       /* UFP Both RD setting */
+       /* DRP = 0, RpVal = 0 (Default), Rd, Rd */
+       rt1711_i2c_write8(
+                       tcpc, TCPC_V10_REG_ROLE_CTRL,
+                       TCPC_V10_REG_ROLE_CTRL_RES_SET(0, 0, CC_RD, CC_RD));
+
+       if (chip->chip_id == RT1711H_DID_A) {
+               rt1711_i2c_write8(
+                               tcpc, TCPC_V10_REG_FAULT_CTRL,
+                               TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OV);
+       }
+
+       /*
+        * CC Detect Debounce : 26.7*val us
+        * Transition window count : spec 12~20us, based on 2.4MHz
+        * DRP Toggle Cycle : 51.2 + 6.4*val ms
+        * DRP Duyt Ctrl : dcSRC: /1024
+        */
+
+       rt1711_i2c_write8(tcpc, RT1711H_REG_TTCPC_FILTER, 5);
+       rt1711_i2c_write8(tcpc, RT1711H_REG_DRP_TOGGLE_CYCLE, 4);
+       rt1711_i2c_write16(tcpc, RT1711H_REG_DRP_DUTY_CTRL, 400);
+
+       /* Vconn OC */
+       rt1711_i2c_write8(tcpc, RT1711H_REG_VCONN_CLIMITEN, 1);
+
+       /* RX/TX Clock Gating (Auto Mode)*/
+       if (!sw_reset)
+               rt1711h_set_clock_gating(tcpc, true);
+
+       tcpci_alert_status_clear(tcpc, 0xffffffff);
+
+       rt1711_init_power_status_mask(tcpc);
+       rt1711_init_alert_mask(tcpc);
+       rt1711_init_fault_mask(tcpc);
+       rt1711_init_rt_mask(tcpc);
+
+       return 0;
+}
+
+int rt1711_alert_status_clear(struct tcpc_device *tcpc, u32 mask)
+{
+       int ret;
+       u16 mask_t1;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       u8 mask_t2;
+#endif
+
+       /* Write 1 clear */
+       mask_t1 = (u16)mask;
+       ret = rt1711_i2c_write16(tcpc, TCPC_V10_REG_ALERT, mask_t1);
+       if (ret < 0)
+               return ret;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       mask_t2 = mask >> 16;
+       ret = rt1711_i2c_write8(tcpc, RT1711H_REG_RT_INT, mask_t2);
+       if (ret < 0)
+               return ret;
+#endif
+
+       return 0;
+}
+
+int rt1711_fault_status_clear(struct tcpc_device *tcpc, u8 status)
+{
+       /* Write 1 clear (Check it later )*/
+       int ret;
+
+       rt1711_i2c_write8(tcpc, TCPC_V10_REG_FAULT_STATUS, status);
+
+       /* discharge ... */
+       ret = rt1711_i2c_read8(tcpc, RT1711H_REG_BMC_CTRL);
+       if (ret < 0)
+               return ret;
+
+       rt1711_i2c_write8(
+                       tcpc, RT1711H_REG_BMC_CTRL,
+                       ret & (~RT1711H_REG_DISCHARGE_EN));
+
+       return 0;
+}
+
+int rt1711_get_alert_status(struct tcpc_device *tcpc, u32 *alert)
+{
+       int ret;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       u8 v2;
+#endif
+
+       ret = rt1711_i2c_read16(tcpc, TCPC_V10_REG_ALERT);
+       if (ret < 0)
+               return ret;
+
+       *alert = (u16)ret;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_INT);
+       if (ret < 0)
+               return ret;
+
+       v2 = (u8)ret;
+       *alert |= v2 << 16;
+#endif
+
+       return 0;
+}
+
+static int rt1711_get_power_status(
+               struct tcpc_device *tcpc, u16 *pwr_status)
+{
+       int ret;
+
+       ret = rt1711_i2c_read8(tcpc, TCPC_V10_REG_POWER_STATUS);
+       if (ret < 0)
+               return ret;
+
+       *pwr_status = 0;
+
+       if (ret & TCPC_V10_REG_POWER_STATUS_VBUS_PRES)
+               *pwr_status |= TCPC_REG_POWER_STATUS_VBUS_PRES;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_STATUS);
+       if (ret < 0)
+               return ret;
+
+       if (ret & RT1711H_REG_VBUS_80)
+               *pwr_status |= TCPC_REG_POWER_STATUS_EXT_VSAFE0V;
+#endif
+       return 0;
+}
+
+int rt1711_get_fault_status(struct tcpc_device *tcpc, u8 *status)
+{
+       int ret;
+
+       ret = rt1711_i2c_read8(tcpc, TCPC_V10_REG_FAULT_STATUS);
+       if (ret < 0)
+               return ret;
+       *status = (u8)ret;
+       return 0;
+}
+
+static int rt1711_get_cc(struct tcpc_device *tcpc, int *cc1, int *cc2)
+{
+       int status, role_ctrl, cc_role;
+       bool act_as_sink, act_as_drp;
+
+       status = rt1711_i2c_read8(tcpc, TCPC_V10_REG_CC_STATUS);
+       if (status < 0)
+               return status;
+
+       role_ctrl = rt1711_i2c_read8(tcpc, TCPC_V10_REG_ROLE_CTRL);
+       if (role_ctrl < 0)
+               return role_ctrl;
+
+       if (status & TCPC_V10_REG_CC_STATUS_DRP_TOGGLING) {
+               *cc1 = TYPEC_CC_DRP_TOGGLING;
+               *cc2 = TYPEC_CC_DRP_TOGGLING;
+               return 0;
+       }
+
+       *cc1 = TCPC_V10_REG_CC_STATUS_CC1(status);
+       *cc2 = TCPC_V10_REG_CC_STATUS_CC2(status);
+
+       act_as_drp = TCPC_V10_REG_ROLE_CTRL_DRP & role_ctrl;
+
+       if (act_as_drp) {
+               act_as_sink = TCPC_V10_REG_CC_STATUS_DRP_RESULT(status);
+       } else {
+               cc_role =  TCPC_V10_REG_CC_STATUS_CC1(role_ctrl);
+               if (cc_role == TYPEC_CC_RP)
+                       act_as_sink = false;
+               else
+                       act_as_sink = true;
+       }
+
+       /*
+        * If status is not open, then OR in termination to convert to
+        * enum tcpc_cc_voltage_status.
+        */
+
+       if (*cc1 != TYPEC_CC_VOLT_OPEN)
+               *cc1 |= (act_as_sink << 2);
+
+       if (*cc2 != TYPEC_CC_VOLT_OPEN)
+               *cc2 |= (act_as_sink << 2);
+
+       rt1711h_init_cc_params(
+                       tcpc,
+                       (u8)tcpc->typec_polarity ? *cc2 : *cc1);
+
+       return 0;
+}
+
+static int rt1711_set_cc(struct tcpc_device *tcpc, int pull)
+{
+       int ret;
+       u8 data;
+       int rp_lvl = TYPEC_CC_PULL_GET_RP_LVL(pull);
+
+       RT1711_INFO("\n");
+       pull = TYPEC_CC_PULL_GET_RES(pull);
+       if (pull == TYPEC_CC_DRP) {
+               data = TCPC_V10_REG_ROLE_CTRL_RES_SET(
+                               1, rp_lvl, TYPEC_CC_RD, TYPEC_CC_RD);
+
+               ret = rt1711_i2c_write8(
+                       tcpc, TCPC_V10_REG_ROLE_CTRL, data);
+
+               if (ret == 0)
+                       ret = rt1711_command(tcpc, TCPM_CMD_LOOK_CONNECTION);
+       } else {
+               data = TCPC_V10_REG_ROLE_CTRL_RES_SET(0, rp_lvl, pull, pull);
+               ret = rt1711_i2c_write8(tcpc, TCPC_V10_REG_ROLE_CTRL, data);
+       }
+
+       return 0;
+}
+
+static int rt1711_set_polarity(struct tcpc_device *tcpc, int polarity)
+{
+       int data;
+
+       data = rt1711h_init_cc_params(tcpc,
+                                     tcpc->typec_remote_cc[polarity]);
+       if (data)
+               return data;
+
+       data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_TCPC_CTRL);
+       if (data < 0)
+               return data;
+
+       data &= ~TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT;
+       data |= polarity ? TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT : 0;
+
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TCPC_CTRL, data);
+}
+
+static int rt1711_set_vconn(struct tcpc_device *tcpc, int enable)
+{
+       int data;
+
+       data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_POWER_CTRL);
+       if (data < 0)
+               return data;
+
+       data &= ~TCPC_V10_REG_POWER_CTRL_VCONN;
+       data |= enable ? TCPC_V10_REG_POWER_CTRL_VCONN : 0;
+
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_POWER_CTRL, data);
+}
+
+#ifdef CONFIG_TCPC_LOW_POWER_MODE
+static int rt1711_set_low_power_mode(
+               struct tcpc_device *tcpc_dev, bool en, int pull)
+{
+       int rv = 0;
+       u8 data;
+
+       if (en) {
+               data = RT1711H_REG_BMCIO_LPEN;
+
+               if (pull & TYPEC_CC_RP)
+                       data |= RT1711H_REG_BMCIO_LPRPRD;
+       } else {
+               data = RT1711H_REG_BMCIO_BG_EN |
+                       RT1711H_REG_VBUS_DET_EN | RT1711H_REG_BMCIO_OSC_EN;
+       }
+       rv = rt1711_i2c_write8(tcpc_dev, RT1711H_REG_BMC_CTRL, data);
+       return rv;
+}
+#endif /* CONFIG_TCPC_LOW_POWER_MODE */
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+static int rt1711_set_msg_header(
+       struct tcpc_device *tcpc, int power_role, int data_role)
+{
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_MSG_HDR_INFO,
+                       TCPC_V10_REG_MSG_HDR_INFO_SET(data_role, power_role));
+}
+
+static int rt1711_set_rx_enable(struct tcpc_device *tcpc, u8 enable)
+{
+       int ret = rt1711h_set_clock_gating(tcpc, !enable);
+
+       if (ret == 0)
+               ret = rt1711_i2c_write8(tcpc, TCPC_V10_REG_RX_DETECT, enable);
+       return ret;
+}
+
+static int rt1711_get_message(
+               struct tcpc_device *tcpc, u32 *payload,
+               u16 *msg_head, enum tcpm_transmit_type *frame_type)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+       int rv;
+       u8 type, cnt = 0;
+       u8 buf[4];
+       const u16 alert_rx =
+               TCPC_V10_REG_ALERT_RX_STATUS | TCPC_V10_REG_RX_OVERFLOW;
+
+       rv = rt1711_block_read(chip->client,
+                              TCPC_V10_REG_RX_BYTE_CNT, 4, buf);
+       cnt = buf[0];
+       type = buf[1];
+       *msg_head = *(u16 *)&buf[2];
+
+       /* TCPC 1.0 ==> no need to subtract the size of msg_head */
+       if (rv >= 0 && cnt > 0) {
+               cnt -= 3; /* MSG_HDR */
+               rv = rt1711_block_read(
+                               chip->client, TCPC_V10_REG_RX_DATA, cnt,
+                               (u8 *)payload);
+       }
+
+       *frame_type = (enum tcpm_transmit_type)type;
+
+       /* Read complete, clear RX status alert bit */
+       tcpci_alert_status_clear(tcpc, alert_rx);
+
+       /*mdelay(1); */
+       return rv;
+}
+
+static int rt1711_set_bist_carrier_mode(
+       struct tcpc_device *tcpc, u8 pattern)
+{
+       /* Don't support this function */
+       return 0;
+}
+
+/* message header (2byte) + data object (7*4) */
+#define RT1711_TRANSMIT_MAX_SIZE       (sizeof(u16) + sizeof(u32) * 7)
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+static int rt1711_retransmit(struct tcpc_device *tcpc)
+{
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TRANSMIT,
+                       TCPC_V10_REG_TRANSMIT_SET(TCPC_TX_SOP));
+}
+#endif
+
+static int rt1711_transmit(struct tcpc_device *tcpc,
+                          enum tcpm_transmit_type type,
+                          u16 header, const u32 *data)
+{
+       struct rt1711_chip *chip = tcpc_get_dev_data(tcpc);
+       int rv;
+       int data_cnt, packet_cnt;
+       u8 temp[RT1711_TRANSMIT_MAX_SIZE];
+
+       if (type < TCPC_TX_HARD_RESET) {
+               data_cnt = sizeof(u32) * PD_HEADER_CNT(header);
+               packet_cnt = data_cnt + sizeof(u16);
+
+               temp[0] = packet_cnt;
+               memcpy(temp + 1, (u8 *)&header, 2);
+               if (data_cnt > 0)
+                       memcpy(temp + 3, (u8 *)data, data_cnt);
+
+               rv = rt1711_block_write(
+                               chip->client,
+                               TCPC_V10_REG_TX_BYTE_CNT,
+                               packet_cnt + 1, (u8 *)temp);
+               if (rv < 0)
+                       return rv;
+       }
+
+       rv = rt1711_i2c_write8(
+                       tcpc, TCPC_V10_REG_TRANSMIT,
+                       TCPC_V10_REG_TRANSMIT_SET(type));
+       return rv;
+}
+
+static int rt1711_set_bist_test_mode(struct tcpc_device *tcpc, bool en)
+{
+       int data;
+
+       data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_TCPC_CTRL);
+       if (data < 0)
+               return data;
+
+       data &= ~TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE;
+       data |= en ? TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE : 0;
+
+       return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TCPC_CTRL, data);
+}
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+static struct tcpc_ops rt1711_tcpc_ops = {
+       .init = rt1711_tcpc_init,
+       .alert_status_clear = rt1711_alert_status_clear,
+       .fault_status_clear = rt1711_fault_status_clear,
+       .get_alert_status = rt1711_get_alert_status,
+       .get_power_status = rt1711_get_power_status,
+       .get_fault_status = rt1711_get_fault_status,
+       .get_cc = rt1711_get_cc,
+       .set_cc = rt1711_set_cc,
+       .set_polarity = rt1711_set_polarity,
+       .set_vconn = rt1711_set_vconn,
+
+#ifdef CONFIG_TCPC_LOW_POWER_MODE
+       .set_low_power_mode = rt1711_set_low_power_mode,
+#endif
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       .set_msg_header = rt1711_set_msg_header,
+       .set_rx_enable = rt1711_set_rx_enable,
+       .get_message = rt1711_get_message,
+       .transmit = rt1711_transmit,
+       .set_bist_test_mode = rt1711_set_bist_test_mode,
+       .set_bist_carrier_mode = rt1711_set_bist_carrier_mode,
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       .retransmit = rt1711_retransmit,
+#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
+
+};
+
+static int rt_parse_dt(struct rt1711_chip *chip, struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+
+       if (!np)
+               return -EINVAL;
+
+       pr_info("%s\n", __func__);
+       chip->irq_gpio = of_get_named_gpio(np, "rt1711,irq_pin", 0);
+
+       return 0;
+}
+
+static int rt1711_tcpcdev_init(struct rt1711_chip *chip, struct device *dev)
+{
+       struct tcpc_desc *desc;
+       struct device_node *np = dev->of_node;
+       u32 val, len;
+       const char *name = "default";
+
+       desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+       if (of_property_read_u32(np, "rt-tcpc,role_def", &val) >= 0) {
+               if (val >= TYPEC_ROLE_NR)
+                       desc->role_def = TYPEC_ROLE_DRP;
+               else
+                       desc->role_def = val;
+       } else {
+               dev_info(dev, "use default Role DRP\n");
+               desc->role_def = TYPEC_ROLE_DRP;
+       }
+
+       if (of_property_read_u32(
+               np, "rt-tcpc,notifier_supply_num", &val) >= 0) {
+               if (val < 0)
+                       desc->notifier_supply_num = 0;
+               else
+                       desc->notifier_supply_num = val;
+       } else {
+               desc->notifier_supply_num = 0;
+       }
+       if (of_property_read_u32(np, "rt-tcpc,rp_level", &val) >= 0) {
+               switch (val) {
+               case 0: /* RP Default */
+                       desc->rp_lvl = TYPEC_CC_RP_DFT;
+                       break;
+               case 1: /* RP 1.5V */
+                       desc->rp_lvl = TYPEC_CC_RP_1_5;
+                       break;
+               case 2: /* RP 3.0V */
+                       desc->rp_lvl = TYPEC_CC_RP_3_0;
+                       break;
+               default:
+                       break;
+               }
+       }
+       of_property_read_string(np, "rt-tcpc,name", (char const **)&name);
+
+       len = strlen(name);
+       desc->name = kzalloc(len + 1, GFP_KERNEL);
+       strcpy((char *)desc->name, name);
+
+       chip->tcpc_desc = desc;
+
+       chip->tcpc = tcpc_device_register(dev,
+                       desc, &rt1711_tcpc_ops, chip);
+       if (IS_ERR(chip->tcpc))
+               return -EINVAL;
+
+       if (chip->chip_id <= RT1711H_DID_B) {
+               chip->tcpc->tcpc_flags =
+                       TCPC_FLAGS_RETRY_CRC_DISCARD |
+                       TCPC_FLAGS_WAIT_HRESET_COMPLETE |
+                       TCPC_FLAGS_LPM_WAKEUP_WATCHDOG;
+       } else {
+               chip->tcpc->tcpc_flags =
+                       TCPC_FLAGS_RETRY_CRC_DISCARD |
+                       TCPC_FLAGS_WAIT_HRESET_COMPLETE |
+                       TCPC_FLAGS_CHECK_RA_DETACHE;
+       }
+       return 0;
+}
+
+#define RICHTEK_1711_VID       0x29cf
+#define RICHTEK_1711_PID       0x1711
+
+static inline int rt1711h_check_revision(struct i2c_client *client)
+{
+       u16 vid, pid, did;
+       int ret;
+       u8 data = 1;
+
+       ret = rt1711_read_device(client, TCPC_V10_REG_VID, 2, &vid);
+       if (ret < 0) {
+               dev_err(&client->dev, "read chip ID fail\n");
+               return -EIO;
+       }
+
+       if (vid != RICHTEK_1711_VID) {
+               pr_info("%s failed, VID=0x%04x\n", __func__, vid);
+               return -ENODEV;
+       }
+
+       ret = rt1711_read_device(client, TCPC_V10_REG_PID, 2, &pid);
+       if (ret < 0) {
+               dev_err(&client->dev, "read product ID fail\n");
+               return -EIO;
+       }
+
+       if (pid != RICHTEK_1711_PID) {
+               pr_info("%s failed, PID=0x%04x\n", __func__, pid);
+               return -ENODEV;
+       }
+
+       ret = rt1711_write_device(client, RT1711H_REG_SWRESET, 1, &data);
+       if (ret < 0)
+               return ret;
+
+       mdelay(1);
+
+       ret = rt1711_read_device(client, TCPC_V10_REG_DID, 2, &did);
+       if (ret < 0) {
+               dev_err(&client->dev, "read device ID fail\n");
+               return -EIO;
+       }
+
+       return did;
+}
+
+static int rt1711_i2c_probe(
+               struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct rt1711_chip *chip;
+       int ret = 0, chip_id;
+       bool use_dt = client->dev.of_node;
+
+       pr_info("%s\n", __func__);
+
+       if (i2c_check_functionality(
+                               client->adapter,
+                               I2C_FUNC_SMBUS_I2C_BLOCK |
+                               I2C_FUNC_SMBUS_BYTE_DATA))
+               pr_info("I2C functionality : OK...\n");
+       else
+               pr_info("I2C functionality check : failuare...\n");
+
+       chip_id = rt1711h_check_revision(client);
+       if (chip_id < 0)
+               return ret;
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       if (use_dt) {
+               rt_parse_dt(chip, &client->dev);
+       } else {
+               dev_err(&client->dev, "no dts node\n");
+               return -ENODEV;
+       }
+       chip->dev = &client->dev;
+       chip->client = client;
+       sema_init(&chip->io_lock, 1);
+       sema_init(&chip->suspend_lock, 1);
+       i2c_set_clientdata(client, chip);
+       INIT_DELAYED_WORK(&chip->poll_work, rt1711_poll_work);
+
+       chip->chip_id = chip_id;
+       pr_info("rt1711h_chipID = 0x%0x\n", chip_id);
+
+       ret = rt1711_regmap_init(chip);
+       if (ret < 0) {
+               dev_err(chip->dev, "rt1711 regmap init fail\n");
+               return -EINVAL;
+       }
+
+       ret = rt1711_tcpcdev_init(chip, &client->dev);
+       if (ret < 0) {
+               dev_err(&client->dev, "rt1711 tcpc dev init fail\n");
+               goto err_tcpc_reg;
+       }
+
+       ret = rt1711_init_alert(chip->tcpc);
+       if (ret < 0) {
+               pr_err("rt1711 init alert fail\n");
+               goto err_irq_init;
+       }
+
+       tcpc_schedule_init_work(chip->tcpc);
+
+       pr_info("%s probe OK!\n", __func__);
+       return 0;
+
+err_irq_init:
+       tcpc_device_unregister(chip->dev, chip->tcpc);
+err_tcpc_reg:
+       rt1711_regmap_deinit(chip);
+       return ret;
+}
+
+static int rt1711_i2c_remove(struct i2c_client *client)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(client);
+
+       if (chip) {
+               cancel_delayed_work_sync(&chip->poll_work);
+
+               tcpc_device_unregister(chip->dev, chip->tcpc);
+               rt1711_regmap_deinit(chip);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt1711_i2c_suspend(struct device *dev)
+{
+       struct rt1711_chip *chip;
+       struct i2c_client *client = to_i2c_client(dev);
+
+       if (client) {
+               chip = i2c_get_clientdata(client);
+               if (chip)
+                       down(&chip->suspend_lock);
+       }
+
+       return 0;
+}
+
+static int rt1711_i2c_resume(struct device *dev)
+{
+       struct rt1711_chip *chip;
+       struct i2c_client *client = to_i2c_client(dev);
+
+       if (client) {
+               chip = i2c_get_clientdata(client);
+               if (chip)
+                       up(&chip->suspend_lock);
+       }
+
+       return 0;
+}
+
+static void rt1711_shutdown(struct i2c_client *client)
+{
+       struct rt1711_chip *chip = i2c_get_clientdata(client);
+
+       /* Please reset IC here */
+       if (chip && chip->irq)
+               disable_irq(chip->irq);
+       i2c_smbus_write_byte_data(client, RT1711H_REG_SWRESET, 0x01);
+}
+
+static int rt1711_pm_suspend_runtime(struct device *device)
+{
+       dev_dbg(device, "pm_runtime: suspending...\n");
+       return 0;
+}
+
+static int rt1711_pm_resume_runtime(struct device *device)
+{
+       dev_dbg(device, "pm_runtime: resuming...\n");
+       return 0;
+}
+
+static const struct dev_pm_ops rt1711_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(
+                       rt1711_i2c_suspend,
+                       rt1711_i2c_resume)
+       SET_RUNTIME_PM_OPS(
+               rt1711_pm_suspend_runtime,
+               rt1711_pm_resume_runtime,
+               NULL
+       )
+};
+
+#define RT1711_PM_OPS  (&rt1711_pm_ops)
+#else
+#define RT1711_PM_OPS  (NULL)
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id rt1711_id_table[] = {
+       {"rt1711", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, rt1711_id_table);
+
+static const struct of_device_id rt_match_table[] = {
+       {.compatible = "richtek,rt1711",},
+       {},
+};
+
+static struct i2c_driver rt1711_driver = {
+       .driver = {
+               .name = "rt1711h",
+               .owner = THIS_MODULE,
+               .of_match_table = rt_match_table,
+               .pm = RT1711_PM_OPS,
+       },
+       .probe = rt1711_i2c_probe,
+       .remove = rt1711_i2c_remove,
+       .shutdown = rt1711_shutdown,
+       .id_table = rt1711_id_table,
+};
+
+static int __init rt1711_init(void)
+{
+       struct device_node *np;
+
+       pr_info("rt1711h_init (%s): initializing...\n", RT1711H_DRV_VERSION);
+       np = of_find_node_by_name(NULL, "rt1711");
+       if (np)
+               pr_info("rt1711h node found...\n");
+       else
+               pr_info("rt1711h node not found...\n");
+
+       return i2c_add_driver(&rt1711_driver);
+}
+module_init(rt1711_init);
+
+static void __exit rt1711_exit(void)
+{
+       i2c_del_driver(&rt1711_driver);
+}
+module_exit(rt1711_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
+MODULE_DESCRIPTION("RT1711 TCPC Driver");
+MODULE_VERSION(RT1711H_DRV_VERSION);
diff --git a/drivers/usb/pd/richtek/tcpci_alert.c b/drivers/usb/pd/richtek/tcpci_alert.c
new file mode 100644 (file)
index 0000000..566419c
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * TCPC Interface for alert handler
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+static int tcpci_alert_cc_changed(struct tcpc_device *tcpc_dev)
+{
+       return tcpc_typec_handle_cc_change(tcpc_dev);
+}
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+
+static inline int tcpci_alert_vsafe0v(struct tcpc_device *tcpc_dev)
+{
+       tcpc_typec_handle_vsafe0v(tcpc_dev);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       pd_put_vbus_safe0v_event(tcpc_dev);
+#endif
+
+       return 0;
+}
+
+#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
+
+void tcpci_vbus_level_init(struct tcpc_device *tcpc_dev, u16 power_status)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+
+       tcpc_dev->vbus_level =
+                       power_status & TCPC_REG_POWER_STATUS_VBUS_PRES ?
+                       TCPC_VBUS_VALID : TCPC_VBUS_INVALID;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       if (power_status & TCPC_REG_POWER_STATUS_EXT_VSAFE0V) {
+               if (tcpc_dev->vbus_level == TCPC_VBUS_INVALID)
+                       tcpc_dev->vbus_level = TCPC_VBUS_SAFE0V;
+               else
+                       TCPC_INFO("ps_confused: 0x%02x\r\n", power_status);
+       }
+#endif
+
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+static int tcpci_alert_power_status_changed(struct tcpc_device *tcpc_dev)
+{
+       int rv = 0;
+       u16 power_status = 0;
+
+       rv = tcpci_get_power_status(tcpc_dev, &power_status);
+       if (rv < 0)
+               return rv;
+
+       tcpci_vbus_level_init(tcpc_dev, power_status);
+
+       TCPC_INFO("ps_change=%d\r\n", tcpc_dev->vbus_level);
+       rv = tcpc_typec_handle_ps_change(
+                       tcpc_dev,
+                       tcpc_dev->vbus_level == TCPC_VBUS_VALID);
+       if (rv < 0)
+               return rv;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       pd_put_vbus_changed_event(tcpc_dev, true);
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       if (tcpc_dev->vbus_level == TCPC_VBUS_SAFE0V)
+               rv = tcpci_alert_vsafe0v(tcpc_dev);
+#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
+
+       return rv;
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+static int tcpci_alert_tx_success(struct tcpc_device *tcpc_dev)
+{
+       u8 tx_state;
+
+       pd_event_t evt = {
+               .event_type = PD_EVT_CTRL_MSG,
+               .msg = PD_CTRL_GOOD_CRC,
+               .pd_msg = NULL,
+       };
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tx_state = tcpc_dev->pd_transmit_state;
+       tcpc_dev->pd_transmit_state = PD_TX_STATE_GOOD_CRC;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
+               pd_put_vdm_event(tcpc_dev, &evt, false);
+       else
+               pd_put_event(tcpc_dev, &evt, false);
+
+       return 0;
+}
+
+static int tcpci_alert_tx_failed(struct tcpc_device *tcpc_dev)
+{
+       u8 tx_state;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tx_state = tcpc_dev->pd_transmit_state;
+       tcpc_dev->pd_transmit_state = PD_TX_STATE_NO_GOOD_CRC;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
+               vdm_put_hw_event(tcpc_dev, PD_HW_TX_FAILED);
+       else
+               pd_put_hw_event(tcpc_dev, PD_HW_TX_FAILED);
+
+       return 0;
+}
+
+static int tcpci_alert_tx_discard(struct tcpc_device *tcpc_dev)
+{
+       u8 tx_state;
+       bool retry_crc_discard = false;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tx_state = tcpc_dev->pd_transmit_state;
+       tcpc_dev->pd_transmit_state = PD_TX_STATE_DISCARD;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       TCPC_INFO("Discard\r\n");
+
+       if (tx_state == PD_TX_STATE_WAIT_CRC_VDM) {
+               pd_put_last_vdm_event(tcpc_dev);
+       } else {
+               retry_crc_discard =
+                       (tcpc_dev->tcpc_flags &
+                                       TCPC_FLAGS_RETRY_CRC_DISCARD) != 0;
+
+               if (retry_crc_discard) {
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+                       tcpc_dev->pd_discard_pending = true;
+                       tcpc_enable_timer(tcpc_dev, PD_TIMER_DISCARD);
+#else
+                       TCPC_ERR("RETRY_CRC_DISCARD\r\n");
+#endif
+               } else {
+                       pd_put_hw_event(tcpc_dev, PD_HW_TX_FAILED);
+               }
+       }
+       return 0;
+}
+
+static int tcpci_alert_recv_msg(struct tcpc_device *tcpc_dev)
+{
+       int retval;
+       pd_msg_t *pd_msg;
+       enum tcpm_transmit_type type;
+
+       pd_msg = pd_alloc_msg(tcpc_dev);
+       if (!pd_msg)
+               return -1;      /* TODO */
+
+       retval = tcpci_get_message(tcpc_dev,
+                                  pd_msg->payload, &pd_msg->msg_hdr, &type);
+       if (retval < 0) {
+               TCPC_INFO("recv_msg failed: %d\r\n", retval);
+               pd_free_msg(tcpc_dev, pd_msg);
+               return retval;
+       }
+
+       pd_msg->frame_type = (u8)type;
+       pd_put_pd_msg_event(tcpc_dev, pd_msg);
+       return 0;
+}
+
+static int tcpci_alert_rx_overflow(struct tcpc_device *tcpc_dev)
+{
+       TCPC_INFO("RX_OVERFLOW\r\n");
+       return 0;
+}
+
+static int tcpci_alert_fault(struct tcpc_device *tcpc_dev)
+{
+       u8 status = 0;
+
+       tcpci_get_fault_status(tcpc_dev, &status);
+       TCPC_INFO("FaultAlert=0x%x\r\n", status);
+       tcpci_fault_status_clear(tcpc_dev, status);
+       return 0;
+}
+
+static int tcpci_alert_recv_hard_reset(struct tcpc_device *tcpc_dev)
+{
+       TCPC_INFO("HardResetAlert\r\n");
+       pd_put_recv_hard_reset_event(tcpc_dev);
+       return 0;
+}
+
+#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
+static int tcpci_alert_wakeup(struct tcpc_device *tcpc_dev)
+{
+       if (tcpc_dev->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) {
+               TCPC_DBG("Wakeup\r\n");
+
+               if (tcpc_dev->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING &&
+                   tcpc_dev->typec_remote_cc[1] == TYPEC_CC_DRP_TOGGLING)
+                       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_WAKEUP);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+typedef struct __tcpci_alert_handler {
+       u32 bit_mask;
+       int (*handler)(struct tcpc_device *tcpc_dev);
+} tcpci_alert_handler_t;
+
+#define DECL_TCPCI_ALERT_HANDLER(xbit, xhandler) {\
+               .bit_mask = 1 << (xbit),\
+               .handler = xhandler, \
+       }
+
+const tcpci_alert_handler_t tcpci_alert_handlers[] = {
+#ifdef CONFIG_USB_POWER_DELIVERY
+       DECL_TCPCI_ALERT_HANDLER(4, tcpci_alert_tx_failed),
+       DECL_TCPCI_ALERT_HANDLER(5, tcpci_alert_tx_discard),
+       DECL_TCPCI_ALERT_HANDLER(6, tcpci_alert_tx_success),
+       DECL_TCPCI_ALERT_HANDLER(2, tcpci_alert_recv_msg),
+       DECL_TCPCI_ALERT_HANDLER(7, NULL),
+       DECL_TCPCI_ALERT_HANDLER(8, NULL),
+       DECL_TCPCI_ALERT_HANDLER(3, tcpci_alert_recv_hard_reset),
+       DECL_TCPCI_ALERT_HANDLER(10, tcpci_alert_rx_overflow),
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
+       DECL_TCPCI_ALERT_HANDLER(16, tcpci_alert_wakeup),
+#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
+       DECL_TCPCI_ALERT_HANDLER(9, tcpci_alert_fault),
+       DECL_TCPCI_ALERT_HANDLER(0, tcpci_alert_cc_changed),
+       DECL_TCPCI_ALERT_HANDLER(1, tcpci_alert_power_status_changed),
+};
+
+static inline int __tcpci_alert(struct tcpc_device *tcpc_dev)
+{
+       int rv, i;
+       u32 alert_status;
+       const u32 alert_rx =
+               TCPC_REG_ALERT_RX_STATUS | TCPC_REG_ALERT_RX_BUF_OVF;
+
+       const u32 alert_sent_hreset =
+               TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED;
+
+       rv = tcpci_get_alert_status(tcpc_dev, &alert_status);
+       if (rv)
+               return rv;
+
+       tcpci_alert_status_clear(tcpc_dev, alert_status & (~alert_rx));
+
+       if (tcpc_dev->typec_role == TYPEC_ROLE_UNKNOWN) {
+               TCPC_INFO("SkipAlert:0x%04x\r\n", alert_status);
+               return 0;
+       }
+
+       if (alert_status & TCPC_REG_ALERT_EXT_VBUS_80)
+               alert_status |= TCPC_REG_ALERT_POWER_STATUS;
+
+#ifdef CONFIG_TYPEC_CAP_RA_DETACH
+       if ((alert_status & TCPC_REG_ALERT_EXT_RA_DETACH) &&
+           (tcpc_dev->tcpc_flags & TCPC_FLAGS_CHECK_RA_DETACHE))
+               alert_status |= TCPC_REG_ALERT_CC_STATUS;
+#endif /* CONFIG_TYPEC_CAP_RA_DETACH */
+
+#ifdef CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER
+       if ((alert_status & alert_sent_hreset) == alert_sent_hreset) {
+               if (tcpc_dev->tcpc_flags & TCPC_FLAGS_WAIT_HRESET_COMPLETE) {
+                       alert_status &= ~alert_sent_hreset;
+                       pd_put_sent_hard_reset_event(tcpc_dev);
+               }
+       }
+#endif /* CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER */
+
+       for (i = 0; i < ARRAY_SIZE(tcpci_alert_handlers); i++) {
+               if (tcpci_alert_handlers[i].bit_mask & alert_status) {
+                       if (tcpci_alert_handlers[i].handler != 0)
+                               tcpci_alert_handlers[i].handler(tcpc_dev);
+               }
+       }
+       return 0;
+}
+
+int tcpci_alert(struct tcpc_device *tcpc_dev)
+{
+       int ret;
+
+       ret = __tcpci_alert(tcpc_dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(tcpci_alert);
+
+/*
+ * [BLOCK] TYPEC device changed
+ */
+
+static inline int tcpci_report_usb_port_attached(struct tcpc_device *tcpc)
+{
+       TCPC_INFO("usb_port_attached\r\n");
+
+       __pm_relax(&tcpc->dettach_temp_wake_lock);
+       __pm_stay_awake(&tcpc->attach_wake_lock);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new);
+#endif /* CONFIG_USB_POWER_DLEIVERY */
+
+       return 0;
+}
+
+static inline int tcpci_report_usb_port_detached(struct tcpc_device *tcpc)
+{
+       TCPC_INFO("usb_port_detached\r\n");
+
+       __pm_wakeup_event(&tcpc->dettach_temp_wake_lock,
+                         jiffies_to_msecs(msecs_to_jiffies(5 * 1000)));
+       __pm_relax(&tcpc->attach_wake_lock);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       pd_put_cc_detached_event(tcpc);
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       return 0;
+}
+
+int tcpci_report_usb_port_changed(struct tcpc_device *tcpc)
+{
+       tcpci_notify_typec_state(tcpc);
+
+       if (tcpc->typec_attach_old == TYPEC_UNATTACHED)
+               tcpci_report_usb_port_attached(tcpc);
+       else if (tcpc->typec_attach_new == TYPEC_UNATTACHED)
+               tcpci_report_usb_port_detached(tcpc);
+       else
+               TCPC_DBG("TCPC Attach Again\r\n");
+
+       return 0;
+}
+EXPORT_SYMBOL(tcpci_report_usb_port_changed);
diff --git a/drivers/usb/pd/richtek/tcpci_core.c b/drivers/usb/pd/richtek/tcpci_core.c
new file mode 100644 (file)
index 0000000..cc98f7d
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Richtek TypeC Port Control Interface Core Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/gpio.h>
+#include <linux/hisi/log/hisi_log.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+#include "pd_dpm_prv.h"
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#define TCPC_CORE_VERSION              "1.1.1_G"
+
+static ssize_t tcpc_show_property(struct device *dev,
+                                 struct device_attribute *attr, char *buf);
+static ssize_t tcpc_store_property(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count);
+
+#define TCPC_DEVICE_ATTR(_name, _mode)                                 \
+{                                                                      \
+       .attr = { .name = #_name, .mode = _mode },                      \
+       .show = tcpc_show_property,                                     \
+       .store = tcpc_store_property,                                   \
+}
+
+static struct class *tcpc_class;
+EXPORT_SYMBOL_GPL(tcpc_class);
+
+static struct device_type tcpc_dev_type;
+
+static struct device_attribute tcpc_device_attributes[] = {
+       TCPC_DEVICE_ATTR(role_def, 0444),
+       TCPC_DEVICE_ATTR(rp_lvl, 0444),
+       TCPC_DEVICE_ATTR(pd_test, 0664),
+       TCPC_DEVICE_ATTR(info, 0444),
+       TCPC_DEVICE_ATTR(timer, 0664),
+       TCPC_DEVICE_ATTR(caps_info, 0444),
+       TCPC_DEVICE_ATTR(cc_orient_info, 0444),
+       TCPC_DEVICE_ATTR(remote_rp_lvl, 0444),
+};
+
+enum {
+       TCPC_DESC_ROLE_DEF = 0,
+       TCPC_DESC_RP_LEVEL,
+       TCPC_DESC_PD_TEST,
+       TCPC_DESC_INFO,
+       TCPC_DESC_TIMER,
+       TCPC_DESC_CAP_INFO,
+       TCPC_DESC_CC_ORIENT_INFO,
+       TCPC_DESC_REMOTE_RP_LEVEL,
+};
+
+static struct attribute *__tcpc_attrs[ARRAY_SIZE(tcpc_device_attributes) + 1];
+static struct attribute_group tcpc_attr_group = {
+       .attrs = __tcpc_attrs,
+};
+
+static const struct attribute_group *tcpc_attr_groups[] = {
+       &tcpc_attr_group,
+       NULL,
+};
+
+static const char * const role_text[] = {
+       "SNK Only",
+       "SRC Only",
+       "DRP",
+       "Try.SRC",
+       "Try.SNK",
+};
+
+static ssize_t tcpc_show_property(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct tcpc_device *tcpc = to_tcpc_device(dev);
+       const ptrdiff_t offset = attr - tcpc_device_attributes;
+       int i = 0;
+       int vmin, vmax, ioper;
+       u8 cc1, cc2;
+       bool from_ic = true;
+       char cc1_buf[32] = {0};
+       char cc2_buf[32] = {0};
+
+       switch (offset) {
+       case TCPC_DESC_CC_ORIENT_INFO:
+               snprintf(buf, 256, "%s\n", tcpc->typec_polarity ? "2" : "1");
+               TCPC_DBG("%s typec_polarity=%s\n", __func__, buf);
+               break;
+       case TCPC_DESC_CAP_INFO:
+               snprintf(buf + strlen(buf), 256, "%s = %d\n%s = %d\n",
+                        "local_selected_cap",
+                       tcpc->pd_port.local_selected_cap,
+                       "remote_selected_cap",
+                       tcpc->pd_port.remote_selected_cap);
+
+               snprintf(buf + strlen(buf), 256, "%s\n",
+                        "local_src_cap(vmin, vmax, ioper)");
+               for (i = 0; i < tcpc->pd_port.local_src_cap.nr; i++) {
+                       pd_extract_pdo_power(
+                               tcpc->pd_port.local_src_cap.pdos[i],
+                               &vmin, &vmax, &ioper);
+                       snprintf(buf + strlen(buf), 256, "%d %d %d\n",
+                                vmin, vmax, ioper);
+               }
+               snprintf(buf + strlen(buf), 256, "%s\n",
+                        "local_snk_cap(vmin, vmax, ioper)");
+               for (i = 0; i < tcpc->pd_port.local_snk_cap.nr; i++) {
+                       pd_extract_pdo_power(
+                               tcpc->pd_port.local_snk_cap.pdos[i],
+                               &vmin, &vmax, &ioper);
+                       snprintf(buf + strlen(buf), 256, "%d %d %d\n",
+                                vmin, vmax, ioper);
+               }
+               snprintf(buf + strlen(buf), 256, "%s\n",
+                        "remote_src_cap(vmin, vmax, ioper)");
+               for (i = 0; i < tcpc->pd_port.remote_src_cap.nr; i++) {
+                       pd_extract_pdo_power(
+                               tcpc->pd_port.remote_src_cap.pdos[i],
+                               &vmin, &vmax, &ioper);
+                       snprintf(buf + strlen(buf), 256, "%d %d %d\n",
+                                vmin, vmax, ioper);
+               }
+               snprintf(buf + strlen(buf), 256, "%s\n",
+                        "remote_snk_cap(vmin, vmax, ioper)");
+               for (i = 0; i < tcpc->pd_port.remote_snk_cap.nr; i++) {
+                       pd_extract_pdo_power(
+                               tcpc->pd_port.remote_snk_cap.pdos[i],
+                               &vmin, &vmax, &ioper);
+                       snprintf(buf + strlen(buf), 256, "%d %d %d\n",
+                                vmin, vmax, ioper);
+               }
+               break;
+       case TCPC_DESC_ROLE_DEF:
+               snprintf(buf, 256, "%s\n", role_text[tcpc->desc.role_def]);
+               break;
+       case TCPC_DESC_RP_LEVEL:
+               if (tcpc->typec_local_rp_level == TYPEC_CC_RP_DFT)
+                       snprintf(buf, 256, "%s\n", "Default");
+               else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_1_5)
+                       snprintf(buf, 256, "%s\n", "1.5");
+               else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_3_0)
+                       snprintf(buf, 256, "%s\n", "3.0");
+               break;
+       case TCPC_DESC_REMOTE_RP_LEVEL:
+               tcpm_inquire_remote_cc(tcpc, &cc1, &cc2, from_ic);
+
+               if (cc1 == TYPEC_CC_VOLT_OPEN)
+                       snprintf(cc1_buf, 256, "%s\n", "OPEN");
+               else if (cc1 == TYPEC_CC_VOLT_RA)
+                       snprintf(cc1_buf, 256, "%s\n", "RA");
+               else if (cc1 == TYPEC_CC_VOLT_RD)
+                       snprintf(cc1_buf, 256, "%s\n", "RD");
+               else if (cc1 == TYPEC_CC_VOLT_SNK_DFT)
+                       snprintf(cc1_buf, 256, "%s\n", "Default");
+               else if (cc1 == TYPEC_CC_VOLT_SNK_1_5)
+                       snprintf(cc1_buf, 256, "%s\n", "1.5");
+               else if (cc1 == TYPEC_CC_VOLT_SNK_3_0)
+                       snprintf(cc1_buf, 256, "%s\n", "3.0");
+               else if (cc1 == TYPEC_CC_DRP_TOGGLING)
+                       snprintf(cc1_buf, 256, "%s\n", "DRP");
+               else
+                       snprintf(cc1_buf, 256, "%s\n", "NULL");
+
+               if (cc2 == TYPEC_CC_VOLT_OPEN)
+                       snprintf(cc2_buf, 256, "%s\n", "OPEN");
+               else if (cc2 == TYPEC_CC_VOLT_RA)
+                       snprintf(cc2_buf, 256, "%s\n", "RA");
+               else if (cc2 == TYPEC_CC_VOLT_RD)
+                       snprintf(cc2_buf, 256, "%s\n", "RD");
+               else if (cc2 == TYPEC_CC_VOLT_SNK_DFT)
+                       snprintf(cc2_buf, 256, "%s\n", "Default");
+               else if (cc2 == TYPEC_CC_VOLT_SNK_1_5)
+                       snprintf(cc2_buf, 256, "%s\n", "1.5");
+               else if (cc2 == TYPEC_CC_VOLT_SNK_3_0)
+                       snprintf(cc2_buf, 256, "%s\n", "3.0");
+               else if (cc2 == TYPEC_CC_DRP_TOGGLING)
+                       snprintf(cc2_buf, 256, "%s\n", "DRP");
+               else
+                       snprintf(cc1_buf, 256, "%s\n", "NULL");
+
+               snprintf(buf, 256, " cc1 %s cc2 %s\n", cc1_buf, cc2_buf);
+
+               break;
+       case TCPC_DESC_PD_TEST:
+               snprintf(buf,
+                        256, "%s\n%s\n%s\n%s\n%s\n", "1: Power Role Swap Test",
+                               "2: Data Role Swap Test", "3: Vconn Swap Test",
+                               "4: soft reset", "5: hard reset");
+               break;
+       case TCPC_DESC_INFO:
+               i += snprintf(buf + i,
+                       256, "|^|==( %s info )==|^|\n", tcpc->desc.name);
+               i += snprintf(buf + i,
+                       256, "role = %s\n", role_text[tcpc->desc.role_def]);
+               if (tcpc->typec_local_rp_level == TYPEC_CC_RP_DFT)
+                       i += snprintf(buf + i, 256, "rplvl = %s\n", "Default");
+               else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_1_5)
+                       i += snprintf(buf + i, 256, "rplvl = %s\n", "1.5");
+               else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_3_0)
+                       i += snprintf(buf + i, 256, "rplvl = %s\n", "3.0");
+               break;
+       default:
+               break;
+       }
+       return strlen(buf);
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+       char *token;
+       int base, cnt;
+
+       token = strsep(&buf, " ");
+
+       for (cnt = 0; cnt < num_of_par; cnt++) {
+               if (token) {
+                       if ((token[1] == 'x') || (token[1] == 'X'))
+                               base = 16;
+                       else
+                               base = 10;
+
+                       if (kstrtoul(token, base, &param1[cnt]) != 0)
+                               return -EINVAL;
+
+                       token = strsep(&buf, " ");
+               } else {
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static ssize_t tcpc_store_property(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct tcpc_device *tcpc = to_tcpc_device(dev);
+       struct tcpm_power_cap cap;
+       const ptrdiff_t offset = attr - tcpc_device_attributes;
+       int ret;
+       long int val;
+
+       switch (offset) {
+       case TCPC_DESC_ROLE_DEF:
+               ret = get_parameters((char *)buf, &val, 1);
+               if (ret < 0) {
+                       dev_err(dev, "get parameters fail\n");
+                       return -EINVAL;
+               }
+
+               tcpm_typec_change_role(tcpc, val);
+               break;
+       case TCPC_DESC_TIMER:
+               ret = get_parameters((char *)buf, &val, 1);
+               if (ret < 0) {
+                       dev_err(dev, "get parameters fail\n");
+                       return -EINVAL;
+               }
+               #ifdef CONFIG_USB_POWER_DELIVERY
+               if (val > 0 && val <= PD_PE_TIMER_END_ID)
+                       pd_enable_timer(&tcpc->pd_port, val);
+               else if (val > PD_PE_TIMER_END_ID && val < PD_TIMER_NR)
+                       tcpc_enable_timer(tcpc, val);
+               #else
+               if (val > 0 && val < PD_TIMER_NR)
+                       tcpc_enable_timer(tcpc, val);
+               #endif /* CONFIG_USB_POWER_DELIVERY */
+               break;
+       #ifdef CONFIG_USB_POWER_DELIVERY
+       case TCPC_DESC_PD_TEST:
+               ret = get_parameters((char *)buf, &val, 1);
+               if (ret < 0) {
+                       dev_err(dev, "get parameters fail\n");
+                       return -EINVAL;
+               }
+               switch (val) {
+               case 1: /* Power Role Swap */
+                       tcpm_power_role_swap(tcpc);
+                       break;
+               case 2: /* Data Role Swap */
+                       tcpm_data_role_swap(tcpc);
+                       break;
+               case 3: /* Vconn Swap */
+                       tcpm_vconn_swap(tcpc);
+                       break;
+               case 4: /* Software Reset */
+                       tcpm_soft_reset(tcpc);
+                       break;
+               case 5: /* Hardware Reset */
+                       tcpm_hard_reset(tcpc);
+                       break;
+               case 6:
+                       tcpm_get_source_cap(tcpc, &cap);
+                       break;
+               case 7:
+                       tcpm_get_sink_cap(tcpc, &cap);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       #endif /* CONFIG_USB_POWER_DELIVERY */
+       default:
+               break;
+       }
+       return count;
+}
+
+static int tcpc_match_device_by_name(struct device *dev, const void *data)
+{
+       const char *name = data;
+       struct tcpc_device *tcpc = dev_get_drvdata(dev);
+
+       return strcmp(tcpc->desc.name, name) == 0;
+}
+
+struct tcpc_device *tcpc_dev_get_by_name(const char *name)
+{
+       struct device *dev = class_find_device(tcpc_class,
+                       NULL, (const void *)name, tcpc_match_device_by_name);
+       return dev ? dev_get_drvdata(dev) : NULL;
+}
+
+static void tcpc_device_release(struct device *dev)
+{
+       struct tcpc_device *tcpc_dev = to_tcpc_device(dev);
+       char buf[1024] = { 0 };
+
+       pr_info("%s : %s device release\n", __func__, dev_name(dev));
+       if (!tcpc_dev)
+               snprintf(buf, sizeof(buf), "the tcpc device is NULL\n");
+       /* Un-init pe thread */
+#ifdef CONFIG_USB_POWER_DELIVERY
+       tcpci_event_deinit(tcpc_dev);
+#endif /* CONFIG_USB_POWER_DELIVERY */
+       /* Un-init timer thread */
+       tcpci_timer_deinit(tcpc_dev);
+       /* Un-init Mutex */
+       /* Do initialization */
+       devm_kfree(dev, tcpc_dev);
+}
+
+static int pd_dpm_wake_lock_call(struct notifier_block *dpm_nb,
+                                unsigned long event, void *data)
+{
+       struct tcpc_device *tcpc = container_of(dpm_nb,
+                                               struct tcpc_device, dpm_nb);
+
+       switch (event) {
+       case PD_WAKE_LOCK:
+               __pm_stay_awake(&tcpc->attach_wake_lock);
+               break;
+       case PD_WAKE_UNLOCK:
+               __pm_relax(&tcpc->attach_wake_lock);
+               break;
+       default:
+               pr_info("%s unknown event (%lu)\n", __func__, event);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static void tcpc_init_work(struct work_struct *work);
+
+struct tcpc_device *tcpc_device_register(struct device *parent,
+                                        struct tcpc_desc *tcpc_desc,
+                                        struct tcpc_ops *ops, void *drv_data)
+{
+       struct tcpc_device *tcpc;
+       int ret = 0;
+
+       pr_info("%s register tcpc device (%s)\n", __func__, tcpc_desc->name);
+       tcpc = devm_kzalloc(parent, sizeof(*tcpc), GFP_KERNEL);
+       if (!tcpc) {
+               pr_err("%s : allocate tcpc memeory failed\n", __func__);
+               return NULL;
+       }
+
+       tcpc->dev.class = tcpc_class;
+       tcpc->dev.type = &tcpc_dev_type;
+       tcpc->dev.parent = parent;
+       tcpc->dev.release = tcpc_device_release;
+       dev_set_drvdata(&tcpc->dev, tcpc);
+       tcpc->drv_data = drv_data;
+       dev_set_name(&tcpc->dev, tcpc_desc->name);
+       tcpc->desc = *tcpc_desc;
+       tcpc->ops = ops;
+       tcpc->typec_local_rp_level = tcpc_desc->rp_lvl;
+
+       ret = device_register(&tcpc->dev);
+       if (ret) {
+               kfree(tcpc);
+               return ERR_PTR(ret);
+       }
+
+       srcu_init_notifier_head(&tcpc->evt_nh);
+       INIT_DELAYED_WORK(&tcpc->init_work, tcpc_init_work);
+
+       mutex_init(&tcpc->access_lock);
+       mutex_init(&tcpc->typec_lock);
+       mutex_init(&tcpc->timer_lock);
+       sema_init(&tcpc->timer_enable_mask_lock, 1);
+       sema_init(&tcpc->timer_tick_lock, 1);
+
+       /* If system support "WAKE_LOCK_IDLE", */
+       /* please use it instead of "WAKE_LOCK_SUSPEND" */
+       wakeup_source_init(&tcpc->attach_wake_lock,
+                          "tcpc_attach_wakelock");
+       wakeup_source_init(&tcpc->dettach_temp_wake_lock,
+                          "tcpc_detach_wakelock");
+
+       tcpc->dpm_nb.notifier_call = pd_dpm_wake_lock_call;
+       ret = register_pd_wake_unlock_notifier(&tcpc->dpm_nb);
+       if (ret < 0) {
+               hisilog_err("%s register_pd_wake_unlock_notifier failed\n",
+                           __func__);
+       } else {
+               hisilog_info("%s register_pd_wake_unlock_notifier OK\n",
+                            __func__);
+       }
+
+       tcpci_timer_init(tcpc);
+#ifdef CONFIG_USB_POWER_DELIVERY
+       tcpci_event_init(tcpc);
+       pd_core_init(tcpc);
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       return tcpc;
+}
+EXPORT_SYMBOL(tcpc_device_register);
+
+static int tcpc_device_irq_enable(struct tcpc_device *tcpc)
+{
+       int ret;
+
+       TCPC_DBG("%s\n", __func__);
+
+       if (!tcpc->ops->init) {
+               pr_err("%s Please implment tcpc ops init function\n",
+                      __func__);
+               return -EINVAL;
+       }
+
+       ret = tcpci_init(tcpc, false);
+       if (ret < 0) {
+               pr_err("%s tcpc init fail\n", __func__);
+               return ret;
+       }
+
+       tcpci_lock_typec(tcpc);
+       ret = tcpc_typec_init(tcpc, tcpc->desc.role_def + 1);
+       tcpci_unlock_typec(tcpc);
+
+       if (ret < 0) {
+               pr_err("%s : tcpc typec init fail\n", __func__);
+               return ret;
+       }
+
+       pr_info("%s : tcpc irq enable OK!\n", __func__);
+       return 0;
+}
+
+static int tcpc_dec_notifier_supply_num(struct tcpc_device *tcp_dev)
+{
+       if (tcp_dev->desc.notifier_supply_num == 0) {
+               pr_info("%s already started\n", __func__);
+               return 0;
+       }
+
+       tcp_dev->desc.notifier_supply_num--;
+       pr_info("%s supply_num = %d\n", __func__,
+               tcp_dev->desc.notifier_supply_num);
+
+       if (tcp_dev->desc.notifier_supply_num == 0) {
+               cancel_delayed_work(&tcp_dev->init_work);
+               tcpc_device_irq_enable(tcp_dev);
+       }
+
+       return 0;
+}
+
+struct tcpc_device *notify_tcp_dev_ready(const char *name)
+{
+       struct tcpc_device *tcpc = tcpc_dev_get_by_name(name);
+
+       if (!tcpc)
+               return NULL;
+
+       tcpc_dec_notifier_supply_num(tcpc);
+       return tcpc;
+}
+
+static void tcpc_init_work(struct work_struct *work)
+{
+       struct tcpc_device *tcpc = container_of(
+               work, struct tcpc_device, init_work.work);
+
+       if (tcpc->desc.notifier_supply_num == 0)
+               return;
+
+       pr_info("%s force start\n", __func__);
+
+       tcpc->desc.notifier_supply_num = 0;
+       tcpc_device_irq_enable(tcpc);
+}
+
+int tcpc_schedule_init_work(struct tcpc_device *tcpc)
+{
+       if (tcpc->desc.notifier_supply_num == 0)
+               return tcpc_device_irq_enable(tcpc);
+
+       pr_info("%s wait %d num\n", __func__, tcpc->desc.notifier_supply_num);
+
+       schedule_delayed_work(
+               &tcpc->init_work, msecs_to_jiffies(30 * 1000));
+       return 0;
+}
+EXPORT_SYMBOL(tcpc_schedule_init_work);
+
+int register_tcp_dev_notifier(struct tcpc_device *tcp_dev,
+                             struct notifier_block *nb)
+{
+       int ret;
+
+       ret = srcu_notifier_chain_register(&tcp_dev->evt_nh, nb);
+       if (ret != 0)
+               return ret;
+
+       tcpc_dec_notifier_supply_num(tcp_dev);
+       return ret;
+}
+EXPORT_SYMBOL(register_tcp_dev_notifier);
+
+int unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev,
+                               struct notifier_block *nb)
+{
+       return srcu_notifier_chain_unregister(&tcp_dev->evt_nh, nb);
+}
+EXPORT_SYMBOL(unregister_tcp_dev_notifier);
+
+void tcpc_device_unregister(struct device *dev, struct tcpc_device *tcpc)
+{
+       if (!tcpc)
+               return;
+
+       tcpc_typec_deinit(tcpc);
+
+       wakeup_source_trash(&tcpc->dettach_temp_wake_lock);
+       wakeup_source_trash(&tcpc->attach_wake_lock);
+
+       device_unregister(&tcpc->dev);
+}
+EXPORT_SYMBOL(tcpc_device_unregister);
+
+void *tcpc_get_dev_data(struct tcpc_device *tcpc)
+{
+       return tcpc->drv_data;
+}
+EXPORT_SYMBOL(tcpc_get_dev_data);
+
+void tcpci_lock_typec(struct tcpc_device *tcpc)
+{
+       mutex_lock(&tcpc->typec_lock);
+}
+EXPORT_SYMBOL(tcpci_lock_typec);
+
+void tcpci_unlock_typec(struct tcpc_device *tcpc)
+{
+       mutex_unlock(&tcpc->typec_lock);
+}
+EXPORT_SYMBOL(tcpci_unlock_typec);
+
+static void tcpc_init_attrs(struct device_type *dev_type)
+{
+       int i;
+
+       dev_type->groups = tcpc_attr_groups;
+       for (i = 0; i < ARRAY_SIZE(tcpc_device_attributes); i++)
+               __tcpc_attrs[i] = &tcpc_device_attributes[i].attr;
+}
+
+static int __init tcpc_class_init(void)
+{
+       pr_info("%s_%s\n", __func__, TCPC_CORE_VERSION);
+
+       tcpc_class = class_create(THIS_MODULE, "hisi_pd");
+       if (IS_ERR(tcpc_class)) {
+               pr_info("Unable to create tcpc class; errno = %ld\n",
+                       PTR_ERR(tcpc_class));
+               return PTR_ERR(tcpc_class);
+       }
+       tcpc_init_attrs(&tcpc_dev_type);
+       //tcpc_class->suspend = NULL;
+       //tcpc_class->resume = NULL;
+
+       pr_info("TCPC class init OK\n");
+       return 0;
+}
+
+static void __exit tcpc_class_exit(void)
+{
+       class_destroy(tcpc_class);
+       pr_info("TCPC class un-init OK\n");
+}
+
+subsys_initcall(tcpc_class_init);
+module_exit(tcpc_class_exit);
+
+MODULE_DESCRIPTION("Richtek TypeC Port Control Core");
+MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
+MODULE_VERSION(TCPC_CORE_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/pd/richtek/tcpci_event.c b/drivers/usb/pd/richtek/tcpci_event.c
new file mode 100644 (file)
index 0000000..f6cf5ae
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * TCPC Interface for event handler
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kthread.h>
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/version.h>
+#include <linux/sched/rt.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+#ifdef CONFIG_USB_PD_POSTPONE_VDM
+static void postpone_vdm_event(struct tcpc_device *tcpc_dev)
+{
+       /*
+        * Postpone VDM retry event due to the retry reason
+        * maybe interrupt by some PD event ....
+        */
+
+        pd_event_t *vdm_event = &tcpc_dev->pd_vdm_event;
+
+       if (tcpc_dev->pd_pending_vdm_event && vdm_event->pd_msg) {
+               tcpc_dev->pd_postpone_vdm_timeout = false;
+               tcpc_restart_timer(tcpc_dev, PD_PE_VDM_POSTPONE);
+       }
+}
+#endif /* CONFIG_USB_PD_POSTPONE_VDM */
+
+pd_msg_t *__pd_alloc_msg(struct tcpc_device *tcpc_dev)
+{
+       int i;
+       u8 mask;
+       char buf[1024] = { 0 };
+
+       for (i = 0, mask = 1; i < PD_MSG_BUF_SIZE; i++, mask <<= 1) {
+               if ((mask & tcpc_dev->pd_msg_buffer_allocated) == 0) {
+                       tcpc_dev->pd_msg_buffer_allocated |= mask;
+                       return tcpc_dev->pd_msg_buffer + i;
+               }
+       }
+
+       PD_ERR("pd_alloc_msg failed\r\n");
+       if (true)
+               snprintf(buf, sizeof(buf), "pd alloc msg failed\n");
+       return (pd_msg_t *)NULL;
+}
+
+pd_msg_t *pd_alloc_msg(struct tcpc_device *tcpc_dev)
+{
+       pd_msg_t *pd_msg = NULL;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       pd_msg = __pd_alloc_msg(tcpc_dev);
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       return pd_msg;
+}
+
+static void __pd_free_msg(struct tcpc_device *tcpc_dev, pd_msg_t *pd_msg)
+{
+       int index = pd_msg - tcpc_dev->pd_msg_buffer;
+       u8 mask = 1 << index;
+       char buf[1024] = { 0 };
+
+       if ((mask & tcpc_dev->pd_msg_buffer_allocated) == 0)
+               snprintf(buf, sizeof(buf), "pd free msg failed\n");
+       tcpc_dev->pd_msg_buffer_allocated &= (~mask);
+}
+
+static void __pd_free_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event)
+{
+       if (pd_event->pd_msg) {
+               __pd_free_msg(tcpc_dev, pd_event->pd_msg);
+               pd_event->pd_msg = NULL;
+       }
+}
+
+void pd_free_msg(struct tcpc_device *tcpc_dev, pd_msg_t *pd_msg)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       __pd_free_msg(tcpc_dev, pd_msg);
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_free_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       __pd_free_event(tcpc_dev, pd_event);
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static bool __pd_get_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event)
+{
+       int index = 0;
+
+       if (tcpc_dev->pd_event_count <= 0)
+               return false;
+
+       tcpc_dev->pd_event_count--;
+
+       *pd_event = tcpc_dev->pd_event_ring_buffer[
+               tcpc_dev->pd_event_head_index];
+
+       if (tcpc_dev->pd_event_count) {
+               index = tcpc_dev->pd_event_head_index + 1;
+               index %= PD_EVENT_BUF_SIZE;
+       }
+       tcpc_dev->pd_event_head_index = index;
+       return true;
+}
+
+bool pd_get_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event)
+{
+       bool ret;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       ret = __pd_get_event(tcpc_dev, pd_event);
+       mutex_unlock(&tcpc_dev->access_lock);
+       return ret;
+}
+
+static bool __pd_put_event(struct tcpc_device *tcpc_dev,
+                          const pd_event_t *pd_event, bool from_port_partner)
+{
+       int index;
+
+#ifdef CONFIG_USB_PD_POSTPONE_OTHER_VDM
+       if (from_port_partner)
+               postpone_vdm_event(tcpc_dev);
+#endif
+
+       if (tcpc_dev->pd_event_count >= PD_EVENT_BUF_SIZE) {
+               PD_ERR("pd_put_event failed\r\n");
+               return false;
+       }
+
+       index = (tcpc_dev->pd_event_head_index + tcpc_dev->pd_event_count);
+       index %= PD_EVENT_BUF_SIZE;
+
+       tcpc_dev->pd_event_count++;
+       tcpc_dev->pd_event_ring_buffer[index] = *pd_event;
+
+       atomic_inc(&tcpc_dev->pending_event);
+       wake_up_interruptible(&tcpc_dev->event_loop_wait_que);
+       return true;
+}
+
+bool pd_put_event(struct tcpc_device *tcpc_dev, const pd_event_t *pd_event,
+                 bool from_port_partner)
+{
+       bool ret;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       ret = __pd_put_event(tcpc_dev, pd_event, from_port_partner);
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+bool pd_get_vdm_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event)
+{
+       pd_event_t delay_evt = {
+               .event_type = PD_EVT_CTRL_MSG,
+               .msg = PD_CTRL_GOOD_CRC,
+               .pd_msg = NULL,
+       };
+
+       pd_event_t *vdm_event = &tcpc_dev->pd_vdm_event;
+
+       if (tcpc_dev->pd_pending_vdm_event) {
+               if (vdm_event->pd_msg && !tcpc_dev->pd_postpone_vdm_timeout)
+                       return false;
+
+               mutex_lock(&tcpc_dev->access_lock);
+               if (tcpc_dev->pd_pending_vdm_good_crc) {
+                       *pd_event = delay_evt;
+                       tcpc_dev->pd_pending_vdm_good_crc = false;
+               } else {
+                       *pd_event = *vdm_event;
+                       tcpc_dev->pd_pending_vdm_event = false;
+               }
+               mutex_unlock(&tcpc_dev->access_lock);
+               return true;
+       }
+
+       return false;
+}
+
+static inline void reset_pe_vdm_state(pd_port_t *pd_port, u32 vdm_hdr)
+{
+       if (PD_VDO_SVDM(vdm_hdr) && PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) {
+               pd_port->reset_vdm_state = true;
+               pd_port->pe_vdm_state = pd_port->pe_pd_state;
+       }
+}
+
+bool pd_put_vdm_event(struct tcpc_device *tcpc_dev,
+                     pd_event_t *pd_event, bool from_port_partner)
+{
+       char buf[1024] = { 0 };
+       pd_msg_t *pd_msg = pd_event->pd_msg;
+
+       mutex_lock(&tcpc_dev->access_lock);
+
+       if (tcpc_dev->pd_pending_vdm_event) {
+               /* If message from port partner, we have to overwrite it */
+
+               if (from_port_partner) {
+                       if (pd_event_msg_match(&tcpc_dev->pd_vdm_event,
+                                              PD_EVT_CTRL_MSG,
+                                                  PD_CTRL_GOOD_CRC)) {
+                               TCPC_DBG("PostponeVDM GoodCRC\r\n");
+                               tcpc_dev->pd_pending_vdm_good_crc = true;
+                       }
+
+                       __pd_free_event(tcpc_dev, &tcpc_dev->pd_vdm_event);
+               } else {
+                       __pd_free_event(tcpc_dev, pd_event);
+                       mutex_unlock(&tcpc_dev->access_lock);
+                       return false;
+               }
+       }
+
+       tcpc_dev->pd_vdm_event = *pd_event;
+       tcpc_dev->pd_pending_vdm_event = true;
+       tcpc_dev->pd_postpone_vdm_timeout = true;
+
+       if (from_port_partner) {
+               if (!pd_msg)
+                       snprintf(buf, sizeof(buf), "the pd_msg is NULL\n");
+               /* pd_msg->time_stamp = 0; */
+               tcpc_dev->pd_last_vdm_msg = *pd_msg;
+               reset_pe_vdm_state(&tcpc_dev->pd_port, pd_msg->payload[0]);
+#ifdef CONFIG_USB_PD_POSTPONE_FIRST_VDM
+               postpone_vdm_event(tcpc_dev);
+               mutex_unlock(&tcpc_dev->access_lock);
+               return true;
+#endif /* CONFIG_USB_PD_POSTPONE_FIRST_VDM */
+       }
+
+       atomic_inc(&tcpc_dev->pending_event); /* do not really wake up process*/
+       wake_up_interruptible(&tcpc_dev->event_loop_wait_que);
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       return true;
+}
+
+bool pd_put_last_vdm_event(struct tcpc_device *tcpc_dev)
+{
+       pd_msg_t *pd_msg = &tcpc_dev->pd_last_vdm_msg;
+       pd_event_t *vdm_event = &tcpc_dev->pd_vdm_event;
+
+       mutex_lock(&tcpc_dev->access_lock);
+
+       vdm_event->event_type = PD_EVT_HW_MSG;
+       vdm_event->msg = PD_HW_RETRY_VDM;
+
+       if (tcpc_dev->pd_pending_vdm_event)
+               __pd_free_event(tcpc_dev, &tcpc_dev->pd_vdm_event);
+
+       vdm_event->pd_msg = __pd_alloc_msg(tcpc_dev);
+
+       if (!vdm_event->pd_msg) {
+               mutex_unlock(&tcpc_dev->access_lock);
+               return false;
+       }
+
+       *vdm_event->pd_msg = *pd_msg;
+       tcpc_dev->pd_pending_vdm_event = true;
+       tcpc_dev->pd_postpone_vdm_timeout = true;
+
+#ifdef CONFIG_USB_PD_POSTPONE_RETRY_VDM
+       reset_pe_vdm_state(&tcpc_dev->pd_port, pd_msg->payload[0]);
+       postpone_vdm_event(tcpc_dev);
+#else
+       atomic_inc(&tcpc_dev->pending_event); /* do not really wake up process*/
+       wake_up_interruptible(&tcpc_dev->event_loop_wait_que);
+#endif /* CONFIG_USB_PD_POSTPONE_RETRY_VDM */
+
+       mutex_unlock(&tcpc_dev->access_lock);
+       return true;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void __pd_event_buf_reset(struct tcpc_device *tcpc_dev)
+{
+       pd_event_t pd_event;
+
+       tcpc_dev->pd_hard_reset_event_pending = false;
+       while (__pd_get_event(tcpc_dev, &pd_event))
+               __pd_free_event(tcpc_dev, &pd_event);
+
+       if (tcpc_dev->pd_pending_vdm_event) {
+               __pd_free_event(tcpc_dev, &tcpc_dev->pd_vdm_event);
+               tcpc_dev->pd_pending_vdm_event = false;
+       }
+
+       tcpc_dev->pd_pending_vdm_good_crc = false;
+}
+
+void pd_event_buf_reset(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       __pd_event_buf_reset(tcpc_dev);
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline bool __pd_put_hw_event(
+       struct tcpc_device *tcpc_dev, u8 hw_event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_HW_MSG,
+               .msg = hw_event,
+               .pd_msg = NULL,
+       };
+
+       return __pd_put_event(tcpc_dev, &evt, false);
+}
+
+static inline bool __pd_put_pe_event(
+       struct tcpc_device *tcpc_dev, u8 pe_event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_PE_MSG,
+               .msg = pe_event,
+               .pd_msg = NULL,
+       };
+
+       return __pd_put_event(tcpc_dev, &evt, false);
+}
+
+void pd_put_cc_detached_event(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+
+       __pd_event_buf_reset(tcpc_dev);
+       __pd_put_hw_event(tcpc_dev, PD_HW_CC_DETACHED);
+
+       tcpc_dev->pd_wait_pe_idle = true;
+       tcpc_dev->pd_wait_pr_swap_complete = false;
+       tcpc_dev->pd_wait_hard_reset_complete = false;
+       tcpc_dev->pd_hard_reset_event_pending = false;
+       tcpc_dev->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE;
+       tcpc_dev->pd_bist_mode = PD_BIST_MODE_DISABLE;
+       tcpc_dev->pd_ping_event_pending = false;
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       tcpc_dev->pd_discard_pending = false;
+#endif
+
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_put_recv_hard_reset_event(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+
+       tcpc_dev->pd_transmit_state = PD_TX_STATE_HARD_RESET;
+
+       if ((!tcpc_dev->pd_hard_reset_event_pending) &&
+           (!tcpc_dev->pd_wait_pe_idle)) {
+               __pd_event_buf_reset(tcpc_dev);
+               __pd_put_hw_event(tcpc_dev, PD_HW_RECV_HARD_RESET);
+               tcpc_dev->pd_bist_mode = PD_BIST_MODE_DISABLE;
+               tcpc_dev->pd_hard_reset_event_pending = true;
+               tcpc_dev->pd_ping_event_pending = false;
+       }
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       tcpc_dev->pd_discard_pending = false;
+#endif
+
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_put_sent_hard_reset_event(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_wait_hard_reset_complete) {
+               tcpc_dev->pd_transmit_state = PD_TX_STATE_GOOD_CRC;
+               __pd_event_buf_reset(tcpc_dev);
+               __pd_put_pe_event(tcpc_dev, PD_PE_HARD_RESET_COMPLETED);
+       } else {
+               TCPC_DBG("[HReset] Unattached\r\n");
+       }
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+bool pd_put_pd_msg_event(struct tcpc_device *tcpc_dev, pd_msg_t *pd_msg)
+{
+       u32 cnt, cmd;
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       bool discard_pending = false;
+#endif
+
+       pd_event_t evt = {
+               .event_type = PD_EVT_PD_MSG,
+               .pd_msg = pd_msg,
+       };
+
+       cnt = PD_HEADER_CNT(pd_msg->msg_hdr);
+       cmd = PD_HEADER_TYPE(pd_msg->msg_hdr);
+
+       /* bist mode */
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_bist_mode != PD_BIST_MODE_DISABLE) {
+               TCPC_DBG("BIST_MODE_RX\r\n");
+               __pd_free_event(tcpc_dev, &evt);
+               mutex_unlock(&tcpc_dev->access_lock);
+               return 0;
+       }
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       if (tcpc_dev->pd_discard_pending &&
+           (pd_msg->frame_type == TCPC_TX_SOP) &&
+               (tcpc_dev->tcpc_flags & TCPC_FLAGS_RETRY_CRC_DISCARD)) {
+               discard_pending = true;
+               tcpc_dev->pd_discard_pending = false;
+
+               if ((cmd == PD_CTRL_GOOD_CRC) && (cnt == 0)) {
+                       TCPC_DBG("RETRANSMIT\r\n");
+                       __pd_free_event(tcpc_dev, &evt);
+                       mutex_unlock(&tcpc_dev->access_lock);
+
+                       /* TODO: check it later */
+                       tcpc_disable_timer(tcpc_dev, PD_TIMER_DISCARD);
+                       tcpci_retransmit(tcpc_dev);
+                       return 0;
+               }
+       }
+#endif
+
+#ifdef CONFIG_USB_PD_DROP_REPEAT_PING
+       if (cnt == 0 && cmd == PD_CTRL_PING) {
+               if (tcpc_dev->pd_ping_event_pending) {
+                       TCPC_DBG("PING\r\n");
+                       __pd_free_event(tcpc_dev, &evt);
+                       mutex_unlock(&tcpc_dev->access_lock);
+                       return 0;
+               }
+
+               tcpc_dev->pd_ping_event_pending = true;
+       }
+#endif
+
+       if (cnt != 0 && cmd == PD_DATA_BIST)
+               tcpc_dev->pd_bist_mode = PD_BIST_MODE_EVENT_PENDING;
+
+       mutex_unlock(&tcpc_dev->access_lock);
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       if (discard_pending) {
+               tcpc_disable_timer(tcpc_dev, PD_TIMER_DISCARD);
+               pd_put_hw_event(tcpc_dev, PD_HW_TX_FAILED);
+       }
+#endif
+
+       if (cnt != 0 && cmd == PD_DATA_VENDOR_DEF)
+               return pd_put_vdm_event(tcpc_dev, &evt, true);
+       return pd_put_event(tcpc_dev, &evt, true);
+}
+
+static void pd_report_vbus_present(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE;
+       __pd_put_hw_event(tcpc_dev, PD_HW_VBUS_PRESENT);
+}
+
+void pd_put_vbus_changed_event(struct tcpc_device *tcpc_dev, bool from_ic)
+{
+       int vbus_valid;
+       bool postpone_vbus_present = false;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       vbus_valid = tcpci_check_vbus_valid(tcpc_dev);
+
+       switch (tcpc_dev->pd_wait_vbus_once) {
+       case PD_WAIT_VBUS_VALID_ONCE:
+               if (vbus_valid) {
+#if CONFIG_USB_PD_VBUS_PRESENT_TOUT
+                       postpone_vbus_present = from_ic;
+#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */
+                       if (!postpone_vbus_present)
+                               pd_report_vbus_present(tcpc_dev);
+               }
+               break;
+
+       case PD_WAIT_VBUS_INVALID_ONCE:
+               if (!vbus_valid) {
+                       tcpc_dev->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE;
+                       __pd_put_hw_event(tcpc_dev, PD_HW_VBUS_ABSENT);
+               }
+               break;
+       }
+       mutex_unlock(&tcpc_dev->access_lock);
+
+#if CONFIG_USB_PD_VBUS_PRESENT_TOUT
+       if (postpone_vbus_present)
+               tcpc_enable_timer(tcpc_dev, PD_TIMER_VBUS_PRESENT);
+#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */
+}
+
+void pd_put_vbus_safe0v_event(struct tcpc_device *tcpc_dev)
+{
+#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT
+       tcpc_disable_timer(tcpc_dev, PD_TIMER_VSAFE0V_TOUT);
+#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */
+
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_wait_vbus_once == PD_WAIT_VBUS_SAFE0V_ONCE) {
+               tcpc_dev->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE;
+               __pd_put_hw_event(tcpc_dev, PD_HW_VBUS_SAFE0V);
+       }
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_put_vbus_stable_event(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_wait_vbus_once == PD_WAIT_VBUS_STABLE_ONCE) {
+               tcpc_dev->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE;
+               __pd_put_hw_event(tcpc_dev, PD_HW_VBUS_STABLE);
+       }
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_put_vbus_present_event(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->access_lock);
+       pd_report_vbus_present(tcpc_dev);
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+/* ---- PD Notify TCPC ---- */
+
+void pd_try_put_pe_idle_event(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_transmit_state <= PD_TX_STATE_WAIT)
+               __pd_put_pe_event(tcpc_dev, PD_PE_IDLE);
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_idle(pd_port_t *pd_port)
+{
+       bool notify_pe_idle = false;
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       if (tcpc_dev->pd_wait_pe_idle) {
+               notify_pe_idle = true;
+               tcpc_dev->pd_wait_pe_idle = false;
+       }
+
+       tcpc_dev->pd_wait_error_recovery = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       pd_update_connect_state(pd_port, PD_CONNECT_NONE);
+
+       if (notify_pe_idle)
+               tcpc_enable_timer(tcpc_dev, TYPEC_RT_TIMER_PE_IDLE);
+}
+
+void pd_notify_pe_wait_vbus_once(pd_port_t *pd_port, int wait_evt)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_vbus_once = wait_evt;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       switch (wait_evt) {
+       case PD_WAIT_VBUS_VALID_ONCE:
+       case PD_WAIT_VBUS_INVALID_ONCE:
+               pd_put_vbus_changed_event(tcpc_dev, false);
+               break;
+       case PD_WAIT_VBUS_SAFE0V_ONCE:
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT
+               if (tcpci_check_vsafe0v(tcpc_dev, true)) {
+                       pd_put_vbus_safe0v_event(tcpc_dev);
+                       break;
+               }
+#else
+               pd_enable_timer(pd_port, PD_TIMER_VSAFE0V_DELAY);
+#endif /* CONFIG_TCPC_VSAFE0V_DETECT */
+
+#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT
+               pd_enable_timer(pd_port, PD_TIMER_VSAFE0V_TOUT);
+#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */
+               break;
+       }
+}
+
+void pd_notify_pe_error_recovery(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_hard_reset_complete = false;
+       tcpc_dev->pd_wait_pr_swap_complete = false;
+       tcpc_dev->pd_wait_error_recovery = true;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       tcpci_set_cc(pd_port->tcpc_dev, TYPEC_CC_OPEN);
+       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_ERROR_RECOVERY);
+}
+
+void pd_notify_pe_transit_to_default(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_hard_reset_event_pending = false;
+       tcpc_dev->pd_wait_hard_reset_complete = true;
+       tcpc_dev->pd_wait_pr_swap_complete = false;
+       tcpc_dev->pd_bist_mode = PD_BIST_MODE_DISABLE;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_hard_reset_completed(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       if (!tcpc_dev->pd_wait_hard_reset_complete)
+               return;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_hard_reset_complete = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_send_hard_reset(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_transmit_state = PD_TX_STATE_WAIT_HARD_RESET;
+       tcpc_dev->pd_wait_hard_reset_complete = true;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_execute_pr_swap(pd_port_t *pd_port, bool start_swap)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       pd_port->during_swap = start_swap;
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_pr_swap_complete = true;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_cancel_pr_swap(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       if (!tcpc_dev->pd_wait_pr_swap_complete)
+               return;
+
+       pd_port->during_swap = false;
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_pr_swap_complete = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+}
+
+void pd_notify_pe_reset_protocol(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_pr_swap_complete = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_noitfy_pe_bist_mode(pd_port_t *pd_port, u8 mode)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_bist_mode = mode;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_recv_ping_event(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_ping_event_pending = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_transmit_msg(
+       pd_port_t *pd_port, u8 type)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_transmit_state = type;
+       mutex_unlock(&tcpc_dev->access_lock);
+}
+
+void pd_notify_pe_pr_changed(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       /* Check mutex later, actually,
+        * typec layer will igrone all cc-change during PR-SWAP
+        */
+
+       /* mutex_lock(&tcpc_dev->access_lock); */
+       tcpc_typec_handle_pe_pr_swap(tcpc_dev);
+       /* mutex_unlock(&tcpc_dev->access_lock); */
+}
+
+void pd_notify_pe_src_explicit_contract(pd_port_t *pd_port)
+{
+       struct tcpc_device *tcpc_dev = pd_port->tcpc_dev;
+
+       if (pd_port->explicit_contract)
+               return;
+
+       /*mutex_lock(&tcpc_dev->access_lock); */
+       tcpc_typec_advertise_explicit_contract(tcpc_dev);
+       /*mutex_unlock(&tcpc_dev->access_lock); */
+}
+
+/* ---- init  ---- */
+static int tcpc_event_thread(void *param)
+{
+       struct tcpc_device *tcpc_dev = param;
+       struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO - 2};
+
+       sched_setscheduler(current, SCHED_FIFO, &sch_param);
+
+       while (true) {
+               wait_event_interruptible(
+                               tcpc_dev->event_loop_wait_que,
+                               atomic_read(&tcpc_dev->pending_event) |
+                               tcpc_dev->event_loop_thead_stop);
+               if (kthread_should_stop() || tcpc_dev->event_loop_thead_stop)
+                       break;
+               do {
+                       atomic_dec_if_positive(&tcpc_dev->pending_event);
+               } while (pd_policy_engine_run(tcpc_dev));
+       }
+
+       return 0;
+}
+
+int tcpci_event_init(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->event_task = kthread_create(tcpc_event_thread, tcpc_dev,
+                       "tcpc_event_%s.%p", dev_name(&tcpc_dev->dev), tcpc_dev);
+       tcpc_dev->event_loop_thead_stop = false;
+
+       init_waitqueue_head(&tcpc_dev->event_loop_wait_que);
+       atomic_set(&tcpc_dev->pending_event, 0);
+       wake_up_process(tcpc_dev->event_task);
+
+       return 0;
+}
+
+int tcpci_event_deinit(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->event_loop_thead_stop = true;
+       wake_up_interruptible(&tcpc_dev->event_loop_wait_que);
+       kthread_stop(tcpc_dev->event_task);
+       return 0;
+}
diff --git a/drivers/usb/pd/richtek/tcpci_timer.c b/drivers/usb/pd/richtek/tcpci_timer.c
new file mode 100644 (file)
index 0000000..521e7ab
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * TCPC Interface for timer handler
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/kthread.h>
+#include <linux/hrtimer.h>
+#include <linux/version.h>
+#include <linux/sched/rt.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_timer.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/rt1711h.h>
+
+#define RT_MASK64(i)   (((u64)1) << i)
+
+#define TIMEOUT_VAL(val)       (val * 1000)
+#define TIMEOUT_RANGE(min, max)                ((min * 4000 + max * 1000) / 5)
+#define TIMEOUT_VAL_US(val)    (val)
+
+/* Debug message Macro */
+#if TCPC_TIMER_DBG_EN
+#define TCPC_TIMER_DBG(tcpc, id)                               \
+{                                                              \
+       RT_DBG_INFO("Trigger %s\n", tcpc_timer_name[id]);       \
+}
+#else
+#define TCPC_TIMER_DBG(format, args...)
+#endif /* TCPC_TIMER_DBG_EN */
+
+#if TCPC_TIMER_INFO_EN
+#define TCPC_TIMER_EN_DBG(tcpc, id)                            \
+{                                                              \
+       RT_DBG_INFO("Enable %s\n", tcpc_timer_name[id]);        \
+}
+#else
+#define TCPC_TIMER_EN_DBG(format, args...)
+#endif /* TCPC_TIMER_INFO_EN */
+
+static inline u64 rt_get_value(u64 *p)
+{
+       unsigned long flags;
+       u64 data;
+
+       raw_local_irq_save(flags);
+       data = *p;
+       raw_local_irq_restore(flags);
+       return data;
+}
+
+static inline void rt_set_value(u64 *p, u64 data)
+{
+       unsigned long flags;
+
+       raw_local_irq_save(flags);
+       *p = data;
+       raw_local_irq_restore(flags);
+}
+
+static inline void rt_clear_bit(int nr, u64 *addr)
+{
+       u64 mask = ((u64)1) << nr;
+       unsigned long flags;
+
+       raw_local_irq_save(flags);
+       *addr &= ~mask;
+       raw_local_irq_restore(flags);
+}
+
+static inline void rt_set_bit(int nr, u64 *addr)
+{
+       u64 mask = ((u64)1) << nr;
+       unsigned long flags;
+
+       raw_local_irq_save(flags);
+       *addr |= mask;
+       raw_local_irq_restore(flags);
+}
+
+const char *tcpc_timer_name[] = {
+#ifdef CONFIG_USB_POWER_DELIVERY
+       "PD_TIMER_BIST_CONT_MODE",
+       "PD_TIMER_DISCOVER_ID",
+       "PD_TIMER_HARD_RESET_COMPLETE",
+       "PD_TIMER_NO_RESPONSE",
+       "PD_TIMER_PS_HARD_RESET",
+       "PD_TIMER_PS_SOURCE_OFF",
+       "PD_TIMER_PS_SOURCE_ON",
+       "PD_TIMER_PS_TRANSITION",
+       "PD_TIMER_SENDER_RESPONSE",
+       "PD_TIMER_SINK_ACTIVITY",
+       "PD_TIMER_SINK_REQUEST",
+       "PD_TIMER_SINK_WAIT_CAP",
+       "PD_TIMER_SOURCE_ACTIVITY",
+       "PD_TIMER_SOURCE_CAPABILITY",
+       "PD_TIMER_SOURCE_START",
+       "PD_TIMER_VCONN_ON",
+       "PD_TIMER_VDM_MODE_ENTRY",
+       "PD_TIMER_VDM_MODE_EXIT",
+       "PD_TIMER_VDM_RESPONSE",
+       "PD_TIMER_SOURCE_TRANSITION",
+       "PD_TIMER_SRC_RECOVER",
+       "PD_TIMER_VSAFE0V_DELAY",
+       "PD_TIMER_VSAFE0V_TOUT",
+       "PD_TIMER_DISCARD",
+       "PD_TIMER_VBUS_STABLE",
+       "PD_TIMER_VBUS_PRESENT",
+       "PD_PE_VDM_POSTPONE",
+
+       "TYPEC_RT_TIMER_PE_IDLE",
+       "TYPEC_RT_TIMER_SAFE0V_DELAY",
+       "TYPEC_RT_TIMER_SAFE0V_TOUT",
+
+       "TYPEC_TRY_TIMER_DRP_TRY",
+       "TYPEC_TRY_TIMER_DRP_TRYWAIT",
+
+       "TYPEC_TIMER_CCDEBOUNCE",
+       "TYPEC_TIMER_PDDEBOUNCE",
+       "TYPEC_TIMER_ERROR_RECOVERY",
+       "TYPEC_TIMER_WAKEUP_TOUT",
+       "TYPEC_TIMER_DRP_SRC_TOGGLE",
+#else
+       "TYPEC_RT_TIMER_SAFE0V_DELAY",
+       "TYPEC_RT_TIMER_SAFE0V_TOUT",
+
+       "TYPEC_TRY_TIMER_DRP_TRY",
+       "TYPEC_TRY_TIMER_DRP_TRYWAIT",
+
+       "TYPEC_TIMER_CCDEBOUNCE",
+       "TYPEC_TIMER_PDDEBOUNCE",
+       "TYPEC_TIMER_WAKEUP_TOUT",
+       "TYPEC_TIMER_DRP_SRC_TOGGLE",
+#endif /* CONFIG_USB_POWER_DELIVERY */
+};
+
+#define PD_TIMER_VSAFE0V_DLY_TOUT              TIMEOUT_VAL(400)
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT
+#define TYPEC_RT_TIMER_SAFE0V_DLY_TOUT         TIMEOUT_VAL(35)
+#else
+#define TYPEC_RT_TIMER_SAFE0V_DLY_TOUT         TIMEOUT_VAL(100)
+#endif
+
+static const u32 tcpc_timer_timeout[PD_TIMER_NR] = {
+#ifdef CONFIG_USB_POWER_DELIVERY
+       TIMEOUT_RANGE(30, 60),          /* PD_TIMER_BIST_CONT_MODE */
+       TIMEOUT_RANGE(40, 50),          /* PD_TIMER_DISCOVER_ID */
+       TIMEOUT_RANGE(4, 5),    /* PD_TIMER_HARD_RESET_COMPLETE (no used) */
+       TIMEOUT_RANGE(4500, 5500),      /* PD_TIMER_NO_RESPONSE */
+       TIMEOUT_RANGE(25, 35),          /* PD_TIMER_PS_HARD_RESET */
+       TIMEOUT_RANGE(750, 920),        /* PD_TIMER_PS_SOURCE_OFF */
+       TIMEOUT_RANGE(390, 480),        /* PD_TIMER_PS_SOURCE_ON, */
+       TIMEOUT_RANGE(450, 550),        /* PD_TIMER_PS_TRANSITION */
+       TIMEOUT_RANGE(24, 30),          /* PD_TIMER_SENDER_RESPONSE */
+       TIMEOUT_RANGE(120, 150),        /* PD_TIMER_SINK_ACTIVITY (no used) */
+       TIMEOUT_RANGE(100, 100),        /* PD_TIMER_SINK_REQUEST */
+       TIMEOUT_RANGE(310, 620),        /* PD_TIMER_SINK_WAIT_CAP */
+       TIMEOUT_RANGE(40, 50),          /* PD_TIMER_SOURCE_ACTIVITY (no used) */
+       TIMEOUT_RANGE(100, 200),        /* PD_TIMER_SOURCE_CAPABILITY */
+       TIMEOUT_VAL(20),                /* PD_TIMER_SOURCE_START */
+       TIMEOUT_VAL(100),               /* PD_TIMER_VCONN_ON */
+       TIMEOUT_RANGE(40, 50),          /* PD_TIMER_VDM_MODE_ENTRY */
+       TIMEOUT_RANGE(40, 50),          /* PD_TIMER_VDM_MODE_EXIT */
+       TIMEOUT_RANGE(24, 30),          /* PD_TIMER_VDM_RESPONSE */
+       TIMEOUT_RANGE(25, 35),          /* PD_TIMER_SOURCE_TRANSITION */
+       TIMEOUT_RANGE(660, 1000),       /* PD_TIMER_SRC_RECOVER */
+
+       /* PD_TIMER (out of spec) */
+       PD_TIMER_VSAFE0V_DLY_TOUT,              /* PD_TIMER_VSAFE0V_DELAY */
+       TIMEOUT_VAL(650),               /* PD_TIMER_VSAFE0V_TOUT */
+       TIMEOUT_VAL(3),                 /* PD_TIMER_DISCARD */
+       /* PD_TIMER_VBUS_STABLE */
+       TIMEOUT_VAL(CONFIG_USB_PD_VBUS_STABLE_TOUT),
+       /* PD_TIMER_VBUS_PRESENT */
+       TIMEOUT_VAL(CONFIG_USB_PD_VBUS_PRESENT_TOUT),
+       TIMEOUT_VAL_US(3500),       /* PD_PE_VDM_POSTPONE */
+
+       /* TYPEC-RT-TIMER */
+       TIMEOUT_VAL(1),                         /* TYPEC_RT_TIMER_PE_IDLE */
+       TYPEC_RT_TIMER_SAFE0V_DLY_TOUT, /* TYPEC_RT_TIMER_SAFE0V_DELAY */
+       TIMEOUT_VAL(650),               /* TYPEC_RT_TIMER_SAFE0V_TOUT */
+
+       /* TYPEC-TRY-TIMER */
+       TIMEOUT_RANGE(75, 150),         /* TYPEC_TRY_TIMER_DRP_TRY */
+       TIMEOUT_RANGE(400, 800),        /* TYPEC_TRY_TIMER_DRP_TRYWAIT */
+
+       /* TYPEC-DEBOUNCE-TIMER */
+       TIMEOUT_RANGE(100, 200),        /* TYPEC_TIMER_CCDEBOUNCE */
+       TIMEOUT_RANGE(10, 10),          /* TYPEC_TIMER_PDDEBOUNCE */
+       TIMEOUT_RANGE(25, 25),          /* TYPEC_TIMER_ERROR_RECOVERY */
+       /* TYPEC_TIMER_WAKEUP_TOUT (out of spec) */
+       TIMEOUT_VAL(300 * 1000),
+       TIMEOUT_VAL(60),                /* TYPEC_TIMER_DRP_SRC_TOGGLE */
+#else
+       /* TYPEC-RT-TIMER */
+       TYPEC_RT_TIMER_SAFE0V_DLY_TOUT, /* TYPEC_RT_TIMER_SAFE0V_DELAY */
+       TIMEOUT_VAL(650),                       /* TYPEC_RT_TIMER_SAFE0V_TOUT */
+
+       /* TYPEC-TRY-TIMER */
+       TIMEOUT_RANGE(75, 150),         /* TYPEC_TRY_TIMER_DRP_TRY */
+       TIMEOUT_RANGE(400, 800),        /* TYPEC_TRY_TIMER_DRP_TRYWAIT */
+
+       TIMEOUT_RANGE(100, 200),        /* TYPEC_TIMER_CCDEBOUNCE */
+       TIMEOUT_RANGE(10, 10),          /* TYPEC_TIMER_PDDEBOUNCE */
+       TYPEC_TIMER_SAFE0V_TOUT,        /* TYPEC_TIMER_SAFE0V (out of spec) */
+       /* TYPEC_TIMER_WAKEUP_TOUT (out of spec) */
+       TIMEOUT_VAL(300 * 1000),
+       TIMEOUT_VAL(60),                        /* TYPEC_TIMER_DRP_SRC_TOGGLE */
+#endif /* CONFIG_USB_POWER_DELIVERY */
+};
+
+typedef enum hrtimer_restart (*tcpc_hrtimer_call)(struct hrtimer *timer);
+
+static inline void on_pe_timer_timeout(
+               struct tcpc_device *tcpc_dev, u32 timer_id)
+{
+#ifdef CONFIG_USB_POWER_DELIVERY
+       pd_event_t pd_event;
+
+       pd_event.event_type = PD_EVT_TIMER_MSG;
+       pd_event.msg = timer_id;
+       pd_event.pd_msg = NULL;
+
+       switch (timer_id) {
+       case PD_TIMER_VDM_MODE_ENTRY:
+       case PD_TIMER_VDM_MODE_EXIT:
+       case PD_TIMER_VDM_RESPONSE:
+               pd_put_vdm_event(tcpc_dev, &pd_event, false);
+               break;
+
+       case PD_TIMER_VSAFE0V_DELAY:
+               pd_put_vbus_safe0v_event(tcpc_dev);
+               break;
+
+#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT
+       case PD_TIMER_VSAFE0V_TOUT:
+               {
+               u16 power_status = 0;
+               int vbus_level = tcpc_dev->vbus_level;
+
+               tcpci_get_power_status(tcpc_dev, &power_status);
+               tcpci_vbus_level_init(tcpc_dev, power_status);
+
+               TCPC_INFO("VSafe0V TOUT: %d - %d\r\n",
+                         tcpc_dev->vbus_level, vbus_level);
+               }
+               pd_put_vbus_safe0v_event(tcpc_dev);
+               break;
+#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       case PD_TIMER_DISCARD:
+               tcpc_dev->pd_discard_pending = false;
+               pd_put_hw_event(tcpc_dev, PD_HW_TX_FAILED);
+               break;
+#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
+
+#if CONFIG_USB_PD_VBUS_STABLE_TOUT
+       case PD_TIMER_VBUS_STABLE:
+               pd_put_vbus_stable_event(tcpc_dev);
+               break;
+#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */
+
+#if CONFIG_USB_PD_VBUS_PRESENT_TOUT
+       case PD_TIMER_VBUS_PRESENT:
+               pd_put_vbus_present_event(tcpc_dev);
+               break;
+#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */
+
+       case PD_PE_VDM_POSTPONE:
+               tcpc_dev->pd_postpone_vdm_timeout = true;
+               atomic_inc(&tcpc_dev->pending_event);
+               wake_up_interruptible(&tcpc_dev->event_loop_wait_que);
+               break;
+
+       default:
+               pd_put_event(tcpc_dev, &pd_event, false);
+               break;
+       }
+#endif
+
+       tcpc_disable_timer(tcpc_dev, timer_id);
+}
+
+#define TCPC_TIMER_TRIGGER()   do \
+{                              \
+       down(&tcpc_dev->timer_tick_lock);                       \
+       rt_set_bit(index, (u64 *)&tcpc_dev->timer_tick);        \
+       up(&tcpc_dev->timer_tick_lock);                         \
+       wake_up_interruptible(&tcpc_dev->timer_wait_que);       \
+} while (0)
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+static enum hrtimer_restart tcpc_timer_bist_cont_mode(struct hrtimer *timer)
+{
+       int index = PD_TIMER_BIST_CONT_MODE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_discover_id(struct hrtimer *timer)
+{
+       int index = PD_TIMER_DISCOVER_ID;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_hard_reset_complete(
+                                               struct hrtimer *timer)
+{
+       int index = PD_TIMER_HARD_RESET_COMPLETE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_no_response(struct hrtimer *timer)
+{
+       int index = PD_TIMER_NO_RESPONSE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_ps_hard_reset(struct hrtimer *timer)
+{
+       int index = PD_TIMER_PS_HARD_RESET;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_ps_source_off(struct hrtimer *timer)
+{
+       int index = PD_TIMER_PS_SOURCE_OFF;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_ps_source_on(struct hrtimer *timer)
+{
+       int index = PD_TIMER_PS_SOURCE_ON;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_ps_transition(struct hrtimer *timer)
+{
+       int index = PD_TIMER_PS_TRANSITION;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_sender_response(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SENDER_RESPONSE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_sink_activity(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SINK_ACTIVITY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_sink_request(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SINK_REQUEST;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_sink_wait_cap(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SINK_WAIT_CAP;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_source_activity(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SOURCE_ACTIVITY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_source_capability(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SOURCE_CAPABILITY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_source_start(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SOURCE_START;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vconn_on(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VCONN_ON;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vdm_mode_entry(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VDM_MODE_ENTRY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vdm_mode_exit(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VDM_MODE_EXIT;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vdm_response(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VDM_RESPONSE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_source_transition(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SOURCE_TRANSITION;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_src_recover(struct hrtimer *timer)
+{
+       int index = PD_TIMER_SRC_RECOVER;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vsafe0v_delay(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VSAFE0V_DELAY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vsafe0v_tout(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VSAFE0V_TOUT;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_error_recovery(struct hrtimer *timer)
+{
+       int index = TYPEC_TIMER_ERROR_RECOVERY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_pd_discard(struct hrtimer *timer)
+{
+       int index = PD_TIMER_DISCARD;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vbus_stable(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VBUS_STABLE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_vbus_present(struct hrtimer *timer)
+{
+       int index = PD_TIMER_VBUS_PRESENT;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart pd_pe_vdm_postpone_timeout(struct hrtimer *timer)
+{
+       int index = PD_PE_VDM_POSTPONE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_rt_pe_idle(struct hrtimer *timer)
+{
+       int index = TYPEC_RT_TIMER_PE_IDLE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+static enum hrtimer_restart tcpc_timer_rt_vsafe0v_delay(struct hrtimer *timer)
+{
+       int index = TYPEC_RT_TIMER_SAFE0V_DELAY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_rt_vsafe0v_tout(struct hrtimer *timer)
+{
+       int index = TYPEC_RT_TIMER_SAFE0V_TOUT;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_try_drp_try(struct hrtimer *timer)
+{
+       int index = TYPEC_TRY_TIMER_DRP_TRY;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_try_drp_trywait(struct hrtimer *timer)
+{
+       int index = TYPEC_TRY_TIMER_DRP_TRYWAIT;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_ccdebounce(struct hrtimer *timer)
+{
+       int index = TYPEC_TIMER_CCDEBOUNCE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_pddebounce(struct hrtimer *timer)
+{
+       int index = TYPEC_TIMER_PDDEBOUNCE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_wakeup(struct hrtimer *timer)
+{
+       int index = TYPEC_TIMER_WAKEUP;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart tcpc_timer_drp_src_toggle(struct hrtimer *timer)
+{
+       int index = TYPEC_TIMER_DRP_SRC_TOGGLE;
+       struct tcpc_device *tcpc_dev =
+               container_of(timer, struct tcpc_device, tcpc_timer[index]);
+
+       TCPC_TIMER_TRIGGER();
+       return HRTIMER_NORESTART;
+}
+
+static tcpc_hrtimer_call tcpc_timer_call[PD_TIMER_NR] = {
+#ifdef CONFIG_USB_POWER_DELIVERY
+       [PD_TIMER_BIST_CONT_MODE] = tcpc_timer_bist_cont_mode,
+       [PD_TIMER_DISCOVER_ID] = tcpc_timer_discover_id,
+       [PD_TIMER_HARD_RESET_COMPLETE] = tcpc_timer_hard_reset_complete,
+       [PD_TIMER_NO_RESPONSE] = tcpc_timer_no_response,
+       [PD_TIMER_PS_HARD_RESET] = tcpc_timer_ps_hard_reset,
+       [PD_TIMER_PS_SOURCE_OFF] = tcpc_timer_ps_source_off,
+       [PD_TIMER_PS_SOURCE_ON] = tcpc_timer_ps_source_on,
+       [PD_TIMER_PS_TRANSITION] = tcpc_timer_ps_transition,
+       [PD_TIMER_SENDER_RESPONSE] = tcpc_timer_sender_response,
+       [PD_TIMER_SINK_ACTIVITY] = tcpc_timer_sink_activity,
+       [PD_TIMER_SINK_REQUEST] = tcpc_timer_sink_request,
+       [PD_TIMER_SINK_WAIT_CAP] = tcpc_timer_sink_wait_cap,
+       [PD_TIMER_SOURCE_ACTIVITY] = tcpc_timer_source_activity,
+       [PD_TIMER_SOURCE_CAPABILITY] = tcpc_timer_source_capability,
+       [PD_TIMER_SOURCE_START] = tcpc_timer_source_start,
+       [PD_TIMER_VCONN_ON] = tcpc_timer_vconn_on,
+       [PD_TIMER_VDM_MODE_ENTRY] = tcpc_timer_vdm_mode_entry,
+       [PD_TIMER_VDM_MODE_EXIT] = tcpc_timer_vdm_mode_exit,
+       [PD_TIMER_VDM_RESPONSE] = tcpc_timer_vdm_response,
+       [PD_TIMER_SOURCE_TRANSITION] = tcpc_timer_source_transition,
+       [PD_TIMER_SRC_RECOVER] = tcpc_timer_src_recover,
+       [PD_TIMER_VSAFE0V_DELAY] = tcpc_timer_vsafe0v_delay,
+       [PD_TIMER_VSAFE0V_TOUT] = tcpc_timer_vsafe0v_tout,
+       [PD_TIMER_DISCARD] = tcpc_timer_pd_discard,
+       [PD_TIMER_VBUS_STABLE] = tcpc_timer_vbus_stable,
+       [PD_TIMER_VBUS_PRESENT] = tcpc_timer_vbus_present,
+       [PD_PE_VDM_POSTPONE] = pd_pe_vdm_postpone_timeout,
+
+       [TYPEC_RT_TIMER_PE_IDLE] = tcpc_timer_rt_pe_idle,
+       [TYPEC_RT_TIMER_SAFE0V_DELAY] = tcpc_timer_rt_vsafe0v_delay,
+       [TYPEC_RT_TIMER_SAFE0V_TOUT] = tcpc_timer_rt_vsafe0v_tout,
+
+       [TYPEC_TRY_TIMER_DRP_TRY] = tcpc_timer_try_drp_try,
+       [TYPEC_TRY_TIMER_DRP_TRYWAIT] = tcpc_timer_try_drp_trywait,
+
+       [TYPEC_TIMER_CCDEBOUNCE] = tcpc_timer_ccdebounce,
+       [TYPEC_TIMER_PDDEBOUNCE] = tcpc_timer_pddebounce,
+       [TYPEC_TIMER_ERROR_RECOVERY] = tcpc_timer_error_recovery,
+       [TYPEC_TIMER_WAKEUP] = tcpc_timer_wakeup,
+       [TYPEC_TIMER_DRP_SRC_TOGGLE] = tcpc_timer_drp_src_toggle,
+#else
+       [TYPEC_RT_TIMER_SAFE0V_DELAY] = tcpc_timer_rt_vsafe0v_delay,
+       [TYPEC_RT_TIMER_SAFE0V_TOUT] = tcpc_timer_rt_vsafe0v_tout,
+
+       [TYPEC_TRY_TIMER_DRP_TRY] = tcpc_timer_try_drp_try,
+       [TYPEC_TRY_TIMER_DRP_TRYWAIT] = tcpc_timer_try_drp_trywait,
+
+       [TYPEC_TIMER_CCDEBOUNCE] = tcpc_timer_ccdebounce,
+       [TYPEC_TIMER_PDDEBOUNCE] = tcpc_timer_pddebounce,
+       [TYPEC_TIMER_WAKEUP] = tcpc_timer_wakup,
+       [TYPEC_TIMER_DRP_SRC_TOGGLE] = tcpc_timer_drp_src_toggle,
+#endif /* CONFIG_USB_POWER_DELIVERY */
+};
+
+/*
+ * [BLOCK] Control Timer
+ */
+
+static inline void tcpc_reset_timer_range(
+               struct tcpc_device *tcpc, int start, int end)
+{
+       int i;
+       u64 mask;
+
+       down(&tcpc->timer_enable_mask_lock);
+       mask = rt_get_value((u64 *)&tcpc->timer_enable_mask);
+       up(&tcpc->timer_enable_mask_lock);
+
+       for (i = start; i <= end; i++) {
+               if (mask & (((u64)1) << i)) {
+                       hrtimer_try_to_cancel(&tcpc->tcpc_timer[i]);
+                       down(&tcpc->timer_enable_mask_lock);
+                       rt_clear_bit(i, (u64 *)&tcpc->timer_enable_mask);
+                       up(&tcpc->timer_enable_mask_lock);
+               }
+       }
+}
+
+void tcpc_restart_timer(struct tcpc_device *tcpc, u32 timer_id)
+{
+       u64 mask;
+
+       down(&tcpc->timer_enable_mask_lock);
+       mask = rt_get_value((u64 *)&tcpc->timer_enable_mask);
+       up(&tcpc->timer_enable_mask_lock);
+       if (mask & (((u64)1) << timer_id))
+               tcpc_disable_timer(tcpc, timer_id);
+       tcpc_enable_timer(tcpc, timer_id);
+}
+
+void tcpc_enable_timer(struct tcpc_device *tcpc, u32 timer_id)
+{
+       u32 r, mod;
+       char buf[1024] = { 0 };
+
+       TCPC_TIMER_EN_DBG(tcpc, timer_id);
+       if (timer_id >= PD_TIMER_NR)
+               snprintf(buf, sizeof(buf),
+                        "the timer_id %d is over PD_TIMER_NR\n",
+                        timer_id);
+       mutex_lock(&tcpc->timer_lock);
+       if (timer_id >= TYPEC_TIMER_START_ID)
+               tcpc_reset_timer_range(tcpc, TYPEC_TIMER_START_ID, PD_TIMER_NR);
+
+       down(&tcpc->timer_enable_mask_lock);
+       rt_set_bit(timer_id, (u64 *)&tcpc->timer_enable_mask);
+       up(&tcpc->timer_enable_mask_lock);
+       r = tcpc_timer_timeout[timer_id] / 1000000;
+       mod = tcpc_timer_timeout[timer_id] % 1000000;
+
+       mutex_unlock(&tcpc->timer_lock);
+       hrtimer_start(&tcpc->tcpc_timer[timer_id],
+                     ktime_set(r, mod * 1000), HRTIMER_MODE_REL);
+}
+
+void tcpc_disable_timer(struct tcpc_device *tcpc_dev, u32 timer_id)
+{
+       u64 mask;
+       char buf[1024] = { 0 };
+
+       down(&tcpc_dev->timer_enable_mask_lock);
+       mask = rt_get_value((u64 *)&tcpc_dev->timer_enable_mask);
+       up(&tcpc_dev->timer_enable_mask_lock);
+
+       if (timer_id >= PD_TIMER_NR) {
+               snprintf(buf, sizeof(buf),
+                        "the timer_id %d is over PD_TIMER_NR\n",
+                        timer_id);
+       }
+       if (mask & (((u64)1) << timer_id)) {
+               hrtimer_try_to_cancel(&tcpc_dev->tcpc_timer[timer_id]);
+               rt_clear_bit(timer_id,
+                            (u64 *)&tcpc_dev->timer_enable_mask);
+       }
+}
+
+void tcpc_timer_reset(struct tcpc_device *tcpc_dev)
+{
+       u64 mask;
+       int i;
+
+       down(&tcpc_dev->timer_enable_mask_lock);
+       mask = rt_get_value((u64 *)&tcpc_dev->timer_enable_mask);
+       up(&tcpc_dev->timer_enable_mask_lock);
+       for (i = 0; i < PD_TIMER_NR; i++)
+               if (mask & (((u64)1) << i))
+                       hrtimer_try_to_cancel(&tcpc_dev->tcpc_timer[i]);
+       rt_set_value((u64 *)&tcpc_dev->timer_enable_mask, 0);
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+void tcpc_reset_pe_timer(struct tcpc_device *tcpc_dev)
+{
+       mutex_lock(&tcpc_dev->timer_lock);
+       tcpc_reset_timer_range(tcpc_dev, 0, PD_PE_TIMER_END_ID);
+       mutex_unlock(&tcpc_dev->timer_lock);
+}
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+void tcpc_reset_typec_debounce_timer(struct tcpc_device *tcpc)
+{
+       mutex_lock(&tcpc->timer_lock);
+       tcpc_reset_timer_range(tcpc, TYPEC_TIMER_START_ID, PD_TIMER_NR);
+       mutex_unlock(&tcpc->timer_lock);
+}
+
+void tcpc_reset_typec_try_timer(struct tcpc_device *tcpc)
+{
+       mutex_lock(&tcpc->timer_lock);
+       tcpc_reset_timer_range(tcpc,
+                              TYPEC_TRY_TIMER_START_ID, TYPEC_TIMER_START_ID);
+       mutex_unlock(&tcpc->timer_lock);
+}
+
+static void tcpc_handle_timer_triggered(struct tcpc_device *tcpc_dev)
+{
+       u64 triggered_timer;
+       int i = 0;
+
+       down(&tcpc_dev->timer_tick_lock);
+       triggered_timer = rt_get_value((u64 *)&tcpc_dev->timer_tick);
+       up(&tcpc_dev->timer_tick_lock);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       for (i = 0; i < PD_PE_TIMER_END_ID; i++) {
+               if (triggered_timer & RT_MASK64(i)) {
+                       TCPC_TIMER_DBG(tcpc_dev, i);
+                       on_pe_timer_timeout(tcpc_dev, i);
+                       down(&tcpc_dev->timer_tick_lock);
+                       rt_clear_bit(i, (u64 *)&tcpc_dev->timer_tick);
+                       up(&tcpc_dev->timer_tick_lock);
+               }
+       }
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       mutex_lock(&tcpc_dev->typec_lock);
+       for (; i < PD_TIMER_NR; i++) {
+               if (triggered_timer & RT_MASK64(i)) {
+                       TCPC_TIMER_DBG(tcpc_dev, i);
+                       tcpc_typec_handle_timeout(tcpc_dev, i);
+                       down(&tcpc_dev->timer_tick_lock);
+                       rt_clear_bit(i, (u64 *)&tcpc_dev->timer_tick);
+                       up(&tcpc_dev->timer_tick_lock);
+               }
+       }
+       mutex_unlock(&tcpc_dev->typec_lock);
+}
+
+static int tcpc_timer_thread(void *param)
+{
+       struct tcpc_device *tcpc_dev = param;
+
+       u64 *timer_tick;
+       struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO - 1};
+
+       down(&tcpc_dev->timer_tick_lock);
+       timer_tick = &tcpc_dev->timer_tick;
+       up(&tcpc_dev->timer_tick_lock);
+
+       sched_setscheduler(current, SCHED_FIFO, &sch_param);
+       while (true) {
+               wait_event_interruptible(tcpc_dev->timer_wait_que,
+                                        ((*timer_tick) ? true : false) |
+                                        tcpc_dev->timer_thead_stop);
+               if (kthread_should_stop() || tcpc_dev->timer_thead_stop)
+                       break;
+               do {
+                       tcpc_handle_timer_triggered(tcpc_dev);
+               } while (*timer_tick);
+       }
+       return 0;
+}
+
+int tcpci_timer_init(struct tcpc_device *tcpc_dev)
+{
+       int i;
+
+       pr_info("PD Timer number = %d\n", PD_TIMER_NR);
+       tcpc_dev->timer_task = kthread_create(tcpc_timer_thread, tcpc_dev,
+                       "tcpc_timer_%s.%p", dev_name(&tcpc_dev->dev), tcpc_dev);
+       init_waitqueue_head(&tcpc_dev->timer_wait_que);
+       down(&tcpc_dev->timer_tick_lock);
+       tcpc_dev->timer_tick = 0;
+       up(&tcpc_dev->timer_tick_lock);
+       rt_set_value((u64 *)&tcpc_dev->timer_enable_mask, 0);
+       wake_up_process(tcpc_dev->timer_task);
+       for (i = 0; i < PD_TIMER_NR; i++) {
+               hrtimer_init(&tcpc_dev->tcpc_timer[i],
+                            CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+               tcpc_dev->tcpc_timer[i].function = tcpc_timer_call[i];
+       }
+
+       pr_info("%s : init OK\n", __func__);
+       return 0;
+}
+
+int tcpci_timer_deinit(struct tcpc_device *tcpc_dev)
+{
+       u64 mask;
+       int i;
+
+       down(&tcpc_dev->timer_enable_mask_lock);
+       mask = rt_get_value((u64 *)&tcpc_dev->timer_enable_mask);
+       up(&tcpc_dev->timer_enable_mask_lock);
+
+       mutex_lock(&tcpc_dev->timer_lock);
+       wake_up_interruptible(&tcpc_dev->timer_wait_que);
+       kthread_stop(tcpc_dev->timer_task);
+       for (i = 0; i < PD_TIMER_NR; i++) {
+               if (mask & (1 << i))
+                       hrtimer_try_to_cancel(&tcpc_dev->tcpc_timer[i]);
+       }
+
+       pr_info("%s : de init OK\n", __func__);
+       mutex_unlock(&tcpc_dev->timer_lock);
+       return 0;
+}
diff --git a/drivers/usb/pd/richtek/tcpci_typec.c b/drivers/usb/pd/richtek/tcpci_typec.c
new file mode 100644 (file)
index 0000000..65b08a3
--- /dev/null
@@ -0,0 +1,1656 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * TCPC Type-C Driver for Richtek
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/cpu.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_timer.h>
+#include <linux/hisi/usb/hub/hisi_hub.h>
+#include <linux/hisi/log/hisi_log.h>
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+#define CONFIG_TYPEC_CAP_TRY_STATE
+#endif
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+#undef CONFIG_TYPEC_CAP_TRY_STATE
+#define CONFIG_TYPEC_CAP_TRY_STATE
+#endif
+
+enum TYPEC_WAIT_PS_STATE {
+       TYPEC_WAIT_PS_DISABLE = 0,
+       TYPEC_WAIT_PS_SNK_VSAFE5V,
+       TYPEC_WAIT_PS_SRC_VSAFE0V,
+       TYPEC_WAIT_PS_SRC_VSAFE5V,
+};
+
+#if TYPEC_DBG_ENABLE
+static const char *const typec_wait_ps_name[] = {
+       "Disable",
+       "SNK_VSafe5V",
+       "SRC_VSafe0V",
+       "SRC_VSafe5V",
+};
+#endif /* TYPEC_DBG_ENABLE */
+
+enum TYPEC_HOST_OR_DEVICE {
+       TYPEC_INIT = 0,
+       TYPEC_HOST,
+       TYPEC_DEVICE
+};
+
+static int oldstatus =  TYPEC_INIT;
+
+static inline void typec_wait_ps_change(struct tcpc_device *tcpc_dev,
+                                       enum TYPEC_WAIT_PS_STATE state)
+{
+#if TYPEC_DBG_ENABLE
+       u8 old_state;
+       u8 new_state = (u8)state;
+
+       old_state = tcpc_dev->typec_wait_ps_change;
+       if (new_state != old_state)
+               TYPEC_DBG("wait_ps=%s\r\n", typec_wait_ps_name[new_state]);
+#endif
+       hisilog_err("%s: typec_wait_ps_change!!!+++++++++++\n", __func__);
+
+#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
+       if (state == TYPEC_WAIT_PS_SRC_VSAFE0V)
+               tcpc_enable_timer(tcpc_dev, TYPEC_RT_TIMER_SAFE0V_TOUT);
+#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */
+
+       if (tcpc_dev->typec_wait_ps_change == TYPEC_WAIT_PS_SRC_VSAFE0V &&
+           state != TYPEC_WAIT_PS_SRC_VSAFE0V) {
+               tcpc_disable_timer(tcpc_dev, TYPEC_RT_TIMER_SAFE0V_DELAY);
+
+#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
+               tcpc_disable_timer(tcpc_dev, TYPEC_RT_TIMER_SAFE0V_TOUT);
+#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */
+       }
+
+       tcpc_dev->typec_wait_ps_change = (u8)state;
+       hisilog_err("%s: typec_wait_ps_change!!!-----------\n", __func__);
+}
+
+/* #define TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE */
+#define TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS
+
+static inline int typec_enable_low_power_mode(
+               struct tcpc_device *tcpc_dev, int pull);
+
+#define typec_get_cc1()                \
+       tcpc_dev->typec_remote_cc[0]
+#define typec_get_cc2()                \
+       tcpc_dev->typec_remote_cc[1]
+#define typec_get_cc_res()     \
+       (tcpc_dev->typec_polarity ? typec_get_cc2() : typec_get_cc1())
+
+#define typec_check_cc1(cc)    \
+       (typec_get_cc1() == (cc))
+
+#define typec_check_cc2(cc)    \
+       (typec_get_cc2() == (cc))
+
+#define typec_check_cc(cc1, cc2)       \
+       (typec_check_cc1(cc1) && typec_check_cc2(cc2))
+
+#define typec_check_cc1_unequal(cc)    \
+       (typec_get_cc1() != (cc))
+
+#define typec_check_cc2_unequal(cc)    \
+       (typec_get_cc2() != (cc))
+
+#define typec_check_cc_unequal(cc1, cc2)       \
+       (typec_check_cc1_unequal(cc1) && typec_check_cc2_unequal(cc2))
+
+#define typec_is_drp_toggling() \
+       (typec_get_cc1() == TYPEC_CC_DRP_TOGGLING)
+
+#define typec_is_cc_open()     \
+       typec_check_cc(TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_OPEN)
+
+/* TYPEC_GET_CC_STATUS */
+
+/*
+ * [BLOCK] TYPEC Connection State Definition
+ */
+
+enum TYPEC_CONNECTION_STATE {
+       typec_disabled = 0,
+       typec_errorrecovery,
+
+       typec_unattached_snk,
+       typec_unattached_src,
+
+       typec_attachwait_snk,
+       typec_attachwait_src,
+
+       typec_attached_snk,
+       typec_attached_src,
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       /* Require : Assert Rp
+        * Exit(-> Attached.SRC) : Detect Rd (tPDDebounce).
+        * Exit(-> TryWait.SNK) : Not detect Rd after tDRPTry
+        */
+       typec_try_src,
+
+       /* Require : Assert Rd
+        * Exit(-> Attached.SNK) : Detect Rp (tCCDebounce) and Vbus present.
+        * Exit(-> Unattached.SNK) : Not detect Rp (tPDDebounce)
+        */
+
+       typec_trywait_snk,
+       typec_trywait_snk_pe,
+#endif
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+
+       /* Require : Assert Rd
+        * Wait for tDRPTry and only then begin monitoring CC.
+        * Exit (-> Attached.SNK) : Detect Rp (tPDDebounce) and Vbus present.
+        * Exit (-> TryWait.SRC) : Not detect Rp for tPDDebounce.
+        */
+       typec_try_snk,
+
+       /*
+        * Require : Assert Rp
+        * Exit (-> Attached.SRC) : Detect Rd (tCCDebounce)
+        * Exit (-> Unattached.SNK) : Not detect Rd after tDRPTry
+        */
+
+       typec_trywait_src,
+       typec_trywait_src_pe,
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+       typec_audioaccessory,
+       typec_debugaccessory,
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+       typec_attached_dbgacc_snk,
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC
+       typec_attached_custom_src,
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+
+       typec_unattachwait_pe,  /* Wait Policy Engine go to Idle */
+};
+
+static const char *const typec_state_name[] = {
+       "Disabled",
+       "ErrorRecovery",
+
+       "Unattached.SNK",
+       "Unattached.SRC",
+
+       "AttachWait.SNK",
+       "AttachWait.SRC",
+
+       "Attached.SNK",
+       "Attached.SRC",
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       "Try.SRC",
+       "TryWait.SNK",
+       "TryWait.SNK.PE",
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       "Try.SNK",
+       "TryWait.SRC",
+       "TryWait.SRC.PE",
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+       "AudioAccessory",
+       "DebugAccessory",
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+       "DBGACC.SNK",
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC
+       "Custom.SRC",
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+
+       "UnattachWait.PE",
+};
+
+static inline void typec_transfer_state(struct tcpc_device *tcpc_dev,
+                                       enum TYPEC_CONNECTION_STATE state)
+{
+       TYPEC_INFO("** %s\r\n", typec_state_name[state]);
+       tcpc_dev->typec_state = (u8)state;
+}
+
+#define TYPEC_NEW_STATE(state)  \
+       (typec_transfer_state(tcpc_dev, state))
+
+/*
+ * [BLOCK] TypeC Alert Attach Status Changed
+ */
+
+static const char *const typec_attach_name[] = {
+       "NULL",
+       "SINK",
+       "SOURCE",
+       "AUDIO",
+       "DEBUG",
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+       "DBGACC_SNK",
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC
+       "CUSTOM_SRC",
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+};
+
+static int typec_alert_attach_state_change(struct tcpc_device *tcpc_dev)
+{
+       int ret = 0;
+
+       if (tcpc_dev->typec_attach_old == tcpc_dev->typec_attach_new) {
+               TYPEC_DBG("Attached-> %s(repeat)\r\n",
+                         typec_attach_name[tcpc_dev->typec_attach_new]);
+               return 0;
+       }
+
+       TYPEC_INFO("Attached-> %s\r\n",
+                  typec_attach_name[tcpc_dev->typec_attach_new]);
+
+       /*Report function */
+       ret = tcpci_report_usb_port_changed(tcpc_dev);
+
+       tcpc_dev->typec_attach_old = tcpc_dev->typec_attach_new;
+       return ret;
+}
+
+/*
+ * [BLOCK] Unattached Entry
+ */
+
+static inline int typec_enable_low_power_mode(
+               struct tcpc_device *tcpc_dev, int pull)
+{
+       int ret = 0;
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       if (tcpc_dev->typec_legacy_cable) {
+               TYPEC_DBG("LPM_LCOnly\r\n");
+               return 0;
+       }
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+       if (tcpc_dev->typec_cable_only) {
+               TYPEC_DBG("LPM_RaOnly\r\n");
+
+#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
+               if (tcpc_dev->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG)
+                       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_WAKEUP);
+#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
+
+               return 0;
+       }
+
+       if (!tcpc_dev->typec_lpm)
+               ret = tcpci_set_low_power_mode(tcpc_dev, true, pull);
+
+       tcpc_dev->typec_lpm = true;
+       return ret;
+}
+
+static inline int typec_disable_low_power_mode(
+               struct tcpc_device *tcpc_dev)
+{
+       int ret = 0;
+
+       if (tcpc_dev->typec_lpm)
+               ret = tcpci_set_low_power_mode(tcpc_dev, false, TYPEC_CC_DRP);
+
+       tcpc_dev->typec_lpm = false;
+       return ret;
+}
+
+static void typec_unattached_power_entry(struct tcpc_device *tcpc_dev)
+{
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+       hisilog_err("%s:!!!+++++++++++\n",
+                   __func__);
+
+       if (tcpc_dev->typec_power_ctrl) {
+               tcpc_dev->typec_power_ctrl = false;
+               tcpci_set_vconn(tcpc_dev, false);
+               tcpci_disable_vbus_control(tcpc_dev);
+       }
+       hisilog_err("%s:!!!-----------\n",
+                   __func__);
+}
+
+static void typec_unattached_entry(struct tcpc_device *tcpc_dev)
+{
+       typec_unattached_power_entry(tcpc_dev);
+
+       switch (tcpc_dev->typec_role) {
+       case TYPEC_ROLE_SNK:
+               TYPEC_NEW_STATE(typec_unattached_snk);
+               TYPEC_DBG("TYPEC_ROLE_SNK\r\n");
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+               typec_enable_low_power_mode(tcpc_dev, TYPEC_CC_RD);
+               break;
+       case TYPEC_ROLE_SRC:
+               TYPEC_NEW_STATE(typec_unattached_src);
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RP);
+               typec_enable_low_power_mode(tcpc_dev, TYPEC_CC_RP);
+               break;
+       default:
+               switch (tcpc_dev->typec_state) {
+               case typec_attachwait_snk:
+               case typec_audioaccessory:
+                       TYPEC_NEW_STATE(typec_unattached_src);
+                       tcpci_set_cc(tcpc_dev, TYPEC_CC_RP);
+                       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_DRP_SRC_TOGGLE);
+                       break;
+               default:
+                       gpio_hub_switch_to_hub();
+                       gpio_hub_typec_power_off();
+                       if (oldstatus == TYPEC_DEVICE) {
+                               TYPEC_DBG("device off, otg host:%d:%d\r\n",
+                                         oldstatus, tcpc_dev->typec_state);
+                               gpio_hub_power_on();
+
+                               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+                               hisi_usb_otg_event(ID_FALL_EVENT);
+                               oldstatus = TYPEC_HOST;
+                       } else if (oldstatus == TYPEC_INIT) {
+                               TYPEC_DBG("init otg host no insert.\r\n");
+                               gpio_hub_power_on();
+                               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+                               hisi_usb_otg_event(ID_FALL_EVENT);
+                               oldstatus = TYPEC_HOST;
+                       } else {
+                               TYPEC_DBG("host off, otg host:%d:%d\r\n",
+                                         oldstatus, tcpc_dev->typec_state);
+                       }
+                       TYPEC_NEW_STATE(typec_unattached_snk);
+                       tcpci_set_cc(tcpc_dev, TYPEC_CC_DRP);
+                       typec_enable_low_power_mode(tcpc_dev, TYPEC_CC_DRP);
+                       break;
+               }
+               break;
+       }
+}
+
+static void typec_unattach_wait_pe_idle_entry(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->typec_attach_new = TYPEC_UNATTACHED;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->typec_attach_old) {
+               TYPEC_NEW_STATE(typec_unattachwait_pe);
+               return;
+       }
+#endif
+
+       typec_unattached_entry(tcpc_dev);
+}
+
+/*
+ * [BLOCK] Attached Entry
+ */
+
+static inline int typec_set_polarity(struct tcpc_device *tcpc_dev,
+                                    bool polarity)
+{
+       tcpc_dev->typec_polarity = polarity;
+       return tcpci_set_polarity(tcpc_dev, polarity);
+}
+
+static inline int typec_set_plug_orient(struct tcpc_device *tcpc_dev,
+                                       u8 res, bool polarity)
+{
+       int rv = typec_set_polarity(tcpc_dev, polarity);
+
+       if (rv)
+               return rv;
+
+       return tcpci_set_cc(tcpc_dev, res);
+}
+
+static void typec_source_attached_with_vbus_entry(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->typec_attach_new = TYPEC_ATTACHED_SRC;
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+}
+
+static inline void typec_source_attached_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_attached_src);
+
+       TYPEC_DBG("typec otg host attach %s\r\n", __func__);
+       oldstatus = TYPEC_HOST;
+       gpio_hub_switch_to_typec();
+       gpio_hub_typec_power_on();
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_SRC_VSAFE5V);
+
+       tcpc_disable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+
+       typec_set_plug_orient(tcpc_dev,
+                             tcpc_dev->typec_local_rp_level,
+                                 typec_check_cc2(TYPEC_CC_VOLT_RD));
+
+       tcpc_dev->typec_power_ctrl = true;
+       tcpci_set_vconn(tcpc_dev, true);
+       tcpci_source_vbus(tcpc_dev,
+                         TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, -1);
+}
+
+static inline void typec_sink_attached_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_attached_snk);
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       tcpc_dev->typec_legacy_cable = false;
+       tcpc_dev->typec_legacy_cable_suspect = 0;
+#endif /* CONFIG_TYPEC_CHECK_LEGAY_CABLE */
+
+       tcpc_dev->typec_attach_new = TYPEC_ATTACHED_SNK;
+
+#ifdef CONFIG_TYPEC_CAP_TRY_STATE
+       if (tcpc_dev->typec_role >= TYPEC_ROLE_DRP)
+               tcpc_reset_typec_try_timer(tcpc_dev);
+#endif /* CONFIG_TYPEC_CAP_TRY_STATE */
+
+       typec_set_plug_orient(tcpc_dev, TYPEC_CC_RD,
+                             typec_check_cc2_unequal(TYPEC_CC_VOLT_OPEN));
+       tcpc_dev->typec_remote_rp_level = typec_get_cc_res();
+
+       tcpc_dev->typec_power_ctrl = true;
+       tcpci_sink_vbus(tcpc_dev, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
+}
+
+static inline void typec_custom_src_attached_entry(
+               struct tcpc_device *tcpc_dev)
+{
+#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC
+       int cc1 = typec_get_cc1();
+       int cc2 = typec_get_cc2();
+
+       if (cc1 == TYPEC_CC_VOLT_SNK_DFT && cc2 == TYPEC_CC_VOLT_SNK_DFT) {
+               TYPEC_NEW_STATE(typec_attached_custom_src);
+               tcpc_dev->typec_attach_new = TYPEC_ATTACHED_CUSTOM_SRC;
+               TYPEC_DBG("typec host mode, device attached\r\n");
+               oldstatus = TYPEC_DEVICE;
+               gpio_hub_power_off();
+               gpio_hub_typec_power_off();
+
+               hisi_usb_otg_event(ID_RISE_EVENT);
+               hisi_usb_otg_event(CHARGER_CONNECT_EVENT);
+               return;
+       }
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+       TYPEC_DBG("[Warning] Same Rp (%d)\r\n", cc1);
+#else
+       TYPEC_DBG("[Warning] CC Both Rp\r\n");
+#endif
+}
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+
+static inline u8 typec_get_sink_dbg_acc_rp_level(
+               int cc1, int cc2)
+{
+       if (cc2 == TYPEC_CC_VOLT_SNK_DFT)
+               return cc1;
+       else
+               return TYPEC_CC_VOLT_SNK_DFT;
+}
+
+static inline void typec_sink_dbg_acc_attached_entry(
+               struct tcpc_device *tcpc_dev)
+{
+       bool polarity;
+       u8 rp_level;
+
+       int cc1 = typec_get_cc1();
+       int cc2 = typec_get_cc2();
+
+       if (cc1 == cc2) {
+               typec_custom_src_attached_entry(tcpc_dev);
+               return;
+       }
+
+       TYPEC_NEW_STATE(typec_attached_dbgacc_snk);
+
+       tcpc_dev->typec_attach_new = TYPEC_ATTACHED_DBGACC_SNK;
+
+       polarity = cc2 > cc1;
+
+       if (polarity)
+               rp_level = typec_get_sink_dbg_acc_rp_level(cc2, cc1);
+       else
+               rp_level = typec_get_sink_dbg_acc_rp_level(cc1, cc2);
+
+       typec_set_plug_orient(tcpc_dev, TYPEC_CC_RD, polarity);
+       tcpc_dev->typec_remote_rp_level = rp_level;
+
+       tcpc_dev->typec_power_ctrl = true;
+       tcpci_sink_vbus(tcpc_dev, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
+}
+#else
+static inline void typec_sink_dbg_acc_attached_entry(
+               struct tcpc_device *tcpc_dev)
+{
+       typec_custom_src_attached_entry(tcpc_dev);
+}
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+/*
+ * [BLOCK] Try.SRC / TryWait.SNK
+ */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+
+static inline void typec_try_src_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_try_src);
+       tcpc_dev->typec_drp_try_timeout = false;
+
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_RP);
+       tcpc_enable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+}
+
+static inline void typec_trywait_snk_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_trywait_snk);
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+       tcpci_set_vconn(tcpc_dev, false);
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+       tcpci_source_vbus(tcpc_dev,
+                         TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0);
+       tcpc_disable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+
+       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+}
+
+static inline void typec_trywait_snk_pe_entry(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->typec_attach_new = TYPEC_UNATTACHED;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->typec_attach_old) {
+               TYPEC_NEW_STATE(typec_trywait_snk_pe);
+               return;
+       }
+#endif
+
+       typec_trywait_snk_entry(tcpc_dev);
+}
+
+#endif /* #ifdef CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+/*
+ * [BLOCK] Try.SNK / TryWait.SRC
+ */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+
+static inline void typec_try_snk_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_try_snk);
+       tcpc_dev->typec_drp_try_timeout = false;
+
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+       tcpc_enable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+}
+
+static inline void typec_trywait_src_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_NEW_STATE(typec_trywait_src);
+       tcpc_dev->typec_drp_try_timeout = false;
+
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_RP);
+       tcpci_sink_vbus(tcpc_dev, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_0V, 0);
+       tcpc_enable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+}
+
+#ifndef CONFIG_USB_POWER_DELIVERY
+static inline void typec_trywait_src_pe_entry(struct tcpc_device *tcpc_dev)
+{
+       tcpc_dev->typec_attach_new = TYPEC_UNATTACHED;
+
+       if (tcpc_dev->typec_attach_old) {
+               TYPEC_NEW_STATE(typec_trywait_src_pe);
+               return;
+       }
+
+       typec_trywait_src_entry(tcpc_dev);
+}
+#endif
+
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+/*
+ * [BLOCK] Attach / Detach
+ */
+
+static inline void typec_cc_snk_detect_vsafe5v_entry(
+               struct tcpc_device *tcpc_dev)
+{
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+       if (typec_check_cc_unequal(TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_OPEN)) {
+               typec_sink_dbg_acc_attached_entry(tcpc_dev);
+               return;
+       }
+
+       TYPEC_DBG("typec device mode, attached\r\n");
+       oldstatus = TYPEC_DEVICE;
+       gpio_hub_power_off();
+       gpio_hub_typec_power_off();
+       hisi_usb_otg_event(ID_RISE_EVENT);
+       gpio_hub_switch_to_typec();
+       hisi_usb_otg_event(CHARGER_CONNECT_EVENT);
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       if (tcpc_dev->typec_role == TYPEC_ROLE_TRY_SRC) {
+               if (tcpc_dev->typec_state == typec_attachwait_snk) {
+                       typec_try_src_entry(tcpc_dev);
+                       return;
+               }
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+       typec_sink_attached_entry(tcpc_dev);
+}
+
+static inline void typec_cc_snk_detect_entry(struct tcpc_device *tcpc_dev)
+{
+       /* If Port Partner act as Source without VBUS, wait vSafe5V */
+       if (tcpci_check_vbus_valid(tcpc_dev))
+               typec_cc_snk_detect_vsafe5v_entry(tcpc_dev);
+       else
+               typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_SNK_VSAFE5V);
+}
+
+static inline void typec_cc_src_detect_vsafe0v_entry(
+               struct tcpc_device *tcpc_dev)
+{
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       if (tcpc_dev->typec_role == TYPEC_ROLE_TRY_SNK) {
+               if (tcpc_dev->typec_state == typec_attachwait_src) {
+                       typec_try_snk_entry(tcpc_dev);
+                       return;
+               }
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+       typec_source_attached_entry(tcpc_dev);
+}
+
+static inline void typec_cc_src_detect_entry(
+               struct tcpc_device *tcpc_dev)
+{
+       /* If Port Partner act as Sink with low VBUS, wait vSafe0v */
+       bool vbus_absent = tcpci_check_vsafe0v(tcpc_dev, true);
+
+       if (vbus_absent)
+               typec_cc_src_detect_vsafe0v_entry(tcpc_dev);
+       else
+               typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_SRC_VSAFE0V);
+}
+
+static inline void typec_cc_src_remove_entry(struct tcpc_device *tcpc_dev)
+{
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       if (tcpc_dev->typec_role == TYPEC_ROLE_TRY_SRC) {
+               switch (tcpc_dev->typec_state) {
+               case typec_attached_src:
+                       typec_trywait_snk_pe_entry(tcpc_dev);
+                       return;
+               case typec_try_src:
+                       typec_trywait_snk_entry(tcpc_dev);
+                       return;
+               }
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+       typec_unattach_wait_pe_idle_entry(tcpc_dev);
+}
+
+static inline void typec_cc_snk_remove_entry(struct tcpc_device *tcpc_dev)
+{
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       if (tcpc_dev->typec_state == typec_try_snk) {
+               typec_trywait_src_entry(tcpc_dev);
+               return;
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+       typec_unattach_wait_pe_idle_entry(tcpc_dev);
+}
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+
+static inline bool typec_check_legacy_cable(
+               struct tcpc_device *tcpc_dev)
+{
+       bool check_legacy = false;
+
+       if (typec_check_cc(TYPEC_CC_VOLT_RD, TYPEC_CC_VOLT_OPEN) ||
+           typec_check_cc(TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_RD))
+               check_legacy = true;
+
+       if (check_legacy &&
+           tcpc_dev->typec_legacy_cable_suspect >=
+               TCPC_LEGACY_CABLE_CONFIRM) {
+               TYPEC_INFO("LC->Suspect\r\n");
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RP_1_5);
+               mdelay(1);
+
+               if (tcpci_get_cc(tcpc_dev) != 0) {
+                       TYPEC_INFO("LC->Confirm\r\n");
+                       tcpc_dev->typec_legacy_cable = true;
+                       tcpc_dev->typec_legacy_cable_suspect =
+                               TCPC_LEGACY_CABLE_CONFIRM;
+                       return true;
+               }
+
+               tcpc_dev->typec_legacy_cable = false;
+               tcpc_dev->typec_legacy_cable_suspect = 0;
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RP);
+       }
+
+       return false;
+}
+
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+static inline bool typec_cc_change_source_entry(struct tcpc_device *tcpc_dev)
+{
+       int cc1, cc2;
+
+       cc1 = typec_get_cc1();
+       cc2 = typec_get_cc2();
+
+       if ((cc1 == TYPEC_CC_VOLT_RD) && (cc2 == TYPEC_CC_VOLT_RD)) {
+               TYPEC_NEW_STATE(typec_debugaccessory);
+               TYPEC_DBG("[Debug] CC1&2 Both Rd\r\n");
+               tcpc_dev->typec_attach_new = TYPEC_ATTACHED_DEBUG;
+       } else if ((cc1 == TYPEC_CC_VOLT_RA) && (cc2 == TYPEC_CC_VOLT_RA)) {
+               TYPEC_NEW_STATE(typec_audioaccessory);
+               TYPEC_DBG("[Audio] CC1&2 Both Ra\r\n");
+               tcpc_dev->typec_attach_new = TYPEC_ATTACHED_AUDIO;
+       } else {
+               if ((cc1 == TYPEC_CC_VOLT_RD) || (cc2 == TYPEC_CC_VOLT_RD)) {
+                       typec_cc_src_detect_entry(tcpc_dev);
+               } else {
+                       if ((cc1 == TYPEC_CC_VOLT_RA) ||
+                           (cc2 == TYPEC_CC_VOLT_RA))
+                               TYPEC_DBG("[Cable] Ra Only\r\n");
+                       typec_cc_src_remove_entry(tcpc_dev);
+               }
+       }
+
+       return true;
+}
+
+static inline bool typec_cc_change_sink_entry(struct tcpc_device *tcpc_dev)
+{
+       if (typec_is_cc_open())
+               typec_cc_snk_remove_entry(tcpc_dev);
+       else
+               typec_cc_snk_detect_entry(tcpc_dev);
+
+       return true;
+}
+
+static inline bool typec_is_act_as_sink_role(
+               struct tcpc_device *tcpc_dev)
+{
+       bool as_sink = true;
+       u8 cc_sum;
+
+       switch (tcpc_dev->typec_local_cc & 0x07) {
+       case TYPEC_CC_RP:
+               as_sink = false;
+               break;
+       case TYPEC_CC_RD:
+               as_sink = true;
+               break;
+       case TYPEC_CC_DRP:
+               cc_sum = typec_get_cc1() + typec_get_cc2();
+               as_sink = (cc_sum >= TYPEC_CC_VOLT_SNK_DFT);
+               break;
+       }
+
+       return as_sink;
+}
+
+static inline bool typec_handle_cc_changed_entry(struct tcpc_device *tcpc_dev)
+{
+       TYPEC_INFO("[CC_Change] %d/%d\r\n", typec_get_cc1(), typec_get_cc2());
+
+       tcpc_dev->typec_attach_new = tcpc_dev->typec_attach_old;
+
+       if (typec_is_act_as_sink_role(tcpc_dev))
+               typec_cc_change_sink_entry(tcpc_dev);
+       else
+               typec_cc_change_source_entry(tcpc_dev);
+
+       typec_alert_attach_state_change(tcpc_dev);
+       return true;
+}
+
+/*
+ * [BLOCK] Handle cc-change event
+ */
+
+static inline void typec_attach_wait_entry(struct tcpc_device *tcpc_dev)
+{
+       bool as_sink;
+
+       if (tcpc_dev->typec_attach_old ||
+           tcpc_dev->typec_state == typec_attached_src) {
+               tcpc_reset_typec_debounce_timer(tcpc_dev);
+               TYPEC_DBG("Attached, Ignore cc_attach\r\n");
+               return;
+       }
+
+       switch (tcpc_dev->typec_state) {
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       case typec_try_src:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+               return;
+
+       case typec_trywait_snk:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
+               return;
+#endif
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       case typec_try_snk:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+               return;
+
+       case typec_trywait_src:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
+               return;
+#endif
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       case typec_unattachwait_pe:
+               TYPEC_INFO("Force PE Idle\r\n");
+               tcpc_dev->pd_wait_pe_idle = false;
+               tcpc_disable_timer(tcpc_dev, TYPEC_RT_TIMER_PE_IDLE);
+               typec_unattached_power_entry(tcpc_dev);
+               break;
+#endif
+       }
+
+       as_sink = typec_is_act_as_sink_role(tcpc_dev);
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       if (!as_sink && typec_check_legacy_cable(tcpc_dev))
+               return;
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+       if (as_sink)
+               TYPEC_NEW_STATE(typec_attachwait_snk);
+       else
+               TYPEC_NEW_STATE(typec_attachwait_src);
+
+       tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
+}
+
+#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS
+static inline int typec_attached_snk_cc_detach(struct tcpc_device *tcpc_dev)
+{
+       int vbus_valid = tcpci_check_vbus_valid(tcpc_dev);
+       bool detach_by_cc = false;
+
+       /* For Ellisys Test, Applying Low VBUS (3.67v) as Sink */
+       if (vbus_valid) {
+               detach_by_cc = true;
+               TYPEC_DBG("Detach_CC (LowVBUS)\r\n");
+       }
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       /* For Source detach during HardReset */
+       if ((!vbus_valid) &&
+           tcpc_dev->pd_wait_hard_reset_complete) {
+               detach_by_cc = true;
+               TYPEC_DBG("Detach_CC (HardReset)\r\n");
+       }
+#endif
+
+       if (detach_by_cc)
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+
+       return 0;
+}
+#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */
+
+static inline void typec_detach_wait_entry(struct tcpc_device *tcpc_dev)
+{
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       bool suspect_legacy = false;
+
+       if (tcpc_dev->typec_state == typec_attachwait_src) {
+               suspect_legacy = true;
+       } else if (tcpc_dev->typec_state == typec_attached_src) {
+               if (tcpc_dev->typec_attach_old != TYPEC_ATTACHED_SRC) {
+                       suspect_legacy = true;
+               } else {
+                       tcpc_dev->typec_legacy_cable = false;
+                       tcpc_dev->typec_legacy_cable_suspect = 0;
+               }
+       }
+
+       if (suspect_legacy) {
+               tcpc_dev->typec_legacy_cable_suspect++;
+               TYPEC_DBG("LC_suspect: %d\r\n",
+                         tcpc_dev->typec_legacy_cable_suspect);
+       }
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+       switch (tcpc_dev->typec_state) {
+#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS
+       case typec_attached_snk:
+               typec_attached_snk_cc_detach(tcpc_dev);
+               break;
+#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */
+
+       case typec_audioaccessory:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
+               break;
+
+#ifdef TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE
+       case typec_attached_src:
+               TYPEC_INFO("Exit Attached.SRC immediately\r\n");
+               tcpc_reset_typec_debounce_timer(tcpc_dev);
+
+                       /* force to terminate TX */
+               tcpci_init(tcpc_dev, true);
+
+               typec_cc_src_remove_entry(tcpc_dev);
+               typec_alert_attach_state_change(tcpc_dev);
+               break;
+#endif /* TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       case typec_try_src:
+               if (tcpc_dev->typec_drp_try_timeout) {
+                       tcpc_enable_timer(tcpc_dev,
+                                         TYPEC_TIMER_PDDEBOUNCE);
+               } else {
+                       tcpc_reset_typec_debounce_timer(tcpc_dev);
+                       TYPEC_DBG("[Try] Igrone cc_detach\r\n");
+               }
+               break;
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       case typec_trywait_src:
+               if (tcpc_dev->typec_drp_try_timeout) {
+                   tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+               } else {
+                       tcpc_reset_typec_debounce_timer(tcpc_dev);
+                       TYPEC_DBG("[Try] Igrone cc_detach\r\n");
+               }
+               break;
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+       default:
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+               break;
+       }
+}
+
+static inline bool typec_is_cc_attach(struct tcpc_device *tcpc_dev)
+{
+       bool cc_attach = false;
+       int cc1 = typec_get_cc1();
+       int cc2 = typec_get_cc2();
+       int cc_res = typec_get_cc_res();
+
+       tcpc_dev->typec_cable_only = false;
+
+       if (tcpc_dev->typec_attach_old) {
+               if ((cc_res != TYPEC_CC_VOLT_OPEN) &&
+                   (cc_res != TYPEC_CC_VOLT_RA))
+                       cc_attach = true;
+       } else {
+               if (cc1 != TYPEC_CC_VOLT_OPEN)
+                       cc_attach = true;
+
+               if (cc2 != TYPEC_CC_VOLT_OPEN)
+                       cc_attach = true;
+
+               /* Cable Only, no device */
+               if ((cc1 + cc2) == TYPEC_CC_VOLT_RA) {
+                       cc_attach = false;
+                       tcpc_dev->typec_cable_only = true;
+               }
+       }
+
+       return cc_attach;
+}
+
+int tcpc_typec_handle_cc_change(struct tcpc_device *tcpc_dev)
+{
+       int ret = tcpci_get_cc(tcpc_dev);
+
+       if (ret < 0)
+               return ret;
+
+       if (typec_is_drp_toggling()) {
+               TYPEC_DBG("[Waring] DRP Toggling\r\n");
+               if (tcpc_dev->typec_lpm)
+                       tcpci_set_low_power_mode(tcpc_dev, true, TYPEC_CC_DRP);
+               return 0;
+       }
+
+       TYPEC_INFO("[CC_Alert] %d/%d\r\n", typec_get_cc1(), typec_get_cc2());
+
+       typec_disable_low_power_mode(tcpc_dev);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->pd_wait_pr_swap_complete) {
+               TYPEC_DBG("[PR.Swap] Ignore CC_Alert\r\n");
+               return 0;
+       }
+
+       if (tcpc_dev->pd_wait_error_recovery) {
+               TYPEC_DBG("[Recovery] Ignore CC_Alert\r\n");
+               return 0;
+       }
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       if ((tcpc_dev->typec_state == typec_try_snk) &&
+           (!tcpc_dev->typec_drp_try_timeout)) {
+               TYPEC_DBG("[Try.SNK] Ignore CC_Alert\r\n");
+               return 0;
+       }
+
+       if (tcpc_dev->typec_state == typec_trywait_src_pe) {
+               TYPEC_DBG("[Try.PE] Ignore CC_Alert\r\n");
+               return 0;
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       if (tcpc_dev->typec_state == typec_trywait_snk_pe) {
+               TYPEC_DBG("[Try.PE] Ignore CC_Alert\r\n");
+               return 0;
+       }
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+       if (tcpc_dev->typec_state == typec_attachwait_snk ||
+           tcpc_dev->typec_state == typec_attachwait_src)
+               typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+       if (typec_is_cc_attach(tcpc_dev))
+               typec_attach_wait_entry(tcpc_dev);
+       else
+               typec_detach_wait_entry(tcpc_dev);
+
+       return 0;
+}
+
+/*
+ * [BLOCK] Handle timeout event
+ */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_STATE
+static inline int typec_handle_drp_try_timeout(struct tcpc_device *tcpc_dev)
+{
+       bool not_detect, en_timer;
+
+       tcpc_dev->typec_drp_try_timeout = true;
+       tcpc_disable_timer(tcpc_dev, TYPEC_TRY_TIMER_DRP_TRY);
+
+       if (typec_is_drp_toggling()) {
+               TYPEC_DBG("[Waring] DRP Toggling\r\n");
+               return 0;
+       }
+
+       not_detect = typec_is_cc_open();
+
+       switch (tcpc_dev->typec_state) {
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       case typec_try_src:
+               en_timer = not_detect;
+               break;
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */
+
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+       case typec_trywait_src:
+               en_timer = not_detect;
+               break;
+
+       case typec_try_snk:
+               en_timer = true;
+               break;
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+
+       default:
+               en_timer = false;
+               break;
+       }
+
+       if (en_timer)
+               tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
+
+       return 0;
+}
+#endif /* CONFIG_TYPEC_CAP_TRY_STATE */
+
+static inline int typec_handle_debounce_timeout(struct tcpc_device *tcpc_dev)
+{
+       if (typec_is_drp_toggling()) {
+               TYPEC_DBG("[Waring] DRP Toggling\r\n");
+               return 0;
+       }
+
+       typec_handle_cc_changed_entry(tcpc_dev);
+       return 0;
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+static inline int typec_handle_error_recovery_timeout(
+               struct tcpc_device *tcpc_dev)
+{
+       /* TODO: Check it later */
+       tcpc_dev->typec_attach_new = TYPEC_UNATTACHED;
+
+       mutex_lock(&tcpc_dev->access_lock);
+       tcpc_dev->pd_wait_error_recovery = false;
+       mutex_unlock(&tcpc_dev->access_lock);
+
+       typec_unattach_wait_pe_idle_entry(tcpc_dev);
+       typec_alert_attach_state_change(tcpc_dev);
+
+       return 0;
+}
+
+static inline int typec_handle_pe_idle(struct tcpc_device *tcpc_dev)
+{
+       switch (tcpc_dev->typec_state) {
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+       case typec_trywait_snk_pe:
+               typec_trywait_snk_entry(tcpc_dev);
+               break;
+#endif
+
+       case typec_unattachwait_pe:
+               typec_unattached_entry(tcpc_dev);
+               break;
+
+       default:
+               TYPEC_DBG("dummy_pe_idle\r\n");
+               break;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+static inline int typec_handle_src_reach_vsafe0v(struct tcpc_device *tcpc_dev)
+{
+       if (typec_is_drp_toggling()) {
+               TYPEC_DBG("[Waring] DRP Toggling\r\n");
+               return 0;
+       }
+
+       typec_cc_src_detect_vsafe0v_entry(tcpc_dev);
+       return 0;
+}
+
+static inline int typec_handle_src_toggle_timeout(struct tcpc_device *tcpc_dev)
+{
+       if (tcpc_dev->typec_state == typec_unattached_src) {
+               TYPEC_NEW_STATE(typec_unattached_snk);
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_DRP);
+               typec_enable_low_power_mode(tcpc_dev, TYPEC_CC_DRP);
+       }
+
+       return 0;
+}
+
+int tcpc_typec_handle_timeout(struct tcpc_device *tcpc_dev, u32 timer_id)
+{
+       int ret = 0;
+
+#ifdef CONFIG_TYPEC_CAP_TRY_STATE
+       if (timer_id == TYPEC_TRY_TIMER_DRP_TRY)
+               return typec_handle_drp_try_timeout(tcpc_dev);
+#endif /* CONFIG_TYPEC_CAP_TRY_STATE */
+
+       if (timer_id >= TYPEC_TIMER_START_ID)
+               tcpc_reset_typec_debounce_timer(tcpc_dev);
+       else if (timer_id >= TYPEC_RT_TIMER_START_ID)
+               tcpc_disable_timer(tcpc_dev, timer_id);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->pd_wait_pr_swap_complete) {
+               TYPEC_DBG("[PR.Swap] Igrone timer_evt\r\n");
+               return 0;
+       }
+
+       if (tcpc_dev->pd_wait_error_recovery &&
+           (timer_id != TYPEC_TIMER_ERROR_RECOVERY)) {
+               TYPEC_DBG("[Recovery] Igrone timer_evt\r\n");
+               return 0;
+       }
+#endif
+
+       switch (timer_id) {
+       case TYPEC_TIMER_CCDEBOUNCE:
+       case TYPEC_TIMER_PDDEBOUNCE:
+               ret = typec_handle_debounce_timeout(tcpc_dev);
+               break;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       case TYPEC_TIMER_ERROR_RECOVERY:
+               ret = typec_handle_error_recovery_timeout(tcpc_dev);
+               break;
+
+       case TYPEC_RT_TIMER_PE_IDLE:
+               ret = typec_handle_pe_idle(tcpc_dev);
+               break;
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       case TYPEC_RT_TIMER_SAFE0V_DELAY:
+               ret = typec_handle_src_reach_vsafe0v(tcpc_dev);
+               break;
+
+       case TYPEC_TIMER_WAKEUP:
+               if (tcpc_dev->typec_lpm || tcpc_dev->typec_cable_only) {
+                       tcpc_dev->typec_lpm = true;
+                       ret =
+                               tcpci_set_low_power_mode(
+                                               tcpc_dev, true,
+                                               (tcpc_dev->typec_role ==
+                                                TYPEC_ROLE_SRC) ?
+                                               TYPEC_CC_RP :
+                                               TYPEC_CC_DRP);
+               }
+               break;
+
+#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
+       case TYPEC_RT_TIMER_SAFE0V_TOUT:
+               ret = tcpc_typec_handle_vsafe0v(tcpc_dev);
+               break;
+#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */
+
+       case TYPEC_TIMER_DRP_SRC_TOGGLE:
+               ret = typec_handle_src_toggle_timeout(tcpc_dev);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * [BLOCK] Handle ps-change event
+ */
+
+static inline int typec_handle_vbus_present(struct tcpc_device *tcpc_dev)
+{
+       switch (tcpc_dev->typec_wait_ps_change) {
+       case TYPEC_WAIT_PS_SNK_VSAFE5V:
+               typec_cc_snk_detect_vsafe5v_entry(tcpc_dev);
+               typec_alert_attach_state_change(tcpc_dev);
+               break;
+       case TYPEC_WAIT_PS_SRC_VSAFE5V:
+               typec_source_attached_with_vbus_entry(tcpc_dev);
+               typec_alert_attach_state_change(tcpc_dev);
+               break;
+       }
+
+       return 0;
+}
+
+static inline int typec_attached_snk_vbus_absent(struct tcpc_device *tcpc_dev)
+{
+#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->pd_wait_hard_reset_complete ||
+           tcpc_dev->pd_hard_reset_event_pending) {
+               if (typec_get_cc_res() != TYPEC_CC_VOLT_OPEN) {
+                       TYPEC_DBG
+                               ("Ignore vbus_absent(snk), HReset & CC!=0\r\n");
+                       return 0;
+               }
+       }
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+       typec_unattach_wait_pe_idle_entry(tcpc_dev);
+       typec_alert_attach_state_change(tcpc_dev);
+#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */
+
+       return 0;
+}
+
+static inline int typec_handle_vbus_absent(struct tcpc_device *tcpc_dev)
+{
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (tcpc_dev->pd_wait_pr_swap_complete) {
+               TYPEC_DBG("[PR.Swap] Igrone vbus_absent\r\n");
+               return 0;
+       }
+
+       if (tcpc_dev->pd_wait_error_recovery) {
+               TYPEC_DBG("[Recovery] Igrone vbus_absent\r\n");
+               return 0;
+       }
+#endif
+
+       if (tcpc_dev->typec_state == typec_attached_snk)
+               typec_attached_snk_vbus_absent(tcpc_dev);
+
+#ifndef CONFIG_TCPC_VSAFE0V_DETECT
+       tcpc_typec_handle_vsafe0v(tcpc_dev);
+#endif /* #ifdef CONFIG_TCPC_VSAFE0V_DETECT */
+
+       return 0;
+}
+
+int tcpc_typec_handle_ps_change(struct tcpc_device *tcpc_dev, int vbus_level)
+{
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       if (tcpc_dev->typec_legacy_cable) {
+               if (vbus_level) {
+                       TYPEC_INFO("LC->Attached\r\n");
+                       tcpc_dev->typec_legacy_cable = false;
+                       tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+               } else {
+                       TYPEC_INFO("LC->Detached\r\n");
+                       tcpc_dev->typec_legacy_cable = false;
+                       tcpci_set_cc(tcpc_dev, TYPEC_CC_DRP);
+                       typec_enable_low_power_mode(tcpc_dev, TYPEC_CC_DRP);
+               }
+               return 0;
+       }
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+       if (typec_is_drp_toggling()) {
+               TYPEC_DBG("[Waring] DRP Toggling\r\n");
+               return 0;
+       }
+
+       if (vbus_level)
+               return typec_handle_vbus_present(tcpc_dev);
+       else
+               return typec_handle_vbus_absent(tcpc_dev);
+}
+
+/*
+ * [BLOCK] Handle PE event
+ */
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+int tcpc_typec_advertise_explicit_contract(struct tcpc_device *tcpc_dev)
+{
+       if (tcpc_dev->typec_local_rp_level == TYPEC_CC_RP_DFT)
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RP_1_5);
+
+       return 0;
+}
+
+int tcpc_typec_handle_pe_pr_swap(struct tcpc_device *tcpc_dev)
+{
+       int ret = 0;
+
+       mutex_lock(&tcpc_dev->typec_lock);
+       switch (tcpc_dev->typec_state) {
+       case typec_attached_snk:
+               TYPEC_NEW_STATE(typec_attached_src);
+               tcpc_dev->typec_attach_old = TYPEC_ATTACHED_SRC;
+               tcpci_set_cc(tcpc_dev, tcpc_dev->typec_local_rp_level);
+               break;
+       case typec_attached_src:
+               TYPEC_NEW_STATE(typec_attached_snk);
+               tcpc_dev->typec_attach_old = TYPEC_ATTACHED_SNK;
+               tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&tcpc_dev->typec_lock);
+       return ret;
+}
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+/*
+ * [BLOCK] Handle reach vSafe0V event
+ */
+
+int tcpc_typec_handle_vsafe0v(struct tcpc_device *tcpc_dev)
+{
+       if (tcpc_dev->typec_wait_ps_change == TYPEC_WAIT_PS_SRC_VSAFE0V)
+               typec_handle_src_reach_vsafe0v(tcpc_dev);
+
+       return 0;
+}
+
+/*
+ * [BLOCK] TCPCI TypeC I/F
+ */
+
+static const char *const typec_role_name[] = {
+       "UNKNOWN",
+       "SNK",
+       "SRC",
+       "DRP",
+       "TrySRC",
+       "TrySNK",
+};
+
+#ifndef CONFIG_USB_POWER_DELIVERY
+int tcpc_typec_swap_role(struct tcpc_device *tcpc_dev)
+{
+       if (tcpc_dev->typec_role < TYPEC_ROLE_DRP)
+               return -1;
+       TYPEC_INFO("%s\r\n", __func__);
+
+       switch (tcpc_dev->typec_state) {
+       case typec_attached_src:
+#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE
+               typec_trywait_snk_pe_entry(tcpc_dev);
+#else
+               TYPEC_INFO("SRC->SNK (X)\r\n");
+#endif /* CONFIG_TYPEC_CAP_TRY_SOURCR */
+               break;
+       case typec_attached_snk:
+#ifdef CONFIG_TYPEC_CAP_TRY_SINK
+               typec_trywait_src_pe_entry(tcpc_dev);
+#else
+               TYPEC_INFO("SNK->SRC (X)\r\n");
+#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
+               break;
+       }
+       return typec_alert_attach_state_change(tcpc_dev);
+}
+#endif /* ifndef CONFIG_USB_POWER_DELIVERY */
+
+int tcpc_typec_set_rp_level(struct tcpc_device *tcpc_dev, u8 res)
+{
+       switch (res) {
+       case TYPEC_CC_RP_DFT:
+       case TYPEC_CC_RP_1_5:
+       case TYPEC_CC_RP_3_0:
+               TYPEC_INFO("TypeC-Rp: %d\r\n", res);
+               tcpc_dev->typec_local_rp_level = res;
+               break;
+
+       default:
+               TYPEC_INFO("TypeC-Unknown-Rp (%d)\r\n", res);
+               return -1;
+       }
+
+#ifdef CONFIG_USB_PD_DBG_ALWAYS_LOCAL_RP
+       tcpci_set_cc(tcpc_dev, tcpc_dev->typec_local_rp_level);
+#else
+       if ((tcpc_dev->typec_attach_old != TYPEC_UNATTACHED) &&
+           (tcpc_dev->typec_attach_new != TYPEC_UNATTACHED)) {
+               return tcpci_set_cc(tcpc_dev, res);
+       }
+#endif
+
+       return 0;
+}
+
+int tcpc_typec_change_role(
+               struct tcpc_device *tcpc_dev, u8 typec_role)
+{
+       u8 local_cc;
+       bool force_unattach = false;
+
+       if (typec_role == TYPEC_ROLE_UNKNOWN ||
+           typec_role >= TYPEC_ROLE_NR) {
+               TYPEC_INFO("Wrong TypeC-Role: %d\r\n", typec_role);
+               return -1;
+       }
+
+       mutex_lock(&tcpc_dev->access_lock);
+
+       tcpc_dev->typec_role = typec_role;
+       TYPEC_INFO("typec_new_role: %s\r\n", typec_role_name[typec_role]);
+
+       local_cc = tcpc_dev->typec_local_cc & 0x07;
+
+       if (typec_role == TYPEC_ROLE_SNK && local_cc == TYPEC_CC_RP)
+               force_unattach = true;
+
+       if (typec_role == TYPEC_ROLE_SRC && local_cc == TYPEC_CC_RD)
+               force_unattach = true;
+
+       if (tcpc_dev->typec_attach_new == TYPEC_UNATTACHED)
+               force_unattach = true;
+
+       if (force_unattach) {
+               TYPEC_DBG("force_unattach\r\n");
+               typec_disable_low_power_mode(tcpc_dev);
+               typec_unattached_entry(tcpc_dev);
+       }
+
+       mutex_unlock(&tcpc_dev->access_lock);
+       return 0;
+}
+
+#ifdef CONFIG_TYPEC_CAP_POWER_OFF_CHARGE
+static int typec_init_power_off_charge(struct tcpc_device *tcpc_dev)
+{
+       int ret = tcpci_get_cc(tcpc_dev);
+
+       if (ret < 0)
+               return ret;
+
+       if (tcpc_dev->typec_role == TYPEC_ROLE_SRC)
+               return 0;
+
+       if (typec_is_cc_open())
+               return 0;
+
+       if (!tcpci_check_vbus_valid(tcpc_dev))
+               return 0;
+
+       TYPEC_DBG("PowerOffCharge\r\n");
+       TYPEC_DBG("init otg host no mache insert.\r\n");
+
+       gpio_hub_power_on();
+       gpio_hub_typec_power_off();
+       hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+       gpio_hub_switch_to_hub();
+       hisi_usb_otg_event(ID_FALL_EVENT);
+       oldstatus = TYPEC_HOST;
+
+       TYPEC_NEW_STATE(typec_unattached_snk);
+       typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
+
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_OPEN);
+       tcpci_set_cc(tcpc_dev, TYPEC_CC_RD);
+
+       return 1;
+}
+#endif /* CONFIG_TYPEC_CAP_POWER_OFF_CHARGE */
+
+int tcpc_typec_init(struct tcpc_device *tcpc_dev, u8 typec_role)
+{
+       int ret;
+
+       if (typec_role >= TYPEC_ROLE_NR) {
+               TYPEC_INFO("Wrong TypeC-Role: %d\r\n", typec_role);
+               return -2;
+       }
+
+       TYPEC_INFO("typec_init: %s\r\n", typec_role_name[typec_role]);
+
+       tcpc_dev->typec_role = typec_role;
+       tcpc_dev->typec_attach_new = TYPEC_UNATTACHED;
+       tcpc_dev->typec_attach_old = TYPEC_UNATTACHED;
+
+       tcpc_dev->typec_remote_cc[0] = TYPEC_CC_VOLT_OPEN;
+       tcpc_dev->typec_remote_cc[1] = TYPEC_CC_VOLT_OPEN;
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       tcpc_dev->typec_legacy_cable = false;
+       tcpc_dev->typec_legacy_cable_suspect = 0;
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+#ifdef CONFIG_TYPEC_CAP_POWER_OFF_CHARGE
+       ret = typec_init_power_off_charge(tcpc_dev);
+       if (ret != 0)
+               return ret;
+#endif /* CONFIG_TYPEC_CAP_POWER_OFF_CHARGE */
+
+       if (typec_role >= TYPEC_ROLE_DRP) {
+               tcpci_get_cc(tcpc_dev);
+               if (tcpc_dev->typec_remote_cc[0] == TYPEC_CC_VOLT_OPEN &&
+                   tcpc_dev->typec_remote_cc[1] == TYPEC_CC_VOLT_OPEN) {
+                       tcpci_set_cc(tcpc_dev, TYPEC_CC_OPEN);
+                       mdelay(50);
+               }
+       }
+
+#ifdef CONFIG_TYPEC_POWER_CTRL_INIT
+       tcpc_dev->typec_power_ctrl = true;
+#endif /* CONFIG_TYPEC_POWER_CTRL_INIT */
+
+       typec_unattached_entry(tcpc_dev);
+       return 0;
+}
+
+void  tcpc_typec_deinit(struct tcpc_device *tcpc_dev)
+{
+}
diff --git a/drivers/usb/pd/richtek/tcpm.c b/drivers/usb/pd/richtek/tcpm.c
new file mode 100644 (file)
index 0000000..01a17a3
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Power Delivery Managert Driver
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hisi/usb/pd/richtek/tcpm.h>
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+#include <linux/hisi/usb/pd/richtek/pd_dpm_core.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_typec.h>
+
+/* Inquire TCPC status */
+
+int tcpm_inquire_remote_cc(struct tcpc_device *tcpc_dev,
+                          u8 *cc1, u8 *cc2, bool from_ic)
+{
+       int rv = 0;
+
+       if (from_ic) {
+               rv = tcpci_get_cc(tcpc_dev);
+               if (rv < 0)
+                       return rv;
+       }
+
+       *cc1 = tcpc_dev->typec_remote_cc[0];
+       *cc2 = tcpc_dev->typec_remote_cc[1];
+       return 0;
+}
+
+int tcpm_inquire_vbus_level(
+       struct tcpc_device *tcpc_dev, bool from_ic)
+{
+       int rv = 0;
+       u16 power_status = 0;
+
+       if (from_ic) {
+               rv = tcpci_get_power_status(tcpc_dev, &power_status);
+               if (rv < 0)
+                       return rv;
+
+               tcpci_vbus_level_init(tcpc_dev, power_status);
+       }
+
+       return tcpc_dev->vbus_level;
+}
+
+bool tcpm_inquire_cc_polarity(
+       struct tcpc_device *tcpc_dev)
+{
+       return tcpc_dev->typec_polarity;
+}
+
+u8 tcpm_inquire_typec_attach_state(
+       struct tcpc_device *tcpc_dev)
+{
+       return tcpc_dev->typec_attach_new;
+}
+
+u8 tcpm_inquire_typec_role(
+       struct tcpc_device *tcpc_dev)
+{
+       return tcpc_dev->typec_role;
+}
+
+u8 tcpm_inquire_typec_local_rp(
+       struct tcpc_device *tcpc_dev)
+{
+       u8 level;
+
+       switch (tcpc_dev->typec_local_rp_level) {
+       case TYPEC_CC_RP_1_5:
+               level = 1;
+               break;
+
+       case TYPEC_CC_RP_3_0:
+               level = 2;
+               break;
+
+       default:
+       case TYPEC_CC_RP_DFT:
+               level = 0;
+               break;
+       }
+
+       return level;
+}
+
+int tcpm_typec_set_rp_level(
+       struct tcpc_device *tcpc_dev, u8 level)
+{
+       u8 res;
+
+       if (level == 2)
+               res = TYPEC_CC_RP_3_0;
+       else if (level == 1)
+               res = TYPEC_CC_RP_1_5;
+       else
+               res = TYPEC_CC_RP_DFT;
+
+       return tcpc_typec_set_rp_level(tcpc_dev, res);
+}
+
+int tcpm_typec_change_role(
+       struct tcpc_device *tcpc_dev, u8 typec_role)
+{
+       return tcpc_typec_change_role(tcpc_dev, typec_role);
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+bool tcpm_inquire_pd_connected(
+       struct tcpc_device *tcpc_dev)
+{
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       return pd_port->pd_connected;
+}
+
+bool tcpm_inquire_pd_prev_connected(
+       struct tcpc_device *tcpc_dev)
+{
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       return pd_port->pd_prev_connected;
+}
+
+u8 tcpm_inquire_pd_data_role(
+       struct tcpc_device *tcpc_dev)
+{
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       return pd_port->data_role;
+}
+
+u8 tcpm_inquire_pd_power_role(
+       struct tcpc_device *tcpc_dev)
+{
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       return pd_port->power_role;
+}
+
+u8 tcpm_inquire_pd_vconn_role(
+       struct tcpc_device *tcpc_dev)
+{
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       return pd_port->vconn_source;
+}
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+/* Request TCPC to send PD Request */
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+int tcpm_power_role_swap(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_PR_SWAP);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_power_role_swap);
+
+int tcpm_data_role_swap(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_DR_SWAP);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_data_role_swap);
+
+int tcpm_vconn_swap(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_VCONN_SWAP);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_vconn_swap);
+
+int tcpm_goto_min(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_GOTOMIN);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_goto_min);
+
+int tcpm_soft_reset(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_SOFTRESET);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_soft_reset);
+
+int tcpm_hard_reset(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_HARDRESET);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_hard_reset);
+
+int tcpm_get_source_cap(
+       struct tcpc_device *tcpc_dev, struct tcpm_power_cap *cap)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_GET_SOURCE_CAP);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       /* TODO: Finish it later */
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_get_source_cap);
+
+int tcpm_get_sink_cap(
+       struct tcpc_device *tcpc_dev, struct tcpm_power_cap *cap)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_GET_SINK_CAP);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       /* TODO: Finish it later */
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_get_sink_cap);
+
+int tcpm_bist_cm2(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_pd_request_event(pd_port,
+                                         PD_DPM_PD_REQUEST_BIST_CM2);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       /* TODO: Finish it later */
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_bist_cm2);
+
+int tcpm_request(struct tcpc_device *tcpc_dev, int mv, int ma)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       mutex_lock(&pd_port->pd_lock);
+       ret = pd_dpm_send_request(pd_port, mv, ma);
+       mutex_unlock(&pd_port->pd_lock);
+
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_request);
+
+int tcpm_error_recovery(struct tcpc_device *tcpc_dev)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       ret = pd_put_dpm_event(pd_port, PD_DPM_ERROR_RECOVERY);
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+
+int tcpm_discover_cable(struct tcpc_device *tcpc_dev, u32 *vdos)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       mutex_lock(&pd_port->pd_lock);
+       pd_port->dpm_flags |= DPM_FLAGS_CHECK_CABLE_ID;
+       ret = vdm_put_dpm_discover_cable_event(pd_port);
+       mutex_unlock(&pd_port->pd_lock);
+
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+
+int tcpm_vdm_request_id(struct tcpc_device *tcpc_dev,
+                       u8 *cnt, u8 *payload)
+{
+       bool ret;
+       pd_port_t *pd_port = &tcpc_dev->pd_port;
+
+       mutex_lock(&pd_port->pd_lock);
+       ret = vdm_put_dpm_vdm_request_event(
+               pd_port, PD_DPM_VDM_REQUEST_DISCOVER_ID);
+       mutex_unlock(&pd_port->pd_lock);
+
+       if (!ret)
+               return TCPM_ERROR_PUT_EVENT;
+
+       return TCPM_SUCCESS;
+}
+
+int tcpm_notify_vbus_stable(
+       struct tcpc_device *tcpc_dev)
+{
+#if CONFIG_USB_PD_VBUS_STABLE_TOUT
+       tcpc_disable_timer(tcpc_dev, PD_TIMER_VBUS_STABLE);
+#endif
+
+       pd_put_vbus_stable_event(tcpc_dev);
+       return TCPM_SUCCESS;
+}
+EXPORT_SYMBOL(tcpm_notify_vbus_stable);
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
diff --git a/include/linux/hisi/log/hisi_log.h b/include/linux/hisi/log/hisi_log.h
new file mode 100644 (file)
index 0000000..7a9b281
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef _LINUX_HISILOG_H
+#define _LINUX_HISILOG_H
+
+#include <linux/printk.h>
+#include <linux/types.h>
+
+enum {
+       HISILOG_ERR         = 1U << 0,
+       HISILOG_WARNING     = 1U << 1,
+       HISILOG_INFO        = 1U << 2,
+       HISILOG_DEBUG       = 1U << 3,
+       HISILOG_DEBUG1      = 1U << 4,
+       HISILOG_DEBUG2      = 1U << 5,
+       HISILOG_DEBUG3      = 1U << 6,
+       HISILOG_DEBUG4      = 1U << 7,
+};
+
+#define HISILOG_TAG_DEFOUTL_LEVEL (HISILOG_ERR \
+               | HISILOG_WARNING \
+               | HISILOG_INFO)
+
+struct hisi_log_tag {
+       const char *name;
+       u32 level;
+};
+
+#define HISILOG_REGIST()       \
+       HISILOG_REGIST_TAG_LEVEL(HISILOG_TAG, HISILOG_TAG_DEFOUTL_LEVEL)
+
+#define HISILOG_REGIST_LEVEL(level)    \
+       HISILOG_REGIST_TAG_LEVEL(HISILOG_TAG, level)
+
+#define HISILOG_REGIST_TAG_LEVEL(name, level)  \
+       _HISILOG_REGIST_TAG_LEVEL(name, level)
+
+#define _HISILOG_REGIST_TAG_LEVEL(name, level) \
+       static struct hisi_log_tag TAG_STRUCT_NAME(name)        \
+__used                                                         \
+__attribute__ ((unused, __section__("__hisilog_tag"))) \
+= { #name, level}
+
+#define hisilog_err(x...) \
+       _hisilog_err(HISILOG_TAG, ##x)
+
+#define _hisilog_err(TAG, x...) \
+       __hisilog_err(TAG, ##x)
+
+#define __hisilog_err(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_ERR) \
+               pr_err(hw_fmt_tag(TAG, E) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_warn(x...) \
+       _hisilog_warn(HISILOG_TAG, ##x)
+
+#define _hisilog_warn(TAG, x...) \
+       __hisilog_warn(TAG, ##x)
+
+#define __hisilog_warn(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_WARNING) \
+               pr_err(hw_fmt_tag(TAG, W) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_info(x...) \
+       _hisilog_info(HISILOG_TAG, ##x)
+
+#define _hisilog_info(TAG, x...) \
+       __hisilog_info(TAG, ##x)
+
+#define __hisilog_info(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_INFO) \
+               pr_info(hw_fmt_tag(TAG, I) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug(x...) \
+       _hisilog_debug(HISILOG_TAG, ##x)
+
+#define _hisilog_debug(TAG, x...) \
+       __hisilog_debug(TAG, ##x)
+
+#define __hisilog_debug(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG) \
+               pr_err(hw_fmt_tag(TAG, D) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug1(x...) \
+       _hisilog_debug1(HISILOG_TAG, ##x)
+
+#define _hisilog_debug1(TAG, x...) \
+       __hisilog_debug1(TAG, ##x)
+
+#define __hisilog_debug1(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG1) \
+               pr_err(hw_fmt_tag(TAG, D1) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug2(x...) \
+       _hisilog_debug2(HISILOG_TAG, ##x)
+
+#define _hisilog_debug2(TAG, x...) \
+       __hisilog_debug2(TAG, ##x)
+
+#define __hisilog_debug2(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG2) \
+               pr_err(hw_fmt_tag(TAG, D2) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug3(x...) \
+       _hisilog_debug3(HISILOG_TAG, ##x)
+
+#define _hisilog_debug3(TAG, x...) \
+       __hisilog_debug3(TAG, ##x)
+
+#define __hisilog_debug3(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG3) \
+               pr_err(hw_fmt_tag(TAG, D3) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug4(x...) \
+       _hisilog_debug4(HISILOG_TAG, ##x)
+
+#define _hisilog_debug4(TAG, x...) \
+       __hisilog_debug4(TAG, ##x)
+
+#define __hisilog_debug4(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG4) \
+               pr_err(hw_fmt_tag(TAG, D4) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define TAG_STRUCT_NAME(name) \
+       _hwtag_##name
+
+#define hw_fmt_tag(TAG, LEVEL) "[" #LEVEL "/" #TAG "] "
+
+#endif
diff --git a/include/linux/hisi/usb/hisi_pd_dev.h b/include/linux/hisi/usb/hisi_pd_dev.h
new file mode 100644 (file)
index 0000000..68b6fc9
--- /dev/null
@@ -0,0 +1,193 @@
+/**********************************************************
+ * Filename:   hisi_typec_dev.h
+ *
+ * Discription: Hisilicon type-c device public head file for
+ *              type-c core driver and chip drivers
+ *
+ * Copyright: (C) 2014 hisilicon.
+ *
+ * Author: Hisilicon
+ *
+ **********************************************************/
+
+#ifndef __HISI_PD_DEV_H__
+#define __HISI_PD_DEV_H__
+
+#include <linux/device.h>
+#include <linux/hisi/usb/hisi_usb.h>
+#include <linux/hisi/log/hisi_log.h>
+
+#define CONFIG_DPM_USB_PD_CUSTOM_DBGACC
+#define CONFIG_DPM_TYPEC_CAP_DBGACC_SNK
+#define CONFIG_DPM_TYPEC_CAP_CUSTOM_SRC
+
+/* type-c inserted plug orientation */
+enum pd_cc_orient {
+       PD_CC_ORIENT_DEFAULT = 0,
+       PD_CC_ORIENT_CC1,
+       PD_CC_ORIENT_CC2,
+       PD_CC_NOT_READY,
+};
+
+enum pd_connect_result {
+       PD_CONNECT_NONE = 0,
+       PD_CONNECT_TYPEC_ONLY,
+       PD_CONNECT_TYPEC_ONLY_SNK_DFT,
+       PD_CONNECT_TYPEC_ONLY_SNK,
+       PD_CONNECT_TYPEC_ONLY_SRC,
+       PD_CONNECT_PE_READY,
+       PD_CONNECT_PE_READY_SNK,
+       PD_CONNECT_PE_READY_SRC,
+
+#ifdef CONFIG_DPM_USB_PD_CUSTOM_DBGACC
+       PD_CONNECT_PE_READY_DBGACC_UFP,
+       PD_CONNECT_PE_READY_DBGACC_DFP,
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+};
+
+enum pd_device_port_power_mode {
+       PD_DEV_PORT_POWERMODE_SOURCE = 0,
+       PD_DEV_PORT_POWERMODE_SINK,
+       PD_DEV_PORT_POWERMODE_NOT_READY,
+};
+
+enum pd_device_port_data_mode {
+       PD_DEV_PORT_DATAMODE_HOST = 0,
+       PD_DEV_PORT_DATAMODE_DEVICE,
+       PD_DEV_PORT_DATAMODE_NOT_READY,
+};
+
+enum pd_device_port_mode {
+       PD_DEV_PORT_MODE_DFP = 0,
+       PD_DEV_PORT_MODE_UFP,
+       PD_DEV_PORT_MODE_NOT_READY,
+};
+
+enum {
+       PD_DPM_PE_EVT_DIS_VBUS_CTRL,
+       PD_DPM_PE_EVT_SOURCE_VCONN,
+       PD_DPM_PE_EVT_SOURCE_VBUS,
+       PD_DPM_PE_EVT_SINK_VBUS,
+       PD_DPM_PE_EVT_PR_SWAP,
+       PD_DPM_PE_EVT_DR_SWAP,
+       PD_DPM_PE_EVT_VCONN_SWAP,
+       PD_DPM_PE_EVT_TYPEC_STATE,
+       PD_DPM_PE_EVT_PD_STATE,
+       PD_DPM_PE_EVT_BC12,
+};
+
+enum pd_typec_attach_type {
+       PD_DPM_TYPEC_UNATTACHED = 0,
+       PD_DPM_TYPEC_ATTACHED_SNK,
+       PD_DPM_TYPEC_ATTACHED_SRC,
+       PD_DPM_TYPEC_ATTACHED_AUDIO,
+       PD_DPM_TYPEC_ATTACHED_DEBUG,
+
+#ifdef CONFIG_DPM_TYPEC_CAP_DBGACC_SNK
+       PD_DPM_TYPEC_ATTACHED_DBGACC_SNK,               /* Rp, Rp */
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+#ifdef CONFIG_DPM_TYPEC_CAP_CUSTOM_SRC
+       PD_DPM_TYPEC_ATTACHED_CUSTOM_SRC,               /* Same Rp */
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+};
+
+enum pd_dpm_charger_event_type {
+       PD_EVENT_CHARGER_TYPE_USB = 0,  /*SDP*/
+       PD_EVENT_CHARGER_TYPE_BC_USB,   /*CDP*/
+       PD_EVENT_CHARGER_TYPE_NON_STANDARD,     /*UNKNOWN*/
+       PD_EVENT_CHARGER_TYPE_STANDARD, /*DCP*/
+       PD_EVENT_CHARGER_TYPE_FCP,      /*FCP*/
+};
+
+enum {
+       PD_DPM_USB_TYPEC_NONE = 0,
+       PD_DPM_USB_TYPEC_DETACHED,
+       PD_DPM_USB_TYPEC_DEVICE_ATTACHED,
+       PD_DPM_USB_TYPEC_HOST_ATTACHED,
+};
+
+enum pd_dpm_uevent_type {
+       PD_DPM_UEVENT_START = 0,
+       PD_DPM_UEVENT_COMPLETE,
+};
+
+enum pd_dpm_wake_lock_type {
+       PD_WAKE_LOCK = 100,
+       PD_WAKE_UNLOCK,
+};
+
+struct pd_dpm_typec_state {
+       bool polarity;
+       int cc1_status;
+       int cc2_status;
+       int old_state;
+       int new_state;
+};
+
+struct pd_dpm_pd_state {
+       u8 connected;
+};
+
+struct pd_dpm_swap_state {
+       u8 new_role;
+};
+
+enum pd_dpm_vbus_type {
+       PD_DPM_VBUS_TYPE_TYPEC = 20,
+       PD_DPM_VBUS_TYPE_PD,
+};
+
+struct pd_dpm_vbus_state {
+       int mv;
+       int ma;
+       u8 vbus_type;
+};
+
+struct pd_dpm_info {
+       struct i2c_client *client;
+       struct device *dev;
+       struct mutex pd_lock;
+       struct mutex sink_vbus_lock;
+
+       struct dual_role_phy_instance *dual_role;
+       struct dual_role_phy_desc *desc;
+
+       enum hisi_charger_type charger_type;
+       struct notifier_block usb_nb;
+       struct atomic_notifier_head pd_evt_nh;
+       struct atomic_notifier_head pd_wake_unlock_evt_nh;
+
+       enum pd_dpm_uevent_type uevent_type;
+       struct work_struct pd_work;
+
+       const char *tcpc_name;
+
+       /* usb state update */
+       struct mutex usb_lock;
+       int pending_usb_event;
+       int last_usb_event;
+       struct workqueue_struct *usb_wq;
+       struct delayed_work usb_state_update_work;
+
+       bool bc12_finish_flag;
+       bool pd_finish_flag;
+       bool pd_source_vbus;
+
+       struct pd_dpm_vbus_state bc12_sink_vbus_state;
+};
+
+/* for chip layer to get class created by core layer */
+struct class *hisi_pd_get_class(void);
+
+struct tcpc_device *tcpc_dev_get_by_name(const char *name);
+
+int register_pd_dpm_notifier(struct notifier_block *nb);
+int unregister_pd_dpm_notifier(struct notifier_block *nb);
+int register_pd_wake_unlock_notifier(struct notifier_block *nb);
+int unregister_pd_wake_unlock_notifier(struct notifier_block *nb);
+int pd_dpm_handle_pe_event(unsigned long event, void *data);
+bool pd_dpm_get_pd_finish_flag(void);
+bool pd_dpm_get_pd_source_vbus(void);
+void pd_dpm_get_typec_state(int *typec_detach);
+#endif
diff --git a/include/linux/hisi/usb/pd/richtek/pd_core.h b/include/linux/hisi/usb/pd/richtek/pd_core.h
new file mode 100644 (file)
index 0000000..8675f83
--- /dev/null
@@ -0,0 +1,1218 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PD_CORE_H_
+#define PD_CORE_H_
+#include <linux/module.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_timer.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_event.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_config.h>
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+#define CONFIG_PD_DISCOVER_CABLE_ID
+#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+#undef CONFIG_PD_DISCOVER_CABLE_ID
+#define CONFIG_PD_DISCOVER_CABLE_ID
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+
+#define PD_SOP_NR      3
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/* Default retry count for transmitting */
+#define PD_RETRY_COUNT 3
+
+#if PD_RETRY_COUNT > 3
+#error "PD_RETRY_COUNT Max = 3"
+#endif
+
+/* --- PD data message helpers --- */
+#define PDO_MAX_OBJECTS   7
+#define PDO_MODES (PDO_MAX_OBJECTS - 1)
+
+/* PDO : Power Data Object */
+/*
+ * 1. The vSafe5V Fixed Supply Object shall always be the first object.
+ * 2. The remaining Fixed Supply Objects,
+ *    if present, shall be sent in voltage order; lowest to highest.
+ * 3. The Battery Supply Objects,
+ *    if present shall be sent in Minimum Voltage order; lowest to highest.
+ * 4. The Variable Supply (non battery) Objects,
+ *    if present, shall be sent in Minimum Voltage order; lowest to highest.
+ */
+#define PDO_TYPE_FIXED    (0 << 30)
+#define PDO_TYPE_BATTERY  BIT(30)
+#define PDO_TYPE_VARIABLE (2 << 30)
+#define PDO_TYPE_MASK     (3 << 30)
+
+#define PDO_FIXED_DUAL_ROLE BIT(29) /* Dual role device */
+#define PDO_FIXED_SUSPEND   BIT(28) /* USB Suspend supported (SRC)*/
+#define PDO_FIXED_HIGH_CAP     BIT(28) /* Higher Capability (SNK )*/
+#define PDO_FIXED_EXTERNAL  BIT(27) /* Externally powered */
+#define PDO_FIXED_COMM_CAP  BIT(26) /* USB Communications Capable */
+#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap command supported */
+
+#define PDO_FIXED_PEAK_CURR(i) \
+       (((i) & 0x03) << 20) /* [21..20] Peak current */
+#define PDO_FIXED_VOLT(mv)  \
+       ((((mv) / 50) & 0x3fff) << 10) /* Voltage in 50mV units */
+#define PDO_FIXED_CURR(ma)  \
+       ((((ma) / 10) & 0x3fff) << 0)  /* Max current in 10mA units */
+
+#define PDO_TYPE(raw)  ((raw) & PDO_TYPE_MASK)
+
+#define PDO_FIXED_EXTRACT_VOLT_RAW(raw)        (((raw) >> 10) & 0x3ff)
+#define PDO_FIXED_EXTRACT_CURR_RAW(raw)        (((raw) >> 0) & 0x3ff)
+#define PDO_FIXED_EXTRACT_VOLT(raw)    (PDO_FIXED_EXTRACT_VOLT_RAW(raw) * 50)
+#define PDO_FIXED_EXTRACT_CURR(raw)    (PDO_FIXED_EXTRACT_CURR_RAW(raw) * 10)
+#define PDO_FIXED_RESET_CURR(raw, ma)  \
+       (((raw) & ~0x3ff) | PDO_FIXED_CURR(ma))
+
+#define PDO_FIXED(mv, ma, flags) (PDO_FIXED_VOLT(mv) |\
+                                 PDO_FIXED_CURR(ma) | (flags))
+
+#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20)
+#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10)
+#define PDO_VAR_OP_CURR(ma)  ((((ma) / 10) & 0x3FF) << 0)
+
+#define PDO_VAR_EXTRACT_MAX_VOLT_RAW(raw)      (((raw) >> 20) & 0x3ff)
+#define PDO_VAR_EXTRACT_MIN_VOLT_RAW(raw)      (((raw) >> 10) & 0x3ff)
+#define PDO_VAR_EXTRACT_CURR_RAW(raw)          (((raw) >> 0) & 0x3ff)
+
+#define PDO_VAR_EXTRACT_MAX_VOLT(raw)  (PDO_VAR_EXTRACT_MAX_VOLT_RAW(raw) * 50)
+#define PDO_VAR_EXTRACT_MIN_VOLT(raw)  (PDO_VAR_EXTRACT_MIN_VOLT_RAW(raw) * 50)
+#define PDO_VAR_EXTRACT_CURR(raw)      (PDO_VAR_EXTRACT_CURR_RAW(raw) * 10)
+
+#define PDO_VAR_RESET_CURR(raw, ma)    \
+       (((raw) & ~0x3ff) | PDO_VAR_OP_CURR(ma))
+
+#define PDO_VAR(min_mv, max_mv, op_ma) \
+                               (PDO_VAR_MIN_VOLT(min_mv) | \
+                                PDO_VAR_MAX_VOLT(max_mv) | \
+                                PDO_VAR_OP_CURR(op_ma)   | \
+                                PDO_TYPE_VARIABLE)
+
+#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20)
+#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10)
+#define PDO_BATT_OP_POWER(mw) ((((mw) / 250) & 0x3FF) << 0)
+
+#define PDO_BATT_EXTRACT_MAX_VOLT_RAW(raw)     (((raw) >> 20) & 0x3ff)
+#define PDO_BATT_EXTRACT_MIN_VOLT_RAW(raw)     (((raw) >> 10) & 0x3ff)
+#define PDO_BATT_EXTRACT_OP_POWER_RAW(raw)     (((raw) >> 0) & 0x3ff)
+
+#define PDO_BATT_EXTRACT_MAX_VOLT(raw) \
+       (PDO_BATT_EXTRACT_MAX_VOLT_RAW(raw) * 50)
+#define PDO_BATT_EXTRACT_MIN_VOLT(raw) \
+       (PDO_BATT_EXTRACT_MIN_VOLT_RAW(raw) * 50)
+#define PDO_BATT_EXTRACT_OP_POWER(raw) \
+       (PDO_BATT_EXTRACT_OP_POWER_RAW(raw) * 250)
+
+#define PDO_BATT(min_mv, max_mv, op_mw) \
+                               (PDO_BATT_MIN_VOLT(min_mv) | \
+                                PDO_BATT_MAX_VOLT(max_mv) | \
+                                PDO_BATT_OP_POWER(op_mw) | \
+                                PDO_TYPE_BATTERY)
+
+/* RDO : Request Data Object */
+#define RDO_OBJ_POS(n)             (((n) & 0x7) << 28)
+#define RDO_POS(rdo)               (((rdo) >> 28) & 0x7)
+#define RDO_GIVE_BACK              BIT(27)
+#define RDO_CAP_MISMATCH           BIT(26)
+#define RDO_COMM_CAP               BIT(25)
+#define RDO_NO_SUSPEND             BIT(24)
+#define RDO_FIXED_VAR_OP_CURR(ma)  ((((ma) / 10) & 0x3FF) << 10)
+#define RDO_FIXED_VAR_MAX_CURR(ma) ((((ma) / 10) & 0x3FF) << 0)
+
+#define RDO_FIXED_VAR_EXTRACT_OP_CURR(raw)     ((((raw) >> 10 & 0x3ff)) * 10)
+#define RDO_FIXED_VAR_EXTRACT_MAX_CURR(raw)    ((((raw) >> 0 & 0x3ff)) * 10)
+
+#define RDO_BATT_OP_POWER(mw)      ((((mw) / 250) & 0x3FF) << 10)
+#define RDO_BATT_MAX_POWER(mw)     ((((mw) / 250) & 0x3FF) << 0)
+
+#define RDO_BATT_EXTRACT_OP_POWER(raw) ((((raw) >> 10 & 0x3ff)) * 250)
+#define RDO_BATT_EXTRACT_MAX_POWER(raw)        ((((raw) >> 0 & 0x3ff)) * 250)
+
+#define RDO_FIXED(n, op_ma, max_ma, flags) \
+                               (RDO_OBJ_POS(n) | (flags) | \
+                               RDO_FIXED_VAR_OP_CURR(op_ma) | \
+                               RDO_FIXED_VAR_MAX_CURR(max_ma))
+
+#define RDO_BATT(n, op_mw, max_mw, flags) \
+                               (RDO_OBJ_POS(n) | (flags) | \
+                               RDO_BATT_OP_POWER(op_mw) | \
+                               RDO_BATT_MAX_POWER(max_mw))
+
+/* BDO : BIST Data Object */
+#define BDO_MODE_RECV       (0 << 28)
+#define BDO_MODE_TRANSMIT   BIT(28)
+#define BDO_MODE_COUNTERS   (2 << 28)
+#define BDO_MODE_CARRIER0   (3 << 28)
+#define BDO_MODE_CARRIER1   (4 << 28)
+#define BDO_MODE_CARRIER2   (5 << 28)
+#define BDO_MODE_CARRIER3   (6 << 28)
+#define BDO_MODE_EYE        (7 << 28)
+#define BDO_MODE_TEST_DATA     (8 << 28)
+
+#define BDO_MODE(obj)          ((obj) & (0xf << 28))
+#define BDO(mode, cnt)      ((mode) | ((cnt) & 0xFFFF))
+
+#define SVID_DISCOVERY_MAX 16
+
+/* Protocol revision */
+#define PD_REV10 0
+#define PD_REV20 1
+
+/* build message header */
+
+#define PD_HEADER_SOP(msg_type, prole, drole, id, cnt) \
+               ((msg_type) | (PD_REV20 << 6) | \
+                ((drole) << 5) | ((prole) << 8) | \
+                ((id) << 9) | ((cnt) << 12))
+
+#define PD_HEADER_SOP_PRIME(msg_type, cable_plug, id, cnt) \
+               ((msg_type) | (PD_REV20 << 6) | \
+                ((cable_plug) << 8) | \
+                ((id) << 9) | ((cnt) << 12))
+
+#define PD_HEADER_CNT(header)  (((header) >> 12) & 7)
+#define PD_HEADER_TYPE(header) ((header) & 0xF)
+#define PD_HEADER_ID(header)   (((header) >> 9) & 7)
+#define PD_HEADER_PR(header)   (((header) >> 8) & 1)
+#define PD_HEADER_DR(header)   (((header) >> 5) & 1)
+
+/*
+ * VDO : Vendor Defined Message Object
+ * VDM object is minimum of VDM header + 6 additional data objects.
+ */
+
+/*
+ * VDM header
+ * ----------
+ * <31:16>  :: SVID
+ * <15>     :: VDM type ( 1b == structured, 0b == unstructured )
+ * <14:13>  :: Structured VDM version (can only be 00 == 1.0 currently)
+ * <12:11>  :: reserved
+ * <10:8>   :: object position (1-7 valid ... used for enter/exit mode only)
+ * <7:6>    :: command type (SVDM only?)
+ * <5>      :: reserved (SVDM), command type (UVDM)
+ * <4:0>    :: command
+ */
+#define VDO_MAX_SIZE           (7)
+#define VDO_MAX_DATA_SIZE      (VDO_MAX_SIZE - 1)
+#define VDO_MAX_SVID_SIZE      (VDO_MAX_DATA_SIZE * 2)
+
+#define VDO(vid, type, custom)                         \
+       (((vid) << 16) |                                \
+        ((type) << 15) |                               \
+        ((custom) & 0x7FFF))
+
+#define VDO_S(svid, cmd_type, cmd, obj)        \
+       VDO(svid, 1, VDO_CMDT(cmd_type) | VDO_OPOS(obj) | (cmd))
+
+#define VDO_SVDM_TYPE     BIT(15)
+#define VDO_SVDM_VERS(x)  ((x) << 13)
+#define VDO_OPOS(x)       ((x) << 8)
+#define VDO_CMDT(x)       ((x) << 6)
+#define VDO_OPOS_MASK     VDO_OPOS(0x7)
+#define VDO_CMDT_MASK     VDO_CMDT(0x3)
+
+#define CMDT_INIT     0
+#define CMDT_RSP_ACK  1
+#define CMDT_RSP_NAK  2
+#define CMDT_RSP_BUSY 3
+
+/* reserved for SVDM ... for Google UVDM */
+#define VDO_SRC_INITIATOR (0 << 5)
+#define VDO_SRC_RESPONDER BIT(5)
+
+#define CMD_DISCOVER_IDENT  1
+#define CMD_DISCOVER_SVID   2
+#define CMD_DISCOVER_MODES  3
+#define CMD_ENTER_MODE      4
+#define CMD_EXIT_MODE       5
+#define CMD_ATTENTION       6
+#define CMD_DP_STATUS      16
+#define CMD_DP_CONFIG      17
+
+#define VDO_CMD_VENDOR(x)    (((10 + (x)) & 0x1f))
+
+/* ChromeOS specific commands */
+#define VDO_CMD_VERSION      VDO_CMD_VENDOR(0)
+#define VDO_CMD_SEND_INFO    VDO_CMD_VENDOR(1)
+#define VDO_CMD_READ_INFO    VDO_CMD_VENDOR(2)
+#define VDO_CMD_REBOOT       VDO_CMD_VENDOR(5)
+#define VDO_CMD_FLASH_ERASE  VDO_CMD_VENDOR(6)
+#define VDO_CMD_FLASH_WRITE  VDO_CMD_VENDOR(7)
+#define VDO_CMD_ERASE_SIG    VDO_CMD_VENDOR(8)
+#define VDO_CMD_PING_ENABLE  VDO_CMD_VENDOR(10)
+#define VDO_CMD_CURRENT      VDO_CMD_VENDOR(11)
+#define VDO_CMD_FLIP         VDO_CMD_VENDOR(12)
+#define VDO_CMD_GET_LOG      VDO_CMD_VENDOR(13)
+#define VDO_CMD_CCD_EN       VDO_CMD_VENDOR(14)
+
+#define PD_VDO_VID(vdo)  ((vdo) >> 16)
+#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1)
+#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7)
+#define PD_VDO_CMD(vdo)  ((vdo) & 0x1f)
+#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3)
+
+/*
+ * SVDM Identity request -> response
+ *
+ * Request is simply properly formatted SVDM header
+ *
+ * Response is 4 data objects:
+ * [0] :: SVDM header
+ * [1] :: Identitiy header
+ * [2] :: Cert Stat VDO
+ * [3] :: (Product | Cable) VDO
+ * [4] :: AMA VDO
+ *
+ */
+#define VDO_INDEX_HDR     0
+#define VDO_INDEX_IDH     1
+#define VDO_INDEX_CSTAT   2
+#define VDO_INDEX_CABLE   3
+#define VDO_INDEX_PRODUCT 3
+#define VDO_INDEX_AMA     4
+#define VDO_I(name) VDO_INDEX_##name
+
+/*
+ * SVDM Identity Header
+ * --------------------
+ * <31>     :: data capable as a USB host
+ * <30>     :: data capable as a USB device
+ * <29:27>  :: product type
+ * <26>     :: modal operation supported (1b == yes)
+ * <25:16>  :: SBZ
+ * <15:0>   :: USB-IF assigned VID for this cable vendor
+ */
+#define IDH_PTYPE_UNDEF  0
+#define IDH_PTYPE_HUB    1
+#define IDH_PTYPE_PERIPH 2
+#define IDH_PTYPE_PCABLE 3
+#define IDH_PTYPE_ACABLE 4
+#define IDH_PTYPE_AMA    5
+
+#define VDO_IDH(usbh, usbd, ptype, is_modal, vid)              \
+       ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27    \
+        | (is_modal) << 26 | ((vid) & 0xffff))
+
+#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7)
+#define PD_IDH_VID(vdo)   ((vdo) & 0xffff)
+
+#define PD_IDH_MODAL_SUPPORT   BIT(26)
+
+/*
+ * Cert Stat VDO
+ * -------------
+ * <31:20> : SBZ
+ * <19:0>  : USB-IF assigned TID for this cable
+ */
+#define VDO_CSTAT(tid)    ((tid) & 0xfffff)
+#define PD_CSTAT_TID(vdo) ((vdo) & 0xfffff)
+
+/*
+ * Product VDO
+ * -----------
+ * <31:16> : USB Product ID
+ * <15:0>  : USB bcdDevice
+ */
+#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff))
+#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff)
+
+/*
+ * Cable VDO
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:20> :: SBZ
+ * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
+ * <17>    :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
+ * <16:13> :: cable latency (0001 == <10ns(~1m length))
+ * <12:11> :: cable termination type (11b == both ends active VCONN req)
+ * <10>    :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <9>     :: SSTX2 Directionality support
+ * <8>     :: SSRX1 Directionality support
+ * <7>     :: SSRX2 Directionality support
+ * <6:5>   :: Vbus current handling capability
+ * <4>     :: Vbus through cable (0b == no, 1b == yes)
+ * <3>     :: SOP" controller present? (0b == no, 1b == yes)
+ * <2:0>   :: USB SS Signaling support
+ */
+#define CABLE_ATYPE 0
+#define CABLE_BTYPE 1
+#define CABLE_CTYPE 2
+#define CABLE_PLUG       0
+#define CABLE_RECEPTACLE 1
+#define CABLE_CURR_1A5   0
+#define CABLE_CURR_3A    1
+#define CABLE_CURR_5A    2
+#define CABLE_USBSS_U2_ONLY  0
+#define CABLE_USBSS_U31_GEN1 1
+#define CABLE_USBSS_U31_GEN2 2
+#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d,\
+                       tx2d, rx1d, rx2d, cur, vps, sopp, usbss) \
+       (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18  \
+        | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11     \
+        | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7       \
+        | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3                \
+        | ((usbss) & 0x7))
+
+#define PD_VDO_CABLE_CURR(x)   (((x) >> 5) & 0x03)
+
+/*
+ * AMA VDO
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:12> :: SBZ
+ * <11>    :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <10>    :: SSTX2 Directionality support
+ * <9>     :: SSRX1 Directionality support
+ * <8>     :: SSRX2 Directionality support
+ * <7:5>   :: Vconn power
+ * <4>     :: Vconn power required
+ * <3>     :: Vbus power required
+ * <2:0>   :: USB SS Signaling support
+ */
+#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
+       (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24                        \
+        | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8      \
+        | ((vcpwr) & 0x3) << 5 | (vcr) << 4 | (vbr) << 3               \
+        | ((usbss) & 0x7))
+
+#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
+#define PD_VDO_AMA_VBUS_REQ(vdo)  (((vdo) >> 3) & 1)
+
+#define AMA_VCONN_PWR_1W   0
+#define AMA_VCONN_PWR_1W5  1
+#define AMA_VCONN_PWR_2W   2
+#define AMA_VCONN_PWR_3W   3
+#define AMA_VCONN_PWR_4W   4
+#define AMA_VCONN_PWR_5W   5
+#define AMA_VCONN_PWR_6W   6
+#define AMA_USBSS_U2_ONLY  0
+#define AMA_USBSS_U31_GEN1 1
+#define AMA_USBSS_U31_GEN2 2
+#define AMA_USBSS_BBONLY   3
+
+/*
+ * SVDM Discover SVIDs request -> response
+ *
+ * Request is properly formatted VDM Header with discover SVIDs command.
+ * Response is a set of SVIDs of all all supported SVIDs with all zero's to
+ * mark the end of SVIDs.  If more than 12 SVIDs are supported command SHOULD be
+ * repeated.
+ */
+#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff))
+#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16)
+#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff)
+
+/*
+ * Google modes capabilities
+ * <31:8> : reserved
+ * <7:0>  : mode
+ */
+#define VDO_MODE_GOOGLE(mode) ((mode) & 0xff)
+
+#define MODE_GOOGLE_FU 1 /* Firmware Update mode */
+
+/*
+ * Mode Capabilities
+ *
+ * Number of VDOs supplied is SID dependent (but <= 6 VDOS?)
+ */
+#define VDO_MODE_CNT_DISPLAYPORT 1
+
+/*
+ * DisplayPort modes capabilities
+ * -------------------------------
+ * <31:24> : SBZ
+ * <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 VDO_MODE_DP(snkp, srcp, usb, gdr, sign, sdir)                  \
+       (((snkp) & 0xff) << 16 | ((srcp) & 0xff) << 8                   \
+        | ((usb) & 1) << 7 | ((gdr) & 1) << 6 | ((sign) & 0xF) << 2    \
+        | ((sdir) & 0x3))
+#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f)   \
+                          : (((x) >> 8) & 0x3f))
+
+#define MODE_DP_PIN_A 0x01
+#define MODE_DP_PIN_B 0x02
+#define MODE_DP_PIN_C 0x04
+#define MODE_DP_PIN_D 0x08
+#define MODE_DP_PIN_E 0x10
+#define MODE_DP_PIN_F 0x20
+
+/* Pin configs B/D/F support multi-function */
+#define MODE_DP_PIN_MF_MASK 0x2a
+/* Pin configs A/B support BR2 signaling levels */
+#define MODE_DP_PIN_BR2_MASK 0x3
+/* Pin configs C/D/E/F support DP signaling levels */
+#define MODE_DP_PIN_DP_MASK 0x3c
+
+#define MODE_DP_V13  0x1
+#define MODE_DP_GEN2 0x2
+
+#define MODE_DP_SNK  0x1
+#define MODE_DP_SRC  0x2
+#define MODE_DP_BOTH 0x3
+
+#define MODE_DP_PORT_CAP(raw)          ((raw) & 0x03)
+#define MODE_DP_SIGNAL_SUPPORT(raw)    (((raw) >> 2) & 0x0f)
+#define MODE_DP_RECEPT(mode)   (((mode) >> 6) & 0x01)
+
+#define MODE_DP_PIN_DFP(mode)  (((mode) >> 8) & 0xff)
+#define MODE_DP_PIN_UFP(mode)  (((mode) >> 16) & 0xff)
+
+#define PD_DP_DFP_D_PIN_CAPS(x)        (MODE_DP_RECEPT(x) ? \
+               MODE_DP_PIN_DFP(x) : MODE_DP_PIN_UFP(x))
+
+#define PD_DP_UFP_D_PIN_CAPS(x)        (MODE_DP_RECEPT(x) ? \
+               MODE_DP_PIN_UFP(x) : MODE_DP_PIN_DFP(x))
+
+/*
+ * DisplayPort Status VDO
+ * ----------------------
+ * <31:9> : SBZ
+ * <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 VDO_DP_STATUS(irq, lvl, amode, usbc, mf, en, lp, conn)         \
+       (((irq) & 1) << 8 | ((lvl) & 1) << 7 | ((amode) & 1) << 6       \
+        | ((usbc) & 1) << 5 | ((mf) & 1) << 4 | ((en) & 1) << 3        \
+        | ((lp) & 1) << 2 | (((conn) & 0x3) << 0))
+
+#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 1)
+#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 1)
+#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 1)
+
+#define PD_VDO_DPSTS_CONNECT(x)        (((x) >> 0) & 0x03)
+
+#define DPSTS_DISCONNECT               0
+
+#define DPSTS_DFP_D_CONNECTED  BIT(0)
+#define DPSTS_UFP_D_CONNECTED  BIT(1)
+#define DPSTS_BOTH_CONNECTED   (DPSTS_DFP_D_CONNECTED | DPSTS_UFP_D_CONNECTED)
+
+/* UFP_U only */
+#define DPSTS_DP_ENABLED               BIT(3)
+#define DPSTS_DP_MF_PREF               BIT(4)
+#define DPSTS_DP_USB_CONFIG            BIT(5)
+#define DPSTS_DP_EXIT_ALT_MODE BIT(6)
+
+/* UFP_D only */
+#define DPSTS_DP_HPD_STATUS            BIT(7)
+#define DPSTS_DP_HPD_IRQ               BIT(8)
+
+/* Per DisplayPort Spec v1.3 Section 3.3 */
+#define HPD_USTREAM_DEBOUNCE_LVL (2 * MSEC)
+#define HPD_USTREAM_DEBOUNCE_IRQ (250)
+#define HPD_DSTREAM_DEBOUNCE_IRQ (750)  /* between 500-1000us */
+
+/*
+ * DisplayPort Configure VDO
+ * -------------------------
+ * <31:24> : SBZ
+ * <23:16> : SBZ
+ * <15:8>  : Pin assignment requested.  Choose one from mode caps.
+ * <7:6>   : SBZ
+ * <5:2>   : signalling : 1h == DP v1.3, 2h == Gen 2
+ *           Oh is only for USB, remaining values are reserved
+ * <1:0>   : cfg : 00 == USB, 01 == DFP_D, 10 == UFP_D, 11 == reserved
+ */
+
+#define DP_CONFIG_USB                          0
+#define DP_CONFIG_DFP_D                                1
+#define DP_CONFIG_UFP_D                                2
+
+#define VDO_DP_CFG(pin, sig, cfg) \
+       (((pin) & 0xff) << 8 | ((sig) & 0xf) << 2 | ((cfg) & 0x3))
+
+#define VDO_DP_DFP_CFG(pin, sig) VDO_DP_CFG(pin, sig, DP_CONFIG_DFP_D)
+#define VDO_DP_UFP_CFG(pin, sig) VDO_DP_CFG(pin, sig, DP_CONFIG_UFP_D)
+
+#define PD_DP_CFG_USB(x)       (((x) & 0x3) == DP_CONFIG_USB)
+#define PD_DP_CFG_DFP_D(x) (((x) & 0x3) == DP_CONFIG_DFP_D)
+#define PD_DP_CFG_UFP_D(x) (((x) & 0x3) == DP_CONFIG_UFP_D)
+#define PD_DP_CFG_DPON(x) (PD_DP_CFG_DFP_D(x) | PD_DP_CFG_UFP_D(x))
+
+#define DP_SIG_DPV13   (0x01)
+#define DP_SIG_GEN2    (0x02)
+
+#define DP_PIN_ASSIGN_SUPPORT_A                BIT(0)
+#define DP_PIN_ASSIGN_SUPPORT_B                BIT(1)
+#define DP_PIN_ASSIGN_SUPPORT_C                BIT(2)
+#define DP_PIN_ASSIGN_SUPPORT_D                BIT(3)
+#define DP_PIN_ASSIGN_SUPPORT_E                BIT(4)
+#define DP_PIN_ASSIGN_SUPPORT_F                BIT(5)
+
+/*
+ * Get the pin assignment mask
+ * for backward compatibility, if it is null,
+ * get the former sink pin assignment we used to be in <23:16>.
+ */
+
+#define PD_DP_CFG_PIN(x) (((x) >> 8) & 0xff)
+
+/*
+ * ChromeOS specific PD device Hardware IDs. Used to identify unique
+ * products and used in VDO_INFO. Note this field is 10 bits.
+ */
+#define USB_PD_HW_DEV_ID_RESERVED    0
+#define USB_PD_HW_DEV_ID_ZINGER      1
+#define USB_PD_HW_DEV_ID_MINIMUFFIN  2
+#define USB_PD_HW_DEV_ID_DINGDONG    3
+#define USB_PD_HW_DEV_ID_HOHO        4
+#define USB_PD_HW_DEV_ID_HONEYBUNS   5
+
+/*
+ * ChromeOS specific VDO_CMD_READ_INFO responds with device info including:
+ * RW Hash: First 20 bytes of SHA-256 of RW (20 bytes)
+ * HW Device ID: unique descriptor for each ChromeOS model (2 bytes)
+ *               top 6 bits are minor revision, bottom 10 bits are major
+ * SW Debug Version: Software version useful for debugging (15 bits)
+ * IS RW: True if currently in RW, False otherwise (1 bit)
+ */
+#define VDO_INFO(id, id_minor, ver, is_rw) ((id_minor) << 26 \
+                                 | ((id) & 0x3ff) << 16 \
+                                 | ((ver) & 0x7fff) << 1 \
+                                 | ((is_rw) & 1))
+#define VDO_INFO_HW_DEV_ID(x)    ((x) >> 16)
+#define VDO_INFO_SW_DBG_VER(x)   (((x) >> 1) & 0x7fff)
+#define VDO_INFO_IS_RW(x)        ((x) & 1)
+
+#define HW_DEV_ID_MAJ(x) ((x) & 0x3ff)
+#define HW_DEV_ID_MIN(x) ((x) >> 10)
+
+/* USB-IF SIDs */
+#define USB_SID_PD             0xff00  /* power delivery */
+#define USB_SID_DISPLAYPORT    0xff01  /* display port */
+#define USB_SID_RICHTEK        0x29cf  /* demo uvdm */
+#define USB_SID_DIRECTCHARGE   0x29cf  /* direct charge */
+
+/* DPM Flags */
+
+#define DPM_FLAGS_PARTNER_DR_POWER             BIT(0)
+#define DPM_FLAGS_PARTNER_DR_DATA              BIT(1)
+#define DPM_FLAGS_PARTNER_EXTPOWER             BIT(2)
+#define DPM_FLAGS_PARTNER_USB_COMM             BIT(3)
+#define DPM_FLAGS_PARTNER_USB_SUSPEND  BIT(4)
+#define DPM_FLAGS_PARTNER_HIGH_CAP             BIT(5)
+
+#define DPM_FLAGS_PARTNER_MISMATCH             BIT(7)
+#define DPM_FLAGS_PARTNER_GIVE_BACK            BIT(8)
+#define DPM_FLAGS_PARTNER_NO_SUSPEND   BIT(9)
+
+#define DPM_FLAGS_RESET_PARTNER_MASK   \
+       (DPM_FLAGS_PARTNER_DR_POWER | DPM_FLAGS_PARTNER_DR_DATA | \
+       DPM_FLAGS_PARTNER_EXTPOWER | DPM_FLAGS_PARTNER_USB_COMM)
+
+#define DPM_FLAGS_CHECK_DC_MODE                        BIT(20)
+#define DPM_FLAGS_CHECK_UFP_SVID               BIT(21)
+#define DPM_FLAGS_CHECK_EXT_POWER              BIT(22)
+#define DPM_FLAGS_CHECK_DP_MODE                        BIT(23)
+#define DPM_FLAGS_CHECK_SINK_CAP               BIT(24)
+#define DPM_FLAGS_CHECK_SOURCE_CAP             BIT(25)
+#define DPM_FLAGS_CHECK_UFP_ID                 BIT(26)
+#define DPM_FLAGS_CHECK_CABLE_ID               BIT(27)
+#define DPM_FLAGS_CHECK_CABLE_ID_DFP           BIT(28)
+#define DPM_FLAGS_CHECK_PR_ROLE                        BIT(29)
+#define DPM_FLAGS_CHECK_DR_ROLE                        BIT(30)
+
+/* DPM_CAPS */
+
+#define DPM_CAP_LOCAL_DR_POWER                 BIT(0)
+#define DPM_CAP_LOCAL_DR_DATA                  BIT(1)
+#define DPM_CAP_LOCAL_EXT_POWER                        BIT(2)
+#define DPM_CAP_LOCAL_USB_COMM                 BIT(3)
+#define DPM_CAP_LOCAL_USB_SUSPEND              BIT(4)
+#define DPM_CAP_LOCAL_HIGH_CAP                 BIT(5)
+#define DPM_CAP_LOCAL_GIVE_BACK                        BIT(6)
+#define DPM_CAP_LOCAL_NO_SUSPEND               BIT(7)
+#define DPM_CAP_LOCAL_VCONN_SUPPLY             BIT(8)
+
+#define DPM_CAP_ATTEMP_ENTER_DC_MODE           BIT(11)
+#define DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP      BIT(12)
+#define DPM_CAP_ATTEMP_ENTER_DP_MODE           BIT(13)
+#define DPM_CAP_ATTEMP_DISCOVER_CABLE          BIT(14)
+#define DPM_CAP_ATTEMP_DISCOVER_ID             BIT(15)
+
+enum dpm_cap_pr_check_prefer {
+       DPM_CAP_PR_CHECK_DISABLE = 0,
+       DPM_CAP_PR_CHECK_PREFER_SNK = 1,
+       DPM_CAP_PR_CHECK_PREFER_SRC = 2,
+};
+
+#define DPM_CAP_PR_CHECK_PROP(cap)                     (((cap) & 0x03) << 16)
+#define DPM_CAP_EXTRACT_PR_CHECK(raw)          (((raw) >> 16) & 0x03)
+#define DPM_CAP_PR_SWAP_REJECT_AS_SRC          BIT(18)
+#define DPM_CAP_PR_SWAP_REJECT_AS_SNK          BIT(19)
+#define DPM_CAP_PR_SWAP_CHECK_GP_SRC           BIT(20)
+#define DPM_CAP_PR_SWAP_CHECK_GP_SNK           BIT(21)
+#define DPM_CAP_PR_SWAP_CHECK_GOOD_POWER       \
+       (DPM_CAP_PR_SWAP_CHECK_GP_SRC | DPM_CAP_PR_SWAP_CHECK_GP_SNK)
+
+enum dpm_cap_dr_check_prefer {
+       DPM_CAP_DR_CHECK_DISABLE = 0,
+       DPM_CAP_DR_CHECK_PREFER_UFP = 1,
+       DPM_CAP_DR_CHECK_PREFER_DFP = 2,
+};
+
+#define DPM_CAP_DR_CHECK_PROP(cap)             (((cap) & 0x03) << 22)
+#define DPM_CAP_EXTRACT_DR_CHECK(raw)          (((raw) >> 22) & 0x03)
+#define DPM_CAP_DR_SWAP_REJECT_AS_DFP          BIT(24)
+#define DPM_CAP_DR_SWAP_REJECT_AS_UFP          BIT(25)
+
+#define DPM_CAP_DP_PREFER_MF                           BIT(29)
+#define DPM_CAP_SNK_PREFER_LOW_VOLTAGE         BIT(30)
+#define DPM_CAP_SNK_IGNORE_MISMATCH_CURRENT    BIT(31)
+
+/* PD counter definitions */
+#define PD_MESSAGE_ID_COUNT    7
+#define PD_HARD_RESET_COUNT    2
+#define PD_CAPS_COUNT                  50
+#define PD_GET_SNK_CAP_RETRIES 3
+#define PD_GET_SRC_CAP_RETRIES 3
+#define PD_DISCOVER_ID_COUNT   3       /* max : 20 */
+
+enum {
+       PD_WAIT_VBUS_DISABLE = 0,
+       PD_WAIT_VBUS_VALID_ONCE = 1,
+       PD_WAIT_VBUS_INVALID_ONCE = 2,
+       PD_WAIT_VBUS_SAFE0V_ONCE = 3,
+       PD_WAIT_VBUS_STABLE_ONCE = 4,
+};
+
+typedef struct __pd_port_power_cababilities {
+       u8 nr;
+       u32 pdos[7];
+} pd_port_power_caps;
+
+#define PD_SVID_DATA_NR                2       /* must < 11 */
+
+typedef struct __svdm_mode {
+       u8 mode_cnt;
+       u32 mode_vdo[VDO_MAX_DATA_SIZE];
+} svdm_mode_t;
+
+struct __svdm_svid_ops;
+typedef struct __svdm_svid_data {
+       bool exist;
+       u16 svid;
+       u8 active_mode;
+       svdm_mode_t local_mode;
+       svdm_mode_t remote_mode;
+       const struct __svdm_svid_ops *ops;
+} svdm_svid_data_t;
+
+typedef struct __svdm_svid_list {
+       u8 cnt;
+       u16 svids[VDO_MAX_SVID_SIZE];
+} svdm_svid_list_t;
+
+typedef struct __pd_port {
+       struct tcpc_device *tcpc_dev;
+       struct mutex pd_lock;
+
+       /* PD */
+       bool explicit_contract;
+       bool invalid_contract;
+       bool vconn_source;
+
+#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+       bool vconn_return;
+#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
+
+       bool pe_ready;
+       bool pd_connected;
+       bool pd_prev_connected;
+       bool msg_output_lock;
+
+       u8 state_machine;
+       u8 pd_connect_state;
+
+       bool reset_vdm_state;
+       u8 pe_pd_state;
+       u8 pe_vdm_state;
+
+       u8 pe_state_next;
+       u8 pe_state_curr;
+
+       u8 data_role;
+       u8 power_role;
+
+       u8 cap_counter;
+       u8 discover_id_counter;
+       u8 hard_reset_counter;
+       u8 snk_cap_count;
+       u8 src_cap_count;
+       u8 get_snk_cap_count;
+       u8 get_src_cap_count;
+
+       u8 msg_id_rx[PD_SOP_NR];
+       u8 msg_id_rx_init[PD_SOP_NR];
+       u8 msg_id_tx[PD_SOP_NR];
+
+       u32 last_rdo;
+       u32 cable_vdos[VDO_MAX_SIZE];
+       bool power_cable_present;
+
+       u8 id_vdo_nr;
+       u32 id_vdos[VDO_MAX_DATA_SIZE];
+
+#ifdef CONFIG_USB_PD_KEEP_SVIDS
+       svdm_svid_list_t remote_svid_list;
+#endif
+
+       u8 svid_data_cnt;
+       svdm_svid_data_t svid_data[PD_SVID_DATA_NR];
+
+       bool during_swap;       /* pr or dr swap */
+
+/* DPM */
+       int request_v;
+       int request_i;
+       int request_v_new;
+       int request_i_new;
+       int request_i_op;
+       int request_i_max;
+
+       u8 local_selected_cap;
+       u8 remote_selected_cap;
+       pd_port_power_caps local_src_cap;
+       pd_port_power_caps local_snk_cap;
+       pd_port_power_caps local_src_cap_default;
+       pd_port_power_caps remote_src_cap;
+       pd_port_power_caps remote_snk_cap;
+
+       u16 mode_svid;
+       u8 mode_obj_pos;
+       bool modal_operation;
+       bool dpm_ack_immediately;
+
+       u32 dpm_flags;
+       u32 dpm_init_flags;
+       u32 dpm_caps;
+       u32 dpm_dfp_retry_cnt;
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       bool custom_dbgacc;
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+} pd_port_t;
+
+int pd_core_init(struct tcpc_device *tcpc_dev);
+int pd_alert_vbus_changed(pd_port_t *pd_port, int vbus_level);
+
+static inline int pd_is_auto_discover_cable_id(pd_port_t *pd_port)
+{
+       if (pd_port->dpm_flags & DPM_FLAGS_CHECK_CABLE_ID) {
+               if (pd_port->discover_id_counter < PD_DISCOVER_ID_COUNT)
+                       return true;
+
+               pd_port->dpm_flags &= ~DPM_FLAGS_CHECK_CABLE_ID;
+               return false;
+       }
+
+       return false;
+}
+
+static inline int pd_is_support_modal_operation(pd_port_t *pd_port)
+{
+       if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT))
+               return false;
+
+       return (pd_port->svid_data_cnt > 0);
+}
+
+/* new definitions*/
+
+#define PD_RX_CAP_PE_IDLE                              (0)
+#define PD_RX_CAP_PE_DISABLE                   (TCPC_RX_CAP_HARD_RESET)
+#define PD_RX_CAP_PE_STARTUP                   (TCPC_RX_CAP_HARD_RESET)
+#define PD_RX_CAP_PE_HARDRESET                 (0)
+#define PD_RX_CAP_PE_SEND_WAIT_CAP     \
+       (TCPC_RX_CAP_HARD_RESET | TCPC_RX_CAP_SOP)
+#define PD_RX_CAP_PE_DISCOVER_CABLE    \
+       (TCPC_RX_CAP_HARD_RESET | TCPC_RX_CAP_SOP_PRIME)
+#define PD_RX_CAP_PE_READY_UFP \
+       (TCPC_RX_CAP_HARD_RESET | TCPC_RX_CAP_SOP)
+
+#ifdef CONFIG_PD_DISCOVER_CABLE_ID
+#define PD_RX_CAP_PE_READY_DFP \
+       (TCPC_RX_CAP_HARD_RESET | TCPC_RX_CAP_SOP | TCPC_RX_CAP_SOP_PRIME)
+#else
+#define PD_RX_CAP_PE_READY_DFP (TCPC_RX_CAP_HARD_RESET | TCPC_RX_CAP_SOP)
+#endif
+
+enum {
+       PD_BIST_MODE_DISABLE = 0,
+       PD_BIST_MODE_EVENT_PENDING,
+       PD_BIST_MODE_TEST_DATA,
+};
+
+void pd_reset_svid_data(pd_port_t *pd_port);
+int pd_reset_protocol_layer(pd_port_t *pd_port);
+
+int pd_set_rx_enable(pd_port_t *pd_port, u8 enable);
+
+int pd_enable_vbus_valid_detection(pd_port_t *pd_port, bool wait_valid);
+int pd_enable_vbus_safe0v_detection(pd_port_t *pd_port);
+int pd_enable_vbus_stable_detection(pd_port_t *pd_port);
+
+u32 pd_reset_pdo_power(u32 pdo, u32 imax);
+
+void pd_extract_rdo_power(
+                         u32 rdo, u32 pdo, u32 *op_curr,
+                         u32 *max_curr);
+
+void pd_extract_pdo_power(u32 pdo,
+                         u32 *vmin, u32 *vmax, u32 *ioper);
+
+u32 pd_extract_cable_curr(u32 vdo);
+
+int pd_set_data_role(pd_port_t *pd_port, u8 dr);
+int pd_set_power_role(pd_port_t *pd_port, u8 pr);
+int pd_init_role(pd_port_t *pd_port, u8 pr, u8 dr, bool vr);
+
+int pd_set_cc_res(pd_port_t *pd_port, int pull);
+int pd_set_vconn(pd_port_t *pd_port, int enable);
+int pd_reset_local_hw(pd_port_t *pd_port);
+
+int pd_enable_bist_test_mode(pd_port_t *pd_port, bool en);
+
+void pd_lock_msg_output(pd_port_t *pd_port);
+void pd_unlock_msg_output(pd_port_t *pd_port);
+
+int pd_update_connect_state(pd_port_t *pd_port, u8 state);
+void pd_update_dpm_request_state(pd_port_t *pd_port, u8 state);
+
+/* ---- PD notify TCPC Policy Engine State Changed ---- */
+
+void pd_try_put_pe_idle_event(pd_port_t *pd_port);
+void pd_notify_pe_transit_to_default(pd_port_t *pd_port);
+void pd_notify_pe_hard_reset_completed(pd_port_t *pd_port);
+void pd_notify_pe_send_hard_reset(pd_port_t *pd_port);
+void pd_notify_pe_idle(pd_port_t *pd_port);
+void pd_notify_pe_wait_vbus_once(pd_port_t *pd_port, int wait_evt);
+void pd_notify_pe_error_recovery(pd_port_t *pd_port);
+void pd_notify_pe_execute_pr_swap(pd_port_t *pd_port, bool start_swap);
+void pd_notify_pe_cancel_pr_swap(pd_port_t *pd_port);
+void pd_notify_pe_reset_protocol(pd_port_t *pd_port);
+void pd_noitfy_pe_bist_mode(pd_port_t *pd_port, u8 mode);
+void pd_notify_pe_pr_changed(pd_port_t *pd_port);
+void pd_notify_pe_src_explicit_contract(pd_port_t *pd_port);
+void pd_notify_pe_transmit_msg(pd_port_t *pd_port, u8 type);
+void pd_notify_pe_recv_ping_event(pd_port_t *pd_port);
+
+/* ---- pd_timer ---- */
+
+static inline void pd_restart_timer(pd_port_t *pd_port, u32 timer_id)
+{
+       return tcpc_restart_timer(pd_port->tcpc_dev, timer_id);
+}
+
+static inline void pd_enable_timer(pd_port_t *pd_port, u32 timer_id)
+{
+       return tcpc_enable_timer(pd_port->tcpc_dev, timer_id);
+}
+
+static inline void pd_disable_timer(pd_port_t *pd_port, u32 timer_id)
+{
+       return tcpc_disable_timer(pd_port->tcpc_dev, timer_id);
+}
+
+static inline void pd_reset_pe_timer(pd_port_t *pd_port)
+{
+       tcpc_reset_pe_timer(pd_port->tcpc_dev);
+}
+
+/* ---- pd_event ---- */
+
+static inline void pd_free_pd_event(pd_port_t *pd_port, pd_event_t *pd_event)
+{
+       pd_free_event(pd_port->tcpc_dev, pd_event);
+}
+
+static inline bool pd_put_pe_event(pd_port_t *pd_port, u8 pe_event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_PE_MSG,
+               .msg = pe_event,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool pd_put_dpm_event(pd_port_t *pd_port, u8 event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = event,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool pd_put_dpm_pd_request_event(
+       pd_port_t *pd_port, u8 event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_PD_REQUEST,
+               .msg_sec = event,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool vdm_put_dpm_vdm_request_event(
+       pd_port_t *pd_port, u8 event)
+{
+       bool ret;
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_VDM_REQUEST,
+               .msg_sec = event,
+               .pd_msg = NULL,
+       };
+
+       ret = pd_put_vdm_event(pd_port->tcpc_dev, &evt, false);
+
+       if (ret) {
+               pd_port->reset_vdm_state = true;
+               pd_port->pe_vdm_state = pd_port->pe_pd_state;
+       }
+
+       return ret;
+}
+
+static inline bool pd_put_dpm_notify_event(pd_port_t *pd_port, u8 notify)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_NOTIFIED,
+               .msg_sec = notify,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool pd_put_dpm_ack_event(pd_port_t *pd_port)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_ACK,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool pd_put_dpm_nak_event(pd_port_t *pd_port, u8 notify)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = PD_DPM_NAK,
+               .msg_sec = notify,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool vdm_put_hw_event(
+       struct tcpc_device *tcpc_dev, u8 hw_event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_HW_MSG,
+               .msg = hw_event,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_vdm_event(tcpc_dev, &evt, false);
+}
+
+static inline bool vdm_put_dpm_event(
+       pd_port_t *pd_port, u8 dpm_event, pd_msg_t *pd_msg)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_DPM_MSG,
+               .msg = dpm_event,
+               .pd_msg = pd_msg,
+       };
+
+       return pd_put_vdm_event(pd_port->tcpc_dev, &evt, false);
+}
+
+static inline bool vdm_put_dpm_notified_event(pd_port_t *pd_port)
+{
+       return vdm_put_dpm_event(pd_port, PD_DPM_NOTIFIED, NULL);
+}
+
+static inline bool vdm_put_dpm_discover_cable_event(pd_port_t *pd_port)
+{
+       pd_port->pe_vdm_state = pd_port->pe_pd_state;
+       return vdm_put_dpm_event(pd_port, PD_DPM_DISCOVER_CABLE_ID, NULL);
+}
+
+static inline bool pd_put_hw_event(
+       struct tcpc_device *tcpc_dev, u8 hw_event)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_HW_MSG,
+               .msg = hw_event,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(tcpc_dev, &evt, false);
+}
+
+static inline bool pd_put_cc_attached_event(
+               struct tcpc_device *tcpc_dev, u8 type)
+{
+       pd_event_t evt = {
+               .event_type = PD_EVT_HW_MSG,
+               .msg = PD_HW_CC_ATTACHED,
+               .msg_sec = type,
+               .pd_msg = NULL,
+       };
+
+       return pd_put_event(tcpc_dev, &evt, false);
+}
+
+/* ---- Handle PD Message ----*/
+
+int pd_handle_soft_reset(pd_port_t *pd_port, u8 state_machine);
+
+/* ---- Send PD Message ----*/
+
+int pd_send_ctrl_msg(
+                    pd_port_t *pd_port, u8 sop_type, u8 msg);
+
+int pd_send_data_msg(pd_port_t *pd_port,
+                    u8 sop_type, u8 msg, u8 cnt, u32 *payload);
+
+int pd_send_soft_reset(pd_port_t *pd_port, u8 state_machine);
+int pd_send_hard_reset(pd_port_t *pd_port);
+
+int pd_send_bist_mode2(pd_port_t *pd_port);
+int pd_disable_bist_mode2(pd_port_t *pd_port);
+
+/* ---- Send / Reply SVDM Command ----*/
+
+/* Auto enable pd_timer_vdm_response if success */
+int pd_send_svdm_request(pd_port_t *pd_port,
+                        u8 sop_type, u16 svid, u8 vdm_cmd,
+                        u8 obj_pos, u8 cnt, u32 *data_obj);
+
+int pd_reply_svdm_request(pd_port_t *pd_port,
+                         pd_event_t *pd_event, u8 reply,
+                         u8 cnt, u32 *data_obj);
+
+static inline int pd_send_vdm_discover_id(
+       pd_port_t *pd_port, u8 sop_type)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, USB_SID_PD, CMD_DISCOVER_IDENT, 0, 0, NULL);
+}
+
+static inline int pd_send_vdm_discover_svids(
+       pd_port_t *pd_port, u8 sop_type)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, USB_SID_PD, CMD_DISCOVER_SVID, 0, 0, NULL);
+}
+
+static inline int pd_send_vdm_discover_modes(
+       pd_port_t *pd_port, u8 sop_type, u16 svid)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, svid, CMD_DISCOVER_MODES, 0, 0, NULL);
+}
+
+static inline int pd_send_vdm_enter_mode(
+       pd_port_t *pd_port, u8 sop_type, u16 svid, u8 obj_pos)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, svid, CMD_ENTER_MODE, obj_pos, 0, NULL);
+}
+
+static inline int pd_send_vdm_exit_mode(
+       pd_port_t *pd_port, u8 sop_type, u16 svid, u8 obj_pos)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, svid, CMD_EXIT_MODE, obj_pos, 0, NULL);
+}
+
+static inline int pd_send_vdm_attention(
+       pd_port_t *pd_port, u8 sop_type, u16 svid, u8 obj_pos)
+{
+       return pd_send_svdm_request(
+               pd_port, sop_type, svid, CMD_ATTENTION, obj_pos, 0, NULL);
+}
+
+static inline int pd_send_vdm_dp_attention(pd_port_t *pd_port,
+                                          u8 sop_type, u8 obj_pos,
+                                          u32 dp_status)
+{
+       return pd_send_svdm_request(pd_port, sop_type,
+               USB_SID_DISPLAYPORT, CMD_ATTENTION, obj_pos, 1, &dp_status);
+}
+
+static inline int pd_send_vdm_dp_status(pd_port_t *pd_port,
+                                       u8 sop_type, u8 obj_pos,
+                                       u8 cnt, u32 *data_obj)
+{
+       return pd_send_svdm_request(pd_port, sop_type,
+               USB_SID_DISPLAYPORT, CMD_DP_STATUS, obj_pos, cnt, data_obj);
+}
+
+static inline int pd_send_vdm_dp_config(pd_port_t *pd_port,
+                                       u8 sop_type, u8 obj_pos,
+                                       u8 cnt, u32 *data_obj)
+{
+       return pd_send_svdm_request(pd_port, sop_type,
+               USB_SID_DISPLAYPORT, CMD_DP_CONFIG, obj_pos, cnt, data_obj);
+}
+
+static inline int pd_reply_svdm_request_simply(
+       pd_port_t *pd_port, pd_event_t *pd_event, u8 reply)
+{
+       return pd_reply_svdm_request(pd_port, pd_event, reply, 0, NULL);
+}
+#endif /* PD_CORE_H_ */
diff --git a/include/linux/hisi/usb/pd/richtek/pd_dpm_core.h b/include/linux/hisi/usb/pd/richtek/pd_dpm_core.h
new file mode 100644 (file)
index 0000000..61ea340
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PD_DPM_CORE_H
+#define PD_DPM_CORE_H
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+
+/* ---- MISC ---- */
+int pd_dpm_enable_vconn(pd_port_t *pd_port, bool en);
+int pd_dpm_send_sink_caps(pd_port_t *pd_port);
+int pd_dpm_send_source_caps(pd_port_t *pd_port);
+
+/* ---- SNK ---- */
+
+bool pd_dpm_send_request(pd_port_t *pd_port, int mv, int ma);
+
+void pd_dpm_snk_evaluate_caps(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_snk_transition_power(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_snk_hard_reset(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- SRC ---- */
+
+void pd_dpm_src_evaluate_request(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_src_transition_power(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_src_hard_reset(pd_port_t *pd_port);
+void pd_dpm_src_inform_cable_vdo(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- UFP : Evaluate VDM Request ---- */
+
+void pd_dpm_ufp_request_id_info(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_ufp_request_svid_info(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_ufp_request_mode_info(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_ufp_request_enter_mode(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_ufp_request_exit_mode(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- UFP : Response VDM Request ---- */
+
+int pd_dpm_ufp_response_id(pd_port_t *pd_port, pd_event_t *pd_event);
+int pd_dpm_ufp_response_svids(pd_port_t *pd_port, pd_event_t *pd_event);
+int pd_dpm_ufp_response_modes(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- DFP : Inform VDM Result ---- */
+
+void pd_dpm_dfp_inform_id(pd_port_t *pd_port, pd_event_t *pd_event, bool ack);
+void pd_dpm_dfp_inform_svids(
+                       pd_port_t *pd_port, pd_event_t *pd_event, bool ack);
+void pd_dpm_dfp_inform_modes(
+                       pd_port_t *pd_port, pd_event_t *pd_event, bool ack);
+void pd_dpm_dfp_inform_enter_mode(
+       pd_port_t *pd_port, pd_event_t *pd_event, bool ack);
+void pd_dpm_dfp_inform_exit_mode(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_dfp_inform_attention(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pd_dpm_dfp_inform_cable_vdo(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- DRP : Inform PowerCap ---- */
+
+void pd_dpm_dr_inform_sink_cap(pd_port_t *pd_port, pd_event_t *pd_event);
+void pd_dpm_dr_inform_source_cap(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- DRP : Data Role Swap ---- */
+
+void pd_dpm_drs_evaluate_swap(pd_port_t *pd_port, uint8_t role);
+void pd_dpm_drs_change_role(pd_port_t *pd_port, uint8_t role);
+
+/* ---- DRP : Power Role Swap ---- */
+
+void pd_dpm_prs_evaluate_swap(pd_port_t *pd_port, uint8_t role);
+void pd_dpm_prs_turn_off_power_sink(pd_port_t *pd_port);
+void pd_dpm_prs_enable_power_source(pd_port_t *pd_port, bool en);
+void pd_dpm_prs_change_role(pd_port_t *pd_port, uint8_t role);
+
+/* ---- DRP : Vconn Swap ---- */
+
+void pd_dpm_vcs_evaluate_swap(pd_port_t *pd_port);
+void pd_dpm_vcs_enable_vconn(pd_port_t *pd_port, bool en);
+
+/* PE : Notify DPM */
+
+int pd_dpm_notify_pe_startup(pd_port_t *pd_port);
+int pd_dpm_notify_pe_hardreset(pd_port_t *pd_port);
+int pd_dpm_notify_pe_ready(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* TCPCI - VBUS Control */
+
+static inline int pd_dpm_check_vbus_valid(pd_port_t *pd_port)
+{
+       return tcpci_check_vbus_valid(pd_port->tcpc_dev);
+}
+
+static inline int pd_dpm_sink_vbus(pd_port_t *pd_port, bool en)
+{
+       int mv = en ? TCPC_VBUS_SINK_5V : TCPC_VBUS_SINK_0V;
+
+       return tcpci_sink_vbus(pd_port->tcpc_dev,
+                               TCP_VBUS_CTRL_REQUEST, mv, -1);
+}
+
+static inline int pd_dpm_source_vbus(pd_port_t *pd_port, bool en)
+{
+       int mv = en ? TCPC_VBUS_SOURCE_5V : TCPC_VBUS_SOURCE_0V;
+
+       return tcpci_source_vbus(pd_port->tcpc_dev,
+                               TCP_VBUS_CTRL_REQUEST, mv, -1);
+}
+#endif /* PD_DPM_CORE_H */
diff --git a/include/linux/hisi/usb/pd/richtek/pd_policy_engine.h b/include/linux/hisi/usb/pd/richtek/pd_policy_engine.h
new file mode 100644 (file)
index 0000000..fe4593a
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PD_POLICY_ENGINE_H_
+#define PD_POLICY_ENGINE_H_
+
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+
+/* ---- Policy Engine State ---- */
+
+enum pd_pe_state_machine {
+       PE_STATE_MACHINE_IDLE = 0,
+       PE_STATE_MACHINE_SINK,
+       PE_STATE_MACHINE_SOURCE,
+       PE_STATE_MACHINE_DR_SWAP,
+       PE_STATE_MACHINE_PR_SWAP,
+       PE_STATE_MACHINE_VCONN_SWAP,
+       PE_STATE_MACHINE_DBGACC,
+};
+
+enum pd_pe_state {
+       PE_SRC_STARTUP = 0,
+       PE_SRC_DISCOVERY,
+       PE_SRC_SEND_CAPABILITIES,
+       PE_SRC_NEGOTIATE_CAPABILITIES,
+       PE_SRC_TRANSITION_SUPPLY,
+       PE_SRC_TRANSITION_SUPPLY2,
+       PE_SRC_READY,
+       PE_SRC_DISABLED,
+       PE_SRC_CAPABILITY_RESPONSE,
+       PE_SRC_HARD_RESET,
+       PE_SRC_HARD_RESET_RECEIVED,
+       PE_SRC_TRANSITION_TO_DEFAULT,
+       PE_SRC_GIVE_SOURCE_CAP,
+       PE_SRC_GET_SINK_CAP,
+       PE_SRC_WAIT_NEW_CAPABILITIES,
+
+       PE_SRC_SEND_SOFT_RESET,
+       PE_SRC_SOFT_RESET,
+       PE_SRC_PING,
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+       PE_SRC_VDM_IDENTITY_REQUEST,
+       PE_SRC_VDM_IDENTITY_ACKED,
+       PE_SRC_VDM_IDENTITY_NAKED,
+#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */
+
+       PE_SNK_STARTUP,
+       PE_SNK_DISCOVERY,
+       PE_SNK_WAIT_FOR_CAPABILITIES,
+       PE_SNK_EVALUATE_CAPABILITY,
+       PE_SNK_SELECT_CAPABILITY,
+       PE_SNK_TRANSITION_SINK,
+       PE_SNK_READY,
+       PE_SNK_HARD_RESET,
+       PE_SNK_TRANSITION_TO_DEFAULT,
+       PE_SNK_GIVE_SINK_CAP,
+       PE_SNK_GET_SOURCE_CAP,
+
+       PE_SNK_SEND_SOFT_RESET,
+       PE_SNK_SOFT_RESET,
+
+       PE_DRS_DFP_UFP_EVALUATE_DR_SWAP,
+       PE_DRS_DFP_UFP_ACCEPT_DR_SWAP,
+       PE_DRS_DFP_UFP_CHANGE_TO_UFP,
+       PE_DRS_DFP_UFP_SEND_DR_SWAP,
+       PE_DRS_DFP_UFP_REJECT_DR_SWAP,
+
+       PE_DRS_UFP_DFP_EVALUATE_DR_SWAP,
+       PE_DRS_UFP_DFP_ACCEPT_DR_SWAP,
+       PE_DRS_UFP_DFP_CHANGE_TO_DFP,
+       PE_DRS_UFP_DFP_SEND_DR_SWAP,
+       PE_DRS_UFP_DFP_REJECT_DR_SWAP,
+
+       PE_PRS_SRC_SNK_EVALUATE_PR_SWAP,
+       PE_PRS_SRC_SNK_ACCEPT_PR_SWAP,
+       PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
+       PE_PRS_SRC_SNK_ASSERT_RD,
+       PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
+       PE_PRS_SRC_SNK_SEND_SWAP,
+       PE_PRS_SRC_SNK_REJECT_PR_SWAP,
+
+       PE_PRS_SNK_SRC_EVALUATE_PR_SWAP,
+       PE_PRS_SNK_SRC_ACCEPT_PR_SWAP,
+       PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
+       PE_PRS_SNK_SRC_ASSERT_RP,
+       PE_PRS_SNK_SRC_SOURCE_ON,
+       PE_PRS_SNK_SRC_SEND_SWAP,
+       PE_PRS_SNK_SRC_REJECT_SWAP,
+
+       PE_DR_SRC_GET_SOURCE_CAP,
+       PE_DR_SRC_GIVE_SINK_CAP,
+       PE_DR_SNK_GET_SINK_CAP,
+       PE_DR_SNK_GIVE_SOURCE_CAP,
+
+       PE_VCS_SEND_SWAP,
+       PE_VCS_EVALUATE_SWAP,
+       PE_VCS_ACCEPT_SWAP,
+       PE_VCS_REJECT_VCONN_SWAP,
+       PE_VCS_WAIT_FOR_VCONN,
+       PE_VCS_TURN_OFF_VCONN,
+       PE_VCS_TURN_ON_VCONN,
+       PE_VCS_SEND_PS_RDY,
+
+       PE_UFP_VDM_GET_IDENTITY,
+       PE_UFP_VDM_SEND_IDENTITY,
+       PE_UFP_VDM_GET_IDENTITY_NAK,
+
+       PE_UFP_VDM_GET_SVIDS,
+       PE_UFP_VDM_SEND_SVIDS,
+       PE_UFP_VDM_GET_SVIDS_NAK,
+
+       PE_UFP_VDM_GET_MODES,
+       PE_UFP_VDM_SEND_MODES,
+       PE_UFP_VDM_GET_MODES_NAK,
+
+       PE_UFP_VDM_EVALUATE_MODE_ENTRY,
+       PE_UFP_VDM_MODE_ENTRY_ACK,
+       PE_UFP_VDM_MODE_ENTRY_NAK,
+
+       PE_UFP_VDM_MODE_EXIT,
+       PE_UFP_VDM_MODE_EXIT_ACK,
+       PE_UFP_VDM_MODE_EXIT_NAK,
+
+       PE_UFP_VDM_ATTENTION_REQUEST,
+
+       PE_DFP_UFP_VDM_IDENTITY_REQUEST,
+       PE_DFP_UFP_VDM_IDENTITY_ACKED,
+       PE_DFP_UFP_VDM_IDENTITY_NAKED,
+
+       PE_DFP_CBL_VDM_IDENTITY_REQUEST,
+       PE_DFP_CBL_VDM_IDENTITY_ACKED,
+       PE_DFP_CBL_VDM_IDENTITY_NAKED,
+
+       PE_DFP_VDM_SVIDS_REQUEST,
+       PE_DFP_VDM_SVIDS_ACKED,
+       PE_DFP_VDM_SVIDS_NAKED,
+
+       PE_DFP_VDM_MODES_REQUEST,
+       PE_DFP_VDM_MODES_ACKED,
+       PE_DFP_VDM_MODES_NAKED,
+
+       PE_DFP_VDM_MODE_ENTRY_REQUEST,
+       PE_DFP_VDM_MODE_ENTRY_ACKED,
+       PE_DFP_VDM_MODE_ENTRY_NAKED,
+
+       PE_DFP_VDM_MODE_EXIT_REQUEST,
+       PE_DFP_VDM_MODE_EXIT_ACKED,
+
+       PE_DFP_VDM_ATTENTION_REQUEST,
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       PE_DBG_READY,
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+       PE_ERROR_RECOVERY,
+
+       PE_BIST_TEST_DATA,
+       PE_BIST_CARRIER_MODE_2,
+
+       PE_IDLE1,       /* Wait tx finished */
+       PE_IDLE2,
+
+       PD_NR_PE_STATES,
+
+       PE_VIRT_HARD_RESET,
+       PE_VIRT_READY,
+};
+
+enum pd_dpm_vdm_request_type {
+       PD_DPM_VDM_REQUEST_DISCOVER_ID =
+               PE_DFP_UFP_VDM_IDENTITY_REQUEST,
+       PD_DPM_VDM_REQUEST_DISCOVER_SVIDS =
+               PE_DFP_VDM_SVIDS_REQUEST,
+       PD_DPM_VDM_REQUEST_DISCOVER_MODES =
+               PE_DFP_VDM_MODES_REQUEST,
+       PD_DPM_VDM_REQUEST_ENTRY_MODE =
+               PE_DFP_VDM_MODE_ENTRY_REQUEST,
+       PD_DPM_VDM_REQUEST_EXIT_MODE =
+               PE_DFP_VDM_MODE_EXIT_REQUEST,
+
+       PD_DPM_VDM_REQUEST_ATTENTION =
+               PE_UFP_VDM_ATTENTION_REQUEST,
+};
+
+/*
+ * > 0 : Process Event
+ * = 0 : No Event
+ * < 0 : Error
+ */
+
+int pd_policy_engine_run(struct tcpc_device *tcpc_dev);
+
+/* ---- Policy Engine (General) ---- */
+
+void pe_power_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (SNK) ---- */
+
+void pe_snk_startup_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_discovery_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_wait_for_capabilities_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_evaluate_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_select_capability_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_transition_sink_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_hard_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_transition_to_default_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_give_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_get_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_send_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_snk_wait_for_capabilities_exit(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_select_capability_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_transition_sink_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_snk_transition_to_default_exit(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (SRC) ---- */
+
+void pe_src_startup_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_discovery_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_send_capabilities_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_negotiate_capabilities_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_transition_supply_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_transition_supply2_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_disabled_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_capability_response_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_hard_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_hard_reset_received_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_transition_to_default_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_give_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_get_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_wait_new_capabilities_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_send_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_soft_reset_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_ping_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+void pe_src_vdm_identity_request_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_vdm_identity_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_vdm_identity_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+#endif
+
+void pe_src_send_capabilities_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_transition_supply_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_transition_to_default_exit(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_src_get_sink_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (DRS) ---- */
+
+void pe_drs_dfp_ufp_evaluate_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_dfp_ufp_accept_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_dfp_ufp_change_to_ufp_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_dfp_ufp_send_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_dfp_ufp_reject_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_drs_ufp_dfp_evaluate_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_ufp_dfp_accept_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_ufp_dfp_change_to_dfp_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_ufp_dfp_send_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_drs_ufp_dfp_reject_dr_swap_entry(
+                       pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (PRS) ---- */
+
+void pe_prs_src_snk_evaluate_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_accept_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_transition_to_off_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_assert_rd_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_wait_source_on_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_src_snk_reject_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_prs_snk_src_evaluate_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_accept_pr_swap_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_transition_to_off_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_assert_rp_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_source_on_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_reject_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_prs_src_snk_wait_source_on_exit(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_transition_to_off_exit(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_prs_snk_src_source_on_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (DR) ---- */
+
+void pe_dr_src_get_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dr_src_give_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dr_snk_get_sink_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dr_snk_give_source_cap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dr_src_get_source_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dr_snk_get_sink_cap_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (VCS) ---- */
+
+void pe_vcs_send_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_evaluate_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_accept_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_reject_vconn_swap_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_wait_for_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_turn_off_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_turn_on_vconn_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_vcs_send_ps_rdy_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_vcs_wait_for_vconn_exit(pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (UFP) ---- */
+
+void pe_ufp_vdm_get_identity_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_send_identity_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_get_identity_nak_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_ufp_vdm_get_svids_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_send_svids_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_get_svids_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_ufp_vdm_get_modes_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_send_modes_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_get_modes_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_ufp_vdm_evaluate_mode_entry_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_mode_entry_ack_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_mode_entry_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_ufp_vdm_mode_exit_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_mode_exit_ack_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_ufp_vdm_mode_exit_nak_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_ufp_vdm_attention_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (DFP) ---- */
+
+void pe_dfp_ufp_vdm_identity_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_ufp_vdm_identity_acked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_ufp_vdm_identity_naked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_cbl_vdm_identity_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_cbl_vdm_identity_acked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_cbl_vdm_identity_naked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_vdm_svids_request_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_svids_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_svids_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_vdm_modes_request_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_modes_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_modes_naked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_vdm_mode_entry_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_mode_entry_acked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_mode_entry_naked_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_vdm_mode_exit_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+void pe_dfp_vdm_mode_exit_acked_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+
+void pe_dfp_vdm_attention_request_entry(
+                               pd_port_t *pd_port, pd_event_t *pd_event);
+
+/* ---- Policy Engine (DBG) ---- */
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+void pe_dbg_ready_entry(pd_port_t *pd_port, pd_event_t *pd_event);
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+#endif /* PD_POLICY_ENGINE_H_ */
diff --git a/include/linux/hisi/usb/pd/richtek/pd_process_evt.h b/include/linux/hisi/usb/pd/richtek/pd_process_evt.h
new file mode 100644 (file)
index 0000000..f0beb8d
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PD_PROCESS_EVT_H_
+#define PD_PROCESS_EVT_H_
+
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+#include <linux/hisi/usb/pd/richtek/pd_policy_engine.h>
+
+typedef struct __pe_state_transition {
+       u8 curr_state; /*state, msg, or cmd */
+       u8 next_state;
+} pe_state_transition_t;
+
+typedef struct __pe_state_reaction {
+       u16 nr_transition;
+       const pe_state_transition_t *state_transition;
+} pe_state_reaction_t;
+
+#define DECL_PE_STATE_TRANSITION(state)        \
+       static const pe_state_transition_t state##_state_transition[]
+
+#define DECL_PE_STATE_REACTION(state)  \
+       static const pe_state_reaction_t state##_reactions = {\
+               .nr_transition = ARRAY_SIZE(state##_state_transition),\
+               .state_transition = state##_state_transition,\
+       }
+
+static inline bool pd_check_pe_state_ready(pd_port_t *pd_port)
+{
+       /* TODO: Handle Port Partner first (skip our get_cap state )*/
+       switch (pd_port->pe_state_curr) {
+       case PE_SNK_READY:
+       case PE_SRC_READY:
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+       case PE_DBG_READY:
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Sink & Source Common Event
+ *--------------------------------------------------------------
+ */
+
+bool pd_process_data_msg_bist(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+
+bool pd_process_protocol_error(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+
+bool pd_process_ctrl_msg_dr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+bool pd_process_dpm_msg_dr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+
+bool pd_process_ctrl_msg_pr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+bool pd_process_dpm_msg_pr_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+
+bool pd_process_ctrl_msg_vconn_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+bool pd_process_dpm_msg_vconn_swap(
+       pd_port_t *pd_port, pd_event_t *pd_event);
+
+bool pd_process_recv_hard_reset(
+               pd_port_t *pd_port, pd_event_t *pd_event, uint8_t hreset_state);
+
+/*
+ *------------------------------------------------------------
+ */
+
+#define PE_TRANSIT_STATE(pd_port, state)       \
+       ((pd_port)->pe_state_next = state)
+
+#define PE_TRANSIT_POWER_STATE(pd_port, sink, source)  \
+       (pd_port->pe_state_next =\
+       ((pd_port->power_role == PD_ROLE_SINK) ? sink : source))
+
+#define PE_TRANSIT_DATA_STATE(pd_port, ufp, dfp)       \
+       (pd_port->pe_state_next =\
+       ((pd_port->data_role == PD_ROLE_UFP) ? ufp : dfp))
+
+#define PE_TRANSIT_READY_STATE(pd_port) \
+       PE_TRANSIT_POWER_STATE(pd_port, PE_SNK_READY, PE_SRC_READY)
+
+#define PE_TRANSIT_HARD_RESET_STATE(pd_port) \
+       PE_TRANSIT_POWER_STATE(pd_port, PE_SNK_HARD_RESET, PE_SRC_HARD_RESET)
+
+#define PE_TRANSIT_SOFT_RESET_STATE(pd_port) \
+       PE_TRANSIT_POWER_STATE(pd_port, PE_SNK_SOFT_RESET, PE_SRC_SOFT_RESET)
+
+#define PE_TRANSIT_VCS_SWAP_STATE(pd_port) \
+       PE_TRANSIT_STATE(pd_port, pd_port->vconn_source ? \
+               PE_VCS_WAIT_FOR_VCONN : PE_VCS_TURN_ON_VCONN)
+
+#define PE_TRANSIT_SEND_SOFT_RESET_STATE(pd_port) \
+       PE_TRANSIT_POWER_STATE(pd_port, \
+       PE_SNK_SEND_SOFT_RESET, PE_SRC_SEND_SOFT_RESET)
+
+/*
+ * ---------------------------------------------------------
+ */
+
+#define PE_MAKE_STATE_TRANSIT(state)   \
+               pd_make_pe_state_transit(\
+                       pd_port, pd_port->pe_state_curr, &state##_reactions)
+/* PE_MAKE_STATE_TRANSIT */
+
+#define PE_MAKE_STATE_TRANSIT_VIRT(state)      \
+               pd_make_pe_state_transit_virt(\
+                       pd_port, pd_port->pe_state_curr, &state##_reactions)
+/* PE_MAKE_STATE_TRANSIT_VIRT */
+
+#define PE_MAKE_STATE_TRANSIT_FORCE(state, force)      \
+               pd_make_pe_state_transit_force(\
+               pd_port, pd_port->pe_state_curr, force, &state##_reactions)
+/* PE_MAKE_STATE_TRANSIT_FORCE */
+
+#define VDM_CMD_STATE_MASK(raw)                ((raw) & 0xdf)
+
+#define PE_MAKE_VDM_CMD_STATE_TRANSIT(state)   \
+               pd_make_pe_state_transit(\
+                       pd_port, \
+                       VDM_CMD_STATE_MASK(pd_event->pd_msg->payload[0]), \
+                       &state##_reactions)
+/* PE_MAKE_VDM_CMD_STATE_TRANSIT */
+
+#define PE_MAKE_VDM_CMD_STATE_TRANSIT_VIRT(state)      \
+               pd_make_pe_state_transit_virt(\
+                       pd_port, \
+                       VDM_CMD_STATE_MASK(pd_event->pd_msg->payload[0]), \
+                       &state##_reactions)
+/* PE_MAKE_VDM_CMD_STATE_TRANSIT_VIRT */
+
+bool pd_make_pe_state_transit(pd_port_t *pd_port, uint8_t curr_state,
+                             const pe_state_reaction_t *state_reaction);
+
+bool pd_make_pe_state_transit_virt(pd_port_t *pd_port, uint8_t curr_state,
+                                  const pe_state_reaction_t *state_reaction);
+
+bool pd_make_pe_state_transit_force(pd_port_t *pd_port,
+                                   u8 curr_state, u8 force_state,
+                                       const pe_state_reaction_t
+                                       *state_reaction);
+
+bool pd_process_event(pd_port_t *pd_port, pd_event_t *pd_event, bool vdm_evt);
+
+bool pd_process_event_snk(pd_port_t *pd_port, pd_event_t *evt);
+bool pd_process_event_src(pd_port_t *pd_port, pd_event_t *evt);
+bool pd_process_event_drs(pd_port_t *pd_port, pd_event_t *evt);
+bool pd_process_event_prs(pd_port_t *pd_port, pd_event_t *evt);
+bool pd_process_event_vdm(pd_port_t *pd_port, pd_event_t *evt);
+bool pd_process_event_vcs(pd_port_t *pd_port, pd_event_t *evt);
+
+#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
+bool pd_process_event_dbg(pd_port_t *pd_port, pd_event_t *evt);
+#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
+
+#endif /* PD_PROCESS_EVT_H_ */
diff --git a/include/linux/hisi/usb/pd/richtek/rt-regmap.h b/include/linux/hisi/usb/pd/richtek/rt-regmap.h
new file mode 100644 (file)
index 0000000..257c8be
--- /dev/null
@@ -0,0 +1,296 @@
+/* include/linux/misc/rt-regmap.h
+ * Header of Richtek regmap with debugfs Driver
+ *
+ * Copyright (C) 2014 Richtek Technology Corp.
+ * Jeff Chang <jeff_chang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef LINUX_MISC_RT_REGMAP_H
+#define LINUX_MISC_RT_REGMAP_H
+
+#include <linux/debugfs.h>
+
+#define RT_REGMAP_VERSION      "1.1.7_G"
+
+enum rt_access_mode {
+       RT_1BYTE_MODE = 1,
+       RT_2BYTE_MODE = 2,
+       RT_4BYTE_MODE = 4,
+};
+
+/* start : the start address of group
+ * end : the end address of group
+ * mode : access mode (1,2,4 bytes)
+ */
+struct rt_access_group {
+       u32 start;
+       u32 end;
+       enum rt_access_mode mode;
+};
+
+/* rt_reg_type
+ * RT_NORMAL   : Write data without mask
+ *                     Read from cache
+ * RT_WBITS    : Write data with mask
+ *                     Read from cache
+ * RT_VOLATILE : Write data to chip directly
+ *                     Read data from chip
+ * RT_RESERVE  : Reserve registers (Write/Read as RT_NORMAL)
+ */
+
+#define RT_REG_TYPE_MASK       (0x03)
+#define RT_NORMAL              (0x00)
+#define RT_WBITS               (0x01)
+#define RT_VOLATILE            (0x02)
+#define RT_RESERVE             (0x03)
+
+/* RT_WR_ONCE : write once will check write data and cache data,
+ *             if write data = cache data, data will not be writen.
+ */
+#define RT_WR_ONCE             (0x08)
+#define RT_NORMAL_WR_ONCE      (RT_NORMAL | RT_WR_ONCE)
+#define RT_WBITS_WR_ONCE       (RT_WBITS | RT_WR_ONCE)
+
+enum rt_data_format {
+       RT_LITTLE_ENDIAN,
+       RT_BIG_ENDIAN,
+};
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/* rt_regmap_mode
+ *  0  0  0  0  0  0  0  0
+ *       |  |  |  |  |  |
+ *       |  |  |  |__|  byte_mode
+ *        |  |__|   ||
+ *        |   ||   Cache_mode
+ *        |   Block_mode
+ *        Debug_mode
+ */
+
+#define RT_BYTE_MODE_MASK      (0x01)
+/* 1 byte for each register*/
+#define RT_SINGLE_BYTE         (0 << 0)
+/* multi bytes for each regiseter*/
+#define RT_MULTI_BYTE          BIT(0)
+
+#define RT_CACHE_MODE_MASK      (0x06)
+/* write to cache and chip synchronously */
+#define RT_CACHE_WR_THROUGH     (0 << 1)
+/* write to cache and chip asynchronously */
+#define RT_CACHE_WR_BACK        BIT(1)
+/* disable cache */
+#define RT_CACHE_DISABLE        (2 << 1)
+
+#define RT_IO_BLK_MODE_MASK (0x18)
+/* pass through all write function */
+#define RT_IO_PASS_THROUGH     (0 << 3)
+/* block all write function */
+#define RT_IO_BLK_ALL          BIT(3)
+/* block cache write function */
+#define RT_IO_BLK_CACHE                (2 << 3)
+/* block chip write function */
+#define RT_IO_BLK_CHIP         (3 << 3)
+
+#define DBG_MODE_MASK  (0x20)
+/* create general debugfs for register map */
+#define RT_DBG_GENERAL (0 << 5)
+/* create node for each regisetr map by register address*/
+#define RT_DBG_SPECIAL BIT(5)
+
+/* struct rt_register
+ *
+ * Ricktek register map structure for store mapping data
+ * @addr: register address.
+ * @name: register name.
+ * @size: register byte size.
+ * @reg_type: register R/W type ( RT_NORMAL, RT_WBITS, RT_VOLATILE, RT_RESERVE)
+ * @wbit_mask: register writeable bits mask;
+ * @cache_data: cache data for store cache value.
+ */
+struct rt_register {
+       u32 addr;
+       const char *name;
+       unsigned int size;
+       unsigned char reg_type;
+       unsigned char *wbit_mask;
+       unsigned char *cache_data;
+};
+
+/* Declare a rt_register by RT_REG_DECL
+ * @_addr: regisetr address.
+ * @_reg_length: register data length.
+ * @_reg_type: register type (rt_reg_type).
+ * @_mask: register writealbe mask.
+ */
+#define RT_REG_DECL(_addr, _reg_length, _reg_type, _mask_...)  \
+       static unsigned char rt_writable_mask_##_addr[_reg_length] = _mask_;\
+       static struct rt_register rt_register_##_addr = {       \
+               .addr = _addr, \
+               .size = _reg_length,\
+               .reg_type = _reg_type,\
+               .wbit_mask = rt_writable_mask_##_addr,\
+       }
+
+/* Declare a rt_register by RT_NAMED_REG_DECL
+ * @_name: a name for a rt_register.
+ */
+#define RT_NAMED_REG_DECL(_addr, _name, _reg_length, _reg_type, _mask_...) \
+       static unsigned char rt_writable_mask_##_addr[_reg_length] = _mask_;\
+       static struct rt_register rt_register_##_addr = {       \
+               .addr = _addr, \
+               .name = _name, \
+               .size = _reg_length,\
+               .reg_type = _reg_type,\
+               .wbit_mask = rt_writable_mask_##_addr,\
+       }
+
+typedef struct rt_register *rt_register_map_t;
+
+#define RT_REG(_addr) (&rt_register_##_addr)
+
+/* rt_regmap_properties
+ * @name: the name of debug node.
+ * @aliases: alisis name of rt_regmap_device.
+ * @register_num: the number of rt_register_map registers.
+ * @rm: rt_regiseter_map pointer array.
+ * @group: register map access group.
+ * @rt_format: default is little endian.
+ * @rt_regmap_mode: rt_regmap_device mode.
+ * @io_log_en: enable/disable io log
+ */
+struct rt_regmap_properties {
+       const char *name;
+       const char *aliases;
+       int register_num;
+       const rt_register_map_t *rm;
+       struct rt_access_group *group;
+       enum rt_data_format rt_format;
+       unsigned char rt_regmap_mode;
+       unsigned char io_log_en:1;
+};
+
+/* A passing struct for rt_regmap_reg_read and rt_regmap_reg_write function
+ * reg: regmap addr.
+ * mask: mask for update bits.
+ * rt_data: register value.
+ */
+struct rt_reg_data {
+       u32 reg;
+       u32 mask;
+       union {
+               u32 data_u32;
+               u16 data_u16;
+               u8 data_u8;
+               u8 data[4];
+       } rt_data;
+};
+
+struct rt_regmap_device;
+
+struct rt_debug_st {
+       void *info;
+       int id;
+};
+
+/* basic chip read/write function */
+struct rt_regmap_fops {
+       int (*read_device)(void *client, u32 addr, int leng, void *dst);
+       int (*write_device)(void *client, u32 addr, int leng, const void *src);
+};
+
+struct rt_regmap_device*
+       rt_regmap_device_register(struct rt_regmap_properties *props,
+                                 struct rt_regmap_fops *rops,
+                                 struct device *parent,
+                                 void *client, void *drvdata);
+
+void rt_regmap_device_unregister(struct rt_regmap_device *rd);
+
+int rt_regmap_cache_init(struct rt_regmap_device *rd);
+
+int rt_regmap_cache_reload(struct rt_regmap_device *rd);
+
+int rt_regmap_block_write(
+               struct rt_regmap_device *rd, u32 reg,
+               int bytes, const void *rc);
+int rt_asyn_regmap_block_write(
+               struct rt_regmap_device *rd, u32 reg,
+               int bytes, const void *rc);
+int rt_regmap_block_read(
+               struct rt_regmap_device *rd, u32 reg,
+               int bytes, void *dst);
+
+int _rt_regmap_reg_read(
+               struct rt_regmap_device *rd,
+               struct rt_reg_data *rrd);
+int _rt_regmap_reg_write(
+               struct rt_regmap_device *rd,
+               struct rt_reg_data *rrd);
+int _rt_asyn_regmap_reg_write(
+               struct rt_regmap_device *rd,
+               struct rt_reg_data *rrd);
+int _rt_regmap_update_bits(
+               struct rt_regmap_device *rd,
+               struct rt_reg_data *rrd);
+
+static inline int rt_regmap_reg_read(struct rt_regmap_device *rd,
+                                    struct rt_reg_data *rrd, u32 reg)
+{
+       rrd->reg = reg;
+       return _rt_regmap_reg_read(rd, rrd);
+};
+
+static inline int rt_regmap_reg_write(struct rt_regmap_device *rd,
+                                     struct rt_reg_data *rrd,
+                                         u32 reg, const u32 data)
+{
+       rrd->reg = reg;
+       rrd->rt_data.data_u32 = data;
+       return _rt_regmap_reg_write(rd, rrd);
+};
+
+static inline int rt_asyn_regmap_reg_write(struct rt_regmap_device *rd,
+                                          struct rt_reg_data *rrd,
+                                          u32 reg, const u32 data)
+{
+       rrd->reg = reg;
+       rrd->rt_data.data_u32 = data;
+       return _rt_asyn_regmap_reg_write(rd, rrd);
+};
+
+static inline int rt_regmap_update_bits(struct rt_regmap_device *rd,
+                                       struct rt_reg_data *rrd,
+                                       u32 reg, u32 mask, u32 data)
+{
+       rrd->reg = reg;
+       rrd->mask = mask;
+       rrd->rt_data.data_u32 = data;
+       return _rt_regmap_update_bits(rd, rrd);
+}
+
+void rt_regmap_cache_backup(struct rt_regmap_device *rd);
+
+void rt_regmap_cache_sync(struct rt_regmap_device *rd);
+void rt_regmap_cache_write_back(struct rt_regmap_device *rd, u32 reg);
+
+int rt_is_reg_readable(struct rt_regmap_device *rd, u32 reg);
+int rt_is_reg_volatile(struct rt_regmap_device *rd, u32 reg);
+int rt_get_regsize(struct rt_regmap_device *rd, u32 reg);
+void rt_cache_getlasterror(struct rt_regmap_device *rd, char *buf);
+void rt_cache_clrlasterror(struct rt_regmap_device *rd);
+
+int rt_regmap_add_debugfs(
+               struct rt_regmap_device *rd, const char *name,
+               umode_t mode, void *data,
+               const struct file_operations *fops);
+
+#define to_rt_regmap_device(obj) container_of(obj, struct rt_regmap_device, dev)
+
+#endif /*LINUX_MISC_RT_REGMAP_H*/
diff --git a/include/linux/hisi/usb/pd/richtek/rt1711h.h b/include/linux/hisi/usb/pd/richtek/rt1711h.h
new file mode 100644 (file)
index 0000000..78ba3c8
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_RT1711H_H
+#define __LINUX_RT1711H_H
+
+#include <linux/hisi/usb/pd/richtek/std_tcpci_v10.h>
+/*show debug message or not */
+#define ENABLE_RT1711_DBG      0
+
+/* RT1711H Private RegMap */
+
+#define RT1711H_REG_CLK_CTRL2                          (0x87)
+#define RT1711H_REG_CLK_CTRL3                          (0x88)
+
+#define RT1711H_REG_BMC_CTRL                           (0x90)
+#define RT1711H_REG_BMCIO_RXDZSEL                      (0x93)
+#define RT1711H_REG_VCONN_CLIMITEN                     (0x95)
+
+#define RT1711H_REG_RT_STATUS                          (0x97)
+#define RT1711H_REG_RT_INT                                     (0x98)
+#define RT1711H_REG_RT_MASK                                    (0x99)
+
+#define RT1711H_REG_IDLE_CTRL                          (0x9B)
+#define RT1711H_REG_INTRST_CTRL                                (0x9C)
+#define RT1711H_REG_WATCHDOG_CTRL                      (0x9D)
+#define RT1711H_REG_I2CRST_CTRL                                (0X9E)
+
+#define RT1711H_REG_SWRESET                            (0xA0)
+#define RT1711H_REG_TTCPC_FILTER                       (0xA1)
+#define RT1711H_REG_DRP_TOGGLE_CYCLE           (0xA2)
+#define RT1711H_REG_DRP_DUTY_CTRL                      (0xA3)
+
+/*
+ * Device ID
+ */
+
+#define RT1711H_DID_A          0x2170
+#define RT1711H_DID_B          0x2171
+#define RT1711H_DID_C          0x2172
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/*
+ * RT1711H_REG_CLK_CTRL2                       (0x87)
+ */
+
+#define RT1711H_REG_CLK_DIV_600K_EN            BIT(7)
+#define RT1711H_REG_CLK_BCLK2_EN               BIT(6)
+#define RT1711H_REG_CLK_BCLK2_TG_EN            BIT(5)
+#define RT1711H_REG_CLK_DIV_300K_EN            BIT(3)
+#define RT1711H_REG_CLK_CK_300K_EN             BIT(2)
+#define RT1711H_REG_CLK_BCLK_EN                        BIT(1)
+#define RT1711H_REG_CLK_BCLK_TH_EN             BIT(0)
+
+/*
+ * RT1711H_REG_CLK_CTRL3                       (0x88)
+ */
+
+#define RT1711H_REG_CLK_OSCMUX_RG_EN           BIT(7)
+#define RT1711H_REG_CLK_CK_24M_EN              BIT(6)
+#define RT1711H_REG_CLK_OSC_RG_EN              BIT(5)
+#define RT1711H_REG_CLK_DIV_2P4M_EN            BIT(4)
+#define RT1711H_REG_CLK_CK_2P4M_EN             BIT(3)
+#define RT1711H_REG_CLK_PCLK_EN                        BIT(2)
+#define RT1711H_REG_CLK_PCLK_RG_EN             BIT(1)
+#define RT1711H_REG_CLK_PCLK_TG_EN             BIT(0)
+
+/*
+ * RT1711H_REG_BMC_CTRL                                (0x90)
+ */
+
+#define RT1711H_REG_IDLE_EN                            BIT(6)
+#define RT1711H_REG_DISCHARGE_EN                       BIT(5)
+#define RT1711H_REG_BMCIO_LPRPRD                       BIT(4)
+#define RT1711H_REG_BMCIO_LPEN                         BIT(3)
+#define RT1711H_REG_BMCIO_BG_EN                                BIT(2)
+#define RT1711H_REG_VBUS_DET_EN                                BIT(1)
+#define RT1711H_REG_BMCIO_OSC_EN                       BIT(0)
+
+/*
+ * RT1711H_REG_RT_STATUS                               (0x97)
+ */
+
+#define RT1711H_REG_RA_DETACH                          BIT(5)
+#define RT1711H_REG_VBUS_80                            BIT(1)
+
+/*
+ * RT1711H_REG_RT_INT                          (0x98)
+ */
+
+#define RT1711H_REG_INT_RA_DETACH                      BIT(5)
+#define RT1711H_REG_INT_WATCHDOG                       BIT(2)
+#define RT1711H_REG_INT_VBUS_80                                BIT(1)
+#define RT1711H_REG_INT_WAKEUP                         BIT(0)
+
+/*
+ * RT1711H_REG_RT_MASK                         (0x99)
+ */
+
+#define RT1711H_REG_M_RA_DETACH                                BIT(5)
+#define RT1711H_REG_M_WATCHDOG                         BIT(2)
+#define RT1711H_REG_M_VBUS_80                          BIT(1)
+#define RT1711H_REG_M_WAKEUP                           BIT(0)
+
+/*
+ * RT1711H_REG_IDLE_CTRL                               (0x9B)
+ */
+
+#define RT1711H_REG_CK_300K_SEL                                BIT(7)
+#define RT1711H_REG_SHIPPING_OFF                       BIT(5)
+#define RT1711H_REG_AUTOIDLE_EN                                BIT(3)
+
+/* timeout = (tout*2+1) * 6.4ms */
+#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \
+       (((ck300) << 7) | ((ship_dis) << 5) | \
+        ((auto_idle) << 3) | ((tout) & 0x07))
+
+/*
+ * RT1711H_REG_INTRST_CTRL                     (0x9C)
+ */
+
+#define RT1711H_REG_INTRST_EN                          BIT(7)
+
+/* timeout = (tout+1) * 0.2sec */
+#define RT1711H_REG_INTRST_SET(en, tout) \
+       (((en) << 7) | ((tout) & 0x03))
+
+/*
+ * RT1711H_REG_WATCHDOG_CTRL           (0x9D)
+ */
+
+#define RT1711H_REG_WATCHDOG_EN                                BIT(7)
+
+/* timeout = (tout+1) * 0.4sec */
+#define RT1711H_REG_WATCHDOG_CTRL_SET(en, tout)        \
+       (((en) << 7) | ((tout) & 0x07))
+
+#if ENABLE_RT1711_DBG
+#define RT1711H_INFO hisilog_err
+#else
+#define RT1711_INFO(foramt, args...)
+#endif
+
+#endif /* #ifndef __LINUX_RT1711H_H */
diff --git a/include/linux/hisi/usb/pd/richtek/std_tcpci_v10.h b/include/linux/hisi/usb/pd/richtek/std_tcpci_v10.h
new file mode 100644 (file)
index 0000000..a50c66e
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef STD_TCPCI_V10_H_
+#define STD_TCPCI_V10_H_
+
+/* Standard TCPC V10 RegMap */
+
+#define TCPC_V10_REG_VID                               (0x00)
+#define TCPC_V10_REG_PID                               (0x02)
+#define TCPC_V10_REG_DID                               (0x04)
+#define TCPC_V10_REG_TYPEC_REV                         (0x06)
+#define TCPC_V10_REG_PD_REV                            (0x08)
+#define TCPC_V10_REG_PDIF_REV                          (0x0A)
+
+#define TCPC_V10_REG_ALERT                             (0x10)
+#define TCPC_V10_REG_ALERT_MASK                                (0x12)
+#define TCPC_V10_REG_POWER_STATUS_MASK                 (0x14)
+#define TCPC_V10_REG_FAULT_STATUS_MASK                 (0x15)
+
+#define TCPC_V10_REG_TCPC_CTRL                         (0x19)
+#define TCPC_V10_REG_ROLE_CTRL                         (0x1A)
+#define TCPC_V10_REG_FAULT_CTRL                                (0x1B)
+#define TCPC_V10_REG_POWER_CTRL                                (0x1C)
+
+#define TCPC_V10_REG_CC_STATUS                         (0x1D)
+#define TCPC_V10_REG_POWER_STATUS                      (0x1E)
+#define TCPC_V10_REG_FAULT_STATUS                      (0x1F)
+
+#define TCPC_V10_REG_COMMAND                           (0x23)
+
+#define TCPC_V10_REG_MSG_HDR_INFO                      (0x2e)
+
+#define TCPC_V10_REG_RX_DETECT                         (0x2f)
+
+#define TCPC_V10_REG_RX_BYTE_CNT                       (0x30)
+#define TCPC_V10_REG_RX_BUF_FRAME_TYPE                 (0x31)
+#define TCPC_V10_REG_RX_HDR                            (0x32)
+#define TCPC_V10_REG_RX_DATA                           (0x34)
+
+#define TCPC_V10_REG_TRANSMIT                          (0x50)
+#define TCPC_V10_REG_TX_BYTE_CNT                       (0x51)
+#define TCPC_V10_REG_TX_HDR                            (0x52)
+#define TCPC_V10_REG_TX_DATA                           (0x54)/* through 0x6f */
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/*
+ * TCPC_V10_REG_ALERT                          (0x10)
+ * TCPC_V10_REG_ALERT_MASK             (0x12)
+ */
+#define TCPC_V10_REG_VBUS_SINK_DISCONNECT              BIT(11)
+#define TCPC_V10_REG_RX_OVERFLOW                       BIT(10)
+#define TCPC_V10_REG_ALERT_FAULT                       BIT(9)
+#define TCPC_V10_REG_ALERT_LO_VOLT                     BIT(8)
+#define TCPC_V10_REG_ALERT_HI_VOLT                     BIT(7)
+#define TCPC_V10_REG_ALERT_TX_SUCCESS                  BIT(6)
+#define TCPC_V10_REG_ALERT_TX_DISCARDED                        BIT(5)
+#define TCPC_V10_REG_ALERT_TX_FAILED                   BIT(4)
+#define TCPC_V10_REG_ALERT_RX_HARD_RST                 BIT(3)
+#define TCPC_V10_REG_ALERT_RX_STATUS                   BIT(2)
+#define TCPC_V10_REG_ALERT_POWER_STATUS                        BIT(1)
+#define TCPC_V10_REG_ALERT_CC_STATUS                   BIT(0)
+
+/*
+ * TCPC_V10_REG_POWER_STATUS_MASK      (0x14)
+ * TCPC_V10_REG_POWER_STATUS                   (0x19)
+ */
+
+#define TCPC_V10_REG_POWER_STATUS_TCPC_INITIAL         BIT(6)
+#define TCPC_V10_REG_POWER_STATUS_SRC_HV               BIT(5)
+#define TCPC_V10_REG_POWER_STATUS_SRC_VBUS             BIT(4)
+#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES_DET                BIT(3)
+#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES            BIT(2)
+#define TCPC_V10_REG_POWER_STATUS_VCONN_PRES           BIT(1)
+#define TCPC_V10_REG_POWER_STATUS_SINK_VBUS            BIT(0)
+
+/*
+ * TCPC_V10_REG_FAULT_STATUS_MASK      (0x15)
+ * TCPC_V10_REG_FAULT_STATUS                   (0x1F)
+ */
+
+#define TCPC_V10_REG_FAULT_STATUS_VCONN_OV             BIT(7)
+#define TCPC_V10_REG_FAULT_STATUS_FORCE_OFF_VBUS       BIT(6)
+#define TCPC_V10_REG_FAULT_STATUS_AUTO_DISC_FAIL       BIT(5)
+#define TCPC_V10_REG_FAULT_STATUS_FORCE_DISC_FAIL      BIT(4)
+#define TCPC_V10_REG_FAULT_STATUS_VBUS_OC              BIT(3)
+#define TCPC_V10_REG_FAULT_STATUS_VBUS_OV              BIT(2)
+#define TCPC_V10_REG_FAULT_STATUS_VCONN_OC             BIT(1)
+#define TCPC_V10_REG_FAULT_STATUS_I2C_ERROR            BIT(0)
+
+/*
+ * TCPC_V10_REG_ROLE_CTRL                      (0x1A)
+ */
+
+#define TCPC_V10_REG_ROLE_CTRL_DRP                     BIT(6)
+
+#define TCPC_V10_REG_ROLE_CTRL_RES_SET(drp, rp, cc1, cc2) \
+               ((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1))
+
+#define CC_RD  0x02
+#define CC_RP  0x01
+#define CC_OPEN        0x03
+#define CC_RA  0x00
+
+/*
+ * TCPC_V10_REG_TCPC_CTRL                      (0x19)
+ */
+
+#define TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE  BIT(1)
+#define TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT     BIT(0)
+
+/*
+ * TCPC_V10_REG_FAULT_CTRL             (0x1B)
+ */
+
+#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OV   BIT(7)
+#define TCPC_V10_REG_FAULT_CTRL_DIS_SNK_VBUS_OC        BIT(2)
+#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OC   BIT(0)
+
+/*
+ * TCPC_V10_REG_POWER_CTRL             (0x1C)
+ */
+
+#define TCPC_V10_REG_POWER_CTRL_VCONN          BIT(0)
+
+/*
+ * TCPC_V10_REG_CC_STATUS                      (0x1D)
+ */
+
+#define TCPC_V10_REG_CC_STATUS_DRP_TOGGLING            BIT(5)
+#define TCPC_V10_REG_CC_STATUS_DRP_RESULT(reg) (((reg) & 0x10) >> 4)
+#define TCPC_V10_REG_CC_STATUS_CC2(reg)  (((reg) & 0xc) >> 2)
+#define TCPC_V10_REG_CC_STATUS_CC1(reg)  ((reg) & 0x3)
+
+/*
+ * TCPC_V10_REG_COMMAND                        (0x23)
+ */
+
+enum tcpm_v10_command {
+       TCPM_CMD_WAKE_I2C = 0x11,
+       TCPM_CMD_DISABLE_VBUS_DETECT = 0x22,
+       TCPM_CMD_ENABLE_VBUS_DETECT = 0x33,
+       TCPM_CMD_DISABLE_SINK_VBUS = 0x44,
+       TCPM_CMD_ENABLE_SINK_VBUS = 0x55,
+       TCPM_CMD_DISABLE_SOURCE_VBUS = 0x66,
+       TCPM_CMD_ENABLE_SOURCE_VBUS = 0x77,
+       TCPM_CMD_SOURCE_VBUS_HV = 0x88,
+       TCPM_CMD_LOOK_CONNECTION = 0x99,
+       TCPM_CMD_RX_ONE_MODE = 0xAA,
+       TCPM_CMD_I2C_IDLE = 0xFF,
+};
+
+/*
+ * TCPC_V10_REG_MSG_HDR_INFO           (0x2e)
+ */
+
+#define TCPC_V10_REG_MSG_HDR_INFO_SET(drole, prole) \
+               ((drole) << 3 | (PD_REV20 << 1) | (prole))
+#define TCPC_V10_REG_MSG_HDR_INFO_DROLE(reg) (((reg) & 0x8) >> 3)
+#define TCPC_V10_REG_MSG_HDR_INFO_PROLE(reg) ((reg) & 0x1)
+
+/*
+ * TCPC_V10_REG_TRANSMIT                               (0x50)
+ */
+
+#define TCPC_V10_REG_TRANSMIT_SET(type) \
+               (PD_RETRY_COUNT << 4 | (type))
+
+#endif /* STD_TCPCI_V10_H_ */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci.h b/include/linux/hisi/usb/pd/richtek/tcpci.h
new file mode 100644 (file)
index 0000000..23f0d1e
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_RT_TCPC_H
+#define __LINUX_RT_TCPC_H
+
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <linux/delay.h>
+
+#include <uapi/linux/sched/types.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_core.h>
+#ifdef CONFIG_USB_POWER_DELIVERY
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#define PE_STATE_FULL_NAME     0
+
+/* provide to TCPC interface */
+int tcpci_report_usb_port_changed(struct tcpc_device *tcpc);
+int tcpc_typec_init(struct tcpc_device *tcpc, u8 typec_role);
+void tcpc_typec_deinit(struct tcpc_device *tcpc);
+int tcpc_dual_role_phy_init(struct tcpc_device *tcpc);
+
+struct tcpc_device *tcpc_device_register(
+               struct device *parent, struct tcpc_desc *tcpc_desc,
+               struct tcpc_ops *ops, void *drv_data);
+void tcpc_device_unregister(
+                       struct device *dev, struct tcpc_device *tcpc);
+
+int tcpc_schedule_init_work(struct tcpc_device *tcpc);
+
+void *tcpc_get_dev_data(struct tcpc_device *tcpc);
+void tcpci_lock_typec(struct tcpc_device *tcpc);
+void tcpci_unlock_typec(struct tcpc_device *tcpc);
+int tcpci_alert(struct tcpc_device *tcpc);
+
+void tcpci_vbus_level_init(
+               struct tcpc_device *tcpc, u16 power_status);
+
+static inline int tcpci_check_vbus_valid(struct tcpc_device *tcpc)
+{
+       return tcpc->vbus_level >= TCPC_VBUS_VALID;
+}
+
+static inline int tcpci_check_vsafe0v(struct tcpc_device *tcpc, bool detect_en)
+{
+       int ret = 0;
+
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
+       ret = (tcpc->vbus_level == TCPC_VBUS_SAFE0V);
+#else
+       ret = (tcpc->vbus_level == TCPC_VBUS_INVALID);
+#endif
+
+       return ret;
+}
+
+static inline int tcpci_alert_status_clear(
+               struct tcpc_device *tcpc, u32 mask)
+{
+       return tcpc->ops->alert_status_clear(tcpc, mask);
+}
+
+static inline int tcpci_fault_status_clear(
+       struct tcpc_device *tcpc, u8 status)
+{
+       if (tcpc->ops->fault_status_clear)
+               return tcpc->ops->fault_status_clear(tcpc, status);
+       return 0;
+}
+
+static inline int tcpci_get_alert_status(
+               struct tcpc_device *tcpc, u32 *alert)
+{
+       return tcpc->ops->get_alert_status(tcpc, alert);
+}
+
+static inline int tcpci_get_fault_status(
+               struct tcpc_device *tcpc, u8 *fault)
+{
+       if (tcpc->ops->get_fault_status)
+               return tcpc->ops->get_fault_status(tcpc, fault);
+       *fault = 0;
+       return 0;
+}
+
+static inline int tcpci_get_power_status(
+               struct tcpc_device *tcpc, u16 *pw_status)
+{
+       return tcpc->ops->get_power_status(tcpc, pw_status);
+}
+
+static inline int tcpci_init(struct tcpc_device *tcpc, bool sw_reset)
+{
+       int ret;
+       u16 power_status;
+
+       ret = tcpc->ops->init(tcpc, sw_reset);
+       if (ret)
+               return ret;
+
+       ret = tcpci_get_power_status(tcpc, &power_status);
+       if (ret)
+               return ret;
+
+       tcpci_vbus_level_init(tcpc, power_status);
+       return 0;
+}
+
+static inline int tcpci_get_cc(struct tcpc_device *tcpc)
+{
+       int ret, cc1, cc2;
+
+       ret = tcpc->ops->get_cc(tcpc, &cc1, &cc2);
+       if (ret < 0)
+               return ret;
+
+       if ((cc1 == tcpc->typec_remote_cc[0]) &&
+           (cc2 == tcpc->typec_remote_cc[1])) {
+               return 0;
+       }
+
+       tcpc->typec_remote_cc[0] = cc1;
+       tcpc->typec_remote_cc[1] = cc2;
+       return 1;
+}
+
+static inline int tcpci_set_cc(struct tcpc_device *tcpc, int pull)
+{
+#ifdef CONFIG_USB_PD_DBG_ALWAYS_LOCAL_RP
+       if (pull == TYPEC_CC_RP)
+               pull = tcpc->typec_local_rp_level;
+#endif /* CONFIG_USB_PD_DBG_ALWAYS_LOCAL_RP */
+
+       if (pull & TYPEC_CC_DRP) {
+               tcpc->typec_remote_cc[0] =
+               tcpc->typec_remote_cc[1] =
+                       TYPEC_CC_DRP_TOGGLING;
+       }
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       if ((pull == TYPEC_CC_DRP) && (tcpc->typec_legacy_cable)) {
+               TCPC_INFO("LegacyCable-->\r\n");
+               pull = TYPEC_CC_RP_1_5;
+       }
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+       tcpc->typec_local_cc = pull;
+       return tcpc->ops->set_cc(tcpc, pull);
+}
+
+static inline int tcpci_set_polarity(struct tcpc_device *tcpc, int polarity)
+{
+       return tcpc->ops->set_polarity(tcpc, polarity);
+}
+
+static inline int tcpci_set_vconn(struct tcpc_device *tcpc, int enable)
+{
+       struct tcp_notify tcp_noti;
+
+       tcp_noti.en_state.en = enable != 0;
+       srcu_notifier_call_chain(&tcpc->evt_nh,
+                                TCP_NOTIFY_SOURCE_VCONN, &tcp_noti);
+
+       return tcpc->ops->set_vconn(tcpc, enable);
+}
+
+static inline int tcpci_set_low_power_mode(
+       struct tcpc_device *tcpc, bool en, int pull)
+{
+       int rv = 0;
+
+#ifdef CONFIG_TCPC_LOW_POWER_MODE
+       rv = tcpc->ops->set_low_power_mode(tcpc, en, pull);
+#endif
+       return rv;
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+static inline int tcpci_set_msg_header(
+       struct tcpc_device *tcpc, int power_role, int data_role)
+{
+       return tcpc->ops->set_msg_header(tcpc, power_role, data_role);
+}
+
+static inline int tcpci_set_rx_enable(struct tcpc_device *tcpc, u8 enable)
+{
+       return tcpc->ops->set_rx_enable(tcpc, enable);
+}
+
+static inline int tcpci_get_message(struct tcpc_device *tcpc,
+                                   u32 *payload, u16 *head,
+                                       enum tcpm_transmit_type *type)
+{
+       return tcpc->ops->get_message(tcpc, payload, head, type);
+}
+
+static inline int tcpci_transmit(struct tcpc_device *tcpc,
+                                enum tcpm_transmit_type type,
+                                u16 header, const u32 *data)
+{
+       return tcpc->ops->transmit(tcpc, type, header, data);
+}
+
+static inline int tcpci_set_bist_test_mode(struct tcpc_device *tcpc, bool en)
+{
+       return tcpc->ops->set_bist_test_mode(tcpc, en);
+}
+
+static inline int tcpci_set_bist_carrier_mode(
+               struct tcpc_device *tcpc, u8 pattern)
+{
+       if (pattern)    /* wait for GoodCRC */
+               usleep_range(240, 260);
+
+       return tcpc->ops->set_bist_carrier_mode(tcpc, pattern);
+}
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+static inline int tcpci_retransmit(struct tcpc_device *tcpc)
+{
+       return tcpc->ops->retransmit(tcpc);
+}
+#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+static inline int tcpci_notify_typec_state(
+       struct tcpc_device *tcpc)
+{
+       struct pd_dpm_typec_state typec_state;
+
+       typec_state.polarity = tcpc->typec_polarity;
+       typec_state.old_state = tcpc->typec_attach_old;
+       typec_state.new_state = tcpc->typec_attach_new;
+
+       pd_dpm_handle_pe_event(PD_DPM_PE_EVT_TYPEC_STATE, &typec_state);
+       return 0;
+}
+
+static inline int tcpci_notify_role_swap(
+       struct tcpc_device *tcpc, u8 event, u8 role)
+{
+#if 1
+       u8 dpm_event;
+       struct pd_dpm_swap_state swap_state;
+
+       switch (event) {
+       case TCP_NOTIFY_DR_SWAP:
+               dpm_event = PD_DPM_PE_EVT_DR_SWAP;
+               break;
+       case TCP_NOTIFY_PR_SWAP:
+               dpm_event = PD_DPM_PE_EVT_PR_SWAP;
+               break;
+       case TCP_NOTIFY_VCONN_SWAP:
+               dpm_event = PD_DPM_PE_EVT_VCONN_SWAP;
+               break;
+       default:
+               return 0;
+       }
+
+       swap_state.new_role = role;
+       return pd_dpm_handle_pe_event(event, &swap_state);
+#else
+       return 0;
+#endif
+}
+
+static inline int tcpci_notify_pd_state(
+       struct tcpc_device *tcpc, u8 connect)
+{
+       struct pd_dpm_pd_state pd_state;
+
+       pd_state.connected = connect;
+       return pd_dpm_handle_pe_event(
+               PD_DPM_PE_EVT_PD_STATE, &pd_state);
+}
+
+static inline int tcpci_disable_vbus_control(struct tcpc_device *tcpc)
+{
+       hisilog_err("%s: !!!++++++++\n", __func__);
+#ifdef CONFIG_TYPEC_USE_DIS_VBUS_CTRL
+       TCPC_DBG("disable_vbus\r\n");
+       pd_dpm_handle_pe_event(PD_DPM_PE_EVT_DIS_VBUS_CTRL, NULL);
+       return 0;
+#else
+       tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SINK_0V, 0);
+       tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SOURCE_0V, 0);
+       return 0;
+#endif
+       hisilog_err("%s: !!!-----------\n",
+                   __func__);
+}
+
+static inline int tcpci_source_vbus(
+       struct tcpc_device *tcpc, u8 type, int mv, int ma)
+{
+       struct pd_dpm_vbus_state vbus_state;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (type >= TCP_VBUS_CTRL_PD && tcpc->pd_port.pd_prev_connected)
+               type |= TCP_VBUS_CTRL_PD_DETECT;
+#endif
+
+       if (ma < 0) {
+               if (mv != 0) {
+                       switch (tcpc->typec_local_rp_level) {
+                       case TYPEC_CC_RP_1_5:
+                               ma = 1500;
+                               break;
+                       case TYPEC_CC_RP_3_0:
+                               ma = 3000;
+                               break;
+                       default:
+                       case TYPEC_CC_RP_DFT:
+                               ma = 500;
+                               break;
+                       }
+               } else {
+                       ma = 0;
+               }
+       }
+
+       vbus_state.ma = ma;
+       vbus_state.mv = mv;
+       vbus_state.vbus_type = type;
+
+       TCPC_DBG("source_vbus: %d mV, %d mA\r\n", ma, mv);
+       pd_dpm_handle_pe_event(PD_DPM_PE_EVT_SOURCE_VBUS, &vbus_state);
+       return 0;
+}
+
+static inline int tcpci_sink_vbus(
+       struct tcpc_device *tcpc, u8 type, int mv, int ma)
+{
+       struct pd_dpm_vbus_state vbus_state;
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       if (type >= TCP_VBUS_CTRL_PD && tcpc->pd_port.pd_prev_connected)
+               type |= TCP_VBUS_CTRL_PD_DETECT;
+#endif
+
+       if (ma < 0) {
+               if (mv != 0) {
+                       switch (tcpc->typec_remote_rp_level) {
+                       case TYPEC_CC_VOLT_SNK_1_5:
+                               ma = 1500;
+                               break;
+                       case TYPEC_CC_VOLT_SNK_3_0:
+                               ma = 3000;
+                               break;
+                       default:
+                       case TYPEC_CC_VOLT_SNK_DFT:
+                               ma = 500;
+                               break;
+                       }
+               } else {
+                       ma = 0;
+               }
+       }
+
+       vbus_state.ma = ma;
+       vbus_state.mv = mv;
+       vbus_state.vbus_type = type;
+
+       TCPC_DBG("sink_vbus: %d mV, %d mA\r\n", ma, mv);
+       pd_dpm_handle_pe_event(PD_DPM_PE_EVT_SINK_VBUS, &vbus_state);
+       return 0;
+}
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+static inline int tcpci_enter_mode(struct tcpc_device *tcpc,
+                                  u16 svid, u8 ops, u32 mode)
+{
+       /* DFP_U : DisplayPort Mode, USB Configuration */
+       TCPC_INFO("EnterMode\r\n");
+       return 0;
+}
+
+static inline int tcpci_exit_mode(
+       struct tcpc_device *tcpc, u16 svid)
+{
+       TCPC_INFO("ExitMode\r\n");
+       return 0;
+}
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#endif /* #ifndef __LINUX_RT_TCPC_H */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci_config.h b/include/linux/hisi/usb/pd/richtek/tcpci_config.h
new file mode 100644 (file)
index 0000000..52037d2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_TCPC_CONFIG_H
+#define __LINUX_TCPC_CONFIG_H
+
+/* default config */
+
+#define CONFIG_RT_REGMAP
+
+#define CONFIG_TYPEC_USE_DIS_VBUS_CTRL
+#define CONFIG_TYPEC_POWER_CTRL_INIT
+
+#define CONFIG_TYPEC_CAP_TRY_SOURCE
+#define CONFIG_TYPEC_CAP_TRY_SINK
+
+#define CONFIG_TYPEC_CAP_DBGACC_SNK
+#define CONFIG_TYPEC_CAP_CUSTOM_SRC
+
+#define CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
+
+#define CONFIG_TYPEC_CHECK_LEGACY_CABLE
+
+#define CONFIG_TYPEC_CAP_RA_DETACH
+#define CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
+
+#define CONFIG_TYPEC_CAP_POWER_OFF_CHARGE
+
+#define CONFIG_TCPC_VSAFE0V_DETECT
+#define CONFIG_TCPC_VSAFE0V_DETECT_IC
+#define CONFIG_TCPC_LOW_POWER_MODE
+#define CONFIG_TCPC_CLOCK_GATING
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+#define CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
+#define CONFIG_USB_PD_DFP_READY_DISCOVER_ID
+
+#define CONFIG_USB_PD_ATTEMP_DISCOVER_ID
+#define CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
+
+#define CONFIG_USB_PD_CUSTOM_DBGACC
+
+#define CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC
+
+#define CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER
+#define CONFIG_USB_PD_DROP_REPEAT_PING
+#define CONFIG_USB_PD_RETRY_CRC_DISCARD
+#define CONFIG_USB_PD_TRANSMIT_BIST2
+#define CONFIG_USB_PD_POSTPONE_VDM
+#define CONFIG_USB_PD_POSTPONE_RETRY_VDM
+#define CONFIG_USB_PD_POSTPONE_FIRST_VDM
+#define CONFIG_USB_PD_POSTPONE_OTHER_VDM
+#define CONFIG_USB_PD_SAFE0V_TIMEOUT
+
+#ifndef CONFIG_USB_PD_DFP_FLOW_RETRY_MAX
+#define CONFIG_USB_PD_DFP_FLOW_RETRY_MAX 2
+#endif /* CONFIG_USB_PD_DFP_FLOW_RETRY_MAX */
+
+#ifndef CONFIG_USB_PD_VBUS_STABLE_TOUT
+#define CONFIG_USB_PD_VBUS_STABLE_TOUT 125
+#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */
+
+#ifndef CONFIG_USB_PD_VBUS_PRESENT_TOUT
+#define CONFIG_USB_PD_VBUS_PRESENT_TOUT        20
+#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+#endif /* __LINUX_TCPC_CONFIG_H */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci_core.h b/include/linux/hisi/usb/pd/richtek/tcpci_core.h
new file mode 100644 (file)
index 0000000..e7d9a32
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_RT_TCPCI_CORE_H
+#define __LINUX_RT_TCPCI_CORE_H
+
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/workqueue.h>
+#include <linux/notifier.h>
+#include <linux/semaphore.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpm.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_timer.h>
+#include <linux/hisi/usb/pd/richtek/tcpci_config.h>
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+#include <linux/hisi/usb/pd/richtek/pd_core.h>
+#endif
+
+/* The switch of log message */
+#define TYPEC_INFO_ENABLE      1
+#define PE_EVENT_DBG_ENABLE    0
+#define PE_STATE_INFO_ENABLE   0
+#define TCPC_INFO_ENABLE       1
+#define TCPC_TIMER_DBG_EN      0
+#define TCPC_TIMER_INFO_EN     0
+#define PE_INFO_ENABLE         1
+#define TCPC_DBG_ENABLE                0
+#define DPM_DBG_ENABLE         0
+#define PD_ERR_ENABLE          1
+#define PE_DBG_ENABLE          0
+#define TYPEC_DBG_ENABLE       0
+
+#define TCPC_ENABLE_ANYMSG     (TCPC_DBG_ENABLE | DPM_DBG_ENABLE | \
+               PD_ERR_ENABLE | PE_INFO_ENABLE | TCPC_TIMER_INFO_EN\
+               | PE_DBG_ENABLE | PE_EVENT_DBG_ENABLE | \
+               PE_STATE_INFO_ENABLE | TCPC_INFO_ENABLE | \
+               TCPC_TIMER_DBG_EN | TYPEC_DBG_ENABLE | \
+               TYPEC_INFO_ENABLE)
+
+#define PE_EVT_INFO_VDM_DIS            0
+#define PE_DBG_RESET_VDM_DIS   1
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+struct tcpc_device;
+
+struct tcpc_desc {
+       u8 role_def;
+       u8 rp_lvl;
+       int notifier_supply_num;
+       char *name;
+};
+
+/* TCPC Power Register Define */
+#define TCPC_REG_POWER_STATUS_EXT_VSAFE0V      BIT(15) /* extend */
+#define TCPC_REG_POWER_STATUS_VBUS_PRES                BIT(2)
+
+/* TCPC Alert Register Define */
+#define TCPC_REG_ALERT_EXT_RA_DETACH           (1 << (16 + 5))
+#define TCPC_REG_ALERT_EXT_WATCHDOG            (1 << (16 + 2))
+#define TCPC_REG_ALERT_EXT_VBUS_80             (1 << (16 + 1))
+#define TCPC_REG_ALERT_EXT_WAKEUP              (1 << (16 + 0))
+
+#define TCPC_REG_ALERT_VBUS_DISCNCT BIT(11)
+#define TCPC_REG_ALERT_RX_BUF_OVF   BIT(10)
+#define TCPC_REG_ALERT_FAULT        BIT(9)
+#define TCPC_REG_ALERT_V_ALARM_LO   BIT(8)
+#define TCPC_REG_ALERT_V_ALARM_HI   BIT(7)
+#define TCPC_REG_ALERT_TX_SUCCESS   BIT(6)
+#define TCPC_REG_ALERT_TX_DISCARDED BIT(5)
+#define TCPC_REG_ALERT_TX_FAILED    BIT(4)
+#define TCPC_REG_ALERT_RX_HARD_RST  BIT(3)
+#define TCPC_REG_ALERT_RX_STATUS    BIT(2)
+#define TCPC_REG_ALERT_POWER_STATUS BIT(1)
+#define TCPC_REG_ALERT_CC_STATUS    BIT(0)
+#define TCPC_REG_ALERT_TX_COMPLETE  (TCPC_REG_ALERT_TX_SUCCESS | \
+               TCPC_REG_ALERT_TX_DISCARDED | \
+               TCPC_REG_ALERT_TX_FAILED)
+
+/* TCPC Behavior Flags */
+#define TCPC_FLAGS_RETRY_CRC_DISCARD           BIT(0)
+#define TCPC_FLAGS_WAIT_HRESET_COMPLETE                BIT(1)
+#define TCPC_FLAGS_CHECK_CC_STABLE             BIT(2)
+#define TCPC_FLAGS_LPM_WAKEUP_WATCHDOG         BIT(3)
+#define TCPC_FLAGS_CHECK_RA_DETACHE            BIT(4)
+
+enum tcpc_cc_pull {
+       TYPEC_CC_RA = 0,
+       TYPEC_CC_RP = 1,
+       TYPEC_CC_RD = 2,
+       TYPEC_CC_OPEN = 3,
+       TYPEC_CC_DRP = 4,       /* from Rd */
+
+       TYPEC_CC_RP_DFT = 1,            /* 0x00 + 1 */
+       TYPEC_CC_RP_1_5 = 9,            /* 0x08 + 1*/
+       TYPEC_CC_RP_3_0 = 17,           /* 0x10 + 1 */
+
+       TYPEC_CC_DRP_DFT = 4,           /* 0x00 + 4 */
+       TYPEC_CC_DRP_1_5 = 12,          /* 0x08 + 4 */
+       TYPEC_CC_DRP_3_0 = 20,          /* 0x10 + 4 */
+};
+
+#define TYPEC_CC_PULL_GET_RES(pull)            ((pull) & 0x07)
+#define TYPEC_CC_PULL_GET_RP_LVL(pull) (((pull) & 0x18) >> 3)
+
+enum tcpm_transmit_type {
+       TCPC_TX_SOP = 0,
+       TCPC_TX_SOP_PRIME = 1,
+       TCPC_TX_SOP_PRIME_PRIME = 2,
+       TCPC_TX_SOP_DEBUG_PRIME = 3,
+       TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4,
+       TCPC_TX_HARD_RESET = 5,
+       TCPC_TX_CABLE_RESET = 6,
+       TCPC_TX_BIST_MODE_2 = 7
+};
+
+enum tcpm_rx_cap_type {
+       TCPC_RX_CAP_SOP = 1 << 0,
+       TCPC_RX_CAP_SOP_PRIME = 1 << 1,
+       TCPC_RX_CAP_SOP_PRIME_PRIME = 1 << 2,
+       TCPC_RX_CAP_SOP_DEBUG_PRIME = 1 << 3,
+       TCPC_RX_CAP_SOP_DEBUG_PRIME_PRIME = 1 << 4,
+       TCPC_RX_CAP_HARD_RESET = 1 << 5,
+       TCPC_RX_CAP_CABLE_RESET = 1 << 6,
+};
+
+struct tcpc_ops {
+       int (*init)(struct tcpc_device *tcpc, bool sw_reset);
+       int (*alert_status_clear)(struct tcpc_device *tcpc, u32 mask);
+       int (*fault_status_clear)(struct tcpc_device *tcpc, u8 status);
+       int (*get_alert_status)(struct tcpc_device *tcpc, u32 *alert);
+       int (*get_power_status)(struct tcpc_device *tcpc, u16 *pwr_status);
+       int (*get_fault_status)(struct tcpc_device *tcpc, u8 *status);
+       int (*get_cc)(struct tcpc_device *tcpc, int *cc1, int *cc2);
+       int (*set_cc)(struct tcpc_device *tcpc, int pull);
+       int (*set_polarity)(struct tcpc_device *tcpc, int polarity);
+       int (*set_vconn)(struct tcpc_device *tcpc, int enable);
+
+#ifdef CONFIG_TCPC_LOW_POWER_MODE
+       int (*set_low_power_mode)(struct tcpc_device *tcpc, bool en, int pull);
+#endif /* CONFIG_TCPC_LOW_POWER_MODE */
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       int (*set_msg_header)(struct tcpc_device *tcpc,
+                             int power_role, int data_role);
+       int (*set_rx_enable)(struct tcpc_device *tcpc, u8 enable);
+       int (*get_message)(struct tcpc_device *tcpc, u32 *payload,
+                          u16 *head, enum tcpm_transmit_type *type);
+       int (*transmit)(struct tcpc_device *tcpc,
+                       enum tcpm_transmit_type type,
+                       u16 header, const u32 *data);
+       int (*set_bist_test_mode)(struct tcpc_device *tcpc, bool en);
+       int (*set_bist_carrier_mode)(struct tcpc_device *tcpc, u8 pattern);
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       int (*retransmit)(struct tcpc_device *tcpc);
+#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
+#endif /* CONFIG_USB_POWER_DELIVERY */
+};
+
+#define TCPC_VBUS_SOURCE_0V            (0)
+#define TCPC_VBUS_SOURCE_5V            (5000)
+
+#define TCPC_VBUS_SINK_0V              (0)
+#define TCPC_VBUS_SINK_5V              (5000)
+
+#define TCPC_LEGACY_CABLE_CONFIRM      50
+
+struct tcpc_device {
+       struct i2c_client *client;
+       struct tcpc_ops *ops;
+       void *drv_data;
+       struct tcpc_desc desc;
+       struct device dev;
+       struct wakeup_source attach_wake_lock;
+       struct wakeup_source dettach_temp_wake_lock;
+       /* For tcpc timer & event */
+       u32 timer_handle_index;
+       struct hrtimer tcpc_timer[PD_TIMER_NR];
+
+       ktime_t last_expire[PD_TIMER_NR];
+       struct delayed_work timer_handle_work[2];
+       struct mutex access_lock;
+       struct mutex typec_lock;
+       struct mutex timer_lock;
+       struct semaphore timer_enable_mask_lock;
+       struct semaphore timer_tick_lock;
+       atomic_t pending_event;
+       u64 timer_tick;
+       u64 timer_enable_mask;
+       wait_queue_head_t event_loop_wait_que;
+       wait_queue_head_t  timer_wait_que;
+       struct task_struct *event_task;
+       struct task_struct *timer_task;
+       bool timer_thead_stop;
+       bool event_loop_thead_stop;
+
+       struct delayed_work     init_work;
+       struct srcu_notifier_head evt_nh;
+
+       /* For TCPC TypeC */
+       u8 typec_state;
+       u8 typec_role;
+       u8 typec_attach_old;
+       u8 typec_attach_new;
+       u8 typec_local_cc;
+       u8 typec_local_rp_level;
+       u8 typec_remote_cc[2];
+       u8 typec_remote_rp_level;
+       u8 typec_wait_ps_change;
+       bool typec_polarity;
+       bool typec_drp_try_timeout;
+       bool typec_lpm;
+       bool typec_cable_only;
+       bool typec_power_ctrl;
+
+#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
+       bool typec_legacy_cable;
+       u8 typec_legacy_cable_suspect;
+#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+       /* Event */
+       u8 pd_event_count;
+       u8 pd_event_head_index;
+       u8 pd_msg_buffer_allocated;
+
+       u8 pd_last_vdm_msg_id;
+       bool pd_pending_vdm_event;
+       bool pd_pending_vdm_good_crc;
+       bool pd_postpone_vdm_timeout;
+
+       pd_msg_t pd_last_vdm_msg;
+       pd_event_t pd_vdm_event;
+
+       pd_msg_t pd_msg_buffer[PD_MSG_BUF_SIZE];
+       pd_event_t pd_event_ring_buffer[PD_EVENT_BUF_SIZE];
+
+       bool pd_wait_pe_idle;
+       bool pd_hard_reset_event_pending;
+       bool pd_wait_hard_reset_complete;
+       bool pd_wait_pr_swap_complete;
+       bool pd_wait_error_recovery;
+       bool pd_ping_event_pending;
+       u8 pd_bist_mode;
+       u8 pd_transmit_state;
+       int pd_wait_vbus_once;
+
+#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
+       bool pd_discard_pending;
+#endif
+
+       u8 tcpc_flags;
+
+       pd_port_t pd_port;
+#endif /* CONFIG_USB_POWER_DELIVERY */
+       u8 vbus_level:2;
+       u8 irq_enabled:1;
+
+       struct notifier_block dpm_nb;
+};
+
+#define to_tcpc_device(obj) container_of(obj, struct tcpc_device, dev)
+
+#define RT_DBG_INFO        hisilog_info
+#define RT_DBG_ERR      hisilog_err
+
+#if TYPEC_DBG_ENABLE
+#define TYPEC_DBG(format, args...)             \
+       RT_DBG_INFO("[TPC-D]" format, ##args)
+#else
+#define TYPEC_DBG(format, args...)
+#endif /* TYPEC_DBG_ENABLE */
+
+#if TYPEC_INFO_ENABLE
+#define TYPEC_INFO(format, args...)    \
+       RT_DBG_INFO("TPC-I:" format, ##args)
+#else
+#define TYPEC_INFO(format, args...)
+#endif /* TYPEC_INFO_ENABLE */
+
+#if TCPC_INFO_ENABLE
+#define TCPC_INFO(format, args...)     \
+       RT_DBG_INFO("[TCPC-I]" format, ##args)
+#else
+#define TCPC_INFO(foramt, args...)
+#endif /* TCPC_INFO_ENABLE */
+
+#if TCPC_DBG_ENABLE
+#define TCPC_DBG(format, args...)      \
+       RT_DBG_INFO("[TCPC-D]" format, ##args)
+#else
+#define TCPC_DBG(format, args...)
+#endif /* TCPC_DBG_ENABLE */
+
+#define TCPC_ERR(format, args...)      \
+       RT_DBG_ERR("[TCPC-E]" format, ##args)
+
+#define DP_ERR(format, args...)        \
+       RT_DBG_ERR("[DP-E]" format, ##args)
+
+#if DPM_DBG_ENABLE
+#define DPM_DBG(format, args...)       \
+       RT_DBG_INFO("DPM-D:" format, ##args)
+#else
+#define DPM_DBG(format, args...)
+#endif /* DPM_DBG_ENABLE */
+
+#if PD_ERR_ENABLE
+#define PD_ERR(format, args...) \
+       RT_DBG_ERR("PD-E:" format, ##args)
+#else
+#define PD_ERR(format, args...)
+#endif /* PD_ERR_ENABLE */
+
+#if PE_INFO_ENABLE
+#define PE_INFO(format, args...)       \
+       RT_DBG_INFO("PE:" format, ##args)
+#else
+#define PE_INFO(format, args...)
+#endif /* PE_INFO_ENABLE */
+
+#if PE_EVENT_DBG_ENABLE
+#define PE_EVT_INFO(format, args...) \
+       RT_DBG_INFO("PE-E:" format, ##args)
+#else
+#define PE_EVT_INFO(format, args...)
+#endif /* PE_EVENT_DBG_ENABLE */
+
+#if PE_DBG_ENABLE
+#define PE_DBG(format, args...)        \
+       RT_DBG_INFO("PE:" format, ##args)
+#else
+#define PE_DBG(format, args...)
+#endif /* PE_DBG_ENABLE */
+
+#if PE_STATE_INFO_ENABLE
+#define PE_STATE_INFO(format, args...) \
+       RT_DBG_INFO("PE:" format, ##args)
+#else
+#define PE_STATE_INFO(format, args...)
+#endif /* PE_STATE_IFNO_ENABLE */
+
+#endif /* #ifndef __LINUX_RT_TCPCI_CORE_H */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci_event.h b/include/linux/hisi/usb/pd/richtek/tcpci_event.h
new file mode 100644 (file)
index 0000000..d2d9b39
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef TCPC_EVENT_BUF_H_INCLUDED
+#define TCPC_EVENT_BUF_H_INCLUDED
+
+#include <linux/hisi/usb/pd/richtek/tcpci_timer.h>
+
+#define PD_MSG_BUF_SIZE                (4 * 2)
+#define PD_EVENT_BUF_SIZE      (8 * 2)
+
+struct tcpc_device;
+typedef struct __pd_port pd_port_t;
+
+typedef struct __pd_msg {
+       u8 frame_type;
+       u16 msg_hdr;
+       u32 payload[7];
+       unsigned long time_stamp;
+} pd_msg_t;
+
+typedef struct __pd_event {
+       u8 event_type;
+       u8 msg;
+       u8 msg_sec;
+       pd_msg_t *pd_msg;
+} pd_event_t;
+
+pd_msg_t *pd_alloc_msg(struct tcpc_device *tcpc_dev);
+void pd_free_msg(struct tcpc_device *tcpc_dev, pd_msg_t *pd_msg);
+
+bool pd_get_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event);
+bool pd_put_event(struct tcpc_device *tcpc_dev,
+                 const pd_event_t *pd_event, bool from_port_partner);
+void pd_free_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event);
+void pd_event_buf_reset(struct tcpc_device *tcpc_dev);
+
+bool pd_get_vdm_event(struct tcpc_device *tcpc_dev, pd_event_t *pd_event);
+bool pd_put_vdm_event(struct tcpc_device *tcpc_dev,
+                     pd_event_t *pd_event, bool from_port_partner);
+
+bool pd_put_last_vdm_event(struct tcpc_device *tcpc_dev);
+
+int tcpci_event_init(struct tcpc_device *tcpc_dev);
+int tcpci_event_deinit(struct tcpc_device *tcpc_dev);
+
+void pd_put_cc_detached_event(struct tcpc_device *tcpc_dev);
+void pd_put_recv_hard_reset_event(struct tcpc_device *tcpc_dev);
+void pd_put_sent_hard_reset_event(struct tcpc_device *tcpc_dev);
+bool pd_put_pd_msg_event(struct tcpc_device *tcpc_dev, pd_msg_t *pd_msg);
+void pd_put_hard_reset_completed_event(struct tcpc_device *tcpc_dev);
+void pd_put_vbus_changed_event(struct tcpc_device *tcpc_dev, bool from_ic);
+void pd_put_vbus_safe0v_event(struct tcpc_device *tcpc_dev);
+void pd_put_vbus_stable_event(struct tcpc_device *tcpc_dev);
+void pd_put_vbus_present_event(struct tcpc_device *tcpc_dev);
+
+enum pd_event_type {
+       PD_EVT_PD_MSG = 0,      /* either ctrl msg or data msg */
+       PD_EVT_CTRL_MSG,
+       PD_EVT_DATA_MSG,
+
+       PD_EVT_DPM_MSG,
+       PD_EVT_HW_MSG,
+       PD_EVT_PE_MSG,
+       PD_EVT_TIMER_MSG,
+};
+
+/* Control Message type */
+enum pd_ctrl_msg_type {
+       /* 0 Reserved */
+       PD_CTRL_GOOD_CRC = 1,
+       PD_CTRL_GOTO_MIN = 2,
+       PD_CTRL_ACCEPT = 3,
+       PD_CTRL_REJECT = 4,
+       PD_CTRL_PING = 5,
+       PD_CTRL_PS_RDY = 6,
+       PD_CTRL_GET_SOURCE_CAP = 7,
+       PD_CTRL_GET_SINK_CAP = 8,
+       PD_CTRL_DR_SWAP = 9,
+       PD_CTRL_PR_SWAP = 10,
+       PD_CTRL_VCONN_SWAP = 11,
+       PD_CTRL_WAIT = 12,
+       PD_CTRL_SOFT_RESET = 13,
+       /* 14-15 Reserved */
+       PD_CTRL_MSG_NR,
+};
+
+/* Data message type */
+enum pd_data_msg_type {
+       /* 0 Reserved */
+       PD_DATA_SOURCE_CAP = 1,
+       PD_DATA_REQUEST = 2,
+       PD_DATA_BIST = 3,
+       PD_DATA_SINK_CAP = 4,
+       /* 5-14 Reserved */
+       PD_DATA_VENDOR_DEF = 15,
+       PD_DATA_MSG_NR,
+};
+
+/* HW Message type */
+enum pd_hw_msg_type {
+       PD_HW_CC_DETACHED = 0,
+       PD_HW_CC_ATTACHED,
+       PD_HW_RECV_HARD_RESET,
+       PD_HW_VBUS_PRESENT,
+       PD_HW_VBUS_ABSENT,
+       PD_HW_VBUS_SAFE0V,
+       PD_HW_VBUS_STABLE,
+       PD_HW_TX_FAILED,        /* no good crc or discard */
+       PD_HW_RETRY_VDM,        /* discard vdm msg */
+       PD_HW_MSG_NR,
+};
+
+/* PE Message type*/
+enum pd_pe_msg_type {
+       PD_PE_RESET_PRL_COMPLETED = 0,
+       PD_PE_POWER_ROLE_AT_DEFAULT,
+       PD_PE_HARD_RESET_COMPLETED,
+       PD_PE_IDLE,
+       PD_PE_MSG_NR,
+};
+
+/* DPM Message type */
+
+enum pd_dpm_msg_type {
+       PD_DPM_NOTIFIED = 0,
+       PD_DPM_ACK = PD_DPM_NOTIFIED,
+       PD_DPM_NAK,
+
+       PD_DPM_PD_REQUEST,
+       PD_DPM_VDM_REQUEST,
+
+       PD_DPM_DISCOVER_CABLE_ID,
+       PD_DPM_CAP_CHANGED,
+
+       PD_DPM_ERROR_RECOVERY,
+
+       PD_DPM_MSG_NR,
+};
+
+enum pd_dpm_notify_type {
+       PD_DPM_NOTIFY_OK = 0,
+       PD_DPM_NOTIFY_CAP_MISMATCH,
+};
+
+enum pd_dpm_nak_type {
+       PD_DPM_NAK_REJECT = 0,
+       PD_DPM_NAK_WAIT = 1,
+       PD_DPM_NAK_REJECT_INVALID = 2,
+};
+
+enum pd_dpm_pd_request_type {
+       PD_DPM_PD_REQUEST_PR_SWAP = 0,
+       PD_DPM_PD_REQUEST_DR_SWAP,
+       PD_DPM_PD_REQUEST_VCONN_SWAP,
+       PD_DPM_PD_REQUEST_GOTOMIN,
+
+       PD_DPM_PD_REQUEST_SOFTRESET,
+       PD_DPM_PD_REQUEST_HARDRESET,
+
+       PD_DPM_PD_REQUEST_GET_SOURCE_CAP,
+       PD_DPM_PD_REQUEST_GET_SINK_CAP,
+
+       PD_DPM_PD_REQUEST_PW_REQUEST,
+       PD_DPM_PD_REQUEST_BIST_CM2,
+       PD_DPM_PD_REQUEST_NR,
+};
+
+enum pd_tx_transmit_state {
+       PD_TX_STATE_GOOD_CRC = 0,
+       PD_TX_STATE_NO_GOOD_CRC,
+       PD_TX_STATE_DISCARD,
+       PD_TX_STATE_HARD_RESET,
+       PD_TX_STATE_NO_RESPONSE,
+
+       PD_TX_STATE_WAIT,
+       PD_TX_STATE_WAIT_CRC_VDM = PD_TX_STATE_WAIT,
+       PD_TX_STATE_WAIT_CRC_PD,
+       PD_TX_STATE_WAIT_HARD_RESET,
+};
+
+static inline bool pd_event_msg_match(pd_event_t *pd_event,
+                                     u8 type, uint8_t msg)
+{
+       if (pd_event->event_type != type)
+               return false;
+
+       return (pd_event->msg == msg);
+}
+
+#endif /* TCPC_EVENT_BUF_H_INCLUDED */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci_timer.h b/include/linux/hisi/usb/pd/richtek/tcpci_timer.h
new file mode 100644 (file)
index 0000000..3a38849
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef TCPC_TIMER_H_INCLUDED
+#define TCPC_TIMER_H_INCLUDED
+
+#include <linux/kernel.h>
+
+struct tcpc_device;
+enum {
+#ifdef CONFIG_USB_POWER_DELIVERY
+       PD_TIMER_BIST_CONT_MODE = 0,
+       PD_TIMER_DISCOVER_ID,
+       PD_TIMER_HARD_RESET_COMPLETE,
+       PD_TIMER_NO_RESPONSE,
+       PD_TIMER_PS_HARD_RESET,
+       PD_TIMER_PS_SOURCE_OFF,
+       PD_TIMER_PS_SOURCE_ON,
+       PD_TIMER_PS_TRANSITION,
+       PD_TIMER_SENDER_RESPONSE,
+       PD_TIMER_SINK_ACTIVITY,
+       PD_TIMER_SINK_REQUEST,
+       PD_TIMER_SINK_WAIT_CAP,
+       PD_TIMER_SOURCE_ACTIVITY,
+       PD_TIMER_SOURCE_CAPABILITY,
+       PD_TIMER_SOURCE_START,
+       PD_TIMER_VCONN_ON,
+       PD_TIMER_VDM_MODE_ENTRY,
+       PD_TIMER_VDM_MODE_EXIT,
+       PD_TIMER_VDM_RESPONSE,
+       PD_TIMER_SOURCE_TRANSITION,
+       PD_TIMER_SRC_RECOVER,
+       PD_TIMER_VSAFE0V_DELAY,
+       PD_TIMER_VSAFE0V_TOUT,
+       PD_TIMER_DISCARD,
+       PD_TIMER_VBUS_STABLE,
+       PD_TIMER_VBUS_PRESENT,
+       PD_PE_VDM_POSTPONE,
+       PD_PE_TIMER_END_ID,
+
+       /* TYPEC-RT-TIMER */
+       TYPEC_RT_TIMER_START_ID = PD_PE_TIMER_END_ID,
+       TYPEC_RT_TIMER_PE_IDLE = TYPEC_RT_TIMER_START_ID,
+       TYPEC_RT_TIMER_SAFE0V_DELAY,
+       TYPEC_RT_TIMER_SAFE0V_TOUT,
+
+       /* TYPEC-TRY-TIMER */
+       TYPEC_TRY_TIMER_START_ID,
+       TYPEC_TRY_TIMER_DRP_TRY = TYPEC_TRY_TIMER_START_ID,
+       TYPEC_TRY_TIMER_DRP_TRYWAIT,
+
+       /* TYPEC-DEBOUNCE-TIMER */
+       TYPEC_TIMER_START_ID,
+       TYPEC_TIMER_CCDEBOUNCE = TYPEC_TIMER_START_ID,
+       TYPEC_TIMER_PDDEBOUNCE,
+       TYPEC_TIMER_ERROR_RECOVERY,
+       TYPEC_TIMER_WAKEUP,
+       TYPEC_TIMER_DRP_SRC_TOGGLE,
+#else
+       TYPEC_RT_TIMER_START_ID = 0,
+       TYPEC_RT_TIMER_SAFE0V_DELAY = TYPEC_RT_TIMER_START_ID,
+       TYPEC_RT_TIMER_SAFE0V_TOUT,
+
+       TYPEC_TRY_TIMER_START_ID,
+       TYPEC_TRY_TIMER_DRP_TRY = TYPEC_TRY_TIMER_START_ID,
+       TYPEC_TRY_TIMER_DRP_TRYWAIT,
+
+       TYPEC_TIMER_START_ID,
+       TYPEC_TIMER_CCDEBOUNCE = TYPEC_TIMER_START_ID,
+       TYPEC_TIMER_PDDEBOUNCE,
+       TYPEC_TIMER_WAKEUP,
+       TYPEC_TIMER_DRP_SRC_TOGGLE,
+#endif /* CONFIG_USB_POWER_DELIVERY */
+       PD_TIMER_NR,
+};
+
+int tcpci_timer_init(struct tcpc_device *tcpc);
+int tcpci_timer_deinit(struct tcpc_device *tcpc);
+void tcpc_restart_timer(struct tcpc_device *tcpc, u32 timer_id);
+void tcpc_enable_timer(struct tcpc_device *tcpc, u32 timer_id);
+void tcpc_disable_timer(
+               struct tcpc_device *tcpc, u32 timer_id);
+void tcpc_reset_typec_try_timer(struct tcpc_device *tcpc);
+void tcpc_reset_typec_debounce_timer(struct tcpc_device *tcpc);
+
+void tcpc_reset_pe_timer(struct tcpc_device *tcpc);
+
+#endif /* TCPC_TIMER_H_INCLUDED */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpci_typec.h b/include/linux/hisi/usb/pd/richtek/tcpci_typec.h
new file mode 100644 (file)
index 0000000..9e9d8b2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_TCPCI_TYPEC_H
+#define __LINUX_TCPCI_TYPEC_H
+#include <linux/hisi/usb/pd/richtek/tcpci.h>
+
+struct tcpc_device;
+
+/******************************************************************************
+ *  Call following function to trigger TYPEC Connection State Change
+ *
+ * 1. H/W -> CC/PS Change.
+ * 2. Timer -> CCDebounce or PDDebounce or others Timeout
+ * 3. Policy Engine -> PR_SWAP, Error_Recovery, PE_Idle
+ *****************************************************************************/
+
+int tcpc_typec_handle_cc_change(
+       struct tcpc_device *tcpc_dev);
+
+int tcpc_typec_handle_ps_change(
+               struct tcpc_device *tcpc_dev, int vbus_level);
+
+int tcpc_typec_handle_timeout(
+               struct tcpc_device *tcpc_dev, u32 timer_id);
+
+int tcpc_typec_handle_vsafe0v(struct tcpc_device *tcpc_dev);
+
+int tcpc_typec_set_rp_level(struct tcpc_device *tcpc_dev, u8 res);
+
+int tcpc_typec_change_role(
+       struct tcpc_device *tcpc_dev, u8 typec_role);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+int tcpc_typec_advertise_explicit_contract(struct tcpc_device *tcpc_dev);
+int tcpc_typec_handle_pe_pr_swap(struct tcpc_device *tcpc_dev);
+#else
+int tcpc_typec_swap_role(struct tcpc_device *tcpc_dev);
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+#endif /* #ifndef __LINUX_TCPCI_TYPEC_H */
diff --git a/include/linux/hisi/usb/pd/richtek/tcpm.h b/include/linux/hisi/usb/pd/richtek/tcpm.h
new file mode 100644 (file)
index 0000000..af3316c
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2016 Richtek Technology Corp.
+ *
+ * Author: TH <tsunghan_tsai@richtek.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef TCPM_H_
+#define TCPM_H_
+
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+
+#include <linux/hisi/usb/pd/richtek/tcpci_config.h>
+
+#include <linux/hisi/usb/hisi_pd_dev.h>
+#include <linux/hisi/log/hisi_log.h>
+
+#ifndef HISILOG_TAG
+#define HISILOG_TAG hisi_pd
+HISILOG_REGIST();
+#endif
+
+struct tcpc_device;
+
+/*
+ * Type-C Port Notify Chain
+ */
+
+enum typec_attach_type {
+       TYPEC_UNATTACHED = 0,
+       TYPEC_ATTACHED_SNK,
+       TYPEC_ATTACHED_SRC,
+       TYPEC_ATTACHED_AUDIO,
+       TYPEC_ATTACHED_DEBUG,
+
+#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
+       TYPEC_ATTACHED_DBGACC_SNK,              /* Rp, Rp */
+#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
+
+#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC
+       TYPEC_ATTACHED_CUSTOM_SRC,              /* Same Rp */
+#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */
+};
+
+enum dpm_request_state {
+       DPM_REQ_NULL,
+       DPM_REQ_QUEUE,
+       DPM_REQ_RUNNING,
+       DPM_REQ_SUCCESS,
+       DPM_REQ_FAILED,
+
+       /* Request failed */
+
+       DPM_REQ_ERR_IDLE = DPM_REQ_FAILED,
+
+       DPM_REQ_ERR_NOT_READY,
+       DPM_REQ_ERR_WRONG_ROLE,
+
+       DPM_REQ_ERR_RECV_HRESET,
+       DPM_REQ_ERR_RECV_SRESET,
+       DPM_REQ_ERR_SEND_HRESET,
+       DPM_REQ_ERR_SEND_SRESET,
+       DPM_REQ_ERR_SEND_BIST,
+
+       /* Internal */
+       DPM_REQ_SUCCESS_CODE,
+
+       DPM_REQ_E_UVDM_ACK,
+       DPM_REQ_E_UVDM_NAK,
+};
+
+/* Power role */
+#define PD_ROLE_SINK   0
+#define PD_ROLE_SOURCE 1
+
+/* Data role */
+#define PD_ROLE_UFP    0
+#define PD_ROLE_DFP    1
+
+/* Vconn role */
+#define PD_ROLE_VCONN_OFF 0
+#define PD_ROLE_VCONN_ON  1
+
+enum {
+       TCP_NOTIFY_DIS_VBUS_CTRL,
+       TCP_NOTIFY_SOURCE_VCONN,
+       TCP_NOTIFY_SOURCE_VBUS,
+       TCP_NOTIFY_SINK_VBUS,
+       TCP_NOTIFY_PR_SWAP,
+       TCP_NOTIFY_DR_SWAP,
+       TCP_NOTIFY_VCONN_SWAP,
+       TCP_NOTIFY_ENTER_MODE,
+       TCP_NOTIFY_EXIT_MODE,
+       TCP_NOTIFY_AMA_DP_STATE,
+       TCP_NOTIFY_AMA_DP_ATTENTION,
+       TCP_NOTIFY_AMA_DP_HPD_STATE,
+
+       TCP_NOTIFY_TYPEC_STATE,
+       TCP_NOTIFY_PD_STATE,
+};
+
+struct tcp_ny_pd_state {
+       u8 connected;
+};
+
+struct tcp_ny_swap_state {
+       u8 new_role;
+};
+
+struct tcp_ny_enable_state {
+       bool en;
+};
+
+struct tcp_ny_typec_state {
+       u8 rp_level;
+       u8 polarity;
+       u8 old_state;
+       u8 new_state;
+};
+
+enum {
+       TCP_VBUS_CTRL_REMOVE = 0,
+       TCP_VBUS_CTRL_TYPEC = 1,
+       TCP_VBUS_CTRL_PD = 2,
+
+       TCP_VBUS_CTRL_HRESET = TCP_VBUS_CTRL_PD,
+       TCP_VBUS_CTRL_PR_SWAP = 3,
+       TCP_VBUS_CTRL_REQUEST = 4,
+
+       TCP_VBUS_CTRL_PD_DETECT = (1 << 7),
+
+       TCP_VBUS_CTRL_PD_HRESET =
+               TCP_VBUS_CTRL_HRESET | TCP_VBUS_CTRL_PD_DETECT,
+
+       TCP_VBUS_CTRL_PD_PR_SWAP =
+               TCP_VBUS_CTRL_PR_SWAP | TCP_VBUS_CTRL_PD_DETECT,
+
+       TCP_VBUS_CTRL_PD_REQUEST =
+               TCP_VBUS_CTRL_REQUEST | TCP_VBUS_CTRL_PD_DETECT,
+};
+
+struct tcp_ny_vbus_state {
+       int mv;
+       int ma;
+       u8 type;
+};
+
+struct tcp_ny_mode_ctrl {
+       u16 svid;
+       u8 ops;
+       u32 mode;
+};
+
+enum {
+       SW_USB = 0,
+       SW_DFP_D,
+       SW_UFP_D,
+};
+
+struct tcp_ny_ama_dp_state {
+       u8 sel_config;
+       u8 signal;
+       u8 pin_assignment;
+       u8 polarity;
+       u8 active;
+};
+
+enum {
+       TCP_DP_UFP_U_MASK = 0x7C,
+       TCP_DP_UFP_U_POWER_LOW = 1 << 2,
+       TCP_DP_UFP_U_ENABLED = 1 << 3,
+       TCP_DP_UFP_U_MF_PREFER = 1 << 4,
+       TCP_DP_UFP_U_USB_CONFIG = 1 << 5,
+       TCP_DP_UFP_U_EXIT_MODE = 1 << 6,
+};
+
+struct tcp_ny_ama_dp_attention {
+       u8 state;
+};
+
+struct tcp_ny_ama_dp_hpd_state {
+       bool irq : 1;
+       bool state : 1;
+};
+
+struct tcp_ny_uvdm {
+       bool ack;
+       u8 uvdm_cnt;
+       u16 uvdm_svid;
+       u32 *uvdm_data;
+};
+
+struct tcp_notify {
+       union {
+               struct tcp_ny_enable_state en_state;
+               struct tcp_ny_vbus_state vbus_state;
+               struct tcp_ny_typec_state typec_state;
+               struct tcp_ny_swap_state swap_state;
+               struct tcp_ny_pd_state pd_state;
+               struct tcp_ny_mode_ctrl mode_ctrl;
+               struct tcp_ny_ama_dp_state ama_dp_state;
+               struct tcp_ny_ama_dp_attention ama_dp_attention;
+               struct tcp_ny_ama_dp_hpd_state ama_dp_hpd_state;
+               struct tcp_ny_uvdm uvdm_msg;
+       };
+};
+
+struct tcpc_device *tcpc_dev_get_by_name(const char *name);
+
+int register_tcp_dev_notifier(
+               struct tcpc_device *tcp_dev,
+               struct notifier_block *nb);
+int unregister_tcp_dev_notifier(
+               struct tcpc_device *tcp_dev,
+               struct notifier_block *nb);
+
+struct tcpc_device *notify_tcp_dev_ready(const char *name);
+
+/*
+ * Type-C Port Control I/F
+ */
+
+enum tcpm_error_list {
+       TCPM_SUCCESS = 0,
+       TCPM_ERROR_UNKNOWN = -1,
+       TCPM_ERROR_UNATTACHED = -2,
+       TCPM_ERROR_PARAMETER = -3,
+       TCPM_ERROR_PUT_EVENT = -4,
+};
+
+#define TCPM_PDO_MAX_SIZE      7
+
+struct tcpm_power_cap {
+       u8 cnt;
+       u32 pdos[TCPM_PDO_MAX_SIZE];
+};
+
+/* Inquire TCPM status */
+
+enum tcpc_cc_voltage_status {
+       TYPEC_CC_VOLT_OPEN = 0,
+       TYPEC_CC_VOLT_RA = 1,
+       TYPEC_CC_VOLT_RD = 2,
+
+       TYPEC_CC_VOLT_SNK_DFT = 5,
+       TYPEC_CC_VOLT_SNK_1_5 = 6,
+       TYPEC_CC_VOLT_SNK_3_0 = 7,
+
+       TYPEC_CC_DRP_TOGGLING = 15,
+};
+
+enum tcpm_vbus_level {
+#ifdef CONFIG_TCPC_VSAFE0V_DETECT
+       TCPC_VBUS_SAFE0V = 0,
+       TCPC_VBUS_INVALID,
+       TCPC_VBUS_VALID,
+#else
+       TCPC_VBUS_INVALID = 0,
+       TCPC_VBUS_VALID,
+#endif
+};
+
+enum typec_role_defination {
+       TYPEC_ROLE_UNKNOWN = 0,
+       TYPEC_ROLE_SNK,
+       TYPEC_ROLE_SRC,
+       TYPEC_ROLE_DRP,
+       TYPEC_ROLE_TRY_SRC,
+       TYPEC_ROLE_TRY_SNK,
+       TYPEC_ROLE_NR,
+};
+
+int tcpm_inquire_remote_cc(
+               struct tcpc_device *tcpc_dev,
+               u8 *cc1, u8 *cc2, bool from_ic);
+int tcpm_inquire_vbus_level(struct tcpc_device *tcpc_dev, bool from_ic);
+bool tcpm_inquire_cc_polarity(struct tcpc_device *tcpc_dev);
+u8 tcpm_inquire_typec_attach_state(struct tcpc_device *tcpc_dev);
+u8 tcpm_inquire_typec_role(struct tcpc_device *tcpc_dev);
+u8 tcpm_inquire_typec_local_rp(struct tcpc_device *tcpc_dev);
+
+int tcpm_typec_set_rp_level(struct tcpc_device *tcpc_dev, u8 level);
+
+int tcpm_typec_change_role(struct tcpc_device *tcpc_dev, u8 typec_role);
+
+#ifdef CONFIG_USB_POWER_DELIVERY
+
+bool tcpm_inquire_pd_connected(struct tcpc_device *tcpc_dev);
+
+bool tcpm_inquire_pd_prev_connected(struct tcpc_device *tcpc_dev);
+
+u8 tcpm_inquire_pd_data_role(struct tcpc_device *tcpc_dev);
+
+u8 tcpm_inquire_pd_power_role(struct tcpc_device *tcpc_dev);
+
+u8 tcpm_inquire_pd_vconn_role(struct tcpc_device *tcpc_dev);
+
+#endif /* CONFIG_USB_POWER_DELIVERY */
+
+/* Request TCPM to send PD Request */
+
+int tcpm_power_role_swap(struct tcpc_device *tcpc_dev);
+int tcpm_data_role_swap(struct tcpc_device *tcpc_dev);
+int tcpm_vconn_swap(struct tcpc_device *tcpc_dev);
+int tcpm_goto_min(struct tcpc_device *tcpc_dev);
+int tcpm_soft_reset(struct tcpc_device *tcpc_dev);
+int tcpm_hard_reset(struct tcpc_device *tcpc_dev);
+int tcpm_get_source_cap(
+               struct tcpc_device *tcpc_dev, struct tcpm_power_cap *cap);
+int tcpm_get_sink_cap(struct tcpc_device *tcpc_dev, struct tcpm_power_cap *cap);
+int tcpm_bist_cm2(struct tcpc_device *tcpc_dev);
+int tcpm_request(struct tcpc_device *tcpc_dev, int mv, int ma);
+int tcpm_error_recovery(struct tcpc_device *tcpc_dev);
+
+/* Request TCPM to send VDM */
+
+int tcpm_discover_cable(struct tcpc_device *tcpc_dev, u32 *vdos);
+
+int tcpm_vdm_request_id(struct tcpc_device *tcpc_dev, u8 *cnt, u8 *payload);
+
+/* Notify TCPM */
+
+int tcpm_notify_vbus_stable(struct tcpc_device *tcpc_dev);
+#endif /* TCPM_H_ */