]>
Commit | Line | Data |
---|---|---|
5f0ffea4 | 1 | /* |
7590d3ce | 2 | * SAMSUNG EXYNOS USB HOST EHCI Controller |
5f0ffea4 RS |
3 | * |
4 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | |
5 | * Vivek Gautam <[email protected]> | |
6 | * | |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
5f0ffea4 RS |
8 | */ |
9 | ||
10 | #include <common.h> | |
aae04d07 | 11 | #include <dm.h> |
e18bf1f9 RS |
12 | #include <fdtdec.h> |
13 | #include <libfdt.h> | |
14 | #include <malloc.h> | |
5f0ffea4 RS |
15 | #include <usb.h> |
16 | #include <asm/arch/cpu.h> | |
7590d3ce | 17 | #include <asm/arch/ehci.h> |
71045da8 | 18 | #include <asm/arch/system.h> |
c48ac113 | 19 | #include <asm/arch/power.h> |
4a271cb1 | 20 | #include <asm/gpio.h> |
e18bf1f9 RS |
21 | #include <asm-generic/errno.h> |
22 | #include <linux/compat.h> | |
5f0ffea4 | 23 | #include "ehci.h" |
5f0ffea4 | 24 | |
e18bf1f9 RS |
25 | /* Declare global data pointer */ |
26 | DECLARE_GLOBAL_DATA_PTR; | |
27 | ||
aae04d07 SG |
28 | struct exynos_ehci_platdata { |
29 | struct usb_platdata usb_plat; | |
30 | fdt_addr_t hcd_base; | |
31 | fdt_addr_t phy_base; | |
32 | struct gpio_desc vbus_gpio; | |
33 | }; | |
aae04d07 | 34 | |
e18bf1f9 RS |
35 | /** |
36 | * Contains pointers to register base addresses | |
37 | * for the usb controller. | |
38 | */ | |
39 | struct exynos_ehci { | |
aae04d07 | 40 | struct ehci_ctrl ctrl; |
e18bf1f9 | 41 | struct exynos_usb_phy *usb; |
24a4775f | 42 | struct ehci_hccr *hcd; |
e18bf1f9 RS |
43 | }; |
44 | ||
aae04d07 SG |
45 | static int ehci_usb_ofdata_to_platdata(struct udevice *dev) |
46 | { | |
47 | struct exynos_ehci_platdata *plat = dev_get_platdata(dev); | |
48 | const void *blob = gd->fdt_blob; | |
49 | unsigned int node; | |
50 | int depth; | |
51 | ||
52 | /* | |
53 | * Get the base address for XHCI controller from the device node | |
54 | */ | |
55 | plat->hcd_base = dev_get_addr(dev); | |
56 | if (plat->hcd_base == FDT_ADDR_T_NONE) { | |
57 | debug("Can't get the XHCI register base address\n"); | |
58 | return -ENXIO; | |
59 | } | |
60 | ||
61 | depth = 0; | |
62 | node = fdtdec_next_compatible_subnode(blob, dev->of_offset, | |
63 | COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); | |
64 | if (node <= 0) { | |
65 | debug("XHCI: Can't get device node for usb3-phy controller\n"); | |
66 | return -ENODEV; | |
67 | } | |
68 | ||
69 | /* | |
70 | * Get the base address for usbphy from the device node | |
71 | */ | |
72 | plat->phy_base = fdtdec_get_addr(blob, node, "reg"); | |
73 | if (plat->phy_base == FDT_ADDR_T_NONE) { | |
74 | debug("Can't get the usbphy register address\n"); | |
75 | return -ENXIO; | |
76 | } | |
77 | ||
78 | /* Vbus gpio */ | |
79 | gpio_request_by_name(dev, "samsung,vbus-gpio", 0, | |
80 | &plat->vbus_gpio, GPIOD_IS_OUT); | |
81 | ||
82 | return 0; | |
83 | } | |
e18bf1f9 | 84 | |
6a23c653 | 85 | static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb) |
5f0ffea4 | 86 | { |
16f9480d IS |
87 | u32 hsic_ctrl; |
88 | ||
5f0ffea4 RS |
89 | clrbits_le32(&usb->usbphyctrl0, |
90 | HOST_CTRL0_FSEL_MASK | | |
91 | HOST_CTRL0_COMMONON_N | | |
92 | /* HOST Phy setting */ | |
93 | HOST_CTRL0_PHYSWRST | | |
94 | HOST_CTRL0_PHYSWRSTALL | | |
95 | HOST_CTRL0_SIDDQ | | |
96 | HOST_CTRL0_FORCESUSPEND | | |
97 | HOST_CTRL0_FORCESLEEP); | |
98 | ||
99 | setbits_le32(&usb->usbphyctrl0, | |
100 | /* Setting up the ref freq */ | |
101 | (CLK_24MHZ << 16) | | |
102 | /* HOST Phy setting */ | |
103 | HOST_CTRL0_LINKSWRST | | |
104 | HOST_CTRL0_UTMISWRST); | |
105 | udelay(10); | |
106 | clrbits_le32(&usb->usbphyctrl0, | |
107 | HOST_CTRL0_LINKSWRST | | |
108 | HOST_CTRL0_UTMISWRST); | |
16f9480d IS |
109 | |
110 | /* HSIC Phy Setting */ | |
111 | hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | | |
112 | HSIC_CTRL_FORCESLEEP | | |
113 | HSIC_CTRL_SIDDQ); | |
114 | ||
115 | clrbits_le32(&usb->hsicphyctrl1, hsic_ctrl); | |
116 | clrbits_le32(&usb->hsicphyctrl2, hsic_ctrl); | |
117 | ||
118 | hsic_ctrl = (((HSIC_CTRL_REFCLKDIV_12 & HSIC_CTRL_REFCLKDIV_MASK) | |
119 | << HSIC_CTRL_REFCLKDIV_SHIFT) | |
120 | | ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK) | |
121 | << HSIC_CTRL_REFCLKSEL_SHIFT) | |
122 | | HSIC_CTRL_UTMISWRST); | |
123 | ||
124 | setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); | |
125 | setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); | |
126 | ||
127 | udelay(10); | |
128 | ||
129 | clrbits_le32(&usb->hsicphyctrl1, HSIC_CTRL_PHYSWRST | | |
130 | HSIC_CTRL_UTMISWRST); | |
131 | ||
132 | clrbits_le32(&usb->hsicphyctrl2, HSIC_CTRL_PHYSWRST | | |
133 | HSIC_CTRL_UTMISWRST); | |
134 | ||
5f0ffea4 RS |
135 | udelay(20); |
136 | ||
137 | /* EHCI Ctrl setting */ | |
138 | setbits_le32(&usb->ehcictrl, | |
139 | EHCICTRL_ENAINCRXALIGN | | |
140 | EHCICTRL_ENAINCR4 | | |
141 | EHCICTRL_ENAINCR8 | | |
142 | EHCICTRL_ENAINCR16); | |
143 | } | |
144 | ||
6a23c653 SR |
145 | static void exynos4412_setup_usb_phy(struct exynos4412_usb_phy *usb) |
146 | { | |
147 | writel(CLK_24MHZ, &usb->usbphyclk); | |
148 | ||
149 | clrbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 | | |
150 | PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 | | |
151 | PHYPWR_NORMAL_MASK_PHY0)); | |
152 | ||
153 | setbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST)); | |
154 | udelay(10); | |
155 | clrbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST)); | |
156 | } | |
157 | ||
158 | static void setup_usb_phy(struct exynos_usb_phy *usb) | |
159 | { | |
160 | set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN); | |
161 | ||
162 | set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN); | |
163 | ||
164 | if (cpu_is_exynos5()) | |
165 | exynos5_setup_usb_phy(usb); | |
166 | else if (cpu_is_exynos4()) | |
167 | if (proid_is_exynos4412()) | |
168 | exynos4412_setup_usb_phy((struct exynos4412_usb_phy *) | |
169 | usb); | |
170 | } | |
171 | ||
172 | static void exynos5_reset_usb_phy(struct exynos_usb_phy *usb) | |
5f0ffea4 | 173 | { |
16f9480d IS |
174 | u32 hsic_ctrl; |
175 | ||
5f0ffea4 RS |
176 | /* HOST_PHY reset */ |
177 | setbits_le32(&usb->usbphyctrl0, | |
178 | HOST_CTRL0_PHYSWRST | | |
179 | HOST_CTRL0_PHYSWRSTALL | | |
180 | HOST_CTRL0_SIDDQ | | |
181 | HOST_CTRL0_FORCESUSPEND | | |
182 | HOST_CTRL0_FORCESLEEP); | |
c48ac113 | 183 | |
16f9480d IS |
184 | /* HSIC Phy reset */ |
185 | hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | | |
186 | HSIC_CTRL_FORCESLEEP | | |
187 | HSIC_CTRL_SIDDQ | | |
188 | HSIC_CTRL_PHYSWRST); | |
189 | ||
190 | setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); | |
191 | setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); | |
6a23c653 SR |
192 | } |
193 | ||
194 | static void exynos4412_reset_usb_phy(struct exynos4412_usb_phy *usb) | |
195 | { | |
196 | setbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 | | |
197 | PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 | | |
198 | PHYPWR_NORMAL_MASK_PHY0)); | |
199 | } | |
200 | ||
201 | /* Reset the EHCI host controller. */ | |
202 | static void reset_usb_phy(struct exynos_usb_phy *usb) | |
203 | { | |
204 | if (cpu_is_exynos5()) | |
205 | exynos5_reset_usb_phy(usb); | |
206 | else if (cpu_is_exynos4()) | |
207 | if (proid_is_exynos4412()) | |
208 | exynos4412_reset_usb_phy((struct exynos4412_usb_phy *) | |
209 | usb); | |
16f9480d | 210 | |
c48ac113 | 211 | set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); |
5f0ffea4 RS |
212 | } |
213 | ||
aae04d07 SG |
214 | static int ehci_usb_probe(struct udevice *dev) |
215 | { | |
216 | struct exynos_ehci_platdata *plat = dev_get_platdata(dev); | |
217 | struct exynos_ehci *ctx = dev_get_priv(dev); | |
218 | struct ehci_hcor *hcor; | |
219 | ||
220 | ctx->hcd = (struct ehci_hccr *)plat->hcd_base; | |
221 | ctx->usb = (struct exynos_usb_phy *)plat->phy_base; | |
222 | hcor = (struct ehci_hcor *)((uint32_t)ctx->hcd + | |
223 | HC_LENGTH(ehci_readl(&ctx->hcd->cr_capbase))); | |
224 | ||
225 | /* setup the Vbus gpio here */ | |
226 | if (dm_gpio_is_valid(&plat->vbus_gpio)) | |
227 | dm_gpio_set_value(&plat->vbus_gpio, 1); | |
228 | ||
229 | setup_usb_phy(ctx->usb); | |
230 | ||
231 | return ehci_register(dev, ctx->hcd, hcor, NULL, 0, USB_INIT_HOST); | |
232 | } | |
233 | ||
234 | static int ehci_usb_remove(struct udevice *dev) | |
235 | { | |
236 | struct exynos_ehci *ctx = dev_get_priv(dev); | |
237 | int ret; | |
238 | ||
239 | ret = ehci_deregister(dev); | |
240 | if (ret) | |
241 | return ret; | |
242 | reset_usb_phy(ctx->usb); | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | static const struct udevice_id ehci_usb_ids[] = { | |
248 | { .compatible = "samsung,exynos-ehci" }, | |
249 | { } | |
250 | }; | |
251 | ||
252 | U_BOOT_DRIVER(usb_ehci) = { | |
253 | .name = "ehci_exynos", | |
254 | .id = UCLASS_USB, | |
255 | .of_match = ehci_usb_ids, | |
256 | .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, | |
257 | .probe = ehci_usb_probe, | |
258 | .remove = ehci_usb_remove, | |
259 | .ops = &ehci_usb_ops, | |
260 | .priv_auto_alloc_size = sizeof(struct exynos_ehci), | |
261 | .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata), | |
262 | .flags = DM_FLAG_ALLOC_PRIV_DMA, | |
263 | }; |