Linux Audio

Check our new training course

Loading...
v6.8
  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");
v4.6
 
  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");