diff options
author | Kamil Debski <k.debski@samsung.com> | 2014-03-06 06:16:48 -0500 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2014-03-08 02:09:44 -0500 |
commit | 06fb01373cae0bbf9af3a2b49e29c4341a5ba311 (patch) | |
tree | c6f6024bb1c0fcdf6c2bfed9cf668521a48e1159 /drivers/phy/phy-exynos4210-usb2.c | |
parent | b5d682f4eb76c98f2ca5658926df43dd05cf2c37 (diff) |
phy: Add new Exynos USB 2.0 PHY driver
Add a new driver for the Exynos USB 2.0 PHY. The new driver uses the generic
PHY framework. The driver includes support for the Exynos 4x10 and 4x12
SoC families.
Signed-off-by: Kamil Debski <k.debski@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy/phy-exynos4210-usb2.c')
-rw-r--r-- | drivers/phy/phy-exynos4210-usb2.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c new file mode 100644 index 000000000000..236a52ad94eb --- /dev/null +++ b/drivers/phy/phy-exynos4210-usb2.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* | ||
2 | * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * Author: Kamil Debski <k.debski@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/phy/phy.h> | ||
15 | #include <linux/regmap.h> | ||
16 | #include "phy-samsung-usb2.h" | ||
17 | |||
18 | /* Exynos USB PHY registers */ | ||
19 | |||
20 | /* PHY power control */ | ||
21 | #define EXYNOS_4210_UPHYPWR 0x0 | ||
22 | |||
23 | #define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) | ||
24 | #define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) | ||
25 | #define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) | ||
26 | #define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) | ||
27 | #define EXYNOS_4210_UPHYPWR_PHY0 ( \ | ||
28 | EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ | ||
29 | EXYNOS_4210_UPHYPWR_PHY0_PWR | \ | ||
30 | EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ | ||
31 | EXYNOS_4210_UPHYPWR_PHY0_SLEEP) | ||
32 | |||
33 | #define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) | ||
34 | #define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) | ||
35 | #define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) | ||
36 | #define EXYNOS_4210_UPHYPWR_PHY1 ( \ | ||
37 | EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ | ||
38 | EXYNOS_4210_UPHYPWR_PHY1_PWR | \ | ||
39 | EXYNOS_4210_UPHYPWR_PHY1_SLEEP) | ||
40 | |||
41 | #define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) | ||
42 | #define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) | ||
43 | #define EXYNOS_4210_UPHYPWR_HSIC0 ( \ | ||
44 | EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ | ||
45 | EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) | ||
46 | |||
47 | #define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) | ||
48 | #define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) | ||
49 | #define EXYNOS_4210_UPHYPWR_HSIC1 ( \ | ||
50 | EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ | ||
51 | EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) | ||
52 | |||
53 | /* PHY clock control */ | ||
54 | #define EXYNOS_4210_UPHYCLK 0x4 | ||
55 | |||
56 | #define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) | ||
57 | #define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 | ||
58 | #define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) | ||
59 | #define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) | ||
60 | #define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) | ||
61 | |||
62 | #define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) | ||
63 | #define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) | ||
64 | #define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) | ||
65 | |||
66 | /* PHY reset control */ | ||
67 | #define EXYNOS_4210_UPHYRST 0x8 | ||
68 | |||
69 | #define EXYNOS_4210_URSTCON_PHY0 BIT(0) | ||
70 | #define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) | ||
71 | #define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) | ||
72 | #define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) | ||
73 | #define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) | ||
74 | #define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) | ||
75 | #define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) | ||
76 | #define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) | ||
77 | #define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) | ||
78 | #define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) | ||
79 | |||
80 | /* Isolation, configured in the power management unit */ | ||
81 | #define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 | ||
82 | #define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) | ||
83 | #define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 | ||
84 | #define EXYNOS_4210_USB_ISOL_HOST BIT(0) | ||
85 | |||
86 | /* USBYPHY1 Floating prevention */ | ||
87 | #define EXYNOS_4210_UPHY1CON 0x34 | ||
88 | #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 | ||
89 | |||
90 | /* Mode switching SUB Device <-> Host */ | ||
91 | #define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c | ||
92 | #define EXYNOS_4210_MODE_SWITCH_MASK 1 | ||
93 | #define EXYNOS_4210_MODE_SWITCH_DEVICE 0 | ||
94 | #define EXYNOS_4210_MODE_SWITCH_HOST 1 | ||
95 | |||
96 | enum exynos4210_phy_id { | ||
97 | EXYNOS4210_DEVICE, | ||
98 | EXYNOS4210_HOST, | ||
99 | EXYNOS4210_HSIC0, | ||
100 | EXYNOS4210_HSIC1, | ||
101 | EXYNOS4210_NUM_PHYS, | ||
102 | }; | ||
103 | |||
104 | /* | ||
105 | * exynos4210_rate_to_clk() converts the supplied clock rate to the value that | ||
106 | * can be written to the phy register. | ||
107 | */ | ||
108 | static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) | ||
109 | { | ||
110 | switch (rate) { | ||
111 | case 12 * MHZ: | ||
112 | *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; | ||
113 | break; | ||
114 | case 24 * MHZ: | ||
115 | *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; | ||
116 | break; | ||
117 | case 48 * MHZ: | ||
118 | *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; | ||
119 | break; | ||
120 | default: | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) | ||
128 | { | ||
129 | struct samsung_usb2_phy_driver *drv = inst->drv; | ||
130 | u32 offset; | ||
131 | u32 mask; | ||
132 | |||
133 | switch (inst->cfg->id) { | ||
134 | case EXYNOS4210_DEVICE: | ||
135 | offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; | ||
136 | mask = EXYNOS_4210_USB_ISOL_DEVICE; | ||
137 | break; | ||
138 | case EXYNOS4210_HOST: | ||
139 | offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; | ||
140 | mask = EXYNOS_4210_USB_ISOL_HOST; | ||
141 | break; | ||
142 | default: | ||
143 | return; | ||
144 | }; | ||
145 | |||
146 | regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); | ||
147 | } | ||
148 | |||
149 | static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | ||
150 | { | ||
151 | struct samsung_usb2_phy_driver *drv = inst->drv; | ||
152 | u32 rstbits = 0; | ||
153 | u32 phypwr = 0; | ||
154 | u32 rst; | ||
155 | u32 pwr; | ||
156 | u32 clk; | ||
157 | |||
158 | switch (inst->cfg->id) { | ||
159 | case EXYNOS4210_DEVICE: | ||
160 | phypwr = EXYNOS_4210_UPHYPWR_PHY0; | ||
161 | rstbits = EXYNOS_4210_URSTCON_PHY0; | ||
162 | break; | ||
163 | case EXYNOS4210_HOST: | ||
164 | phypwr = EXYNOS_4210_UPHYPWR_PHY1; | ||
165 | rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | | ||
166 | EXYNOS_4210_URSTCON_PHY1_P0 | | ||
167 | EXYNOS_4210_URSTCON_PHY1_P1P2 | | ||
168 | EXYNOS_4210_URSTCON_HOST_LINK_ALL | | ||
169 | EXYNOS_4210_URSTCON_HOST_LINK_P0; | ||
170 | writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); | ||
171 | break; | ||
172 | case EXYNOS4210_HSIC0: | ||
173 | phypwr = EXYNOS_4210_UPHYPWR_HSIC0; | ||
174 | rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | | ||
175 | EXYNOS_4210_URSTCON_HOST_LINK_P1; | ||
176 | break; | ||
177 | case EXYNOS4210_HSIC1: | ||
178 | phypwr = EXYNOS_4210_UPHYPWR_HSIC1; | ||
179 | rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | | ||
180 | EXYNOS_4210_URSTCON_HOST_LINK_P2; | ||
181 | break; | ||
182 | }; | ||
183 | |||
184 | if (on) { | ||
185 | clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); | ||
186 | clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; | ||
187 | clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; | ||
188 | writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); | ||
189 | |||
190 | pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); | ||
191 | pwr &= ~phypwr; | ||
192 | writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); | ||
193 | |||
194 | rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); | ||
195 | rst |= rstbits; | ||
196 | writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); | ||
197 | udelay(10); | ||
198 | rst &= ~rstbits; | ||
199 | writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); | ||
200 | /* The following delay is necessary for the reset sequence to be | ||
201 | * completed */ | ||
202 | udelay(80); | ||
203 | } else { | ||
204 | pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); | ||
205 | pwr |= phypwr; | ||
206 | writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) | ||
211 | { | ||
212 | /* Order of initialisation is important - first power then isolation */ | ||
213 | exynos4210_phy_pwr(inst, 1); | ||
214 | exynos4210_isol(inst, 0); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) | ||
220 | { | ||
221 | exynos4210_isol(inst, 1); | ||
222 | exynos4210_phy_pwr(inst, 0); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | |||
228 | static const struct samsung_usb2_common_phy exynos4210_phys[] = { | ||
229 | { | ||
230 | .label = "device", | ||
231 | .id = EXYNOS4210_DEVICE, | ||
232 | .power_on = exynos4210_power_on, | ||
233 | .power_off = exynos4210_power_off, | ||
234 | }, | ||
235 | { | ||
236 | .label = "host", | ||
237 | .id = EXYNOS4210_HOST, | ||
238 | .power_on = exynos4210_power_on, | ||
239 | .power_off = exynos4210_power_off, | ||
240 | }, | ||
241 | { | ||
242 | .label = "hsic0", | ||
243 | .id = EXYNOS4210_HSIC0, | ||
244 | .power_on = exynos4210_power_on, | ||
245 | .power_off = exynos4210_power_off, | ||
246 | }, | ||
247 | { | ||
248 | .label = "hsic1", | ||
249 | .id = EXYNOS4210_HSIC1, | ||
250 | .power_on = exynos4210_power_on, | ||
251 | .power_off = exynos4210_power_off, | ||
252 | }, | ||
253 | {}, | ||
254 | }; | ||
255 | |||
256 | const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { | ||
257 | .has_mode_switch = 0, | ||
258 | .num_phys = EXYNOS4210_NUM_PHYS, | ||
259 | .phys = exynos4210_phys, | ||
260 | .rate_to_clk = exynos4210_rate_to_clk, | ||
261 | }; | ||