Commit | Line | Data |
---|---|---|
06fb0137 KD |
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 | }, | |
06fb0137 KD |
253 | }; |
254 | ||
255 | const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { | |
256 | .has_mode_switch = 0, | |
257 | .num_phys = EXYNOS4210_NUM_PHYS, | |
258 | .phys = exynos4210_phys, | |
259 | .rate_to_clk = exynos4210_rate_to_clk, | |
260 | }; |