2 * STiH407 family DWC3 specific Glue layer
5 * Patrice Chotard <patrice.chotard@st.com>
7 * SPDX-License-Identifier: GPL-2.0+
18 #include <reset-uclass.h>
22 #include <linux/usb/dwc3.h>
23 #include <linux/usb/otg.h>
24 #include <dwc3-sti-glue.h>
26 DECLARE_GLOBAL_DATA_PTR
;
29 * struct sti_dwc3_glue_platdata - dwc3 STi glue driver private structure
30 * @syscfg_base: addr for the glue syscfg
31 * @glue_base: addr for the glue registers
32 * @syscfg_offset: usb syscfg control offset
33 * @powerdown_ctl: rest controller for powerdown signal
34 * @softreset_ctl: reset controller for softreset signal
35 * @mode: drd static host/device config
37 struct sti_dwc3_glue_platdata
{
38 phys_addr_t syscfg_base
;
39 phys_addr_t glue_base
;
40 phys_addr_t syscfg_offset
;
41 struct reset_ctl powerdown_ctl
;
42 struct reset_ctl softreset_ctl
;
43 enum usb_dr_mode mode
;
46 static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata
*plat
)
50 val
= readl(plat
->syscfg_base
+ plat
->syscfg_offset
);
52 val
&= USB3_CONTROL_MASK
;
55 case USB_DR_MODE_PERIPHERAL
:
56 val
&= ~(USB3_DELAY_VBUSVALID
57 | USB3_SEL_FORCE_OPMODE
| USB3_FORCE_OPMODE(0x3)
58 | USB3_SEL_FORCE_DPPULLDOWN2
| USB3_FORCE_DPPULLDOWN2
59 | USB3_SEL_FORCE_DMPULLDOWN2
| USB3_FORCE_DMPULLDOWN2
);
61 val
|= USB3_DEVICE_NOT_HOST
| USB3_FORCE_VBUSVALID
;
64 case USB_DR_MODE_HOST
:
65 val
&= ~(USB3_DEVICE_NOT_HOST
| USB3_FORCE_VBUSVALID
66 | USB3_SEL_FORCE_OPMODE
| USB3_FORCE_OPMODE(0x3)
67 | USB3_SEL_FORCE_DPPULLDOWN2
| USB3_FORCE_DPPULLDOWN2
68 | USB3_SEL_FORCE_DMPULLDOWN2
| USB3_FORCE_DMPULLDOWN2
);
70 val
|= USB3_DELAY_VBUSVALID
;
74 pr_err("Unsupported mode of operation %d\n", plat
->mode
);
77 writel(val
, plat
->syscfg_base
+ plat
->syscfg_offset
);
82 static void sti_dwc3_glue_init(struct sti_dwc3_glue_platdata
*plat
)
86 reg
= readl(plat
->glue_base
+ CLKRST_CTRL
);
88 reg
|= AUX_CLK_EN
| EXT_CFG_RESET_N
| XHCI_REVISION
;
89 reg
&= ~SW_PIPEW_RESET_N
;
91 writel(reg
, plat
->glue_base
+ CLKRST_CTRL
);
93 /* configure mux for vbus, powerpresent and bvalid signals */
94 reg
= readl(plat
->glue_base
+ USB2_VBUS_MNGMNT_SEL1
);
96 reg
|= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG
) |
97 SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG
) |
98 SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG
);
100 writel(reg
, plat
->glue_base
+ USB2_VBUS_MNGMNT_SEL1
);
102 setbits_le32(plat
->glue_base
+ CLKRST_CTRL
, SW_PIPEW_RESET_N
);
105 static int sti_dwc3_glue_ofdata_to_platdata(struct udevice
*dev
)
107 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
108 struct udevice
*syscon
;
109 struct regmap
*regmap
;
113 ret
= fdtdec_get_int_array(gd
->fdt_blob
, dev_of_offset(dev
),
114 "reg", reg
, ARRAY_SIZE(reg
));
116 pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret
);
120 plat
->glue_base
= reg
[0];
121 plat
->syscfg_offset
= reg
[2];
123 /* get corresponding syscon phandle */
124 ret
= uclass_get_device_by_phandle(UCLASS_SYSCON
, dev
, "st,syscfg",
127 pr_err("unable to find syscon device (%d)\n", ret
);
131 /* get syscfg-reg base address */
132 regmap
= syscon_get_regmap(syscon
);
134 pr_err("unable to find regmap\n");
137 plat
->syscfg_base
= regmap
->base
;
139 /* get powerdown reset */
140 ret
= reset_get_by_name(dev
, "powerdown", &plat
->powerdown_ctl
);
142 pr_err("can't get powerdown reset for %s (%d)", dev
->name
, ret
);
146 /* get softreset reset */
147 ret
= reset_get_by_name(dev
, "softreset", &plat
->softreset_ctl
);
149 pr_err("can't get soft reset for %s (%d)", dev
->name
, ret
);
154 static int sti_dwc3_glue_bind(struct udevice
*dev
)
156 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
159 /* check if one subnode is present */
160 dwc3_node
= fdt_first_subnode(gd
->fdt_blob
, dev_of_offset(dev
));
161 if (dwc3_node
<= 0) {
162 pr_err("Can't find subnode for %s\n", dev
->name
);
166 /* check if the subnode compatible string is the dwc3 one*/
167 if (fdt_node_check_compatible(gd
->fdt_blob
, dwc3_node
,
169 pr_err("Can't find dwc3 subnode for %s\n", dev
->name
);
173 /* retrieve the DWC3 dual role mode */
174 plat
->mode
= usb_get_dr_mode(dwc3_node
);
175 if (plat
->mode
== USB_DR_MODE_UNKNOWN
)
176 /* by default set dual role mode to HOST */
177 plat
->mode
= USB_DR_MODE_HOST
;
179 return dm_scan_fdt_dev(dev
);
182 static int sti_dwc3_glue_probe(struct udevice
*dev
)
184 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
187 /* deassert both powerdown and softreset */
188 ret
= reset_deassert(&plat
->powerdown_ctl
);
190 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
194 ret
= reset_deassert(&plat
->softreset_ctl
);
196 pr_err("DWC3 soft reset deassert failed: %d", ret
);
200 ret
= sti_dwc3_glue_drd_init(plat
);
204 sti_dwc3_glue_init(plat
);
209 ret
= reset_assert(&plat
->softreset_ctl
);
211 pr_err("DWC3 soft reset deassert failed: %d", ret
);
216 ret
= reset_assert(&plat
->powerdown_ctl
);
218 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
223 static int sti_dwc3_glue_remove(struct udevice
*dev
)
225 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
228 /* assert both powerdown and softreset */
229 ret
= reset_assert(&plat
->powerdown_ctl
);
231 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
235 ret
= reset_assert(&plat
->softreset_ctl
);
237 pr_err("DWC3 soft reset deassert failed: %d", ret
);
242 static const struct udevice_id sti_dwc3_glue_ids
[] = {
243 { .compatible
= "st,stih407-dwc3" },
247 U_BOOT_DRIVER(dwc3_sti_glue
) = {
248 .name
= "dwc3_sti_glue",
250 .of_match
= sti_dwc3_glue_ids
,
251 .ofdata_to_platdata
= sti_dwc3_glue_ofdata_to_platdata
,
252 .probe
= sti_dwc3_glue_probe
,
253 .remove
= sti_dwc3_glue_remove
,
254 .bind
= sti_dwc3_glue_bind
,
255 .platdata_auto_alloc_size
= sizeof(struct sti_dwc3_glue_platdata
),
256 .flags
= DM_FLAG_ALLOC_PRIV_DMA
,