1
2
3
4
5
6
7
8
9
10
11
12#include <linux/interrupt.h>
13#include <linux/kernel.h>
14#include <linux/of.h>
15#include <linux/of_platform.h>
16#include <linux/spinlock.h>
17#include <linux/module.h>
18#include <asm/io.h>
19#include <asm/prom.h>
20#include <asm/mpc52xx.h>
21#include <asm/time.h>
22
23#include <linux/fsl/bestcomm/bestcomm.h>
24#include <linux/fsl/bestcomm/bestcomm_priv.h>
25#include <linux/fsl/bestcomm/gen_bd.h>
26
27MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
28MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver");
29MODULE_LICENSE("GPL");
30
31#define LPBFIFO_REG_PACKET_SIZE (0x00)
32#define LPBFIFO_REG_START_ADDRESS (0x04)
33#define LPBFIFO_REG_CONTROL (0x08)
34#define LPBFIFO_REG_ENABLE (0x0C)
35#define LPBFIFO_REG_BYTES_DONE_STATUS (0x14)
36#define LPBFIFO_REG_FIFO_DATA (0x40)
37#define LPBFIFO_REG_FIFO_STATUS (0x44)
38#define LPBFIFO_REG_FIFO_CONTROL (0x48)
39#define LPBFIFO_REG_FIFO_ALARM (0x4C)
40
41struct mpc52xx_lpbfifo {
42 struct device *dev;
43 phys_addr_t regs_phys;
44 void __iomem *regs;
45 int irq;
46 spinlock_t lock;
47
48 struct bcom_task *bcom_tx_task;
49 struct bcom_task *bcom_rx_task;
50 struct bcom_task *bcom_cur_task;
51
52
53 struct mpc52xx_lpbfifo_request *req;
54 int dma_irqs_enabled;
55};
56
57
58static struct mpc52xx_lpbfifo lpbfifo;
59
60
61
62
63static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
64{
65 size_t transfer_size = req->size - req->pos;
66 struct bcom_bd *bd;
67 void __iomem *reg;
68 u32 *data;
69 int i;
70 int bit_fields;
71 int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
72 int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
73 int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
74
75
76 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
77
78
79 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001);
80 if (!dma) {
81
82
83
84
85
86
87
88
89
90 if (transfer_size > 512)
91 transfer_size = 512;
92
93
94 if (write) {
95 reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
96 data = req->data + req->pos;
97 for (i = 0; i < transfer_size; i += 4)
98 out_be32(reg, *data++);
99 }
100
101
102 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301);
103 } else {
104
105
106
107
108
109
110
111 if (write) {
112 out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4);
113 out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7);
114 lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;
115 } else {
116 out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff);
117 out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0);
118 lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;
119
120 if (poll_dma) {
121 if (lpbfifo.dma_irqs_enabled) {
122 disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
123 lpbfifo.dma_irqs_enabled = 0;
124 }
125 } else {
126 if (!lpbfifo.dma_irqs_enabled) {
127 enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
128 lpbfifo.dma_irqs_enabled = 1;
129 }
130 }
131 }
132
133 bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
134 bd->status = transfer_size;
135 if (!write) {
136
137
138
139
140
141
142
143
144
145
146
147
148 transfer_size += 4;
149 }
150 bd->data[0] = req->data_phys + req->pos;
151 bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
152
153
154 bit_fields = 0x00000201;
155
156
157 if (write && (!poll_dma))
158 bit_fields |= 0x00000100;
159 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields);
160 }
161
162
163 out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS,
164 req->offset + req->pos);
165 out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size);
166
167 bit_fields = req->cs << 24 | 0x000008;
168 if (!write)
169 bit_fields |= 0x010000;
170 out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields);
171
172
173 if (!lpbfifo.req->defer_xfer_start)
174 out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);
175 if (dma)
176 bcom_enable(lpbfifo.bcom_cur_task);
177}
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)
221{
222 struct mpc52xx_lpbfifo_request *req;
223 u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
224 void __iomem *reg;
225 u32 *data;
226 int count, i;
227 int do_callback = 0;
228 u32 ts;
229 unsigned long flags;
230 int dma, write, poll_dma;
231
232 spin_lock_irqsave(&lpbfifo.lock, flags);
233 ts = get_tbl();
234
235 req = lpbfifo.req;
236 if (!req) {
237 spin_unlock_irqrestore(&lpbfifo.lock, flags);
238 pr_err("bogus LPBFIFO IRQ\n");
239 return IRQ_HANDLED;
240 }
241
242 dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
243 write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
244 poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
245
246 if (dma && !write) {
247 spin_unlock_irqrestore(&lpbfifo.lock, flags);
248 pr_err("bogus LPBFIFO IRQ (dma and not writing)\n");
249 return IRQ_HANDLED;
250 }
251
252 if ((status & 0x01) == 0) {
253 goto out;
254 }
255
256
257 if (status & 0x10) {
258 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
259 do_callback = 1;
260 goto out;
261 }
262
263
264 count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
265 count &= 0x00ffffff;
266
267 if (!dma && !write) {
268
269 reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
270 data = req->data + req->pos;
271 for (i = 0; i < count; i += 4)
272 *data++ = in_be32(reg);
273 }
274
275
276 req->pos += count;
277
278
279 if (req->size - req->pos)
280 mpc52xx_lpbfifo_kick(req);
281 else
282 do_callback = 1;
283
284 out:
285
286 out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01);
287
288 if (dma && (status & 0x11)) {
289
290
291
292
293
294
295
296
297
298
299 bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
300 }
301 req->last_byte = ((u8 *)req->data)[req->size - 1];
302
303
304
305 if (do_callback)
306 lpbfifo.req = NULL;
307
308 if (irq != 0)
309 req->irq_count++;
310
311 req->irq_ticks += get_tbl() - ts;
312 spin_unlock_irqrestore(&lpbfifo.lock, flags);
313
314
315 if (do_callback && req->callback)
316 req->callback(req);
317
318 return IRQ_HANDLED;
319}
320
321
322
323
324
325
326static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)
327{
328 struct mpc52xx_lpbfifo_request *req;
329 unsigned long flags;
330 u32 status;
331 u32 ts;
332
333 spin_lock_irqsave(&lpbfifo.lock, flags);
334 ts = get_tbl();
335
336 req = lpbfifo.req;
337 if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {
338 spin_unlock_irqrestore(&lpbfifo.lock, flags);
339 return IRQ_HANDLED;
340 }
341
342 if (irq != 0)
343 req->irq_count++;
344
345 if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {
346 spin_unlock_irqrestore(&lpbfifo.lock, flags);
347
348 req->buffer_not_done_cnt++;
349 if ((req->buffer_not_done_cnt % 1000) == 0)
350 pr_err("transfer stalled\n");
351
352 return IRQ_HANDLED;
353 }
354
355 bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
356
357 req->last_byte = ((u8 *)req->data)[req->size - 1];
358
359 req->pos = status & 0x00ffffff;
360
361
362 lpbfifo.req = NULL;
363
364
365 req->irq_ticks += get_tbl() - ts;
366 spin_unlock_irqrestore(&lpbfifo.lock, flags);
367
368 if (req->callback)
369 req->callback(req);
370
371 return IRQ_HANDLED;
372}
373
374
375
376
377void mpc52xx_lpbfifo_poll(void)
378{
379 struct mpc52xx_lpbfifo_request *req = lpbfifo.req;
380 int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
381 int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
382
383
384
385
386 if (dma && write)
387 mpc52xx_lpbfifo_irq(0, NULL);
388 else
389 mpc52xx_lpbfifo_bcom_irq(0, NULL);
390}
391EXPORT_SYMBOL(mpc52xx_lpbfifo_poll);
392
393
394
395
396
397int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)
398{
399 unsigned long flags;
400
401 if (!lpbfifo.regs)
402 return -ENODEV;
403
404 spin_lock_irqsave(&lpbfifo.lock, flags);
405
406
407 if (lpbfifo.req) {
408 spin_unlock_irqrestore(&lpbfifo.lock, flags);
409 return -EBUSY;
410 }
411
412
413 lpbfifo.req = req;
414 req->irq_count = 0;
415 req->irq_ticks = 0;
416 req->buffer_not_done_cnt = 0;
417 req->pos = 0;
418
419 mpc52xx_lpbfifo_kick(req);
420 spin_unlock_irqrestore(&lpbfifo.lock, flags);
421 return 0;
422}
423EXPORT_SYMBOL(mpc52xx_lpbfifo_submit);
424
425int mpc52xx_lpbfifo_start_xfer(struct mpc52xx_lpbfifo_request *req)
426{
427 unsigned long flags;
428
429 if (!lpbfifo.regs)
430 return -ENODEV;
431
432 spin_lock_irqsave(&lpbfifo.lock, flags);
433
434
435
436
437
438 if (lpbfifo.req && !lpbfifo.req->defer_xfer_start) {
439 spin_unlock_irqrestore(&lpbfifo.lock, flags);
440 return -EBUSY;
441 }
442
443
444
445
446
447 if (lpbfifo.req && lpbfifo.req == req &&
448 lpbfifo.req->defer_xfer_start) {
449 out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);
450 }
451
452 spin_unlock_irqrestore(&lpbfifo.lock, flags);
453 return 0;
454}
455EXPORT_SYMBOL(mpc52xx_lpbfifo_start_xfer);
456
457void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)
458{
459 unsigned long flags;
460
461 spin_lock_irqsave(&lpbfifo.lock, flags);
462 if (lpbfifo.req == req) {
463
464 bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);
465 bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);
466 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
467 lpbfifo.req = NULL;
468 }
469 spin_unlock_irqrestore(&lpbfifo.lock, flags);
470}
471EXPORT_SYMBOL(mpc52xx_lpbfifo_abort);
472
473static int mpc52xx_lpbfifo_probe(struct platform_device *op)
474{
475 struct resource res;
476 int rc = -ENOMEM;
477
478 if (lpbfifo.dev != NULL)
479 return -ENOSPC;
480
481 lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0);
482 if (!lpbfifo.irq)
483 return -ENODEV;
484
485 if (of_address_to_resource(op->dev.of_node, 0, &res))
486 return -ENODEV;
487 lpbfifo.regs_phys = res.start;
488 lpbfifo.regs = of_iomap(op->dev.of_node, 0);
489 if (!lpbfifo.regs)
490 return -ENOMEM;
491
492 spin_lock_init(&lpbfifo.lock);
493
494
495 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
496
497
498 rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0,
499 "mpc52xx-lpbfifo", &lpbfifo);
500 if (rc)
501 goto err_irq;
502
503
504 lpbfifo.bcom_rx_task =
505 bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
506 BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
507 16*1024*1024);
508 if (!lpbfifo.bcom_rx_task)
509 goto err_bcom_rx;
510
511 rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),
512 mpc52xx_lpbfifo_bcom_irq, 0,
513 "mpc52xx-lpbfifo-rx", &lpbfifo);
514 if (rc)
515 goto err_bcom_rx_irq;
516
517 lpbfifo.dma_irqs_enabled = 1;
518
519
520 lpbfifo.bcom_tx_task =
521 bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
522 BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC);
523 if (!lpbfifo.bcom_tx_task)
524 goto err_bcom_tx;
525
526 lpbfifo.dev = &op->dev;
527 return 0;
528
529 err_bcom_tx:
530 free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
531 err_bcom_rx_irq:
532 bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
533 err_bcom_rx:
534 err_irq:
535 iounmap(lpbfifo.regs);
536 lpbfifo.regs = NULL;
537
538 dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n");
539 return -ENODEV;
540}
541
542
543static int mpc52xx_lpbfifo_remove(struct platform_device *op)
544{
545 if (lpbfifo.dev != &op->dev)
546 return 0;
547
548
549 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
550
551
552 free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo);
553 bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task);
554
555
556 free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
557 bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
558
559 free_irq(lpbfifo.irq, &lpbfifo);
560 iounmap(lpbfifo.regs);
561 lpbfifo.regs = NULL;
562 lpbfifo.dev = NULL;
563
564 return 0;
565}
566
567static const struct of_device_id mpc52xx_lpbfifo_match[] = {
568 { .compatible = "fsl,mpc5200-lpbfifo", },
569 {},
570};
571MODULE_DEVICE_TABLE(of, mpc52xx_lpbfifo_match);
572
573static struct platform_driver mpc52xx_lpbfifo_driver = {
574 .driver = {
575 .name = "mpc52xx-lpbfifo",
576 .of_match_table = mpc52xx_lpbfifo_match,
577 },
578 .probe = mpc52xx_lpbfifo_probe,
579 .remove = mpc52xx_lpbfifo_remove,
580};
581module_platform_driver(mpc52xx_lpbfifo_driver);
582