]>
Commit | Line | Data |
---|---|---|
e8d548d5 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> | |
18 | #include <linux/io.h> | |
19 | #include "./common.h" | |
20 | #include "./pipe.h" | |
21 | ||
4bd04811 KM |
22 | /* |
23 | * packet info function | |
24 | */ | |
6acb95d4 KM |
25 | void usbhs_pkt_init(struct usbhs_pkt *pkt) |
26 | { | |
27 | INIT_LIST_HEAD(&pkt->node); | |
28 | } | |
29 | ||
659d4954 KM |
30 | void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, |
31 | void *buf, int len, int zero) | |
6acb95d4 KM |
32 | { |
33 | list_del_init(&pkt->node); | |
34 | list_add_tail(&pkt->node, &pipe->list); | |
35 | ||
659d4954 KM |
36 | pkt->pipe = pipe; |
37 | pkt->buf = buf; | |
38 | pkt->length = len; | |
39 | pkt->zero = zero; | |
40 | pkt->actual = 0; | |
6acb95d4 KM |
41 | } |
42 | ||
43 | void usbhs_pkt_pop(struct usbhs_pkt *pkt) | |
44 | { | |
45 | list_del_init(&pkt->node); | |
46 | } | |
47 | ||
48 | struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe) | |
49 | { | |
50 | if (list_empty(&pipe->list)) | |
51 | return NULL; | |
52 | ||
53 | return list_entry(pipe->list.next, struct usbhs_pkt, node); | |
54 | } | |
55 | ||
659d4954 KM |
56 | /* |
57 | * irq enable/disable function | |
58 | */ | |
59 | #define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e) | |
60 | #define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e) | |
61 | #define usbhsf_irq_callback_ctrl(pipe, status, enable) \ | |
62 | ({ \ | |
63 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); \ | |
64 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); \ | |
65 | u16 status = (1 << usbhs_pipe_number(pipe)); \ | |
66 | if (!mod) \ | |
67 | return; \ | |
68 | if (enable) \ | |
69 | mod->irq_##status |= status; \ | |
70 | else \ | |
71 | mod->irq_##status &= ~status; \ | |
72 | usbhs_irq_callback_update(priv, mod); \ | |
73 | }) | |
74 | ||
75 | static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable) | |
76 | { | |
77 | /* | |
78 | * And DCP pipe can NOT use "ready interrupt" for "send" | |
79 | * it should use "empty" interrupt. | |
80 | * see | |
81 | * "Operation" - "Interrupt Function" - "BRDY Interrupt" | |
82 | * | |
83 | * on the other hand, normal pipe can use "ready interrupt" for "send" | |
84 | * even though it is single/double buffer | |
85 | */ | |
86 | if (usbhs_pipe_is_dcp(pipe)) | |
87 | usbhsf_irq_empty_ctrl(pipe, enable); | |
88 | else | |
89 | usbhsf_irq_ready_ctrl(pipe, enable); | |
90 | } | |
91 | ||
92 | static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable) | |
93 | { | |
94 | usbhsf_irq_ready_ctrl(pipe, enable); | |
95 | } | |
96 | ||
e8d548d5 KM |
97 | /* |
98 | * FIFO ctrl | |
99 | */ | |
100 | static void usbhsf_send_terminator(struct usbhs_pipe *pipe) | |
101 | { | |
102 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | |
103 | ||
104 | usbhs_bset(priv, CFIFOCTR, BVAL, BVAL); | |
105 | } | |
106 | ||
107 | static int usbhsf_fifo_barrier(struct usbhs_priv *priv) | |
108 | { | |
109 | int timeout = 1024; | |
110 | ||
111 | do { | |
112 | /* The FIFO port is accessible */ | |
113 | if (usbhs_read(priv, CFIFOCTR) & FRDY) | |
114 | return 0; | |
115 | ||
116 | udelay(10); | |
117 | } while (timeout--); | |
118 | ||
119 | return -EBUSY; | |
120 | } | |
121 | ||
122 | static void usbhsf_fifo_clear(struct usbhs_pipe *pipe) | |
123 | { | |
124 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | |
125 | ||
126 | if (!usbhs_pipe_is_dcp(pipe)) | |
127 | usbhsf_fifo_barrier(priv); | |
128 | ||
129 | usbhs_write(priv, CFIFOCTR, BCLR); | |
130 | } | |
131 | ||
132 | static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv) | |
133 | { | |
134 | return usbhs_read(priv, CFIFOCTR) & DTLN_MASK; | |
135 | } | |
136 | ||
137 | static int usbhsf_fifo_select(struct usbhs_pipe *pipe, int write) | |
138 | { | |
139 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | |
140 | struct device *dev = usbhs_priv_to_dev(priv); | |
141 | int timeout = 1024; | |
142 | u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */ | |
143 | u16 base = usbhs_pipe_number(pipe); /* CURPIPE */ | |
144 | ||
145 | if (usbhs_pipe_is_dcp(pipe)) | |
146 | base |= (1 == write) << 5; /* ISEL */ | |
147 | ||
148 | /* "base" will be used below */ | |
149 | usbhs_write(priv, CFIFOSEL, base | MBW_32); | |
150 | ||
151 | /* check ISEL and CURPIPE value */ | |
152 | while (timeout--) { | |
153 | if (base == (mask & usbhs_read(priv, CFIFOSEL))) | |
154 | return 0; | |
155 | udelay(10); | |
156 | } | |
157 | ||
158 | dev_err(dev, "fifo select error\n"); | |
159 | ||
160 | return -EIO; | |
161 | } | |
162 | ||
163 | /* | |
164 | * PIO fifo functions | |
165 | */ | |
166 | int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe) | |
167 | { | |
168 | return usbhsf_fifo_select(pipe, 1); | |
169 | } | |
170 | ||
4bd04811 | 171 | int usbhs_fifo_write(struct usbhs_pkt *pkt) |
e8d548d5 | 172 | { |
4bd04811 | 173 | struct usbhs_pipe *pipe = pkt->pipe; |
e8d548d5 | 174 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
4bd04811 | 175 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); |
659d4954 | 176 | struct device *dev = usbhs_priv_to_dev(priv); |
e8d548d5 | 177 | void __iomem *addr = priv->base + CFIFO; |
659d4954 | 178 | u8 *buf; |
e8d548d5 KM |
179 | int maxp = usbhs_pipe_get_maxpacket(pipe); |
180 | int total_len; | |
4bd04811 | 181 | int i, ret, len; |
659d4954 | 182 | int is_short, is_done; |
e8d548d5 KM |
183 | |
184 | ret = usbhs_pipe_is_accessible(pipe); | |
185 | if (ret < 0) | |
659d4954 | 186 | goto usbhs_fifo_write_busy; |
e8d548d5 KM |
187 | |
188 | ret = usbhsf_fifo_select(pipe, 1); | |
189 | if (ret < 0) | |
659d4954 | 190 | goto usbhs_fifo_write_busy; |
e8d548d5 KM |
191 | |
192 | ret = usbhsf_fifo_barrier(priv); | |
193 | if (ret < 0) | |
659d4954 | 194 | goto usbhs_fifo_write_busy; |
e8d548d5 | 195 | |
659d4954 KM |
196 | buf = pkt->buf + pkt->actual; |
197 | len = pkt->length - pkt->actual; | |
198 | len = min(len, maxp); | |
199 | total_len = len; | |
200 | is_short = total_len < maxp; | |
e8d548d5 KM |
201 | |
202 | /* | |
203 | * FIXME | |
204 | * | |
205 | * 32-bit access only | |
206 | */ | |
659d4954 | 207 | if (len >= 4 && !((unsigned long)buf & 0x03)) { |
e8d548d5 KM |
208 | iowrite32_rep(addr, buf, len / 4); |
209 | len %= 4; | |
210 | buf += total_len - len; | |
211 | } | |
212 | ||
213 | /* the rest operation */ | |
214 | for (i = 0; i < len; i++) | |
215 | iowrite8(buf[i], addr + (0x03 - (i & 0x03))); | |
216 | ||
659d4954 KM |
217 | /* |
218 | * variable update | |
219 | */ | |
220 | pkt->actual += total_len; | |
221 | ||
222 | if (pkt->actual < pkt->length) | |
223 | is_done = 0; /* there are remainder data */ | |
224 | else if (is_short) | |
225 | is_done = 1; /* short packet */ | |
226 | else | |
227 | is_done = !pkt->zero; /* send zero packet ? */ | |
228 | ||
229 | /* | |
230 | * pipe/irq handling | |
231 | */ | |
232 | if (is_short) | |
e8d548d5 KM |
233 | usbhsf_send_terminator(pipe); |
234 | ||
659d4954 | 235 | usbhsf_tx_irq_ctrl(pipe, !is_done); |
4bd04811 KM |
236 | usbhs_pipe_enable(pipe); |
237 | ||
659d4954 KM |
238 | dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", |
239 | usbhs_pipe_number(pipe), | |
240 | pkt->length, pkt->actual, is_done, pkt->zero); | |
241 | ||
242 | /* | |
243 | * Transmission end | |
244 | */ | |
245 | if (is_done) { | |
246 | if (usbhs_pipe_is_dcp(pipe)) | |
247 | usbhs_dcp_control_transfer_done(pipe); | |
248 | ||
249 | if (info->tx_done) | |
250 | info->tx_done(pkt); | |
4bd04811 KM |
251 | } |
252 | ||
253 | return 0; | |
659d4954 KM |
254 | |
255 | usbhs_fifo_write_busy: | |
256 | /* | |
257 | * pipe is busy. | |
258 | * retry in interrupt | |
259 | */ | |
260 | usbhsf_tx_irq_ctrl(pipe, 1); | |
261 | ||
262 | return ret; | |
e8d548d5 KM |
263 | } |
264 | ||
265 | int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) | |
266 | { | |
267 | int ret; | |
268 | ||
269 | /* | |
270 | * select pipe and enable it to prepare packet receive | |
271 | */ | |
272 | ret = usbhsf_fifo_select(pipe, 0); | |
273 | if (ret < 0) | |
274 | return ret; | |
275 | ||
276 | usbhs_pipe_enable(pipe); | |
659d4954 | 277 | usbhsf_rx_irq_ctrl(pipe, 1); |
e8d548d5 KM |
278 | |
279 | return ret; | |
280 | } | |
281 | ||
4bd04811 | 282 | int usbhs_fifo_read(struct usbhs_pkt *pkt) |
e8d548d5 | 283 | { |
4bd04811 | 284 | struct usbhs_pipe *pipe = pkt->pipe; |
e8d548d5 | 285 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
659d4954 | 286 | struct device *dev = usbhs_priv_to_dev(priv); |
e8d548d5 | 287 | void __iomem *addr = priv->base + CFIFO; |
659d4954 KM |
288 | u8 *buf; |
289 | u32 data = 0; | |
290 | int maxp = usbhs_pipe_get_maxpacket(pipe); | |
4bd04811 | 291 | int rcv_len, len; |
e8d548d5 | 292 | int i, ret; |
4bd04811 | 293 | int total_len = 0; |
659d4954 | 294 | int is_done = 0; |
e8d548d5 KM |
295 | |
296 | ret = usbhsf_fifo_select(pipe, 0); | |
297 | if (ret < 0) | |
298 | return ret; | |
299 | ||
300 | ret = usbhsf_fifo_barrier(priv); | |
301 | if (ret < 0) | |
302 | return ret; | |
303 | ||
304 | rcv_len = usbhsf_fifo_rcv_len(priv); | |
305 | ||
659d4954 KM |
306 | buf = pkt->buf + pkt->actual; |
307 | len = pkt->length - pkt->actual; | |
308 | len = min(len, rcv_len); | |
309 | total_len = len; | |
310 | ||
e8d548d5 KM |
311 | /* |
312 | * Buffer clear if Zero-Length packet | |
313 | * | |
314 | * see | |
315 | * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function" | |
316 | */ | |
317 | if (0 == rcv_len) { | |
318 | usbhsf_fifo_clear(pipe); | |
4bd04811 | 319 | goto usbhs_fifo_read_end; |
e8d548d5 KM |
320 | } |
321 | ||
e8d548d5 KM |
322 | /* |
323 | * FIXME | |
324 | * | |
325 | * 32-bit access only | |
326 | */ | |
659d4954 | 327 | if (len >= 4 && !((unsigned long)buf & 0x03)) { |
e8d548d5 KM |
328 | ioread32_rep(addr, buf, len / 4); |
329 | len %= 4; | |
659d4954 | 330 | buf += total_len - len; |
e8d548d5 KM |
331 | } |
332 | ||
333 | /* the rest operation */ | |
334 | for (i = 0; i < len; i++) { | |
335 | if (!(i & 0x03)) | |
336 | data = ioread32(addr); | |
337 | ||
338 | buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; | |
339 | } | |
340 | ||
659d4954 KM |
341 | pkt->actual += total_len; |
342 | ||
4bd04811 | 343 | usbhs_fifo_read_end: |
659d4954 KM |
344 | if ((pkt->actual == pkt->length) || /* receive all data */ |
345 | (total_len < maxp)) /* short packet */ | |
346 | is_done = 1; | |
347 | ||
348 | dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", | |
349 | usbhs_pipe_number(pipe), | |
350 | pkt->length, pkt->actual, is_done, pkt->zero); | |
351 | ||
352 | if (is_done) { | |
353 | struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); | |
354 | ||
355 | usbhsf_rx_irq_ctrl(pipe, 0); | |
356 | usbhs_pipe_disable(pipe); | |
357 | ||
358 | if (info->rx_done) | |
359 | info->rx_done(pkt); | |
4bd04811 KM |
360 | } |
361 | ||
362 | return 0; | |
e8d548d5 | 363 | } |