Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.2.
  1/*
  2 * Samsung SoC USB 1.1/2.0 PHY driver
  3 *
  4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  5 * Author: Kamil Debski <k.debski@samsung.com>
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of the GNU General Public License version 2 as
  9 * published by the Free Software Foundation.
 10 */
 11
 12#include <linux/clk.h>
 13#include <linux/mfd/syscon.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 16#include <linux/of_address.h>
 17#include <linux/phy/phy.h>
 18#include <linux/platform_device.h>
 19#include <linux/spinlock.h>
 20#include "phy-samsung-usb2.h"
 21
 22static int samsung_usb2_phy_power_on(struct phy *phy)
 23{
 24	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
 25	struct samsung_usb2_phy_driver *drv = inst->drv;
 26	int ret;
 27
 28	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
 29		inst->cfg->label);
 30	ret = clk_prepare_enable(drv->clk);
 31	if (ret)
 32		goto err_main_clk;
 33	ret = clk_prepare_enable(drv->ref_clk);
 34	if (ret)
 35		goto err_instance_clk;
 36	if (inst->cfg->power_on) {
 37		spin_lock(&drv->lock);
 38		ret = inst->cfg->power_on(inst);
 39		spin_unlock(&drv->lock);
 40	}
 41
 42	return 0;
 43
 44err_instance_clk:
 45	clk_disable_unprepare(drv->clk);
 46err_main_clk:
 47	return ret;
 48}
 49
 50static int samsung_usb2_phy_power_off(struct phy *phy)
 51{
 52	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
 53	struct samsung_usb2_phy_driver *drv = inst->drv;
 54	int ret = 0;
 55
 56	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
 57		inst->cfg->label);
 58	if (inst->cfg->power_off) {
 59		spin_lock(&drv->lock);
 60		ret = inst->cfg->power_off(inst);
 61		spin_unlock(&drv->lock);
 62	}
 63	clk_disable_unprepare(drv->ref_clk);
 64	clk_disable_unprepare(drv->clk);
 65	return ret;
 66}
 67
 68static struct phy_ops samsung_usb2_phy_ops = {
 69	.power_on	= samsung_usb2_phy_power_on,
 70	.power_off	= samsung_usb2_phy_power_off,
 71	.owner		= THIS_MODULE,
 72};
 73
 74static struct phy *samsung_usb2_phy_xlate(struct device *dev,
 75					struct of_phandle_args *args)
 76{
 77	struct samsung_usb2_phy_driver *drv;
 78
 79	drv = dev_get_drvdata(dev);
 80	if (!drv)
 81		return ERR_PTR(-EINVAL);
 82
 83	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
 84		return ERR_PTR(-ENODEV);
 85
 86	return drv->instances[args->args[0]].phy;
 87}
 88
 89static const struct of_device_id samsung_usb2_phy_of_match[] = {
 90#ifdef CONFIG_PHY_EXYNOS4210_USB2
 91	{
 92		.compatible = "samsung,exynos4210-usb2-phy",
 93		.data = &exynos4210_usb2_phy_config,
 94	},
 95#endif
 96#ifdef CONFIG_PHY_EXYNOS4X12_USB2
 97	{
 98		.compatible = "samsung,exynos4x12-usb2-phy",
 99		.data = &exynos4x12_usb2_phy_config,
100	},
101#endif
102#ifdef CONFIG_PHY_EXYNOS5250_USB2
103	{
104		.compatible = "samsung,exynos5250-usb2-phy",
105		.data = &exynos5250_usb2_phy_config,
106	},
107#endif
108	{ },
109};
110
111static int samsung_usb2_phy_probe(struct platform_device *pdev)
112{
113	const struct of_device_id *match;
114	const struct samsung_usb2_phy_config *cfg;
115	struct device *dev = &pdev->dev;
116	struct phy_provider *phy_provider;
117	struct resource *mem;
118	struct samsung_usb2_phy_driver *drv;
119	int i, ret;
120
121	if (!pdev->dev.of_node) {
122		dev_err(dev, "This driver is required to be instantiated from device tree\n");
123		return -EINVAL;
124	}
125
126	match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
127	if (!match) {
128		dev_err(dev, "of_match_node() failed\n");
129		return -EINVAL;
130	}
131	cfg = match->data;
132
133	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
134		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
135								GFP_KERNEL);
136	if (!drv)
137		return -ENOMEM;
138
139	dev_set_drvdata(dev, drv);
140	spin_lock_init(&drv->lock);
141
142	drv->cfg = cfg;
143	drv->dev = dev;
144
145	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
146	drv->reg_phy = devm_ioremap_resource(dev, mem);
147	if (IS_ERR(drv->reg_phy)) {
148		dev_err(dev, "Failed to map register memory (phy)\n");
149		return PTR_ERR(drv->reg_phy);
150	}
151
152	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
153		"samsung,pmureg-phandle");
154	if (IS_ERR(drv->reg_pmu)) {
155		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
156		return PTR_ERR(drv->reg_pmu);
157	}
158
159	if (drv->cfg->has_mode_switch) {
160		drv->reg_sys = syscon_regmap_lookup_by_phandle(
161				pdev->dev.of_node, "samsung,sysreg-phandle");
162		if (IS_ERR(drv->reg_sys)) {
163			dev_err(dev, "Failed to map system registers (via syscon)\n");
164			return PTR_ERR(drv->reg_sys);
165		}
166	}
167
168	drv->clk = devm_clk_get(dev, "phy");
169	if (IS_ERR(drv->clk)) {
170		dev_err(dev, "Failed to get clock of phy controller\n");
171		return PTR_ERR(drv->clk);
172	}
173
174	drv->ref_clk = devm_clk_get(dev, "ref");
175	if (IS_ERR(drv->ref_clk)) {
176		dev_err(dev, "Failed to get reference clock for the phy controller\n");
177		return PTR_ERR(drv->ref_clk);
178	}
179
180	drv->ref_rate = clk_get_rate(drv->ref_clk);
181	if (drv->cfg->rate_to_clk) {
182		ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
183		if (ret)
184			return ret;
185	}
186
187	for (i = 0; i < drv->cfg->num_phys; i++) {
188		char *label = drv->cfg->phys[i].label;
189		struct samsung_usb2_phy_instance *p = &drv->instances[i];
190
191		dev_dbg(dev, "Creating phy \"%s\"\n", label);
192		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
193		if (IS_ERR(p->phy)) {
194			dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
195				label);
196			return PTR_ERR(p->phy);
197		}
198
199		p->cfg = &drv->cfg->phys[i];
200		p->drv = drv;
201		phy_set_bus_width(p->phy, 8);
202		phy_set_drvdata(p->phy, p);
203	}
204
205	phy_provider = devm_of_phy_provider_register(dev,
206							samsung_usb2_phy_xlate);
207	if (IS_ERR(phy_provider)) {
208		dev_err(drv->dev, "Failed to register phy provider\n");
209		return PTR_ERR(phy_provider);
210	}
211
212	return 0;
213}
214
215static struct platform_driver samsung_usb2_phy_driver = {
216	.probe	= samsung_usb2_phy_probe,
217	.driver = {
218		.of_match_table	= samsung_usb2_phy_of_match,
219		.name		= "samsung-usb2-phy",
220		.owner		= THIS_MODULE,
221	}
222};
223
224module_platform_driver(samsung_usb2_phy_driver);
225MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
226MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
227MODULE_LICENSE("GPL v2");
228MODULE_ALIAS("platform:samsung-usb2-phy");