1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * STiH407 family DWC3 specific Glue layer
4  *
5  * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
6  * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics.
7  */
8 
9 #include <common.h>
10 #include <log.h>
11 #include <asm/io.h>
12 #include <dm.h>
13 #include <errno.h>
14 #include <dm/lists.h>
15 #include <regmap.h>
16 #include <reset-uclass.h>
17 #include <syscon.h>
18 #include <usb.h>
19 
20 #include <linux/usb/dwc3.h>
21 #include <linux/usb/otg.h>
22 #include <dwc3-sti-glue.h>
23 
24 DECLARE_GLOBAL_DATA_PTR;
25 
26 /*
27  * struct sti_dwc3_glue_platdata - dwc3 STi glue driver private structure
28  * @syscfg_base:	addr for the glue syscfg
29  * @glue_base:		addr for the glue registers
30  * @syscfg_offset:	usb syscfg control offset
31  * @powerdown_ctl:	rest controller for powerdown signal
32  * @softreset_ctl:	reset controller for softreset signal
33  * @mode:		drd static host/device config
34  */
35 struct sti_dwc3_glue_platdata {
36 	phys_addr_t syscfg_base;
37 	phys_addr_t glue_base;
38 	phys_addr_t syscfg_offset;
39 	struct reset_ctl powerdown_ctl;
40 	struct reset_ctl softreset_ctl;
41 	enum usb_dr_mode mode;
42 };
43 
sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata * plat)44 static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata *plat)
45 {
46 	unsigned long val;
47 
48 	val = readl(plat->syscfg_base + plat->syscfg_offset);
49 
50 	val &= USB3_CONTROL_MASK;
51 
52 	switch (plat->mode) {
53 	case USB_DR_MODE_PERIPHERAL:
54 		val &= ~(USB3_DELAY_VBUSVALID
55 			| USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
56 			| USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
57 			| USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
58 
59 		val |= USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID;
60 		break;
61 
62 	case USB_DR_MODE_HOST:
63 		val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID
64 			| USB3_SEL_FORCE_OPMODE	| USB3_FORCE_OPMODE(0x3)
65 			| USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
66 			| USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
67 
68 		val |= USB3_DELAY_VBUSVALID;
69 		break;
70 
71 	default:
72 		pr_err("Unsupported mode of operation %d\n", plat->mode);
73 		return -EINVAL;
74 	}
75 	writel(val, plat->syscfg_base + plat->syscfg_offset);
76 
77 	return 0;
78 }
79 
sti_dwc3_glue_init(struct sti_dwc3_glue_platdata * plat)80 static void sti_dwc3_glue_init(struct sti_dwc3_glue_platdata *plat)
81 {
82 	unsigned long reg;
83 
84 	reg = readl(plat->glue_base + CLKRST_CTRL);
85 
86 	reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION;
87 	reg &= ~SW_PIPEW_RESET_N;
88 
89 	writel(reg, plat->glue_base + CLKRST_CTRL);
90 
91 	/* configure mux for vbus, powerpresent and bvalid signals */
92 	reg = readl(plat->glue_base + USB2_VBUS_MNGMNT_SEL1);
93 
94 	reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) |
95 	       SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) |
96 	       SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG);
97 
98 	writel(reg, plat->glue_base + USB2_VBUS_MNGMNT_SEL1);
99 
100 	setbits_le32(plat->glue_base + CLKRST_CTRL, SW_PIPEW_RESET_N);
101 }
102 
sti_dwc3_glue_ofdata_to_platdata(struct udevice * dev)103 static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev)
104 {
105 	struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
106 	struct udevice *syscon;
107 	struct regmap *regmap;
108 	int ret;
109 	u32 reg[4];
110 
111 	ret = ofnode_read_u32_array(dev->node, "reg", reg, ARRAY_SIZE(reg));
112 	if (ret) {
113 		pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret);
114 		return ret;
115 	}
116 
117 	plat->glue_base = reg[0];
118 	plat->syscfg_offset = reg[2];
119 
120 	/* get corresponding syscon phandle */
121 	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "st,syscfg",
122 					   &syscon);
123 	if (ret) {
124 		pr_err("unable to find syscon device (%d)\n", ret);
125 		return ret;
126 	}
127 
128 	/* get syscfg-reg base address */
129 	regmap = syscon_get_regmap(syscon);
130 	if (!regmap) {
131 		pr_err("unable to find regmap\n");
132 		return -ENODEV;
133 	}
134 	plat->syscfg_base = regmap->ranges[0].start;
135 
136 	/* get powerdown reset */
137 	ret = reset_get_by_name(dev, "powerdown", &plat->powerdown_ctl);
138 	if (ret) {
139 		pr_err("can't get powerdown reset for %s (%d)", dev->name, ret);
140 		return ret;
141 	}
142 
143 	/* get softreset reset */
144 	ret = reset_get_by_name(dev, "softreset", &plat->softreset_ctl);
145 	if (ret)
146 		pr_err("can't get soft reset for %s (%d)", dev->name, ret);
147 
148 	return ret;
149 };
150 
sti_dwc3_glue_bind(struct udevice * dev)151 static int sti_dwc3_glue_bind(struct udevice *dev)
152 {
153 	struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
154 	ofnode node, dwc3_node;
155 
156 	/* Find snps,dwc3 node from subnode */
157 	ofnode_for_each_subnode(node, dev->node) {
158 		if (ofnode_device_is_compatible(node, "snps,dwc3"))
159 			dwc3_node = node;
160 	}
161 
162 	if (!ofnode_valid(node)) {
163 		pr_err("Can't find dwc3 subnode for %s\n", dev->name);
164 		return -ENODEV;
165 	}
166 
167 	/* retrieve the DWC3 dual role mode */
168 	plat->mode = usb_get_dr_mode(dwc3_node);
169 	if (plat->mode == USB_DR_MODE_UNKNOWN)
170 		/* by default set dual role mode to HOST */
171 		plat->mode = USB_DR_MODE_HOST;
172 
173 	return dm_scan_fdt_dev(dev);
174 }
175 
sti_dwc3_glue_probe(struct udevice * dev)176 static int sti_dwc3_glue_probe(struct udevice *dev)
177 {
178 	struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
179 	int ret;
180 
181 	/* deassert both powerdown and softreset */
182 	ret = reset_deassert(&plat->powerdown_ctl);
183 	if (ret < 0) {
184 		pr_err("DWC3 powerdown reset deassert failed: %d", ret);
185 		return ret;
186 	}
187 
188 	ret = reset_deassert(&plat->softreset_ctl);
189 	if (ret < 0) {
190 		pr_err("DWC3 soft reset deassert failed: %d", ret);
191 		goto softreset_err;
192 	}
193 
194 	ret = sti_dwc3_glue_drd_init(plat);
195 	if (ret)
196 		goto init_err;
197 
198 	sti_dwc3_glue_init(plat);
199 
200 	return 0;
201 
202 init_err:
203 	ret = reset_assert(&plat->softreset_ctl);
204 	if (ret < 0) {
205 		pr_err("DWC3 soft reset deassert failed: %d", ret);
206 		return ret;
207 	}
208 
209 softreset_err:
210 	ret = reset_assert(&plat->powerdown_ctl);
211 	if (ret < 0)
212 		pr_err("DWC3 powerdown reset deassert failed: %d", ret);
213 
214 	return ret;
215 }
216 
sti_dwc3_glue_remove(struct udevice * dev)217 static int sti_dwc3_glue_remove(struct udevice *dev)
218 {
219 	struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
220 	int ret;
221 
222 	/* assert both powerdown and softreset */
223 	ret = reset_assert(&plat->powerdown_ctl);
224 	if (ret < 0) {
225 		pr_err("DWC3 powerdown reset deassert failed: %d", ret);
226 		return ret;
227 	}
228 
229 	ret = reset_assert(&plat->softreset_ctl);
230 	if (ret < 0)
231 		pr_err("DWC3 soft reset deassert failed: %d", ret);
232 
233 	return ret;
234 }
235 
236 static const struct udevice_id sti_dwc3_glue_ids[] = {
237 	{ .compatible = "st,stih407-dwc3" },
238 	{ }
239 };
240 
241 U_BOOT_DRIVER(dwc3_sti_glue) = {
242 	.name = "dwc3_sti_glue",
243 	.id = UCLASS_NOP,
244 	.of_match = sti_dwc3_glue_ids,
245 	.ofdata_to_platdata = sti_dwc3_glue_ofdata_to_platdata,
246 	.probe = sti_dwc3_glue_probe,
247 	.remove = sti_dwc3_glue_remove,
248 	.bind = sti_dwc3_glue_bind,
249 	.platdata_auto_alloc_size = sizeof(struct sti_dwc3_glue_platdata),
250 	.flags = DM_FLAG_ALLOC_PRIV_DMA,
251 };
252