Merge tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[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 int exynos4_usb_phy1_init(struct platform_device *pdev)
30 {
31         struct clk *otg_clk;
32         struct clk *xusbxti_clk;
33         u32 phyclk;
34         u32 rstcon;
35         int err;
36
37         atomic_inc(&host_usage);
38
39         otg_clk = clk_get(&pdev->dev, "otg");
40         if (IS_ERR(otg_clk)) {
41                 dev_err(&pdev->dev, "Failed to get otg clock\n");
42                 return PTR_ERR(otg_clk);
43         }
44
45         err = clk_enable(otg_clk);
46         if (err) {
47                 clk_put(otg_clk);
48                 return err;
49         }
50
51         if (exynos4_usb_host_phy_is_on())
52                 return 0;
53
54         writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
55                         S5P_USBHOST_PHY_CONTROL);
56
57         /* set clock frequency for PLL */
58         phyclk = readl(EXYNOS4_PHYCLK) & ~CLKSEL_MASK;
59
60         xusbxti_clk = clk_get(&pdev->dev, "xusbxti");
61         if (xusbxti_clk && !IS_ERR(xusbxti_clk)) {
62                 switch (clk_get_rate(xusbxti_clk)) {
63                 case 12 * MHZ:
64                         phyclk |= CLKSEL_12M;
65                         break;
66                 case 24 * MHZ:
67                         phyclk |= CLKSEL_24M;
68                         break;
69                 default:
70                 case 48 * MHZ:
71                         /* default reference clock */
72                         break;
73                 }
74                 clk_put(xusbxti_clk);
75         }
76
77         writel(phyclk, EXYNOS4_PHYCLK);
78
79         /* floating prevention logic: disable */
80         writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON);
81
82         /* set to normal HSIC 0 and 1 of PHY1 */
83         writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK),
84                         EXYNOS4_PHYPWR);
85
86         /* set to normal standard USB of PHY1 */
87         writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR);
88
89         /* reset all ports of both PHY and Link */
90         rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK |
91                 PHY1_SWRST_MASK;
92         writel(rstcon, EXYNOS4_RSTCON);
93         udelay(10);
94
95         rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK);
96         writel(rstcon, EXYNOS4_RSTCON);
97         udelay(80);
98
99         clk_disable(otg_clk);
100         clk_put(otg_clk);
101
102         return 0;
103 }
104
105 static int exynos4_usb_phy1_exit(struct platform_device *pdev)
106 {
107         struct clk *otg_clk;
108         int err;
109
110         if (atomic_dec_return(&host_usage) > 0)
111                 return 0;
112
113         otg_clk = clk_get(&pdev->dev, "otg");
114         if (IS_ERR(otg_clk)) {
115                 dev_err(&pdev->dev, "Failed to get otg clock\n");
116                 return PTR_ERR(otg_clk);
117         }
118
119         err = clk_enable(otg_clk);
120         if (err) {
121                 clk_put(otg_clk);
122                 return err;
123         }
124
125         writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN),
126                         EXYNOS4_PHYPWR);
127
128         writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE,
129                         S5P_USBHOST_PHY_CONTROL);
130
131         clk_disable(otg_clk);
132         clk_put(otg_clk);
133
134         return 0;
135 }
136
137 int s5p_usb_phy_init(struct platform_device *pdev, int type)
138 {
139         if (type == S5P_USB_PHY_HOST)
140                 return exynos4_usb_phy1_init(pdev);
141
142         return -EINVAL;
143 }
144
145 int s5p_usb_phy_exit(struct platform_device *pdev, int type)
146 {
147         if (type == S5P_USB_PHY_HOST)
148                 return exynos4_usb_phy1_exit(pdev);
149
150         return -EINVAL;
151 }