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 | |
14ff96e0 | 333 | if (!!is_host ^ !!dir_in) |
f1407d5c KM |
334 | dir |= DIR_OUT; |
335 | ||
ad6f2a8b KM |
336 | if (!dir) |
337 | usbhsp_flags_set(pipe, IS_DIR_IN); | |
338 | ||
f1407d5c | 339 | /* SHTNAK */ |
356db7ed | 340 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) && |
f1407d5c KM |
341 | !dir) |
342 | shtnak = SHTNAK; | |
343 | ||
344 | /* EPNUM */ | |
f5aa889f | 345 | epnum = 0; /* see usbhs_pipe_config_update() */ |
f1407d5c KM |
346 | |
347 | return type | | |
348 | bfre | | |
349 | dblb | | |
350 | cntmd | | |
351 | dir | | |
352 | shtnak | | |
353 | epnum; | |
354 | } | |
355 | ||
f5aa889f | 356 | static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) |
f1407d5c | 357 | { |
e8d548d5 KM |
358 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
359 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); | |
f1407d5c KM |
360 | struct device *dev = usbhs_priv_to_dev(priv); |
361 | int pipe_num = usbhs_pipe_number(pipe); | |
362 | int is_double = usbhsp_possible_double_buffer(pipe); | |
363 | u16 buff_size; | |
364 | u16 bufnmb; | |
365 | u16 bufnmb_cnt; | |
366 | ||
367 | /* | |
368 | * PIPEBUF | |
369 | * | |
370 | * see | |
371 | * - "Register Descriptions" - "PIPEBUF" register | |
372 | * - "Features" - "Pipe configuration" | |
373 | * - "Operation" - "FIFO Buffer Memory" | |
374 | * - "Operation" - "Pipe Control" | |
375 | * | |
376 | * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724) | |
377 | * | |
378 | * BUFNMB: PIPE | |
379 | * 0: pipe0 (DCP 256byte) | |
380 | * 1: - | |
381 | * 2: - | |
382 | * 3: - | |
383 | * 4: pipe6 (INT 64byte) | |
384 | * 5: pipe7 (INT 64byte) | |
385 | * 6: pipe8 (INT 64byte) | |
386 | * 7: pipe9 (INT 64byte) | |
387 | * 8 - xx: free (for BULK, ISOC) | |
388 | */ | |
389 | ||
390 | /* | |
391 | * FIXME | |
392 | * | |
393 | * it doesn't have good buffer allocator | |
394 | * | |
395 | * DCP : 256 byte | |
396 | * BULK: 512 byte | |
397 | * INT : 64 byte | |
398 | * ISOC: 512 byte | |
399 | */ | |
356db7ed | 400 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) |
f1407d5c | 401 | buff_size = 256; |
356db7ed | 402 | else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) |
f1407d5c KM |
403 | buff_size = 64; |
404 | else | |
405 | buff_size = 512; | |
406 | ||
407 | /* change buff_size to register value */ | |
408 | bufnmb_cnt = (buff_size / 64) - 1; | |
409 | ||
410 | /* BUFNMB has been reserved for INT pipe | |
411 | * see above */ | |
356db7ed | 412 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) { |
f1407d5c KM |
413 | bufnmb = pipe_num - 2; |
414 | } else { | |
415 | bufnmb = info->bufnmb_last; | |
416 | info->bufnmb_last += bufnmb_cnt + 1; | |
417 | ||
418 | /* | |
419 | * double buffer | |
420 | */ | |
421 | if (is_double) | |
422 | info->bufnmb_last += bufnmb_cnt + 1; | |
423 | } | |
424 | ||
425 | dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n", | |
426 | pipe_num, buff_size, bufnmb); | |
427 | ||
428 | return (0x1f & bufnmb_cnt) << 10 | | |
429 | (0xff & bufnmb) << 0; | |
430 | } | |
431 | ||
bc6fbf59 KM |
432 | void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, |
433 | u16 epnum, u16 maxp) | |
f5aa889f | 434 | { |
bc6fbf59 KM |
435 | if (devsel > 0xA) { |
436 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | |
437 | struct device *dev = usbhs_priv_to_dev(priv); | |
438 | ||
439 | dev_err(dev, "devsel error %d\n", devsel); | |
440 | ||
441 | devsel = 0; | |
442 | } | |
443 | ||
f5aa889f KM |
444 | usbhsp_pipe_barrier(pipe); |
445 | ||
7fd097e7 KM |
446 | pipe->maxp = maxp; |
447 | ||
f5aa889f | 448 | usbhsp_pipe_select(pipe); |
bc6fbf59 KM |
449 | usbhsp_pipe_maxp_set(pipe, 0xFFFF, |
450 | (devsel << 12) | | |
451 | maxp); | |
f5aa889f KM |
452 | |
453 | if (!usbhs_pipe_is_dcp(pipe)) | |
454 | usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); | |
455 | } | |
456 | ||
f1407d5c KM |
457 | /* |
458 | * pipe control | |
459 | */ | |
460 | int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) | |
461 | { | |
7fd097e7 KM |
462 | /* |
463 | * see | |
464 | * usbhs_pipe_config_update() | |
465 | * usbhs_dcp_malloc() | |
466 | */ | |
467 | return pipe->maxp; | |
f1407d5c KM |
468 | } |
469 | ||
470 | int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) | |
471 | { | |
472 | return usbhsp_flags_has(pipe, IS_DIR_IN); | |
473 | } | |
474 | ||
ad6f2a8b KM |
475 | int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) |
476 | { | |
477 | return usbhsp_flags_has(pipe, IS_DIR_HOST); | |
478 | } | |
479 | ||
3edeee38 | 480 | void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) |
f1407d5c | 481 | { |
6e6db82b | 482 | u16 mask = (SQCLR | SQSET); |
3edeee38 KM |
483 | u16 val; |
484 | ||
485 | /* | |
486 | * sequence | |
487 | * 0 : data0 | |
488 | * 1 : data1 | |
489 | * -1 : no change | |
490 | */ | |
491 | switch (sequence) { | |
492 | case 0: | |
493 | val = SQCLR; | |
494 | break; | |
495 | case 1: | |
496 | val = SQSET; | |
497 | break; | |
498 | default: | |
499 | return; | |
500 | } | |
6e6db82b KM |
501 | |
502 | usbhsp_pipectrl_set(pipe, mask, val); | |
f1407d5c KM |
503 | } |
504 | ||
08e6c611 KM |
505 | void usbhs_pipe_clear(struct usbhs_pipe *pipe) |
506 | { | |
507 | usbhsp_pipectrl_set(pipe, ACLRM, ACLRM); | |
508 | usbhsp_pipectrl_set(pipe, ACLRM, 0); | |
509 | } | |
510 | ||
f1407d5c KM |
511 | static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) |
512 | { | |
513 | struct usbhs_pipe *pos, *pipe; | |
514 | int i; | |
515 | ||
516 | /* | |
517 | * find target pipe | |
518 | */ | |
519 | pipe = NULL; | |
520 | usbhs_for_each_pipe_with_dcp(pos, priv, i) { | |
356db7ed | 521 | if (!usbhs_pipe_type_is(pos, type)) |
f1407d5c KM |
522 | continue; |
523 | if (usbhsp_flags_has(pos, IS_USED)) | |
524 | continue; | |
525 | ||
526 | pipe = pos; | |
527 | break; | |
528 | } | |
529 | ||
530 | if (!pipe) | |
531 | return NULL; | |
532 | ||
533 | /* | |
534 | * initialize pipe flags | |
535 | */ | |
536 | usbhsp_flags_init(pipe); | |
537 | usbhsp_flags_set(pipe, IS_USED); | |
538 | ||
539 | return pipe; | |
540 | } | |
541 | ||
4bd04811 | 542 | void usbhs_pipe_init(struct usbhs_priv *priv, |
e73a9891 | 543 | int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) |
f1407d5c | 544 | { |
e8d548d5 | 545 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
546 | struct usbhs_pipe *pipe; |
547 | int i; | |
548 | ||
549 | /* | |
550 | * FIXME | |
551 | * | |
552 | * driver needs good allocator. | |
553 | * | |
554 | * find first free buffer area (BULK, ISOC) | |
555 | * (DCP, INT area is fixed) | |
556 | * | |
557 | * buffer number 0 - 3 have been reserved for DCP | |
558 | * see | |
559 | * usbhsp_to_bufnmb | |
560 | */ | |
561 | info->bufnmb_last = 4; | |
562 | usbhs_for_each_pipe_with_dcp(pipe, priv, i) { | |
356db7ed | 563 | if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) |
f1407d5c KM |
564 | info->bufnmb_last++; |
565 | ||
566 | usbhsp_flags_init(pipe); | |
d77e3f4e | 567 | pipe->fifo = NULL; |
f1407d5c | 568 | pipe->mod_private = NULL; |
6acb95d4 | 569 | INIT_LIST_HEAD(&pipe->list); |
45e13e6e | 570 | |
e8d548d5 | 571 | /* pipe force init */ |
08e6c611 | 572 | usbhs_pipe_clear(pipe); |
f1407d5c | 573 | } |
4bd04811 | 574 | |
e73a9891 | 575 | info->dma_map_ctrl = dma_map_ctrl; |
f1407d5c KM |
576 | } |
577 | ||
578 | struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, | |
f5aa889f KM |
579 | int endpoint_type, |
580 | int dir_in) | |
f1407d5c KM |
581 | { |
582 | struct device *dev = usbhs_priv_to_dev(priv); | |
f1407d5c | 583 | struct usbhs_pipe *pipe; |
0deb3e77 | 584 | int is_host = usbhs_mod_is_host(priv); |
f1407d5c | 585 | int ret; |
f5aa889f | 586 | u16 pipecfg, pipebuf; |
f1407d5c | 587 | |
f5aa889f | 588 | pipe = usbhsp_get_pipe(priv, endpoint_type); |
f429ea3f KM |
589 | if (!pipe) { |
590 | dev_err(dev, "can't get pipe (%s)\n", | |
f5aa889f | 591 | usbhsp_pipe_name[endpoint_type]); |
f1407d5c | 592 | return NULL; |
f429ea3f | 593 | } |
f1407d5c | 594 | |
6acb95d4 KM |
595 | INIT_LIST_HEAD(&pipe->list); |
596 | ||
e8d548d5 | 597 | usbhs_pipe_disable(pipe); |
f1407d5c KM |
598 | |
599 | /* make sure pipe is not busy */ | |
600 | ret = usbhsp_pipe_barrier(pipe); | |
601 | if (ret < 0) { | |
602 | dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe)); | |
603 | return NULL; | |
604 | } | |
605 | ||
f5aa889f KM |
606 | pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in); |
607 | pipebuf = usbhsp_setup_pipebuff(pipe); | |
f1407d5c | 608 | |
f1407d5c KM |
609 | usbhsp_pipe_select(pipe); |
610 | usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); | |
611 | usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); | |
f1407d5c | 612 | |
6e6db82b | 613 | usbhs_pipe_sequence_data0(pipe); |
f1407d5c KM |
614 | |
615 | dev_dbg(dev, "enable pipe %d : %s (%s)\n", | |
616 | usbhs_pipe_number(pipe), | |
3cf8ed12 | 617 | usbhs_pipe_name(pipe), |
f1407d5c KM |
618 | usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); |
619 | ||
f5aa889f KM |
620 | /* |
621 | * epnum / maxp are still not set to this pipe. | |
622 | * call usbhs_pipe_config_update() after this function !! | |
623 | */ | |
624 | ||
f1407d5c KM |
625 | return pipe; |
626 | } | |
627 | ||
d77e3f4e KM |
628 | void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) |
629 | { | |
630 | if (pipe->fifo) | |
631 | pipe->fifo->pipe = NULL; | |
632 | ||
633 | pipe->fifo = fifo; | |
634 | ||
635 | if (fifo) | |
636 | fifo->pipe = pipe; | |
637 | } | |
638 | ||
639 | ||
f1407d5c KM |
640 | /* |
641 | * dcp control | |
642 | */ | |
643 | struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) | |
644 | { | |
645 | struct usbhs_pipe *pipe; | |
646 | ||
647 | pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL); | |
648 | if (!pipe) | |
649 | return NULL; | |
650 | ||
f5aa889f KM |
651 | INIT_LIST_HEAD(&pipe->list); |
652 | ||
f1407d5c | 653 | /* |
f5aa889f | 654 | * call usbhs_pipe_config_update() after this function !! |
f1407d5c KM |
655 | */ |
656 | ||
f1407d5c KM |
657 | return pipe; |
658 | } | |
659 | ||
660 | void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) | |
661 | { | |
e2eddc61 KM |
662 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
663 | ||
e8d548d5 | 664 | WARN_ON(!usbhs_pipe_is_dcp(pipe)); |
f1407d5c | 665 | |
e8d548d5 | 666 | usbhs_pipe_enable(pipe); |
e2eddc61 KM |
667 | |
668 | if (!usbhs_mod_is_host(priv)) /* funconly */ | |
669 | usbhsp_pipectrl_set(pipe, CCPL, CCPL); | |
f1407d5c KM |
670 | } |
671 | ||
92352071 KM |
672 | void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) |
673 | { | |
674 | usbhsp_pipe_cfg_set(pipe, DIR_OUT, | |
675 | dir_out ? DIR_OUT : 0); | |
676 | } | |
677 | ||
f1407d5c KM |
678 | /* |
679 | * pipe module function | |
680 | */ | |
681 | int usbhs_pipe_probe(struct usbhs_priv *priv) | |
682 | { | |
e8d548d5 | 683 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
684 | struct usbhs_pipe *pipe; |
685 | struct device *dev = usbhs_priv_to_dev(priv); | |
686 | u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); | |
687 | int pipe_size = usbhs_get_dparam(priv, pipe_size); | |
688 | int i; | |
689 | ||
690 | /* This driver expects 1st pipe is DCP */ | |
691 | if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) { | |
692 | dev_err(dev, "1st PIPE is not DCP\n"); | |
693 | return -EINVAL; | |
694 | } | |
695 | ||
696 | info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL); | |
697 | if (!info->pipe) { | |
698 | dev_err(dev, "Could not allocate pipe\n"); | |
699 | return -ENOMEM; | |
700 | } | |
701 | ||
702 | info->size = pipe_size; | |
703 | ||
704 | /* | |
705 | * init pipe | |
706 | */ | |
707 | usbhs_for_each_pipe_with_dcp(pipe, priv, i) { | |
708 | pipe->priv = priv; | |
356db7ed KM |
709 | |
710 | usbhs_pipe_type(pipe) = | |
711 | pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; | |
f1407d5c KM |
712 | |
713 | dev_dbg(dev, "pipe %x\t: %s\n", | |
714 | i, usbhsp_pipe_name[pipe_type[i]]); | |
715 | } | |
716 | ||
717 | return 0; | |
718 | } | |
719 | ||
720 | void usbhs_pipe_remove(struct usbhs_priv *priv) | |
721 | { | |
e8d548d5 | 722 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
f1407d5c KM |
723 | |
724 | kfree(info->pipe); | |
725 | } |