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