diff options
Diffstat (limited to 'drivers/usb/renesas_usbhs/mod.c')
-rw-r--r-- | drivers/usb/renesas_usbhs/mod.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c new file mode 100644 index 00000000000..a577f8f4064 --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * Renesas USB driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program; if not, write to the Free Software | ||
14 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
15 | * | ||
16 | */ | ||
17 | #include <linux/interrupt.h> | ||
18 | |||
19 | #include "./common.h" | ||
20 | #include "./mod.h" | ||
21 | |||
22 | #define usbhs_priv_to_modinfo(priv) (&priv->mod_info) | ||
23 | #define usbhs_mod_info_call(priv, func, param...) \ | ||
24 | ({ \ | ||
25 | struct usbhs_mod_info *info; \ | ||
26 | info = usbhs_priv_to_modinfo(priv); \ | ||
27 | !info->func ? 0 : \ | ||
28 | info->func(param); \ | ||
29 | }) | ||
30 | |||
31 | /* | ||
32 | * autonomy | ||
33 | * | ||
34 | * these functions are used if platform doesn't have external phy. | ||
35 | * -> there is no "notify_hotplug" callback from platform | ||
36 | * -> call "notify_hotplug" by itself | ||
37 | * -> use own interrupt to connect/disconnect | ||
38 | * -> it mean module clock is always ON | ||
39 | * ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
40 | */ | ||
41 | static int usbhsm_autonomy_get_vbus(struct platform_device *pdev) | ||
42 | { | ||
43 | struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); | ||
44 | |||
45 | return VBSTS & usbhs_read(priv, INTSTS0); | ||
46 | } | ||
47 | |||
48 | static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, | ||
49 | struct usbhs_irq_state *irq_state) | ||
50 | { | ||
51 | struct platform_device *pdev = usbhs_priv_to_pdev(priv); | ||
52 | |||
53 | return usbhsc_drvcllbck_notify_hotplug(pdev); | ||
54 | } | ||
55 | |||
56 | void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) | ||
57 | { | ||
58 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
59 | |||
60 | info->irq_vbus = usbhsm_autonomy_irq_vbus; | ||
61 | priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; | ||
62 | |||
63 | usbhs_irq_callback_update(priv, NULL); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * host / gadget functions | ||
68 | * | ||
69 | * renesas_usbhs host/gadget can register itself by below functions. | ||
70 | * these functions are called when probe | ||
71 | * | ||
72 | */ | ||
73 | void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id) | ||
74 | { | ||
75 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
76 | |||
77 | info->mod[id] = mod; | ||
78 | mod->priv = priv; | ||
79 | } | ||
80 | |||
81 | struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) | ||
82 | { | ||
83 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
84 | struct usbhs_mod *ret = NULL; | ||
85 | |||
86 | switch (id) { | ||
87 | case USBHS_HOST: | ||
88 | case USBHS_GADGET: | ||
89 | ret = info->mod[id]; | ||
90 | break; | ||
91 | } | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod) | ||
97 | { | ||
98 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
99 | |||
100 | if (!mod) | ||
101 | return -EINVAL; | ||
102 | |||
103 | return info->mod[USBHS_HOST] == mod; | ||
104 | } | ||
105 | |||
106 | struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv) | ||
107 | { | ||
108 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
109 | |||
110 | return info->curt; | ||
111 | } | ||
112 | |||
113 | int usbhs_mod_change(struct usbhs_priv *priv, int id) | ||
114 | { | ||
115 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
116 | struct usbhs_mod *mod = NULL; | ||
117 | int ret = 0; | ||
118 | |||
119 | /* id < 0 mean no current */ | ||
120 | switch (id) { | ||
121 | case USBHS_HOST: | ||
122 | case USBHS_GADGET: | ||
123 | mod = info->mod[id]; | ||
124 | break; | ||
125 | default: | ||
126 | ret = -EINVAL; | ||
127 | } | ||
128 | info->curt = mod; | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | static irqreturn_t usbhs_interrupt(int irq, void *data); | ||
134 | int usbhs_mod_probe(struct usbhs_priv *priv) | ||
135 | { | ||
136 | struct device *dev = usbhs_priv_to_dev(priv); | ||
137 | int ret; | ||
138 | |||
139 | /* | ||
140 | * install host/gadget driver | ||
141 | */ | ||
142 | ret = usbhs_mod_gadget_probe(priv); | ||
143 | if (ret < 0) | ||
144 | return ret; | ||
145 | |||
146 | /* irq settings */ | ||
147 | ret = request_irq(priv->irq, usbhs_interrupt, | ||
148 | IRQF_DISABLED, dev_name(dev), priv); | ||
149 | if (ret) { | ||
150 | dev_err(dev, "irq request err\n"); | ||
151 | goto mod_init_gadget_err; | ||
152 | } | ||
153 | |||
154 | return ret; | ||
155 | |||
156 | mod_init_gadget_err: | ||
157 | usbhs_mod_gadget_remove(priv); | ||
158 | |||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | void usbhs_mod_remove(struct usbhs_priv *priv) | ||
163 | { | ||
164 | usbhs_mod_gadget_remove(priv); | ||
165 | free_irq(priv->irq, priv); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * status functions | ||
170 | */ | ||
171 | int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state) | ||
172 | { | ||
173 | switch (irq_state->dvstctr & RHST) { | ||
174 | case RHST_LOW_SPEED: | ||
175 | return USB_SPEED_LOW; | ||
176 | case RHST_FULL_SPEED: | ||
177 | return USB_SPEED_FULL; | ||
178 | case RHST_HIGH_SPEED: | ||
179 | return USB_SPEED_HIGH; | ||
180 | } | ||
181 | |||
182 | return USB_SPEED_UNKNOWN; | ||
183 | } | ||
184 | |||
185 | int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) | ||
186 | { | ||
187 | int state = irq_state->intsts0 & DVSQ_MASK; | ||
188 | |||
189 | switch (state) { | ||
190 | case POWER_STATE: | ||
191 | case DEFAULT_STATE: | ||
192 | case ADDRESS_STATE: | ||
193 | case CONFIGURATION_STATE: | ||
194 | return state; | ||
195 | } | ||
196 | |||
197 | return -EIO; | ||
198 | } | ||
199 | |||
200 | int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state) | ||
201 | { | ||
202 | /* | ||
203 | * return value | ||
204 | * | ||
205 | * IDLE_SETUP_STAGE | ||
206 | * READ_DATA_STAGE | ||
207 | * READ_STATUS_STAGE | ||
208 | * WRITE_DATA_STAGE | ||
209 | * WRITE_STATUS_STAGE | ||
210 | * NODATA_STATUS_STAGE | ||
211 | * SEQUENCE_ERROR | ||
212 | */ | ||
213 | return (int)irq_state->intsts0 & CTSQ_MASK; | ||
214 | } | ||
215 | |||
216 | static void usbhs_status_get_each_irq(struct usbhs_priv *priv, | ||
217 | struct usbhs_irq_state *state) | ||
218 | { | ||
219 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); | ||
220 | |||
221 | state->intsts0 = usbhs_read(priv, INTSTS0); | ||
222 | state->intsts1 = usbhs_read(priv, INTSTS1); | ||
223 | |||
224 | state->dvstctr = usbhs_read(priv, DVSTCTR); | ||
225 | |||
226 | /* mask */ | ||
227 | if (mod) { | ||
228 | state->brdysts = usbhs_read(priv, BRDYSTS); | ||
229 | state->nrdysts = usbhs_read(priv, NRDYSTS); | ||
230 | state->bempsts = usbhs_read(priv, BEMPSTS); | ||
231 | |||
232 | state->bempsts &= mod->irq_bempsts; | ||
233 | state->brdysts &= mod->irq_brdysts; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * interrupt | ||
239 | */ | ||
240 | #define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */ | ||
241 | #define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */ | ||
242 | static irqreturn_t usbhs_interrupt(int irq, void *data) | ||
243 | { | ||
244 | struct usbhs_priv *priv = data; | ||
245 | struct usbhs_irq_state irq_state; | ||
246 | |||
247 | usbhs_status_get_each_irq(priv, &irq_state); | ||
248 | |||
249 | /* | ||
250 | * clear interrupt | ||
251 | * | ||
252 | * The hardware is _very_ picky to clear interrupt bit. | ||
253 | * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value. | ||
254 | * | ||
255 | * see | ||
256 | * "Operation" | ||
257 | * - "Control Transfer (DCP)" | ||
258 | * - Function :: VALID bit should 0 | ||
259 | */ | ||
260 | usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); | ||
261 | usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); | ||
262 | |||
263 | usbhs_write(priv, BRDYSTS, 0); | ||
264 | usbhs_write(priv, NRDYSTS, 0); | ||
265 | usbhs_write(priv, BEMPSTS, 0); | ||
266 | |||
267 | /* | ||
268 | * call irq callback functions | ||
269 | * see also | ||
270 | * usbhs_irq_setting_update | ||
271 | */ | ||
272 | if (irq_state.intsts0 & VBINT) | ||
273 | usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); | ||
274 | |||
275 | if (irq_state.intsts0 & DVST) | ||
276 | usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); | ||
277 | |||
278 | if (irq_state.intsts0 & CTRT) | ||
279 | usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state); | ||
280 | |||
281 | if (irq_state.intsts0 & BEMP) | ||
282 | usbhs_mod_call(priv, irq_empty, priv, &irq_state); | ||
283 | |||
284 | if (irq_state.intsts0 & BRDY) | ||
285 | usbhs_mod_call(priv, irq_ready, priv, &irq_state); | ||
286 | |||
287 | return IRQ_HANDLED; | ||
288 | } | ||
289 | |||
290 | void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) | ||
291 | { | ||
292 | u16 intenb0 = 0; | ||
293 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
294 | |||
295 | usbhs_write(priv, INTENB0, 0); | ||
296 | |||
297 | usbhs_write(priv, BEMPENB, 0); | ||
298 | usbhs_write(priv, BRDYENB, 0); | ||
299 | |||
300 | /* | ||
301 | * see also | ||
302 | * usbhs_interrupt | ||
303 | */ | ||
304 | |||
305 | /* | ||
306 | * it don't enable DVSE (intenb0) here | ||
307 | * but "mod->irq_dev_state" will be called. | ||
308 | */ | ||
309 | if (info->irq_vbus) | ||
310 | intenb0 |= VBSE; | ||
311 | |||
312 | if (mod) { | ||
313 | if (mod->irq_ctrl_stage) | ||
314 | intenb0 |= CTRE; | ||
315 | |||
316 | if (mod->irq_empty && mod->irq_bempsts) { | ||
317 | usbhs_write(priv, BEMPENB, mod->irq_bempsts); | ||
318 | intenb0 |= BEMPE; | ||
319 | } | ||
320 | |||
321 | if (mod->irq_ready && mod->irq_brdysts) { | ||
322 | usbhs_write(priv, BRDYENB, mod->irq_brdysts); | ||
323 | intenb0 |= BRDYE; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | usbhs_write(priv, INTENB0, intenb0); | ||
328 | } | ||