Commit | Line | Data |
---|---|---|
f1407d5c KM |
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/delay.h> | |
f1407d5c KM |
18 | #include <linux/slab.h> |
19 | #include "./common.h" | |
20 | #include "./pipe.h" | |
21 | ||
22 | /* | |
23 | * macros | |
24 | */ | |
f1407d5c KM |
25 | #define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2) |
26 | ||
f1407d5c KM |
27 | #define usbhsp_flags_set(p, f) ((p)->flags |= USBHS_PIPE_FLAGS_##f) |
28 | #define usbhsp_flags_clr(p, f) ((p)->flags &= ~USBHS_PIPE_FLAGS_##f) | |
29 | #define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f) | |
30 | #define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0) | |
31 | ||
f1407d5c KM |
32 | /* |
33 | * for debug | |
34 | */ | |
35 | static char *usbhsp_pipe_name[] = { | |
36 | [USB_ENDPOINT_XFER_CONTROL] = "DCP", | |
37 | [USB_ENDPOINT_XFER_BULK] = "BULK", | |
38 | [USB_ENDPOINT_XFER_INT] = "INT", | |
39 | [USB_ENDPOINT_XFER_ISOC] = "ISO", | |
40 | }; | |
41 | ||
3cf8ed12 KM |
42 | char *usbhs_pipe_name(struct usbhs_pipe *pipe) |
43 | { | |
44 | return usbhsp_pipe_name[usbhs_pipe_type(pipe)]; | |
45 | } | |
46 | ||
f1407d5c KM |
47 | /* |
48 | * DCPCTR/PIPEnCTR functions | |
49 | */ | |
50 | static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val) | |
51 | { | |
e8d548d5 | 52 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
f1407d5c KM |
53 | int offset = usbhsp_addr_offset(pipe); |
54 | ||
e8d548d5 | 55 | if (usbhs_pipe_is_dcp(pipe)) |
f1407d5c KM |
56 | usbhs_bset(priv, DCPCTR, mask, val); |
57 | else | |
58 | usbhs_bset(priv, PIPEnCTR + offset, mask, val); | |
59 | } | |
60 | ||
61 | static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe) | |
62 | { | |
e8d548d5 | 63 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
f1407d5c KM |
64 | int offset = usbhsp_addr_offset(pipe); |
65 | ||
e8d548d5 | 66 | if (usbhs_pipe_is_dcp(pipe)) |
f1407d5c KM |
67 | return usbhs_read(priv, DCPCTR); |
68 | else | |
69 | return usbhs_read(priv, PIPEnCTR + offset); | |
70 | } | |
71 | ||
72 | /* | |
73 | * DCP/PIPE functions | |
74 | */ | |
75 | static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, | |
76 | u16 dcp_reg, u16 pipe_reg, | |
77 | u16 mask, u16 val) | |
78 | { | |
e8d548d5 | 79 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
f1407d5c | 80 | |
e8d548d5 | 81 | if (usbhs_pipe_is_dcp(pipe)) |
f1407d5c KM |
82 | usbhs_bset(priv, dcp_reg, mask, val); |
83 | else | |
84 | usbhs_bset(priv, pipe_reg, mask, val); | |
85 | } | |
86 | ||
f1407d5c KM |
87 | /* |
88 | * DCPCFG/PIPECFG functions | |
89 | */ | |
90 | static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) | |
91 | { | |
92 | __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val); | |
93 | } | |
94 | ||
95 | /* | |
96 | * PIPEBUF | |
97 | */ | |
98 | static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val) | |
99 | { | |
e8d548d5 | 100 | if (usbhs_pipe_is_dcp(pipe)) |
f1407d5c KM |
101 | return; |
102 | ||
103 | __usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val); | |
104 | } | |
105 | ||
106 | /* | |
107 | * DCPMAXP/PIPEMAXP | |
108 | */ | |
109 | static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val) | |
110 | { | |
111 | __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val); | |
112 | } | |
113 | ||
f1407d5c KM |
114 | /* |
115 | * pipe control functions | |
116 | */ | |
117 | static void usbhsp_pipe_select(struct usbhs_pipe *pipe) | |
118 | { | |
e8d548d5 | 119 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
f1407d5c KM |
120 | |
121 | /* | |
122 | * On pipe, this is necessary before | |
123 | * accesses to below registers. | |
124 | * | |
125 | * PIPESEL : usbhsp_pipe_select | |
126 | * PIPECFG : usbhsp_pipe_cfg_xxx | |
127 | * PIPEBUF : usbhsp_pipe_buf_xxx | |
128 | * PIPEMAXP : usbhsp_pipe_maxp_xxx | |
129 | * PIPEPERI | |
130 | */ | |
131 | ||
132 | /* | |
133 | * if pipe is dcp, no pipe is selected. | |
134 | * it is no problem, because dcp have its register | |
135 | */ | |
136 | usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe)); | |
137 | } | |
138 | ||
139 | static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) | |
140 | { | |
e8d548d5 | 141 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
f1407d5c KM |
142 | int timeout = 1024; |
143 | u16 val; | |
144 | ||
145 | /* | |
146 | * make sure.... | |
147 | * | |
148 | * Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is | |
149 | * specified by the CURPIPE bits. | |
150 | * When changing the setting of this bit after changing | |
151 | * the PID bits for the selected pipe from BUF to NAK, | |
152 | * check that CSSTS = 0 and PBUSY = 0. | |
153 | */ | |
154 | ||
155 | /* | |
156 | * CURPIPE bit = 0 | |
157 | * | |
158 | * see also | |
159 | * "Operation" | |
160 | * - "Pipe Control" | |
161 | * - "Pipe Control Registers Switching Procedure" | |
162 | */ | |
163 | usbhs_write(priv, CFIFOSEL, 0); | |
e8d548d5 | 164 | usbhs_pipe_disable(pipe); |
f1407d5c KM |
165 | |
166 | do { | |
167 | val = usbhsp_pipectrl_get(pipe); | |
168 | val &= CSSTS | PID_MASK; | |
169 | if (!val) | |
170 | return 0; | |
171 | ||
172 | udelay(10); | |
173 | ||
174 | } while (timeout--); | |
175 | ||
f1407d5c KM |
176 | return -EBUSY; |
177 | } | |
178 | ||
e8d548d5 | 179 | int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe) |
f1407d5c KM |
180 | { |
181 | u16 val; | |
182 | ||
183 | val = usbhsp_pipectrl_get(pipe); | |
184 | if (val & BSTS) | |
185 | return 0; | |
186 | ||
187 | return -EBUSY; | |
188 | } | |
189 | ||
190 | /* | |
191 | * PID ctrl | |
192 | */ | |
193 | static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe) | |
194 | { | |
195 | u16 pid = usbhsp_pipectrl_get(pipe); | |
196 | ||
197 | pid &= PID_MASK; | |
198 | ||
199 | /* | |
200 | * see | |
201 | * "Pipe n Control Register" - "PID" | |
202 | */ | |
203 | switch (pid) { | |
204 | case PID_STALL11: | |
205 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10); | |
206 | /* fall-through */ | |
207 | case PID_STALL10: | |
208 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); | |
209 | } | |
210 | } | |
211 | ||
e8d548d5 | 212 | void usbhs_pipe_disable(struct usbhs_pipe *pipe) |
f1407d5c | 213 | { |
c786e09c KM |
214 | int timeout = 1024; |
215 | u16 val; | |
216 | ||
f1407d5c KM |
217 | /* see "Pipe n Control Register" - "PID" */ |
218 | __usbhsp_pid_try_nak_if_stall(pipe); | |
219 | ||
220 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); | |
c786e09c KM |
221 | |
222 | do { | |
223 | val = usbhsp_pipectrl_get(pipe); | |
224 | val &= PBUSY; | |
225 | if (!val) | |
226 | break; | |
227 | ||
228 | udelay(10); | |
229 | } while (timeout--); | |
f1407d5c KM |
230 | } |
231 | ||
e8d548d5 | 232 | void usbhs_pipe_enable(struct usbhs_pipe *pipe) |
f1407d5c KM |
233 | { |
234 | /* see "Pipe n Control Register" - "PID" */ | |
235 | __usbhsp_pid_try_nak_if_stall(pipe); | |
236 | ||
237 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF); | |
238 | } | |
239 | ||
e8d548d5 | 240 | void usbhs_pipe_stall(struct usbhs_pipe *pipe) |
f1407d5c KM |
241 | { |
242 | u16 pid = usbhsp_pipectrl_get(pipe); | |
243 | ||
244 | pid &= PID_MASK; | |
245 | ||
246 | /* | |
247 | * see | |
248 | * "Pipe n Control Register" - "PID" | |
249 | */ | |
250 | switch (pid) { | |
251 | case PID_NAK: | |
252 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10); | |
253 | break; | |
254 | case PID_BUF: | |
255 | usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11); | |
256 | break; | |
257 | } | |
258 | } | |
259 | ||
9cf1b06e KM |
260 | int usbhs_pipe_is_stall(struct usbhs_pipe *pipe) |
261 | { | |
262 | u16 pid = usbhsp_pipectrl_get(pipe) & PID_MASK; | |
263 | ||
264 | return (int)(pid == PID_STALL10 || pid == PID_STALL11); | |
265 | } | |
266 | ||
f1407d5c KM |
267 | /* |
268 | * pipe setup | |
269 | */ | |
270 | static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) | |
271 | { | |
272 | /* | |
273 | * only ISO / BULK pipe can use double buffer | |
274 | */ | |
356db7ed KM |
275 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) || |
276 | usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) | |
f1407d5c KM |
277 | return 1; |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, | |
f5aa889f KM |
283 | int is_host, |
284 | int dir_in) | |
f1407d5c KM |
285 | { |
286 | u16 type = 0; | |
287 | u16 bfre = 0; | |
288 | u16 dblb = 0; | |
289 | u16 cntmd = 0; | |
290 | u16 dir = 0; | |
291 | u16 epnum = 0; | |
292 | u16 shtnak = 0; | |
293 | u16 type_array[] = { | |
294 | [USB_ENDPOINT_XFER_BULK] = TYPE_BULK, | |
295 | [USB_ENDPOINT_XFER_INT] = TYPE_INT, | |
296 | [USB_ENDPOINT_XFER_ISOC] = TYPE_ISO, | |
297 | }; | |
298 | int is_double = usbhsp_possible_double_buffer(pipe); | |
299 | ||
e8d548d5 | 300 | if (usbhs_pipe_is_dcp(pipe)) |
f1407d5c KM |
301 | return -EINVAL; |
302 | ||
303 | /* | |
304 | * PIPECFG | |
305 | * | |
306 | * see | |
307 | * - "Register Descriptions" - "PIPECFG" register | |
308 | * - "Features" - "Pipe configuration" | |
309 | * - "Operation" - "Pipe Control" | |
310 | */ | |
311 | ||
312 | /* TYPE */ | |
356db7ed | 313 | type = type_array[usbhs_pipe_type(pipe)]; |
f1407d5c KM |
314 | |
315 | /* BFRE */ | |
356db7ed KM |
316 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || |
317 | usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) | |
f1407d5c KM |
318 | bfre = 0; /* FIXME */ |
319 | ||
320 | /* DBLB */ | |
356db7ed KM |
321 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || |
322 | usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) | |
f1407d5c KM |
323 | dblb = (is_double) ? DBLB : 0; |
324 | ||
325 | /* CNTMD */ | |
356db7ed | 326 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) |
f1407d5c KM |
327 | cntmd = 0; /* FIXME */ |
328 | ||
329 | /* DIR */ | |
f5aa889f | 330 | if (dir_in) |
ad6f2a8b | 331 | usbhsp_flags_set(pipe, IS_DIR_HOST); |
f1407d5c | 332 | |
f5aa889f KM |
333 | if ((is_host && !dir_in) || |
334 | (!is_host && dir_in)) | |
f1407d5c KM |
335 | dir |= DIR_OUT; |
336 | ||
ad6f2a8b KM |
337 | if (!dir) |
338 | usbhsp_flags_set(pipe, IS_DIR_IN); | |
339 | ||
f1407d5c | 340 | /* SHTNAK */ |
356db7ed | 341 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) && |
f1407d5c KM |
342 | !dir) |
343 | shtnak = SHTNAK; | |
344 | ||
345 | /* EPNUM */ | |
f5aa889f | 346 | epnum = 0; /* see usbhs_pipe_config_update() */ |
f1407d5c KM |
347 | |
348 | return type | | |
349 | bfre | | |
350 | dblb | | |
351 | cntmd | | |
352 | dir | | |
353 | shtnak | | |
354 | epnum; | |
355 | } | |
356 | ||
f5aa889f | 357 | static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) |
f1407d5c | 358 | { |
e8d548d5 KM |
359 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
360 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); | |
f1407d5c KM |
361 | struct device *dev = usbhs_priv_to_dev(priv); |
362 | int pipe_num = usbhs_pipe_number(pipe); | |
363 | int is_double = usbhsp_possible_double_buffer(pipe); | |
364 | u16 buff_size; | |
365 | u16 bufnmb; | |
366 | u16 bufnmb_cnt; | |
367 | ||
368 | /* | |
369 | * PIPEBUF | |
370 | * | |
371 | * see | |
372 | * - "Register Descriptions" - "PIPEBUF" register | |
373 | * - "Features" - "Pipe configuration" | |
374 | * - "Operation" - "FIFO Buffer Memory" | |
375 | * - "Operation" - "Pipe Control" | |
376 | * | |
377 | * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724) | |
378 | * | |
379 | * BUFNMB: PIPE | |
380 | * 0: pipe0 (DCP 256byte) | |
381 | * 1: - | |
382 | * 2: - | |
383 | * 3: - | |
384 | * 4: pipe6 (INT 64byte) | |
385 | * 5: pipe7 (INT 64byte) | |
386 | * 6: pipe8 (INT 64byte) | |
387 | * 7: pipe9 (INT 64byte) | |
388 | * 8 - xx: free (for BULK, ISOC) | |
389 | */ | |
390 | ||
391 | /* | |
392 | * FIXME | |
393 | * | |
394 | * it doesn't have good buffer allocator | |
395 | * | |
396 | * DCP : 256 byte | |
397 | * BULK: 512 byte | |
398 | * INT : 64 byte | |
399 | * ISOC: 512 byte | |
400 | */ | |
356db7ed | 401 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) |
f1407d5c | 402 | buff_size = 256; |
356db7ed | 403 | else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) |
f1407d5c KM |
404 | buff_size = 64; |
405 | else | |
406 | buff_size = 512; | |
407 | ||
408 | /* change buff_size to register value */ | |
409 | bufnmb_cnt = (buff_size / 64) - 1; | |
410 | ||
411 | /* BUFNMB has been reserved for INT pipe | |
412 | * see above */ | |
356db7ed | 413 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) { |
f1407d5c KM |
414 | bufnmb = pipe_num - 2; |
415 | } else { | |
416 | bufnmb = info->bufnmb_last; | |
417 | info->bufnmb_last += bufnmb_cnt + 1; | |
418 | ||
419 | /* | |
420 | * double buffer | |
421 | */ | |
422 | if (is_double) | |
423 | info->bufnmb_last += bufnmb_cnt + 1; | |
424 | } | |
425 | ||
426 | dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n", | |
427 | pipe_num, buff_size, bufnmb); | |
428 | ||
429 | return (0x1f & bufnmb_cnt) << 10 | | |
430 | (0xff & bufnmb) << 0; | |
431 | } | |
432 | ||
bc6fbf59 KM |
433 | void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, |
434 | u16 epnum, u16 maxp) | |
f5aa889f | 435 | { |
bc6fbf59 KM |
436 | if (devsel > 0xA) { |
437 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | |
438 | struct device *dev = usbhs_priv_to_dev(priv); | |
439 | ||
440 | dev_err(dev, "devsel error %d\n", devsel); | |
441 | ||
442 | devsel = 0; | |
443 | } | |
444 | ||
f5aa889f KM |
445 | usbhsp_pipe_barrier(pipe); |
446 | ||
7fd097e7 KM |
447 | pipe->maxp = maxp; |
448 | ||
f5aa889f | 449 | usbhsp_pipe_select(pipe); |
bc6fbf59 KM |
450 | usbhsp_pipe_maxp_set(pipe, 0xFFFF, |
451 | (devsel << 12) | | |
452 | maxp); | |
f5aa889f KM |
453 | |
454 | if (!usbhs_pipe_is_dcp(pipe)) | |
455 | usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); | |
456 | } | |
457 | ||
f1407d5c KM |
458 | /* |
459 | * pipe control | |
460 | */ | |
461 | int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) | |
462 | { | |
7fd097e7 KM |
463 | /* |
464 | * see | |
465 | * usbhs_pipe_config_update() | |
466 | * usbhs_dcp_malloc() | |
467 | */ | |
468 | return pipe->maxp; | |
f1407d5c KM |
469 | } |
470 | ||
471 | int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) | |
472 | { | |
473 | return usbhsp_flags_has(pipe, IS_DIR_IN); | |
474 | } | |
475 | ||
ad6f2a8b KM |
476 | int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) |
477 | { | |
478 | return usbhsp_flags_has(pipe, IS_DIR_HOST); | |
479 | } | |
480 | ||
6e6db82b | 481 | void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data) |
f1407d5c | 482 | { |
6e6db82b KM |
483 | u16 mask = (SQCLR | SQSET); |
484 | u16 val = (data) ? SQSET : SQCLR; | |
485 | ||
486 | usbhsp_pipectrl_set(pipe, mask, val); | |
f1407d5c KM |
487 | } |
488 | ||
08e6c611 KM |
489 | void usbhs_pipe_clear(struct usbhs_pipe *pipe) |
490 | { | |
491 | usbhsp_pipectrl_set(pipe, ACLRM, ACLRM); | |
492 | usbhsp_pipectrl_set(pipe, ACLRM, 0); | |
493 | } | |
494 | ||
f1407d5c KM |
495 | static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) |
496 | { | |
497 | struct usbhs_pipe *pos, *pipe; | |
498 | int i; | |
499 | ||
500 | /* | |
501 | * find target pipe | |
502 | */ | |
503 | pipe = NULL; | |
504 | usbhs_for_each_pipe_with_dcp(pos, priv, i) { | |
356db7ed | 505 | if (!usbhs_pipe_type_is(pos, type)) |
f1407d5c KM |
506 | continue; |
507 | if (usbhsp_flags_has(pos, IS_USED)) | |
508 | continue; | |
509 | ||
510 | pipe = pos; | |
511 | break; | |
512 | } | |
513 | ||
514 | if (!pipe) | |
515 | return NULL; | |
516 | ||
517 | /* | |
518 | * initialize pipe flags | |
519 | */ | |
520 | usbhsp_flags_init(pipe); | |
521 | usbhsp_flags_set(pipe, IS_USED); | |
522 | ||
523 | return pipe; | |
524 | } | |
525 | ||
4bd04811 | 526 | void usbhs_pipe_init(struct usbhs_priv *priv, |
e73a9891 | 527 | int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) |
f1407d5c | 528 | { |
e8d548d5 | 529 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
530 | struct usbhs_pipe *pipe; |
531 | int i; | |
532 | ||
533 | /* | |
534 | * FIXME | |
535 | * | |
536 | * driver needs good allocator. | |
537 | * | |
538 | * find first free buffer area (BULK, ISOC) | |
539 | * (DCP, INT area is fixed) | |
540 | * | |
541 | * buffer number 0 - 3 have been reserved for DCP | |
542 | * see | |
543 | * usbhsp_to_bufnmb | |
544 | */ | |
545 | info->bufnmb_last = 4; | |
546 | usbhs_for_each_pipe_with_dcp(pipe, priv, i) { | |
356db7ed | 547 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) |
f1407d5c KM |
548 | info->bufnmb_last++; |
549 | ||
550 | usbhsp_flags_init(pipe); | |
d77e3f4e | 551 | pipe->fifo = NULL; |
f1407d5c | 552 | pipe->mod_private = NULL; |
6acb95d4 | 553 | INIT_LIST_HEAD(&pipe->list); |
45e13e6e | 554 | |
e8d548d5 | 555 | /* pipe force init */ |
08e6c611 | 556 | usbhs_pipe_clear(pipe); |
f1407d5c | 557 | } |
4bd04811 | 558 | |
e73a9891 | 559 | info->dma_map_ctrl = dma_map_ctrl; |
f1407d5c KM |
560 | } |
561 | ||
562 | struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, | |
f5aa889f KM |
563 | int endpoint_type, |
564 | int dir_in) | |
f1407d5c KM |
565 | { |
566 | struct device *dev = usbhs_priv_to_dev(priv); | |
f1407d5c | 567 | struct usbhs_pipe *pipe; |
0deb3e77 | 568 | int is_host = usbhs_mod_is_host(priv); |
f1407d5c | 569 | int ret; |
f5aa889f | 570 | u16 pipecfg, pipebuf; |
f1407d5c | 571 | |
f5aa889f | 572 | pipe = usbhsp_get_pipe(priv, endpoint_type); |
f429ea3f KM |
573 | if (!pipe) { |
574 | dev_err(dev, "can't get pipe (%s)\n", | |
f5aa889f | 575 | usbhsp_pipe_name[endpoint_type]); |
f1407d5c | 576 | return NULL; |
f429ea3f | 577 | } |
f1407d5c | 578 | |
6acb95d4 KM |
579 | INIT_LIST_HEAD(&pipe->list); |
580 | ||
e8d548d5 | 581 | usbhs_pipe_disable(pipe); |
f1407d5c KM |
582 | |
583 | /* make sure pipe is not busy */ | |
584 | ret = usbhsp_pipe_barrier(pipe); | |
585 | if (ret < 0) { | |
586 | dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe)); | |
587 | return NULL; | |
588 | } | |
589 | ||
f5aa889f KM |
590 | pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in); |
591 | pipebuf = usbhsp_setup_pipebuff(pipe); | |
f1407d5c | 592 | |
f1407d5c KM |
593 | usbhsp_pipe_select(pipe); |
594 | usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); | |
595 | usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); | |
f1407d5c | 596 | |
6e6db82b | 597 | usbhs_pipe_sequence_data0(pipe); |
f1407d5c KM |
598 | |
599 | dev_dbg(dev, "enable pipe %d : %s (%s)\n", | |
600 | usbhs_pipe_number(pipe), | |
3cf8ed12 | 601 | usbhs_pipe_name(pipe), |
f1407d5c KM |
602 | usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); |
603 | ||
f5aa889f KM |
604 | /* |
605 | * epnum / maxp are still not set to this pipe. | |
606 | * call usbhs_pipe_config_update() after this function !! | |
607 | */ | |
608 | ||
f1407d5c KM |
609 | return pipe; |
610 | } | |
611 | ||
d77e3f4e KM |
612 | void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) |
613 | { | |
614 | if (pipe->fifo) | |
615 | pipe->fifo->pipe = NULL; | |
616 | ||
617 | pipe->fifo = fifo; | |
618 | ||
619 | if (fifo) | |
620 | fifo->pipe = pipe; | |
621 | } | |
622 | ||
623 | ||
f1407d5c KM |
624 | /* |
625 | * dcp control | |
626 | */ | |
627 | struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) | |
628 | { | |
629 | struct usbhs_pipe *pipe; | |
630 | ||
631 | pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL); | |
632 | if (!pipe) | |
633 | return NULL; | |
634 | ||
f5aa889f KM |
635 | INIT_LIST_HEAD(&pipe->list); |
636 | ||
f1407d5c | 637 | /* |
f5aa889f | 638 | * call usbhs_pipe_config_update() after this function !! |
f1407d5c KM |
639 | */ |
640 | ||
f1407d5c KM |
641 | return pipe; |
642 | } | |
643 | ||
644 | void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) | |
645 | { | |
e2eddc61 KM |
646 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
647 | ||
e8d548d5 | 648 | WARN_ON(!usbhs_pipe_is_dcp(pipe)); |
f1407d5c | 649 | |
e8d548d5 | 650 | usbhs_pipe_enable(pipe); |
e2eddc61 KM |
651 | |
652 | if (!usbhs_mod_is_host(priv)) /* funconly */ | |
653 | usbhsp_pipectrl_set(pipe, CCPL, CCPL); | |
f1407d5c KM |
654 | } |
655 | ||
92352071 KM |
656 | void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) |
657 | { | |
658 | usbhsp_pipe_cfg_set(pipe, DIR_OUT, | |
659 | dir_out ? DIR_OUT : 0); | |
660 | } | |
661 | ||
f1407d5c KM |
662 | /* |
663 | * pipe module function | |
664 | */ | |
665 | int usbhs_pipe_probe(struct usbhs_priv *priv) | |
666 | { | |
e8d548d5 | 667 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
668 | struct usbhs_pipe *pipe; |
669 | struct device *dev = usbhs_priv_to_dev(priv); | |
670 | u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); | |
671 | int pipe_size = usbhs_get_dparam(priv, pipe_size); | |
672 | int i; | |
673 | ||
674 | /* This driver expects 1st pipe is DCP */ | |
675 | if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) { | |
676 | dev_err(dev, "1st PIPE is not DCP\n"); | |
677 | return -EINVAL; | |
678 | } | |
679 | ||
680 | info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL); | |
681 | if (!info->pipe) { | |
682 | dev_err(dev, "Could not allocate pipe\n"); | |
683 | return -ENOMEM; | |
684 | } | |
685 | ||
686 | info->size = pipe_size; | |
687 | ||
688 | /* | |
689 | * init pipe | |
690 | */ | |
691 | usbhs_for_each_pipe_with_dcp(pipe, priv, i) { | |
692 | pipe->priv = priv; | |
356db7ed KM |
693 | |
694 | usbhs_pipe_type(pipe) = | |
695 | pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; | |
f1407d5c KM |
696 | |
697 | dev_dbg(dev, "pipe %x\t: %s\n", | |
698 | i, usbhsp_pipe_name[pipe_type[i]]); | |
699 | } | |
700 | ||
701 | return 0; | |
702 | } | |
703 | ||
704 | void usbhs_pipe_remove(struct usbhs_priv *priv) | |
705 | { | |
e8d548d5 | 706 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
707 | |
708 | kfree(info->pipe); | |
709 | } |