diff options
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r-- | drivers/usb/phy/Kconfig | 15 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/phy-samsung-usb.c | 726 | ||||
-rw-r--r-- | drivers/usb/phy/phy-samsung-usb.h | 247 | ||||
-rw-r--r-- | drivers/usb/phy/phy-samsung-usb2.c | 509 |
5 files changed, 785 insertions, 713 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 97de6de9b4b9..e8cd52ac5c05 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig | |||
@@ -86,11 +86,18 @@ config OMAP_USB3 | |||
86 | on/off the PHY. | 86 | on/off the PHY. |
87 | 87 | ||
88 | config SAMSUNG_USBPHY | 88 | config SAMSUNG_USBPHY |
89 | bool "Samsung USB PHY controller Driver" | 89 | tristate "Samsung USB PHY Driver" |
90 | depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS | ||
91 | help | 90 | help |
92 | Enable this to support Samsung USB phy controller for samsung | 91 | Enable this to support Samsung USB phy helper driver for Samsung SoCs. |
93 | SoCs. | 92 | This driver provides common interface to interact, for Samsung USB 2.0 PHY |
93 | driver and later for Samsung USB 3.0 PHY driver. | ||
94 | |||
95 | config SAMSUNG_USB2PHY | ||
96 | tristate "Samsung USB 2.0 PHY controller Driver" | ||
97 | select SAMSUNG_USBPHY | ||
98 | help | ||
99 | Enable this to support Samsung USB 2.0 (High Speed) PHY controller | ||
100 | driver for Samsung SoCs. | ||
94 | 101 | ||
95 | config TWL4030_USB | 102 | config TWL4030_USB |
96 | tristate "TWL4030 USB Transceiver Driver" | 103 | tristate "TWL4030 USB Transceiver Driver" |
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5fb4a5d55945..8cd355f051f6 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile | |||
@@ -18,6 +18,7 @@ obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o | |||
18 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o | 18 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o |
19 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o | 19 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o |
20 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o | 20 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o |
21 | obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o | ||
21 | obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o | 22 | obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o |
22 | obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o | 23 | obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o |
23 | obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o | 24 | obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o |
diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 967101ec15fd..7b118ee5f5e4 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c | |||
@@ -1,12 +1,13 @@ | |||
1 | /* linux/drivers/usb/phy/samsung-usbphy.c | 1 | /* linux/drivers/usb/phy/phy-samsung-usb.c |
2 | * | 2 | * |
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | 3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. |
4 | * http://www.samsung.com | 4 | * http://www.samsung.com |
5 | * | 5 | * |
6 | * Author: Praveen Paneri <p.paneri@samsung.com> | 6 | * Author: Praveen Paneri <p.paneri@samsung.com> |
7 | * | 7 | * |
8 | * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and | 8 | * Samsung USB-PHY helper driver with common function calls; |
9 | * OHCI-EXYNOS controllers. | 9 | * interacts with Samsung USB 2.0 PHY controller driver and later |
10 | * with Samsung USB 3.0 PHY driver. | ||
10 | * | 11 | * |
11 | * This program is free software; you can redistribute it and/or modify | 12 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2 as | 13 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,233 +22,16 @@ | |||
21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
22 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
23 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
24 | #include <linux/delay.h> | ||
25 | #include <linux/device.h> | 25 | #include <linux/device.h> |
26 | #include <linux/err.h> | 26 | #include <linux/err.h> |
27 | #include <linux/io.h> | 27 | #include <linux/io.h> |
28 | #include <linux/of.h> | 28 | #include <linux/of.h> |
29 | #include <linux/of_address.h> | 29 | #include <linux/of_address.h> |
30 | #include <linux/usb/otg.h> | ||
31 | #include <linux/usb/samsung_usb_phy.h> | 30 | #include <linux/usb/samsung_usb_phy.h> |
32 | #include <linux/platform_data/samsung-usbphy.h> | ||
33 | 31 | ||
34 | /* Register definitions */ | 32 | #include "phy-samsung-usb.h" |
35 | 33 | ||
36 | #define SAMSUNG_PHYPWR (0x00) | 34 | int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) |
37 | |||
38 | #define PHYPWR_NORMAL_MASK (0x19 << 0) | ||
39 | #define PHYPWR_OTG_DISABLE (0x1 << 4) | ||
40 | #define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) | ||
41 | #define PHYPWR_FORCE_SUSPEND (0x1 << 1) | ||
42 | /* For Exynos4 */ | ||
43 | #define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) | ||
44 | #define PHYPWR_SLEEP_PHY0 (0x1 << 5) | ||
45 | |||
46 | #define SAMSUNG_PHYCLK (0x04) | ||
47 | |||
48 | #define PHYCLK_MODE_USB11 (0x1 << 6) | ||
49 | #define PHYCLK_EXT_OSC (0x1 << 5) | ||
50 | #define PHYCLK_COMMON_ON_N (0x1 << 4) | ||
51 | #define PHYCLK_ID_PULL (0x1 << 2) | ||
52 | #define PHYCLK_CLKSEL_MASK (0x3 << 0) | ||
53 | #define PHYCLK_CLKSEL_48M (0x0 << 0) | ||
54 | #define PHYCLK_CLKSEL_12M (0x2 << 0) | ||
55 | #define PHYCLK_CLKSEL_24M (0x3 << 0) | ||
56 | |||
57 | #define SAMSUNG_RSTCON (0x08) | ||
58 | |||
59 | #define RSTCON_PHYLINK_SWRST (0x1 << 2) | ||
60 | #define RSTCON_HLINK_SWRST (0x1 << 1) | ||
61 | #define RSTCON_SWRST (0x1 << 0) | ||
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 | |||
161 | #ifndef MHZ | ||
162 | #define MHZ (1000*1000) | ||
163 | #endif | ||
164 | |||
165 | #ifndef KHZ | ||
166 | #define KHZ (1000) | ||
167 | #endif | ||
168 | |||
169 | #define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) | ||
170 | #define S3C64XX_USBPHY_ENABLE (0x1 << 16) | ||
171 | #define EXYNOS_USBPHY_ENABLE (0x1 << 0) | ||
172 | #define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) | ||
173 | |||
174 | enum samsung_cpu_type { | ||
175 | TYPE_S3C64XX, | ||
176 | TYPE_EXYNOS4210, | ||
177 | TYPE_EXYNOS5250, | ||
178 | }; | ||
179 | |||
180 | /* | ||
181 | * struct samsung_usbphy_drvdata - driver data for various SoC variants | ||
182 | * @cpu_type: machine identifier | ||
183 | * @devphy_en_mask: device phy enable mask for PHY CONTROL register | ||
184 | * @hostphy_en_mask: host phy enable mask for PHY CONTROL register | ||
185 | * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from | ||
186 | * mapped address of system controller. | ||
187 | * @hostphy_reg_offset: offset to HOST PHY CONTROL register from | ||
188 | * mapped address of system controller. | ||
189 | * | ||
190 | * Here we have a separate mask for device type phy. | ||
191 | * Having different masks for host and device type phy helps | ||
192 | * in setting independent masks in case of SoCs like S5PV210, | ||
193 | * in which PHY0 and PHY1 enable bits belong to same register | ||
194 | * placed at position 0 and 1 respectively. | ||
195 | * Although for newer SoCs like exynos these bits belong to | ||
196 | * different registers altogether placed at position 0. | ||
197 | */ | ||
198 | struct samsung_usbphy_drvdata { | ||
199 | int cpu_type; | ||
200 | int devphy_en_mask; | ||
201 | int hostphy_en_mask; | ||
202 | u32 devphy_reg_offset; | ||
203 | u32 hostphy_reg_offset; | ||
204 | }; | ||
205 | |||
206 | /* | ||
207 | * struct samsung_usbphy - transceiver driver state | ||
208 | * @phy: transceiver structure | ||
209 | * @plat: platform data | ||
210 | * @dev: The parent device supplied to the probe function | ||
211 | * @clk: usb phy clock | ||
212 | * @regs: usb phy controller registers memory base | ||
213 | * @pmuregs: USB device PHY_CONTROL register memory base | ||
214 | * @sysreg: USB2.0 PHY_CFG register memory base | ||
215 | * @ref_clk_freq: reference clock frequency selection | ||
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 | ||
220 | * @lock: lock for phy operations | ||
221 | */ | ||
222 | struct samsung_usbphy { | ||
223 | struct usb_phy phy; | ||
224 | struct samsung_usbphy_data *plat; | ||
225 | struct device *dev; | ||
226 | struct clk *clk; | ||
227 | void __iomem *regs; | ||
228 | void __iomem *pmuregs; | ||
229 | void __iomem *sysreg; | ||
230 | int ref_clk_freq; | ||
231 | const struct samsung_usbphy_drvdata *drv_data; | ||
232 | enum samsung_usb_phy_type phy_type; | ||
233 | atomic_t phy_usage; | ||
234 | spinlock_t lock; | ||
235 | }; | ||
236 | |||
237 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) | ||
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 | |||
250 | static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) | ||
251 | { | 35 | { |
252 | struct device_node *usbphy_sys; | 36 | struct device_node *usbphy_sys; |
253 | 37 | ||
@@ -282,13 +66,14 @@ err0: | |||
282 | of_node_put(usbphy_sys); | 66 | of_node_put(usbphy_sys); |
283 | return -ENXIO; | 67 | return -ENXIO; |
284 | } | 68 | } |
69 | EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); | ||
285 | 70 | ||
286 | /* | 71 | /* |
287 | * Set isolation here for phy. | 72 | * Set isolation here for phy. |
288 | * Here 'on = true' would mean USB PHY block is isolated, hence | 73 | * Here 'on = true' would mean USB PHY block is isolated, hence |
289 | * de-activated and vice-versa. | 74 | * de-activated and vice-versa. |
290 | */ | 75 | */ |
291 | static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) | 76 | void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) |
292 | { | 77 | { |
293 | void __iomem *reg = NULL; | 78 | void __iomem *reg = NULL; |
294 | u32 reg_val; | 79 | u32 reg_val; |
@@ -336,11 +121,12 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) | |||
336 | 121 | ||
337 | writel(reg_val, reg); | 122 | writel(reg_val, reg); |
338 | } | 123 | } |
124 | EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation); | ||
339 | 125 | ||
340 | /* | 126 | /* |
341 | * Configure the mode of working of usb-phy here: HOST/DEVICE. | 127 | * Configure the mode of working of usb-phy here: HOST/DEVICE. |
342 | */ | 128 | */ |
343 | static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) | 129 | void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) |
344 | { | 130 | { |
345 | u32 reg; | 131 | u32 reg; |
346 | 132 | ||
@@ -358,13 +144,14 @@ static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) | |||
358 | 144 | ||
359 | writel(reg, sphy->sysreg); | 145 | writel(reg, sphy->sysreg); |
360 | } | 146 | } |
147 | EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); | ||
361 | 148 | ||
362 | /* | 149 | /* |
363 | * PHYs are different for USB Device and USB Host. | 150 | * PHYs are different for USB Device and USB Host. |
364 | * This make sure that correct PHY type is selected before | 151 | * This make sure that correct PHY type is selected before |
365 | * any operation on PHY. | 152 | * any operation on PHY. |
366 | */ | 153 | */ |
367 | static int samsung_usbphy_set_type(struct usb_phy *phy, | 154 | int samsung_usbphy_set_type(struct usb_phy *phy, |
368 | enum samsung_usb_phy_type phy_type) | 155 | enum samsung_usb_phy_type phy_type) |
369 | { | 156 | { |
370 | struct samsung_usbphy *sphy = phy_to_sphy(phy); | 157 | struct samsung_usbphy *sphy = phy_to_sphy(phy); |
@@ -373,11 +160,12 @@ static int samsung_usbphy_set_type(struct usb_phy *phy, | |||
373 | 160 | ||
374 | return 0; | 161 | return 0; |
375 | } | 162 | } |
163 | EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); | ||
376 | 164 | ||
377 | /* | 165 | /* |
378 | * Returns reference clock frequency selection value | 166 | * Returns reference clock frequency selection value |
379 | */ | 167 | */ |
380 | static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | 168 | int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) |
381 | { | 169 | { |
382 | struct clk *ref_clk; | 170 | struct clk *ref_clk; |
383 | int refclk_freq = 0; | 171 | int refclk_freq = 0; |
@@ -387,9 +175,9 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | |||
387 | * external crystal clock XXTI | 175 | * external crystal clock XXTI |
388 | */ | 176 | */ |
389 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | 177 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) |
390 | ref_clk = clk_get(sphy->dev, "ext_xtal"); | 178 | ref_clk = devm_clk_get(sphy->dev, "ext_xtal"); |
391 | else | 179 | else |
392 | ref_clk = clk_get(sphy->dev, "xusbxti"); | 180 | ref_clk = devm_clk_get(sphy->dev, "xusbxti"); |
393 | if (IS_ERR(ref_clk)) { | 181 | if (IS_ERR(ref_clk)) { |
394 | dev_err(sphy->dev, "Failed to get reference clock\n"); | 182 | dev_err(sphy->dev, "Failed to get reference clock\n"); |
395 | return PTR_ERR(ref_clk); | 183 | return PTR_ERR(ref_clk); |
@@ -445,484 +233,4 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | |||
445 | 233 | ||
446 | return refclk_freq; | 234 | return refclk_freq; |
447 | } | 235 | } |
448 | 236 | EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); | |
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 | |||
564 | static void samsung_usbphy_enable(struct samsung_usbphy *sphy) | ||
565 | { | ||
566 | void __iomem *regs = sphy->regs; | ||
567 | u32 phypwr; | ||
568 | u32 phyclk; | ||
569 | u32 rstcon; | ||
570 | |||
571 | /* set clock frequency for PLL */ | ||
572 | phyclk = sphy->ref_clk_freq; | ||
573 | phypwr = readl(regs + SAMSUNG_PHYPWR); | ||
574 | rstcon = readl(regs + SAMSUNG_RSTCON); | ||
575 | |||
576 | switch (sphy->drv_data->cpu_type) { | ||
577 | case TYPE_S3C64XX: | ||
578 | phyclk &= ~PHYCLK_COMMON_ON_N; | ||
579 | phypwr &= ~PHYPWR_NORMAL_MASK; | ||
580 | rstcon |= RSTCON_SWRST; | ||
581 | break; | ||
582 | case TYPE_EXYNOS4210: | ||
583 | phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; | ||
584 | rstcon |= RSTCON_SWRST; | ||
585 | default: | ||
586 | break; | ||
587 | } | ||
588 | |||
589 | writel(phyclk, regs + SAMSUNG_PHYCLK); | ||
590 | /* Configure PHY0 for normal operation*/ | ||
591 | writel(phypwr, regs + SAMSUNG_PHYPWR); | ||
592 | /* reset all ports of PHY and Link */ | ||
593 | writel(rstcon, regs + SAMSUNG_RSTCON); | ||
594 | udelay(10); | ||
595 | rstcon &= ~RSTCON_SWRST; | ||
596 | writel(rstcon, regs + SAMSUNG_RSTCON); | ||
597 | } | ||
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 | |||
634 | static void samsung_usbphy_disable(struct samsung_usbphy *sphy) | ||
635 | { | ||
636 | void __iomem *regs = sphy->regs; | ||
637 | u32 phypwr; | ||
638 | |||
639 | phypwr = readl(regs + SAMSUNG_PHYPWR); | ||
640 | |||
641 | switch (sphy->drv_data->cpu_type) { | ||
642 | case TYPE_S3C64XX: | ||
643 | phypwr |= PHYPWR_NORMAL_MASK; | ||
644 | break; | ||
645 | case TYPE_EXYNOS4210: | ||
646 | phypwr |= PHYPWR_NORMAL_MASK_PHY0; | ||
647 | default: | ||
648 | break; | ||
649 | } | ||
650 | |||
651 | /* Disable analog and otg block power */ | ||
652 | writel(phypwr, regs + SAMSUNG_PHYPWR); | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * The function passed to the usb driver for phy initialization | ||
657 | */ | ||
658 | static int samsung_usbphy_init(struct usb_phy *phy) | ||
659 | { | ||
660 | struct samsung_usbphy *sphy; | ||
661 | struct usb_bus *host = NULL; | ||
662 | unsigned long flags; | ||
663 | int ret = 0; | ||
664 | |||
665 | sphy = phy_to_sphy(phy); | ||
666 | |||
667 | host = phy->otg->host; | ||
668 | |||
669 | /* Enable the phy clock */ | ||
670 | ret = clk_prepare_enable(sphy->clk); | ||
671 | if (ret) { | ||
672 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); | ||
673 | return ret; | ||
674 | } | ||
675 | |||
676 | spin_lock_irqsave(&sphy->lock, flags); | ||
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 | |||
687 | /* Disable phy isolation */ | ||
688 | if (sphy->plat && sphy->plat->pmu_isolation) | ||
689 | sphy->plat->pmu_isolation(false); | ||
690 | else | ||
691 | samsung_usbphy_set_isolation(sphy, false); | ||
692 | |||
693 | /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ | ||
694 | samsung_usbphy_cfg_sel(sphy); | ||
695 | |||
696 | /* Initialize usb phy registers */ | ||
697 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | ||
698 | samsung_exynos5_usbphy_enable(sphy); | ||
699 | else | ||
700 | samsung_usbphy_enable(sphy); | ||
701 | |||
702 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
703 | |||
704 | /* Disable the phy clock */ | ||
705 | clk_disable_unprepare(sphy->clk); | ||
706 | |||
707 | return ret; | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * The function passed to the usb driver for phy shutdown | ||
712 | */ | ||
713 | static void samsung_usbphy_shutdown(struct usb_phy *phy) | ||
714 | { | ||
715 | struct samsung_usbphy *sphy; | ||
716 | struct usb_bus *host = NULL; | ||
717 | unsigned long flags; | ||
718 | |||
719 | sphy = phy_to_sphy(phy); | ||
720 | |||
721 | host = phy->otg->host; | ||
722 | |||
723 | if (clk_prepare_enable(sphy->clk)) { | ||
724 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | spin_lock_irqsave(&sphy->lock, flags); | ||
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 | |||
739 | /* De-initialize usb phy registers */ | ||
740 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | ||
741 | samsung_exynos5_usbphy_disable(sphy); | ||
742 | else | ||
743 | samsung_usbphy_disable(sphy); | ||
744 | |||
745 | /* Enable phy isolation */ | ||
746 | if (sphy->plat && sphy->plat->pmu_isolation) | ||
747 | sphy->plat->pmu_isolation(true); | ||
748 | else | ||
749 | samsung_usbphy_set_isolation(sphy, true); | ||
750 | |||
751 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
752 | |||
753 | clk_disable_unprepare(sphy->clk); | ||
754 | } | ||
755 | |||
756 | static const struct of_device_id samsung_usbphy_dt_match[]; | ||
757 | |||
758 | static inline const struct samsung_usbphy_drvdata | ||
759 | *samsung_usbphy_get_driver_data(struct platform_device *pdev) | ||
760 | { | ||
761 | if (pdev->dev.of_node) { | ||
762 | const struct of_device_id *match; | ||
763 | match = of_match_node(samsung_usbphy_dt_match, | ||
764 | pdev->dev.of_node); | ||
765 | return match->data; | ||
766 | } | ||
767 | |||
768 | return (struct samsung_usbphy_drvdata *) | ||
769 | platform_get_device_id(pdev)->driver_data; | ||
770 | } | ||
771 | |||
772 | static int samsung_usbphy_probe(struct platform_device *pdev) | ||
773 | { | ||
774 | struct samsung_usbphy *sphy; | ||
775 | struct usb_otg *otg; | ||
776 | struct samsung_usbphy_data *pdata = pdev->dev.platform_data; | ||
777 | const struct samsung_usbphy_drvdata *drv_data; | ||
778 | struct device *dev = &pdev->dev; | ||
779 | struct resource *phy_mem; | ||
780 | void __iomem *phy_base; | ||
781 | struct clk *clk; | ||
782 | int ret; | ||
783 | |||
784 | phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
785 | if (!phy_mem) { | ||
786 | dev_err(dev, "%s: missing mem resource\n", __func__); | ||
787 | return -ENODEV; | ||
788 | } | ||
789 | |||
790 | phy_base = devm_ioremap_resource(dev, phy_mem); | ||
791 | if (IS_ERR(phy_base)) | ||
792 | return PTR_ERR(phy_base); | ||
793 | |||
794 | sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); | ||
795 | if (!sphy) | ||
796 | return -ENOMEM; | ||
797 | |||
798 | otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); | ||
799 | if (!otg) | ||
800 | return -ENOMEM; | ||
801 | |||
802 | drv_data = samsung_usbphy_get_driver_data(pdev); | ||
803 | |||
804 | if (drv_data->cpu_type == TYPE_EXYNOS5250) | ||
805 | clk = devm_clk_get(dev, "usbhost"); | ||
806 | else | ||
807 | clk = devm_clk_get(dev, "otg"); | ||
808 | |||
809 | if (IS_ERR(clk)) { | ||
810 | dev_err(dev, "Failed to get otg clock\n"); | ||
811 | return PTR_ERR(clk); | ||
812 | } | ||
813 | |||
814 | sphy->dev = dev; | ||
815 | |||
816 | if (dev->of_node) { | ||
817 | ret = samsung_usbphy_parse_dt(sphy); | ||
818 | if (ret < 0) | ||
819 | return ret; | ||
820 | } else { | ||
821 | if (!pdata) { | ||
822 | dev_err(dev, "no platform data specified\n"); | ||
823 | return -EINVAL; | ||
824 | } | ||
825 | } | ||
826 | |||
827 | sphy->plat = pdata; | ||
828 | sphy->regs = phy_base; | ||
829 | sphy->clk = clk; | ||
830 | sphy->drv_data = drv_data; | ||
831 | sphy->phy.dev = sphy->dev; | ||
832 | sphy->phy.label = "samsung-usbphy"; | ||
833 | sphy->phy.init = samsung_usbphy_init; | ||
834 | sphy->phy.shutdown = samsung_usbphy_shutdown; | ||
835 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); | ||
836 | |||
837 | sphy->phy.otg = otg; | ||
838 | sphy->phy.otg->phy = &sphy->phy; | ||
839 | sphy->phy.otg->set_host = samsung_usbphy_set_host; | ||
840 | |||
841 | spin_lock_init(&sphy->lock); | ||
842 | |||
843 | platform_set_drvdata(pdev, sphy); | ||
844 | |||
845 | return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); | ||
846 | } | ||
847 | |||
848 | static int samsung_usbphy_remove(struct platform_device *pdev) | ||
849 | { | ||
850 | struct samsung_usbphy *sphy = platform_get_drvdata(pdev); | ||
851 | |||
852 | usb_remove_phy(&sphy->phy); | ||
853 | |||
854 | if (sphy->pmuregs) | ||
855 | iounmap(sphy->pmuregs); | ||
856 | if (sphy->sysreg) | ||
857 | iounmap(sphy->sysreg); | ||
858 | |||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { | ||
863 | .cpu_type = TYPE_S3C64XX, | ||
864 | .devphy_en_mask = S3C64XX_USBPHY_ENABLE, | ||
865 | }; | ||
866 | |||
867 | static const struct samsung_usbphy_drvdata usbphy_exynos4 = { | ||
868 | .cpu_type = TYPE_EXYNOS4210, | ||
869 | .devphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
870 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
871 | }; | ||
872 | |||
873 | static struct samsung_usbphy_drvdata usbphy_exynos5 = { | ||
874 | .cpu_type = TYPE_EXYNOS5250, | ||
875 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
876 | .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, | ||
877 | }; | ||
878 | |||
879 | #ifdef CONFIG_OF | ||
880 | static const struct of_device_id samsung_usbphy_dt_match[] = { | ||
881 | { | ||
882 | .compatible = "samsung,s3c64xx-usbphy", | ||
883 | .data = &usbphy_s3c64xx, | ||
884 | }, { | ||
885 | .compatible = "samsung,exynos4210-usbphy", | ||
886 | .data = &usbphy_exynos4, | ||
887 | }, { | ||
888 | .compatible = "samsung,exynos5250-usbphy", | ||
889 | .data = &usbphy_exynos5 | ||
890 | }, | ||
891 | {}, | ||
892 | }; | ||
893 | MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); | ||
894 | #endif | ||
895 | |||
896 | static struct platform_device_id samsung_usbphy_driver_ids[] = { | ||
897 | { | ||
898 | .name = "s3c64xx-usbphy", | ||
899 | .driver_data = (unsigned long)&usbphy_s3c64xx, | ||
900 | }, { | ||
901 | .name = "exynos4210-usbphy", | ||
902 | .driver_data = (unsigned long)&usbphy_exynos4, | ||
903 | }, { | ||
904 | .name = "exynos5250-usbphy", | ||
905 | .driver_data = (unsigned long)&usbphy_exynos5, | ||
906 | }, | ||
907 | {}, | ||
908 | }; | ||
909 | |||
910 | MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); | ||
911 | |||
912 | static struct platform_driver samsung_usbphy_driver = { | ||
913 | .probe = samsung_usbphy_probe, | ||
914 | .remove = samsung_usbphy_remove, | ||
915 | .id_table = samsung_usbphy_driver_ids, | ||
916 | .driver = { | ||
917 | .name = "samsung-usbphy", | ||
918 | .owner = THIS_MODULE, | ||
919 | .of_match_table = of_match_ptr(samsung_usbphy_dt_match), | ||
920 | }, | ||
921 | }; | ||
922 | |||
923 | module_platform_driver(samsung_usbphy_driver); | ||
924 | |||
925 | MODULE_DESCRIPTION("Samsung USB phy controller"); | ||
926 | MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>"); | ||
927 | MODULE_LICENSE("GPL"); | ||
928 | MODULE_ALIAS("platform:samsung-usbphy"); | ||
diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h new file mode 100644 index 000000000000..481737d743d5 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb.h | |||
@@ -0,0 +1,247 @@ | |||
1 | /* linux/drivers/usb/phy/phy-samsung-usb.h | ||
2 | * | ||
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and | ||
7 | * OHCI-EXYNOS controllers. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/usb/phy.h> | ||
20 | |||
21 | /* Register definitions */ | ||
22 | |||
23 | #define SAMSUNG_PHYPWR (0x00) | ||
24 | |||
25 | #define PHYPWR_NORMAL_MASK (0x19 << 0) | ||
26 | #define PHYPWR_OTG_DISABLE (0x1 << 4) | ||
27 | #define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) | ||
28 | #define PHYPWR_FORCE_SUSPEND (0x1 << 1) | ||
29 | /* For Exynos4 */ | ||
30 | #define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) | ||
31 | #define PHYPWR_SLEEP_PHY0 (0x1 << 5) | ||
32 | |||
33 | #define SAMSUNG_PHYCLK (0x04) | ||
34 | |||
35 | #define PHYCLK_MODE_USB11 (0x1 << 6) | ||
36 | #define PHYCLK_EXT_OSC (0x1 << 5) | ||
37 | #define PHYCLK_COMMON_ON_N (0x1 << 4) | ||
38 | #define PHYCLK_ID_PULL (0x1 << 2) | ||
39 | #define PHYCLK_CLKSEL_MASK (0x3 << 0) | ||
40 | #define PHYCLK_CLKSEL_48M (0x0 << 0) | ||
41 | #define PHYCLK_CLKSEL_12M (0x2 << 0) | ||
42 | #define PHYCLK_CLKSEL_24M (0x3 << 0) | ||
43 | |||
44 | #define SAMSUNG_RSTCON (0x08) | ||
45 | |||
46 | #define RSTCON_PHYLINK_SWRST (0x1 << 2) | ||
47 | #define RSTCON_HLINK_SWRST (0x1 << 1) | ||
48 | #define RSTCON_SWRST (0x1 << 0) | ||
49 | |||
50 | /* EXYNOS5 */ | ||
51 | #define EXYNOS5_PHY_HOST_CTRL0 (0x00) | ||
52 | |||
53 | #define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) | ||
54 | |||
55 | #define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) | ||
56 | #define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) | ||
57 | #define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) | ||
58 | #define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) | ||
59 | |||
60 | #define HOST_CTRL0_FSEL_MASK (0x7 << 16) | ||
61 | #define HOST_CTRL0_FSEL(_x) ((_x) << 16) | ||
62 | |||
63 | #define FSEL_CLKSEL_50M (0x7) | ||
64 | #define FSEL_CLKSEL_24M (0x5) | ||
65 | #define FSEL_CLKSEL_20M (0x4) | ||
66 | #define FSEL_CLKSEL_19200K (0x3) | ||
67 | #define FSEL_CLKSEL_12M (0x2) | ||
68 | #define FSEL_CLKSEL_10M (0x1) | ||
69 | #define FSEL_CLKSEL_9600K (0x0) | ||
70 | |||
71 | #define HOST_CTRL0_TESTBURNIN (0x1 << 11) | ||
72 | #define HOST_CTRL0_RETENABLE (0x1 << 10) | ||
73 | #define HOST_CTRL0_COMMONON_N (0x1 << 9) | ||
74 | #define HOST_CTRL0_SIDDQ (0x1 << 6) | ||
75 | #define HOST_CTRL0_FORCESLEEP (0x1 << 5) | ||
76 | #define HOST_CTRL0_FORCESUSPEND (0x1 << 4) | ||
77 | #define HOST_CTRL0_WORDINTERFACE (0x1 << 3) | ||
78 | #define HOST_CTRL0_UTMISWRST (0x1 << 2) | ||
79 | #define HOST_CTRL0_LINKSWRST (0x1 << 1) | ||
80 | #define HOST_CTRL0_PHYSWRST (0x1 << 0) | ||
81 | |||
82 | #define EXYNOS5_PHY_HOST_TUNE0 (0x04) | ||
83 | |||
84 | #define EXYNOS5_PHY_HSIC_CTRL1 (0x10) | ||
85 | |||
86 | #define EXYNOS5_PHY_HSIC_TUNE1 (0x14) | ||
87 | |||
88 | #define EXYNOS5_PHY_HSIC_CTRL2 (0x20) | ||
89 | |||
90 | #define EXYNOS5_PHY_HSIC_TUNE2 (0x24) | ||
91 | |||
92 | #define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) | ||
93 | #define HSIC_CTRL_REFCLKSEL (0x2 << 23) | ||
94 | |||
95 | #define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) | ||
96 | #define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) | ||
97 | #define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) | ||
98 | #define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) | ||
99 | #define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) | ||
100 | #define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) | ||
101 | #define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) | ||
102 | |||
103 | #define HSIC_CTRL_SIDDQ (0x1 << 6) | ||
104 | #define HSIC_CTRL_FORCESLEEP (0x1 << 5) | ||
105 | #define HSIC_CTRL_FORCESUSPEND (0x1 << 4) | ||
106 | #define HSIC_CTRL_WORDINTERFACE (0x1 << 3) | ||
107 | #define HSIC_CTRL_UTMISWRST (0x1 << 2) | ||
108 | #define HSIC_CTRL_PHYSWRST (0x1 << 0) | ||
109 | |||
110 | #define EXYNOS5_PHY_HOST_EHCICTRL (0x30) | ||
111 | |||
112 | #define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) | ||
113 | #define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) | ||
114 | #define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) | ||
115 | #define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) | ||
116 | |||
117 | #define EXYNOS5_PHY_HOST_OHCICTRL (0x34) | ||
118 | |||
119 | #define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) | ||
120 | #define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) | ||
121 | #define HOST_OHCICTRL_CNTSEL (0x1 << 1) | ||
122 | #define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) | ||
123 | |||
124 | #define EXYNOS5_PHY_OTG_SYS (0x38) | ||
125 | |||
126 | #define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) | ||
127 | #define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) | ||
128 | #define OTG_SYS_PHY0_SWRST (0x1 << 12) | ||
129 | |||
130 | #define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) | ||
131 | #define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) | ||
132 | #define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) | ||
133 | #define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) | ||
134 | |||
135 | #define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) | ||
136 | #define OTG_SYS_COMMON_ON (0x1 << 7) | ||
137 | |||
138 | #define OTG_SYS_FSEL_MASK (0x7 << 4) | ||
139 | #define OTG_SYS_FSEL(_x) ((_x) << 4) | ||
140 | |||
141 | #define OTG_SYS_FORCESLEEP (0x1 << 3) | ||
142 | #define OTG_SYS_OTGDISABLE (0x1 << 2) | ||
143 | #define OTG_SYS_SIDDQ_UOTG (0x1 << 1) | ||
144 | #define OTG_SYS_FORCESUSPEND (0x1 << 0) | ||
145 | |||
146 | #define EXYNOS5_PHY_OTG_TUNE (0x40) | ||
147 | |||
148 | #ifndef MHZ | ||
149 | #define MHZ (1000*1000) | ||
150 | #endif | ||
151 | |||
152 | #ifndef KHZ | ||
153 | #define KHZ (1000) | ||
154 | #endif | ||
155 | |||
156 | #define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) | ||
157 | #define S3C64XX_USBPHY_ENABLE (0x1 << 16) | ||
158 | #define EXYNOS_USBPHY_ENABLE (0x1 << 0) | ||
159 | #define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) | ||
160 | |||
161 | enum samsung_cpu_type { | ||
162 | TYPE_S3C64XX, | ||
163 | TYPE_EXYNOS4210, | ||
164 | TYPE_EXYNOS5250, | ||
165 | }; | ||
166 | |||
167 | /* | ||
168 | * struct samsung_usbphy_drvdata - driver data for various SoC variants | ||
169 | * @cpu_type: machine identifier | ||
170 | * @devphy_en_mask: device phy enable mask for PHY CONTROL register | ||
171 | * @hostphy_en_mask: host phy enable mask for PHY CONTROL register | ||
172 | * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from | ||
173 | * mapped address of system controller. | ||
174 | * @hostphy_reg_offset: offset to HOST PHY CONTROL register from | ||
175 | * mapped address of system controller. | ||
176 | * | ||
177 | * Here we have a separate mask for device type phy. | ||
178 | * Having different masks for host and device type phy helps | ||
179 | * in setting independent masks in case of SoCs like S5PV210, | ||
180 | * in which PHY0 and PHY1 enable bits belong to same register | ||
181 | * placed at position 0 and 1 respectively. | ||
182 | * Although for newer SoCs like exynos these bits belong to | ||
183 | * different registers altogether placed at position 0. | ||
184 | */ | ||
185 | struct samsung_usbphy_drvdata { | ||
186 | int cpu_type; | ||
187 | int devphy_en_mask; | ||
188 | int hostphy_en_mask; | ||
189 | u32 devphy_reg_offset; | ||
190 | u32 hostphy_reg_offset; | ||
191 | }; | ||
192 | |||
193 | /* | ||
194 | * struct samsung_usbphy - transceiver driver state | ||
195 | * @phy: transceiver structure | ||
196 | * @plat: platform data | ||
197 | * @dev: The parent device supplied to the probe function | ||
198 | * @clk: usb phy clock | ||
199 | * @regs: usb phy controller registers memory base | ||
200 | * @pmuregs: USB device PHY_CONTROL register memory base | ||
201 | * @sysreg: USB2.0 PHY_CFG register memory base | ||
202 | * @ref_clk_freq: reference clock frequency selection | ||
203 | * @drv_data: driver data available for different SoCs | ||
204 | * @phy_type: Samsung SoCs specific phy types: #HOST | ||
205 | * #DEVICE | ||
206 | * @phy_usage: usage count for phy | ||
207 | * @lock: lock for phy operations | ||
208 | */ | ||
209 | struct samsung_usbphy { | ||
210 | struct usb_phy phy; | ||
211 | struct samsung_usbphy_data *plat; | ||
212 | struct device *dev; | ||
213 | struct clk *clk; | ||
214 | void __iomem *regs; | ||
215 | void __iomem *pmuregs; | ||
216 | void __iomem *sysreg; | ||
217 | int ref_clk_freq; | ||
218 | const struct samsung_usbphy_drvdata *drv_data; | ||
219 | enum samsung_usb_phy_type phy_type; | ||
220 | atomic_t phy_usage; | ||
221 | spinlock_t lock; | ||
222 | }; | ||
223 | |||
224 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) | ||
225 | |||
226 | static const struct of_device_id samsung_usbphy_dt_match[]; | ||
227 | |||
228 | static inline const struct samsung_usbphy_drvdata | ||
229 | *samsung_usbphy_get_driver_data(struct platform_device *pdev) | ||
230 | { | ||
231 | if (pdev->dev.of_node) { | ||
232 | const struct of_device_id *match; | ||
233 | match = of_match_node(samsung_usbphy_dt_match, | ||
234 | pdev->dev.of_node); | ||
235 | return match->data; | ||
236 | } | ||
237 | |||
238 | return (struct samsung_usbphy_drvdata *) | ||
239 | platform_get_device_id(pdev)->driver_data; | ||
240 | } | ||
241 | |||
242 | extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy); | ||
243 | extern void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on); | ||
244 | extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy); | ||
245 | extern int samsung_usbphy_set_type(struct usb_phy *phy, | ||
246 | enum samsung_usb_phy_type phy_type); | ||
247 | extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy); | ||
diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c new file mode 100644 index 000000000000..dce968151505 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb2.c | |||
@@ -0,0 +1,509 @@ | |||
1 | /* linux/drivers/usb/phy/phy-samsung-usb2.c | ||
2 | * | ||
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * Author: Praveen Paneri <p.paneri@samsung.com> | ||
7 | * | ||
8 | * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and | ||
9 | * OHCI-EXYNOS controllers. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/of.h> | ||
29 | #include <linux/usb/otg.h> | ||
30 | #include <linux/usb/samsung_usb_phy.h> | ||
31 | #include <linux/platform_data/samsung-usbphy.h> | ||
32 | |||
33 | #include "phy-samsung-usb.h" | ||
34 | |||
35 | static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) | ||
36 | { | ||
37 | if (!otg) | ||
38 | return -ENODEV; | ||
39 | |||
40 | if (!otg->host) | ||
41 | otg->host = host; | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static bool exynos5_phyhost_is_on(void *regs) | ||
47 | { | ||
48 | u32 reg; | ||
49 | |||
50 | reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
51 | |||
52 | return !(reg & HOST_CTRL0_SIDDQ); | ||
53 | } | ||
54 | |||
55 | static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) | ||
56 | { | ||
57 | void __iomem *regs = sphy->regs; | ||
58 | u32 phyclk = sphy->ref_clk_freq; | ||
59 | u32 phyhost; | ||
60 | u32 phyotg; | ||
61 | u32 phyhsic; | ||
62 | u32 ehcictrl; | ||
63 | u32 ohcictrl; | ||
64 | |||
65 | /* | ||
66 | * phy_usage helps in keeping usage count for phy | ||
67 | * so that the first consumer enabling the phy is also | ||
68 | * the last consumer to disable it. | ||
69 | */ | ||
70 | |||
71 | atomic_inc(&sphy->phy_usage); | ||
72 | |||
73 | if (exynos5_phyhost_is_on(regs)) { | ||
74 | dev_info(sphy->dev, "Already power on PHY\n"); | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | /* Host configuration */ | ||
79 | phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
80 | |||
81 | /* phy reference clock configuration */ | ||
82 | phyhost &= ~HOST_CTRL0_FSEL_MASK; | ||
83 | phyhost |= HOST_CTRL0_FSEL(phyclk); | ||
84 | |||
85 | /* host phy reset */ | ||
86 | phyhost &= ~(HOST_CTRL0_PHYSWRST | | ||
87 | HOST_CTRL0_PHYSWRSTALL | | ||
88 | HOST_CTRL0_SIDDQ | | ||
89 | /* Enable normal mode of operation */ | ||
90 | HOST_CTRL0_FORCESUSPEND | | ||
91 | HOST_CTRL0_FORCESLEEP); | ||
92 | |||
93 | /* Link reset */ | ||
94 | phyhost |= (HOST_CTRL0_LINKSWRST | | ||
95 | HOST_CTRL0_UTMISWRST | | ||
96 | /* COMMON Block configuration during suspend */ | ||
97 | HOST_CTRL0_COMMONON_N); | ||
98 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
99 | udelay(10); | ||
100 | phyhost &= ~(HOST_CTRL0_LINKSWRST | | ||
101 | HOST_CTRL0_UTMISWRST); | ||
102 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
103 | |||
104 | /* OTG configuration */ | ||
105 | phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); | ||
106 | |||
107 | /* phy reference clock configuration */ | ||
108 | phyotg &= ~OTG_SYS_FSEL_MASK; | ||
109 | phyotg |= OTG_SYS_FSEL(phyclk); | ||
110 | |||
111 | /* Enable normal mode of operation */ | ||
112 | phyotg &= ~(OTG_SYS_FORCESUSPEND | | ||
113 | OTG_SYS_SIDDQ_UOTG | | ||
114 | OTG_SYS_FORCESLEEP | | ||
115 | OTG_SYS_REFCLKSEL_MASK | | ||
116 | /* COMMON Block configuration during suspend */ | ||
117 | OTG_SYS_COMMON_ON); | ||
118 | |||
119 | /* OTG phy & link reset */ | ||
120 | phyotg |= (OTG_SYS_PHY0_SWRST | | ||
121 | OTG_SYS_LINKSWRST_UOTG | | ||
122 | OTG_SYS_PHYLINK_SWRESET | | ||
123 | OTG_SYS_OTGDISABLE | | ||
124 | /* Set phy refclk */ | ||
125 | OTG_SYS_REFCLKSEL_CLKCORE); | ||
126 | |||
127 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
128 | udelay(10); | ||
129 | phyotg &= ~(OTG_SYS_PHY0_SWRST | | ||
130 | OTG_SYS_LINKSWRST_UOTG | | ||
131 | OTG_SYS_PHYLINK_SWRESET); | ||
132 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
133 | |||
134 | /* HSIC phy configuration */ | ||
135 | phyhsic = (HSIC_CTRL_REFCLKDIV_12 | | ||
136 | HSIC_CTRL_REFCLKSEL | | ||
137 | HSIC_CTRL_PHYSWRST); | ||
138 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
139 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
140 | udelay(10); | ||
141 | phyhsic &= ~HSIC_CTRL_PHYSWRST; | ||
142 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
143 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
144 | |||
145 | udelay(80); | ||
146 | |||
147 | /* enable EHCI DMA burst */ | ||
148 | ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); | ||
149 | ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | | ||
150 | HOST_EHCICTRL_ENAINCR4 | | ||
151 | HOST_EHCICTRL_ENAINCR8 | | ||
152 | HOST_EHCICTRL_ENAINCR16); | ||
153 | writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); | ||
154 | |||
155 | /* set ohci_suspend_on_n */ | ||
156 | ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); | ||
157 | ohcictrl |= HOST_OHCICTRL_SUSPLGCY; | ||
158 | writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); | ||
159 | } | ||
160 | |||
161 | static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) | ||
162 | { | ||
163 | void __iomem *regs = sphy->regs; | ||
164 | u32 phypwr; | ||
165 | u32 phyclk; | ||
166 | u32 rstcon; | ||
167 | |||
168 | /* set clock frequency for PLL */ | ||
169 | phyclk = sphy->ref_clk_freq; | ||
170 | phypwr = readl(regs + SAMSUNG_PHYPWR); | ||
171 | rstcon = readl(regs + SAMSUNG_RSTCON); | ||
172 | |||
173 | switch (sphy->drv_data->cpu_type) { | ||
174 | case TYPE_S3C64XX: | ||
175 | phyclk &= ~PHYCLK_COMMON_ON_N; | ||
176 | phypwr &= ~PHYPWR_NORMAL_MASK; | ||
177 | rstcon |= RSTCON_SWRST; | ||
178 | break; | ||
179 | case TYPE_EXYNOS4210: | ||
180 | phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; | ||
181 | rstcon |= RSTCON_SWRST; | ||
182 | default: | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | writel(phyclk, regs + SAMSUNG_PHYCLK); | ||
187 | /* Configure PHY0 for normal operation*/ | ||
188 | writel(phypwr, regs + SAMSUNG_PHYPWR); | ||
189 | /* reset all ports of PHY and Link */ | ||
190 | writel(rstcon, regs + SAMSUNG_RSTCON); | ||
191 | udelay(10); | ||
192 | rstcon &= ~RSTCON_SWRST; | ||
193 | writel(rstcon, regs + SAMSUNG_RSTCON); | ||
194 | } | ||
195 | |||
196 | static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) | ||
197 | { | ||
198 | void __iomem *regs = sphy->regs; | ||
199 | u32 phyhost; | ||
200 | u32 phyotg; | ||
201 | u32 phyhsic; | ||
202 | |||
203 | if (atomic_dec_return(&sphy->phy_usage) > 0) { | ||
204 | dev_info(sphy->dev, "still being used\n"); | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | phyhsic = (HSIC_CTRL_REFCLKDIV_12 | | ||
209 | HSIC_CTRL_REFCLKSEL | | ||
210 | HSIC_CTRL_SIDDQ | | ||
211 | HSIC_CTRL_FORCESLEEP | | ||
212 | HSIC_CTRL_FORCESUSPEND); | ||
213 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); | ||
214 | writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); | ||
215 | |||
216 | phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); | ||
217 | phyhost |= (HOST_CTRL0_SIDDQ | | ||
218 | HOST_CTRL0_FORCESUSPEND | | ||
219 | HOST_CTRL0_FORCESLEEP | | ||
220 | HOST_CTRL0_PHYSWRST | | ||
221 | HOST_CTRL0_PHYSWRSTALL); | ||
222 | writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); | ||
223 | |||
224 | phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); | ||
225 | phyotg |= (OTG_SYS_FORCESUSPEND | | ||
226 | OTG_SYS_SIDDQ_UOTG | | ||
227 | OTG_SYS_FORCESLEEP); | ||
228 | writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); | ||
229 | } | ||
230 | |||
231 | static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) | ||
232 | { | ||
233 | void __iomem *regs = sphy->regs; | ||
234 | u32 phypwr; | ||
235 | |||
236 | phypwr = readl(regs + SAMSUNG_PHYPWR); | ||
237 | |||
238 | switch (sphy->drv_data->cpu_type) { | ||
239 | case TYPE_S3C64XX: | ||
240 | phypwr |= PHYPWR_NORMAL_MASK; | ||
241 | break; | ||
242 | case TYPE_EXYNOS4210: | ||
243 | phypwr |= PHYPWR_NORMAL_MASK_PHY0; | ||
244 | default: | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | /* Disable analog and otg block power */ | ||
249 | writel(phypwr, regs + SAMSUNG_PHYPWR); | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * The function passed to the usb driver for phy initialization | ||
254 | */ | ||
255 | static int samsung_usb2phy_init(struct usb_phy *phy) | ||
256 | { | ||
257 | struct samsung_usbphy *sphy; | ||
258 | struct usb_bus *host = NULL; | ||
259 | unsigned long flags; | ||
260 | int ret = 0; | ||
261 | |||
262 | sphy = phy_to_sphy(phy); | ||
263 | |||
264 | host = phy->otg->host; | ||
265 | |||
266 | /* Enable the phy clock */ | ||
267 | ret = clk_prepare_enable(sphy->clk); | ||
268 | if (ret) { | ||
269 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | spin_lock_irqsave(&sphy->lock, flags); | ||
274 | |||
275 | if (host) { | ||
276 | /* setting default phy-type for USB 2.0 */ | ||
277 | if (!strstr(dev_name(host->controller), "ehci") || | ||
278 | !strstr(dev_name(host->controller), "ohci")) | ||
279 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); | ||
280 | } else { | ||
281 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); | ||
282 | } | ||
283 | |||
284 | /* Disable phy isolation */ | ||
285 | if (sphy->plat && sphy->plat->pmu_isolation) | ||
286 | sphy->plat->pmu_isolation(false); | ||
287 | else | ||
288 | samsung_usbphy_set_isolation(sphy, false); | ||
289 | |||
290 | /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ | ||
291 | samsung_usbphy_cfg_sel(sphy); | ||
292 | |||
293 | /* Initialize usb phy registers */ | ||
294 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | ||
295 | samsung_exynos5_usb2phy_enable(sphy); | ||
296 | else | ||
297 | samsung_usb2phy_enable(sphy); | ||
298 | |||
299 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
300 | |||
301 | /* Disable the phy clock */ | ||
302 | clk_disable_unprepare(sphy->clk); | ||
303 | |||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * The function passed to the usb driver for phy shutdown | ||
309 | */ | ||
310 | static void samsung_usb2phy_shutdown(struct usb_phy *phy) | ||
311 | { | ||
312 | struct samsung_usbphy *sphy; | ||
313 | struct usb_bus *host = NULL; | ||
314 | unsigned long flags; | ||
315 | |||
316 | sphy = phy_to_sphy(phy); | ||
317 | |||
318 | host = phy->otg->host; | ||
319 | |||
320 | if (clk_prepare_enable(sphy->clk)) { | ||
321 | dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | spin_lock_irqsave(&sphy->lock, flags); | ||
326 | |||
327 | if (host) { | ||
328 | /* setting default phy-type for USB 2.0 */ | ||
329 | if (!strstr(dev_name(host->controller), "ehci") || | ||
330 | !strstr(dev_name(host->controller), "ohci")) | ||
331 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); | ||
332 | } else { | ||
333 | samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); | ||
334 | } | ||
335 | |||
336 | /* De-initialize usb phy registers */ | ||
337 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | ||
338 | samsung_exynos5_usb2phy_disable(sphy); | ||
339 | else | ||
340 | samsung_usb2phy_disable(sphy); | ||
341 | |||
342 | /* Enable phy isolation */ | ||
343 | if (sphy->plat && sphy->plat->pmu_isolation) | ||
344 | sphy->plat->pmu_isolation(true); | ||
345 | else | ||
346 | samsung_usbphy_set_isolation(sphy, true); | ||
347 | |||
348 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
349 | |||
350 | clk_disable_unprepare(sphy->clk); | ||
351 | } | ||
352 | |||
353 | static int samsung_usb2phy_probe(struct platform_device *pdev) | ||
354 | { | ||
355 | struct samsung_usbphy *sphy; | ||
356 | struct usb_otg *otg; | ||
357 | struct samsung_usbphy_data *pdata = pdev->dev.platform_data; | ||
358 | const struct samsung_usbphy_drvdata *drv_data; | ||
359 | struct device *dev = &pdev->dev; | ||
360 | struct resource *phy_mem; | ||
361 | void __iomem *phy_base; | ||
362 | struct clk *clk; | ||
363 | int ret; | ||
364 | |||
365 | phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
366 | if (!phy_mem) { | ||
367 | dev_err(dev, "%s: missing mem resource\n", __func__); | ||
368 | return -ENODEV; | ||
369 | } | ||
370 | |||
371 | phy_base = devm_ioremap_resource(dev, phy_mem); | ||
372 | if (IS_ERR(phy_base)) | ||
373 | return PTR_ERR(phy_base); | ||
374 | |||
375 | sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); | ||
376 | if (!sphy) | ||
377 | return -ENOMEM; | ||
378 | |||
379 | otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); | ||
380 | if (!otg) | ||
381 | return -ENOMEM; | ||
382 | |||
383 | drv_data = samsung_usbphy_get_driver_data(pdev); | ||
384 | |||
385 | if (drv_data->cpu_type == TYPE_EXYNOS5250) | ||
386 | clk = devm_clk_get(dev, "usbhost"); | ||
387 | else | ||
388 | clk = devm_clk_get(dev, "otg"); | ||
389 | |||
390 | if (IS_ERR(clk)) { | ||
391 | dev_err(dev, "Failed to get otg clock\n"); | ||
392 | return PTR_ERR(clk); | ||
393 | } | ||
394 | |||
395 | sphy->dev = dev; | ||
396 | |||
397 | if (dev->of_node) { | ||
398 | ret = samsung_usbphy_parse_dt(sphy); | ||
399 | if (ret < 0) | ||
400 | return ret; | ||
401 | } else { | ||
402 | if (!pdata) { | ||
403 | dev_err(dev, "no platform data specified\n"); | ||
404 | return -EINVAL; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | sphy->plat = pdata; | ||
409 | sphy->regs = phy_base; | ||
410 | sphy->clk = clk; | ||
411 | sphy->drv_data = drv_data; | ||
412 | sphy->phy.dev = sphy->dev; | ||
413 | sphy->phy.label = "samsung-usb2phy"; | ||
414 | sphy->phy.init = samsung_usb2phy_init; | ||
415 | sphy->phy.shutdown = samsung_usb2phy_shutdown; | ||
416 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); | ||
417 | |||
418 | sphy->phy.otg = otg; | ||
419 | sphy->phy.otg->phy = &sphy->phy; | ||
420 | sphy->phy.otg->set_host = samsung_usbphy_set_host; | ||
421 | |||
422 | spin_lock_init(&sphy->lock); | ||
423 | |||
424 | platform_set_drvdata(pdev, sphy); | ||
425 | |||
426 | return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); | ||
427 | } | ||
428 | |||
429 | static int samsung_usb2phy_remove(struct platform_device *pdev) | ||
430 | { | ||
431 | struct samsung_usbphy *sphy = platform_get_drvdata(pdev); | ||
432 | |||
433 | usb_remove_phy(&sphy->phy); | ||
434 | |||
435 | if (sphy->pmuregs) | ||
436 | iounmap(sphy->pmuregs); | ||
437 | if (sphy->sysreg) | ||
438 | iounmap(sphy->sysreg); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { | ||
444 | .cpu_type = TYPE_S3C64XX, | ||
445 | .devphy_en_mask = S3C64XX_USBPHY_ENABLE, | ||
446 | }; | ||
447 | |||
448 | static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { | ||
449 | .cpu_type = TYPE_EXYNOS4210, | ||
450 | .devphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
451 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
452 | }; | ||
453 | |||
454 | static struct samsung_usbphy_drvdata usb2phy_exynos5 = { | ||
455 | .cpu_type = TYPE_EXYNOS5250, | ||
456 | .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
457 | .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, | ||
458 | }; | ||
459 | |||
460 | #ifdef CONFIG_OF | ||
461 | static const struct of_device_id samsung_usbphy_dt_match[] = { | ||
462 | { | ||
463 | .compatible = "samsung,s3c64xx-usb2phy", | ||
464 | .data = &usb2phy_s3c64xx, | ||
465 | }, { | ||
466 | .compatible = "samsung,exynos4210-usb2phy", | ||
467 | .data = &usb2phy_exynos4, | ||
468 | }, { | ||
469 | .compatible = "samsung,exynos5250-usb2phy", | ||
470 | .data = &usb2phy_exynos5 | ||
471 | }, | ||
472 | {}, | ||
473 | }; | ||
474 | MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); | ||
475 | #endif | ||
476 | |||
477 | static struct platform_device_id samsung_usbphy_driver_ids[] = { | ||
478 | { | ||
479 | .name = "s3c64xx-usb2phy", | ||
480 | .driver_data = (unsigned long)&usb2phy_s3c64xx, | ||
481 | }, { | ||
482 | .name = "exynos4210-usb2phy", | ||
483 | .driver_data = (unsigned long)&usb2phy_exynos4, | ||
484 | }, { | ||
485 | .name = "exynos5250-usb2phy", | ||
486 | .driver_data = (unsigned long)&usb2phy_exynos5, | ||
487 | }, | ||
488 | {}, | ||
489 | }; | ||
490 | |||
491 | MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); | ||
492 | |||
493 | static struct platform_driver samsung_usb2phy_driver = { | ||
494 | .probe = samsung_usb2phy_probe, | ||
495 | .remove = samsung_usb2phy_remove, | ||
496 | .id_table = samsung_usbphy_driver_ids, | ||
497 | .driver = { | ||
498 | .name = "samsung-usb2phy", | ||
499 | .owner = THIS_MODULE, | ||
500 | .of_match_table = of_match_ptr(samsung_usbphy_dt_match), | ||
501 | }, | ||
502 | }; | ||
503 | |||
504 | module_platform_driver(samsung_usb2phy_driver); | ||
505 | |||
506 | MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); | ||
507 | MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>"); | ||
508 | MODULE_LICENSE("GPL"); | ||
509 | MODULE_ALIAS("platform:samsung-usb2phy"); | ||