File Index
Files and directories
Sign in
FILE

phy-rockchip-usb.c

File preview with syntax highlighting.

linux-6.19.14/drivers/phy/rockchip/phy-rockchip-usb.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Rockchip usb PHY driver
  4 *
  5 * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
  6 * Copyright (C) 2014 ROCKCHIP, Inc.
  7 */
  8
  9#include <linux/clk.h>
 10#include <linux/clk-provider.h>
 11#include <linux/hw_bitfield.h>
 12#include <linux/io.h>
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/mutex.h>
 16#include <linux/of.h>
 17#include <linux/phy/phy.h>
 18#include <linux/platform_device.h>
 19#include <linux/property.h>
 20#include <linux/regulator/consumer.h>
 21#include <linux/reset.h>
 22#include <linux/regmap.h>
 23#include <linux/mfd/syscon.h>
 24#include <linux/delay.h>
 25
 26static int enable_usb_uart;
 27
 28#define UOC_CON0					0x00
 29#define UOC_CON0_SIDDQ					BIT(13)
 30#define UOC_CON0_DISABLE				BIT(4)
 31#define UOC_CON0_COMMON_ON_N				BIT(0)
 32
 33#define UOC_CON2					0x08
 34#define UOC_CON2_SOFT_CON_SEL				BIT(2)
 35
 36#define UOC_CON3					0x0c
 37/* bits present on rk3188 and rk3288 phys */
 38#define UOC_CON3_UTMI_TERMSEL_FULLSPEED			BIT(5)
 39#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC		1UL
 40#define UOC_CON3_UTMI_XCVRSEELCT_MASK			GENMASK(4, 3)
 41#define UOC_CON3_UTMI_OPMODE_NODRIVING			1UL
 42#define UOC_CON3_UTMI_OPMODE_MASK			GENMASK(2, 1)
 43#define UOC_CON3_UTMI_SUSPENDN				BIT(0)
 44
 45struct rockchip_usb_phys {
 46	int reg;
 47	const char *pll_name;
 48};
 49
 50struct rockchip_usb_phy_base;
 51struct rockchip_usb_phy_pdata {
 52	struct rockchip_usb_phys *phys;
 53	int (*init_usb_uart)(struct regmap *grf,
 54			     const struct rockchip_usb_phy_pdata *pdata);
 55	int usb_uart_phy;
 56};
 57
 58struct rockchip_usb_phy_base {
 59	struct device *dev;
 60	struct regmap *reg_base;
 61	const struct rockchip_usb_phy_pdata *pdata;
 62};
 63
 64struct rockchip_usb_phy {
 65	struct rockchip_usb_phy_base *base;
 66	struct device_node *np;
 67	unsigned int	reg_offset;
 68	struct clk	*clk;
 69	struct clk      *clk480m;
 70	struct clk_hw	clk480m_hw;
 71	struct phy	*phy;
 72	bool		uart_enabled;
 73	struct reset_control *reset;
 74	struct regulator *vbus;
 75};
 76
 77static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
 78					   bool siddq)
 79{
 80	u32 val = FIELD_PREP_WM16(UOC_CON0_SIDDQ, siddq);
 81
 82	return regmap_write(phy->base->reg_base, phy->reg_offset, val);
 83}
 84
 85static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
 86						unsigned long parent_rate)
 87{
 88	return 480000000;
 89}
 90
 91static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
 92{
 93	struct rockchip_usb_phy *phy = container_of(hw,
 94						    struct rockchip_usb_phy,
 95						    clk480m_hw);
 96
 97	if (phy->vbus)
 98		regulator_disable(phy->vbus);
 99
100	/* Power down usb phy analog blocks by set siddq 1 */
101	rockchip_usb_phy_power(phy, 1);
102}
103
104static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
105{
106	struct rockchip_usb_phy *phy = container_of(hw,
107						    struct rockchip_usb_phy,
108						    clk480m_hw);
109
110	/* Power up usb phy analog blocks by set siddq 0 */
111	return rockchip_usb_phy_power(phy, 0);
112}
113
114static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
115{
116	struct rockchip_usb_phy *phy = container_of(hw,
117						    struct rockchip_usb_phy,
118						    clk480m_hw);
119	int ret;
120	u32 val;
121
122	ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
123	if (ret < 0)
124		return ret;
125
126	return (val & UOC_CON0_SIDDQ) ? 0 : 1;
127}
128
129static const struct clk_ops rockchip_usb_phy480m_ops = {
130	.enable = rockchip_usb_phy480m_enable,
131	.disable = rockchip_usb_phy480m_disable,
132	.is_enabled = rockchip_usb_phy480m_is_enabled,
133	.recalc_rate = rockchip_usb_phy480m_recalc_rate,
134};
135
136static int rockchip_usb_phy_power_off(struct phy *_phy)
137{
138	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
139
140	if (phy->uart_enabled)
141		return -EBUSY;
142
143	clk_disable_unprepare(phy->clk480m);
144
145	return 0;
146}
147
148static int rockchip_usb_phy_power_on(struct phy *_phy)
149{
150	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
151
152	if (phy->uart_enabled)
153		return -EBUSY;
154
155	if (phy->vbus) {
156		int ret;
157
158		ret = regulator_enable(phy->vbus);
159		if (ret)
160			return ret;
161	}
162
163	return clk_prepare_enable(phy->clk480m);
164}
165
166static int rockchip_usb_phy_reset(struct phy *_phy)
167{
168	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
169
170	if (phy->reset) {
171		reset_control_assert(phy->reset);
172		udelay(10);
173		reset_control_deassert(phy->reset);
174	}
175
176	return 0;
177}
178
179static const struct phy_ops ops = {
180	.power_on	= rockchip_usb_phy_power_on,
181	.power_off	= rockchip_usb_phy_power_off,
182	.reset		= rockchip_usb_phy_reset,
183	.owner		= THIS_MODULE,
184};
185
186static void rockchip_usb_phy_action(void *data)
187{
188	struct rockchip_usb_phy *rk_phy = data;
189
190	if (!rk_phy->uart_enabled) {
191		of_clk_del_provider(rk_phy->np);
192		clk_unregister(rk_phy->clk480m);
193	}
194
195	if (rk_phy->clk)
196		clk_put(rk_phy->clk);
197}
198
199static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
200				 struct device_node *child)
201{
202	struct rockchip_usb_phy *rk_phy;
203	unsigned int reg_offset;
204	const char *clk_name;
205	struct clk_init_data init;
206	int err, i;
207
208	rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
209	if (!rk_phy)
210		return -ENOMEM;
211
212	rk_phy->base = base;
213	rk_phy->np = child;
214
215	if (of_property_read_u32(child, "reg", &reg_offset)) {
216		dev_err(base->dev, "missing reg property in node %pOFn\n",
217			child);
218		return -EINVAL;
219	}
220
221	rk_phy->reset = of_reset_control_get(child, "phy-reset");
222	if (IS_ERR(rk_phy->reset))
223		rk_phy->reset = NULL;
224
225	rk_phy->reg_offset = reg_offset;
226
227	rk_phy->clk = of_clk_get_by_name(child, "phyclk");
228	if (IS_ERR(rk_phy->clk))
229		rk_phy->clk = NULL;
230
231	i = 0;
232	init.name = NULL;
233	while (base->pdata->phys[i].reg) {
234		if (base->pdata->phys[i].reg == reg_offset) {
235			init.name = base->pdata->phys[i].pll_name;
236			break;
237		}
238		i++;
239	}
240
241	if (!init.name) {
242		dev_err(base->dev, "phy data not found\n");
243		return -EINVAL;
244	}
245
246	if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
247		dev_dbg(base->dev, "phy%d used as uart output\n", i);
248		rk_phy->uart_enabled = true;
249	} else {
250		if (rk_phy->clk) {
251			clk_name = __clk_get_name(rk_phy->clk);
252			init.flags = 0;
253			init.parent_names = &clk_name;
254			init.num_parents = 1;
255		} else {
256			init.flags = 0;
257			init.parent_names = NULL;
258			init.num_parents = 0;
259		}
260
261		init.ops = &rockchip_usb_phy480m_ops;
262		rk_phy->clk480m_hw.init = &init;
263
264		rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
265		if (IS_ERR(rk_phy->clk480m)) {
266			err = PTR_ERR(rk_phy->clk480m);
267			goto err_clk;
268		}
269
270		err = of_clk_add_provider(child, of_clk_src_simple_get,
271					rk_phy->clk480m);
272		if (err < 0)
273			goto err_clk_prov;
274	}
275
276	err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
277				       rk_phy);
278	if (err)
279		return err;
280
281	rk_phy->phy = devm_phy_create(base->dev, child, &ops);
282	if (IS_ERR(rk_phy->phy)) {
283		dev_err(base->dev, "failed to create PHY\n");
284		return PTR_ERR(rk_phy->phy);
285	}
286	phy_set_drvdata(rk_phy->phy, rk_phy);
287
288	rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
289	if (IS_ERR(rk_phy->vbus)) {
290		if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
291			return PTR_ERR(rk_phy->vbus);
292		rk_phy->vbus = NULL;
293	}
294
295	/*
296	 * When acting as uart-pipe, just keep clock on otherwise
297	 * only power up usb phy when it use, so disable it when init
298	 */
299	if (rk_phy->uart_enabled)
300		return clk_prepare_enable(rk_phy->clk);
301	else
302		return rockchip_usb_phy_power(rk_phy, 1);
303
304err_clk_prov:
305	if (!rk_phy->uart_enabled)
306		clk_unregister(rk_phy->clk480m);
307err_clk:
308	if (rk_phy->clk)
309		clk_put(rk_phy->clk);
310	return err;
311}
312
313static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
314	.phys = (struct rockchip_usb_phys[]){
315		{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
316		{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
317		{ /* sentinel */ }
318	},
319};
320
321static int __init rockchip_init_usb_uart_common(struct regmap *grf,
322				const struct rockchip_usb_phy_pdata *pdata)
323{
324	int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
325	int ret;
326	u32 val;
327
328	/*
329	 * COMMON_ON and DISABLE settings are described in the TRM,
330	 * but were not present in the original code.
331	 * Also disable the analog phy components to save power.
332	 */
333	val = FIELD_PREP_WM16(UOC_CON0_COMMON_ON_N, 1) |
334	      FIELD_PREP_WM16(UOC_CON0_DISABLE, 1) |
335	      FIELD_PREP_WM16(UOC_CON0_SIDDQ, 1);
336	ret = regmap_write(grf, regoffs + UOC_CON0, val);
337	if (ret)
338		return ret;
339
340	val = FIELD_PREP_WM16(UOC_CON2_SOFT_CON_SEL, 1);
341	ret = regmap_write(grf, regoffs + UOC_CON2, val);
342	if (ret)
343		return ret;
344
345	val = FIELD_PREP_WM16(UOC_CON3_UTMI_SUSPENDN, 0) |
346	      FIELD_PREP_WM16(UOC_CON3_UTMI_OPMODE_MASK,
347			   UOC_CON3_UTMI_OPMODE_NODRIVING) |
348	      FIELD_PREP_WM16(UOC_CON3_UTMI_XCVRSEELCT_MASK,
349			   UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC) |
350	      FIELD_PREP_WM16(UOC_CON3_UTMI_TERMSEL_FULLSPEED, 1);
351	ret = regmap_write(grf, UOC_CON3, val);
352	if (ret)
353		return ret;
354
355	return 0;
356}
357
358#define RK3188_UOC0_CON0				0x10c
359#define RK3188_UOC0_CON0_BYPASSSEL			BIT(9)
360#define RK3188_UOC0_CON0_BYPASSDMEN			BIT(8)
361
362/*
363 * Enable the bypass of uart2 data through the otg usb phy.
364 * See description of rk3288-variant for details.
365 */
366static int __init rk3188_init_usb_uart(struct regmap *grf,
367				const struct rockchip_usb_phy_pdata *pdata)
368{
369	u32 val;
370	int ret;
371
372	ret = rockchip_init_usb_uart_common(grf, pdata);
373	if (ret)
374		return ret;
375
376	val = FIELD_PREP_WM16(RK3188_UOC0_CON0_BYPASSSEL, 1) |
377	      FIELD_PREP_WM16(RK3188_UOC0_CON0_BYPASSDMEN, 1);
378	ret = regmap_write(grf, RK3188_UOC0_CON0, val);
379	if (ret)
380		return ret;
381
382	return 0;
383}
384
385static const struct rockchip_usb_phy_pdata rk3188_pdata = {
386	.phys = (struct rockchip_usb_phys[]){
387		{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
388		{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
389		{ /* sentinel */ }
390	},
391	.init_usb_uart = rk3188_init_usb_uart,
392	.usb_uart_phy = 0,
393};
394
395#define RK3288_UOC0_CON3				0x32c
396#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
397#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
398
399/*
400 * Enable the bypass of uart2 data through the otg usb phy.
401 * Original description in the TRM.
402 * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
403 * 2. Disable the pull-up resistance on the D+ line by setting
404 *    OPMODE0[1:0] to 2’b01.
405 * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
406 *    mode, set COMMONONN to 1’b1.
407 * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
408 * 5. Set BYPASSSEL0 to 1’b1.
409 * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
410 * To receive data, monitor FSVPLUS0.
411 *
412 * The actual code in the vendor kernel does some things differently.
413 */
414static int __init rk3288_init_usb_uart(struct regmap *grf,
415				const struct rockchip_usb_phy_pdata *pdata)
416{
417	u32 val;
418	int ret;
419
420	ret = rockchip_init_usb_uart_common(grf, pdata);
421	if (ret)
422		return ret;
423
424	val = FIELD_PREP_WM16(RK3288_UOC0_CON3_BYPASSSEL, 1) |
425	      FIELD_PREP_WM16(RK3288_UOC0_CON3_BYPASSDMEN, 1);
426	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
427	if (ret)
428		return ret;
429
430	return 0;
431}
432
433static const struct rockchip_usb_phy_pdata rk3288_pdata = {
434	.phys = (struct rockchip_usb_phys[]){
435		{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
436		{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
437		{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
438		{ /* sentinel */ }
439	},
440	.init_usb_uart = rk3288_init_usb_uart,
441	.usb_uart_phy = 0,
442};
443
444static int rockchip_usb_phy_probe(struct platform_device *pdev)
445{
446	struct device *dev = &pdev->dev;
447	struct rockchip_usb_phy_base *phy_base;
448	struct phy_provider *phy_provider;
449	struct device_node *child;
450	int err;
451
452	phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
453	if (!phy_base)
454		return -ENOMEM;
455
456	phy_base->pdata = device_get_match_data(dev);
457	if (!phy_base->pdata) {
458		dev_err(dev, "missing phy data\n");
459		return -EINVAL;
460	}
461
462	phy_base->dev = dev;
463	phy_base->reg_base = ERR_PTR(-ENODEV);
464	if (dev->parent && dev->parent->of_node)
465		phy_base->reg_base = syscon_node_to_regmap(
466						dev->parent->of_node);
467	if (IS_ERR(phy_base->reg_base))
468		phy_base->reg_base = syscon_regmap_lookup_by_phandle(
469						dev->of_node, "rockchip,grf");
470	if (IS_ERR(phy_base->reg_base)) {
471		dev_err(&pdev->dev, "Missing rockchip,grf property\n");
472		return PTR_ERR(phy_base->reg_base);
473	}
474
475	for_each_available_child_of_node(dev->of_node, child) {
476		err = rockchip_usb_phy_init(phy_base, child);
477		if (err) {
478			of_node_put(child);
479			return err;
480		}
481	}
482
483	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
484	return PTR_ERR_OR_ZERO(phy_provider);
485}
486
487static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
488	{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
489	{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
490	{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
491	{}
492};
493
494MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
495
496static struct platform_driver rockchip_usb_driver = {
497	.probe		= rockchip_usb_phy_probe,
498	.driver		= {
499		.name	= "rockchip-usb-phy",
500		.of_match_table = rockchip_usb_phy_dt_ids,
501	},
502};
503
504module_platform_driver(rockchip_usb_driver);
505
506#ifndef MODULE
507static int __init rockchip_init_usb_uart(void)
508{
509	const struct of_device_id *match;
510	const struct rockchip_usb_phy_pdata *data;
511	struct device_node *np;
512	struct regmap *grf;
513	int ret;
514
515	if (!enable_usb_uart)
516		return 0;
517
518	np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
519					     &match);
520	if (!np) {
521		pr_err("%s: failed to find usbphy node\n", __func__);
522		return -ENOTSUPP;
523	}
524
525	pr_debug("%s: using settings for %s\n", __func__, match->compatible);
526	data = match->data;
527
528	if (!data->init_usb_uart) {
529		pr_err("%s: usb-uart not available on %s\n",
530		       __func__, match->compatible);
531		return -ENOTSUPP;
532	}
533
534	grf = ERR_PTR(-ENODEV);
535	if (np->parent)
536		grf = syscon_node_to_regmap(np->parent);
537	if (IS_ERR(grf))
538		grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
539	if (IS_ERR(grf)) {
540		pr_err("%s: Missing rockchip,grf property, %lu\n",
541		       __func__, PTR_ERR(grf));
542		return PTR_ERR(grf);
543	}
544
545	ret = data->init_usb_uart(grf, data);
546	if (ret) {
547		pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
548		enable_usb_uart = 0;
549		return ret;
550	}
551
552	return 0;
553}
554early_initcall(rockchip_init_usb_uart);
555
556static int __init rockchip_usb_uart(char *buf)
557{
558	enable_usb_uart = true;
559	return 0;
560}
561early_param("rockchip.usb_uart", rockchip_usb_uart);
562#endif
563
564MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
565MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
566MODULE_LICENSE("GPL v2");
~/ Read-only file preview.