Merge tag 'cleanup-initcall' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[firefly-linux-kernel-4.4.55.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         /* set clock frequency for PLL */
35         phyclk = readl(EXYNOS4_PHYCLK) & ~CLKSEL_MASK;
36
37         xusbxti_clk = clk_get(&pdev->dev, "xusbxti");
38         if (xusbxti_clk && !IS_ERR(xusbxti_clk)) {
39                 switch (clk_get_rate(xusbxti_clk)) {
40                 case 12 * MHZ:
41                         phyclk |= CLKSEL_12M;
42                         break;
43                 case 24 * MHZ:
44                         phyclk |= CLKSEL_24M;
45                         break;
46                 default:
47                 case 48 * MHZ:
48                         /* default reference clock */
49                         break;
50                 }
51                 clk_put(xusbxti_clk);
52         }
53
54         writel(phyclk, EXYNOS4_PHYCLK);
55 }
56
57 static int exynos4210_usb_phy0_init(struct platform_device *pdev)
58 {
59         u32 rstcon;
60
61         writel(readl(S5P_USBDEVICE_PHY_CONTROL) | S5P_USBDEVICE_PHY_ENABLE,
62                         S5P_USBDEVICE_PHY_CONTROL);
63
64         exynos4210_usb_phy_clkset(pdev);
65
66         /* set to normal PHY0 */
67         writel((readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK), EXYNOS4_PHYPWR);
68
69         /* reset PHY0 and Link */
70         rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK;
71         writel(rstcon, EXYNOS4_RSTCON);
72         udelay(10);
73
74         rstcon &= ~PHY0_SWRST_MASK;
75         writel(rstcon, EXYNOS4_RSTCON);
76
77         return 0;
78 }
79
80 static int exynos4210_usb_phy0_exit(struct platform_device *pdev)
81 {
82         writel((readl(EXYNOS4_PHYPWR) | PHY0_ANALOG_POWERDOWN |
83                                 PHY0_OTG_DISABLE), EXYNOS4_PHYPWR);
84
85         writel(readl(S5P_USBDEVICE_PHY_CONTROL) & ~S5P_USBDEVICE_PHY_ENABLE,
86                         S5P_USBDEVICE_PHY_CONTROL);
87
88         return 0;
89 }
90
91 static int exynos4210_usb_phy1_init(struct platform_device *pdev)
92 {
93         struct clk *otg_clk;
94         u32 rstcon;
95         int err;
96
97         atomic_inc(&host_usage);
98
99         otg_clk = clk_get(&pdev->dev, "otg");
100         if (IS_ERR(otg_clk)) {
101                 dev_err(&pdev->dev, "Failed to get otg clock\n");
102                 return PTR_ERR(otg_clk);
103         }
104
105         err = clk_enable(otg_clk);
106         if (err) {
107                 clk_put(otg_clk);
108                 return err;
109         }
110
111         if (exynos4_usb_host_phy_is_on())
112                 return 0;
113
114         writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
115                         S5P_USBHOST_PHY_CONTROL);
116
117         exynos4210_usb_phy_clkset(pdev);
118
119         /* floating prevention logic: disable */
120         writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON);
121
122         /* set to normal HSIC 0 and 1 of PHY1 */
123         writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK),
124                         EXYNOS4_PHYPWR);
125
126         /* set to normal standard USB of PHY1 */
127         writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR);
128
129         /* reset all ports of both PHY and Link */
130         rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK |
131                 PHY1_SWRST_MASK;
132         writel(rstcon, EXYNOS4_RSTCON);
133         udelay(10);
134
135         rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK);
136         writel(rstcon, EXYNOS4_RSTCON);
137         udelay(80);
138
139         clk_disable(otg_clk);
140         clk_put(otg_clk);
141
142         return 0;
143 }
144
145 static int exynos4210_usb_phy1_exit(struct platform_device *pdev)
146 {
147         struct clk *otg_clk;
148         int err;
149
150         if (atomic_dec_return(&host_usage) > 0)
151                 return 0;
152
153         otg_clk = clk_get(&pdev->dev, "otg");
154         if (IS_ERR(otg_clk)) {
155                 dev_err(&pdev->dev, "Failed to get otg clock\n");
156                 return PTR_ERR(otg_clk);
157         }
158
159         err = clk_enable(otg_clk);
160         if (err) {
161                 clk_put(otg_clk);
162                 return err;
163         }
164
165         writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN),
166                         EXYNOS4_PHYPWR);
167
168         writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE,
169                         S5P_USBHOST_PHY_CONTROL);
170
171         clk_disable(otg_clk);
172         clk_put(otg_clk);
173
174         return 0;
175 }
176
177 int s5p_usb_phy_init(struct platform_device *pdev, int type)
178 {
179         if (type == S5P_USB_PHY_DEVICE)
180                 return exynos4210_usb_phy0_init(pdev);
181         else if (type == S5P_USB_PHY_HOST)
182                 return exynos4210_usb_phy1_init(pdev);
183
184         return -EINVAL;
185 }
186
187 int s5p_usb_phy_exit(struct platform_device *pdev, int type)
188 {
189         if (type == S5P_USB_PHY_DEVICE)
190                 return exynos4210_usb_phy0_exit(pdev);
191         else if (type == S5P_USB_PHY_HOST)
192                 return exynos4210_usb_phy1_exit(pdev);
193
194         return -EINVAL;
195 }