summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFaiz Authar <faizauthar@gmail.com>2017-04-27 18:14:55 +0700
committerDavide Garberi <dade.garberi@gmail.com>2022-07-27 18:59:05 +0200
commit5dafae2558993f60611f1f6bd69c11c6cf88225e (patch)
treec79bd304ad24e69de43616cd898d1b46ff372975
parenta84facefcb7d4dbdfe1a4ad9655146540b62ed8f (diff)
drivers: add cclogic usb type-c driver
Change-Id: I783aaf058c42e85712f861369cc85db2d6adab61 Signed-off-by: Faiz Authar <faizauthar@gmail.com> Signed-off-by: Davide Garberi <dade.garberi@gmail.com>
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/cclogic/Kconfig46
-rw-r--r--drivers/misc/cclogic/Makefile12
-rw-r--r--drivers/misc/cclogic/cclogic-class.c284
-rw-r--r--drivers/misc/cclogic/cclogic-class.h19
-rw-r--r--drivers/misc/cclogic/cclogic-core.c1757
-rw-r--r--drivers/misc/cclogic/cclogic-core.h169
-rw-r--r--drivers/misc/cclogic/pi5usb30216.c543
-rw-r--r--drivers/misc/cclogic/pi5usb30216d.c262
-rw-r--r--drivers/misc/cclogic/ptn5150h.c398
-rw-r--r--drivers/misc/cclogic/tusb320.c423
-rw-r--r--drivers/misc/cclogic/tusb320hai.c476
-rw-r--r--include/linux/cclogic-core.h3
13 files changed, 4393 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 52f75b1faec0..04881982d992 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -592,4 +592,5 @@ source "drivers/misc/mic/Kconfig"
source "drivers/misc/genwqe/Kconfig"
source "drivers/misc/echo/Kconfig"
source "drivers/misc/cxl/Kconfig"
+source "drivers/misc/cclogic/Kconfig"
endmenu
diff --git a/drivers/misc/cclogic/Kconfig b/drivers/misc/cclogic/Kconfig
new file mode 100644
index 000000000000..445243f08079
--- /dev/null
+++ b/drivers/misc/cclogic/Kconfig
@@ -0,0 +1,46 @@
+#
+# 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_PI5USB
+ depends on I2C
+ tristate "Pericom Pi5usb30216 Support"
+ ---help---
+ The Pericom pi5usb30216 chip is used as cc-logic of TYPEC
+ interface.
+
+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_TUSB320
+ depends on I2C
+ tristate "TI TUSB320 Support"
+ ---help---
+ The TI tusb320 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
+
+config TYPEC_CCLOGIC_PTN5150H
+ depends on I2C
+ tristate "NXP ptn5150h Support"
+ ---help---
+ The NXP ptn5150h 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..5a15aeb0d038
--- /dev/null
+++ b/drivers/misc/cclogic/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for cclogic device.
+#
+
+obj-$(CONFIG_CCLOGIC) += cclogic-core.o
+obj-$(CONFIG_CCLOGIC) += cclogic-class.o
+obj-$(CONFIG_TYPEC_CCLOGIC_PI5USB) += pi5usb30216.o
+obj-$(CONFIG_TYPEC_CCLOGIC_TUSB320) += tusb320.o
+obj-$(CONFIG_TYPEC_CCLOGIC_TUSB320HAI) += tusb320hai.o
+obj-$(CONFIG_TYPEC_CCLOGIC_PTN5150H) += ptn5150h.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..fffce515ed1a
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-class.c
@@ -0,0 +1,284 @@
+#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..f9b664c4b339
--- /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..56eb1a977b46
--- /dev/null
+++ b/drivers/misc/cclogic/cclogic-core.c
@@ -0,0 +1,1757 @@
+//
+// cclogic-core.c
+//
+// Core of CC-Logic drivers
+//
+#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 <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/cclogic-core.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 = 0;
+
+#define DRIVER_NAME "cclogic"
+/*
+ *
+ */
+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_optimum_mode(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 (1 != sscanf(buf, "%d", &value)) {
+ 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 (1 != sscanf(buf, "%d", &value)) {
+ 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 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_PRODUCT_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 = 0;
+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;
+
+ pr_debug("[%s][%d]\n", __func__, __LINE__);
+ cc_otg_state = 0;
+ switch(state->evt){
+ case CCLOGIC_EVENT_DETACHED:
+ 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:
+ 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;
+ }
+ }
+
+ /*
+ if(state->vbus){
+ switch(state->charger){
+ case CCLOGIC_CURRENT_NONE:
+ case CCLOGIC_CURRENT_DEFAULT:
+ case CCLOGIC_CURRENT_MEDIUM:
+ case CCLOGIC_CURRENT_ACCESSORY:
+ case CCLOGIC_CURRENT_HIGH:
+ }
+ }
+ */
+
+ 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);
+ mdelay(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);
+ }
+
+ 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 = 0;
+
+ 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 = 0;
+
+ 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);
+ mdelay(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,
+#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_RUNTIME
+/*
+ *
+ */
+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);
+ }
+
+ //if(!gpio_get_value(pdata->platform_data->irq_plug)){
+ //}
+ cclogic_power_on(cclogic_dev, false);
+ pinctrl_select_state(cclogic_dev->pin, cclogic_dev->pin_suspend);
+ //if (gpio_is_valid(pdata->ccchip_power_gpio))
+ // gpio_set_value(pdata->ccchip_power_gpio,0);
+
+ 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);
+
+ //if (gpio_is_valid(pdata->ccchip_power_gpio))
+ // gpio_set_value(pdata->ccchip_power_gpio,1);
+
+ 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 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);
+
+ if(cclogic_priv)
+ 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..99443d821d15
--- /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 DEBUG
+
+#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;
+};
+
+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/pi5usb30216.c b/drivers/misc/cclogic/pi5usb30216.c
new file mode 100644
index 000000000000..d4f6ecf63258
--- /dev/null
+++ b/drivers/misc/cclogic/pi5usb30216.c
@@ -0,0 +1,543 @@
+//
+// pi5usb30216.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 <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 "pericom,pi5usb30216"
+
+
+#define MAX_RETRIES_FOR_PLUGIN_SLOW 4 //about 2 seconds
+
+#define REG_BASE 1
+#define MAX_REG_NUM 4
+
+#define REG_ID (0x01-REG_BASE)
+#define VERSION_ID (0x00<<3)
+#define VENDOR_ID (0x00<<0)
+#define PIUSB30216_DEVID (VERSION_ID|VENDOR_ID)
+
+#define REG_CONTROL (0x02-REG_BASE)
+#define POWERSAVING_MODE (0x01<<7)
+#define PORT_SETTING_INTERRUPT_NOMASK (0x00)
+#define CHARGING_CURRENT_MODE_DEFAULT (0x00<<3)
+#define CHARGING_CURRENT_MODE_MEDIUM (0x01<<3)
+#define CHARGING_CURRENT_MODE_HIGH (0x02<<3)
+#define PORT_SETTING_DEVICE (0x00<<1)
+#define PORT_SETTING_HOST (0x01<<1)
+#define PORT_SETTING_DUALROLE (0x02<<1)
+#define PORT_SETTING_INTERRUPT_MASK (0x01)
+#define PORT_SETTING_INTERRUPT_NOMASK (0x00)
+
+#define REG_INTERRUPT (0x03-REG_BASE)
+#define INTERRUPT_STATE_ATTACHED_BIT (0x01)
+#define INTERRUPT_STATE_DETACHED_BIT (0x02)
+
+#define REG_CC_STATE (0x04-REG_BASE)
+#define CCSTATUS_VBUS_BIT (0x01<<7)
+#define CCSTATUS_CHARGING_CURRENT_BIT (0x03<<5)
+#define CCSTATUS_CHARGING_CURRENT_STANDBY (0x00<<5)
+#define CCSTATUS_CHARGING_CURRENT_DEFAULT (0x01<<5)
+#define CCSTATUS_CHARGING_CURRENT_MEDIUM (0x02<<5)
+#define CCSTATUS_CHARGING_CURRENT_HIGH (0x03<<5)
+
+#define CCSTATUS_ATTACHED_PORT_STATUS_BIT (0x07<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_STANDBY (0x00<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_DEVICE (0x01<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_HOST (0x02<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_AUDIO (0x03<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_DEBUG (0x04<<2)
+#define CCSTATUS_ATTACHED_PORT_STATUS_VBUS_CC1_CC2 (0x05<<2)
+
+#define CCSTATUS_PLUG_POLARITY_BIT (0x03)
+#define CCSTATUS_PLUG_POLARITY_STANDBY (0x00)
+#define CCSTATUS_PLUG_POLARITY_CC1 (0x01)
+#define CCSTATUS_PLUG_POLARITY_CC2 (0x02)
+#define CCSTATUS_PLUG_POLARITY_CC1_CC2 (0x03)
+
+
+
+/**
+ * pi5usb30216_write_i2c()
+ */
+static int pi5usb30216_write_i2c(struct i2c_client *client, u8 reg, u8 data)
+{
+ int ret = 0;
+ char val[MAX_REG_NUM] = {0};
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ val[reg] = data;
+
+ ret = i2c_master_send(client, val, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,"cclogic:%s-->i2c send error\n",__func__);
+ return ret;
+ }
+
+ return 0;
+
+}
+/**
+ * pi5usb30216_parse_cclogic_state()
+ */
+static void pi5usb30216_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&INTERRUPT_STATE_DETACHED_BIT) &&
+ (reg3&INTERRUPT_STATE_ATTACHED_BIT)){
+ pr_err("cclogic:%s-->detach and attach in the same time\n",
+ __func__);
+ }else{
+ if(reg3&INTERRUPT_STATE_DETACHED_BIT){
+ result->evt = CCLOGIC_EVENT_DETACHED;
+ }
+ if(reg3&INTERRUPT_STATE_ATTACHED_BIT){
+ result->evt = CCLOGIC_EVENT_ATTACHED;
+ }
+ }
+
+ if(reg4&CCSTATUS_VBUS_BIT){
+ result->vbus = true;
+ }
+
+ switch(reg4&CCSTATUS_CHARGING_CURRENT_BIT){
+ case CCSTATUS_CHARGING_CURRENT_STANDBY:
+ result->charger = CCLOGIC_CURRENT_NONE;
+ break;
+ case CCSTATUS_CHARGING_CURRENT_DEFAULT:
+ result->charger = CCLOGIC_CURRENT_DEFAULT;
+ break;
+ case CCSTATUS_CHARGING_CURRENT_MEDIUM:
+ result->charger = CCLOGIC_CURRENT_MEDIUM;
+ break;
+ case CCSTATUS_CHARGING_CURRENT_HIGH:
+ result->charger = CCLOGIC_CURRENT_HIGH;
+ break;
+ }
+
+ switch(reg4&CCSTATUS_PLUG_POLARITY_BIT){
+ case CCSTATUS_PLUG_POLARITY_CC1:
+ result->cc = CCLOGIC_CC1;
+ break;
+ case CCSTATUS_PLUG_POLARITY_CC2:
+ result->cc = CCLOGIC_CC2;
+ break;
+ case CCSTATUS_PLUG_POLARITY_STANDBY:
+ case CCSTATUS_PLUG_POLARITY_CC1_CC2:
+ default:
+ break;
+ }
+
+ switch(reg4&CCSTATUS_ATTACHED_PORT_STATUS_BIT){
+ case CCSTATUS_ATTACHED_PORT_STATUS_STANDBY:
+ result->device = CCLOGIC_NO_DEVICE;
+ break;
+ case CCSTATUS_ATTACHED_PORT_STATUS_DEVICE:
+ result->device = CCLOGIC_USB_DEVICE;
+ break;
+ case CCSTATUS_ATTACHED_PORT_STATUS_HOST:
+ result->device = CCLOGIC_USB_HOST;
+ break;
+ case CCSTATUS_ATTACHED_PORT_STATUS_DEBUG:
+ result->device = CCLOGIC_DEBUG_DEVICE;
+ break;
+ case CCSTATUS_ATTACHED_PORT_STATUS_AUDIO://mode audio
+ result->device = CCLOGIC_AUDIO_DEVICE;
+ break;
+ default:
+ result->device = CCLOGIC_DEVICE_UNKNOWN;
+ break;
+ }
+
+}
+
+/**
+ * pi5usb30216_get_state()
+ */
+static int pi5usb30216_get_state(struct i2c_client *client,
+ struct cclogic_state *state)
+{
+ char reg[MAX_REG_NUM] = {0};
+ static int reg3,reg4;
+ static int repeat_times = 0;
+ static int audio_accessory_flag = 0;
+ static int debug_accessory_flag = 0;
+ int ret = 0;
+
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ state->vbus = false;
+ state->cc = CCLOGIC_CC_UNKNOWN;
+ state->evt = CCLOGIC_EVENT_DETACHED;
+ state->device = CCLOGIC_DEVICE_UNKNOWN;
+ state->charger = CCLOGIC_CURRENT_NONE;
+
+ mdelay(10);
+
+ ret = i2c_master_recv(client, reg, MAX_REG_NUM);
+ if (ret < 0) {
+ 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]);
+
+ reg3 = reg[REG_INTERRUPT];
+ reg4 = reg[REG_CC_STATE];
+
+ if(repeat_times > 3){
+ repeat_times = 0;
+ }
+
+ switch(reg3){
+ case 0x01:
+ switch(reg4){
+ case 0x00:
+ repeat_times = 0;
+ audio_accessory_flag = 0;
+ debug_accessory_flag = 0;
+ ret = 0;
+ break;
+ case 0x05:
+ case 0x06:
+ if(repeat_times>=2){
+ repeat_times = 0;
+ mdelay(50);
+ //Device Plug in
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ }else{
+ repeat_times++;
+ mdelay(240);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,
+ PORT_SETTING_INTERRUPT_MASK);//0x01
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ mdelay(100);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,\
+ CHARGING_CURRENT_MODE_DEFAULT|\
+ PORT_SETTING_DUALROLE|\
+ PORT_SETTING_INTERRUPT_NOMASK);//0x04
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ ret = -1;
+ }
+ break;
+ case 0x13:
+ if(repeat_times >= 1){
+ repeat_times = 0;
+ mdelay(50);
+ debug_accessory_flag = 1;
+ //Debug Plug in
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ }else{
+ repeat_times++;
+ //p2_3 = 0;
+ debug_accessory_flag = 0;
+ mdelay(240);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,
+ PORT_SETTING_INTERRUPT_MASK);//0x01
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ mdelay(100);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,\
+ CHARGING_CURRENT_MODE_DEFAULT|\
+ PORT_SETTING_DUALROLE|\
+ PORT_SETTING_INTERRUPT_NOMASK);//0x04
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ ret = 0;
+ }
+ break;
+ case 0xa8:
+ ret = 0;
+ break;
+ case 0x0f:
+ audio_accessory_flag=1;
+ mdelay(50);
+ //Audio Plug in
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0x93:
+ if(debug_accessory_flag){
+ reg4 &= 0x7f;
+ }
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0x8f:
+ if(audio_accessory_flag){
+ reg4 &= 0x7f;
+ }
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0xa9:
+ case 0xaa:
+ case 0xc9:
+ case 0xca:
+ case 0xe9:
+ case 0xea:
+ //Host plug in
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ break;
+ case 0x00:
+ switch(reg4){
+ case 0x00:
+ repeat_times = 0;
+ audio_accessory_flag = 0;
+ debug_accessory_flag = 0;
+ ret = 0;
+ break;
+ case 0x97:
+ if(debug_accessory_flag || audio_accessory_flag){
+ mdelay(100);
+ ret = 0;
+ }else if(repeat_times>=3){
+ repeat_times = 0;
+ ret = 0;
+ }else{
+ repeat_times++;
+ mdelay(240);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,
+ PORT_SETTING_INTERRUPT_MASK);//0x01
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ mdelay(100);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,\
+ CHARGING_CURRENT_MODE_DEFAULT|\
+ PORT_SETTING_DUALROLE|\
+ PORT_SETTING_INTERRUPT_NOMASK);//0x04
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",
+ __func__);
+ return ret;
+ }
+ ret = 0;
+ }
+ break;
+ case 0x93:
+ if(debug_accessory_flag){
+ reg4 &= 0x7f;
+ }
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0x8f:
+ if(audio_accessory_flag){
+ reg4 &= 0x7f;
+ }
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0xa9:
+ case 0xaa:
+ case 0xc9:
+ case 0xca:
+ case 0xe9:
+ case 0xea:
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ default:
+ ret = -1;
+ break;
+ }
+ break;
+ case 0x02:
+ audio_accessory_flag = 0;
+ debug_accessory_flag = 0;
+ repeat_times=0;
+ pi5usb30216_parse_cclogic_state(reg3,reg4,state);
+ ret = 0;
+ break;
+ default:
+ audio_accessory_flag = 0;
+ debug_accessory_flag = 0;
+ repeat_times=0;
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * pi5usb30216_check_chip()
+ */
+static int pi5usb30216_check_chip(struct i2c_client *client)
+{
+ int ret;
+ char reg_test_w[MAX_REG_NUM] = {0};
+ char reg_test_r[MAX_REG_NUM] = {0};
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ /* ID check */
+ ret = i2c_master_recv(client, reg_test_r, 2);
+ if(ret<0){
+ dev_err(&client->dev,"cclogic:%s: i2c read error\n", __func__);
+ return ret;
+ }
+
+ if(reg_test_r[REG_ID] != PIUSB30216_DEVID){
+ dev_err(&client->dev,"cclogic:%s: devid mismatch"
+ " (0x%02x!=0x%02x)\n", __func__,reg_test_r[REG_ID],
+ PIUSB30216_DEVID);
+ return -ENODEV;
+ }
+
+ /* i2c R/W test */
+ reg_test_w[REG_CONTROL] = 0x55;
+ ret = i2c_master_send(client, reg_test_w, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,"cclogic:%s:i2c write error\n", __func__);
+ return ret;
+ }
+
+ ret = i2c_master_recv(client, reg_test_r, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,"cclogic:%s:i2c read error\n", __func__);
+ return ret;
+ }
+ if(reg_test_r[REG_CONTROL]!=0x55){
+ dev_err(&client->dev,"cclogic:%s:i2c reg r/w test failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+
+}
+
+/**
+ * pi5usb30216_reset_chip()
+ */
+static int pi5usb30216_reset_chip(struct i2c_client *client)
+{
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return 0;
+}
+
+/**
+ * pi5usb30216_config_chip()
+ */
+static int pi5usb30216_config_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = pi5usb30216_write_i2c(client,REG_CONTROL,
+ CHARGING_CURRENT_MODE_DEFAULT|PORT_SETTING_DUALROLE|\
+ PORT_SETTING_INTERRUPT_NOMASK);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct cclogic_chip pi5usb30216_chip = {
+ .chip_name = DRIVER_NAME,
+ .get_state = pi5usb30216_get_state,
+ .ack_irq = NULL,
+ .chip_config = pi5usb30216_config_chip,
+ .chip_reset = pi5usb30216_reset_chip,
+ .chip_check = pi5usb30216_check_chip,
+ .typec_version = 10,//spec 1.0
+ .support = 0,
+};
+
+/**
+ * pi5usb30216_init()
+ */
+static int __init pi5usb30216_init(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return cclogic_register(&pi5usb30216_chip);
+}
+
+/**
+ * pi5usb30216_exit()
+ */
+static void __exit pi5usb30216_exit(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ cclogic_unregister(&pi5usb30216_chip);
+ return;
+}
+
+late_initcall(pi5usb30216_init);
+module_exit(pi5usb30216_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Yang ShaoYing <yangsy2@lenovo.com>");
+MODULE_DESCRIPTION("Drivers for Type-C CC-Logic chip of Pericom Pi5usb30216");
+MODULE_ALIAS("platform:cc-logic");
diff --git a/drivers/misc/cclogic/pi5usb30216d.c b/drivers/misc/cclogic/pi5usb30216d.c
new file mode 100644
index 000000000000..6832328f1a47
--- /dev/null
+++ b/drivers/misc/cclogic/pi5usb30216d.c
@@ -0,0 +1,262 @@
+//
+// 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 <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 "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/ptn5150h.c b/drivers/misc/cclogic/ptn5150h.c
new file mode 100644
index 000000000000..f073afa56790
--- /dev/null
+++ b/drivers/misc/cclogic/ptn5150h.c
@@ -0,0 +1,398 @@
+//
+// ptn5150.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 "nxp,ptn5150h"
+
+#define PTN5150_DEVID 0xb
+
+/**
+ * ptn5150_read_i2c()
+ */
+static int ptn5150_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;
+}
+
+/**
+ * ptn5150_recv_i2c()
+ */
+static int ptn5150_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;
+}
+/**
+ * ptn5150_write_i2c()
+ */
+static int ptn5150_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;
+
+}
+
+/**
+ * ptn5150_parse_cclogic_state()
+ */
+static void ptn5150_parse_cclogic_state(int reg3, int reg4, int reg19,
+ 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(reg3&0x2){//No interrupt
+ result->evt = CCLOGIC_EVENT_DETACHED;
+ }else if(reg3&0x1){
+ result->evt = CCLOGIC_EVENT_ATTACHED;
+ }else
+ result->evt = CCLOGIC_EVENT_NONE;
+
+ switch(reg4&0x3){
+ case 0x0:
+ result->cc = CCLOGIC_CC_UNKNOWN;break;
+ case 0x1:
+ result->cc = CCLOGIC_CC1;break;
+ case 0x2:
+ result->cc = CCLOGIC_CC2;break;
+ case 0x3:
+ result->cc = CCLOGIC_CC1_CC2;break;
+ }
+
+ 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;
+ }
+
+ if(reg4&0x80)
+ result->vbus = true;
+
+ switch(reg4&0x1C){
+ case 0x00:
+ result->device = CCLOGIC_NO_DEVICE;break;
+ case 0x04:
+ result->device = CCLOGIC_USB_HOST;break;
+ case 0x08:
+ result->device = CCLOGIC_USB_DEVICE;break;
+ case 0x0C:
+ result->device = CCLOGIC_AUDIO_DEVICE;break;
+ case 0x10:
+ result->device = CCLOGIC_DEBUG_DEVICE;break;
+ }
+}
+
+/**
+ * ptn5150_reset_chip()
+ */
+static int ptn5150_reset_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = ptn5150_write_i2c(client,0x10,0x1);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ mdelay(20);
+
+ return ret;
+}
+
+#if 0
+/**
+ * ptn5150_trymode()
+ */
+static int ptn5150_trymode(struct i2c_client *client, int mode)
+{
+ int ret;
+ int regval;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ regval = ptn5150_read_i2c(client,0xa);
+ if(regval<0){
+ dev_err(&client->dev,"cclogic:%s: i2c read error\n",
+ __func__);
+ return regval;
+ }
+
+ if(mode == CCLOGIC_MODE_UFP){
+ regval = (regval&0xf9) | 0x02;
+ }else{
+ regval = (regval&0xf9) | 0x06;
+ }
+ ret = ptn5150_write_i2c(client,0xa,regval);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+#endif
+
+
+/**
+ * ptn5150_config_chip()
+ */
+static int ptn5150_config_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = ptn5150_write_i2c(client,0x09,0x1);//disable con_det output
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = ptn5150_write_i2c(client,0x43,0x40);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = ptn5150_write_i2c(client,0x4c,0x34);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = ptn5150_write_i2c(client,0x2,0x4);//charger:default unmask interrupt
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = ptn5150_write_i2c(client,0x18,0x00);//unmask interrupt
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+
+/**
+ * ptn5150_check_chip()
+ */
+static int ptn5150_check_chip(struct i2c_client *client)
+{
+ char buf;
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ /* ID check */
+ ret = ptn5150_recv_i2c(client, 1, &buf, 1);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c recv error\n", __func__);
+ return -ENODEV;
+ }
+
+ if(buf != PTN5150_DEVID){
+ dev_err(&client->dev,"cclogic:%s: device version mismatch (%x != %x)\n",
+ __func__,buf,PTN5150_DEVID);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * ptn5150_get_state()
+ */
+static int ptn5150_get_state(struct i2c_client *client,
+ struct cclogic_state *state)
+{
+ int reg3,reg4,reg19;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ reg3 = ptn5150_read_i2c(client,0x3);
+ if(reg3<0){
+ dev_err(&client->dev,"cclogic:%s-->i2c read error,reg3=%d\n",
+ __func__,reg3);
+ return reg3;
+ }
+
+ reg4 = ptn5150_read_i2c(client,0x4);
+ if(reg4<0){
+ dev_err(&client->dev,"cclogic:%s-->i2c read error,reg4=%d\n",
+ __func__,reg4);
+ return reg4;
+ }
+
+ reg19 = ptn5150_read_i2c(client,0x19);
+ if(reg19<0){
+ dev_err(&client->dev,"cclogic:%s-->i2c read error,reg19=%d\n",
+ __func__,reg19);
+ return reg19;
+ }
+
+
+ pr_debug("cclogic:%s-->i2c register value: reg3=0x%02x reg4=0x%02x reg19=0x%02x\n",
+ __func__,reg3,reg4,reg19);
+
+ ptn5150_parse_cclogic_state(reg3,reg4,reg19,state);
+
+ return 0;
+}
+
+
+static struct cclogic_chip ptn5150_chip = {
+ .chip_name = DRIVER_NAME,
+ .get_state = ptn5150_get_state,
+ .ack_irq = NULL,
+ .chip_config = ptn5150_config_chip,
+ .chip_reset = ptn5150_reset_chip,
+ .chip_check = ptn5150_check_chip,
+#if 0
+ .chip_trymode = ptn5150_trymode,
+ .typec_version = 11,//spec 1.1
+ .support = CCLOGIC_SUPPORT_MODE_DUAL,
+#else
+ .typec_version = 10,//spec 1.0
+#endif
+};
+
+/**
+ * ptn5150_init()
+ */
+static int __init ptn5150_init(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return cclogic_register(&ptn5150_chip);
+}
+
+/**
+ * ptn5150_exit()
+ */
+static void __exit ptn5150_exit(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ cclogic_unregister(&ptn5150_chip);
+ return;
+}
+
+late_initcall(ptn5150_init);
+module_exit(ptn5150_exit);
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Yang ShaoYing <yangsy2@lenovo.com>");
+MODULE_DESCRIPTION("Drivers for usb type-C CC-Logic chip of NXP5150H");
+MODULE_ALIAS("platform:cc-logic");
diff --git a/drivers/misc/cclogic/tusb320.c b/drivers/misc/cclogic/tusb320.c
new file mode 100644
index 000000000000..e6fd1715c36c
--- /dev/null
+++ b/drivers/misc/cclogic/tusb320.c
@@ -0,0 +1,423 @@
+//
+// tusb320.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,tusb320"
+
+#define MAX_REG_NUM 11
+
+#define TUSB320_DEVID "023BSUT"
+
+/**
+ * tusb320_read_i2c()
+ */
+static int tusb320_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;
+}
+
+/**
+ * tusb320_recv_i2c()
+ */
+static int tusb320_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;
+}
+/**
+ * tusb320_write_i2c()
+ */
+static int tusb320_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;
+
+}
+
+/**
+ * tusb320_parse_cclogic_state()
+ */
+static void tusb320_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;
+ }
+
+}
+
+/**
+ * tusb320_reset_chip()
+ */
+static int tusb320_reset_chip(struct i2c_client *client)
+{
+ int retries = 10;
+ int regval;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ regval = tusb320_write_i2c(client,0xa,0x8);
+ if(regval){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return regval;
+ }
+
+ while(retries--){
+ regval = tusb320_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;
+ }
+ mdelay(100);
+ }
+ if(regval&0x08){
+ return -1;
+ }
+ return 0;
+
+}
+
+/**
+ * tusb320_config_chip()
+ */
+static int tusb320_config_chip(struct i2c_client *client)
+{
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ ret = tusb320_write_i2c(client,0x8,0);//charger:default
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = tusb320_write_i2c(client,0xa,0x30);//DRP
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+
+ return ret;
+
+}
+
+/**
+ * tusb320_check_chip()
+ */
+static int tusb320_check_chip(struct i2c_client *client)
+{
+ char buf[8];
+ int ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ /* ID check */
+ ret = tusb320_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;
+}
+
+/**
+ * tusb320_ack_irq()
+ */
+static int tusb320_ack_irq(struct i2c_client *client)
+{
+ int reg9,ret;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ reg9 = tusb320_read_i2c(client,0x9);
+ if(reg9<0){
+ dev_err(&client->dev,"cclogic:%s-->i2c read error\n", __func__);
+ return reg9;
+ }
+
+ /* clear interrupt */
+ ret = tusb320_write_i2c(client,0x9,reg9);
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s-->i2c write error\n",__func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * tusb320_get_state()
+ */
+static int tusb320_get_state(struct i2c_client *client,
+ struct cclogic_state *state)
+{
+ int reg8,reg9;
+ static int flag = 0;
+
+ pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ reg9 = tusb320_read_i2c(client,0x9);
+ if(reg9<0){
+ dev_err(&client->dev,"cclogic:%s-->i2c read error,reg9=%d\n",
+ __func__,reg9);
+ return reg9;
+ }
+
+ reg8 = tusb320_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);
+
+ tusb320_parse_cclogic_state(reg8,reg9,state);
+
+#if 1 //for chip bug
+ if(state->evt == CCLOGIC_EVENT_DETACHED){//skip detach event
+ return 0;
+ }
+
+ if(!flag && (state->device == CCLOGIC_USB_DEVICE)){
+ tusb320_reset_chip(client);
+ flag = 1;
+ return -1;
+ }
+
+ flag = 0;
+#endif
+
+ return 0;
+}
+
+
+static struct cclogic_chip tusb320_chip = {
+ .chip_name = DRIVER_NAME,
+ .get_state = tusb320_get_state,
+ .ack_irq = tusb320_ack_irq,
+ .chip_config = tusb320_config_chip,
+ .chip_reset = tusb320_reset_chip,
+ .chip_check = tusb320_check_chip,
+ .typec_version = 10,//spec 1.0
+ .support = 0,
+};
+
+/**
+ * tusb320_init()
+ */
+static int __init tusb320_init(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ return cclogic_register(&tusb320_chip);
+}
+
+/**
+ * tusb320_exit()
+ */
+static void __exit tusb320_exit(void)
+{
+ pr_info("cclogic:[%s][%d]\n", __func__, __LINE__);
+
+ cclogic_unregister(&tusb320_chip);
+ return;
+}
+
+late_initcall(tusb320_init);
+module_exit(tusb320_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");
diff --git a/drivers/misc/cclogic/tusb320hai.c b/drivers/misc/cclogic/tusb320hai.c
new file mode 100644
index 000000000000..5089c02b1abf
--- /dev/null
+++ b/drivers/misc/cclogic/tusb320hai.c
@@ -0,0 +1,476 @@
+//
+// 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;
+ }
+ mdelay(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;
+ }
+
+ mdelay(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);//charger:default
+ if(ret){
+ dev_err(&client->dev,"cclogic:%s: i2c write error\n", __func__);
+ return ret;
+ }
+ ret = tusb320hai_write_i2c(client,0xa,0x32);//DRP and try.snk
+ 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 = 0;
+
+ 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) //skip UFP and DFP mode
+ 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,
+};
+
+/**
+ * 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");
diff --git a/include/linux/cclogic-core.h b/include/linux/cclogic-core.h
new file mode 100644
index 000000000000..6ffb9d744abc
--- /dev/null
+++ b/include/linux/cclogic-core.h
@@ -0,0 +1,3 @@
+
+extern int cclogic_get_otg_state(void);
+