Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[pandora-kernel.git] / arch / arm / mach-exynos / setup-usb-phy.c
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
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
29 static void exynos4210_usb_phy_clkset(struct platform_device *pdev)
30 {
31         struct clk *xusbxti_clk;
32         u32 phyclk;
33
34         xusbxti_clk = clk_get(&pdev->dev, "xusbxti");
35         if (xusbxti_clk && !IS_ERR(xusbxti_clk)) {
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);
80                 }
81                 clk_put(xusbxti_clk);
82         }
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;
122         u32 rstcon;
123         int err;
124
125         atomic_inc(&host_usage);
126
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
139         if (exynos4_usb_host_phy_is_on())
140                 return 0;
141
142         writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
143                         S5P_USBHOST_PHY_CONTROL);
144
145         exynos4210_usb_phy_clkset(pdev);
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);
165         udelay(80);
166
167         clk_disable(otg_clk);
168         clk_put(otg_clk);
169
170         return 0;
171 }
172
173 static int exynos4210_usb_phy1_exit(struct platform_device *pdev)
174 {
175         struct clk *otg_clk;
176         int err;
177
178         if (atomic_dec_return(&host_usage) > 0)
179                 return 0;
180
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 {
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);
211
212         return -EINVAL;
213 }
214
215 int s5p_usb_phy_exit(struct platform_device *pdev, int type)
216 {
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);
221
222         return -EINVAL;
223 }