kernel-brax3-ubuntu-touch/drivers/misc/mediatek/extcon/extcon-mtk-usb.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

746 lines
19 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 MediaTek Inc.
*/
#include <linux/extcon-provider.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/role.h>
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include "extcon-mtk-usb.h"
/* pri add by liuyong 20231016 start */
#include "../../../power/supply/mtk_charger.h"
/* pri add by liuyong 20231016 start */
#if IS_ENABLED(CONFIG_TCPC_CLASS)
#include "tcpm.h"
#endif
/* prize liuyong, add for fac test, 20231024, start */
struct mtk_extcon_info *g_extcon;
static struct delayed_work delay_work_t;
/* prize liuyong, add for fac test, 20231024, end */
static const unsigned int usb_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_NONE,
};
static void mtk_usb_extcon_update_role(struct work_struct *work)
{
struct usb_role_info *role = container_of(to_delayed_work(work),
struct usb_role_info, dwork);
struct mtk_extcon_info *extcon = role->extcon;
unsigned int cur_dr, new_dr;
cur_dr = extcon->c_role;
new_dr = role->d_role;
dev_info(extcon->dev, "cur_dr(%d) new_dr(%d)\n", cur_dr, new_dr);
/* none -> device */
if (cur_dr == USB_ROLE_NONE &&
new_dr == USB_ROLE_DEVICE) {
extcon_set_state_sync(extcon->edev, EXTCON_USB, true);
/* none -> host */
} else if (cur_dr == USB_ROLE_NONE &&
new_dr == USB_ROLE_HOST) {
extcon_set_state_sync(extcon->edev, EXTCON_USB_HOST, true);
/* device -> none */
} else if (cur_dr == USB_ROLE_DEVICE &&
new_dr == USB_ROLE_NONE) {
extcon_set_state_sync(extcon->edev, EXTCON_USB, false);
/* host -> none */
} else if (cur_dr == USB_ROLE_HOST &&
new_dr == USB_ROLE_NONE) {
extcon_set_state_sync(extcon->edev, EXTCON_USB_HOST, false);
/* device -> host */
} else if (cur_dr == USB_ROLE_DEVICE &&
new_dr == USB_ROLE_HOST) {
extcon_set_state_sync(extcon->edev, EXTCON_USB, false);
extcon_set_state_sync(extcon->edev, EXTCON_USB_HOST, true);
/* host -> device */
} else if (cur_dr == USB_ROLE_HOST &&
new_dr == USB_ROLE_DEVICE) {
extcon_set_state_sync(extcon->edev, EXTCON_USB_HOST, false);
extcon_set_state_sync(extcon->edev, EXTCON_USB, true);
}
/* usb role switch */
if (extcon->role_sw)
usb_role_switch_set_role(extcon->role_sw, new_dr);
extcon->c_role = new_dr;
kfree(role);
}
static int mtk_usb_extcon_set_role(struct mtk_extcon_info *extcon,
unsigned int role)
{
struct usb_role_info *role_info;
/* create and prepare worker */
role_info = kzalloc(sizeof(*role_info), GFP_ATOMIC);
if (!role_info)
return -ENOMEM;
INIT_DELAYED_WORK(&role_info->dwork, mtk_usb_extcon_update_role);
role_info->extcon = extcon;
role_info->d_role = role;
/* issue connection work */
queue_delayed_work(extcon->extcon_wq, &role_info->dwork, 0);
return 0;
}
/* prize liuyong, add for fac test, 20231024, start */
int usb_set_role(int role)
{
if (!g_extcon) {
pr_err("g_extcon is NULL,return..........\n");
return -1;
}
pr_err("mtk_usb_extcon_set_role:%d\n",role);
if (role) {
mtk_usb_extcon_set_role(g_extcon,USB_ROLE_DEVICE);
} else {
mtk_usb_extcon_set_role(g_extcon,USB_ROLE_NONE);
}
return 0;
}
EXPORT_SYMBOL(usb_set_role);
/* prize liuyong, add for fac test, 20231024, end */
static bool usb_is_online(struct mtk_extcon_info *extcon)
{
union power_supply_propval pval;
union power_supply_propval tval;
int ret;
ret = power_supply_get_property(extcon->usb_psy,
POWER_SUPPLY_PROP_ONLINE, &pval);
if (ret < 0) {
dev_info(extcon->dev, "failed to get online prop\n");
return false;
}
ret = power_supply_get_property(extcon->usb_psy,
POWER_SUPPLY_PROP_TYPE, &tval);
if (ret < 0) {
dev_info(extcon->dev, "failed to get usb type\n");
return false;
}
dev_info(extcon->dev, "online=%d, type=%d\n", pval.intval, tval.intval);
if (pval.intval && (tval.intval == POWER_SUPPLY_TYPE_USB ||
tval.intval == POWER_SUPPLY_TYPE_USB_CDP))
return true;
else
return false;
}
static void mtk_usb_extcon_psy_detector(struct work_struct *work)
{
struct mtk_extcon_info *extcon = container_of(to_delayed_work(work),
struct mtk_extcon_info, wq_psy);
/* Workaround for PR_SWAP, IF tcpc_dev, then do not switch role. */
/* Since we will set USB to none when type-c plug out */
if (extcon->tcpc_dev) {
if (usb_is_online(extcon) && extcon->c_role == USB_ROLE_NONE)
mtk_usb_extcon_set_role(extcon, USB_ROLE_DEVICE);
} else {
if (usb_is_online(extcon))
mtk_usb_extcon_set_role(extcon, USB_ROLE_DEVICE);
else
mtk_usb_extcon_set_role(extcon, USB_ROLE_NONE);
}
}
static int mtk_usb_extcon_psy_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct power_supply *psy = data;
struct mtk_extcon_info *extcon = container_of(nb,
struct mtk_extcon_info, psy_nb);
if (event != PSY_EVENT_PROP_CHANGED || psy != extcon->usb_psy)
return NOTIFY_DONE;
queue_delayed_work(system_power_efficient_wq, &extcon->wq_psy, 0);
return NOTIFY_DONE;
}
static int mtk_usb_extcon_psy_init(struct mtk_extcon_info *extcon)
{
int ret = 0;
struct device *dev = extcon->dev;
if (!of_property_read_bool(dev->of_node, "charger")) {
ret = -EINVAL;
goto fail;
}
extcon->usb_psy = devm_power_supply_get_by_phandle(dev, "charger");
if (IS_ERR_OR_NULL(extcon->usb_psy)) {
/* try to get by name */
extcon->usb_psy = power_supply_get_by_name("primary_chg");
if (IS_ERR_OR_NULL(extcon->usb_psy)) {
dev_err(dev, "fail to get usb_psy\n");
ret = -EINVAL;
goto fail;
}
}
INIT_DELAYED_WORK(&extcon->wq_psy, mtk_usb_extcon_psy_detector);
extcon->psy_nb.notifier_call = mtk_usb_extcon_psy_notifier;
ret = power_supply_reg_notifier(&extcon->psy_nb);
if (ret)
dev_err(dev, "fail to register notifer\n");
fail:
dev_err(dev, "extcon fail to register notifer\n");
return ret;
}
static struct charger_device *primary_charger;
static int mtk_usb_extcon_set_vbus_v1(bool is_on) {
if (!primary_charger) {
primary_charger = get_charger_by_name("primary_chg");
if (!primary_charger) {
pr_info("%s: get primary charger device failed\n", __func__);
return -ENODEV;
}
}
if (is_on) {
charger_dev_enable_otg(primary_charger, true);
charger_dev_set_boost_current_limit(primary_charger,1500000);
} else {
charger_dev_enable_otg(primary_charger, false);
}
return 0;
}
static int mtk_usb_extcon_set_vbus(struct mtk_extcon_info *extcon,
bool is_on)
{
/* pri modify by liuyong, config otg 20231016 start */
//struct regulator *vbus = extcon->vbus;
struct device *dev = extcon->dev;
//int ret;
dev_info(dev, "vbus turn %s\n", is_on ? "on" : "off");
#if 1
mtk_usb_extcon_set_vbus_v1(is_on);
#else
/* vbus is optional */
if (!vbus || extcon->vbus_on == is_on)
return 0;
dev_info(dev, "vbus turn %s\n", is_on ? "on" : "off");
if (is_on) {
if (extcon->vbus_vol) {
ret = regulator_set_voltage(vbus,
extcon->vbus_vol, extcon->vbus_vol);
if (ret) {
dev_err(dev, "vbus regulator set voltage failed\n");
return ret;
}
}
if (extcon->vbus_cur) {
ret = regulator_set_current_limit(vbus,
extcon->vbus_cur, extcon->vbus_cur);
if (ret) {
dev_err(dev, "vbus regulator set current failed\n");
return ret;
}
}
ret = regulator_enable(vbus);
if (ret) {
dev_err(dev, "vbus regulator enable failed\n");
return ret;
}
} else {
regulator_disable(vbus);
}
#endif
/* pri modify by liuyong, config otg 20231016 end */
extcon->vbus_on = is_on;
return 0;
}
static int mtk_usb_extcon_vbus_init(struct mtk_extcon_info *extcon)
{
int ret = 0;
struct device *dev = extcon->dev;
if (!of_property_read_bool(dev->of_node, "vbus-supply")) {
ret = -EINVAL;
goto fail;
}
extcon->vbus = devm_regulator_get_exclusive(dev, "vbus");
if (IS_ERR(extcon->vbus)) {
/* try to get by name */
extcon->vbus = devm_regulator_get_exclusive(dev, "usb-otg-vbus");
if (IS_ERR(extcon->vbus)) {
dev_err(dev, "failed to get vbus\n");
ret = PTR_ERR(extcon->vbus);
extcon->vbus = NULL;
goto fail;
}
}
/* sync vbus state */
extcon->vbus_on = regulator_is_enabled(extcon->vbus);
dev_info(dev, "vbus is %s\n", extcon->vbus_on ? "on" : "off");
if (!of_property_read_u32(dev->of_node, "vbus-voltage",
&extcon->vbus_vol))
dev_info(dev, "vbus-voltage=%d", extcon->vbus_vol);
if (!of_property_read_u32(dev->of_node, "vbus-current",
&extcon->vbus_cur))
dev_info(dev, "vbus-current=%d", extcon->vbus_cur);
fail:
dev_err(dev, "mtk_usb_extcon_vbus_init failed\n");
return ret;
}
#if IS_ENABLED(CONFIG_TCPC_CLASS)
static int mtk_extcon_tcpc_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct tcp_notify *noti = data;
struct mtk_extcon_info *extcon =
container_of(nb, struct mtk_extcon_info, tcpc_nb);
struct device *dev = extcon->dev;
bool vbus_on;
switch (event) {
case TCP_NOTIFY_SOURCE_VBUS:
dev_info(dev, "source vbus = %dmv\n",
noti->vbus_state.mv);
vbus_on = (noti->vbus_state.mv) ? true : false;
mtk_usb_extcon_set_vbus(extcon, vbus_on);
break;
case TCP_NOTIFY_TYPEC_STATE:
dev_info(dev, "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) {
dev_info(dev, "Type-C SRC plug in\n");
mtk_usb_extcon_set_role(extcon, USB_ROLE_HOST);
} else if (!(extcon->bypss_typec_sink) &&
noti->typec_state.old_state == TYPEC_UNATTACHED &&
(noti->typec_state.new_state == TYPEC_ATTACHED_SNK ||
noti->typec_state.new_state == TYPEC_ATTACHED_NORP_SRC ||
noti->typec_state.new_state == TYPEC_ATTACHED_CUSTOM_SRC ||
noti->typec_state.new_state == TYPEC_ATTACHED_DBGACC_SNK)) {
dev_info(dev, "Type-C SINK plug in\n");
mtk_usb_extcon_set_role(extcon, USB_ROLE_DEVICE);
} 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.old_state == TYPEC_ATTACHED_CUSTOM_SRC ||
noti->typec_state.old_state == TYPEC_ATTACHED_DBGACC_SNK) &&
noti->typec_state.new_state == TYPEC_UNATTACHED) {
dev_info(dev, "Type-C plug out\n");
mtk_usb_extcon_set_role(extcon, USB_ROLE_NONE);
}
break;
case TCP_NOTIFY_DR_SWAP:
dev_info(dev, "%s dr_swap, new role=%d\n",
__func__, noti->swap_state.new_role);
if (noti->swap_state.new_role == PD_ROLE_UFP &&
extcon->c_role != USB_ROLE_DEVICE) {
dev_info(dev, "switch role to device\n");
mtk_usb_extcon_set_role(extcon, USB_ROLE_NONE);
mtk_usb_extcon_set_role(extcon, USB_ROLE_DEVICE);
} else if (noti->swap_state.new_role == PD_ROLE_DFP &&
extcon->c_role != USB_ROLE_HOST) {
dev_info(dev, "switch role to host\n");
mtk_usb_extcon_set_role(extcon, USB_ROLE_NONE);
mtk_usb_extcon_set_role(extcon, USB_ROLE_HOST);
}
break;
}
return NOTIFY_OK;
}
static int mtk_usb_extcon_tcpc_init(struct mtk_extcon_info *extcon)
{
struct tcpc_device *tcpc_dev;
struct device_node *np = extcon->dev->of_node;
const char *tcpc_name;
int ret;
ret = of_property_read_string(np, "tcpc", &tcpc_name);
if (ret < 0)
return -ENODEV;
tcpc_dev = tcpc_dev_get_by_name(tcpc_name);
if (!tcpc_dev) {
dev_err(extcon->dev, "get tcpc device fail\n");
return -ENODEV;
}
extcon->tcpc_nb.notifier_call = mtk_extcon_tcpc_notifier;
ret = register_tcp_dev_notifier(tcpc_dev, &extcon->tcpc_nb,
TCP_NOTIFY_TYPE_USB | TCP_NOTIFY_TYPE_VBUS |
TCP_NOTIFY_TYPE_MISC);
if (ret < 0) {
dev_err(extcon->dev, "register notifer fail\n");
return -EINVAL;
}
extcon->tcpc_dev = tcpc_dev;
return 0;
}
#endif
static void mtk_usb_extcon_detect_cable(struct work_struct *work)
{
struct mtk_extcon_info *extcon = container_of(to_delayed_work(work),
struct mtk_extcon_info, wq_detcable);
int id;
/* check ID and update cable state */
id = extcon->id_gpiod ?
gpiod_get_value_cansleep(extcon->id_gpiod) : 1;
/* at first we clean states which are no longer active */
if (id) {
mtk_usb_extcon_set_vbus(extcon, false);
mtk_usb_extcon_set_role(extcon, USB_ROLE_NONE);
} else {
mtk_usb_extcon_set_vbus(extcon, true);
mtk_usb_extcon_set_role(extcon, USB_ROLE_HOST);
}
}
static irqreturn_t mtk_usb_idpin_handle(int irq, void *dev_id)
{
struct mtk_extcon_info *extcon = dev_id;
/* issue detection work */
queue_delayed_work(system_power_efficient_wq, &extcon->wq_detcable, 0);
return IRQ_HANDLED;
}
static int mtk_usb_extcon_id_pin_init(struct mtk_extcon_info *extcon)
{
int ret = 0;
int id;
extcon->id_gpiod = devm_gpiod_get(extcon->dev, "id", GPIOD_IN);
if (!extcon->id_gpiod || IS_ERR(extcon->id_gpiod)) {
dev_info(extcon->dev, "failed to get id gpio\n");
return -ENODEV;
}
extcon->id_irq = gpiod_to_irq(extcon->id_gpiod);
if (extcon->id_irq < 0) {
dev_info(extcon->dev, "failed to get ID IRQ\n");
return extcon->id_irq;
}
INIT_DELAYED_WORK(&extcon->wq_detcable, mtk_usb_extcon_detect_cable);
ret = devm_request_threaded_irq(extcon->dev, extcon->id_irq, NULL,
mtk_usb_idpin_handle, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(extcon->dev), extcon);
if (ret < 0) {
dev_info(extcon->dev, "failed to request handler for ID IRQ\n");
return ret;
}
/* get id pin value when boot on */
id = extcon->id_gpiod ?
gpiod_get_value_cansleep(extcon->id_gpiod) : 1;
dev_info(extcon->dev, "id value : %d\n", id);
if (!id) {
mtk_usb_extcon_set_vbus(extcon, true);
mtk_usb_extcon_set_role(extcon, USB_ROLE_HOST);
}
return 0;
}
#if IS_ENABLED(CONFIG_TCPC_CLASS)
#define PROC_FILE_SMT "mtk_typec"
#define FILE_SMT_U2_CC_MODE "smt_u2_cc_mode"
static int usb_cc_smt_procfs_show(struct seq_file *s, void *unused)
{
struct mtk_extcon_info *extcon = s->private;
struct device_node *np = extcon->dev->of_node;
const char *tcpc_name;
uint8_t cc1, cc2;
int ret;
ret = of_property_read_string(np, "tcpc", &tcpc_name);
if (ret < 0)
return -ENODEV;
extcon->tcpc_dev = tcpc_dev_get_by_name(tcpc_name);
if (!extcon->tcpc_dev)
return -ENODEV;
tcpm_inquire_remote_cc(extcon->tcpc_dev, &cc1, &cc2, false);
dev_info(extcon->dev, "cc1=%d, cc2=%d\n", cc1, cc2);
if (cc1 == TYPEC_CC_VOLT_OPEN || cc1 == TYPEC_CC_DRP_TOGGLING)
seq_puts(s, "0\n");
else if (cc2 == TYPEC_CC_VOLT_OPEN || cc2 == TYPEC_CC_DRP_TOGGLING)
seq_puts(s, "0\n");
else
seq_puts(s, "1\n");
return 0;
}
static int usb_cc_smt_procfs_open(struct inode *inode, struct file *file)
{
return single_open(file, usb_cc_smt_procfs_show, PDE_DATA(inode));
}
static const struct proc_ops usb_cc_smt_procfs_fops = {
.proc_open = usb_cc_smt_procfs_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int mtk_usb_extcon_procfs_init(struct mtk_extcon_info *extcon)
{
struct proc_dir_entry *file, *root;
int ret = 0;
root = proc_mkdir(PROC_FILE_SMT, NULL);
if (!root) {
dev_info(extcon->dev, "fail creating proc dir: %s\n",
PROC_FILE_SMT);
ret = -ENOMEM;
goto error;
}
file = proc_create_data(FILE_SMT_U2_CC_MODE, 0400, root,
&usb_cc_smt_procfs_fops, extcon);
if (!file) {
dev_info(extcon->dev, "fail creating proc file: %s\n",
FILE_SMT_U2_CC_MODE);
ret = -ENOMEM;
goto error;
}
dev_info(extcon->dev, "success creating proc file: %s\n",
FILE_SMT_U2_CC_MODE);
error:
dev_info(extcon->dev, "%s ret:%d\n", __func__, ret);
return ret;
}
#endif
static int mtk_usb_extcon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_extcon_info *extcon;
#if IS_ENABLED(CONFIG_TCPC_CLASS)
const char *tcpc_name;
#endif
int ret;
extcon = devm_kzalloc(&pdev->dev, sizeof(*extcon), GFP_KERNEL);
if (!extcon)
return -ENOMEM;
extcon->dev = dev;
/* extcon */
extcon->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
if (IS_ERR(extcon->edev)) {
dev_err(dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
ret = devm_extcon_dev_register(dev, extcon->edev);
if (ret < 0) {
dev_info(dev, "failed to register extcon device\n");
return ret;
}
/* usb role switch */
extcon->role_sw = usb_role_switch_get(extcon->dev);
if (IS_ERR(extcon->role_sw)) {
dev_err(dev, "failed to get usb role\n");
return PTR_ERR(extcon->role_sw);
}
/* initial usb role */
if (extcon->role_sw)
extcon->c_role = USB_ROLE_NONE;
/* vbus */
ret = mtk_usb_extcon_vbus_init(extcon);
if (ret < 0)
dev_err(dev, "failed to init vbus\n");
extcon->bypss_typec_sink =
of_property_read_bool(dev->of_node,
"mediatek,bypss-typec-sink");
#if IS_ENABLED(CONFIG_TCPC_CLASS)
ret = of_property_read_string(dev->of_node, "tcpc", &tcpc_name);
if (of_property_read_bool(dev->of_node, "mediatek,u2") && ret == 0
&& strcmp(tcpc_name, "type_c_port0") == 0) {
u32 prop_value;
if (!of_property_read_u32(dev->of_node, "mediatek,u2", &prop_value) && !prop_value)
dev_info(dev, "mediatek,u2 is false explicitly\n");
else
mtk_usb_extcon_procfs_init(extcon);
}
#endif
extcon->extcon_wq = create_singlethread_workqueue("extcon_usb");
if (!extcon->extcon_wq)
return -ENOMEM;
/* get id resources */
ret = mtk_usb_extcon_id_pin_init(extcon);
if (ret < 0)
dev_info(dev, "failed to init id pin\n");
/* power psy */
ret = mtk_usb_extcon_psy_init(extcon);
if (ret < 0)
dev_err(dev, "failed to init psy\n");
#if IS_ENABLED(CONFIG_TCPC_CLASS)
/* tcpc */
ret = mtk_usb_extcon_tcpc_init(extcon);
if (ret < 0)
dev_err(dev, "failed to init tcpc\n");
#endif
platform_set_drvdata(pdev, extcon);
/* prize liuyong, add for fac test, 20231024, start */
g_extcon = extcon;
/* prize liuyong, add for fac test, 20231024, end */
return 0;
}
static int mtk_usb_extcon_remove(struct platform_device *pdev)
{
return 0;
}
static void mtk_usb_extcon_shutdown(struct platform_device *pdev)
{
struct mtk_extcon_info *extcon = platform_get_drvdata(pdev);
dev_info(extcon->dev, "shutdown\n");
mtk_usb_extcon_set_vbus(extcon, false);
}
static const struct of_device_id mtk_usb_extcon_of_match[] = {
{ .compatible = "mediatek,extcon-usb", },
{ },
};
MODULE_DEVICE_TABLE(of, mtk_usb_extcon_of_match);
static struct platform_driver mtk_usb_extcon_driver = {
.probe = mtk_usb_extcon_probe,
.remove = mtk_usb_extcon_remove,
.shutdown = mtk_usb_extcon_shutdown,
.driver = {
.name = "mtk-extcon-usb",
.of_match_table = mtk_usb_extcon_of_match,
},
};
/* prize liuyong, add for fac test, 20231024, start */
static void delay_work_work(struct work_struct *work)
{
static count = 0;
if (!primary_charger) {
primary_charger = get_charger_by_name("primary_chg");
if (!primary_charger) {
pr_info("%s: get primary charger device failed\n", __func__);
if (count >= 10){
platform_driver_register(&mtk_usb_extcon_driver);
} else {
schedule_delayed_work(&delay_work_t, msecs_to_jiffies(500));
count++;
}
}
}
if (primary_charger != NULL) {
pr_info("%s: get primary charger device success, %d\n", __func__, count);
platform_driver_register(&mtk_usb_extcon_driver);
}
}
static int __init mtk_usb_extcon_init(void)
{
printk("mtk_usb_extcon_init\n");
INIT_DELAYED_WORK(&delay_work_t, delay_work_work);
schedule_delayed_work(&delay_work_t, msecs_to_jiffies(1000));
return 0;
//return platform_driver_register(&mtk_usb_extcon_driver);
}
late_initcall_sync(mtk_usb_extcon_init);
/* prize liuyong, add for fac test, 20231024, end */
static void __exit mtk_usb_extcon_exit(void)
{
platform_driver_unregister(&mtk_usb_extcon_driver);
}
module_exit(mtk_usb_extcon_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek Extcon USB Driver");