diff options
-rw-r--r-- | drivers/phy/phy-exynos4x12-usb2.c | 112 | ||||
-rw-r--r-- | drivers/phy/phy-exynos5250-usb2.c | 2 | ||||
-rw-r--r-- | drivers/phy/phy-samsung-usb2.h | 3 |
3 files changed, 77 insertions, 40 deletions
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index d92a7cc5698a..63134d8bda08 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c | |||
@@ -86,13 +86,23 @@ | |||
86 | #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) | 86 | #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) |
87 | #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) | 87 | #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) |
88 | #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) | 88 | #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) |
89 | /* The following bit defines are presented in the | ||
90 | * order taken from the Exynos4412 reference manual. | ||
91 | * | ||
92 | * During experiments with the hardware and debugging | ||
93 | * it was determined that the hardware behaves contrary | ||
94 | * to the manual. | ||
95 | * | ||
96 | * The following bit values were chaned accordingly to the | ||
97 | * results of real hardware experiments. | ||
98 | */ | ||
89 | #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) | 99 | #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) |
90 | #define EXYNOS_4x12_URSTCON_HSIC0 BIT(5) | 100 | #define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) |
91 | #define EXYNOS_4x12_URSTCON_HSIC1 BIT(6) | 101 | #define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) |
92 | #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) | 102 | #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) |
93 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8) | 103 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) |
94 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) | 104 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) |
95 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10) | 105 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) |
96 | 106 | ||
97 | /* Isolation, configured in the power management unit */ | 107 | /* Isolation, configured in the power management unit */ |
98 | #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 | 108 | #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 |
@@ -188,6 +198,7 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) | |||
188 | clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); | 198 | clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); |
189 | clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; | 199 | clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; |
190 | clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; | 200 | clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; |
201 | clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; | ||
191 | writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); | 202 | writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); |
192 | } | 203 | } |
193 | 204 | ||
@@ -198,27 +209,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |||
198 | u32 phypwr = 0; | 209 | u32 phypwr = 0; |
199 | u32 rst; | 210 | u32 rst; |
200 | u32 pwr; | 211 | u32 pwr; |
201 | u32 mode = 0; | ||
202 | u32 switch_mode = 0; | ||
203 | 212 | ||
204 | switch (inst->cfg->id) { | 213 | switch (inst->cfg->id) { |
205 | case EXYNOS4x12_DEVICE: | 214 | case EXYNOS4x12_DEVICE: |
206 | phypwr = EXYNOS_4x12_UPHYPWR_PHY0; | 215 | phypwr = EXYNOS_4x12_UPHYPWR_PHY0; |
207 | rstbits = EXYNOS_4x12_URSTCON_PHY0; | 216 | rstbits = EXYNOS_4x12_URSTCON_PHY0; |
208 | mode = EXYNOS_4x12_MODE_SWITCH_DEVICE; | ||
209 | switch_mode = 1; | ||
210 | break; | 217 | break; |
211 | case EXYNOS4x12_HOST: | 218 | case EXYNOS4x12_HOST: |
212 | phypwr = EXYNOS_4x12_UPHYPWR_PHY1; | 219 | phypwr = EXYNOS_4x12_UPHYPWR_PHY1; |
213 | rstbits = EXYNOS_4x12_URSTCON_HOST_PHY; | 220 | rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | |
214 | mode = EXYNOS_4x12_MODE_SWITCH_HOST; | 221 | EXYNOS_4x12_URSTCON_PHY1 | |
215 | switch_mode = 1; | 222 | EXYNOS_4x12_URSTCON_HOST_LINK_P0; |
216 | break; | 223 | break; |
217 | case EXYNOS4x12_HSIC0: | 224 | case EXYNOS4x12_HSIC0: |
218 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; | 225 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; |
219 | rstbits = EXYNOS_4x12_URSTCON_HSIC1 | | 226 | rstbits = EXYNOS_4x12_URSTCON_HSIC0 | |
220 | EXYNOS_4x12_URSTCON_HOST_LINK_P0 | | 227 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; |
221 | EXYNOS_4x12_URSTCON_HOST_PHY; | ||
222 | break; | 228 | break; |
223 | case EXYNOS4x12_HSIC1: | 229 | case EXYNOS4x12_HSIC1: |
224 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; | 230 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; |
@@ -228,11 +234,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |||
228 | }; | 234 | }; |
229 | 235 | ||
230 | if (on) { | 236 | if (on) { |
231 | if (switch_mode) | ||
232 | regmap_update_bits(drv->reg_sys, | ||
233 | EXYNOS_4x12_MODE_SWITCH_OFFSET, | ||
234 | EXYNOS_4x12_MODE_SWITCH_MASK, mode); | ||
235 | |||
236 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); | 237 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); |
237 | pwr &= ~phypwr; | 238 | pwr &= ~phypwr; |
238 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); | 239 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); |
@@ -253,41 +254,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |||
253 | } | 254 | } |
254 | } | 255 | } |
255 | 256 | ||
256 | static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) | 257 | static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) |
257 | { | 258 | { |
258 | struct samsung_usb2_phy_driver *drv = inst->drv; | 259 | if (inst->int_cnt++ > 0) |
260 | return; | ||
259 | 261 | ||
260 | inst->enabled = 1; | ||
261 | exynos4x12_setup_clk(inst); | 262 | exynos4x12_setup_clk(inst); |
262 | exynos4x12_phy_pwr(inst, 1); | ||
263 | exynos4x12_isol(inst, 0); | 263 | exynos4x12_isol(inst, 0); |
264 | exynos4x12_phy_pwr(inst, 1); | ||
265 | } | ||
266 | |||
267 | static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) | ||
268 | { | ||
269 | struct samsung_usb2_phy_driver *drv = inst->drv; | ||
270 | |||
271 | if (inst->ext_cnt++ > 0) | ||
272 | return 0; | ||
264 | 273 | ||
265 | /* Power on the device, as it is necessary for HSIC to work */ | 274 | if (inst->cfg->id == EXYNOS4x12_HOST) { |
266 | if (inst->cfg->id == EXYNOS4x12_HSIC0) { | 275 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, |
267 | struct samsung_usb2_phy_instance *device = | 276 | EXYNOS_4x12_MODE_SWITCH_MASK, |
268 | &drv->instances[EXYNOS4x12_DEVICE]; | 277 | EXYNOS_4x12_MODE_SWITCH_HOST); |
269 | exynos4x12_phy_pwr(device, 1); | 278 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); |
270 | exynos4x12_isol(device, 0); | ||
271 | } | 279 | } |
272 | 280 | ||
281 | if (inst->cfg->id == EXYNOS4x12_DEVICE) | ||
282 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | ||
283 | EXYNOS_4x12_MODE_SWITCH_MASK, | ||
284 | EXYNOS_4x12_MODE_SWITCH_DEVICE); | ||
285 | |||
286 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | ||
287 | inst->cfg->id == EXYNOS4x12_HSIC1) { | ||
288 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); | ||
289 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); | ||
290 | } | ||
291 | |||
292 | exynos4x12_power_on_int(inst); | ||
293 | |||
273 | return 0; | 294 | return 0; |
274 | } | 295 | } |
275 | 296 | ||
276 | static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) | 297 | static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) |
277 | { | 298 | { |
278 | struct samsung_usb2_phy_driver *drv = inst->drv; | 299 | if (inst->int_cnt-- > 1) |
279 | struct samsung_usb2_phy_instance *device = | 300 | return; |
280 | &drv->instances[EXYNOS4x12_DEVICE]; | ||
281 | 301 | ||
282 | inst->enabled = 0; | ||
283 | exynos4x12_isol(inst, 1); | 302 | exynos4x12_isol(inst, 1); |
284 | exynos4x12_phy_pwr(inst, 0); | 303 | exynos4x12_phy_pwr(inst, 0); |
304 | } | ||
285 | 305 | ||
286 | if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) { | 306 | static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) |
287 | exynos4x12_isol(device, 1); | 307 | { |
288 | exynos4x12_phy_pwr(device, 0); | 308 | struct samsung_usb2_phy_driver *drv = inst->drv; |
309 | |||
310 | if (inst->ext_cnt-- > 1) | ||
311 | return 0; | ||
312 | |||
313 | if (inst->cfg->id == EXYNOS4x12_DEVICE) | ||
314 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | ||
315 | EXYNOS_4x12_MODE_SWITCH_MASK, | ||
316 | EXYNOS_4x12_MODE_SWITCH_HOST); | ||
317 | |||
318 | if (inst->cfg->id == EXYNOS4x12_HOST) | ||
319 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | ||
320 | |||
321 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | ||
322 | inst->cfg->id == EXYNOS4x12_HSIC1) { | ||
323 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | ||
324 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); | ||
289 | } | 325 | } |
290 | 326 | ||
327 | exynos4x12_power_off_int(inst); | ||
328 | |||
291 | return 0; | 329 | return 0; |
292 | } | 330 | } |
293 | 331 | ||
diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 94179afda951..1c139aa0d074 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c | |||
@@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) | |||
318 | 318 | ||
319 | break; | 319 | break; |
320 | } | 320 | } |
321 | inst->enabled = 1; | ||
322 | exynos5250_isol(inst, 0); | 321 | exynos5250_isol(inst, 0); |
323 | 322 | ||
324 | return 0; | 323 | return 0; |
@@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) | |||
331 | u32 otg; | 330 | u32 otg; |
332 | u32 hsic; | 331 | u32 hsic; |
333 | 332 | ||
334 | inst->enabled = 0; | ||
335 | exynos5250_isol(inst, 1); | 333 | exynos5250_isol(inst, 1); |
336 | 334 | ||
337 | switch (inst->cfg->id) { | 335 | switch (inst->cfg->id) { |
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index 45b3170652bd..918847843a95 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h | |||
@@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance { | |||
29 | const struct samsung_usb2_common_phy *cfg; | 29 | const struct samsung_usb2_common_phy *cfg; |
30 | struct phy *phy; | 30 | struct phy *phy; |
31 | struct samsung_usb2_phy_driver *drv; | 31 | struct samsung_usb2_phy_driver *drv; |
32 | bool enabled; | 32 | int int_cnt; |
33 | int ext_cnt; | ||
33 | }; | 34 | }; |
34 | 35 | ||
35 | struct samsung_usb2_phy_driver { | 36 | struct samsung_usb2_phy_driver { |