kernel_samsung_a34x-permissive/drivers/phy/mediatek/phy-mtk.c

487 lines
9.8 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/phy/mediatek/mtk_usb_phy.h>
#include <phy-mtk.h>
#define DRIVER_NAME "mtk_phy"
u32 phy_debug_level = K_ERR | K_ALET;
module_param(phy_debug_level, int, 0644);
MODULE_PARM_DESC(phy_debug_level, "Debug Print Log for mtkphy");
int usb_mtkphy_switch_to_usb(struct phy *phy)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_switch_to_usb)
phycfg->usb_phy_switch_to_usb(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_switch_to_usb);
int usb_mtkphy_switch_to_uart(struct phy *phy)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_switch_to_uart)
phycfg->usb_phy_switch_to_uart(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_switch_to_uart);
int usb_mtkphy_check_in_uart_mode(struct phy *phy)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_check_in_uart_mode)
ret = phycfg->usb_phy_check_in_uart_mode(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_check_in_uart_mode);
int usb_mtkphy_dump_usb2uart_reg(struct phy *phy)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_dump_usb2uart_reg)
phycfg->usb_phy_dump_usb2uart_reg(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_dump_usb2uart_reg);
int usb_mtkphy_sib_enable_switch(struct phy *phy, bool enable)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_sib_enable_switch)
phycfg->usb_phy_sib_enable_switch(instance, enable);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_sib_enable_switch);
int usb_mtkphy_sib_enable_switch_status(struct phy *phy)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_sib_switch_status)
phycfg->usb_phy_sib_switch_status(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_sib_enable_switch_status);
int usb_mtkphy_u3_loop_back_test(struct phy *phy)
{
int ret;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_u3_loop_back_test)
ret = phycfg->usb_phy_u3_loop_back_test(instance);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_u3_loop_back_test);
int usb_mtkphy_slew_rate_calibration(struct phy *phy)
{
int ret = 0;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_slew_rate_calibration);
int usb_mtkphy_switch_to_bc11(struct phy *phy, bool on)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_switch_to_bc11)
phycfg->usb_phy_switch_to_bc11(instance, on);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_switch_to_bc11);
int usb_mtkphy_dpdm_pulldown(struct phy *phy, bool enable)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_dpdm_pulldown)
phycfg->usb_phy_dpdm_pulldown(instance, enable);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_dpdm_pulldown);
int usb_mtkphy_lpm_enable(struct phy *phy, bool on)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_lpm_enable)
ret = phycfg->usb_phy_lpm_enable(instance, on);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_lpm_enable);
int usb_mtkphy_host_mode(struct phy *phy, bool on)
{
int ret = 0;
if (!phy)
ret = -EINVAL;
if (phy) {
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
const struct mtk_phy_interface *phycfg;
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_host_mode)
ret = phycfg->usb_phy_host_mode(instance, on);
else
ret = -ENOTSUPP;
} else
ret = -EINVAL;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_host_mode);
int usb_mtkphy_io_read(struct phy *phy, u32 reg)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_io_read)
ret = phycfg->usb_phy_io_read(instance, reg);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_io_read);
int usb_mtkphy_io_write(struct phy *phy, u32 val, u32 reg)
{
int ret = 0;
struct mtk_phy_instance *instance;
const struct mtk_phy_interface *phycfg;
if (!phy)
return -EINVAL;
instance = phy_get_drvdata(phy);
phycfg = instance->phycfg;
if (phycfg && phycfg->usb_phy_io_write)
phycfg->usb_phy_io_write(instance, val, reg);
else
ret = -ENOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(usb_mtkphy_io_write);
static int mtk_phy_init(struct phy *phy)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
instance->phycfg->usb_phy_init(instance);
return 0;
}
static int mtk_phy_power_on(struct phy *phy)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
instance->phycfg->usb_phy_recover(instance);
return 0;
}
static int mtk_phy_power_off(struct phy *phy)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
instance->phycfg->usb_phy_savecurrent(instance);
return 0;
}
static struct phy_ops mtk_u3phy_ops = {
.init = mtk_phy_init,
.exit = NULL,
.power_on = mtk_phy_power_on,
.power_off = mtk_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy *mtk_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct mtk_phy_drv *drv;
drv = dev_get_drvdata(dev);
if (!drv)
return ERR_PTR(-EINVAL);
if (WARN_ON(args->args[0] >= drv->phycfg->num_phys))
return ERR_PTR(-ENODEV);
return drv->phys[args->args[0]]->phy;
}
static int mtk_usb_phy_probe(struct platform_device *pdev)
{
int retval, i;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const struct mtk_usbphy_config *mtkcfg;
struct device_node *np = pdev->dev.of_node;
struct mtk_phy_drv *mtkphy;
struct phy_provider *provider;
if (!pdev->dev.of_node) {
dev_info(dev, "This driver is required to be instantiated from device tree\n");
return -EINVAL;
}
match = of_match_node(mtk_phy_of_match, pdev->dev.of_node);
if (!match) {
dev_info(dev, "of_match_node() failed\n");
return -EINVAL;
}
mtkcfg = match->data;
mtkphy = devm_kzalloc(dev, sizeof(*mtkphy), GFP_KERNEL);
if (!mtkphy)
return -ENOMEM;
mtkphy->nphys = mtkcfg->num_phys;
mtkphy->phys = devm_kcalloc(dev, mtkphy->nphys,
sizeof(*mtkphy->phys), GFP_KERNEL);
if (!mtkphy->phys) {
devm_kfree(dev, mtkphy);
return -ENOMEM;
}
mtkphy->phycfg = mtkcfg;
mtkphy->dev = dev;
platform_set_drvdata(pdev, mtkphy);
/* init phy driver */
retval = mtkcfg->usb_drv_init(pdev, mtkphy);
if (retval)
return retval;
pm_runtime_enable(dev);
retval = pm_runtime_get_sync(dev);
if (retval < 0)
goto err1;
pm_runtime_forbid(dev);
for (i = 0; i < mtkphy->phycfg->num_phys; i++) {
struct mtk_phy_instance *instance = mtkphy->phys[i];
char *name = mtkphy->phycfg->phys[i].name;
struct phy *phy;
instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
if (!instance) {
retval = -ENOMEM;
goto err2;
}
instance->port_base = mtkphy->phy_base
+ mtkphy->phycfg->phys[i].reg_offset;
phy = devm_phy_create(dev, np, &mtk_u3phy_ops);
if (IS_ERR(phy)) {
dev_info(dev, "Failed to create usb2_phy \"%s\"\n",
name);
retval = PTR_ERR(phy);
goto err2;
}
instance->phy = phy;
instance->phycfg = &mtkphy->phycfg->phys[i];
instance->phy_drv = mtkphy;
instance->phycfg->usb_phy_inst_init(instance);
mtkphy->phys[i] = instance;
phy_set_drvdata(instance->phy, instance);
}
provider = devm_of_phy_provider_register(dev,
mtk_phy_xlate);
return PTR_ERR_OR_ZERO(provider);
err2:
pm_runtime_allow(dev);
err1:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
mtkcfg->usb_drv_exit(pdev, mtkphy);
return retval;
}
static int mtk_usb_phy_remove(struct platform_device *pdev)
{
/* pm_runtime_put_sync(&pdev->dev);*/
/* pm_runtime_disable(&pdev->dev);*/
return 0;
}
static struct platform_driver mtk_usb_phy_driver = {
.probe = mtk_usb_phy_probe,
.remove = mtk_usb_phy_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = mtk_phy_of_match,
},
};
static int __init usb_phy_mtk_init(void)
{
return platform_driver_register(&mtk_usb_phy_driver);
}
module_init(usb_phy_mtk_init);
static void __exit usb_phy_mtk_exit(void)
{
platform_driver_unregister(&mtk_usb_phy_driver);
}
module_exit(usb_phy_mtk_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Mediatek USB transceiver driver");