blob: 0b2eebedf7395704b5638e24ae87cde87afd5a65 [file] [log] [blame]
/*
* Copyright (C) 2016 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/atomic.h>
#include "inc/tcpm.h"
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <extcon_usb.h>
#ifdef CONFIG_MTK_USB_TYPEC_U3_MUX
#include "usb_switch.h"
#include "typec.h"
#endif
static struct notifier_block otg_nb;
static bool usbc_otg_attached;
static struct tcpc_device *otg_tcpc_dev;
static struct mutex tcpc_otg_lock;
static bool tcpc_boost_on;
static int tcpc_otg_enable(void)
{
if (!usbc_otg_attached) {
mt_usbhost_connect();
usbc_otg_attached = true;
}
return 0;
}
/* LiYue@BSP.CHG.Basic, 2019/09/13, Modify for OTG */
int tcpc_otg_disable(void)
/*ELSE VENDOR_EDIT*/
//static int tcpc_otg_disable(void)
/*END VENDOR_EDIT*/
{
if (usbc_otg_attached) {
mt_usbhost_disconnect();
usbc_otg_attached = false;
}
return 0;
}
/* LiYue@BSP.CHG.Basic, 2019/09/13, Add for OTG */
EXPORT_SYMBOL(tcpc_otg_disable);
/*END VENDOR_EDIT*/
/*VENDOR_EDIT*/
/* LiYue@BSP.CHG.Basic, 2019/09/13, Modify for OTG */
void tcpc_power_work_call(bool enable)
/*ELSE*/
//static void tcpc_power_work_call(bool enable)
/*END VENDOR_EDIT*/
{
if (enable) {
if (!tcpc_boost_on) {
mt_vbus_on();
tcpc_boost_on = true;
}
} else {
if (tcpc_boost_on) {
mt_vbus_off();
tcpc_boost_on = false;
}
}
}
/* LiYue@BSP.CHG.Basic, 2019/09/13, Add for OTG */
EXPORT_SYMBOL(tcpc_power_work_call);
/*END VENDOR_EDIT*/
static int otg_tcp_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
struct tcp_notify *noti = data;
bool otg_power_enable, otg_on;
mutex_lock(&tcpc_otg_lock);
otg_on = usbc_otg_attached;
mutex_unlock(&tcpc_otg_lock);
switch (event) {
case TCP_NOTIFY_SOURCE_VBUS:
pr_info("%s source vbus = %dmv\n",
__func__, noti->vbus_state.mv);
otg_power_enable = (noti->vbus_state.mv) ? true : false;
tcpc_power_work_call(otg_power_enable);
break;
case TCP_NOTIFY_TYPEC_STATE:
pr_info("%s, TCP_NOTIFY_TYPEC_STATE, old_state=%d, new_state=%d\n",
__func__, noti->typec_state.old_state,
noti->typec_state.new_state);
if (noti->typec_state.old_state == TYPEC_UNATTACHED &&
noti->typec_state.new_state == TYPEC_ATTACHED_SRC) {
pr_info("%s OTG Plug in\n", __func__);
/* LiYue@BSP.CHG.Basic, 2019/09/13, Add for charging */
//oppo_wake_up_usbtemp_thread();
printk(KERN_ERR "!!!!! otg_tcp_notifier_call: [1]\n");
/*END VENDOR_EDIT*/
tcpc_otg_enable();
} else if ((noti->typec_state.old_state == TYPEC_ATTACHED_SRC ||
noti->typec_state.old_state == TYPEC_ATTACHED_SNK) &&
noti->typec_state.new_state == TYPEC_UNATTACHED) {
if (otg_on) {
pr_info("%s OTG Plug out\n", __func__);
/* LiYue@BSP.CHG.Basic, 2019/09/13, Add for charging */
printk(KERN_ERR "!!!!! otg_tcp_notifier_call: [0]\n");
/*END VENDOR_EDIT*/
tcpc_otg_disable();
} else {
pr_info("%s USB Plug out\n", __func__);
mt_usb_disconnect();
}
}
/* switch U3 mux */
#ifdef CONFIG_MTK_USB_TYPEC_U3_MUX
if (noti->typec_state.new_state == TYPEC_ATTACHED_CUSTOM_SRC ||
noti->typec_state.new_state == TYPEC_ATTACHED_SNK) {
usb3_switch_dps_en(false);
if (noti->typec_state.polarity == 0)
usb3_switch_ctrl_sel(CC1_SIDE);
else
usb3_switch_ctrl_sel(CC2_SIDE);
} else if (noti->typec_state.new_state == TYPEC_ATTACHED_SRC) {
usb3_switch_dps_en(false);
if (noti->typec_state.polarity == 0)
usb3_switch_ctrl_sel(CC2_SIDE);
else
usb3_switch_ctrl_sel(CC1_SIDE);
} else if (noti->typec_state.new_state == TYPEC_UNATTACHED) {
usb3_switch_dps_en(true);
}
#endif
break;
case TCP_NOTIFY_DR_SWAP:
pr_info("%s TCP_NOTIFY_DR_SWAP, new role=%d\n",
__func__, noti->swap_state.new_role);
if (otg_on &&
noti->swap_state.new_role == PD_ROLE_UFP) {
pr_info("%s switch role to device\n", __func__);
tcpc_otg_disable();
mt_usb_connect();
} else if (!otg_on &&
noti->swap_state.new_role == PD_ROLE_DFP) {
pr_info("%s switch role to host\n", __func__);
mt_usb_disconnect();
tcpc_otg_enable();
}
break;
}
return NOTIFY_OK;
}
static int __init mtk_typec_init(void)
{
int ret;
mutex_init(&tcpc_otg_lock);
otg_tcpc_dev = tcpc_dev_get_by_name("type_c_port0");
if (!otg_tcpc_dev) {
pr_info("%s get tcpc device type_c_port0 fail\n", __func__);
return -ENODEV;
}
otg_nb.notifier_call = otg_tcp_notifier_call;
ret = register_tcp_dev_notifier(otg_tcpc_dev, &otg_nb,
TCP_NOTIFY_TYPE_USB|TCP_NOTIFY_TYPE_VBUS|TCP_NOTIFY_TYPE_MISC);
if (ret < 0) {
pr_info("%s register tcpc notifer fail\n", __func__);
return -EINVAL;
}
return 0;
}
late_initcall(mtk_typec_init);
static void __exit mtk_typec_init_cleanup(void)
{
}
module_exit(mtk_typec_init_cleanup);