mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 07:34:12 +08:00
usb typec: mt6360: Add support for mt6360 Type-C driver
Mediatek MT6360 is a multi-functional IC that includes USB Type-C. It works with Type-C Port Controller Manager to provide USB PD and USB Type-C functionalities. Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Signed-off-by: ChiYuan Huang <cy_huang@richtek.com> Link: https://lore.kernel.org/r/1598928042-22115-1-git-send-email-u0084500@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f5f875b561
commit
e1aefcdd39
@ -27,6 +27,14 @@ config TYPEC_RT1711H
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
|
||||
config TYPEC_MT6360
|
||||
tristate "Mediatek MT6360 Type-C driver"
|
||||
depends on MFD_MT6360
|
||||
help
|
||||
Mediatek MT6360 is a multi-functional IC that includes
|
||||
USB Type-C. It works with Type-C Port Controller Manager
|
||||
to provide USB PD and USB Type-C functionalities.
|
||||
|
||||
endif # TYPEC_TCPCI
|
||||
|
||||
config TYPEC_FUSB302
|
||||
|
@ -5,3 +5,4 @@ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
|
||||
typec_wcove-y := wcove.o
|
||||
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
|
||||
obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
|
||||
obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o
|
||||
|
212
drivers/usb/typec/tcpm/tcpci_mt6360.c
Normal file
212
drivers/usb/typec/tcpm/tcpci_mt6360.c
Normal file
@ -0,0 +1,212 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 MediaTek Inc.
|
||||
*
|
||||
* Author: ChiYuan Huang <cy_huang@richtek.com>
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/usb/tcpm.h>
|
||||
|
||||
#include "tcpci.h"
|
||||
|
||||
#define MT6360_REG_VCONNCTRL1 0x8C
|
||||
#define MT6360_REG_MODECTRL2 0x8F
|
||||
#define MT6360_REG_SWRESET 0xA0
|
||||
#define MT6360_REG_DEBCTRL1 0xA1
|
||||
#define MT6360_REG_DRPCTRL1 0xA2
|
||||
#define MT6360_REG_DRPCTRL2 0xA3
|
||||
#define MT6360_REG_I2CTORST 0xBF
|
||||
#define MT6360_REG_RXCTRL2 0xCF
|
||||
#define MT6360_REG_CTDCTRL2 0xEC
|
||||
|
||||
/* MT6360_REG_VCONNCTRL1 */
|
||||
#define MT6360_VCONNCL_ENABLE BIT(0)
|
||||
/* MT6360_REG_RXCTRL2 */
|
||||
#define MT6360_OPEN40M_ENABLE BIT(7)
|
||||
/* MT6360_REG_CTDCTRL2 */
|
||||
#define MT6360_RPONESHOT_ENABLE BIT(6)
|
||||
|
||||
struct mt6360_tcpc_info {
|
||||
struct tcpci_data tdata;
|
||||
struct tcpci *tcpci;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static inline int mt6360_tcpc_read16(struct regmap *regmap,
|
||||
unsigned int reg, u16 *val)
|
||||
{
|
||||
return regmap_raw_read(regmap, reg, val, sizeof(u16));
|
||||
}
|
||||
|
||||
static inline int mt6360_tcpc_write16(struct regmap *regmap,
|
||||
unsigned int reg, u16 val)
|
||||
{
|
||||
return regmap_raw_write(regmap, reg, &val, sizeof(u16));
|
||||
}
|
||||
|
||||
static int mt6360_tcpc_init(struct tcpci *tcpci, struct tcpci_data *tdata)
|
||||
{
|
||||
struct regmap *regmap = tdata->regmap;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(regmap, MT6360_REG_SWRESET, 0x01);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* after reset command, wait 1~2ms to wait IC action */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/* write all alert to masked */
|
||||
ret = mt6360_tcpc_write16(regmap, TCPC_ALERT_MASK, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* config I2C timeout reset enable , and timeout to 200ms */
|
||||
ret = regmap_write(regmap, MT6360_REG_I2CTORST, 0x8F);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* config CC Detect Debounce : 26.7*val us */
|
||||
ret = regmap_write(regmap, MT6360_REG_DEBCTRL1, 0x10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DRP Toggle Cycle : 51.2 + 6.4*val ms */
|
||||
ret = regmap_write(regmap, MT6360_REG_DRPCTRL1, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DRP Duyt Ctrl : dcSRC: /1024 */
|
||||
ret = mt6360_tcpc_write16(regmap, MT6360_REG_DRPCTRL2, 330);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable VCONN Current Limit function */
|
||||
ret = regmap_update_bits(regmap, MT6360_REG_VCONNCTRL1, MT6360_VCONNCL_ENABLE,
|
||||
MT6360_VCONNCL_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable cc open 40ms when pmic send vsysuv signal */
|
||||
ret = regmap_update_bits(regmap, MT6360_REG_RXCTRL2, MT6360_OPEN40M_ENABLE,
|
||||
MT6360_OPEN40M_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable Rpdet oneshot detection */
|
||||
ret = regmap_update_bits(regmap, MT6360_REG_CTDCTRL2, MT6360_RPONESHOT_ENABLE,
|
||||
MT6360_RPONESHOT_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set shipping mode off, AUTOIDLE on */
|
||||
return regmap_write(regmap, MT6360_REG_MODECTRL2, 0x7A);
|
||||
}
|
||||
|
||||
static irqreturn_t mt6360_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mt6360_tcpc_info *mti = dev_id;
|
||||
|
||||
return tcpci_irq(mti->tcpci);
|
||||
}
|
||||
|
||||
static int mt6360_tcpc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6360_tcpc_info *mti;
|
||||
int ret;
|
||||
|
||||
mti = devm_kzalloc(&pdev->dev, sizeof(*mti), GFP_KERNEL);
|
||||
if (!mti)
|
||||
return -ENOMEM;
|
||||
|
||||
mti->dev = &pdev->dev;
|
||||
|
||||
mti->tdata.regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!mti->tdata.regmap) {
|
||||
dev_err(&pdev->dev, "Failed to get parent regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mti->irq = platform_get_irq_byname(pdev, "PD_IRQB");
|
||||
if (mti->irq < 0)
|
||||
return mti->irq;
|
||||
|
||||
mti->tdata.init = mt6360_tcpc_init;
|
||||
mti->tcpci = tcpci_register_port(&pdev->dev, &mti->tdata);
|
||||
if (IS_ERR(mti->tcpci)) {
|
||||
dev_err(&pdev->dev, "Failed to register tcpci port\n");
|
||||
return PTR_ERR(mti->tcpci);
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(mti->dev, mti->irq, NULL, mt6360_irq, IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), mti);
|
||||
if (ret) {
|
||||
dev_err(mti->dev, "Failed to register irq\n");
|
||||
tcpci_unregister_port(mti->tcpci);
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
platform_set_drvdata(pdev, mti);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_tcpc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev);
|
||||
|
||||
disable_irq(mti->irq);
|
||||
tcpci_unregister_port(mti->tcpci);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mt6360_tcpc_suspend(struct device *dev)
|
||||
{
|
||||
struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(mti->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mt6360_tcpc_resume(struct device *dev)
|
||||
{
|
||||
struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(mti->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mt6360_tcpc_pm_ops, mt6360_tcpc_suspend, mt6360_tcpc_resume);
|
||||
|
||||
static const struct of_device_id __maybe_unused mt6360_tcpc_of_id[] = {
|
||||
{ .compatible = "mediatek,mt6360-tcpc", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6360_tcpc_of_id);
|
||||
|
||||
static struct platform_driver mt6360_tcpc_driver = {
|
||||
.driver = {
|
||||
.name = "mt6360-tcpc",
|
||||
.pm = &mt6360_tcpc_pm_ops,
|
||||
.of_match_table = mt6360_tcpc_of_id,
|
||||
},
|
||||
.probe = mt6360_tcpc_probe,
|
||||
.remove = mt6360_tcpc_remove,
|
||||
};
|
||||
module_platform_driver(mt6360_tcpc_driver);
|
||||
|
||||
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
|
||||
MODULE_DESCRIPTION("MT6360 USB Type-C Port Controller Interface Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
Reference in New Issue
Block a user