1 // SPDX-License-Identifier: GPL-2.0+
3 * STiH407 family DWC3 specific Glue layer
5 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
6 * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics.
14 #include <linux/libfdt.h>
17 #include <reset-uclass.h>
21 #include <linux/usb/dwc3.h>
22 #include <linux/usb/otg.h>
23 #include <dwc3-sti-glue.h>
25 DECLARE_GLOBAL_DATA_PTR
;
28 * struct sti_dwc3_glue_platdata - dwc3 STi glue driver private structure
29 * @syscfg_base: addr for the glue syscfg
30 * @glue_base: addr for the glue registers
31 * @syscfg_offset: usb syscfg control offset
32 * @powerdown_ctl: rest controller for powerdown signal
33 * @softreset_ctl: reset controller for softreset signal
34 * @mode: drd static host/device config
36 struct sti_dwc3_glue_platdata
{
37 phys_addr_t syscfg_base
;
38 phys_addr_t glue_base
;
39 phys_addr_t syscfg_offset
;
40 struct reset_ctl powerdown_ctl
;
41 struct reset_ctl softreset_ctl
;
42 enum usb_dr_mode mode
;
45 static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata
*plat
)
49 val
= readl(plat
->syscfg_base
+ plat
->syscfg_offset
);
51 val
&= USB3_CONTROL_MASK
;
54 case USB_DR_MODE_PERIPHERAL
:
55 val
&= ~(USB3_DELAY_VBUSVALID
56 | USB3_SEL_FORCE_OPMODE
| USB3_FORCE_OPMODE(0x3)
57 | USB3_SEL_FORCE_DPPULLDOWN2
| USB3_FORCE_DPPULLDOWN2
58 | USB3_SEL_FORCE_DMPULLDOWN2
| USB3_FORCE_DMPULLDOWN2
);
60 val
|= USB3_DEVICE_NOT_HOST
| USB3_FORCE_VBUSVALID
;
63 case USB_DR_MODE_HOST
:
64 val
&= ~(USB3_DEVICE_NOT_HOST
| USB3_FORCE_VBUSVALID
65 | USB3_SEL_FORCE_OPMODE
| USB3_FORCE_OPMODE(0x3)
66 | USB3_SEL_FORCE_DPPULLDOWN2
| USB3_FORCE_DPPULLDOWN2
67 | USB3_SEL_FORCE_DMPULLDOWN2
| USB3_FORCE_DMPULLDOWN2
);
69 val
|= USB3_DELAY_VBUSVALID
;
73 pr_err("Unsupported mode of operation %d\n", plat
->mode
);
76 writel(val
, plat
->syscfg_base
+ plat
->syscfg_offset
);
81 static void sti_dwc3_glue_init(struct sti_dwc3_glue_platdata
*plat
)
85 reg
= readl(plat
->glue_base
+ CLKRST_CTRL
);
87 reg
|= AUX_CLK_EN
| EXT_CFG_RESET_N
| XHCI_REVISION
;
88 reg
&= ~SW_PIPEW_RESET_N
;
90 writel(reg
, plat
->glue_base
+ CLKRST_CTRL
);
92 /* configure mux for vbus, powerpresent and bvalid signals */
93 reg
= readl(plat
->glue_base
+ USB2_VBUS_MNGMNT_SEL1
);
95 reg
|= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG
) |
96 SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG
) |
97 SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG
);
99 writel(reg
, plat
->glue_base
+ USB2_VBUS_MNGMNT_SEL1
);
101 setbits_le32(plat
->glue_base
+ CLKRST_CTRL
, SW_PIPEW_RESET_N
);
104 static int sti_dwc3_glue_ofdata_to_platdata(struct udevice
*dev
)
106 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
107 struct udevice
*syscon
;
108 struct regmap
*regmap
;
112 ret
= fdtdec_get_int_array(gd
->fdt_blob
, dev_of_offset(dev
),
113 "reg", reg
, ARRAY_SIZE(reg
));
115 pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret
);
119 plat
->glue_base
= reg
[0];
120 plat
->syscfg_offset
= reg
[2];
122 /* get corresponding syscon phandle */
123 ret
= uclass_get_device_by_phandle(UCLASS_SYSCON
, dev
, "st,syscfg",
126 pr_err("unable to find syscon device (%d)\n", ret
);
130 /* get syscfg-reg base address */
131 regmap
= syscon_get_regmap(syscon
);
133 pr_err("unable to find regmap\n");
136 plat
->syscfg_base
= regmap
->base
;
138 /* get powerdown reset */
139 ret
= reset_get_by_name(dev
, "powerdown", &plat
->powerdown_ctl
);
141 pr_err("can't get powerdown reset for %s (%d)", dev
->name
, ret
);
145 /* get softreset reset */
146 ret
= reset_get_by_name(dev
, "softreset", &plat
->softreset_ctl
);
148 pr_err("can't get soft reset for %s (%d)", dev
->name
, ret
);
153 static int sti_dwc3_glue_bind(struct udevice
*dev
)
155 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
158 /* check if one subnode is present */
159 dwc3_node
= fdt_first_subnode(gd
->fdt_blob
, dev_of_offset(dev
));
160 if (dwc3_node
<= 0) {
161 pr_err("Can't find subnode for %s\n", dev
->name
);
165 /* check if the subnode compatible string is the dwc3 one*/
166 if (fdt_node_check_compatible(gd
->fdt_blob
, dwc3_node
,
168 pr_err("Can't find dwc3 subnode for %s\n", dev
->name
);
172 /* retrieve the DWC3 dual role mode */
173 plat
->mode
= usb_get_dr_mode(dwc3_node
);
174 if (plat
->mode
== USB_DR_MODE_UNKNOWN
)
175 /* by default set dual role mode to HOST */
176 plat
->mode
= USB_DR_MODE_HOST
;
178 return dm_scan_fdt_dev(dev
);
181 static int sti_dwc3_glue_probe(struct udevice
*dev
)
183 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
186 /* deassert both powerdown and softreset */
187 ret
= reset_deassert(&plat
->powerdown_ctl
);
189 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
193 ret
= reset_deassert(&plat
->softreset_ctl
);
195 pr_err("DWC3 soft reset deassert failed: %d", ret
);
199 ret
= sti_dwc3_glue_drd_init(plat
);
203 sti_dwc3_glue_init(plat
);
208 ret
= reset_assert(&plat
->softreset_ctl
);
210 pr_err("DWC3 soft reset deassert failed: %d", ret
);
215 ret
= reset_assert(&plat
->powerdown_ctl
);
217 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
222 static int sti_dwc3_glue_remove(struct udevice
*dev
)
224 struct sti_dwc3_glue_platdata
*plat
= dev_get_platdata(dev
);
227 /* assert both powerdown and softreset */
228 ret
= reset_assert(&plat
->powerdown_ctl
);
230 pr_err("DWC3 powerdown reset deassert failed: %d", ret
);
234 ret
= reset_assert(&plat
->softreset_ctl
);
236 pr_err("DWC3 soft reset deassert failed: %d", ret
);
241 static const struct udevice_id sti_dwc3_glue_ids
[] = {
242 { .compatible
= "st,stih407-dwc3" },
246 U_BOOT_DRIVER(dwc3_sti_glue
) = {
247 .name
= "dwc3_sti_glue",
249 .of_match
= sti_dwc3_glue_ids
,
250 .ofdata_to_platdata
= sti_dwc3_glue_ofdata_to_platdata
,
251 .probe
= sti_dwc3_glue_probe
,
252 .remove
= sti_dwc3_glue_remove
,
253 .bind
= sti_dwc3_glue_bind
,
254 .platdata_auto_alloc_size
= sizeof(struct sti_dwc3_glue_platdata
),
255 .flags
= DM_FLAG_ALLOC_PRIV_DMA
,