source "drivers/usb/isp1760/Kconfig"
+source "drivers/usb/pd/Kconfig"
+
comment "USB port drivers"
if USB
obj-$(CONFIG_TYPEC) += typec/
obj-$(CONFIG_USB_ROLE_SWITCH) += roles/
+
+obj-$(CONFIG_TCPC_CLASS) += pd/
--- /dev/null
+source "drivers/usb/pd/richtek/Kconfig"
--- /dev/null
+obj-y += hisi_pd.o
+obj-y += richtek/
--- /dev/null
+/************************************************************
+ *
+ * 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>");
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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 */
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* 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, ¶m1[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, ®_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, ®val[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 (®_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");
--- /dev/null
+/*
+ * 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, ¶m);
+ 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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, ¶m1[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");
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
+{
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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
--- /dev/null
+/**********************************************************
+ * 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
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/* 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*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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_ */