1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller
4  *
5  * Copyright (C) 2024 StarFive Technology Co., Ltd.
6  *
7  * Author:	Minda Chen <minda.chen@starfivetech.com>
8  */
9 
10 #include <asm/io.h>
11 #include <clk.h>
12 #include <dm.h>
13 #include <dm/device_compat.h>
14 #include <linux/bitops.h>
15 #include <linux/usb/otg.h>
16 #include <malloc.h>
17 #include <reset.h>
18 #include <regmap.h>
19 #include <syscon.h>
20 
21 #include "core.h"
22 
23 #define USB_STRAP_HOST			BIT(17)
24 #define USB_STRAP_DEVICE		BIT(18)
25 #define USB_STRAP_MASK			GENMASK(18, 16)
26 
27 #define USB_SUSPENDM_HOST		BIT(19)
28 #define USB_SUSPENDM_MASK		BIT(19)
29 
30 #define USB_MISC_CFG_MASK		GENMASK(23, 20)
31 #define USB_SUSPENDM_BYPS		BIT(20)
32 #define USB_PLL_EN			BIT(22)
33 #define USB_REFCLK_MODE			BIT(23)
34 
35 struct cdns_starfive {
36 	struct udevice *dev;
37 	struct regmap *stg_syscon;
38 	struct reset_ctl_bulk resets;
39 	struct clk_bulk clks;
40 	u32 stg_usb_mode;
41 	enum usb_dr_mode mode;
42 };
43 
cdns_mode_init(struct cdns_starfive * data,enum usb_dr_mode mode)44 static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode)
45 {
46 	unsigned int strap, suspendm;
47 
48 	regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
49 			   USB_MISC_CFG_MASK,
50 			   USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE);
51 
52 	switch (mode) {
53 	case USB_DR_MODE_HOST:
54 		strap = USB_STRAP_HOST;
55 		suspendm = USB_SUSPENDM_HOST;
56 		break;
57 	case USB_DR_MODE_PERIPHERAL:
58 		strap = USB_STRAP_DEVICE;
59 		suspendm = 0;
60 		break;
61 	default:
62 		return;
63 	}
64 
65 	regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
66 			   USB_SUSPENDM_MASK | USB_STRAP_MASK,
67 			   strap | suspendm);
68 }
69 
cdns_clk_rst_deinit(struct cdns_starfive * data)70 static void cdns_clk_rst_deinit(struct cdns_starfive *data)
71 {
72 	reset_assert_bulk(&data->resets);
73 	clk_disable_bulk(&data->clks);
74 }
75 
cdns_clk_rst_init(struct cdns_starfive * data)76 static int cdns_clk_rst_init(struct cdns_starfive *data)
77 {
78 	int ret;
79 
80 	ret = clk_get_bulk(data->dev, &data->clks);
81 	if (ret)
82 		return ret;
83 
84 	ret = reset_get_bulk(data->dev, &data->resets);
85 	if (ret)
86 		goto err_clk;
87 
88 	ret = clk_enable_bulk(&data->clks);
89 	if (ret) {
90 		dev_err(data->dev, "clk enable failed: %d\n", ret);
91 		goto err_en_clk;
92 	}
93 
94 	ret = reset_deassert_bulk(&data->resets);
95 	if (ret) {
96 		dev_err(data->dev, "reset deassert failed: %d\n", ret);
97 		goto err_reset;
98 	}
99 
100 	return 0;
101 
102 err_reset:
103 	clk_disable_bulk(&data->clks);
104 err_en_clk:
105 	reset_release_bulk(&data->resets);
106 err_clk:
107 	clk_release_bulk(&data->clks);
108 
109 	return ret;
110 }
111 
cdns_starfive_get_syscon(struct cdns_starfive * data)112 static int cdns_starfive_get_syscon(struct cdns_starfive *data)
113 {
114 	struct ofnode_phandle_args phandle;
115 	int ret;
116 
117 	ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0,
118 					 &phandle);
119 	if (ret < 0) {
120 		dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret);
121 		return ret;
122 	}
123 
124 	data->stg_syscon = syscon_node_to_regmap(phandle.node);
125 	if (IS_ERR(data->stg_syscon)) {
126 		dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon));
127 		return PTR_ERR(data->stg_syscon);
128 	}
129 
130 	data->stg_usb_mode = phandle.args[0];
131 
132 	return 0;
133 }
134 
cdns_starfive_probe(struct udevice * dev)135 static int cdns_starfive_probe(struct udevice *dev)
136 {
137 	struct cdns_starfive *data = dev_get_plat(dev);
138 	enum usb_dr_mode dr_mode;
139 	int ret;
140 
141 	data->dev = dev;
142 
143 	ret = cdns_starfive_get_syscon(data);
144 	if (ret)
145 		return ret;
146 
147 	dr_mode = usb_get_dr_mode(dev_ofnode(dev));
148 
149 	data->mode = dr_mode;
150 	ret = cdns_clk_rst_init(data);
151 	if (ret) {
152 		dev_err(data->dev, "clk reset failed: %d\n", ret);
153 		return ret;
154 	}
155 	cdns_mode_init(data, dr_mode);
156 
157 	return 0;
158 }
159 
cdns_starfive_remove(struct udevice * dev)160 static int cdns_starfive_remove(struct udevice *dev)
161 {
162 	struct cdns_starfive *data = dev_get_plat(dev);
163 
164 	cdns_clk_rst_deinit(data);
165 	return 0;
166 }
167 
168 static const struct udevice_id cdns_starfive_of_match[] = {
169 	{ .compatible = "starfive,jh7110-usb", },
170 	{},
171 };
172 
173 U_BOOT_DRIVER(cdns_starfive) = {
174 	.name = "cdns-starfive",
175 	.id = UCLASS_NOP,
176 	.of_match = cdns_starfive_of_match,
177 	.bind = cdns3_bind,
178 	.probe = cdns_starfive_probe,
179 	.remove = cdns_starfive_remove,
180 	.plat_auto	= sizeof(struct cdns_starfive),
181 	.flags = DM_FLAG_OS_PREPARE,
182 };
183