Commit | Line | Data |
---|---|---|
8f1d169f JS |
1 | /* |
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | |
3 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include <linux/clk.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/err.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <mach/regs-pmu.h> | |
18 | #include <mach/regs-usb-phy.h> | |
19 | #include <plat/cpu.h> | |
20 | #include <plat/usb-phy.h> | |
21 | ||
6e7eb170 JH |
22 | static atomic_t host_usage; |
23 | ||
24 | static int exynos4_usb_host_phy_is_on(void) | |
25 | { | |
26 | return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1; | |
27 | } | |
28 | ||
8ea2d9e7 | 29 | static void exynos4210_usb_phy_clkset(struct platform_device *pdev) |
8f1d169f | 30 | { |
8f1d169f JS |
31 | struct clk *xusbxti_clk; |
32 | u32 phyclk; | |
8ea2d9e7 | 33 | |
8ea2d9e7 LM |
34 | xusbxti_clk = clk_get(&pdev->dev, "xusbxti"); |
35 | if (xusbxti_clk && !IS_ERR(xusbxti_clk)) { | |
c4c71355 SK |
36 | if (soc_is_exynos4210()) { |
37 | /* set clock frequency for PLL */ | |
38 | phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4210_CLKSEL_MASK; | |
39 | ||
40 | switch (clk_get_rate(xusbxti_clk)) { | |
41 | case 12 * MHZ: | |
42 | phyclk |= EXYNOS4210_CLKSEL_12M; | |
43 | break; | |
44 | case 48 * MHZ: | |
45 | phyclk |= EXYNOS4210_CLKSEL_48M; | |
46 | break; | |
47 | default: | |
48 | case 24 * MHZ: | |
49 | phyclk |= EXYNOS4210_CLKSEL_24M; | |
50 | break; | |
51 | } | |
52 | writel(phyclk, EXYNOS4_PHYCLK); | |
53 | } else if (soc_is_exynos4212() || soc_is_exynos4412()) { | |
54 | /* set clock frequency for PLL */ | |
55 | phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4X12_CLKSEL_MASK; | |
56 | ||
57 | switch (clk_get_rate(xusbxti_clk)) { | |
58 | case 9600 * KHZ: | |
59 | phyclk |= EXYNOS4X12_CLKSEL_9600K; | |
60 | break; | |
61 | case 10 * MHZ: | |
62 | phyclk |= EXYNOS4X12_CLKSEL_10M; | |
63 | break; | |
64 | case 12 * MHZ: | |
65 | phyclk |= EXYNOS4X12_CLKSEL_12M; | |
66 | break; | |
67 | case 19200 * KHZ: | |
68 | phyclk |= EXYNOS4X12_CLKSEL_19200K; | |
69 | break; | |
70 | case 20 * MHZ: | |
71 | phyclk |= EXYNOS4X12_CLKSEL_20M; | |
72 | break; | |
73 | default: | |
74 | case 24 * MHZ: | |
75 | /* default reference clock */ | |
76 | phyclk |= EXYNOS4X12_CLKSEL_24M; | |
77 | break; | |
78 | } | |
79 | writel(phyclk, EXYNOS4_PHYCLK); | |
8ea2d9e7 LM |
80 | } |
81 | clk_put(xusbxti_clk); | |
82 | } | |
8ea2d9e7 LM |
83 | } |
84 | ||
85 | static int exynos4210_usb_phy0_init(struct platform_device *pdev) | |
86 | { | |
87 | u32 rstcon; | |
88 | ||
89 | writel(readl(S5P_USBDEVICE_PHY_CONTROL) | S5P_USBDEVICE_PHY_ENABLE, | |
90 | S5P_USBDEVICE_PHY_CONTROL); | |
91 | ||
92 | exynos4210_usb_phy_clkset(pdev); | |
93 | ||
94 | /* set to normal PHY0 */ | |
95 | writel((readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK), EXYNOS4_PHYPWR); | |
96 | ||
97 | /* reset PHY0 and Link */ | |
98 | rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK; | |
99 | writel(rstcon, EXYNOS4_RSTCON); | |
100 | udelay(10); | |
101 | ||
102 | rstcon &= ~PHY0_SWRST_MASK; | |
103 | writel(rstcon, EXYNOS4_RSTCON); | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
108 | static int exynos4210_usb_phy0_exit(struct platform_device *pdev) | |
109 | { | |
110 | writel((readl(EXYNOS4_PHYPWR) | PHY0_ANALOG_POWERDOWN | | |
111 | PHY0_OTG_DISABLE), EXYNOS4_PHYPWR); | |
112 | ||
113 | writel(readl(S5P_USBDEVICE_PHY_CONTROL) & ~S5P_USBDEVICE_PHY_ENABLE, | |
114 | S5P_USBDEVICE_PHY_CONTROL); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static int exynos4210_usb_phy1_init(struct platform_device *pdev) | |
120 | { | |
121 | struct clk *otg_clk; | |
8f1d169f JS |
122 | u32 rstcon; |
123 | int err; | |
124 | ||
6e7eb170 JH |
125 | atomic_inc(&host_usage); |
126 | ||
8f1d169f JS |
127 | otg_clk = clk_get(&pdev->dev, "otg"); |
128 | if (IS_ERR(otg_clk)) { | |
129 | dev_err(&pdev->dev, "Failed to get otg clock\n"); | |
130 | return PTR_ERR(otg_clk); | |
131 | } | |
132 | ||
133 | err = clk_enable(otg_clk); | |
134 | if (err) { | |
135 | clk_put(otg_clk); | |
136 | return err; | |
137 | } | |
138 | ||
6e7eb170 JH |
139 | if (exynos4_usb_host_phy_is_on()) |
140 | return 0; | |
141 | ||
8f1d169f JS |
142 | writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE, |
143 | S5P_USBHOST_PHY_CONTROL); | |
144 | ||
8ea2d9e7 | 145 | exynos4210_usb_phy_clkset(pdev); |
8f1d169f JS |
146 | |
147 | /* floating prevention logic: disable */ | |
148 | writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON); | |
149 | ||
150 | /* set to normal HSIC 0 and 1 of PHY1 */ | |
151 | writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK), | |
152 | EXYNOS4_PHYPWR); | |
153 | ||
154 | /* set to normal standard USB of PHY1 */ | |
155 | writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR); | |
156 | ||
157 | /* reset all ports of both PHY and Link */ | |
158 | rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK | | |
159 | PHY1_SWRST_MASK; | |
160 | writel(rstcon, EXYNOS4_RSTCON); | |
161 | udelay(10); | |
162 | ||
163 | rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK); | |
164 | writel(rstcon, EXYNOS4_RSTCON); | |
2b431ff7 | 165 | udelay(80); |
8f1d169f JS |
166 | |
167 | clk_disable(otg_clk); | |
168 | clk_put(otg_clk); | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
8ea2d9e7 | 173 | static int exynos4210_usb_phy1_exit(struct platform_device *pdev) |
8f1d169f JS |
174 | { |
175 | struct clk *otg_clk; | |
176 | int err; | |
177 | ||
6e7eb170 JH |
178 | if (atomic_dec_return(&host_usage) > 0) |
179 | return 0; | |
180 | ||
8f1d169f JS |
181 | otg_clk = clk_get(&pdev->dev, "otg"); |
182 | if (IS_ERR(otg_clk)) { | |
183 | dev_err(&pdev->dev, "Failed to get otg clock\n"); | |
184 | return PTR_ERR(otg_clk); | |
185 | } | |
186 | ||
187 | err = clk_enable(otg_clk); | |
188 | if (err) { | |
189 | clk_put(otg_clk); | |
190 | return err; | |
191 | } | |
192 | ||
193 | writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN), | |
194 | EXYNOS4_PHYPWR); | |
195 | ||
196 | writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE, | |
197 | S5P_USBHOST_PHY_CONTROL); | |
198 | ||
199 | clk_disable(otg_clk); | |
200 | clk_put(otg_clk); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | int s5p_usb_phy_init(struct platform_device *pdev, int type) | |
206 | { | |
8ea2d9e7 LM |
207 | if (type == S5P_USB_PHY_DEVICE) |
208 | return exynos4210_usb_phy0_init(pdev); | |
209 | else if (type == S5P_USB_PHY_HOST) | |
210 | return exynos4210_usb_phy1_init(pdev); | |
8f1d169f JS |
211 | |
212 | return -EINVAL; | |
213 | } | |
214 | ||
215 | int s5p_usb_phy_exit(struct platform_device *pdev, int type) | |
216 | { | |
8ea2d9e7 LM |
217 | if (type == S5P_USB_PHY_DEVICE) |
218 | return exynos4210_usb_phy0_exit(pdev); | |
219 | else if (type == S5P_USB_PHY_HOST) | |
220 | return exynos4210_usb_phy1_exit(pdev); | |
8f1d169f JS |
221 | |
222 | return -EINVAL; | |
223 | } |