Commit | Line | Data |
---|---|---|
949ccc3a MK |
1 | /* |
2 | * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support | |
3 | * | |
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | |
5 | * Authors: 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 "phy-samsung-usb2.h" | |
16 | ||
17 | /* Exynos USB PHY registers */ | |
18 | ||
19 | /* PHY power control */ | |
20 | #define S5PV210_UPHYPWR 0x0 | |
21 | ||
22 | #define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) | |
23 | #define S5PV210_UPHYPWR_PHY0_PWR BIT(3) | |
24 | #define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) | |
25 | #define S5PV210_UPHYPWR_PHY0 ( \ | |
26 | S5PV210_UPHYPWR_PHY0_SUSPEND | \ | |
27 | S5PV210_UPHYPWR_PHY0_PWR | \ | |
28 | S5PV210_UPHYPWR_PHY0_OTG_PWR) | |
29 | ||
30 | #define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) | |
31 | #define S5PV210_UPHYPWR_PHY1_PWR BIT(7) | |
32 | #define S5PV210_UPHYPWR_PHY1 ( \ | |
33 | S5PV210_UPHYPWR_PHY1_SUSPEND | \ | |
34 | S5PV210_UPHYPWR_PHY1_PWR) | |
35 | ||
36 | /* PHY clock control */ | |
37 | #define S5PV210_UPHYCLK 0x4 | |
38 | ||
39 | #define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) | |
40 | #define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) | |
41 | #define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) | |
42 | #define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) | |
43 | ||
44 | #define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) | |
45 | #define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) | |
46 | #define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) | |
47 | ||
48 | /* PHY reset control */ | |
49 | #define S5PV210_UPHYRST 0x8 | |
50 | ||
51 | #define S5PV210_URSTCON_PHY0 BIT(0) | |
52 | #define S5PV210_URSTCON_OTG_HLINK BIT(1) | |
53 | #define S5PV210_URSTCON_OTG_PHYLINK BIT(2) | |
54 | #define S5PV210_URSTCON_PHY1_ALL BIT(3) | |
55 | #define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) | |
56 | ||
57 | /* Isolation, configured in the power management unit */ | |
58 | #define S5PV210_USB_ISOL_OFFSET 0x680c | |
59 | #define S5PV210_USB_ISOL_DEVICE BIT(0) | |
60 | #define S5PV210_USB_ISOL_HOST BIT(1) | |
61 | ||
62 | ||
63 | enum s5pv210_phy_id { | |
64 | S5PV210_DEVICE, | |
65 | S5PV210_HOST, | |
66 | S5PV210_NUM_PHYS, | |
67 | }; | |
68 | ||
69 | /* | |
70 | * s5pv210_rate_to_clk() converts the supplied clock rate to the value that | |
71 | * can be written to the phy register. | |
72 | */ | |
73 | static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) | |
74 | { | |
75 | switch (rate) { | |
76 | case 12 * MHZ: | |
77 | *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; | |
78 | break; | |
79 | case 24 * MHZ: | |
80 | *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; | |
81 | break; | |
82 | case 48 * MHZ: | |
83 | *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; | |
84 | break; | |
85 | default: | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) | |
93 | { | |
94 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
95 | u32 mask; | |
96 | ||
97 | switch (inst->cfg->id) { | |
98 | case S5PV210_DEVICE: | |
99 | mask = S5PV210_USB_ISOL_DEVICE; | |
100 | break; | |
101 | case S5PV210_HOST: | |
102 | mask = S5PV210_USB_ISOL_HOST; | |
103 | break; | |
104 | default: | |
105 | return; | |
106 | }; | |
107 | ||
108 | regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, | |
109 | mask, on ? 0 : mask); | |
110 | } | |
111 | ||
112 | static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |
113 | { | |
114 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
115 | u32 rstbits = 0; | |
116 | u32 phypwr = 0; | |
117 | u32 rst; | |
118 | u32 pwr; | |
119 | ||
120 | switch (inst->cfg->id) { | |
121 | case S5PV210_DEVICE: | |
122 | phypwr = S5PV210_UPHYPWR_PHY0; | |
123 | rstbits = S5PV210_URSTCON_PHY0; | |
124 | break; | |
125 | case S5PV210_HOST: | |
126 | phypwr = S5PV210_UPHYPWR_PHY1; | |
127 | rstbits = S5PV210_URSTCON_PHY1_ALL | | |
128 | S5PV210_URSTCON_HOST_LINK_ALL; | |
129 | break; | |
130 | }; | |
131 | ||
132 | if (on) { | |
133 | writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); | |
134 | ||
135 | pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); | |
136 | pwr &= ~phypwr; | |
137 | writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); | |
138 | ||
139 | rst = readl(drv->reg_phy + S5PV210_UPHYRST); | |
140 | rst |= rstbits; | |
141 | writel(rst, drv->reg_phy + S5PV210_UPHYRST); | |
142 | udelay(10); | |
143 | rst &= ~rstbits; | |
144 | writel(rst, drv->reg_phy + S5PV210_UPHYRST); | |
145 | } else { | |
146 | pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); | |
147 | pwr |= phypwr; | |
148 | writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); | |
149 | } | |
150 | } | |
151 | ||
152 | static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) | |
153 | { | |
154 | s5pv210_isol(inst, 0); | |
155 | s5pv210_phy_pwr(inst, 1); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) | |
161 | { | |
162 | s5pv210_phy_pwr(inst, 0); | |
163 | s5pv210_isol(inst, 1); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { | |
169 | [S5PV210_DEVICE] = { | |
170 | .label = "device", | |
171 | .id = S5PV210_DEVICE, | |
172 | .power_on = s5pv210_power_on, | |
173 | .power_off = s5pv210_power_off, | |
174 | }, | |
175 | [S5PV210_HOST] = { | |
176 | .label = "host", | |
177 | .id = S5PV210_HOST, | |
178 | .power_on = s5pv210_power_on, | |
179 | .power_off = s5pv210_power_off, | |
180 | }, | |
181 | }; | |
182 | ||
183 | const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { | |
184 | .num_phys = ARRAY_SIZE(s5pv210_phys), | |
185 | .phys = s5pv210_phys, | |
186 | .rate_to_clk = s5pv210_rate_to_clk, | |
187 | }; |