// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 MediaTek Inc. */ #include #include #include #include #include #include #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");