summaryrefslogtreecommitdiff
path: root/drivers/misc/cclogic
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/cclogic')
-rw-r--r--drivers/misc/cclogic/Kconfig25
-rw-r--r--drivers/misc/cclogic/Makefile9
-rw-r--r--drivers/misc/cclogic/cclogic-class.c292
-rw-r--r--drivers/misc/cclogic/cclogic-class.h19
-rw-r--r--drivers/misc/cclogic/cclogic-core.c1686
-rw-r--r--drivers/misc/cclogic/cclogic-core.h169
-rw-r--r--drivers/misc/cclogic/pi5usb30216d.c245
-rw-r--r--drivers/misc/cclogic/tusb320hai.c445
8 files changed, 2890 insertions, 0 deletions
diff --git a/drivers/misc/cclogic/Kconfig b/drivers/misc/cclogic/Kconfig
new file mode 100644
index 000000000000..dfe6b585a41c
--- /dev/null
+++ b/drivers/misc/cclogic/Kconfig
@@ -0,0 +1,25 @@
+#
+# cclogic devices for type-C connector
+#
+menuconfig CCLOGIC
+ tristate "cclogic devices for Type-C connector"
+ depends on I2C
+ help
+ This is cclogic devices for Type-C connector.
+
+if CCLOGIC
+
+config TYPEC_CCLOGIC_PI5USBD
+ depends on I2C
+ tristate "Pericom Pi5usb30216d Support"
+ ---help---
+ The Pericom pi5usb30216d chip is used as cc-logic of TYPEC
+ interface.
+
+config TYPEC_CCLOGIC_TUSB320HAI
+ depends on I2C
+ tristate "TI TUSB320Hai Support"
+ ---help---
+ The TI tusb320hai chip is used as cc-logic of TYPEC
+
+endif # CCLOGIC
diff --git a/drivers/misc/cclogic/Makefile b/drivers/misc/cclogic/Makefile
new file mode 100644
index 000000000000..b0256a428144
--- /dev/null
+++ b/drivers/misc/cclogic/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for cclogic device.
+#
+
+obj-$(CONFIG_CCLOGIC) += cclogic-core.o
+obj-$(CONFIG_CCLOGIC) += cclogic-class.o
+obj-$(CONFIG_TYPEC_CCLOGIC_TUSB320HAI) += tusb320hai.o
+obj-$(CONFIG_TYPEC_CCLOGIC_PI5USBD) += pi5usb30216d.o
+
diff --git a/drivers/misc/cclogic/cclogic-class.c b/drivers/misc/cclogic/cclogic-class.c
new file mode 100644
index 000000000000..a58e7b6575b5
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-class.c
@@ -0,0 +1,292 @@
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+
+#include "cclogic-class.h"
+#include "cclogic-core.h"
+
+struct class *cclogic_class;
+static atomic_t device_count;
+
+/*
+ * USB port's supported modes. (read-only)
+ * Contents: "", "ufp", "dfp", or "ufp dfp".
+*/
+static ssize_t cclogic_supported_modes_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (cdev->support & (CCLOGIC_SUPPORT_MODE_DUAL))
+ return sprintf(buf, "ufp dfp");
+ else if (cdev->support & CCLOGIC_SUPPORT_MODE_UFP)
+ return sprintf(buf, "ufp");
+ else if (cdev->support & CCLOGIC_SUPPORT_MODE_DFP)
+ return sprintf(buf, "dfp");
+ else {
+ *buf = '\0';
+ return 0;
+ }
+}
+
+/*
+ * USB port's current mode. (read-write if configurable)
+ * Contents: "", "ufp", or "dfp".
+*/
+static ssize_t cclogic_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+ struct cclogic_state *pstate = &cclogic_dev->state;
+ int mode;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ mode = cclogic_get_connected_mode(pstate);
+ if (mode == CCLOGIC_MODE_UFP)
+ return sprintf(buf, "ufp");
+ else if (mode == CCLOGIC_MODE_DFP)
+ return sprintf(buf, "dfp");
+ else {
+ *buf = '\0';
+ return 0;
+ }
+}
+
+/*
+ * USB port's current mode. (read-write if configurable)
+ * Contents: "", "ufp", or "dfp".
+*/
+static ssize_t cclogic_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!strcmp(buf, "ufp"))
+ cclogic_set_mode(cclogic_dev, CCLOGIC_MODE_UFP);
+ else if (!strcmp(buf, "dfp"))
+ cclogic_set_mode(cclogic_dev, CCLOGIC_MODE_DFP);
+
+ return count;
+}
+
+/*
+ * USB port's current power role. (read-write if configurable)
+ * Contents: "", "source", or "sink".
+*/
+static ssize_t cclogic_power_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+ struct cclogic_state *pstate = &cclogic_dev->state;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (cclogic_get_power_role(pstate) == CCLOGIC_POWER_SINK)
+ return sprintf(buf, "sink");
+ else if (cclogic_get_power_role(pstate) == CCLOGIC_POWER_SOURCE)
+ return sprintf(buf, "source");
+ else {
+ *buf = '\0';
+ return 0;
+ }
+}
+
+/*
+ * USB port's current power role. (read-write if configurable)
+ * Contents: "", "source", or "sink".
+*/
+static ssize_t cclogic_power_role_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!strcmp(buf, "sink"))
+ cclogic_set_power_role(cclogic_dev, CCLOGIC_POWER_SINK);
+ else if (!strcmp(buf, "source"))
+ cclogic_set_power_role(cclogic_dev, CCLOGIC_POWER_SOURCE);
+
+ return count;
+}
+
+/*
+ * USB port's current data role. (read-write if configurable)
+ * Contents: "", "host", or "device".
+*/
+static ssize_t cclogic_data_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+ struct cclogic_state *pstate = &cclogic_dev->state;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (cclogic_get_data_role(pstate) == CCLOGIC_DATA_HOST)
+ return sprintf(buf, "host");
+ else if (cclogic_get_data_role(pstate) == CCLOGIC_DATA_DEVICE)
+ return sprintf(buf, "device");
+ else {
+ *buf = '\0';
+ return 0;
+ }
+}
+
+/*
+ * USB port's current data role. (read-write if configurable)
+ * Contents: "", "host", or "device".
+*/
+static ssize_t cclogic_data_role_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev);
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!strcmp(buf, "host"))
+ cclogic_set_data_role(cclogic_dev, CCLOGIC_DATA_HOST);
+ else if (!strcmp(buf, "device"))
+ cclogic_set_data_role(cclogic_dev, CCLOGIC_DATA_DEVICE);
+
+ return count;
+}
+
+void cclogic_class_update_state(struct cclogic_class_dev *cdev)
+{
+ kobject_uevent(&cdev->dev->kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL(cclogic_class_update_state);
+
+static int create_cclogic_class(void)
+{
+ if (!cclogic_class) {
+ cclogic_class = class_create(THIS_MODULE, "dual_role_usb");
+ if (IS_ERR(cclogic_class))
+ return PTR_ERR(cclogic_class);
+ }
+
+ return 0;
+}
+
+int cclogic_class_register(struct cclogic_class_dev *cdev)
+{
+ int ret;
+ struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev);
+
+ if (!cclogic_class) {
+ ret = create_cclogic_class();
+ if (ret < 0)
+ return ret;
+ }
+
+ cdev->index = atomic_inc_return(&device_count);
+ cdev->dev = device_create(cclogic_class, NULL,
+ MKDEV(0, cdev->index), NULL, cdev->name);
+ if (IS_ERR(cdev->dev))
+ return PTR_ERR(cdev->dev);
+
+ cdev->support = cclogic_dev->ops->support;
+
+ cdev->device_supported_modes_attr.attr.name = "supported_modes";
+ cdev->device_supported_modes_attr.attr.mode = S_IRUGO;
+ cdev->device_supported_modes_attr.show = cclogic_supported_modes_show;
+ sysfs_attr_init(&cdev->device_supported_modes_attr.attr);
+ ret = device_create_file(cdev->dev, &cdev->device_supported_modes_attr);
+ if (ret < 0)
+ goto err_create_file_1;
+
+ cdev->device_mode_attr.attr.name = "mode";
+ cdev->device_mode_attr.attr.mode = S_IRUGO;
+ cdev->device_mode_attr.show = cclogic_mode_show;
+ if ((cdev->support & CCLOGIC_SUPPORT_MODE_DUAL) == CCLOGIC_SUPPORT_MODE_DUAL) {
+ cdev->device_mode_attr.attr.mode |= S_IWUSR;
+ cdev->device_mode_attr.store = cclogic_mode_store;
+ }
+ sysfs_attr_init(&cdev->device_mode_attr.attr);
+ ret = device_create_file(cdev->dev, &cdev->device_mode_attr);
+ if (ret < 0)
+ goto err_create_file_2;
+
+ cdev->device_power_role_attr.attr.name = "power_role";
+ cdev->device_power_role_attr.attr.mode = S_IRUGO;
+ cdev->device_power_role_attr.show = cclogic_power_role_show;
+ if ((cdev->support & CCLOGIC_SUPPORT_POWER_SWAP) == CCLOGIC_SUPPORT_POWER_SWAP) {
+ cdev->device_power_role_attr.attr.mode |= S_IWUSR;
+ cdev->device_power_role_attr.store = cclogic_power_role_store;
+ }
+ sysfs_attr_init(&cdev->device_power_role_attr.attr);
+ ret = device_create_file(cdev->dev, &cdev->device_power_role_attr);
+ if (ret < 0)
+ goto err_create_file_3;
+
+ cdev->device_data_role_attr.attr.name = "data_role";
+ cdev->device_data_role_attr.attr.mode = S_IRUGO;
+ cdev->device_data_role_attr.show = cclogic_data_role_show;
+ if ((cdev->support & CCLOGIC_SUPPORT_DATA_SWAP) == CCLOGIC_SUPPORT_DATA_SWAP) {
+ cdev->device_data_role_attr.attr.mode |= S_IWUSR;
+ cdev->device_data_role_attr.store = cclogic_data_role_store;
+ }
+ sysfs_attr_init(&cdev->device_data_role_attr.attr);
+ ret = device_create_file(cdev->dev, &cdev->device_data_role_attr);
+ if (ret < 0)
+ goto err_create_file_4;
+
+ dev_set_drvdata(cdev->dev, cdev);
+
+ return 0;
+
+err_create_file_4:
+ device_remove_file(cdev->dev, &cdev->device_power_role_attr);
+err_create_file_3:
+ device_remove_file(cdev->dev, &cdev->device_mode_attr);
+err_create_file_2:
+ device_remove_file(cdev->dev, &cdev->device_supported_modes_attr);
+err_create_file_1:
+ device_destroy(cclogic_class, MKDEV(0, cdev->index));
+ printk(KERN_ERR "cclogic-class: Failed to register driver %s\n", cdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL(cclogic_class_register);
+
+void cclogic_class_unregister(struct cclogic_class_dev *cdev)
+{
+ device_remove_file(cdev->dev, &cdev->device_supported_modes_attr);
+ device_remove_file(cdev->dev, &cdev->device_mode_attr);
+ device_remove_file(cdev->dev, &cdev->device_power_role_attr);
+ device_remove_file(cdev->dev, &cdev->device_data_role_attr);
+ dev_set_drvdata(cdev->dev, NULL);
+ device_destroy(cclogic_class, MKDEV(0, cdev->index));
+}
+EXPORT_SYMBOL(cclogic_class_unregister);
+
+static int __init cclogic_class_init(void)
+{
+ return create_cclogic_class();
+}
+
+static void __exit cclogic_class_exit(void)
+{
+ class_destroy(cclogic_class);
+}
+
+module_init(cclogic_class_init);
+module_exit(cclogic_class_exit);
+
+MODULE_AUTHOR("yangshaoying <yangsy2@zuk.com>");
+MODULE_DESCRIPTION("cclogic class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/cclogic/cclogic-class.h b/drivers/misc/cclogic/cclogic-class.h
new file mode 100644
index 000000000000..c5276839ee65
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-class.h
@@ -0,0 +1,19 @@
+#ifndef __CCLOGIC_CLASS_H__
+#define __CCLOGIC_CLASS_H__
+
+struct cclogic_class_dev {
+ const char *name;
+ struct device *dev;
+ int index;
+ unsigned int support;
+ struct device_attribute device_supported_modes_attr;
+ struct device_attribute device_mode_attr;
+ struct device_attribute device_power_role_attr;
+ struct device_attribute device_data_role_attr;
+};
+
+extern int cclogic_class_register(struct cclogic_class_dev *dev);
+extern void cclogic_class_unregister(struct cclogic_class_dev *dev);
+extern void cclogic_class_update_state(struct cclogic_class_dev *cdev);
+
+#endif
diff --git a/drivers/misc/cclogic/cclogic-core.c b/drivers/misc/cclogic/cclogic-core.c
new file mode 100644
index 000000000000..47efe02e20f9
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-core.c
@@ -0,0 +1,1686 @@
+/*
+ * cclogic-core.c
+ *
+ * Core of CC-Logic drivers
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/wakelock.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/cclogic-core.h>
+#include <linux/notifier.h>
+#include <linux/export.h>
+
+#include "cclogic-core.h"
+#include "cclogic-class.h"
+
+
+static struct cclogic_dev *cclogic_priv;
+static struct mutex cclogic_ops_lock;
+
+static int m_plug_state;
+
+#define DRIVER_NAME "cclogic"
+
+static BLOCKING_NOTIFIER_HEAD(cclogic_notifier_list);
+
+/*
+ * cclogic_register_client - register a client notifier
+ * @nb: notifier block to callback on events
+ */
+int cclogic_register_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&cclogic_notifier_list, nb);
+}
+EXPORT_SYMBOL(cclogic_register_client);
+
+/*
+ * cclogic_unregister_client - unregister a client notifier
+ * @nb: notifier block to callback on events
+ */
+int cclogic_unregister_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&cclogic_notifier_list, nb);
+}
+EXPORT_SYMBOL(cclogic_unregister_client);
+
+/* cclogic_notifier_call_chain - notify clients of fb_events */
+int cclogic_notifier_call_chain(unsigned long val, void *v)
+{
+ return blocking_notifier_call_chain(&cclogic_notifier_list, val, v);
+}
+EXPORT_SYMBOL_GPL(cclogic_notifier_call_chain);
+
+static void cclogic_patch_state(struct cclogic_dev *pdata)
+{
+ struct cclogic_state *state = &pdata->state;
+
+ if (gpio_get_value(pdata->platform_data->irq_plug)) {
+ state->evt = CCLOGIC_EVENT_DETACHED;
+ state->device = CCLOGIC_NO_DEVICE;
+ state->vbus = false;
+ state->cc = CCLOGIC_CC1;
+ }
+}
+
+int cclogic_vbus_power_on(struct cclogic_dev *cclogic_dev, bool enable)
+{
+ struct cclogic_platform *p = cclogic_dev->platform_data;
+ int ret;
+
+ pr_debug("[%s][%d] enable=%d\n", __func__, __LINE__, enable);
+
+ if (!p->vbus_reg)
+ return 0;
+
+ if (enable) {
+ if (cclogic_dev->vbus_on)
+ return 0;
+
+ ret = regulator_enable(p->vbus_reg);
+ if (ret) {
+ dev_err(&cclogic_dev->i2c_client->dev, "Failed to enable vbus_reg\n");
+ return ret;
+ }
+ cclogic_dev->vbus_on = true;
+ } else{
+ if (!cclogic_dev->vbus_on)
+ return 0;
+ ret = regulator_disable(p->vbus_reg);
+ if (ret) {
+ dev_err(&cclogic_dev->i2c_client->dev, "Failed to disable vbus_reg\n");
+ return ret;
+ }
+ cclogic_dev->vbus_on = false;
+ }
+
+ return 0;
+}
+
+static int cclogic_reg_set_optimum_mode_check(struct regulator *reg,
+ int load_uA)
+{
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_load(reg, load_uA) : 0;
+}
+
+static int cclogic_power_on(struct cclogic_dev *cclogic_dev, bool on)
+{
+ int ret = 0;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (cclogic_dev->platform_data->i2c_pull_up) {
+ if (on == false) {
+ if (cclogic_dev->regulator_en) {
+ cclogic_reg_set_optimum_mode_check(
+ cclogic_dev->vcc_i2c, 0);
+ regulator_disable(cclogic_dev->vcc_i2c);
+ cclogic_dev->regulator_en = false;
+ }
+ } else{
+ if (!cclogic_dev->regulator_en) {
+ ret = cclogic_reg_set_optimum_mode_check(
+ cclogic_dev->vcc_i2c, CCLOGIC_I2C_LOAD_UA);
+ if (ret < 0) {
+ dev_err(&cclogic_dev->i2c_client->dev,
+ "%s-->Regulator vcc_i2c set_opt failed rc=%d\n",
+ __func__, ret);
+ goto error_reg_opt_i2c;
+ }
+ ret = regulator_enable(cclogic_dev->vcc_i2c);
+ if (ret) {
+ dev_err(&cclogic_dev->i2c_client->dev,
+ "%s-->Regulator vcc_i2c enable failed rc=%d\n",
+ __func__, ret);
+ goto error_reg_en_vcc_i2c;
+ }
+ cclogic_dev->regulator_en = true;
+ }
+ }
+ }
+ return 0;
+
+error_reg_en_vcc_i2c:
+ cclogic_reg_set_optimum_mode_check(cclogic_dev->vcc_i2c, 0);
+error_reg_opt_i2c:
+ cclogic_dev->regulator_en = false;
+ return ret;
+}
+
+static int cclogic_regulator_configure(struct cclogic_dev *cclogic_dev, bool on)
+{
+ int ret = 0;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!cclogic_dev->platform_data->i2c_pull_up)
+ return 0;
+ if (on == false && cclogic_dev->vcc_i2c) {
+ if (regulator_count_voltages(cclogic_dev->vcc_i2c) > 0)
+ regulator_set_voltage(cclogic_dev->vcc_i2c, 0,
+ CCLOGIC_I2C_VTG_MAX_UV);
+ regulator_put(cclogic_dev->vcc_i2c);
+ cclogic_dev->vcc_i2c = NULL;
+ } else if (!cclogic_dev->vcc_i2c) {
+ cclogic_dev->vcc_i2c =
+ regulator_get(&cclogic_dev->i2c_client->dev, "vcc_i2c");
+ if (IS_ERR(cclogic_dev->vcc_i2c)) {
+ dev_err(&cclogic_dev->i2c_client->dev,
+ "%s: Failed to get i2c regulator\n",
+ __func__);
+ ret = PTR_ERR(cclogic_dev->vcc_i2c);
+ goto err_get_vtg_i2c;
+ }
+
+ if (regulator_count_voltages(cclogic_dev->vcc_i2c) > 0) {
+ ret = regulator_set_voltage(cclogic_dev->vcc_i2c,
+ CCLOGIC_I2C_VTG_MIN_UV,
+ CCLOGIC_I2C_VTG_MAX_UV);
+ if (ret) {
+ dev_err(&cclogic_dev->i2c_client->dev,
+ "%s-->reg set i2c vtg failed ret =%d\n",
+ __func__, ret);
+ goto err_set_vtg_i2c;
+ }
+ }
+ }
+
+ return 0;
+
+err_set_vtg_i2c:
+ regulator_put(cclogic_dev->vcc_i2c);
+err_get_vtg_i2c:
+ cclogic_dev->vcc_i2c = NULL;
+ return ret;
+
+};
+
+static int cclogic_irq_enable(struct cclogic_dev *cclogic_dev, bool enable)
+{
+ int ret = 0;
+
+ pr_debug("[%s][%d] enable=%d irq_enabled=%d\n", __func__, __LINE__,
+ enable, cclogic_dev->irq_enabled);
+
+ if (enable) {
+ if (!cclogic_dev->irq_enabled) {
+ enable_irq(cclogic_dev->irq_working);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ enable_irq(cclogic_dev->irq_plug);
+ cclogic_dev->irq_enabled = true;
+ }
+ } else {
+ if (cclogic_dev->irq_enabled) {
+ disable_irq(cclogic_dev->irq_working);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ disable_irq(cclogic_dev->irq_plug);
+ cclogic_dev->irq_enabled = false;
+ }
+ }
+
+ return ret;
+}
+
+static int cclogic_parse_dt(struct device *dev, struct cclogic_platform *pdata)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *temp;
+ int idx = 0;
+ int ret;
+ unsigned int val;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ pdata->chip_num = 0;
+
+ pdata->irq_working = of_get_named_gpio_flags(np, "cc_logic,irq-working", 0,
+ &pdata->irq_working_flags);
+ pdata->irq_plug = of_get_named_gpio_flags(np, "cc_logic,irq-plug", 0,
+ &pdata->irq_plug_flags);
+ pdata->function_switch_gpio1 = of_get_named_gpio(np,
+ "cc_logic,function-switch-gpio1", 0);
+ pdata->function_switch_gpio10 = of_get_named_gpio(np,
+ "cc_logic,function-switch-gpio10", 0);
+ pdata->function_switch_gpio2 = of_get_named_gpio(np,
+ "cc_logic,function-switch-gpio2", 0);
+ pdata->usb_ss_gpio = of_get_named_gpio(np, "cc_logic,usb-ss-gpio", 0);
+ pdata->enb_gpio = of_get_named_gpio(np, "cc_logic,power-control", 0);
+ pdata->i2c_pull_up = of_property_read_bool(np, "cc_logic,i2c-pull-up");
+
+ pdata->ccchip_power_gpio = of_get_named_gpio_flags(np,
+ "cc_logic,bypass-power-control", 0, NULL);
+
+ if (of_get_property(np, "vcc_otg-supply", NULL)) {
+ pdata->vbus_reg = devm_regulator_get(dev, "vcc_otg");
+ if (IS_ERR(pdata->vbus_reg)) {
+ dev_err(dev, "Failed to get vbus regulator\n");
+ ret = PTR_ERR(pdata->vbus_reg);
+ return ret;
+ }
+ }
+
+ for_each_child_of_node(np, temp) {
+ if (idx > CCLOGIC_MAX_SUPPORT_CHIP) {
+ dev_err(dev, "%s-->too many devices\n", __func__);
+ break;
+ }
+ ret = of_property_read_string(temp, "chip-name",
+ &pdata->chip[idx].chip_name);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(temp, "chip-address", &val);
+ if (ret)
+ return ret;
+
+ pdata->chip[idx].address = val;
+
+ pdata->chip[idx].enb = of_property_read_bool(temp, "cc_logic,power-active-high");
+
+ pr_debug("%s--> chip:%s, address:0x%02x, enb=%d\n", __func__,
+ pdata->chip[idx].chip_name, val, pdata->chip[idx].enb);
+
+ idx++;
+ }
+
+ pdata->chip_num = idx;
+
+ return 0;
+}
+
+static irqreturn_t cclogic_irq(int irq, void *data)
+{
+ struct cclogic_dev *cclogic_dev = (struct cclogic_dev *)data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!cclogic_dev || !cclogic_dev->i2c_client)
+ return IRQ_HANDLED;
+
+ if (!wake_lock_active(&cclogic_dev->wakelock))
+ wake_lock(&cclogic_dev->wakelock);
+ cancel_delayed_work(&cclogic_dev->work);
+ schedule_delayed_work(&cclogic_dev->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cclogic_plug_irq(int irq, void *data)
+{
+ struct cclogic_dev *cclogic_dev = (struct cclogic_dev *)data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (!cclogic_dev || !cclogic_dev->i2c_client)
+ return IRQ_HANDLED;
+
+ if (!wake_lock_active(&cclogic_dev->wakelock_plug)) {
+ pm_runtime_get(cclogic_dev->dev);
+ wake_lock(&cclogic_dev->wakelock_plug);
+ }
+
+ m_plug_state = 1;
+
+ schedule_delayed_work(&cclogic_dev->plug_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef DEV_STAGE_DEBUG
+static ssize_t cclogic_show_real_status(struct cclogic_state *pdata, char *buf)
+{
+ char *vbus = NULL;
+ char *charging = NULL;
+ char *port = NULL;
+ char *polarity = NULL;
+ char *evt = NULL;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ switch (pdata->evt) {
+ case CCLOGIC_EVENT_NONE:
+ evt = "No Event";
+ break;
+ case CCLOGIC_EVENT_DETACHED:
+ evt = "Detached Event";
+ break;
+ case CCLOGIC_EVENT_ATTACHED:
+ evt = "Attached Event";
+ break;
+ default:
+ evt = "unknown event";
+ }
+
+ if (pdata->vbus)
+ vbus = "vbus=1";
+ else
+ vbus = "vbus=0";
+
+ switch (pdata->cc) {
+ case CCLOGIC_CC1:
+ polarity = "polarity=cc1";
+ break;
+ case CCLOGIC_CC2:
+ polarity = "polarity=cc2";
+ break;
+ case CCLOGIC_CC_UNKNOWN:
+ polarity = "polarity=unknown";
+ break;
+ case CCLOGIC_CC1_CC2:
+ polarity = "polarity=cc1_cc2";
+ break;
+ }
+
+ switch (pdata->device) {
+ case CCLOGIC_NO_DEVICE:
+ port = "attached=nothing";
+ break;
+ case CCLOGIC_USB_DEVICE:
+ port = "attached=device";
+ break;
+ case CCLOGIC_USB_HOST:
+ port = "attached=host";
+ break;
+ case CCLOGIC_DEBUG_DEVICE:
+ if (pdata->vbus)
+ port = "attached=debug_vbus";
+ else
+ port = "attached=debug_novbus";
+ break;
+ case CCLOGIC_AUDIO_DEVICE:
+ port = "attached=audio";
+ break;
+ case CCLOGIC_DEVICE_UNKNOWN:
+ port = "attached=unknown";
+ break;
+ }
+
+ switch (pdata->charger) {
+ case CCLOGIC_CURRENT_NONE:
+ charging = "charging=none";
+ break;
+ case CCLOGIC_CURRENT_DEFAULT:
+ charging = "charging=default";
+ break;
+ case CCLOGIC_CURRENT_MEDIUM:
+ charging = "charging=medium";
+ break;
+ case CCLOGIC_CURRENT_ACCESSORY:
+ charging = "charging=from accessory";
+ break;
+ case CCLOGIC_CURRENT_HIGH:
+ charging = "charging=high";
+ break;
+ }
+
+ return sprintf(buf, " %15s:%8s;%25s;%23s;%15s\n",
+ evt, vbus, charging, port, polarity);
+
+}
+
+static ssize_t cclogic_set_chip_power_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int value;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->ccchip_power_gpio)) {
+ if (sscanf(buf, "%d", &value) != 1) {
+ dev_err(dev, "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+
+ gpio_set_value_cansleep(cclogic_dev->platform_data->ccchip_power_gpio, value);
+ }
+
+ return count;
+}
+
+static ssize_t cclogic_get_chip_power_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int ret;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->ccchip_power_gpio)) {
+ ret = gpio_get_value_cansleep(cclogic_dev->platform_data->ccchip_power_gpio);
+ if (!ret)
+ return sprintf(buf, "disabled\n");
+ else
+ return sprintf(buf, "enabled\n");
+ } else
+ return sprintf(buf, "No chip power control pin\n");
+}
+
+static DEVICE_ATTR(chip_power, S_IRUGO|S_IWUSR, cclogic_get_chip_power_enable, cclogic_set_chip_power_enable);
+
+static ssize_t cclogic_chip_store_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int value;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->enb_gpio)) {
+ if (sscanf(buf, "%d", &value) != 1) {
+ dev_err(dev, "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+
+ gpio_set_value_cansleep(cclogic_dev->platform_data->enb_gpio, cclogic_dev->enb == value);
+ }
+
+ return count;
+}
+
+static ssize_t cclogic_chip_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int ret;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->enb_gpio)) {
+ ret = gpio_get_value_cansleep(cclogic_dev->platform_data->enb_gpio);
+ if (ret == cclogic_dev->enb)
+ return sprintf(buf, "enabled\n");
+ else
+ return sprintf(buf, "disabled\n");
+ } else
+ return sprintf(buf, "No enabled pin\n");
+}
+
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, cclogic_chip_show_enable, cclogic_chip_store_enable);
+
+static ssize_t cclogic_show_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int ret;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+#ifdef CCLOGIC_UPDATE_REAL_STATUS
+ pm_runtime_get_sync(dev);
+ mutex_lock(&cclogic_ops_lock);
+ if (cclogic_dev->ops && cclogic_dev->ops->get_state) {
+ ret = cclogic_dev->ops->get_state(cclogic_dev->i2c_client,
+ &cclogic_dev->state);
+ if (ret) {
+ mutex_unlock(&cclogic_ops_lock);
+ pm_runtime_put(dev);
+ return sprintf(buf, "error\n");
+ }
+ } else{
+ mutex_unlock(&cclogic_ops_lock);
+ pm_runtime_put(dev);
+ return sprintf(buf, "no chip\n");
+ }
+ mutex_unlock(&cclogic_ops_lock);
+ pm_runtime_put(dev);
+
+#endif
+ cclogic_patch_state(cclogic_dev);
+ return cclogic_show_real_status(&cclogic_dev->state, buf);
+}
+
+static DEVICE_ATTR(status, S_IRUGO, cclogic_show_status, NULL);
+
+#endif
+
+static ssize_t cclogic_show_cc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ return sprintf(buf, "%d\n", cclogic_dev->state.cc);
+
+}
+
+static DEVICE_ATTR(cc, S_IRUGO, cclogic_show_cc, NULL);
+
+static ssize_t cclogic_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int size;
+ int reg, value;
+
+ if (cclogic_dev->ops && cclogic_dev->ops->read) {
+ reg = 0x08;
+ value = cclogic_dev->ops->read(cclogic_dev->i2c_client, reg);
+ size += sprintf(&buf[size], "[0x%02x] register = 0x%02x\n", reg, value);
+ pr_debug("[0x%x] = [0x%x]\n", reg, value);
+ reg = 0x09;
+ value = cclogic_dev->ops->read(cclogic_dev->i2c_client, reg);
+ size += sprintf(&buf[size], "[0x%02x] register = 0x%02x\n", reg, value);
+ pr_debug("[0x%x] = [0x%x]\n", reg, value);
+ reg = 0x0A;
+ value = cclogic_dev->ops->read(cclogic_dev->i2c_client, reg);
+ size += sprintf(&buf[size], "[0x%02x] register = 0x%02x\n", reg, value);
+ pr_debug("[0x%x] = [0x%x]\n", reg, value);
+ reg = 0x45;
+ value = cclogic_dev->ops->read(cclogic_dev->i2c_client, reg);
+ size += sprintf(&buf[size], "[0x%02x] register = 0x%02x\n", reg, value);
+ pr_debug("[0x%x] = [0x%x]\n", reg, value);
+ reg = 0xA0;
+ value = cclogic_dev->ops->read(cclogic_dev->i2c_client, reg);
+ size += sprintf(&buf[size], "[0x%02x] register = 0x%02x\n", reg, value);
+ pr_debug("[0x%x] = [0x%x]\n", reg, value);
+ }
+
+ return size;
+}
+
+static ssize_t cclogic_reg_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ int reg, value;
+ char rw[10];
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+ gpio_set_value_cansleep(cclogic_dev->platform_data->enb_gpio, 1);
+
+ sscanf(buf, "%s %x %x", rw, &reg, &value);
+ if (!strcmp(rw, "write")) {
+ if (cclogic_dev->ops && cclogic_dev->ops->read)
+ cclogic_dev->ops->write(cclogic_dev->i2c_client, reg, value);
+ }
+ return size;
+}
+
+static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, cclogic_reg_show, cclogic_reg_store);
+
+
+static void cclogic_func_set(struct cclogic_platform *p, enum cclogic_func_type func)
+{
+ switch (func) {
+ case CCLOGIC_FUNC_HIZ:
+ gpio_set_value_cansleep(p->function_switch_gpio2, 0);
+ if (gpio_is_valid(p->function_switch_gpio1))
+ gpio_set_value_cansleep(p->function_switch_gpio1, 0);
+ if (gpio_is_valid(p->function_switch_gpio10))
+ gpio_set_value_cansleep(p->function_switch_gpio10, 1);
+ break;
+ case CCLOGIC_FUNC_USB:
+ gpio_set_value_cansleep(p->function_switch_gpio2, 1);
+ if (gpio_is_valid(p->function_switch_gpio1))
+ gpio_set_value_cansleep(p->function_switch_gpio1, 0);
+ if (gpio_is_valid(p->function_switch_gpio10))
+ gpio_set_value_cansleep(p->function_switch_gpio10, 0);
+ break;
+ case CCLOGIC_FUNC_AUDIO:
+ gpio_set_value_cansleep(p->function_switch_gpio2, 0);
+ if (gpio_is_valid(p->function_switch_gpio1))
+#ifdef CONFIG_MACH_ZUK_Z2_PLUS
+ gpio_set_value_cansleep(p->function_switch_gpio1, 0);
+#else
+ gpio_set_value_cansleep(p->function_switch_gpio1, 1);
+#endif
+ if (gpio_is_valid(p->function_switch_gpio10))
+ gpio_set_value_cansleep(p->function_switch_gpio10, 0);
+ break;
+ case CCLOGIC_FUNC_UART:
+ gpio_set_value_cansleep(p->function_switch_gpio2, 1);
+ if (gpio_is_valid(p->function_switch_gpio1))
+ gpio_set_value_cansleep(p->function_switch_gpio1, 0);
+ if (gpio_is_valid(p->function_switch_gpio10))
+ gpio_set_value_cansleep(p->function_switch_gpio10, 0);
+ break;
+ }
+}
+
+int cclogic_get_connected_mode(struct cclogic_state *state)
+{
+ if (state->evt == CCLOGIC_EVENT_DETACHED)
+ return CCLOGIC_MODE_NONE;
+
+ if (state->device == CCLOGIC_NO_DEVICE)
+ return CCLOGIC_MODE_NONE;
+ else if (state->device == CCLOGIC_USB_DEVICE)
+ return CCLOGIC_MODE_DFP;
+ else if (state->device == CCLOGIC_USB_HOST)
+ return CCLOGIC_MODE_UFP;
+ else if (state->vbus)
+ return CCLOGIC_MODE_UFP;
+ else
+ return CCLOGIC_MODE_DFP;
+
+ return CCLOGIC_MODE_NONE;
+}
+EXPORT_SYMBOL(cclogic_get_connected_mode);
+
+int cclogic_set_mode(struct cclogic_dev *pdata, int mode)
+{
+ int ret = 0;
+
+ if (pdata->ops->chip_trymode)
+ ret = pdata->ops->chip_trymode(pdata->i2c_client, mode);
+ return ret;
+}
+EXPORT_SYMBOL(cclogic_set_mode);
+
+int cclogic_get_power_role(struct cclogic_state *state)
+{
+ if (cclogic_get_connected_mode(state) == CCLOGIC_MODE_NONE)
+ return CCLOGIC_POWER_NONE;
+
+ if (state->vbus)
+ return CCLOGIC_POWER_SINK;
+ else
+ return CCLOGIC_POWER_SOURCE;
+}
+EXPORT_SYMBOL(cclogic_get_power_role);
+
+int cclogic_set_power_role(struct cclogic_dev *pdata, int role)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cclogic_set_power_role);
+
+int cclogic_get_data_role(struct cclogic_state *state)
+{
+ if (cclogic_get_connected_mode(state) == CCLOGIC_MODE_NONE)
+ return CCLOGIC_DATA_NONE;
+
+ if (state->device == CCLOGIC_USB_DEVICE)
+ return CCLOGIC_DATA_HOST;
+ else if (state->device == CCLOGIC_USB_HOST)
+ return CCLOGIC_DATA_DEVICE;
+
+ return CCLOGIC_DATA_NONE;
+}
+EXPORT_SYMBOL(cclogic_get_data_role);
+
+int cclogic_set_data_role(struct cclogic_dev *pdata, int role)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cclogic_set_data_role);
+
+static int cc_otg_state;
+int cclogic_get_otg_state(void)
+{
+ return cc_otg_state;
+}
+EXPORT_SYMBOL(cclogic_get_otg_state);
+
+static int cclogic_do_real_work(struct cclogic_state *state,
+ struct cclogic_dev *pdata)
+{
+ int ret = 0;
+ struct cclogic_platform *p = pdata->platform_data;
+ static int cclogic_last_status;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+ cc_otg_state = 0;
+ switch (state->evt) {
+ case CCLOGIC_EVENT_DETACHED:
+ cclogic_notifier_call_chain(CCLOGIC_EVENT_DETACHED, NULL);
+ pr_debug("%s-->cable detached\n", __func__);
+ cclogic_vbus_power_on(pdata, false);
+ cclogic_func_set(p, CCLOGIC_FUNC_UART);
+ ret = pdata->ops->chip_config(pdata->i2c_client);
+ goto out;
+ case CCLOGIC_EVENT_NONE:
+ pr_debug("%s-->No event\n", __func__);
+ break;
+ case CCLOGIC_EVENT_ATTACHED:
+ cclogic_notifier_call_chain(CCLOGIC_EVENT_ATTACHED, NULL);
+ pr_debug("%s-->cable attached\n", __func__);
+ break;
+ }
+
+ if (state->device == CCLOGIC_USB_DEVICE) {/* in order to disable usb3.0 when in host mode */
+ switch (state->cc) {
+ case CCLOGIC_CC1:
+ pr_debug("%s-->usb_ss signal to cc2\n", __func__);
+ if (gpio_is_valid(p->usb_ss_gpio))
+ gpio_set_value_cansleep(p->usb_ss_gpio, 0);
+ break;
+ case CCLOGIC_CC2:
+ pr_debug("%s-->usb_ss signal to cc1\n", __func__);
+ if (gpio_is_valid(p->usb_ss_gpio))
+ gpio_set_value_cansleep(p->usb_ss_gpio, 1);
+ break;
+ case CCLOGIC_CC_UNKNOWN:
+ default:
+ pr_debug("%s-->usb_ss signal to unknown\n", __func__);
+ ret = -1;
+ break;
+ }
+
+ } else {
+ switch (state->cc) {
+ case CCLOGIC_CC1:
+ pr_debug("%s-->usb_ss signal to cc1\n", __func__);
+ if (gpio_is_valid(p->usb_ss_gpio))
+ gpio_set_value_cansleep(p->usb_ss_gpio, 1);
+ break;
+ case CCLOGIC_CC2:
+ pr_debug("%s-->usb_ss signal to cc2\n", __func__);
+ if (gpio_is_valid(p->usb_ss_gpio))
+ gpio_set_value_cansleep(p->usb_ss_gpio, 0);
+ break;
+ case CCLOGIC_CC_UNKNOWN:
+ default:
+ pr_debug("%s-->usb_ss signal to unknown\n", __func__);
+ ret = -1;
+ break;
+ }
+ }
+
+ switch (state->device) {
+ case CCLOGIC_NO_DEVICE:/* nothing attached */
+ pr_debug("%s-->nothing attached,switch to UART\n", __func__);
+ cclogic_vbus_power_on(pdata, false);
+ cclogic_func_set(p, CCLOGIC_FUNC_UART);
+ ret = pdata->ops->chip_config(pdata->i2c_client);
+ break;
+ case CCLOGIC_USB_DEVICE:
+ pr_debug("%s-->function switch set to usb host\n", __func__);
+ cc_otg_state = 1;
+ cclogic_func_set(p, CCLOGIC_FUNC_HIZ);
+ cclogic_vbus_power_on(pdata, true);
+ msleep(300);
+ cclogic_func_set(p, CCLOGIC_FUNC_USB);
+ break;
+ case CCLOGIC_USB_HOST:
+ pr_debug("%s-->function switch set to usb device\n", __func__);
+ cclogic_vbus_power_on(pdata, false);
+ cclogic_func_set(p, CCLOGIC_FUNC_USB);
+ break;
+ case CCLOGIC_DEBUG_DEVICE:
+ pr_debug("%s-->function switch set to debug device,vbus=%d\n", __func__, state->vbus);
+ cclogic_func_set(p, CCLOGIC_FUNC_HIZ);
+ break;
+ case CCLOGIC_AUDIO_DEVICE:
+ pr_debug("%s-->function switch set to audio device(HiZ)\n", __func__);
+ cclogic_func_set(p, CCLOGIC_FUNC_HIZ);
+ break;
+ case CCLOGIC_DEVICE_UNKNOWN:
+ default:
+ dev_err(&pdata->i2c_client->dev, "%s-->function unknown, switch to HiZ\n",
+ __func__);
+ cclogic_func_set(p, CCLOGIC_FUNC_HIZ);
+ ret = -1;
+ }
+
+out:
+ if (pdata->typec_version == 11)
+ cclogic_class_update_state(&pdata->cdev);
+ if (unlikely(state->evt == CCLOGIC_EVENT_DETACHED &&
+ cclogic_last_status == CCLOGIC_EVENT_DETACHED)) {
+ msleep(20);
+ pr_debug("cclogic: reset driver ic, try again\n");
+ ret = pdata->ops->chip_reset(pdata->i2c_client);
+ }
+ cclogic_last_status = state->evt;
+
+ return ret;
+}
+
+static void cclogic_do_work(struct work_struct *w)
+{
+ struct cclogic_dev *pdata = container_of(w,
+ struct cclogic_dev, work.work);
+ int ret = 0;
+ static int retries;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (pm_runtime_suspended(pdata->dev))
+ return;
+
+ mutex_lock(&cclogic_ops_lock);
+
+ ret = pdata->ops->get_state(pdata->i2c_client, &pdata->state);
+ if (ret)
+ goto work_end;
+
+ if (pdata->ops->ack_irq) {
+ ret = pdata->ops->ack_irq(pdata->i2c_client);
+ if (ret)
+ goto work_end;
+ }
+
+ cclogic_patch_state(pdata);
+ ret = cclogic_do_real_work(&pdata->state, pdata);
+
+work_end:
+ mutex_unlock(&cclogic_ops_lock);
+
+ if (ret || !gpio_get_value(cclogic_priv->platform_data->irq_working)) {
+ retries++;
+ if (retries <= CCLOGIC_MAX_RETRIES) {
+ schedule_delayed_work(&pdata->work, msecs_to_jiffies(100));
+ return;
+ } else
+ pr_err("[%s][%d] still in error,more than %d retries\n", __func__, __LINE__, CCLOGIC_MAX_RETRIES);
+ }
+
+ if (wake_lock_active(&pdata->wakelock))
+ wake_unlock(&pdata->wakelock);
+ retries = 0;
+}
+
+static void cclogic_do_plug_work(struct work_struct *w)
+{
+ struct cclogic_dev *pdata = container_of(w,
+ struct cclogic_dev, plug_work.work);
+ struct cclogic_platform *p = pdata->platform_data;
+ static int retries;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (m_plug_state) {
+ if (gpio_get_value(pdata->platform_data->irq_plug)) {
+ if (retries < 10) {
+ retries++;
+ schedule_delayed_work(&pdata->plug_work, msecs_to_jiffies(100));
+ } else{
+ m_plug_state = 0;
+ cancel_delayed_work(&cclogic_priv->work);
+ flush_delayed_work(&cclogic_priv->work);
+ cclogic_vbus_power_on(pdata, false);
+ cclogic_func_set(p, CCLOGIC_FUNC_UART);
+ retries = 0;
+ if (wake_lock_active(&cclogic_priv->wakelock_plug)) {
+ pm_runtime_put(pdata->dev);
+ wake_unlock(&cclogic_priv->wakelock_plug);
+ }
+ }
+ } else{
+ retries = 0;
+ if (wake_lock_active(&cclogic_priv->wakelock_plug))
+ wake_unlock(&cclogic_priv->wakelock_plug);
+ }
+ } else{
+ retries = 0;
+ if (wake_lock_active(&cclogic_priv->wakelock_plug)) {
+ pm_runtime_put(pdata->dev);
+ wake_unlock(&cclogic_priv->wakelock_plug);
+ }
+ }
+}
+
+static int cclogic_init_gpio(struct cclogic_dev *cclogic_dev)
+{
+ struct i2c_client *client = cclogic_dev->i2c_client;
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+ int ret = 0;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(pdata->function_switch_gpio1)) {
+ ret = gpio_request(pdata->function_switch_gpio1,
+ "cclogic_func_gpio1");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->function_switch_gpio1);
+ goto err_gpio1;
+ }
+ ret = gpio_direction_output(pdata->function_switch_gpio1, 0);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->function_switch_gpio1);
+ goto err_gpio1_dir;
+ }
+ }
+
+ if (gpio_is_valid(pdata->function_switch_gpio10)) {
+ ret = gpio_request(pdata->function_switch_gpio10,
+ "cclogic_func_gpio10");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->function_switch_gpio10);
+ goto err_gpio1_dir;
+ }
+ ret = gpio_direction_output(pdata->function_switch_gpio10, 0);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->function_switch_gpio10);
+ goto err_gpio10_dir;
+ }
+ }
+
+ if (gpio_is_valid(pdata->function_switch_gpio2)) {
+ ret = gpio_request(pdata->function_switch_gpio2,
+ "cclogic_func_gpio2");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->function_switch_gpio2);
+ goto err_gpio10_dir;
+ }
+ ret = gpio_direction_output(pdata->function_switch_gpio2, 1);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->function_switch_gpio2);
+ goto err_gpio2_dir;
+ }
+ } else {
+ ret = -ENODEV;
+ dev_err(&client->dev,
+ "%s-->function_switch_gpio2 not provided\n", __func__);
+ goto err_gpio10_dir;
+ }
+
+ if (gpio_is_valid(pdata->usb_ss_gpio)) {
+ ret = gpio_request(pdata->usb_ss_gpio, "usb_ss_gpio");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->usb_ss_gpio);
+ goto err_gpio2_dir;
+ }
+ ret = gpio_direction_output(pdata->usb_ss_gpio, 0);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->usb_ss_gpio);
+ goto err_ss_gpio;
+ }
+ }
+
+ if (gpio_is_valid(pdata->enb_gpio)) {
+ ret = gpio_request(pdata->enb_gpio, "enb_gpio");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->enb_gpio);
+ goto err_ss_gpio;
+ }
+ ret = gpio_direction_output(pdata->enb_gpio, !cclogic_dev->enb);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->enb_gpio);
+ goto err_enb_gpio;
+ }
+ }
+
+ if (gpio_is_valid(pdata->ccchip_power_gpio)) {
+ ret = gpio_request(pdata->ccchip_power_gpio, "chip_power_gpio");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, pdata->ccchip_power_gpio);
+ goto err_enb_gpio;
+ }
+ ret = gpio_direction_output(pdata->ccchip_power_gpio, 1);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, pdata->ccchip_power_gpio);
+ goto err_cc_power_gpio;
+ }
+ }
+
+ return ret;
+
+err_cc_power_gpio:
+ if (gpio_is_valid(pdata->ccchip_power_gpio))
+ gpio_free(pdata->ccchip_power_gpio);
+
+err_enb_gpio:
+ if (gpio_is_valid(pdata->enb_gpio))
+ gpio_free(pdata->enb_gpio);
+
+err_ss_gpio:
+ if (gpio_is_valid(pdata->usb_ss_gpio))
+ gpio_free(pdata->usb_ss_gpio);
+
+err_gpio2_dir:
+ if (gpio_is_valid(pdata->function_switch_gpio2))
+ gpio_free(pdata->function_switch_gpio2);
+
+err_gpio10_dir:
+ if (gpio_is_valid(pdata->function_switch_gpio10))
+ gpio_free(pdata->function_switch_gpio10);
+
+err_gpio1_dir:
+ if (gpio_is_valid(pdata->function_switch_gpio1))
+ gpio_free(pdata->function_switch_gpio1);
+err_gpio1:
+ return ret;
+}
+
+static int cclogic_remove_gpio(struct cclogic_dev *cclogic_dev)
+{
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(pdata->enb_gpio))
+ gpio_free(pdata->enb_gpio);
+
+ if (gpio_is_valid(pdata->usb_ss_gpio))
+ gpio_free(pdata->usb_ss_gpio);
+
+ if (gpio_is_valid(pdata->function_switch_gpio2))
+ gpio_free(pdata->function_switch_gpio2);
+
+ if (gpio_is_valid(pdata->function_switch_gpio1))
+ gpio_free(pdata->function_switch_gpio1);
+
+ if (gpio_is_valid(pdata->function_switch_gpio10))
+ gpio_free(pdata->function_switch_gpio10);
+
+ if (gpio_is_valid(pdata->ccchip_power_gpio))
+ gpio_free(pdata->ccchip_power_gpio);
+
+ return 0;
+
+}
+
+/* cclogic_match_id() */
+static int cclogic_match_id(const char *name)
+{
+ int i = 0;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (unlikely(!name)) {
+ pr_err("%s-->No name\n", __func__);
+ return -ENODEV;
+ }
+
+ if (cclogic_priv && cclogic_priv->platform_data) {
+ for (i = 0; i < cclogic_priv->platform_data->chip_num; i++) {
+ struct cclogic_of_chip *chip =
+ &cclogic_priv->platform_data->chip[i];
+
+ if (!chip->chip_name) {
+ pr_err("%s-->%s mismatch\n", __func__, name);
+ return -ENODEV;
+ }
+ if (!strcmp(chip->chip_name, name))
+ break;
+ }
+ } else {
+ pr_err("%s-->%s mismatch\n", __func__, name);
+ return -ENODEV;
+ }
+
+ if (i >= cclogic_priv->platform_data->chip_num) {
+ pr_err("%s-->%s mismatch\n", __func__, name);
+ return -ENODEV;
+ }
+ return i;
+}
+
+/* cclogic_register() */
+int cclogic_register(struct cclogic_chip *c)
+{
+ int ret = 0;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ mutex_lock(&cclogic_ops_lock);
+ if (cclogic_priv->ops) {
+ mutex_unlock(&cclogic_ops_lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&cclogic_ops_lock);
+
+ ret = cclogic_match_id(c->chip_name);
+ if (ret < 0)
+ return ret;
+
+ c->addr = cclogic_priv->platform_data->chip[ret].address;
+ cclogic_priv->enb = cclogic_priv->platform_data->chip[ret].enb;
+
+ if (!c->chip_check)
+ return -ENODEV;
+
+ pm_runtime_get_sync(cclogic_priv->dev);
+ wake_lock(&cclogic_priv->wakelock_plug);
+ msleep(100);
+
+ mutex_lock(&cclogic_ops_lock);
+
+ cclogic_priv->i2c_client->addr = c->addr;
+
+ if (c->chip_reset) {
+ ret = c->chip_reset(cclogic_priv->i2c_client);
+ if (ret)
+ goto err_ret;
+ }
+ ret = c->chip_check(cclogic_priv->i2c_client);
+ if (ret)
+ goto err_ret;
+ ret = c->chip_config(cclogic_priv->i2c_client);
+ if (ret)
+ goto err_ret;
+
+ pr_info("%s select chip:%s\n", __func__, c->chip_name);
+
+ cclogic_priv->ops = c;
+ cclogic_priv->typec_version = c->typec_version;
+
+ mutex_unlock(&cclogic_ops_lock);
+
+ if (cclogic_priv->typec_version == 11) {
+ /* cclogic_priv->cdev.name = c->chip_name; */
+ cclogic_priv->cdev.name = "otg_default";
+ ret = cclogic_class_register(&cclogic_priv->cdev);
+ if (ret) {
+ dev_err(cclogic_priv->dev,
+ "Failed to setup class dev for cclogic driver");
+ goto err_ret1;
+ }
+ }
+
+ cclogic_irq_enable(cclogic_priv, true);
+
+ m_plug_state = 1;
+ schedule_delayed_work(&cclogic_priv->plug_work, 0);
+
+ return 0;
+
+err_ret:
+ mutex_unlock(&cclogic_ops_lock);
+err_ret1:
+ pm_runtime_put(cclogic_priv->dev);
+ return ret;
+}
+EXPORT_SYMBOL(cclogic_register);
+
+/*cclogic_unregister() */
+void cclogic_unregister(struct cclogic_chip *c)
+{
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (cclogic_priv->ops != c)
+ return;
+
+ cancel_delayed_work(&cclogic_priv->work);
+ flush_delayed_work(&cclogic_priv->work);
+ cancel_delayed_work(&cclogic_priv->plug_work);
+ flush_delayed_work(&cclogic_priv->plug_work);
+
+ cclogic_irq_enable(cclogic_priv, false);
+
+ if (wake_lock_active(&cclogic_priv->wakelock))
+ wake_unlock(&cclogic_priv->wakelock);
+ pm_runtime_put(cclogic_priv->dev);
+
+ mutex_lock(&cclogic_ops_lock);
+ cclogic_priv->ops = NULL;
+ mutex_unlock(&cclogic_ops_lock);
+
+ if (cclogic_priv->typec_version == 11) {
+ cclogic_priv->typec_version = 10;
+ cclogic_class_unregister(&cclogic_priv->cdev);
+ }
+
+ return;
+}
+EXPORT_SYMBOL(cclogic_unregister);
+
+static struct attribute *cclogic_attrs[] = {
+#ifdef DEV_STAGE_DEBUG
+ &dev_attr_chip_power.attr,
+ &dev_attr_status.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_reg.attr,
+#endif
+ &dev_attr_cc.attr,
+ NULL,
+};
+
+static struct attribute_group cclogic_attr_group = {
+ .attrs = cclogic_attrs,
+};
+
+/* cclogic_probe() */
+static int cclogic_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int ret = 0;
+ struct cclogic_dev *cclogic_dev = cclogic_priv;
+ struct cclogic_platform *platform_data;
+
+ pr_info("%s start\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "%s: i2c check failed\n", __func__);
+ ret = -ENODEV;
+ goto err_i2c;
+ }
+
+ if (!cclogic_dev) {
+ ret = -ENODEV;
+ goto err_i2c;
+ }
+
+ memset(cclogic_dev, 0, sizeof(*cclogic_dev));
+
+ if (client->dev.of_node) {
+ platform_data = devm_kzalloc(&client->dev,
+ sizeof(*platform_data), GFP_KERNEL);
+ if (!platform_data) {
+ dev_err(&client->dev, "%s-->Failed to allocate memory\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_i2c;
+ }
+
+ ret = cclogic_parse_dt(&client->dev, platform_data);
+ if (ret) {
+ dev_err(&client->dev, "%s-->Failed parse dt\n", __func__);
+ goto err_i2c;
+ }
+ } else
+ platform_data = client->dev.platform_data;
+
+ cclogic_dev->pin = devm_pinctrl_get(&client->dev);
+ if (IS_ERR_OR_NULL(cclogic_dev->pin)) {
+ ret = -ENODEV;
+ goto err_i2c;
+ }
+
+ cclogic_dev->pin_active = pinctrl_lookup_state(cclogic_dev->pin, "cc_active");
+ if (IS_ERR(cclogic_dev->pin_active)) {
+ dev_err(&client->dev, "Unable find cc_active\n");
+ ret = PTR_ERR(cclogic_dev->pin_active);
+ goto err_i2c;
+ }
+
+ cclogic_dev->pin_suspend = pinctrl_lookup_state(cclogic_dev->pin, "cc_sleep");
+ if (IS_ERR(cclogic_dev->pin_suspend)) {
+ dev_err(&client->dev, "Unable find cc_sleep\n");
+ ret = PTR_ERR(cclogic_dev->pin_suspend);
+ goto err_i2c;
+ }
+
+ ret = pinctrl_select_state(cclogic_dev->pin, cclogic_dev->pin_active);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable select active state in pinctrl\n");
+ goto err_i2c;
+ }
+
+
+ cclogic_dev->platform_data = platform_data;
+ cclogic_dev->i2c_client = client;
+ cclogic_dev->dev = &client->dev;
+
+ i2c_set_clientdata(client, cclogic_dev);
+
+ pm_runtime_set_suspended(&client->dev);
+
+ ret = cclogic_regulator_configure(cclogic_dev, true);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s-->Failed to configure regulators\n",
+ __func__);
+ goto err_i2c;
+ }
+
+ ret = cclogic_power_on(cclogic_dev, true);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s-->Failed to power on\n", __func__);
+ goto err_regulator_conf;
+ }
+
+ ret = cclogic_init_gpio(cclogic_dev);
+ if (ret) {
+ dev_err(&client->dev, "%s-->error in set gpio\n", __func__);
+ goto err_regulator_on;
+ }
+
+ pm_runtime_enable(&client->dev);
+
+ if (gpio_is_valid(platform_data->irq_working)) {
+ /* configure cclogic irq working */
+ ret = gpio_request(platform_data->irq_working, "cclogic_irq_working");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, platform_data->irq_working);
+ goto err_set_gpio;
+ }
+ ret = gpio_direction_input(platform_data->irq_working);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, platform_data->irq_working);
+ goto err_irq_working_dir;
+ }
+ } else {
+ dev_err(&client->dev, "%s-->irq gpio not provided\n", __func__);
+ goto err_set_gpio;
+ }
+ cclogic_dev->irq_working = gpio_to_irq(platform_data->irq_working);
+
+ if (gpio_is_valid(platform_data->irq_plug)) {
+ /* configure cclogic irq plug */
+ ret = gpio_request(platform_data->irq_plug, "cclogic_irq_plug");
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to request gpio [%d]\n",
+ __func__, platform_data->irq_plug);
+ goto err_irq_working_dir;
+ }
+ ret = gpio_direction_input(platform_data->irq_plug);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->unable to set direction for gpio [%d]\n",
+ __func__, platform_data->irq_plug);
+ goto err_irq_plug_dir;
+ }
+ cclogic_dev->irq_plug = gpio_to_irq(platform_data->irq_plug);
+ } else {
+ dev_err(&client->dev, "%s-->irq plug gpio not provided\n", __func__);
+ goto err_irq_working_dir;
+ }
+
+
+ wake_lock_init(&cclogic_dev->wakelock, WAKE_LOCK_SUSPEND,
+ "cclogic_wakelock");
+ wake_lock_init(&cclogic_dev->wakelock_plug, WAKE_LOCK_SUSPEND,
+ "cclogic_wakelock_plug");
+
+ device_init_wakeup(cclogic_dev->dev, 1);
+
+ INIT_DELAYED_WORK(&cclogic_dev->work, cclogic_do_work);
+ INIT_DELAYED_WORK(&cclogic_dev->plug_work, cclogic_do_plug_work);
+
+ ret = sysfs_create_group(&client->dev.kobj, &cclogic_attr_group);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s-->Unable to create sysfs for cclogic, errors: %d\n",
+ __func__, ret);
+ goto err_chip_check;
+ }
+
+ ret = request_threaded_irq(cclogic_dev->irq_working, NULL, cclogic_irq,
+ cclogic_dev->platform_data->irq_working_flags | IRQF_ONESHOT, DRIVER_NAME,
+ cclogic_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s: Failed to create working-irq thread\n", __func__);
+ goto err_irq_req;
+ }
+
+ if (gpio_is_valid(platform_data->irq_plug)) {
+ ret = request_threaded_irq(cclogic_dev->irq_plug, NULL, cclogic_plug_irq,
+ cclogic_dev->platform_data->irq_plug_flags | IRQF_ONESHOT, DRIVER_NAME,
+ cclogic_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s: Failed to create plug-irq thread\n", __func__);
+ goto err_irq_enable;
+ }
+ }
+ disable_irq(cclogic_dev->irq_working);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ disable_irq(cclogic_dev->irq_plug);
+
+ pr_info("%s Success\n", __func__);
+
+ return 0;
+
+ cancel_delayed_work_sync(&cclogic_dev->work);
+ cancel_delayed_work_sync(&cclogic_dev->plug_work);
+ cclogic_irq_enable(cclogic_dev, false);
+
+ if (gpio_is_valid(platform_data->irq_plug))
+ free_irq(cclogic_dev->irq_plug, cclogic_dev);
+
+err_irq_enable:
+ free_irq(cclogic_dev->irq_working, cclogic_dev);
+err_irq_req:
+ sysfs_remove_group(&client->dev.kobj, &cclogic_attr_group);
+err_chip_check:
+ device_init_wakeup(cclogic_dev->dev, 0);
+ wake_lock_destroy(&cclogic_dev->wakelock);
+ wake_lock_destroy(&cclogic_dev->wakelock_plug);
+err_irq_plug_dir:
+ if (gpio_is_valid(platform_data->irq_plug))
+ gpio_free(platform_data->irq_plug);
+err_irq_working_dir:
+ if (gpio_is_valid(platform_data->irq_working))
+ gpio_free(platform_data->irq_working);
+err_set_gpio:
+ cclogic_remove_gpio(cclogic_dev);
+err_regulator_on:
+ cclogic_power_on(cclogic_dev, false);
+err_regulator_conf:
+ cclogic_regulator_configure(cclogic_dev, false);
+err_i2c:
+ dev_err(&client->dev, "%s Failed\n", __func__);
+
+ return ret;
+}
+
+/* cclogic_remove() */
+static int cclogic_remove(struct i2c_client *client)
+{
+ struct cclogic_dev *cclogic_dev;
+
+ pr_info("%s\n", __func__);
+
+ cclogic_dev = i2c_get_clientdata(client);
+
+ cclogic_irq_enable(cclogic_dev, false);
+
+ cclogic_vbus_power_on(cclogic_dev, false);
+
+ sysfs_remove_group(&client->dev.kobj, &cclogic_attr_group);
+
+ device_init_wakeup(cclogic_dev->dev, 0);
+ wake_lock_destroy(&cclogic_dev->wakelock);
+ wake_lock_destroy(&cclogic_dev->wakelock_plug);
+
+ cancel_delayed_work_sync(&cclogic_dev->work);
+ cancel_delayed_work_sync(&cclogic_dev->plug_work);
+
+ free_irq(cclogic_dev->irq_working, cclogic_dev);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ free_irq(cclogic_dev->irq_plug, cclogic_dev);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->ccchip_power_gpio))
+ gpio_set_value(cclogic_dev->platform_data->ccchip_power_gpio, 0);
+
+ disable_irq_wake(cclogic_dev->irq_working);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ disable_irq_wake(cclogic_dev->irq_plug);
+
+ cclogic_remove_gpio(cclogic_dev);
+
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_working))
+ gpio_free(cclogic_dev->platform_data->irq_working);
+ if (gpio_is_valid(cclogic_dev->platform_data->irq_plug))
+ gpio_free(cclogic_dev->platform_data->irq_plug);
+
+ pinctrl_select_state(cclogic_dev->pin, cclogic_dev->pin_suspend);
+
+ cclogic_power_on(cclogic_dev, false);
+
+ cclogic_regulator_configure(cclogic_dev, false);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cclogic_runtime_suspend(struct device *dev)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(pdata->enb_gpio))
+ gpio_direction_output(pdata->enb_gpio, !cclogic_dev->enb);
+
+ return 0;
+}
+
+static int cclogic_runtime_resume(struct device *dev)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ if (gpio_is_valid(pdata->enb_gpio))
+ gpio_direction_output(pdata->enb_gpio, cclogic_dev->enb);
+
+ return 0;
+}
+
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int cclogic_suspend(struct device *dev)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ disable_irq(cclogic_dev->irq_working);
+ enable_irq_wake(cclogic_dev->irq_working);
+ if (gpio_is_valid(pdata->irq_plug)) {
+ disable_irq(cclogic_dev->irq_plug);
+ enable_irq_wake(cclogic_dev->irq_plug);
+ }
+
+ cclogic_power_on(cclogic_dev, false);
+ pinctrl_select_state(cclogic_dev->pin, cclogic_dev->pin_suspend);
+
+ return 0;
+}
+
+static int cclogic_resume(struct device *dev)
+{
+ struct cclogic_dev *cclogic_dev = dev_get_drvdata(dev);
+ struct cclogic_platform *pdata = cclogic_dev->platform_data;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ pinctrl_select_state(cclogic_dev->pin, cclogic_dev->pin_active);
+
+ cclogic_power_on(cclogic_dev, true);
+
+ disable_irq_wake(cclogic_dev->irq_working);
+ enable_irq(cclogic_dev->irq_working);
+ if (gpio_is_valid(pdata->irq_plug)) {
+ disable_irq_wake(cclogic_dev->irq_plug);
+ enable_irq(cclogic_dev->irq_plug);
+ }
+
+ return 0;
+}
+#endif
+
+void cclogic_shutdown(struct i2c_client *client)
+{
+ cclogic_remove(client);
+}
+
+static const struct dev_pm_ops cclogic_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cclogic_suspend, cclogic_resume)
+ SET_RUNTIME_PM_OPS(cclogic_runtime_suspend, cclogic_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id cclogic_id_table[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, cclogic_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id cclogic_match_table[] = {
+ { .compatible = "typec,cclogic", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, cclogic_match_table);
+#else
+#define cclogic_match_table NULL
+#endif
+
+static struct i2c_driver cclogic_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cclogic_pm_ops,
+ .of_match_table = cclogic_match_table,
+ },
+ .probe = cclogic_probe,
+ .remove = cclogic_remove,
+ .shutdown = cclogic_shutdown,
+ .id_table = cclogic_id_table,
+};
+
+static int __init cclogic_init(void)
+{
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ mutex_init(&cclogic_ops_lock);
+
+ cclogic_priv = kzalloc(sizeof(*cclogic_priv), GFP_KERNEL);
+ if (cclogic_priv == NULL) {
+ pr_err("%s-->failed to allocate memory for module data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return i2c_add_driver(&cclogic_driver);
+}
+
+static void __exit cclogic_exit(void)
+{
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+
+ i2c_del_driver(&cclogic_driver);
+
+ kfree(cclogic_priv);
+ cclogic_priv = NULL;
+
+ return;
+}
+
+module_init(cclogic_init);
+module_exit(cclogic_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Yang ShaoYing <yangsy2@lenovo.com>");
+MODULE_DESCRIPTION("Drivers core for CC-Logic");
+MODULE_ALIAS("platform:cc-logic");
diff --git a/drivers/misc/cclogic/cclogic-core.h b/drivers/misc/cclogic/cclogic-core.h
new file mode 100644
index 000000000000..7be98e4a619e
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-core.h
@@ -0,0 +1,169 @@
+#ifndef __CCLOGIC_COMMON_H
+#define __CCLOGIC_COMMON_H
+
+#include <linux/wakelock.h>
+#include "cclogic-class.h"
+
+#define DEV_STAGE_DEBUG
+#define CCLOGIC_UPDATE_REAL_STATUS
+
+#ifdef DEBUG
+#undef pr_debug
+#undef pr_info
+#define pr_debug(fmt, args...) pr_err(fmt, ##args)
+#define pr_info(fmt, args...) pr_err(fmt, ##args)
+#endif
+
+
+#define CCLOGIC_I2C_VTG_MIN_UV 1800000
+#define CCLOGIC_I2C_VTG_MAX_UV 1800000
+#define CCLOGIC_I2C_LOAD_UA 1800
+
+#define CCLOGIC_MAX_SUPPORT_CHIP 2
+/* #define CCLOGIC_MAX_RETRIES 100 */
+#define CCLOGIC_MAX_RETRIES 5
+
+struct cclogic_of_chip {
+ const char *chip_name;
+ int enb;
+ int address;
+};
+
+#define CCLOGIC_SUPPORT_MODE_DFP (1<<0)
+#define CCLOGIC_SUPPORT_MODE_UFP (1<<1)
+#define CCLOGIC_SUPPORT_MODE_DUAL ((1<<0)|(1<<1))
+#define CCLOGIC_SUPPORT_POWER_SOURCE (1<<2)
+#define CCLOGIC_SUPPORT_POWER_SINK (1<<3)
+#define CCLOGIC_SUPPORT_POWER_SWAP ((1<<2)|(1<<3))
+#define CCLOGIC_SUPPORT_DATA_HOST (1<<4)
+#define CCLOGIC_SUPPORT_DATA_DEVICE (1<<5)
+#define CCLOGIC_SUPPORT_DATA_SWAP ((1<<4)|(1<<5))
+
+#define CCLOGIC_MODE_NONE 0
+#define CCLOGIC_MODE_DFP 1
+#define CCLOGIC_MODE_UFP 2
+#define CCLOGIC_MODE_DRP 3
+#define CCLOGIC_POWER_NONE 0
+#define CCLOGIC_POWER_SOURCE 1
+#define CCLOGIC_POWER_SINK 2
+#define CCLOGIC_DATA_NONE 0
+#define CCLOGIC_DATA_HOST 1
+#define CCLOGIC_DATA_DEVICE 2
+
+struct cclogic_platform {
+ unsigned int irq_working_flags;
+ unsigned int irq_working;
+ unsigned int irq_plug_flags;
+ unsigned int irq_plug;
+ bool i2c_pull_up;
+ unsigned int function_switch_gpio1;
+ unsigned int function_switch_gpio10;
+ unsigned int function_switch_gpio2;
+ unsigned int usb_ss_gpio;
+ unsigned int enb_gpio;
+ unsigned int ccchip_power_gpio;
+ int chip_num;
+ struct regulator *vbus_reg;
+ struct cclogic_of_chip chip[CCLOGIC_MAX_SUPPORT_CHIP];
+};
+
+
+enum cclogic_attached_type {
+ CCLOGIC_DEVICE_UNKNOWN,
+ CCLOGIC_NO_DEVICE,
+ CCLOGIC_USB_DEVICE,
+ CCLOGIC_USB_HOST,
+ CCLOGIC_DEBUG_DEVICE,
+ CCLOGIC_AUDIO_DEVICE,
+};
+
+enum cclogic_current_type {
+ CCLOGIC_CURRENT_NONE,
+ CCLOGIC_CURRENT_DEFAULT,
+ CCLOGIC_CURRENT_MEDIUM,
+ CCLOGIC_CURRENT_ACCESSORY,
+ CCLOGIC_CURRENT_HIGH,
+};
+
+enum cclogic_event_type {
+ CCLOGIC_EVENT_NONE,
+ CCLOGIC_EVENT_DETACHED,
+ CCLOGIC_EVENT_ATTACHED,
+};
+
+enum cclogic_cc_type {
+ CCLOGIC_CC_UNKNOWN,
+ CCLOGIC_CC1,
+ CCLOGIC_CC2,
+ CCLOGIC_CC1_CC2,
+};
+
+struct cclogic_state {
+ bool vbus;
+ enum cclogic_cc_type cc;
+ enum cclogic_event_type evt;
+ enum cclogic_attached_type device;
+ enum cclogic_current_type charger;
+};
+
+struct cclogic_chip;
+struct cclogic_dev {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ unsigned int irq_working;
+ unsigned int irq_plug;
+ bool irq_enabled;
+ struct regulator *vcc_i2c;
+ bool regulator_en;
+ struct delayed_work work;
+ struct delayed_work plug_work;
+ struct cclogic_platform *platform_data;
+ struct wake_lock wakelock;
+ struct wake_lock wakelock_plug;
+ bool vbus_on;
+ struct cclogic_chip *ops;
+ struct cclogic_state state;
+ struct pinctrl *pin;
+ struct pinctrl_state *pin_active;
+ struct pinctrl_state *pin_suspend;
+ struct cclogic_class_dev cdev;
+ int enb;
+ unsigned int typec_version;
+};
+
+struct cclogic_chip {
+ const char *chip_name;
+ unsigned char addr;
+ unsigned int typec_version;
+ unsigned int support;
+ int (*get_state)(struct i2c_client *client, struct cclogic_state *result);
+ int (*ack_irq)(struct i2c_client *client);
+ int (*chip_config)(struct i2c_client *client);
+ int (*chip_reset)(struct i2c_client *client);
+ int (*chip_check)(struct i2c_client *client);
+ int (*chip_trymode)(struct i2c_client *client, int mode);
+ struct list_head chip_list;
+ int (*read)(struct i2c_client *client, u8 reg);
+ int (*write)(struct i2c_client *client, u8 reg, u8 data);
+};
+
+enum cclogic_func_type {
+ CCLOGIC_FUNC_HIZ,
+ CCLOGIC_FUNC_USB,
+ CCLOGIC_FUNC_AUDIO,
+ CCLOGIC_FUNC_UART,
+};
+
+
+
+extern int cclogic_register(struct cclogic_chip *c);
+extern void cclogic_unregister(struct cclogic_chip *c);
+
+extern int cclogic_get_connected_mode(struct cclogic_state *state);
+extern int cclogic_set_mode(struct cclogic_dev *pdata, int mode);
+extern int cclogic_get_power_role(struct cclogic_state *state);
+extern int cclogic_set_power_role(struct cclogic_dev *pdata, int role);
+extern int cclogic_get_data_role(struct cclogic_state *state);
+extern int cclogic_set_data_role(struct cclogic_dev *pdata, int role);
+
+#endif
diff --git a/drivers/misc/cclogic/pi5usb30216d.c b/drivers/misc/cclogic/pi5usb30216d.c
new file mode 100644
index 000000000000..ba7d6449c736
--- /dev/null
+++ b/drivers/misc/cclogic/pi5usb30216d.c
@@ -0,0 +1,245 @@
+/*
+ * pi5usb30216d.c
+ *
+ * Drivers for usb type-C interface's CC-Logic chip of Pericom
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/wakelock.h>
+#include "cclogic-core.h"
+
+
+#define DRIVER_NAME "pericom,pi5usb30216d"
+
+
+/*
+ * pi5usb30216d_write_i2c()
+ * only modified 0x2 register value.
+ */
+static int pi5usb30216d_write_i2c(struct i2c_client *client, u8 reg, u8 data)
+{
+ int ret = 0;
+ char val[4] = {0};
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ val[reg-1] = data;
+
+ ret = i2c_master_send(client, val, 2);
+ if (ret != 2) {
+ dev_err(&client->dev, "cclogic:%s-->i2c send error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+
+}
+/* pi5usb30216d_parse_cclogic_state() */
+static void pi5usb30216d_parse_cclogic_state(int reg3, int reg4,
+ struct cclogic_state *result)
+{
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ result->vbus = false;
+ result->cc = CCLOGIC_CC_UNKNOWN;
+ result->evt = CCLOGIC_EVENT_NONE;
+ result->device = CCLOGIC_DEVICE_UNKNOWN;
+ result->charger = CCLOGIC_CURRENT_NONE;
+
+ if (reg3 == 0x3) {
+ pr_err("cclogic:%s-->detach and attach in the same time\n",
+ __func__);
+ } else{
+ if (reg3&0x2)
+ result->evt = CCLOGIC_EVENT_DETACHED;
+ if (reg3&0x1)
+ result->evt = CCLOGIC_EVENT_ATTACHED;
+ }
+
+ if (reg4&0x80)
+ result->vbus = true;
+
+ switch (reg4&0x60) {
+ case 0x00:
+ result->charger = CCLOGIC_CURRENT_NONE;
+ break;
+ case 0x20:
+ result->charger = CCLOGIC_CURRENT_DEFAULT;
+ break;
+ case 0x40:
+ result->charger = CCLOGIC_CURRENT_MEDIUM;
+ break;
+ case 0x60:
+ result->charger = CCLOGIC_CURRENT_HIGH;
+ break;
+ }
+
+ switch (reg4&0x3) {
+ case 0x01:
+ result->cc = CCLOGIC_CC1;
+ break;
+ case 0x02:
+ result->cc = CCLOGIC_CC2;
+ break;
+ default:
+ break;
+ }
+
+ switch (reg4&0x1C) {
+ case 0x00:
+ result->device = CCLOGIC_NO_DEVICE;
+ break;
+ case 0x04:
+ result->device = CCLOGIC_USB_DEVICE;
+ break;
+ case 0x08:
+ result->device = CCLOGIC_USB_HOST;
+ break;
+ case 0x0C:
+ result->device = CCLOGIC_AUDIO_DEVICE;
+ break;
+ case 0x10:
+ result->device = CCLOGIC_DEBUG_DEVICE;
+ break;
+ default:
+ result->device = CCLOGIC_DEVICE_UNKNOWN;
+ break;
+ }
+
+}
+
+/* pi5usb30216d_get_state() */
+static int pi5usb30216d_get_state(struct i2c_client *client,
+ struct cclogic_state *state)
+{
+ char reg[4] = {0};
+ int ret = 0;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = i2c_master_recv(client, reg, 4);
+ if (ret != 4) {
+ dev_err(&client->dev, "cclogic:%s-->i2c recv error\n", __func__);
+ return ret;
+ }
+ pr_debug("cclogic:%s-->i2c reg value: 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ __func__, reg[0], reg[1], reg[2], reg[3]);
+
+ pi5usb30216d_parse_cclogic_state(reg[2], reg[3], state);
+
+ return 0;
+}
+
+/* pi5usb30216d_check_chip() */
+static int pi5usb30216d_check_chip(struct i2c_client *client)
+{
+ int ret;
+ char reg_test_r = {0};
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ /* ID check */
+ ret = i2c_master_recv(client, &reg_test_r, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "cclogic:%s: i2c read error\n", __func__);
+ return ret;
+ }
+
+ if (reg_test_r != 0x20) {
+ dev_err(&client->dev, "cclogic:%s: devid mismatch (0x%02x!=0x20)\n",
+ __func__, reg_test_r);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+/* pi5usb30216d_trymode() */
+static int pi5usb30216d_trymode(struct i2c_client *client, int mode)
+{
+ int ret;
+ char reg;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ if (mode == CCLOGIC_MODE_UFP) {
+ reg = 0x60;
+ pr_debug("cclogic:trymode sink\n");
+ } else{
+ reg = 0x62;
+ pr_debug("cclogic:trymode source\n");
+ }
+ ret = pi5usb30216d_write_i2c(client, 0x2, reg);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+/* pi5usb30216d_config_chip() */
+static int pi5usb30216d_config_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = pi5usb30216d_write_i2c(client, 0x2, 0x66);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct cclogic_chip pi5usb30216d_chip = {
+ .chip_name = DRIVER_NAME,
+ .get_state = pi5usb30216d_get_state,
+ .ack_irq = NULL,
+ .chip_config = pi5usb30216d_config_chip,
+ .chip_reset = NULL,
+ .chip_check = pi5usb30216d_check_chip,
+ .chip_trymode = pi5usb30216d_trymode,
+ .typec_version = 11,/* spec 1.1 */
+ .support = CCLOGIC_SUPPORT_MODE_DUAL,
+};
+
+/* pi5usb30216d_init() */
+static int __init pi5usb30216d_init(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return cclogic_register(&pi5usb30216d_chip);
+}
+
+/* pi5usb30216d_exit() */
+static void __exit pi5usb30216d_exit(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ cclogic_unregister(&pi5usb30216d_chip);
+ return;
+}
+
+late_initcall(pi5usb30216d_init);
+module_exit(pi5usb30216d_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Yang ShaoYing <yangsy2@lenovo.com>");
+MODULE_DESCRIPTION("Drivers for Type-C CC-Logic chip of Pericom pi5usb30216dd");
+MODULE_ALIAS("platform:cc-logic");
diff --git a/drivers/misc/cclogic/tusb320hai.c b/drivers/misc/cclogic/tusb320hai.c
new file mode 100644
index 000000000000..d7f9c4f60bb9
--- /dev/null
+++ b/drivers/misc/cclogic/tusb320hai.c
@@ -0,0 +1,445 @@
+/*
+ * tusb320hai.c
+ *
+ * Drivers for usb type-C interface's CC-Logic chip of TI
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/wakelock.h>
+#include "cclogic-core.h"
+
+
+#define DRIVER_NAME "ti,tusb320hai"
+
+#define MAX_REG_NUM 11
+
+#define TUSB320_DEVID "023BSUT"
+
+/* tusb320hai_read_i2c() */
+static int tusb320hai_read_i2c(struct i2c_client *client, u8 reg)
+{
+ struct i2c_msg msg[2];
+ int data = 0;
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &reg;
+ msg[0].len = sizeof(reg);
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = (char *)&data;
+ msg[1].len = 1;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret != 2) {
+ dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n",
+ __func__);
+ return ret;
+ }
+
+ return data;
+}
+
+/* tusb320hai_recv_i2c() */
+static int tusb320hai_recv_i2c(struct i2c_client *client, char baseaddr,
+ char *buf, int len)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &baseaddr;
+ msg[0].len = 1;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = len;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret != 2) {
+ dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+/* tusb320hai_write_i2c() */
+static int tusb320hai_write_i2c(struct i2c_client *client, u8 reg, u8 data)
+{
+ int ret = 0;
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ buf[0] = reg;
+ buf[1] = data;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = sizeof(buf);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+
+}
+
+/* tusb320hai_parse_cclogic_state() */
+static void tusb320hai_parse_cclogic_state(int reg8, int reg9,
+ struct cclogic_state *result)
+{
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ result->vbus = false;
+ result->cc = CCLOGIC_CC_UNKNOWN;
+ result->evt = CCLOGIC_EVENT_NONE;
+ result->device = CCLOGIC_NO_DEVICE;
+ result->charger = CCLOGIC_CURRENT_NONE;
+
+ if (!(reg9&0x10)) {/* No interrupt */
+ result->evt = CCLOGIC_EVENT_NONE;
+ } else{
+ result->evt = CCLOGIC_EVENT_ATTACHED;
+ }
+
+ if (reg9&0x20)
+ result->cc = CCLOGIC_CC2;
+ else
+ result->cc = CCLOGIC_CC1;
+
+ switch (reg9&0xc0) {
+ case 0x00:/* nothing attached */
+ if (reg9&0x10)
+ result->evt = CCLOGIC_EVENT_DETACHED;
+ result->vbus = false;
+ result->device = CCLOGIC_NO_DEVICE;
+ break;
+ case 0x40:
+ result->device = CCLOGIC_USB_DEVICE;
+ result->vbus = false;
+ break;
+ case 0x80:
+ result->device = CCLOGIC_USB_HOST;
+ result->vbus = true;
+ break;
+ case 0xC0:/* accessory */
+ switch (reg8&0x0e) {
+ case 0x00: /* No Accessory attached */
+ if (reg9&0x10)
+ result->evt = CCLOGIC_EVENT_DETACHED;
+ result->vbus = false;
+ result->device = CCLOGIC_NO_DEVICE;
+ break;
+ case 0x08: /* Audio Accessory DFP */
+ result->vbus = false;
+ result->device = CCLOGIC_AUDIO_DEVICE;
+ break;
+ case 0x0a: /* Audio Accessory UFP */
+ result->vbus = true;
+ result->device = CCLOGIC_AUDIO_DEVICE;
+ break;
+ case 0x0e:/* Debug Accessory UFP */
+ result->vbus = true;
+ result->device = CCLOGIC_DEBUG_DEVICE;
+ break;
+ case 0x0c:/* Debug Accessory DFP set to Hi-Z */
+ result->vbus = false;
+ result->device = CCLOGIC_DEBUG_DEVICE;
+ break;
+ default:/* Reserve */
+ result->vbus = false;
+ result->device = CCLOGIC_NO_DEVICE;
+ break;
+ }
+
+ break;
+ }
+
+
+ switch (reg8&0x30) {
+ case 0x00:
+ result->charger = CCLOGIC_CURRENT_DEFAULT;
+ break;
+ case 0x10:
+ result->charger = CCLOGIC_CURRENT_MEDIUM;
+ break;
+ case 0x20:
+ result->charger = CCLOGIC_CURRENT_ACCESSORY;
+ /* result->vbus = false; */
+ break;
+ case 0x30:
+ result->charger = CCLOGIC_CURRENT_HIGH;
+ break;
+ }
+
+}
+
+/* tusb320hai_reset_chip() */
+static int tusb320hai_reset_chip(struct i2c_client *client)
+{
+ int retries = 10;
+ int regval;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ regval = tusb320hai_write_i2c(client, 0xa, 0x8);
+ if (regval) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return regval;
+ }
+
+ while (retries--) {
+ regval = tusb320hai_read_i2c(client, 0xa);
+ if (regval < 0) {
+ dev_err(&client->dev, "cclogic:%s: i2c read error\n",
+ __func__);
+ return regval;
+ }
+ if (!(regval&0x08))
+ return 0;
+ msleep(100);
+ }
+ if (regval&0x08)
+ return -1;
+ return 0;
+}
+
+/* tusb320hai_trymode() */
+static int tusb320hai_trymode(struct i2c_client *client, int mode)
+{
+ int ret;
+ int regval;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = tusb320hai_write_i2c(client, 0xa, 0x33);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ if (mode == CCLOGIC_MODE_UFP) {
+ regval = 0x13;
+ pr_debug("cclogic:trymode sink\n");
+ } else{
+ regval = 0x23;
+ pr_debug("cclogic:trymode source\n");
+ }
+ ret = tusb320hai_write_i2c(client, 0xa, regval);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ msleep(6);
+
+ ret = tusb320hai_write_i2c(client, 0xa, regval & 0xfe);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+
+/* tusb320hai_config_chip() */
+static int tusb320hai_config_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = tusb320hai_write_i2c(client, 0x8, 0);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = tusb320hai_write_i2c(client, 0xa, 0x32);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+
+/* tusb320hai_check_chip() */
+static int tusb320hai_check_chip(struct i2c_client *client)
+{
+ char buf[8];
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ /* ID check */
+ ret = tusb320hai_recv_i2c(client, 0, buf, 8);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s: i2c recv error\n", __func__);
+ return -ENODEV;
+ }
+
+ if (memcmp(buf, TUSB320_DEVID, sizeof(TUSB320_DEVID))) {
+ dev_err(&client->dev, "cclogic:%s: devid mismatch (%s != %s)\n",
+ __func__, buf, TUSB320_DEVID);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* tusb320hai_ack_irq() */
+static int tusb320hai_ack_irq(struct i2c_client *client)
+{
+ int reg9, ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ reg9 = tusb320hai_read_i2c(client, 0x9);
+ if (reg9 < 0) {
+ dev_err(&client->dev, "cclogic:%s-->i2c read error\n", __func__);
+ return reg9;
+ }
+
+ /* clear interrupt */
+ ret = tusb320hai_write_i2c(client, 0x9, reg9);
+ if (ret) {
+ dev_err(&client->dev, "cclogic:%s-->i2c write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* tusb320hai_get_state() */
+static int tusb320hai_get_state(struct i2c_client *client,
+ struct cclogic_state *state)
+{
+ int reg8, reg9, rega;
+ static int flag;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ reg9 = tusb320hai_read_i2c(client, 0x9);
+ if (reg9 < 0) {
+ dev_err(&client->dev, "cclogic:%s-->i2c read error,reg9=%d\n",
+ __func__, reg9);
+ return reg9;
+ }
+
+ reg8 = tusb320hai_read_i2c(client, 0x8);
+ if (reg8 < 0) {
+ dev_err(&client->dev, "cclogic:%s-->i2c read error,reg8=%d\n",
+ __func__, reg8);
+ return reg8;
+ }
+
+ pr_debug("cclogic:%s-->i2c register value: reg8=0x%02x reg9=0x%02x\n",
+ __func__, reg8, reg9);
+
+ tusb320hai_parse_cclogic_state(reg8, reg9, state);
+
+#if 1 /* for chip bug */
+ if (state->evt == CCLOGIC_EVENT_DETACHED) {/* skip detach event */
+ return 0;
+ }
+
+ rega = tusb320hai_read_i2c(client, 0xa);
+ if (rega < 0) {
+ dev_err(&client->dev, "cclogic:%s: i2c read error\n",
+ __func__);
+ return rega;
+ }
+
+ if ((rega & 0x30) != 0x30)
+ return 0;
+
+ if (!flag && (state->device == CCLOGIC_USB_DEVICE)) {
+ tusb320hai_reset_chip(client);
+ tusb320hai_config_chip(client);
+ flag = 1;
+ return -1;
+ }
+
+ flag = 0;
+#endif
+
+ return 0;
+}
+
+
+static struct cclogic_chip tusb320hai_chip = {
+ .chip_name = DRIVER_NAME,
+ .get_state = tusb320hai_get_state,
+ .ack_irq = tusb320hai_ack_irq,
+ .chip_config = tusb320hai_config_chip,
+ .chip_reset = tusb320hai_reset_chip,
+ .chip_check = tusb320hai_check_chip,
+ .chip_trymode = tusb320hai_trymode,
+ .typec_version = 11,/* spec 1.1 */
+ .support = CCLOGIC_SUPPORT_MODE_DUAL,
+ .read = tusb320hai_read_i2c,
+ .write = tusb320hai_write_i2c,
+};
+
+/* tusb320hai_init() */
+static int __init tusb320hai_init(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return cclogic_register(&tusb320hai_chip);
+}
+
+/* tusb320hai_exit() */
+static void __exit tusb320hai_exit(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ cclogic_unregister(&tusb320hai_chip);
+ return;
+}
+
+late_initcall(tusb320hai_init);
+module_exit(tusb320hai_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Yang ShaoYing <yangsy2@lenovo.com>");
+MODULE_DESCRIPTION("Drivers for usb type-C CC-Logic chip of TI Tusb320");
+MODULE_ALIAS("platform:cc-logic");