Linux Audio

Check our new training course

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