Files
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

936 lines
23 KiB
C

/*
* Copyright (C) 2017 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.
*/
#ifdef CONFIG_USB_MTK_OTG
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include "musb_core.h"
#include <linux/platform_device.h>
#include "musbhsdma.h"
#include "usb20.h"
#include <linux/of_irq.h>
#include <linux/of_address.h>
#ifdef CONFIG_MTK_USB_TYPEC
#ifdef CONFIG_TCPC_CLASS
#include "tcpm.h"
#include <linux/workqueue.h>
#include <linux/mutex.h>
static struct notifier_block otg_nb;
static struct tcpc_device *otg_tcpc_dev;
static struct delayed_work register_otg_work;
static int otg_tcp_notifier_call(struct notifier_block *nb,
unsigned long event, void *data);
#define TCPC_OTG_DEV_NAME "type_c_port0"
static void do_register_otg_work(struct work_struct *data)
{
#define REGISTER_OTG_WORK_DELAY 500
static int ret;
if (!otg_tcpc_dev)
otg_tcpc_dev = tcpc_dev_get_by_name(TCPC_OTG_DEV_NAME);
if (!otg_tcpc_dev) {
DBG(0, "get type_c_port0 fail\n");
queue_delayed_work(mtk_musb->st_wq, &register_otg_work,
msecs_to_jiffies(REGISTER_OTG_WORK_DELAY));
return;
}
otg_nb.notifier_call = otg_tcp_notifier_call;
ret = register_tcp_dev_notifier(otg_tcpc_dev, &otg_nb,
TCP_NOTIFY_TYPE_VBUS | TCP_NOTIFY_TYPE_USB |
TCP_NOTIFY_TYPE_MISC);
if (ret < 0) {
DBG(0, "register OTG <%p> fail\n", otg_tcpc_dev);
queue_delayed_work(mtk_musb->st_wq, &register_otg_work,
msecs_to_jiffies(REGISTER_OTG_WORK_DELAY));
return;
}
DBG(0, "register OTG <%p> ok\n", otg_tcpc_dev);
}
#endif
#endif
static void mt_usb_host_connect(int delay);
static void mt_usb_host_disconnect(int delay);
#ifdef CONFIG_MTK_CHARGER
#if CONFIG_MTK_GAUGE_VERSION == 30
#include <mt-plat/charger_class.h>
static struct charger_device *primary_charger;
#endif
#endif
#include <mt-plat/mtk_boot_common.h>
struct device_node *usb_node;
static int iddig_eint_num;
static ktime_t ktime_start, ktime_end;
static struct musb_fifo_cfg fifo_cfg_host[] = {
{ .hw_ep_num = 1, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 1, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 2, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 2, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 3, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 3, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 4, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 4, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 5, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 5, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 6, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 6, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 7, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 7, .style = MUSB_FIFO_RX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 8, .style = MUSB_FIFO_TX,
.maxpacket = 512, .mode = MUSB_BUF_SINGLE},
{ .hw_ep_num = 8, .style = MUSB_FIFO_RX,
.maxpacket = 64, .mode = MUSB_BUF_SINGLE},
};
u32 delay_time = 15;
module_param(delay_time, int, 0644);
u32 delay_time1 = 55;
module_param(delay_time1, int, 0644);
u32 iddig_cnt;
module_param(iddig_cnt, int, 0644);
static bool vbus_on;
module_param(vbus_on, bool, 0644);
static int vbus_control;
module_param(vbus_control, int, 0644);
bool usb20_check_vbus_on(void)
{
DBG(0, "vbus_on<%d>\n", vbus_on);
return vbus_on;
}
static void _set_vbus(int is_on)
{
#ifdef CONFIG_MTK_CHARGER
#if CONFIG_MTK_GAUGE_VERSION == 30
if (!primary_charger) {
DBG(0, "vbus_init<%d>\n", vbus_on);
primary_charger = get_charger_by_name("primary_chg");
if (!primary_charger) {
DBG(0, "get primary charger device failed\n");
return;
}
}
#endif
#endif
DBG(0, "op<%d>, status<%d>\n", is_on, vbus_on);
if (is_on && !vbus_on) {
/* update flag 1st then enable VBUS to make
* host mode correct used by PMIC
*/
vbus_on = true;
#ifdef CONFIG_MTK_CHARGER
#if CONFIG_MTK_GAUGE_VERSION == 30
charger_dev_enable_otg(primary_charger, true);
charger_dev_set_boost_current_limit(primary_charger, 1500000);
#else
set_chr_enable_otg(0x1);
set_chr_boost_current_limit(1500);
#endif
#endif
} else if (!is_on && vbus_on) {
/* disable VBUS 1st then update flag
* to make host mode correct used by PMIC
*/
vbus_on = false;
#ifdef CONFIG_MTK_CHARGER
#if CONFIG_MTK_GAUGE_VERSION == 30
charger_dev_enable_otg(primary_charger, false);
#else
set_chr_enable_otg(0x0);
#endif
#endif
}
}
void mt_usb_set_vbus(struct musb *musb, int is_on)
{
#ifndef FPGA_PLATFORM
DBG(0, "is_on<%d>, control<%d>\n", is_on, vbus_control);
if (!vbus_control)
return;
if (is_on)
_set_vbus(1);
else
_set_vbus(0);
#endif
}
int mt_usb_get_vbus_status(struct musb *musb)
{
#if 1
return true;
#else
int ret = 0;
if ((musb_readb(musb->mregs, MUSB_DEVCTL) &
MUSB_DEVCTL_VBUS) != MUSB_DEVCTL_VBUS)
ret = 1;
else
DBG(0, "VBUS error, devctl=%x, power=%d\n",
musb_readb(musb->mregs, MUSB_DEVCTL),
musb->power);
pr_debug("vbus ready = %d\n", ret);
return ret;
#endif
}
#if defined(CONFIG_USBIF_COMPLIANCE)
u32 sw_deboun_time = 1;
#else
u32 sw_deboun_time = 400;
#endif
module_param(sw_deboun_time, int, 0644);
u32 typec_control;
module_param(typec_control, int, 0644);
static bool typec_req_host;
static bool iddig_req_host;
static void do_host_work(struct work_struct *data);
static void issue_host_work(int ops, int delay, bool on_st)
{
struct mt_usb_work *work;
if (!mtk_musb) {
DBG(0, "mtk_musb = NULL\n");
return;
}
/* create and prepare worker */
work = kzalloc(sizeof(struct mt_usb_work), GFP_ATOMIC);
if (!work) {
DBG(0, "work is NULL, directly return\n");
return;
}
work->ops = ops;
INIT_DELAYED_WORK(&work->dwork, do_host_work);
/* issue connection work */
DBG(0, "issue work, ops<%d>, delay<%d>, on_st<%d>\n",
ops, delay, on_st);
if (on_st)
queue_delayed_work(mtk_musb->st_wq,
&work->dwork, msecs_to_jiffies(delay));
else
schedule_delayed_work(&work->dwork,
msecs_to_jiffies(delay));
}
void mt_usb_host_connect(int delay)
{
typec_req_host = true;
DBG(0, "%s\n", typec_req_host ? "connect" : "disconnect");
issue_host_work(CONNECTION_OPS_CONN, delay, true);
}
void mt_usb_host_disconnect(int delay)
{
typec_req_host = false;
DBG(0, "%s\n", typec_req_host ? "connect" : "disconnect");
issue_host_work(CONNECTION_OPS_DISC, delay, true);
}
#ifdef CONFIG_MTK_USB_TYPEC
#ifdef CONFIG_TCPC_CLASS
static void do_vbus_work(struct work_struct *data)
{
struct mt_usb_work *work =
container_of(data, struct mt_usb_work, dwork.work);
bool vbus_on = (work->ops ==
VBUS_OPS_ON ? true : false);
_set_vbus(vbus_on);
/* free kfree */
kfree(work);
}
static void issue_vbus_work(int ops, int delay)
{
struct mt_usb_work *work;
if (!mtk_musb) {
DBG(0, "mtk_musb = NULL\n");
return;
}
/* create and prepare worker */
work = kzalloc(sizeof(struct mt_usb_work), GFP_ATOMIC);
if (!work) {
DBG(0, "work is NULL, directly return\n");
return;
}
work->ops = ops;
INIT_DELAYED_WORK(&work->dwork, do_vbus_work);
/* issue vbus work */
DBG(0, "issue work, ops<%d>, delay<%d>\n", ops, delay);
queue_delayed_work(mtk_musb->st_wq,
&work->dwork, msecs_to_jiffies(delay));
}
static void mt_usb_vbus_on(int delay)
{
DBG(0, "vbus_on\n");
issue_vbus_work(VBUS_OPS_ON, delay);
}
static void mt_usb_vbus_off(int delay)
{
DBG(0, "vbus_off\n");
issue_vbus_work(VBUS_OPS_OFF, delay);
}
static int otg_tcp_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
struct tcp_notify *noti = data;
switch (event) {
case TCP_NOTIFY_SOURCE_VBUS:
DBG(0, "source vbus = %dmv\n", noti->vbus_state.mv);
if (noti->vbus_state.mv)
mt_usb_vbus_on(0);
else
mt_usb_vbus_off(0);
break;
case TCP_NOTIFY_TYPEC_STATE:
DBG(0, "TCP_NOTIFY_TYPEC_STATE, old_state=%d, new_state=%d\n",
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) {
DBG(0, "OTG Plug in\n");
mt_usb_host_connect(0);
} else if ((noti->typec_state.old_state == TYPEC_ATTACHED_SRC ||
noti->typec_state.old_state == TYPEC_ATTACHED_SNK ||
noti->typec_state.old_state ==
TYPEC_ATTACHED_NORP_SRC) &&
noti->typec_state.new_state == TYPEC_UNATTACHED) {
if (is_host_active(mtk_musb)) {
DBG(0, "OTG Plug out\n");
mt_usb_host_disconnect(0);
} else {
DBG(0, "USB Plug out\n");
mt_usb_dev_disconnect();
}
#ifdef CONFIG_MTK_UART_USB_SWITCH
} else if ((noti->typec_state.new_state ==
TYPEC_ATTACHED_SNK ||
noti->typec_state.new_state ==
TYPEC_ATTACHED_CUSTOM_SRC ||
noti->typec_state.new_state ==
TYPEC_ATTACHED_NORP_SRC) &&
in_uart_mode) {
pr_info("%s USB cable plugged-in in UART mode. "
"Switch to USB mode.\n", __func__);
usb_phy_switch_to_usb();
#endif
}
break;
case TCP_NOTIFY_DR_SWAP:
DBG(0, "TCP_NOTIFY_DR_SWAP, new role=%d\n",
noti->swap_state.new_role);
if (is_host_active(mtk_musb) &&
noti->swap_state.new_role == PD_ROLE_UFP) {
DBG(0, "switch role to device\n");
mt_usb_host_disconnect(0);
mt_usb_connect();
} else if (is_peripheral_active(mtk_musb) &&
noti->swap_state.new_role == PD_ROLE_DFP) {
DBG(0, "switch role to host\n");
mt_usb_dev_disconnect();
mt_usb_host_connect(0);
}
break;
}
return NOTIFY_OK;
}
#endif
#endif
static bool musb_is_host(void)
{
bool host_mode = 0;
if (typec_control)
host_mode = typec_req_host;
else
host_mode = iddig_req_host;
return host_mode;
}
void musb_session_restart(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
musb_writeb(mbase, MUSB_DEVCTL,
(musb_readb(mbase,
MUSB_DEVCTL) & (~MUSB_DEVCTL_SESSION)));
DBG(0, "[MUSB] stopped session for VBUSERROR interrupt\n");
USBPHY_SET32(0x6c, (0x3c<<8));
USBPHY_SET32(0x6c, (0x10<<0));
USBPHY_CLR32(0x6c, (0x2c<<0));
DBG(0, "[MUSB] force PHY to idle, 0x6c=%x\n", USBPHY_READ32(0x6c));
mdelay(5);
USBPHY_CLR32(0x6c, (0x3c<<8));
USBPHY_CLR32(0x6c, (0x3c<<0));
DBG(0, "[MUSB] let PHY resample VBUS, 0x6c=%x\n"
, USBPHY_READ32(0x6c));
musb_writeb(mbase, MUSB_DEVCTL,
(musb_readb(mbase,
MUSB_DEVCTL) | MUSB_DEVCTL_SESSION));
DBG(0, "[MUSB] restart session\n");
}
static struct delayed_work host_plug_test_work;
int host_plug_test_enable; /* default disable */
module_param(host_plug_test_enable, int, 0644);
int host_plug_in_test_period_ms = 5000;
module_param(host_plug_in_test_period_ms, int, 0644);
int host_plug_out_test_period_ms = 5000;
module_param(host_plug_out_test_period_ms, int, 0644);
int host_test_vbus_off_time_us = 3000;
module_param(host_test_vbus_off_time_us, int, 0644);
int host_test_vbus_only = 1;
module_param(host_test_vbus_only, int, 0644);
static int host_plug_test_triggered;
void switch_int_to_device(struct musb *musb)
{
irq_set_irq_type(iddig_eint_num, IRQF_TRIGGER_HIGH);
enable_irq(iddig_eint_num);
DBG(0, "switch_int_to_device is done\n");
}
void switch_int_to_host(struct musb *musb)
{
irq_set_irq_type(iddig_eint_num, IRQF_TRIGGER_LOW);
enable_irq(iddig_eint_num);
DBG(0, "switch_int_to_host is done\n");
}
static void do_host_plug_test_work(struct work_struct *data)
{
static ktime_t ktime_begin, ktime_end;
static s64 diff_time;
static int host_on;
static struct wakeup_source host_test_wakelock;
static int wake_lock_inited;
if (!wake_lock_inited) {
DBG(0, "wake_lock_init\n");
wakeup_source_init(&host_test_wakelock,
"host.test.lock");
wake_lock_inited = 1;
}
host_plug_test_triggered = 1;
/* sync global status */
mb();
__pm_stay_awake(&host_test_wakelock);
DBG(0, "BEGIN");
ktime_begin = ktime_get();
host_on = 1;
while (1) {
if (!musb_is_host() && host_on) {
DBG(0, "about to exit");
break;
}
msleep(50);
ktime_end = ktime_get();
diff_time = ktime_to_ms(ktime_sub(ktime_end, ktime_begin));
if (host_on && diff_time >= host_plug_in_test_period_ms) {
host_on = 0;
DBG(0, "OFF\n");
ktime_begin = ktime_get();
/* simulate plug out */
_set_vbus(0);
udelay(host_test_vbus_off_time_us);
if (!host_test_vbus_only)
issue_host_work(CONNECTION_OPS_DISC, 0, false);
} else if (!host_on && diff_time >=
host_plug_out_test_period_ms) {
host_on = 1;
DBG(0, "ON\n");
ktime_begin = ktime_get();
if (!host_test_vbus_only)
issue_host_work(CONNECTION_OPS_CONN, 0, false);
_set_vbus(1);
msleep(100);
}
}
/* wait host_work done */
msleep(1000);
host_plug_test_triggered = 0;
__pm_relax(&host_test_wakelock);
DBG(0, "END\n");
}
#define ID_PIN_WORK_RECHECK_TIME 30 /* 30 ms */
#define ID_PIN_WORK_BLOCK_TIMEOUT 30000 /* 30000 ms */
static void do_host_work(struct work_struct *data)
{
u8 devctl = 0;
unsigned long flags;
static int inited, timeout; /* default to 0 */
static s64 diff_time;
bool host_on;
int usb_clk_state = NO_CHANGE;
struct mt_usb_work *work =
container_of(data, struct mt_usb_work, dwork.work);
/* kernel_init_done should be set in
* early-init stage through init.$platform.usb.rc
*/
while (!inited && !kernel_init_done &&
!mtk_musb->is_ready && !timeout) {
ktime_end = ktime_get();
diff_time = ktime_to_ms(ktime_sub(ktime_end, ktime_start));
DBG_LIMIT(3,
"init_done:%d, is_ready:%d, inited:%d, TO:%d, diff:%lld",
kernel_init_done,
mtk_musb->is_ready,
inited,
timeout,
diff_time);
if (diff_time > ID_PIN_WORK_BLOCK_TIMEOUT) {
DBG(0, "diff_time:%lld\n", diff_time);
timeout = 1;
}
msleep(ID_PIN_WORK_RECHECK_TIME);
}
if (!inited) {
DBG(0, "PASS,init_done:%d,is_ready:%d,inited:%d, TO:%d\n",
kernel_init_done, mtk_musb->is_ready,
inited, timeout);
inited = 1;
}
/* always prepare clock and check if need to unprepater later */
/* clk_prepare_cnt +1 here */
usb_prepare_clock(true);
down(&mtk_musb->musb_lock);
host_on = (work->ops ==
CONNECTION_OPS_CONN ? true : false);
DBG(0, "work start, is_host=%d, host_on=%d\n",
mtk_musb->is_host, host_on);
if (host_on && !mtk_musb->is_host) {
/* switch to HOST state before turn on VBUS */
MUSB_HST_MODE(mtk_musb);
/* to make sure all event clear */
msleep(32);
#ifdef CONFIG_MTK_UAC_POWER_SAVING
if (!usb_on_sram) {
int ret;
ret = gpd_switch_to_sram(mtk_musb->controller);
DBG(0, "gpd_switch_to_sram, ret<%d>\n", ret);
if (ret == 0)
usb_on_sram = 1;
}
#endif
/* setup fifo for host mode */
ep_config_from_table_for_host(mtk_musb);
if (!mtk_musb->host_suspend)
__pm_stay_awake(mtk_musb->usb_lock);
mt_usb_set_vbus(mtk_musb, 1);
/* this make PHY operation workable */
musb_platform_enable(mtk_musb);
/* for no VBUS sensing IP*/
#if 1
/* wait VBUS ready */
msleep(100);
/* clear session*/
devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
musb_writeb(mtk_musb->mregs,
MUSB_DEVCTL, (devctl&(~MUSB_DEVCTL_SESSION)));
set_usb_phy_mode(PHY_IDLE_MODE);
/* wait */
mdelay(5);
/* restart session */
devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
musb_writeb(mtk_musb->mregs,
MUSB_DEVCTL, (devctl | MUSB_DEVCTL_SESSION));
set_usb_phy_mode(PHY_HOST_ACTIVE);
#endif
musb_start(mtk_musb);
if (!typec_control && !host_plug_test_triggered)
switch_int_to_device(mtk_musb);
if (host_plug_test_enable && !host_plug_test_triggered)
queue_delayed_work(mtk_musb->st_wq,
&host_plug_test_work, 0);
usb_clk_state = OFF_TO_ON;
} else if (!host_on && mtk_musb->is_host) {
/* switch from host -> device */
/* for device no disconnect interrupt */
spin_lock_irqsave(&mtk_musb->lock, flags);
if (mtk_musb->is_active) {
DBG(0, "for not receiving disconnect interrupt\n");
usb_hcd_resume_root_hub(musb_to_hcd(mtk_musb));
musb_root_disconnect(mtk_musb);
}
spin_unlock_irqrestore(&mtk_musb->lock, flags);
DBG(1, "devctl is %x\n",
musb_readb(mtk_musb->mregs, MUSB_DEVCTL));
musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0);
if (mtk_musb->usb_lock->active)
__pm_relax(mtk_musb->usb_lock);
mt_usb_set_vbus(mtk_musb, 0);
/* for no VBUS sensing IP */
#if 1
set_usb_phy_mode(PHY_IDLE_MODE);
#endif
musb_stop(mtk_musb);
if (!typec_control && !host_plug_test_triggered)
switch_int_to_host(mtk_musb);
#ifdef CONFIG_MTK_UAC_POWER_SAVING
if (usb_on_sram) {
gpd_switch_to_dram(mtk_musb->controller);
usb_on_sram = 0;
}
#endif
/* to make sure all event clear */
msleep(32);
mtk_musb->xceiv->otg->state = OTG_STATE_B_IDLE;
/* switch to DEV state after turn off VBUS */
MUSB_DEV_MODE(mtk_musb);
usb_clk_state = ON_TO_OFF;
}
DBG(0, "work end, is_host=%d\n", mtk_musb->is_host);
up(&mtk_musb->musb_lock);
if (usb_clk_state == ON_TO_OFF) {
/* clock on -> of: clk_prepare_cnt -2 */
usb_prepare_clock(false);
usb_prepare_clock(false);
} else if (usb_clk_state == NO_CHANGE) {
/* clock no change : clk_prepare_cnt -1 */
usb_prepare_clock(false);
}
/* free mt_usb_work */
kfree(work);
}
static irqreturn_t mt_usb_ext_iddig_int(int irq, void *dev_id)
{
iddig_cnt++;
iddig_req_host = !iddig_req_host;
DBG(0, "id pin assert, %s\n", iddig_req_host ?
"connect" : "disconnect");
if (iddig_req_host)
mt_usb_host_connect(0);
else
mt_usb_host_disconnect(0);
disable_irq_nosync(iddig_eint_num);
return IRQ_HANDLED;
}
static const struct of_device_id otg_iddig_of_match[] = {
{.compatible = "mediatek,usb_iddig_bi_eint"},
{},
};
static int otg_iddig_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
iddig_eint_num = irq_of_parse_and_map(node, 0);
DBG(0, "iddig_eint_num<%d>\n", iddig_eint_num);
if (iddig_eint_num < 0)
return -ENODEV;
ret = request_irq(iddig_eint_num, mt_usb_ext_iddig_int,
IRQF_TRIGGER_LOW, "USB_IDDIG", NULL);
if (ret) {
DBG(0,
"request EINT <%d> fail, ret<%d>\n",
iddig_eint_num, ret);
return ret;
}
return 0;
}
static struct platform_driver otg_iddig_driver = {
.probe = otg_iddig_probe,
/* .remove = otg_iddig_remove, */
/* .shutdown = otg_iddig_shutdown, */
.driver = {
.name = "otg_iddig",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(otg_iddig_of_match),
},
};
static int iddig_int_init(void)
{
int ret = 0;
ret = platform_driver_register(&otg_iddig_driver);
if (ret)
DBG(0, "ret:%d\n", ret);
return 0;
}
void mt_usb_otg_init(struct musb *musb)
{
/* BYPASS OTG function in special mode */
if (get_boot_mode() == META_BOOT
#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING
|| get_boot_mode() == KERNEL_POWER_OFF_CHARGING_BOOT
|| get_boot_mode() == LOW_POWER_OFF_CHARGING_BOOT
#endif
) {
#ifdef CONFIG_MTK_USB_TYPEC
DBG(0, "with TYPEC in special mode %d, keep going\n",
get_boot_mode());
#else
DBG(0, "w/o TYPEC in special mode %d, skip\n",
get_boot_mode());
return;
#endif
}
/* test */
INIT_DELAYED_WORK(&host_plug_test_work, do_host_plug_test_work);
ktime_start = ktime_get();
/* CONNECTION MANAGEMENT*/
#ifdef CONFIG_MTK_USB_TYPEC
DBG(0, "host controlled by TYPEC\n");
typec_control = 1;
#ifdef CONFIG_TCPC_CLASS
INIT_DELAYED_WORK(&register_otg_work, do_register_otg_work);
queue_delayed_work(mtk_musb->st_wq, &register_otg_work, 0);
vbus_control = 0;
#endif
#else
DBG(0, "host controlled by IDDIG\n");
iddig_int_init();
vbus_control = 1;
#endif
/* EP table */
musb->fifo_cfg_host = fifo_cfg_host;
musb->fifo_cfg_host_size = ARRAY_SIZE(fifo_cfg_host);
}
void mt_usb_otg_exit(struct musb *musb)
{
DBG(0, "OTG disable vbus\n");
mt_usb_set_vbus(mtk_musb, 0);
}
enum {
DO_IT = 0,
REVERT,
};
static void bypass_disc_circuit(int act)
{
u32 val;
usb_prepare_enable_clock(true);
val = USBPHY_READ32(0x18);
DBG(0, "val<0x%x>\n", val);
/* 0x18, 13-12 RG_USB20_HSRX_MMODE_SELE, dft:00 */
if (act == DO_IT) {
USBPHY_CLR32(0x18, (0x10<<8));
USBPHY_SET32(0x18, (0x20<<8));
} else {
USBPHY_CLR32(0x18, (0x10<<8));
USBPHY_CLR32(0x18, (0x20<<8));
}
val = USBPHY_READ32(0x18);
DBG(0, "val<0x%x>\n", val);
usb_prepare_enable_clock(false);
}
static void disc_threshold_to_max(int act)
{
u32 val;
usb_prepare_enable_clock(true);
val = USBPHY_READ32(0x18);
DBG(0, "val<0x%x>\n", val);
/* 0x18, 7-4 RG_USB20_DISCTH, dft:1000 */
if (act == DO_IT) {
USBPHY_SET32(0x18, (0xf0<<0));
} else {
USBPHY_CLR32(0x18, (0x70<<0));
USBPHY_SET32(0x18, (0x80<<0));
}
val = USBPHY_READ32(0x18);
DBG(0, "val<0x%x>\n", val);
usb_prepare_enable_clock(false);
}
static int option;
static int set_option(const char *val, const struct kernel_param *kp)
{
int local_option;
int rv;
/* update module parameter */
rv = param_set_int(val, kp);
if (rv)
return rv;
/* update local_option */
rv = kstrtoint(val, 10, &local_option);
if (rv != 0)
return rv;
DBG(0, "option:%d, local_option:%d\n", option, local_option);
switch (local_option) {
case 0:
DBG(0, "case %d\n", local_option);
iddig_int_init();
break;
case 1:
DBG(0, "case %d\n", local_option);
mt_usb_host_connect(0);
break;
case 2:
DBG(0, "case %d\n", local_option);
mt_usb_host_disconnect(0);
break;
case 3:
DBG(0, "case %d\n", local_option);
mt_usb_host_connect(3000);
break;
case 4:
DBG(0, "case %d\n", local_option);
mt_usb_host_disconnect(3000);
break;
case 5:
DBG(0, "case %d\n", local_option);
disc_threshold_to_max(DO_IT);
break;
case 6:
DBG(0, "case %d\n", local_option);
disc_threshold_to_max(REVERT);
break;
case 7:
DBG(0, "case %d\n", local_option);
bypass_disc_circuit(DO_IT);
break;
case 8:
DBG(0, "case %d\n", local_option);
bypass_disc_circuit(REVERT);
break;
case 9:
DBG(0, "case %d\n", local_option);
_set_vbus(1);
break;
case 10:
DBG(0, "case %d\n", local_option);
_set_vbus(0);
break;
default:
break;
}
return 0;
}
static struct kernel_param_ops option_param_ops = {
.set = set_option,
.get = param_get_int,
};
module_param_cb(option, &option_param_ops, &option, 0644);
#else
#include "musb_core.h"
/* for not define CONFIG_USB_MTK_OTG */
void mt_usb_otg_init(struct musb *musb) {}
void mt_usb_otg_exit(struct musb *musb) {}
void mt_usb_set_vbus(struct musb *musb, int is_on) {}
int mt_usb_get_vbus_status(struct musb *musb) {return 1; }
void switch_int_to_device(struct musb *musb) {}
void switch_int_to_host(struct musb *musb) {}
void musb_session_restart(struct musb *musb) {}
#endif