0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/interrupt.h>
0012 #include <linux/kernel.h>
0013 #include <linux/of.h>
0014 #include <linux/of_address.h>
0015 #include <linux/of_irq.h>
0016 #include <linux/of_platform.h>
0017 #include <linux/spinlock.h>
0018 #include <linux/module.h>
0019 #include <asm/io.h>
0020 #include <asm/mpc52xx.h>
0021 #include <asm/time.h>
0022
0023 #include <linux/fsl/bestcomm/bestcomm.h>
0024 #include <linux/fsl/bestcomm/bestcomm_priv.h>
0025 #include <linux/fsl/bestcomm/gen_bd.h>
0026
0027 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
0028 MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver");
0029 MODULE_LICENSE("GPL");
0030
0031 #define LPBFIFO_REG_PACKET_SIZE (0x00)
0032 #define LPBFIFO_REG_START_ADDRESS (0x04)
0033 #define LPBFIFO_REG_CONTROL (0x08)
0034 #define LPBFIFO_REG_ENABLE (0x0C)
0035 #define LPBFIFO_REG_BYTES_DONE_STATUS (0x14)
0036 #define LPBFIFO_REG_FIFO_DATA (0x40)
0037 #define LPBFIFO_REG_FIFO_STATUS (0x44)
0038 #define LPBFIFO_REG_FIFO_CONTROL (0x48)
0039 #define LPBFIFO_REG_FIFO_ALARM (0x4C)
0040
0041 struct mpc52xx_lpbfifo {
0042 struct device *dev;
0043 phys_addr_t regs_phys;
0044 void __iomem *regs;
0045 int irq;
0046 spinlock_t lock;
0047
0048 struct bcom_task *bcom_tx_task;
0049 struct bcom_task *bcom_rx_task;
0050 struct bcom_task *bcom_cur_task;
0051
0052
0053 struct mpc52xx_lpbfifo_request *req;
0054 int dma_irqs_enabled;
0055 };
0056
0057
0058 static struct mpc52xx_lpbfifo lpbfifo;
0059
0060
0061
0062
0063 static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
0064 {
0065 size_t transfer_size = req->size - req->pos;
0066 struct bcom_bd *bd;
0067 void __iomem *reg;
0068 u32 *data;
0069 int i;
0070 int bit_fields;
0071 int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
0072 int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
0073 int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
0074
0075
0076 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
0077
0078
0079 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001);
0080 if (!dma) {
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090 if (transfer_size > 512)
0091 transfer_size = 512;
0092
0093
0094 if (write) {
0095 reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
0096 data = req->data + req->pos;
0097 for (i = 0; i < transfer_size; i += 4)
0098 out_be32(reg, *data++);
0099 }
0100
0101
0102 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301);
0103 } else {
0104
0105
0106
0107
0108
0109
0110
0111 if (write) {
0112 out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4);
0113 out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7);
0114 lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;
0115 } else {
0116 out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff);
0117 out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0);
0118 lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;
0119
0120 if (poll_dma) {
0121 if (lpbfifo.dma_irqs_enabled) {
0122 disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
0123 lpbfifo.dma_irqs_enabled = 0;
0124 }
0125 } else {
0126 if (!lpbfifo.dma_irqs_enabled) {
0127 enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
0128 lpbfifo.dma_irqs_enabled = 1;
0129 }
0130 }
0131 }
0132
0133 bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
0134 bd->status = transfer_size;
0135 if (!write) {
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 transfer_size += 4;
0149 }
0150 bd->data[0] = req->data_phys + req->pos;
0151 bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
0152
0153
0154 bit_fields = 0x00000201;
0155
0156
0157 if (write && (!poll_dma))
0158 bit_fields |= 0x00000100;
0159 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields);
0160 }
0161
0162
0163 out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS,
0164 req->offset + req->pos);
0165 out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size);
0166
0167 bit_fields = req->cs << 24 | 0x000008;
0168 if (!write)
0169 bit_fields |= 0x010000;
0170 out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields);
0171
0172
0173 if (!lpbfifo.req->defer_xfer_start)
0174 out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);
0175 if (dma)
0176 bcom_enable(lpbfifo.bcom_cur_task);
0177 }
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220 static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)
0221 {
0222 struct mpc52xx_lpbfifo_request *req;
0223 u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
0224 void __iomem *reg;
0225 u32 *data;
0226 int count, i;
0227 int do_callback = 0;
0228 u32 ts;
0229 unsigned long flags;
0230 int dma, write, poll_dma;
0231
0232 spin_lock_irqsave(&lpbfifo.lock, flags);
0233 ts = mftb();
0234
0235 req = lpbfifo.req;
0236 if (!req) {
0237 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0238 pr_err("bogus LPBFIFO IRQ\n");
0239 return IRQ_HANDLED;
0240 }
0241
0242 dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
0243 write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
0244 poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
0245
0246 if (dma && !write) {
0247 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0248 pr_err("bogus LPBFIFO IRQ (dma and not writing)\n");
0249 return IRQ_HANDLED;
0250 }
0251
0252 if ((status & 0x01) == 0) {
0253 goto out;
0254 }
0255
0256
0257 if (status & 0x10) {
0258 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
0259 do_callback = 1;
0260 goto out;
0261 }
0262
0263
0264 count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
0265 count &= 0x00ffffff;
0266
0267 if (!dma && !write) {
0268
0269 reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
0270 data = req->data + req->pos;
0271 for (i = 0; i < count; i += 4)
0272 *data++ = in_be32(reg);
0273 }
0274
0275
0276 req->pos += count;
0277
0278
0279 if (req->size - req->pos)
0280 mpc52xx_lpbfifo_kick(req);
0281 else
0282 do_callback = 1;
0283
0284 out:
0285
0286 out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01);
0287
0288 if (dma && (status & 0x11)) {
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299 bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
0300 }
0301 req->last_byte = ((u8 *)req->data)[req->size - 1];
0302
0303
0304
0305 if (do_callback)
0306 lpbfifo.req = NULL;
0307
0308 if (irq != 0)
0309 req->irq_count++;
0310
0311 req->irq_ticks += mftb() - ts;
0312 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0313
0314
0315 if (do_callback && req->callback)
0316 req->callback(req);
0317
0318 return IRQ_HANDLED;
0319 }
0320
0321
0322
0323
0324
0325
0326 static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)
0327 {
0328 struct mpc52xx_lpbfifo_request *req;
0329 unsigned long flags;
0330 u32 status;
0331 u32 ts;
0332
0333 spin_lock_irqsave(&lpbfifo.lock, flags);
0334 ts = mftb();
0335
0336 req = lpbfifo.req;
0337 if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {
0338 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0339 return IRQ_HANDLED;
0340 }
0341
0342 if (irq != 0)
0343 req->irq_count++;
0344
0345 if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {
0346 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0347
0348 req->buffer_not_done_cnt++;
0349 if ((req->buffer_not_done_cnt % 1000) == 0)
0350 pr_err("transfer stalled\n");
0351
0352 return IRQ_HANDLED;
0353 }
0354
0355 bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
0356
0357 req->last_byte = ((u8 *)req->data)[req->size - 1];
0358
0359 req->pos = status & 0x00ffffff;
0360
0361
0362 lpbfifo.req = NULL;
0363
0364
0365 req->irq_ticks += mftb() - ts;
0366 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0367
0368 if (req->callback)
0369 req->callback(req);
0370
0371 return IRQ_HANDLED;
0372 }
0373
0374
0375
0376
0377 void mpc52xx_lpbfifo_poll(void)
0378 {
0379 struct mpc52xx_lpbfifo_request *req = lpbfifo.req;
0380 int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
0381 int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
0382
0383
0384
0385
0386 if (dma && write)
0387 mpc52xx_lpbfifo_irq(0, NULL);
0388 else
0389 mpc52xx_lpbfifo_bcom_irq(0, NULL);
0390 }
0391 EXPORT_SYMBOL(mpc52xx_lpbfifo_poll);
0392
0393
0394
0395
0396
0397 int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)
0398 {
0399 unsigned long flags;
0400
0401 if (!lpbfifo.regs)
0402 return -ENODEV;
0403
0404 spin_lock_irqsave(&lpbfifo.lock, flags);
0405
0406
0407 if (lpbfifo.req) {
0408 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0409 return -EBUSY;
0410 }
0411
0412
0413 lpbfifo.req = req;
0414 req->irq_count = 0;
0415 req->irq_ticks = 0;
0416 req->buffer_not_done_cnt = 0;
0417 req->pos = 0;
0418
0419 mpc52xx_lpbfifo_kick(req);
0420 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0421 return 0;
0422 }
0423 EXPORT_SYMBOL(mpc52xx_lpbfifo_submit);
0424
0425 int mpc52xx_lpbfifo_start_xfer(struct mpc52xx_lpbfifo_request *req)
0426 {
0427 unsigned long flags;
0428
0429 if (!lpbfifo.regs)
0430 return -ENODEV;
0431
0432 spin_lock_irqsave(&lpbfifo.lock, flags);
0433
0434
0435
0436
0437
0438 if (lpbfifo.req && !lpbfifo.req->defer_xfer_start) {
0439 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0440 return -EBUSY;
0441 }
0442
0443
0444
0445
0446
0447 if (lpbfifo.req && lpbfifo.req == req &&
0448 lpbfifo.req->defer_xfer_start) {
0449 out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);
0450 }
0451
0452 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0453 return 0;
0454 }
0455 EXPORT_SYMBOL(mpc52xx_lpbfifo_start_xfer);
0456
0457 void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)
0458 {
0459 unsigned long flags;
0460
0461 spin_lock_irqsave(&lpbfifo.lock, flags);
0462 if (lpbfifo.req == req) {
0463
0464 bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);
0465 bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);
0466 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
0467 lpbfifo.req = NULL;
0468 }
0469 spin_unlock_irqrestore(&lpbfifo.lock, flags);
0470 }
0471 EXPORT_SYMBOL(mpc52xx_lpbfifo_abort);
0472
0473 static int mpc52xx_lpbfifo_probe(struct platform_device *op)
0474 {
0475 struct resource res;
0476 int rc = -ENOMEM;
0477
0478 if (lpbfifo.dev != NULL)
0479 return -ENOSPC;
0480
0481 lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0);
0482 if (!lpbfifo.irq)
0483 return -ENODEV;
0484
0485 if (of_address_to_resource(op->dev.of_node, 0, &res))
0486 return -ENODEV;
0487 lpbfifo.regs_phys = res.start;
0488 lpbfifo.regs = of_iomap(op->dev.of_node, 0);
0489 if (!lpbfifo.regs)
0490 return -ENOMEM;
0491
0492 spin_lock_init(&lpbfifo.lock);
0493
0494
0495 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
0496
0497
0498 rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0,
0499 "mpc52xx-lpbfifo", &lpbfifo);
0500 if (rc)
0501 goto err_irq;
0502
0503
0504 lpbfifo.bcom_rx_task =
0505 bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
0506 BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
0507 16*1024*1024);
0508 if (!lpbfifo.bcom_rx_task)
0509 goto err_bcom_rx;
0510
0511 rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),
0512 mpc52xx_lpbfifo_bcom_irq, 0,
0513 "mpc52xx-lpbfifo-rx", &lpbfifo);
0514 if (rc)
0515 goto err_bcom_rx_irq;
0516
0517 lpbfifo.dma_irqs_enabled = 1;
0518
0519
0520 lpbfifo.bcom_tx_task =
0521 bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
0522 BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC);
0523 if (!lpbfifo.bcom_tx_task)
0524 goto err_bcom_tx;
0525
0526 lpbfifo.dev = &op->dev;
0527 return 0;
0528
0529 err_bcom_tx:
0530 free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
0531 err_bcom_rx_irq:
0532 bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
0533 err_bcom_rx:
0534 err_irq:
0535 iounmap(lpbfifo.regs);
0536 lpbfifo.regs = NULL;
0537
0538 dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n");
0539 return -ENODEV;
0540 }
0541
0542
0543 static int mpc52xx_lpbfifo_remove(struct platform_device *op)
0544 {
0545 if (lpbfifo.dev != &op->dev)
0546 return 0;
0547
0548
0549 out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
0550
0551
0552 free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo);
0553 bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task);
0554
0555
0556 free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
0557 bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
0558
0559 free_irq(lpbfifo.irq, &lpbfifo);
0560 iounmap(lpbfifo.regs);
0561 lpbfifo.regs = NULL;
0562 lpbfifo.dev = NULL;
0563
0564 return 0;
0565 }
0566
0567 static const struct of_device_id mpc52xx_lpbfifo_match[] = {
0568 { .compatible = "fsl,mpc5200-lpbfifo", },
0569 {},
0570 };
0571 MODULE_DEVICE_TABLE(of, mpc52xx_lpbfifo_match);
0572
0573 static struct platform_driver mpc52xx_lpbfifo_driver = {
0574 .driver = {
0575 .name = "mpc52xx-lpbfifo",
0576 .of_match_table = mpc52xx_lpbfifo_match,
0577 },
0578 .probe = mpc52xx_lpbfifo_probe,
0579 .remove = mpc52xx_lpbfifo_remove,
0580 };
0581 module_platform_driver(mpc52xx_lpbfifo_driver);