Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
  4 */
  5
  6#include <linux/clk.h>
  7#include <linux/delay.h>
  8#include <linux/err.h>
  9#include <linux/io.h>
 10#include <linux/kernel.h>
 11#include <linux/module.h>
 12#include <linux/of.h>
 13#include <linux/of_device.h>
 14#include <linux/phy/phy.h>
 15#include <linux/platform_device.h>
 16#include <linux/regmap.h>
 17#include <linux/regulator/consumer.h>
 18#include <linux/reset.h>
 19#include <linux/slab.h>
 20
 21#define USB2_PHY_USB_PHY_UTMI_CTRL0		(0x3c)
 22#define SLEEPM					BIT(0)
 23#define OPMODE_MASK				GENMASK(4, 3)
 24#define OPMODE_NORMAL				(0x00)
 25#define OPMODE_NONDRIVING			BIT(3)
 26#define TERMSEL					BIT(5)
 27
 28#define USB2_PHY_USB_PHY_UTMI_CTRL1		(0x40)
 29#define XCVRSEL					BIT(0)
 30
 31#define USB2_PHY_USB_PHY_UTMI_CTRL5		(0x50)
 32#define POR					BIT(1)
 33
 34#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0	(0x54)
 35#define RETENABLEN				BIT(3)
 36#define FSEL_MASK				GENMASK(7, 5)
 37#define FSEL_DEFAULT				(0x3 << 4)
 38
 39#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1	(0x58)
 40#define VBUSVLDEXTSEL0				BIT(4)
 41#define PLLBTUNE				BIT(5)
 42
 43#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2	(0x5c)
 44#define VREGBYPASS				BIT(0)
 45
 46#define USB2_PHY_USB_PHY_HS_PHY_CTRL1		(0x60)
 47#define VBUSVLDEXT0				BIT(0)
 48
 49#define USB2_PHY_USB_PHY_HS_PHY_CTRL2		(0x64)
 50#define USB2_AUTO_RESUME			BIT(0)
 51#define USB2_SUSPEND_N				BIT(2)
 52#define USB2_SUSPEND_N_SEL			BIT(3)
 53
 54#define USB2_PHY_USB_PHY_CFG0			(0x94)
 55#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN	BIT(0)
 56#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN		BIT(1)
 57
 58#define USB2_PHY_USB_PHY_REFCLK_CTRL		(0xa0)
 59#define REFCLK_SEL_MASK				GENMASK(1, 0)
 60#define REFCLK_SEL_DEFAULT			(0x2 << 0)
 61
 62static const char * const qcom_snps_hsphy_vreg_names[] = {
 63	"vdda-pll", "vdda33", "vdda18",
 64};
 65
 66#define SNPS_HS_NUM_VREGS		ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
 67
 68/**
 69 * struct qcom_snps_hsphy - snps hs phy attributes
 70 *
 71 * @phy: generic phy
 72 * @base: iomapped memory space for snps hs phy
 73 *
 74 * @cfg_ahb_clk: AHB2PHY interface clock
 75 * @ref_clk: phy reference clock
 76 * @iface_clk: phy interface clock
 77 * @phy_reset: phy reset control
 78 * @vregs: regulator supplies bulk data
 79 * @phy_initialized: if PHY has been initialized correctly
 80 * @mode: contains the current mode the PHY is in
 81 */
 82struct qcom_snps_hsphy {
 83	struct phy *phy;
 84	void __iomem *base;
 85
 86	struct clk *cfg_ahb_clk;
 87	struct clk *ref_clk;
 88	struct reset_control *phy_reset;
 89	struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
 90
 91	bool phy_initialized;
 92	enum phy_mode mode;
 93};
 94
 95static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
 96						u32 mask, u32 val)
 97{
 98	u32 reg;
 99
100	reg = readl_relaxed(base + offset);
101	reg &= ~mask;
102	reg |= val & mask;
103	writel_relaxed(reg, base + offset);
104
105	/* Ensure above write is completed */
106	readl_relaxed(base + offset);
107}
108
109static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy)
110{
111	dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n");
112
113	if (hsphy->mode == PHY_MODE_USB_HOST) {
114		/* Enable auto-resume to meet remote wakeup timing */
115		qcom_snps_hsphy_write_mask(hsphy->base,
116					   USB2_PHY_USB_PHY_HS_PHY_CTRL2,
117					   USB2_AUTO_RESUME,
118					   USB2_AUTO_RESUME);
119		usleep_range(500, 1000);
120		qcom_snps_hsphy_write_mask(hsphy->base,
121					   USB2_PHY_USB_PHY_HS_PHY_CTRL2,
122					   0, USB2_AUTO_RESUME);
123	}
124
125	clk_disable_unprepare(hsphy->cfg_ahb_clk);
126	return 0;
127}
128
129static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy)
130{
131	int ret;
132
133	dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n");
134
135	ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
136	if (ret) {
137		dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n");
138		return ret;
139	}
140
141	return 0;
142}
143
144static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev)
145{
146	struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
147
148	if (!hsphy->phy_initialized)
149		return 0;
150
151	qcom_snps_hsphy_suspend(hsphy);
152	return 0;
153}
154
155static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev)
156{
157	struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
158
159	if (!hsphy->phy_initialized)
160		return 0;
161
162	qcom_snps_hsphy_resume(hsphy);
163	return 0;
164}
165
166static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
167				    int submode)
168{
169	struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
170
171	hsphy->mode = mode;
172	return 0;
173}
174
175static int qcom_snps_hsphy_init(struct phy *phy)
176{
177	struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
178	int ret;
179
180	dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
181
182	ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
183	if (ret)
184		return ret;
185
186	ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
187	if (ret) {
188		dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
189		goto poweroff_phy;
190	}
191
192	ret = reset_control_assert(hsphy->phy_reset);
193	if (ret) {
194		dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
195		goto disable_ahb_clk;
196	}
197
198	usleep_range(100, 150);
199
200	ret = reset_control_deassert(hsphy->phy_reset);
201	if (ret) {
202		dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
203		goto disable_ahb_clk;
204	}
205
206	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
207					UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
208					UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
209	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
210							POR, POR);
211	qcom_snps_hsphy_write_mask(hsphy->base,
212					USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
213					FSEL_MASK, 0);
214	qcom_snps_hsphy_write_mask(hsphy->base,
215					USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
216					PLLBTUNE, PLLBTUNE);
217	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
218					REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
219	qcom_snps_hsphy_write_mask(hsphy->base,
220					USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
221					VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
222	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
223					VBUSVLDEXT0, VBUSVLDEXT0);
224
225	qcom_snps_hsphy_write_mask(hsphy->base,
226					USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
227					VREGBYPASS, VREGBYPASS);
228
229	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
230					USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
231					USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
232
233	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
234					SLEEPM, SLEEPM);
235
236	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
237					POR, 0);
238
239	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
240					USB2_SUSPEND_N_SEL, 0);
241
242	qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
243					UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
244
245	hsphy->phy_initialized = true;
246
247	return 0;
248
249disable_ahb_clk:
250	clk_disable_unprepare(hsphy->cfg_ahb_clk);
251poweroff_phy:
252	regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
253
254	return ret;
255}
256
257static int qcom_snps_hsphy_exit(struct phy *phy)
258{
259	struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
260
261	reset_control_assert(hsphy->phy_reset);
262	clk_disable_unprepare(hsphy->cfg_ahb_clk);
263	regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
264	hsphy->phy_initialized = false;
265
266	return 0;
267}
268
269static const struct phy_ops qcom_snps_hsphy_gen_ops = {
270	.init		= qcom_snps_hsphy_init,
271	.exit		= qcom_snps_hsphy_exit,
272	.set_mode	= qcom_snps_hsphy_set_mode,
273	.owner		= THIS_MODULE,
274};
275
276static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
277	{ .compatible	= "qcom,sm8150-usb-hs-phy", },
278	{ .compatible	= "qcom,usb-snps-hs-7nm-phy", },
279	{ .compatible	= "qcom,usb-snps-femto-v2-phy",	},
280	{ }
281};
282MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);
283
284static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {
285	SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend,
286			   qcom_snps_hsphy_runtime_resume, NULL)
287};
288
289static int qcom_snps_hsphy_probe(struct platform_device *pdev)
290{
291	struct device *dev = &pdev->dev;
292	struct qcom_snps_hsphy *hsphy;
293	struct phy_provider *phy_provider;
294	struct phy *generic_phy;
295	int ret, i;
296	int num;
297
298	hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);
299	if (!hsphy)
300		return -ENOMEM;
301
302	hsphy->base = devm_platform_ioremap_resource(pdev, 0);
303	if (IS_ERR(hsphy->base))
304		return PTR_ERR(hsphy->base);
305
306	hsphy->ref_clk = devm_clk_get(dev, "ref");
307	if (IS_ERR(hsphy->ref_clk)) {
308		ret = PTR_ERR(hsphy->ref_clk);
309		if (ret != -EPROBE_DEFER)
310			dev_err(dev, "failed to get ref clk, %d\n", ret);
311		return ret;
312	}
313
314	hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
315	if (IS_ERR(hsphy->phy_reset)) {
316		dev_err(dev, "failed to get phy core reset\n");
317		return PTR_ERR(hsphy->phy_reset);
318	}
319
320	num = ARRAY_SIZE(hsphy->vregs);
321	for (i = 0; i < num; i++)
322		hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];
323
324	ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);
325	if (ret) {
326		if (ret != -EPROBE_DEFER)
327			dev_err(dev, "failed to get regulator supplies: %d\n",
328				ret);
329		return ret;
330	}
331
332	pm_runtime_set_active(dev);
333	pm_runtime_enable(dev);
334	/*
335	 * Prevent runtime pm from being ON by default. Users can enable
336	 * it using power/control in sysfs.
337	 */
338	pm_runtime_forbid(dev);
339
340	generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
341	if (IS_ERR(generic_phy)) {
342		ret = PTR_ERR(generic_phy);
343		dev_err(dev, "failed to create phy, %d\n", ret);
344		return ret;
345	}
346	hsphy->phy = generic_phy;
347
348	dev_set_drvdata(dev, hsphy);
349	phy_set_drvdata(generic_phy, hsphy);
350
351	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
352	if (!IS_ERR(phy_provider))
353		dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
354	else
355		pm_runtime_disable(dev);
356
357	return PTR_ERR_OR_ZERO(phy_provider);
358}
359
360static struct platform_driver qcom_snps_hsphy_driver = {
361	.probe		= qcom_snps_hsphy_probe,
362	.driver = {
363		.name	= "qcom-snps-hs-femto-v2-phy",
364		.pm = &qcom_snps_hsphy_pm_ops,
365		.of_match_table = qcom_snps_hsphy_of_match_table,
366	},
367};
368
369module_platform_driver(qcom_snps_hsphy_driver);
370
371MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");
372MODULE_LICENSE("GPL v2");