]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
13194f3b VG |
2 | /* |
3 | * SAMSUNG EXYNOS5 USB HOST XHCI Controller | |
4 | * | |
5 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | |
6 | * Vivek Gautam <gautam.vivek@samsung.com> | |
7 | * Vikas Sajjan <vikas.sajjan@samsung.com> | |
13194f3b VG |
8 | */ |
9 | ||
10 | /* | |
11 | * This file is a conglomeration for DWC3-init sequence and further | |
12 | * exynos5 specific PHY-init sequence. | |
13 | */ | |
14 | ||
d678a59d | 15 | #include <common.h> |
52e69357 | 16 | #include <dm.h> |
13194f3b | 17 | #include <fdtdec.h> |
f7ae49fc | 18 | #include <log.h> |
401d1c4f | 19 | #include <asm/global_data.h> |
c05ed00a | 20 | #include <linux/delay.h> |
b08c8c48 | 21 | #include <linux/libfdt.h> |
13194f3b VG |
22 | #include <malloc.h> |
23 | #include <usb.h> | |
24 | #include <watchdog.h> | |
25 | #include <asm/arch/cpu.h> | |
26 | #include <asm/arch/power.h> | |
27 | #include <asm/arch/xhci-exynos.h> | |
4a271cb1 | 28 | #include <asm/gpio.h> |
5d97dff0 | 29 | #include <linux/errno.h> |
13194f3b VG |
30 | #include <linux/compat.h> |
31 | #include <linux/usb/dwc3.h> | |
32 | ||
1708a123 | 33 | #include <usb/xhci.h> |
13194f3b VG |
34 | |
35 | /* Declare global data pointer */ | |
36 | DECLARE_GLOBAL_DATA_PTR; | |
37 | ||
8a8d24bd | 38 | struct exynos_xhci_plat { |
52e69357 SG |
39 | fdt_addr_t hcd_base; |
40 | fdt_addr_t phy_base; | |
41 | struct gpio_desc vbus_gpio; | |
42 | }; | |
52e69357 | 43 | |
13194f3b VG |
44 | /** |
45 | * Contains pointers to register base addresses | |
46 | * for the usb controller. | |
47 | */ | |
48 | struct exynos_xhci { | |
8a8d24bd | 49 | struct usb_plat usb_plat; |
52e69357 | 50 | struct xhci_ctrl ctrl; |
13194f3b VG |
51 | struct exynos_usb3_phy *usb3_phy; |
52 | struct xhci_hccr *hcd; | |
53 | struct dwc3 *dwc3_reg; | |
54 | }; | |
55 | ||
d1998a9f | 56 | static int xhci_usb_of_to_plat(struct udevice *dev) |
52e69357 | 57 | { |
8a8d24bd | 58 | struct exynos_xhci_plat *plat = dev_get_plat(dev); |
52e69357 SG |
59 | const void *blob = gd->fdt_blob; |
60 | unsigned int node; | |
61 | int depth; | |
62 | ||
63 | /* | |
64 | * Get the base address for XHCI controller from the device node | |
65 | */ | |
2548493a | 66 | plat->hcd_base = dev_read_addr(dev); |
52e69357 SG |
67 | if (plat->hcd_base == FDT_ADDR_T_NONE) { |
68 | debug("Can't get the XHCI register base address\n"); | |
69 | return -ENXIO; | |
70 | } | |
71 | ||
72 | depth = 0; | |
e160f7d4 | 73 | node = fdtdec_next_compatible_subnode(blob, dev_of_offset(dev), |
52e69357 SG |
74 | COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); |
75 | if (node <= 0) { | |
76 | debug("XHCI: Can't get device node for usb3-phy controller\n"); | |
77 | return -ENODEV; | |
78 | } | |
79 | ||
80 | /* | |
81 | * Get the base address for usbphy from the device node | |
82 | */ | |
83 | plat->phy_base = fdtdec_get_addr(blob, node, "reg"); | |
84 | if (plat->phy_base == FDT_ADDR_T_NONE) { | |
85 | debug("Can't get the usbphy register address\n"); | |
86 | return -ENXIO; | |
87 | } | |
88 | ||
89 | /* Vbus gpio */ | |
90 | gpio_request_by_name(dev, "samsung,vbus-gpio", 0, | |
91 | &plat->vbus_gpio, GPIOD_IS_OUT); | |
92 | ||
93 | return 0; | |
94 | } | |
13194f3b VG |
95 | |
96 | static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) | |
97 | { | |
98 | u32 reg; | |
99 | ||
100 | /* enabling usb_drd phy */ | |
101 | set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); | |
102 | ||
103 | /* Reset USB 3.0 PHY */ | |
104 | writel(0x0, &phy->phy_reg0); | |
105 | ||
106 | clrbits_le32(&phy->phy_param0, | |
107 | /* Select PHY CLK source */ | |
108 | PHYPARAM0_REF_USE_PAD | | |
109 | /* Set Loss-of-Signal Detector sensitivity */ | |
110 | PHYPARAM0_REF_LOSLEVEL_MASK); | |
111 | setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL); | |
112 | ||
113 | writel(0x0, &phy->phy_resume); | |
114 | ||
115 | /* | |
116 | * Setting the Frame length Adj value[6:1] to default 0x20 | |
117 | * See xHCI 1.0 spec, 5.2.4 | |
118 | */ | |
119 | setbits_le32(&phy->link_system, | |
120 | LINKSYSTEM_XHCI_VERSION_CONTROL | | |
121 | LINKSYSTEM_FLADJ(0x20)); | |
122 | ||
123 | /* Set Tx De-Emphasis level */ | |
124 | clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK); | |
125 | setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH); | |
126 | ||
127 | setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL); | |
128 | ||
129 | /* PHYTEST POWERDOWN Control */ | |
130 | clrbits_le32(&phy->phy_test, | |
131 | PHYTEST_POWERDOWN_SSP | | |
132 | PHYTEST_POWERDOWN_HSP); | |
133 | ||
134 | /* UTMI Power Control */ | |
135 | writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi); | |
136 | ||
137 | /* Use core clock from main PLL */ | |
138 | reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | | |
139 | /* Default 24Mhz crystal clock */ | |
140 | PHYCLKRST_FSEL(FSEL_CLKSEL_24M) | | |
141 | PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | | |
142 | PHYCLKRST_SSC_REFCLKSEL(0x88) | | |
143 | /* Force PortReset of PHY */ | |
144 | PHYCLKRST_PORTRESET | | |
145 | /* Digital power supply in normal operating mode */ | |
146 | PHYCLKRST_RETENABLEN | | |
147 | /* Enable ref clock for SS function */ | |
148 | PHYCLKRST_REF_SSP_EN | | |
149 | /* Enable spread spectrum */ | |
150 | PHYCLKRST_SSC_EN | | |
151 | /* Power down HS Bias and PLL blocks in suspend mode */ | |
152 | PHYCLKRST_COMMONONN; | |
153 | ||
154 | writel(reg, &phy->phy_clk_rst); | |
155 | ||
156 | /* giving time to Phy clock to settle before resetting */ | |
157 | udelay(10); | |
158 | ||
159 | reg &= ~PHYCLKRST_PORTRESET; | |
160 | writel(reg, &phy->phy_clk_rst); | |
161 | } | |
162 | ||
163 | static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy) | |
164 | { | |
165 | setbits_le32(&phy->phy_utmi, | |
166 | PHYUTMI_OTGDISABLE | | |
167 | PHYUTMI_FORCESUSPEND | | |
168 | PHYUTMI_FORCESLEEP); | |
169 | ||
170 | clrbits_le32(&phy->phy_clk_rst, | |
171 | PHYCLKRST_REF_SSP_EN | | |
172 | PHYCLKRST_SSC_EN | | |
173 | PHYCLKRST_COMMONONN); | |
174 | ||
175 | /* PHYTEST POWERDOWN Control to remove leakage current */ | |
176 | setbits_le32(&phy->phy_test, | |
177 | PHYTEST_POWERDOWN_SSP | | |
178 | PHYTEST_POWERDOWN_HSP); | |
179 | ||
180 | /* disabling usb_drd phy */ | |
181 | set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE); | |
182 | } | |
183 | ||
13194f3b VG |
184 | static int exynos_xhci_core_init(struct exynos_xhci *exynos) |
185 | { | |
186 | int ret; | |
187 | ||
188 | exynos5_usb3_phy_init(exynos->usb3_phy); | |
189 | ||
190 | ret = dwc3_core_init(exynos->dwc3_reg); | |
191 | if (ret) { | |
192 | debug("failed to initialize core\n"); | |
193 | return -EINVAL; | |
194 | } | |
195 | ||
196 | /* We are hard-coding DWC3 core to Host Mode */ | |
197 | dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | static void exynos_xhci_core_exit(struct exynos_xhci *exynos) | |
203 | { | |
204 | exynos5_usb3_phy_exit(exynos->usb3_phy); | |
205 | } | |
206 | ||
52e69357 SG |
207 | static int xhci_usb_probe(struct udevice *dev) |
208 | { | |
8a8d24bd | 209 | struct exynos_xhci_plat *plat = dev_get_plat(dev); |
52e69357 SG |
210 | struct exynos_xhci *ctx = dev_get_priv(dev); |
211 | struct xhci_hcor *hcor; | |
212 | int ret; | |
213 | ||
214 | ctx->hcd = (struct xhci_hccr *)plat->hcd_base; | |
215 | ctx->usb3_phy = (struct exynos_usb3_phy *)plat->phy_base; | |
216 | ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); | |
217 | hcor = (struct xhci_hcor *)((uint32_t)ctx->hcd + | |
218 | HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase))); | |
219 | ||
220 | /* setup the Vbus gpio here */ | |
221 | if (dm_gpio_is_valid(&plat->vbus_gpio)) | |
222 | dm_gpio_set_value(&plat->vbus_gpio, 1); | |
223 | ||
224 | ret = exynos_xhci_core_init(ctx); | |
225 | if (ret) { | |
226 | puts("XHCI: failed to initialize controller\n"); | |
227 | return -EINVAL; | |
228 | } | |
229 | ||
230 | return xhci_register(dev, ctx->hcd, hcor); | |
231 | } | |
232 | ||
233 | static int xhci_usb_remove(struct udevice *dev) | |
234 | { | |
235 | struct exynos_xhci *ctx = dev_get_priv(dev); | |
236 | int ret; | |
237 | ||
238 | ret = xhci_deregister(dev); | |
239 | if (ret) | |
240 | return ret; | |
241 | exynos_xhci_core_exit(ctx); | |
242 | ||
243 | return 0; | |
244 | } | |
245 | ||
246 | static const struct udevice_id xhci_usb_ids[] = { | |
247 | { .compatible = "samsung,exynos5250-xhci" }, | |
248 | { } | |
249 | }; | |
250 | ||
251 | U_BOOT_DRIVER(usb_xhci) = { | |
252 | .name = "xhci_exynos", | |
253 | .id = UCLASS_USB, | |
254 | .of_match = xhci_usb_ids, | |
d1998a9f | 255 | .of_to_plat = xhci_usb_of_to_plat, |
52e69357 SG |
256 | .probe = xhci_usb_probe, |
257 | .remove = xhci_usb_remove, | |
258 | .ops = &xhci_usb_ops, | |
8a8d24bd | 259 | .plat_auto = sizeof(struct exynos_xhci_plat), |
41575d8e | 260 | .priv_auto = sizeof(struct exynos_xhci), |
52e69357 SG |
261 | .flags = DM_FLAG_ALLOC_PRIV_DMA, |
262 | }; |