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