phy-lpc18xx-usb-otg.c (3432B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * PHY driver for NXP LPC18xx/43xx internal USB OTG PHY 4 * 5 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> 6 */ 7 8#include <linux/clk.h> 9#include <linux/err.h> 10#include <linux/mfd/syscon.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/phy/phy.h> 14#include <linux/platform_device.h> 15#include <linux/regmap.h> 16 17/* USB OTG PHY register offset and bit in CREG */ 18#define LPC18XX_CREG_CREG0 0x004 19#define LPC18XX_CREG_CREG0_USB0PHY BIT(5) 20 21struct lpc18xx_usb_otg_phy { 22 struct phy *phy; 23 struct clk *clk; 24 struct regmap *reg; 25}; 26 27static int lpc18xx_usb_otg_phy_init(struct phy *phy) 28{ 29 struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); 30 int ret; 31 32 /* The PHY must be clocked at 480 MHz */ 33 ret = clk_set_rate(lpc->clk, 480000000); 34 if (ret) 35 return ret; 36 37 return clk_prepare(lpc->clk); 38} 39 40static int lpc18xx_usb_otg_phy_exit(struct phy *phy) 41{ 42 struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); 43 44 clk_unprepare(lpc->clk); 45 46 return 0; 47} 48 49static int lpc18xx_usb_otg_phy_power_on(struct phy *phy) 50{ 51 struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); 52 int ret; 53 54 ret = clk_enable(lpc->clk); 55 if (ret) 56 return ret; 57 58 /* The bit in CREG is cleared to enable the PHY */ 59 ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0, 60 LPC18XX_CREG_CREG0_USB0PHY, 0); 61 if (ret) { 62 clk_disable(lpc->clk); 63 return ret; 64 } 65 66 return 0; 67} 68 69static int lpc18xx_usb_otg_phy_power_off(struct phy *phy) 70{ 71 struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy); 72 int ret; 73 74 ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0, 75 LPC18XX_CREG_CREG0_USB0PHY, 76 LPC18XX_CREG_CREG0_USB0PHY); 77 if (ret) 78 return ret; 79 80 clk_disable(lpc->clk); 81 82 return 0; 83} 84 85static const struct phy_ops lpc18xx_usb_otg_phy_ops = { 86 .init = lpc18xx_usb_otg_phy_init, 87 .exit = lpc18xx_usb_otg_phy_exit, 88 .power_on = lpc18xx_usb_otg_phy_power_on, 89 .power_off = lpc18xx_usb_otg_phy_power_off, 90 .owner = THIS_MODULE, 91}; 92 93static int lpc18xx_usb_otg_phy_probe(struct platform_device *pdev) 94{ 95 struct phy_provider *phy_provider; 96 struct lpc18xx_usb_otg_phy *lpc; 97 98 lpc = devm_kzalloc(&pdev->dev, sizeof(*lpc), GFP_KERNEL); 99 if (!lpc) 100 return -ENOMEM; 101 102 lpc->reg = syscon_node_to_regmap(pdev->dev.of_node->parent); 103 if (IS_ERR(lpc->reg)) { 104 dev_err(&pdev->dev, "failed to get syscon\n"); 105 return PTR_ERR(lpc->reg); 106 } 107 108 lpc->clk = devm_clk_get(&pdev->dev, NULL); 109 if (IS_ERR(lpc->clk)) { 110 dev_err(&pdev->dev, "failed to get clock\n"); 111 return PTR_ERR(lpc->clk); 112 } 113 114 lpc->phy = devm_phy_create(&pdev->dev, NULL, &lpc18xx_usb_otg_phy_ops); 115 if (IS_ERR(lpc->phy)) { 116 dev_err(&pdev->dev, "failed to create PHY\n"); 117 return PTR_ERR(lpc->phy); 118 } 119 120 phy_set_drvdata(lpc->phy, lpc); 121 122 phy_provider = devm_of_phy_provider_register(&pdev->dev, 123 of_phy_simple_xlate); 124 125 return PTR_ERR_OR_ZERO(phy_provider); 126} 127 128static const struct of_device_id lpc18xx_usb_otg_phy_match[] = { 129 { .compatible = "nxp,lpc1850-usb-otg-phy" }, 130 { } 131}; 132MODULE_DEVICE_TABLE(of, lpc18xx_usb_otg_phy_match); 133 134static struct platform_driver lpc18xx_usb_otg_phy_driver = { 135 .probe = lpc18xx_usb_otg_phy_probe, 136 .driver = { 137 .name = "lpc18xx-usb-otg-phy", 138 .of_match_table = lpc18xx_usb_otg_phy_match, 139 }, 140}; 141module_platform_driver(lpc18xx_usb_otg_phy_driver); 142 143MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); 144MODULE_DESCRIPTION("NXP LPC18xx/43xx USB OTG PHY driver"); 145MODULE_LICENSE("GPL v2");