Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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");