diff options
author | Vivek Gautam <gautam.vivek@samsung.com> | 2013-01-22 08:00:41 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-01-23 06:39:11 -0500 |
commit | 8c1b3e16e902b010f79e2d299927ec43b495f1c7 (patch) | |
tree | 851e81f1b30a3f63d7925ab45c8783f94f39ba65 /drivers/usb/phy | |
parent | b506eebc504caaf863b5c5a68a1e1d304d610482 (diff) |
usb: phy: samsung: Add host phy support to samsung-phy driver
This patch adds host phy support to samsung-usbphy driver and
further adds support for samsung's exynos5250 usb-phy.
Signed-off-by: Praveen Paneri <p.paneri@samsung.com>
Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r-- | drivers/usb/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/phy/samsung-usbphy.c | 513 |
2 files changed, 486 insertions, 29 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 36a85b675429..fae4d08c0ddd 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig | |||
@@ -48,7 +48,7 @@ config USB_RCAR_PHY | |||
48 | 48 | ||
49 | config SAMSUNG_USBPHY | 49 | config SAMSUNG_USBPHY |
50 | bool "Samsung USB PHY controller Driver" | 50 | bool "Samsung USB PHY controller Driver" |
51 | depends on USB_S3C_HSOTG | 51 | depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS |
52 | select USB_OTG_UTILS | 52 | select USB_OTG_UTILS |
53 | help | 53 | help |
54 | Enable this to support Samsung USB phy controller for samsung | 54 | Enable this to support Samsung USB phy controller for samsung |
diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 30aebb59d803..9e9861c673ab 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c | |||
@@ -5,7 +5,8 @@ | |||
5 | * | 5 | * |
6 | * Author: Praveen Paneri <p.paneri@samsung.com> | 6 | * Author: Praveen Paneri <p.paneri@samsung.com> |
7 | * | 7 | * |
8 | * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller | 8 | * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and |
9 | * OHCI-EXYNOS controllers. | ||
9 | * | 10 | * |
10 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,11 +22,13 @@ | |||
21 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
22 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
23 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/device.h> | ||
24 | #include <linux/err.h> | 26 | #include <linux/err.h> |
25 | #include <linux/io.h> | 27 | #include <linux/io.h> |
26 | #include <linux/of.h> | 28 | #include <linux/of.h> |
27 | #include <linux/of_address.h> | 29 | #include <linux/of_address.h> |
28 | #include <linux/usb/otg.h> | 30 | #include <linux/usb/otg.h> |
31 | #include <linux/usb/samsung_usb_phy.h> | ||
29 | #include <linux/platform_data/samsung-usbphy.h> | 32 | #include <linux/platform_data/samsung-usbphy.h> |
30 | 33 | ||
31 | /* Register definitions */ | 34 | /* Register definitions */ |
@@ -57,24 +60,132 @@ | |||
57 | #define RSTCON_HLINK_SWRST (0x1 << 1) | 60 | #define RSTCON_HLINK_SWRST (0x1 << 1) |
58 | #define RSTCON_SWRST (0x1 << 0) | 61 | #define RSTCON_SWRST (0x1 << 0) |
59 | 62 | ||
63 | /* EXYNOS5 */ | ||
64 | #define EXYNOS5_PHY_HOST_CTRL0 (0x00) | ||
65 | |||
66 | #define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) | ||
67 | |||
68 | #define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) | ||
69 | #define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) | ||
70 | #define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) | ||
71 | #define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) | ||
72 | |||
73 | #define HOST_CTRL0_FSEL_MASK (0x7 << 16) | ||
74 | #define HOST_CTRL0_FSEL(_x) ((_x) << 16) | ||
75 | |||
76 | #define FSEL_CLKSEL_50M (0x7) | ||
77 | #define FSEL_CLKSEL_24M (0x5) | ||
78 | #define FSEL_CLKSEL_20M (0x4) | ||
79 | #define FSEL_CLKSEL_19200K (0x3) | ||
80 | #define FSEL_CLKSEL_12M (0x2) | ||
81 | #define FSEL_CLKSEL_10M (0x1) | ||
82 | #define FSEL_CLKSEL_9600K (0x0) | ||
83 | |||
84 | #define HOST_CTRL0_TESTBURNIN (0x1 << 11) | ||
85 | #define HOST_CTRL0_RETENABLE (0x1 << 10) | ||
86 | #define HOST_CTRL0_COMMONON_N (0x1 << 9) | ||
87 | #define HOST_CTRL0_SIDDQ (0x1 << 6) | ||
88 | #define HOST_CTRL0_FORCESLEEP (0x1 << 5) | ||
89 | #define HOST_CTRL0_FORCESUSPEND (0x1 << 4) | ||
90 | #define HOST_CTRL0_WORDINTERFACE (0x1 << 3) | ||
91 | #define HOST_CTRL0_UTMISWRST (0x1 << 2) | ||
92 | #define HOST_CTRL0_LINKSWRST (0x1 << 1) | ||
93 | #define HOST_CTRL0_PHYSWRST (0x1 << 0) | ||
94 | |||
95 | #define EXYNOS5_PHY_HOST_TUNE0 (0x04) | ||
96 | |||
97 | #define EXYNOS5_PHY_HSIC_CTRL1 (0x10) | ||
98 | |||
99 | #define EXYNOS5_PHY_HSIC_TUNE1 (0x14) | ||
100 | |||
101 | #define EXYNOS5_PHY_HSIC_CTRL2 (0x20) | ||
102 | |||
103 | #define EXYNOS5_PHY_HSIC_TUNE2 (0x24) | ||
104 | |||
105 | #define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) | ||
106 | #define HSIC_CTRL_REFCLKSEL (0x2 << 23) | ||
107 | |||
108 | #define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) | ||
109 | #define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) | ||
110 | #define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) | ||
111 | #define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) | ||
112 | #define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) | ||
113 | #define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) | ||
114 | #define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) | ||
115 | |||
116 | #define HSIC_CTRL_SIDDQ (0x1 << 6) | ||
117 | #define HSIC_CTRL_FORCESLEEP (0x1 << 5) | ||
118 | #define HSIC_CTRL_FORCESUSPEND (0x1 << 4) | ||
119 | #define HSIC_CTRL_WORDINTERFACE (0x1 << 3) | ||
120 | #define HSIC_CTRL_UTMISWRST (0x1 << 2) | ||
121 | #define HSIC_CTRL_PHYSWRST (0x1 << 0) | ||
122 | |||
123 | #define EXYNOS5_PHY_HOST_EHCICTRL (0x30) | ||
124 | |||
125 | #define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) | ||
126 | #define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) | ||
127 | #define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) | ||
128 | #define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) | ||
129 | |||
130 | #define EXYNOS5_PHY_HOST_OHCICTRL (0x34) | ||
131 | |||
132 | #define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) | ||
133 | #define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) | ||
134 | #define HOST_OHCICTRL_CNTSEL (0x1 << 1) | ||
135 | #define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) | ||
136 | |||
137 | #define EXYNOS5_PHY_OTG_SYS (0x38) | ||
138 | |||
139 | #define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) | ||
140 | #define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) | ||
141 | #define OTG_SYS_PHY0_SWRST (0x1 << 12) | ||
142 | |||
143 | #define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) | ||
144 | #define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) | ||
145 | #define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) | ||
146 | #define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) | ||
147 | |||
148 | #define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) | ||
149 | #define OTG_SYS_COMMON_ON (0x1 << 7) | ||
150 | |||
151 | #define OTG_SYS_FSEL_MASK (0x7 << 4) | ||
152 | #define OTG_SYS_FSEL(_x) ((_x) << 4) | ||
153 | |||
154 | #define OTG_SYS_FORCESLEEP (0x1 << 3) | ||
155 | #define OTG_SYS_OTGDISABLE (0x1 << 2) | ||
156 | #define OTG_SYS_SIDDQ_UOTG (0x1 << 1) | ||
157 | #define OTG_SYS_FORCESUSPEND (0x1 << 0) | ||
158 | |||
159 | #define EXYNOS5_PHY_OTG_TUNE (0x40) | ||
160 | |||
60 | #ifndef MHZ | 161 | #ifndef MHZ |
61 | #define MHZ (1000*1000) | 162 | #define MHZ (1000*1000) |
62 | #endif | 163 | #endif |
63 | 164 | ||
165 | #ifndef KHZ | ||
166 | #define KHZ (1000) | ||
167 | #endif | ||
168 | |||
169 | #define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) | ||
64 | #define S3C64XX_USBPHY_ENABLE (0x1 << 16) | 170 | #define S3C64XX_USBPHY_ENABLE (0x1 << 16) |
65 | #define EXYNOS_USBPHY_ENABLE (0x1 << 0) | 171 | #define EXYNOS_USBPHY_ENABLE (0x1 << 0) |
172 | #define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) | ||
66 | 173 | ||
67 | enum samsung_cpu_type { | 174 | enum samsung_cpu_type { |
68 | TYPE_S3C64XX, | 175 | TYPE_S3C64XX, |
69 | TYPE_EXYNOS4210, | 176 | TYPE_EXYNOS4210, |
177 | TYPE_EXYNOS5250, | ||
70 | }; | 178 | }; |
71 | 179 | ||
72 | /* | 180 | /* |
73 | * struct samsung_usbphy_drvdata - driver data for various SoC variants | 181 | * struct samsung_usbphy_drvdata - driver data for various SoC variants |
74 | * @cpu_type: machine identifier | 182 | * @cpu_type: machine identifier |
75 | * @devphy_en_mask: device phy enable mask for PHY CONTROL register | 183 | * @devphy_en_mask: device phy enable mask for PHY CONTROL register |
184 | * @hostphy_en_mask: host phy enable mask for PHY CONTROL register | ||
76 | * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from | 185 | * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from |
77 | * mapped address of system controller. | 186 | * mapped address of system controller. |
187 | * @hostphy_reg_offset: offset to HOST PHY CONTROL register from | ||
188 | * mapped address of system controller. | ||
78 | * | 189 | * |
79 | * Here we have a separate mask for device type phy. | 190 | * Here we have a separate mask for device type phy. |
80 | * Having different masks for host and device type phy helps | 191 | * Having different masks for host and device type phy helps |
@@ -87,7 +198,9 @@ enum samsung_cpu_type { | |||
87 | struct samsung_usbphy_drvdata { | 198 | struct samsung_usbphy_drvdata { |
88 | int cpu_type; | 199 | int cpu_type; |
89 | int devphy_en_mask; | 200 | int devphy_en_mask; |
201 | int hostphy_en_mask; | ||
90 | u32 devphy_reg_offset; | 202 | u32 devphy_reg_offset; |
203 | u32 hostphy_reg_offset; | ||
91 | }; | 204 | }; |
92 | 205 | ||
93 | /* | 206 | /* |
@@ -98,8 +211,12 @@ struct samsung_usbphy_drvdata { | |||
98 | * @clk: usb phy clock | 211 | * @clk: usb phy clock |
99 | * @regs: usb phy controller registers memory base | 212 | * @regs: usb phy controller registers memory base |
100 | * @pmuregs: USB device PHY_CONTROL register memory base | 213 | * @pmuregs: USB device PHY_CONTROL register memory base |
214 | * @sysreg: USB2.0 PHY_CFG register memory base | ||
101 | * @ref_clk_freq: reference clock frequency selection | 215 | * @ref_clk_freq: reference clock frequency selection |
102 | * @drv_data: driver data available for different SoCs | 216 | * @drv_data: driver data available for different SoCs |
217 | * @phy_type: Samsung SoCs specific phy types: #HOST | ||
218 | * #DEVICE | ||
219 | * @phy_usage: usage count for phy | ||
103 | * @lock: lock for phy operations | 220 | * @lock: lock for phy operations |
104 | */ | 221 | */ |
105 | struct samsung_usbphy { | 222 | struct samsung_usbphy { |
@@ -109,13 +226,27 @@ struct samsung_usbphy { | |||
109 | struct clk *clk; | 226 | struct clk *clk; |
110 | void __iomem *regs; | 227 | void __iomem *regs; |
111 | void __iomem *pmuregs; | 228 | void __iomem *pmuregs; |
229 | void __iomem *sysreg; | ||
112 | int ref_clk_freq; | 230 | int ref_clk_freq; |
113 | const struct samsung_usbphy_drvdata *drv_data; | 231 | const struct samsung_usbphy_drvdata *drv_data; |
232 | enum samsung_usb_phy_type phy_type; | ||
233 | atomic_t phy_usage; | ||
114 | spinlock_t lock; | 234 | spinlock_t lock; |
115 | }; | 235 | }; |
116 | 236 | ||
117 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) | 237 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) |
118 | 238 | ||
239 | int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) | ||
240 | { | ||
241 | if (!otg) | ||
242 | return -ENODEV; | ||
243 | |||
244 | if (!otg->host) | ||
245 | otg->host = host; | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
119 | static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) | 250 | static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) |
120 | { | 251 | { |
121 | struct device_node *usbphy_sys; | 252 | struct device_node *usbphy_sys; |
@@ -129,14 +260,27 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) | |||
129 | 260 | ||
130 | sphy->pmuregs = of_iomap(usbphy_sys, 0); | 261 | sphy->pmuregs = of_iomap(usbphy_sys, 0); |
131 | 262 | ||
132 | of_node_put(usbphy_sys); | ||
133 | |||
134 | if (sphy->pmuregs == NULL) { | 263 | if (sphy->pmuregs == NULL) { |
135 | dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); | 264 | dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); |
136 | return -ENODEV; | 265 | goto err0; |
137 | } | 266 | } |
138 | 267 | ||
268 | sphy->sysreg = of_iomap(usbphy_sys, 1); | ||
269 | |||
270 | /* | ||
271 | * Not returning error code here, since this situation is not fatal. | ||
272 | * Few SoCs may not have this switch available | ||
273 | */ | ||
274 | if (sphy->sysreg == NULL) | ||
275 | dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); | ||
276 | |||
277 | of_node_put(usbphy_sys); | ||
278 | |||
139 | return 0; | 279 | return 0; |
280 | |||
281 | err0: | ||
282 | of_node_put(usbphy_sys); | ||
283 | return -ENXIO; | ||
140 | } | 284 | } |
141 | 285 | ||
142 | /* | 286 | /* |
@@ -146,17 +290,42 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) | |||
146 | */ | 290 | */ |
147 | static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) | 291 | static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) |
148 | { | 292 | { |
149 | void __iomem *reg; | 293 | void __iomem *reg = NULL; |
150 | u32 reg_val; | 294 | u32 reg_val; |
151 | u32 en_mask; | 295 | u32 en_mask = 0; |
152 | 296 | ||
153 | if (!sphy->pmuregs) { | 297 | if (!sphy->pmuregs) { |
154 | dev_warn(sphy->dev, "Can't set pmu isolation\n"); | 298 | dev_warn(sphy->dev, "Can't set pmu isolation\n"); |
155 | return; | 299 | return; |
156 | } | 300 | } |
157 | 301 | ||
158 | reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; | 302 | switch (sphy->drv_data->cpu_type) { |
159 | en_mask = sphy->drv_data->devphy_en_mask; | 303 | case TYPE_S3C64XX: |
304 | /* | ||
305 | * Do nothing: We will add here once S3C64xx goes for DT support | ||
306 | */ | ||
307 | break; | ||
308 | case TYPE_EXYNOS4210: | ||
309 | /* | ||
310 | * Fall through since exynos4210 and exynos5250 have similar | ||
311 | * register architecture: two separate registers for host and | ||
312 | * device phy control with enable bit at position 0. | ||
313 | */ | ||
314 | case TYPE_EXYNOS5250: | ||
315 | if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { | ||
316 | reg = sphy->pmuregs + | ||
317 | sphy->drv_data->devphy_reg_offset; | ||
318 | en_mask = sphy->drv_data->devphy_en_mask; | ||
319 | } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { | ||
320 | reg = sphy->pmuregs + | ||
321 | sphy->drv_data->hostphy_reg_offset; | ||
322 | en_mask = sphy->drv_data->hostphy_en_mask; | ||
323 | } | ||
324 | break; | ||
325 | default: | ||
326 | dev_err(sphy->dev, "Invalid SoC type\n"); | ||
327 | return; | ||
328 | } | ||
160 | 329 | ||
161 | reg_val = readl(reg); | 330 | reg_val = readl(reg); |
162 | 331 | ||
@@ -169,6 +338,43 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) | |||
169 | } | 338 | } |
170 | 339 | ||
171 | /* | 340 | /* |
341 | * Configure the mode of working of usb-phy here: HOST/DEVICE. | ||
342 | */ | ||
343 | static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) | ||
344 | { | ||
345 | u32 reg; | ||
346 | |||
347 | if (!sphy->sysreg) { | ||
348 | dev_warn(sphy->dev, "Can't configure specified phy mode\n"); | ||
349 | return; | ||
350 | } | ||
351 | |||
352 | reg = readl(sphy->sysreg); | ||
353 | |||
354 | if (sphy->phy_type == USB_PHY_TYPE_DEVICE) | ||
355 | reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; | ||
356 | else if (sphy->phy_type == USB_PHY_TYPE_HOST) | ||
357 | reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; | ||
358 | |||
359 | writel(reg, sphy->sysreg); | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * PHYs are different for USB Device and USB Host. | ||
364 | * This make sure that correct PHY type is selected before | ||
365 | * any operation on PHY. | ||
366 | */ | ||
367 | static int samsung_usbphy_set_type(struct usb_phy *phy, | ||
368 | enum samsung_usb_phy_type phy_type) | ||
369 | { | ||
370 | struct samsung_usbphy *sphy = phy_to_sphy(phy); | ||
371 | |||
372 | sphy->phy_type = phy_type; | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | /* | ||
172 | * Returns reference clock frequency selection value | 378 | * Returns reference clock frequency selection value |
173 | */ | 379 | */ |
174 | static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | 380 | static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) |
@@ -176,34 +382,185 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | |||
176 | struct clk *ref_clk; | 382 | struct clk *ref_clk; |
177 | int refclk_freq = 0; | 383 | int refclk_freq = 0; |
178 | 384 | ||
179 | ref_clk = clk_get(sphy->dev, "xusbxti"); | 385 | /* |
386 | * In exynos5250 USB host and device PHY use | ||
387 | * external crystal clock XXTI | ||
388 | */ | ||
389 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | ||
390 | ref_clk = clk_get(sphy->dev, "ext_xtal"); | ||
391 | else | ||
392 | ref_clk = clk_get(sphy->dev, "xusbxti"); | ||
180 | if (IS_ERR(ref_clk)) { | 393 | if (IS_ERR(ref_clk)) { |
181 | dev_err(sphy->dev, "Failed to get reference clock\n"); | 394 | dev_err(sphy->dev, "Failed to get reference clock\n"); |
182 | return PTR_ERR(ref_clk); | 395 | return PTR_ERR(ref_clk); |
183 | } | 396 | } |
184 | 397 | ||
185 | switch (clk_get_rate(ref_clk)) { | 398 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) { |
186 | case 12 * MHZ: | 399 | /* set clock frequency for PLL */ |
187 | refclk_freq = PHYCLK_CLKSEL_12M; | 400 | switch (clk_get_rate(ref_clk)) { |
188 | break; | 401 | case 9600 * KHZ: |
189 | case 24 * MHZ: | 402 | refclk_freq = FSEL_CLKSEL_9600K; |
190 | refclk_freq = PHYCLK_CLKSEL_24M; | 403 | break; |
191 | break; | 404 | case 10 * MHZ: |
192 | case 48 * MHZ: | 405 | refclk_freq = FSEL_CLKSEL_10M; |
193 | refclk_freq = PHYCLK_CLKSEL_48M; | 406 | break; |
194 | break; | 407 | case 12 * MHZ: |
195 | default: | 408 | refclk_freq = FSEL_CLKSEL_12M; |
196 | if (sphy->drv_data->cpu_type == TYPE_S3C64XX) | 409 | break; |
197 | refclk_freq = PHYCLK_CLKSEL_48M; | 410 | case 19200 * KHZ: |
198 | else | 411 | refclk_freq = FSEL_CLKSEL_19200K; |
412 | break; | ||
413 | case 20 * MHZ: | ||
414 | refclk_freq = FSEL_CLKSEL_20M; | ||
415 | break; | ||
416 | case 50 * MHZ: | ||
417 | refclk_freq = FSEL_CLKSEL_50M; | ||
418 | break; | ||
419 | case 24 * MHZ: | ||
420 | default: | ||
421 | /* default reference clock */ | ||
422 | refclk_freq = FSEL_CLKSEL_24M; | ||
423 | break; | ||
424 | } | ||
425 | } else { | ||
426 | switch (clk_get_rate(ref_clk)) { | ||
427 | case 12 * MHZ: | ||
428 | refclk_freq = PHYCLK_CLKSEL_12M; | ||
429 | break; | ||
430 | case 24 * MHZ: | ||
199 | refclk_freq = PHYCLK_CLKSEL_24M; | 431 | refclk_freq = PHYCLK_CLKSEL_24M; |
200 | break; | 432 | break; |
433 | case 48 * MHZ: | ||
434 | refclk_freq = PHYCLK_CLKSEL_48M; | ||
435 | break; | ||
436 | default: | ||
437 | if (sphy->drv_data->cpu_type == TYPE_S3C64XX) | ||
438 | refclk_freq = PHYCLK_CLKSEL_48M; | ||
439 | else | ||
440 | refclk_freq = PHYCLK_CLKSEL_24M; | ||
441 | break; | ||
442 | } | ||
201 | } | 443 | } |
202 | clk_put(ref_clk); | 444 | clk_put(ref_clk); |
203 | 445 | ||
204 | return refclk_freq; | 446 | return refclk_freq; |
205 | } | 447 | } |
206 | 448 | ||
449 | static bool exynos5_phyhost_is_on(void *regs) | ||
450 | { | ||
451 | u32 reg; | ||
452 | |||
453 | reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
454 | |||
455 | return !(reg & HOST_CTRL0_SIDDQ); | ||
456 | } | ||
457 | |||
458 | static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) | ||
459 | { | ||
460 | void __iomem *regs = sphy->regs; | ||
461 | u32 phyclk = sphy->ref_clk_freq; | ||
462 | u32 phyhost; | ||
463 | u32 phyotg; | ||
464 | u32 phyhsic; | ||
465 | u32 ehcictrl; | ||
466 | u32 ohcictrl; | ||
467 | |||
468 | /* | ||
469 | * phy_usage helps in keeping usage count for phy | ||
470 | * so that the first consumer enabling the phy is also | ||
471 | * the last consumer to disable it. | ||
472 | */ | ||
473 | |||
474 | atomic_inc(&sphy->phy_usage); | ||
475 | |||
476 | if (exynos5_phyhost_is_on(regs)) { | ||
477 | dev_info(sphy->dev, "Already power on PHY\n"); | ||
478 | return; | ||
479 | } | ||
480 | |||
481 | /* Host configuration */ | ||
482 | phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
483 | |||
484 | /* phy reference clock configuration */ | ||
485 | phyhost &= ~HOST_CTRL0_FSEL_MASK; | ||
486 | phyhost |= HOST_CTRL0_FSEL(phyclk); | ||
487 | |||
488 | /* host phy reset */ | ||
489 | phyhost &= ~(HOST_CTRL0_PHYSWRST | | ||
490 | HOST_CTRL0_PHYSWRSTALL | | ||
491 | HOST_CTRL0_SIDDQ | | ||
492 | /* Enable normal mode of operation */ | ||
493 | HOST_CTRL0_FORCESUSPEND | | ||
494 | HOST_CTRL0_FORCESLEEP); | ||
495 | |||
496 | /* Link reset */ | ||
497 | phyhost |= (HOST_CTRL0_LINKSWRST | | ||
498 | HOST_CTRL0_UTMISWRST | | ||
499 | /* COMMON Block configuration during suspend */ | ||
500 | HOST_CTRL0_COMMONON_N); | ||
501 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
502 | udelay(10); | ||
503 | phyhost &= ~(HOST_CTRL0_LINKSWRST | | ||
504 | HOST_CTRL0_UTMISWRST); | ||
505 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
506 | |||
507 | /* OTG configuration */ | ||
508 | phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); | ||
509 | |||
510 | /* phy reference clock configuration */ | ||
511 | phyotg &= ~OTG_SYS_FSEL_MASK; | ||
512 | phyotg |= OTG_SYS_FSEL(phyclk); | ||
513 | |||
514 | /* Enable normal mode of operation */ | ||
515 | phyotg &= ~(OTG_SYS_FORCESUSPEND | | ||
516 | OTG_SYS_SIDDQ_UOTG | | ||
517 | OTG_SYS_FORCESLEEP | | ||
518 | OTG_SYS_REFCLKSEL_MASK | | ||
519 | /* COMMON Block configuration during suspend */ | ||
520 | OTG_SYS_COMMON_ON); | ||
521 | |||
522 | /* OTG phy & link reset */ | ||
523 | phyotg |= (OTG_SYS_PHY0_SWRST | | ||
524 | OTG_SYS_LINKSWRST_UOTG | | ||
525 | OTG_SYS_PHYLINK_SWRESET | | ||
526 | OTG_SYS_OTGDISABLE | | ||
527 | /* Set phy refclk */ | ||
528 | OTG_SYS_REFCLKSEL_CLKCORE); | ||
529 | |||
530 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
531 | udelay(10); | ||
532 | phyotg &= ~(OTG_SYS_PHY0_SWRST | | ||
533 | OTG_SYS_LINKSWRST_UOTG | | ||
534 | OTG_SYS_PHYLINK_SWRESET); | ||
535 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
536 | |||
537 | /* HSIC phy configuration */ | ||
538 | phyhsic = (HSIC_CTRL_REFCLKDIV_12 | | ||
539 | HSIC_CTRL_REFCLKSEL | | ||
540 | HSIC_CTRL_PHYSWRST); | ||
541 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
542 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
543 | udelay(10); | ||
544 | phyhsic &= ~HSIC_CTRL_PHYSWRST; | ||
545 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
546 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
547 | |||
548 | udelay(80); | ||
549 | |||
550 | /* enable EHCI DMA burst */ | ||
551 | ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); | ||
552 | ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | | ||
553 | HOST_EHCICTRL_ENAINCR4 | | ||
554 | HOST_EHCICTRL_ENAINCR8 | | ||
555 | HOST_EHCICTRL_ENAINCR16); | ||
556 | writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); | ||
557 | |||
558 | /* set ohci_suspend_on_n */ | ||
559 | ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); | ||
560 | ohcictrl |= HOST_OHCICTRL_SUSPLGCY; | ||
561 | writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); | ||
562 | } | ||
563 | |||
207 | static void samsung_usbphy_enable(struct samsung_usbphy *sphy) | 564 | static void samsung_usbphy_enable(struct samsung_usbphy *sphy) |
208 | { | 565 | { |
209 | void __iomem *regs = sphy->regs; | 566 | void __iomem *regs = sphy->regs; |
@@ -239,6 +596,41 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) | |||
239 | writel(rstcon, regs + SAMSUNG_RSTCON); | 596 | writel(rstcon, regs + SAMSUNG_RSTCON); |
240 | } | 597 | } |
241 | 598 | ||
599 | static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) | ||
600 | { | ||
601 | void __iomem *regs = sphy->regs; | ||
602 | u32 phyhost; | ||
603 | u32 phyotg; | ||
604 | u32 phyhsic; | ||
605 | |||
606 | if (atomic_dec_return(&sphy->phy_usage) > 0) { | ||
607 | dev_info(sphy->dev, "still being used\n"); | ||
608 | return; | ||
609 | } | ||
610 | |||
611 | phyhsic = (HSIC_CTRL_REFCLKDIV_12 | | ||
612 | HSIC_CTRL_REFCLKSEL | | ||
613 | HSIC_CTRL_SIDDQ | | ||
614 | HSIC_CTRL_FORCESLEEP | | ||
615 | HSIC_CTRL_FORCESUSPEND); | ||
616 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
617 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
618 | |||
619 | phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
620 | phyhost |= (HOST_CTRL0_SIDDQ | | ||
621 | HOST_CTRL0_FORCESUSPEND | | ||
622 | HOST_CTRL0_FORCESLEEP | | ||
623 | HOST_CTRL0_PHYSWRST | | ||
624 | HOST_CTRL0_PHYSWRSTALL); | ||
625 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
626 | |||
627 | phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); | ||
628 | phyotg |= (OTG_SYS_FORCESUSPEND | | ||
629 | OTG_SYS_SIDDQ_UOTG | | ||
630 | OTG_SYS_FORCESLEEP); | ||
631 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
632 | } | ||
633 | |||
242 | static void samsung_usbphy_disable(struct samsung_usbphy *sphy) | 634 | static void samsung_usbphy_disable(struct samsung_usbphy *sphy) |
243 | { | 635 | { |
244 | void __iomem *regs = sphy->regs; | 636 | void __iomem *regs = sphy->regs; |
@@ -266,11 +658,14 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) | |||
266 | static int samsung_usbphy_init(struct usb_phy *phy) | 658 | static int samsung_usbphy_init(struct usb_phy *phy) |
267 | { | 659 | { |
268 | struct samsung_usbphy *sphy; | 660 | struct samsung_usbphy *sphy; |
661 | struct usb_bus *host = NULL; | ||
269 | unsigned long flags; | 662 | unsigned long flags; |
270 | int ret = 0; | 663 | int ret = 0; |
271 | 664 | ||
272 | sphy = phy_to_sphy(phy); | 665 | sphy = phy_to_sphy(phy); |
273 | 666 | ||
667 | host = phy->otg->host; | ||
668 | |||
274 | /* Enable the phy clock */ | 669 | /* Enable the phy clock */ |
275 | ret = clk_prepare_enable(sphy->clk); | 670 | ret = clk_prepare_enable(sphy->clk); |
276 | if (ret) { | 671 | if (ret) { |
@@ -280,19 +675,35 @@ static int samsung_usbphy_init(struct usb_phy *phy) | |||
280 | 675 | ||
281 | spin_lock_irqsave(&sphy->lock, flags); | 676 | spin_lock_irqsave(&sphy->lock, flags); |
282 | 677 | ||
678 | if (host) { | ||
679 | /* setting default phy-type for USB 2.0 */ | ||
680 | if (!strstr(dev_name(host->controller), "ehci") || | ||
681 | !strstr(dev_name(host->controller), "ohci")) | ||
682 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); | ||
683 | } else { | ||
684 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); | ||
685 | } | ||
686 | |||
283 | /* Disable phy isolation */ | 687 | /* Disable phy isolation */ |
284 | if (sphy->plat && sphy->plat->pmu_isolation) | 688 | if (sphy->plat && sphy->plat->pmu_isolation) |
285 | sphy->plat->pmu_isolation(false); | 689 | sphy->plat->pmu_isolation(false); |
286 | else | 690 | else |
287 | samsung_usbphy_set_isolation(sphy, false); | 691 | samsung_usbphy_set_isolation(sphy, false); |
288 | 692 | ||
693 | /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ | ||
694 | samsung_usbphy_cfg_sel(sphy); | ||
695 | |||
289 | /* Initialize usb phy registers */ | 696 | /* Initialize usb phy registers */ |
290 | samsung_usbphy_enable(sphy); | 697 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) |
698 | samsung_exynos5_usbphy_enable(sphy); | ||
699 | else | ||
700 | samsung_usbphy_enable(sphy); | ||
291 | 701 | ||
292 | spin_unlock_irqrestore(&sphy->lock, flags); | 702 | spin_unlock_irqrestore(&sphy->lock, flags); |
293 | 703 | ||
294 | /* Disable the phy clock */ | 704 | /* Disable the phy clock */ |
295 | clk_disable_unprepare(sphy->clk); | 705 | clk_disable_unprepare(sphy->clk); |
706 | |||
296 | return ret; | 707 | return ret; |
297 | } | 708 | } |
298 | 709 | ||
@@ -302,10 +713,13 @@ static int samsung_usbphy_init(struct usb_phy *phy) | |||
302 | static void samsung_usbphy_shutdown(struct usb_phy *phy) | 713 | static void samsung_usbphy_shutdown(struct usb_phy *phy) |
303 | { | 714 | { |
304 | struct samsung_usbphy *sphy; | 715 | struct samsung_usbphy *sphy; |
716 | struct usb_bus *host = NULL; | ||
305 | unsigned long flags; | 717 | unsigned long flags; |
306 | 718 | ||
307 | sphy = phy_to_sphy(phy); | 719 | sphy = phy_to_sphy(phy); |
308 | 720 | ||
721 | host = phy->otg->host; | ||
722 | |||
309 | if (clk_prepare_enable(sphy->clk)) { | 723 | if (clk_prepare_enable(sphy->clk)) { |
310 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); | 724 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); |
311 | return; | 725 | return; |
@@ -313,8 +727,20 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) | |||
313 | 727 | ||
314 | spin_lock_irqsave(&sphy->lock, flags); | 728 | spin_lock_irqsave(&sphy->lock, flags); |
315 | 729 | ||
730 | if (host) { | ||
731 | /* setting default phy-type for USB 2.0 */ | ||
732 | if (!strstr(dev_name(host->controller), "ehci") || | ||
733 | !strstr(dev_name(host->controller), "ohci")) | ||
734 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); | ||
735 | } else { | ||
736 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); | ||
737 | } | ||
738 | |||
316 | /* De-initialize usb phy registers */ | 739 | /* De-initialize usb phy registers */ |
317 | samsung_usbphy_disable(sphy); | 740 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) |
741 | samsung_exynos5_usbphy_disable(sphy); | ||
742 | else | ||
743 | samsung_usbphy_disable(sphy); | ||
318 | 744 | ||
319 | /* Enable phy isolation */ | 745 | /* Enable phy isolation */ |
320 | if (sphy->plat && sphy->plat->pmu_isolation) | 746 | if (sphy->plat && sphy->plat->pmu_isolation) |
@@ -346,7 +772,9 @@ static inline const struct samsung_usbphy_drvdata | |||
346 | static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | 772 | static int __devinit samsung_usbphy_probe(struct platform_device *pdev) |
347 | { | 773 | { |
348 | struct samsung_usbphy *sphy; | 774 | struct samsung_usbphy *sphy; |
775 | struct usb_otg *otg; | ||
349 | struct samsung_usbphy_data *pdata = pdev->dev.platform_data; | 776 | struct samsung_usbphy_data *pdata = pdev->dev.platform_data; |
777 | const struct samsung_usbphy_drvdata *drv_data; | ||
350 | struct device *dev = &pdev->dev; | 778 | struct device *dev = &pdev->dev; |
351 | struct resource *phy_mem; | 779 | struct resource *phy_mem; |
352 | void __iomem *phy_base; | 780 | void __iomem *phy_base; |
@@ -369,7 +797,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | |||
369 | if (!sphy) | 797 | if (!sphy) |
370 | return -ENOMEM; | 798 | return -ENOMEM; |
371 | 799 | ||
372 | clk = devm_clk_get(dev, "otg"); | 800 | otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); |
801 | if (!otg) | ||
802 | return -ENOMEM; | ||
803 | |||
804 | drv_data = samsung_usbphy_get_driver_data(pdev); | ||
805 | |||
806 | if (drv_data->cpu_type == TYPE_EXYNOS5250) | ||
807 | clk = devm_clk_get(dev, "usbhost"); | ||
808 | else | ||
809 | clk = devm_clk_get(dev, "otg"); | ||
810 | |||
373 | if (IS_ERR(clk)) { | 811 | if (IS_ERR(clk)) { |
374 | dev_err(dev, "Failed to get otg clock\n"); | 812 | dev_err(dev, "Failed to get otg clock\n"); |
375 | return PTR_ERR(clk); | 813 | return PTR_ERR(clk); |
@@ -391,13 +829,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | |||
391 | sphy->plat = pdata; | 829 | sphy->plat = pdata; |
392 | sphy->regs = phy_base; | 830 | sphy->regs = phy_base; |
393 | sphy->clk = clk; | 831 | sphy->clk = clk; |
832 | sphy->drv_data = drv_data; | ||
394 | sphy->phy.dev = sphy->dev; | 833 | sphy->phy.dev = sphy->dev; |
395 | sphy->phy.label = "samsung-usbphy"; | 834 | sphy->phy.label = "samsung-usbphy"; |
396 | sphy->phy.init = samsung_usbphy_init; | 835 | sphy->phy.init = samsung_usbphy_init; |
397 | sphy->phy.shutdown = samsung_usbphy_shutdown; | 836 | sphy->phy.shutdown = samsung_usbphy_shutdown; |
398 | sphy->drv_data = samsung_usbphy_get_driver_data(pdev); | ||
399 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); | 837 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); |
400 | 838 | ||
839 | sphy->phy.otg = otg; | ||
840 | sphy->phy.otg->phy = &sphy->phy; | ||
841 | sphy->phy.otg->set_host = samsung_usbphy_set_host; | ||
842 | |||
401 | spin_lock_init(&sphy->lock); | 843 | spin_lock_init(&sphy->lock); |
402 | 844 | ||
403 | platform_set_drvdata(pdev, sphy); | 845 | platform_set_drvdata(pdev, sphy); |
@@ -413,6 +855,8 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) | |||
413 | 855 | ||
414 | if (sphy->pmuregs) | 856 | if (sphy->pmuregs) |
415 | iounmap(sphy->pmuregs); | 857 | iounmap(sphy->pmuregs); |
858 | if (sphy->sysreg) | ||
859 | iounmap(sphy->sysreg); | ||
416 | 860 | ||
417 | return 0; | 861 | return 0; |
418 | } | 862 | } |
@@ -425,6 +869,13 @@ static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { | |||
425 | static const struct samsung_usbphy_drvdata usbphy_exynos4 = { | 869 | static const struct samsung_usbphy_drvdata usbphy_exynos4 = { |
426 | .cpu_type = TYPE_EXYNOS4210, | 870 | .cpu_type = TYPE_EXYNOS4210, |
427 | .devphy_en_mask = EXYNOS_USBPHY_ENABLE, | 871 | .devphy_en_mask = EXYNOS_USBPHY_ENABLE, |
872 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
873 | }; | ||
874 | |||
875 | static struct samsung_usbphy_drvdata usbphy_exynos5 = { | ||
876 | .cpu_type = TYPE_EXYNOS5250, | ||
877 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
878 | .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, | ||
428 | }; | 879 | }; |
429 | 880 | ||
430 | #ifdef CONFIG_OF | 881 | #ifdef CONFIG_OF |
@@ -435,6 +886,9 @@ static const struct of_device_id samsung_usbphy_dt_match[] = { | |||
435 | }, { | 886 | }, { |
436 | .compatible = "samsung,exynos4210-usbphy", | 887 | .compatible = "samsung,exynos4210-usbphy", |
437 | .data = &usbphy_exynos4, | 888 | .data = &usbphy_exynos4, |
889 | }, { | ||
890 | .compatible = "samsung,exynos5250-usbphy", | ||
891 | .data = &usbphy_exynos5 | ||
438 | }, | 892 | }, |
439 | {}, | 893 | {}, |
440 | }; | 894 | }; |
@@ -448,6 +902,9 @@ static struct platform_device_id samsung_usbphy_driver_ids[] = { | |||
448 | }, { | 902 | }, { |
449 | .name = "exynos4210-usbphy", | 903 | .name = "exynos4210-usbphy", |
450 | .driver_data = (unsigned long)&usbphy_exynos4, | 904 | .driver_data = (unsigned long)&usbphy_exynos4, |
905 | }, { | ||
906 | .name = "exynos5250-usbphy", | ||
907 | .driver_data = (unsigned long)&usbphy_exynos5, | ||
451 | }, | 908 | }, |
452 | {}, | 909 | {}, |
453 | }; | 910 | }; |