1 /*
2 *
3 * phy-hi3516cv500-usb.c
4 *
5 * usb phy driver special for hi3516cv500
6 *
7 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24 #include <asm/arch-hi3516cv500/platform.h>
25 #include <dm.h>
26 #include <usb.h>
27 #include <usb/xhci.h>
28 #include "phy-hisi-usb.h"
29
30 /* offset 0x140 */
31 #define USB2_UTMI_PCTRL (0x1 << 15)
32 #define USB2_PHY_TEST_SRST_REQ (0x1 << 14)
33 #define USB2_UTMI_CKSEL (0x1 << 13)
34 #define USB2_UTMI_CKEN (0x1 << 12)
35 #define USB2_REF_CKEN (0x1 << 9)
36 #define USB2_BUS_CKEN (0x1 << 8)
37 #define USB2_VCC_SRST_REQ (0x1 << 3)
38 #define USB2_PHY_CKEN (0x1 << 2)
39 #define USB2_PHY_PORT_TREQ (0x1 << 1)
40 #define USB2_PHY_REQ (0x1 << 0)
41
42 #define REG_GUSB3PIPECTL0 0xc2c0
43 #define PCS_SSP_SOFT_RESET (0x1 << 31)
44 #define PORT_DISABLE_SUSPEND (0x1 << 17)
45
46 #define REG_GCTL 0xc110
47 #define PORT_CAP_DIR (0x3 << 12)
48 #define PORT_SET_HOST (0x1 << 12)
49
50 #define GTXTHRCFG 0xc108
51 #define USB2_G_TXTHRCFG 0x23100000
52
53 #define GRXTHRCFG 0xc10c
54 #define USB2_G_RXTHRCFG 0x23100000
55
56 #define USB2_INNO_PHY_BASE_REG 0x10110000
57 #define USB2_PHY_CLK_OUTPUT_REG 0x18
58 #define USB2_PHY_CLK_OUTPUT_VAL 0x0c
59
60 #define USB2_VBUS_IO_BASE_REG 0x10ff0000
61 #define USB2_VBUS_IO_OFFSET 0x40
62 #define USB2_VBUS_IO_VAL 0x431
63
64 #define HS_HIGH_HEIGHT_TUNING_OFFSET 0x8
65 #define HS_HIGH_HEIGHT_TUNING_MASK (0x7 << 4)
66 #define HS_HIGH_HEIGHT_TUNING_VAL (0x5 << 4)
67
68 #define PRE_EMPHASIS_TUNING_OFFSET 0x0
69 #define PRE_EMPHASIS_TUNING_MASK (0x7 << 0)
70 #define PRE_EMPHASIS_TUNING_VAL (0x7 << 0)
71
72 #define PRE_EMPHASIS_STRENGTH_OFFSET 0x14
73 #define PRE_EMPHASIS_STRENGTH_MASK (0x7 << 2)
74 #define PRE_EMPHASIS_STRENGTH_VAL (0x3 << 2)
75
76 #define HS_SLEW_RATE_TUNING_OFFSET 0x74
77 #define HS_SLEW_RATE_TUNING_MASK (0x7 << 1)
78 #define HS_SLEW_RATE_TUNING_VAL (0x7 << 1)
79
80 #define DISCONNECT_TRIGGER_OFFSET 0x10
81 #define DISCONNECT_TRIGGER_MASK (0xf << 4)
82 #define DISCONNECT_TRIGGER_VAL (0xd << 4)
83
84 static uintptr_t xhci_base = 0;
85
xhci_hcd_init(int index,struct xhci_hccr ** hccr,struct xhci_hcor ** hcor)86 int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
87 {
88 if ((hccr == NULL) || (hcor == NULL))
89 return -EINVAL;
90
91 xhci_base = USB3_CTRL_REG_BASE;
92
93 *hccr = (struct xhci_hccr *)(xhci_base);
94 *hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
95 HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
96
97 return 0;
98 }
99
usb2_eye_config(void)100 void usb2_eye_config(void)
101 {
102 unsigned int reg;
103
104 /* HS eye height tuning */
105 reg = readl(USB2_INNO_PHY_BASE_REG + HS_HIGH_HEIGHT_TUNING_OFFSET);
106 reg &= ~HS_HIGH_HEIGHT_TUNING_MASK;
107 reg |= HS_HIGH_HEIGHT_TUNING_VAL;
108 writel(reg, USB2_INNO_PHY_BASE_REG + HS_HIGH_HEIGHT_TUNING_OFFSET);
109
110 /* Pre-emphasis tuning */
111 reg = readl(USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_TUNING_OFFSET);
112 reg &= ~PRE_EMPHASIS_TUNING_MASK;
113 reg |= PRE_EMPHASIS_TUNING_VAL;
114 writel(reg, USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_TUNING_OFFSET);
115
116 /* Pre-emphasis strength */
117 reg = readl(USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_STRENGTH_OFFSET);
118 reg &= ~PRE_EMPHASIS_STRENGTH_MASK;
119 reg |= PRE_EMPHASIS_STRENGTH_VAL;
120 writel(reg, USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_STRENGTH_OFFSET);
121
122 /* HS driver slew rate tunning */
123 reg = readl(USB2_INNO_PHY_BASE_REG + HS_SLEW_RATE_TUNING_OFFSET);
124 reg &= ~HS_SLEW_RATE_TUNING_MASK;
125 reg |= HS_SLEW_RATE_TUNING_VAL;
126 writel(reg, USB2_INNO_PHY_BASE_REG + HS_SLEW_RATE_TUNING_OFFSET);
127
128 /* HOST disconnects detection trigger point */
129 reg = readl(USB2_INNO_PHY_BASE_REG + DISCONNECT_TRIGGER_OFFSET);
130 reg &= ~DISCONNECT_TRIGGER_MASK;
131 reg |= DISCONNECT_TRIGGER_VAL;
132 writel(reg, USB2_INNO_PHY_BASE_REG + DISCONNECT_TRIGGER_OFFSET);
133 }
134
phy_hiusb_init_clk(int index)135 void phy_hiusb_init_clk(int index)
136 {
137 unsigned int reg;
138
139 /* set inno phy output clock */
140 writel(USB2_PHY_CLK_OUTPUT_VAL, USB2_INNO_PHY_BASE_REG +
141 USB2_PHY_CLK_OUTPUT_REG);
142 udelay(U_LEVEL1);
143
144 /* open phy ref cken */
145 reg = readl(CRG_REG_BASE + REG_CRG80);
146 reg |= USB2_PHY_CKEN;
147 writel(reg, CRG_REG_BASE + REG_CRG80);
148 udelay(U_LEVEL1);
149
150 /* open utmi pctrl */
151 reg = readl(CRG_REG_BASE + REG_CRG80);
152 reg &= ~USB2_UTMI_PCTRL;
153 writel(reg, CRG_REG_BASE + REG_CRG80);
154 udelay(U_LEVEL1);
155
156 /* open utmi cksel */
157 reg = readl(CRG_REG_BASE + REG_CRG80);
158 reg &= ~USB2_UTMI_CKSEL;
159 writel(reg, CRG_REG_BASE + REG_CRG80);
160 udelay(U_LEVEL1);
161
162 /* open utmi cken */
163 reg = readl(CRG_REG_BASE + REG_CRG80);
164 reg |= USB2_UTMI_CKEN;
165 writel(reg, CRG_REG_BASE + REG_CRG80);
166 udelay(U_LEVEL1);
167
168 /* open controller ref cken */
169 reg = readl(CRG_REG_BASE + REG_CRG80);
170 reg |= USB2_REF_CKEN;
171 writel(reg, CRG_REG_BASE + REG_CRG80);
172 udelay(U_LEVEL1);
173
174 /* open bus cken */
175 reg = readl(CRG_REG_BASE + REG_CRG80);
176 reg |= USB2_BUS_CKEN;
177 writel(reg, CRG_REG_BASE + REG_CRG80);
178 udelay(U_LEVEL6);
179
180 /* cancel POR reset */
181 reg = readl(CRG_REG_BASE + REG_CRG80);
182 reg &= ~USB2_PHY_REQ;
183 writel(reg, CRG_REG_BASE + REG_CRG80);
184 udelay(U_LEVEL6);
185
186 /* cancel TPOR reset */
187 reg = readl(CRG_REG_BASE + REG_CRG80);
188 reg &= ~USB2_PHY_PORT_TREQ;
189 writel(reg, CRG_REG_BASE + REG_CRG80);
190 udelay(U_LEVEL6);
191 }
192
phy_hiusb_init(int index)193 void phy_hiusb_init(int index)
194 {
195 unsigned int reg;
196
197 /* usb phy reset */
198 reg = readl(CRG_REG_BASE + REG_CRG80);
199 reg |= USB2_PHY_TEST_SRST_REQ;
200 writel(reg, CRG_REG_BASE + REG_CRG80);
201 udelay(U_LEVEL5);
202
203 /* cancel usb phy srst */
204 reg = readl(CRG_REG_BASE + REG_CRG80);
205 reg &= ~USB2_PHY_TEST_SRST_REQ;
206 writel(reg, CRG_REG_BASE + REG_CRG80);
207 udelay(U_LEVEL2);
208
209 /* usb2 vcc reset */
210 reg = readl(CRG_REG_BASE + REG_CRG80);
211 reg |= USB2_VCC_SRST_REQ;
212 writel(reg, CRG_REG_BASE + REG_CRG80);
213 udelay(U_LEVEL6);
214
215 phy_hiusb_init_clk(index);
216
217 /* cancel vcc reset */
218 reg = readl(CRG_REG_BASE + REG_CRG80);
219 reg &= ~USB2_VCC_SRST_REQ;
220 writel(reg, CRG_REG_BASE + REG_CRG80);
221 udelay(U_LEVEL6);
222
223 /* usb2 test vbus using gpio */
224 writel(USB2_VBUS_IO_VAL, USB2_VBUS_IO_BASE_REG + USB2_VBUS_IO_OFFSET);
225 udelay(U_LEVEL2);
226
227 /* USB2 Controller configs */
228 reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
229 reg |= PCS_SSP_SOFT_RESET;
230 writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
231 udelay(U_LEVEL2);
232
233 reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
234 reg &= ~PORT_CAP_DIR;
235 reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
236 writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
237 udelay(U_LEVEL2);
238
239 reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
240 reg &= ~PCS_SSP_SOFT_RESET;
241 reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
242 writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
243 udelay(U_LEVEL2);
244
245 writel(USB2_G_TXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
246 writel(USB2_G_RXTHRCFG, USB3_CTRL_REG_BASE + GRXTHRCFG);
247 udelay(U_LEVEL2);
248
249 /* USB2 eye config */
250 usb2_eye_config();
251 }
252 EXPORT_SYMBOL(phy_hiusb_init);
253
xhci_hcd_stop(int index)254 void xhci_hcd_stop(int index)
255 {
256 unsigned int reg;
257
258 /* usb2 vcc reset */
259 reg = readl(CRG_REG_BASE + REG_CRG80);
260 reg |= USB2_VCC_SRST_REQ;
261 writel(reg, CRG_REG_BASE + REG_CRG80);
262 udelay(U_LEVEL6);
263 }
264 EXPORT_SYMBOL(xhci_hcd_stop);
265