summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-mem.c
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@bootlin.com>2018-11-06 11:05:33 -0500
committerMark Brown <broonie@kernel.org>2018-11-20 11:26:57 -0500
commitaa167f3fed0c37e0e4c707d4331d827661f46644 (patch)
tree41a1b7431e865d9a477f1d3eac1850654fefb258 /drivers/spi/spi-mem.c
parentf86c24f4795303e4024bc113196de32782f6ccb5 (diff)
spi: spi-mem: Add a new API to support direct mapping
Most modern SPI controllers can directly map a SPI memory (or a portion of the SPI memory) in the CPU address space. Most of the time this brings significant performance improvements as it automates the whole process of sending SPI memory operations every time a new region is accessed. This new API allows SPI memory drivers to create direct mappings and then use them to access the memory instead of using spi_mem_exec_op(). Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-mem.c')
-rw-r--r--drivers/spi/spi-mem.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 7916e655afc8..b12a7974b665 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -432,6 +432,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
432} 432}
433EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); 433EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
434 434
435static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
436 u64 offs, size_t len, void *buf)
437{
438 struct spi_mem_op op = desc->info.op_tmpl;
439 int ret;
440
441 op.addr.val = desc->info.offset + offs;
442 op.data.buf.in = buf;
443 op.data.nbytes = len;
444 ret = spi_mem_adjust_op_size(desc->mem, &op);
445 if (ret)
446 return ret;
447
448 ret = spi_mem_exec_op(desc->mem, &op);
449 if (ret)
450 return ret;
451
452 return op.data.nbytes;
453}
454
455static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
456 u64 offs, size_t len, const void *buf)
457{
458 struct spi_mem_op op = desc->info.op_tmpl;
459 int ret;
460
461 op.addr.val = desc->info.offset + offs;
462 op.data.buf.out = buf;
463 op.data.nbytes = len;
464 ret = spi_mem_adjust_op_size(desc->mem, &op);
465 if (ret)
466 return ret;
467
468 ret = spi_mem_exec_op(desc->mem, &op);
469 if (ret)
470 return ret;
471
472 return op.data.nbytes;
473}
474
475/**
476 * spi_mem_dirmap_create() - Create a direct mapping descriptor
477 * @mem: SPI mem device this direct mapping should be created for
478 * @info: direct mapping information
479 *
480 * This function is creating a direct mapping descriptor which can then be used
481 * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
482 * If the SPI controller driver does not support direct mapping, this function
483 * fallback to an implementation using spi_mem_exec_op(), so that the caller
484 * doesn't have to bother implementing a fallback on his own.
485 *
486 * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
487 */
488struct spi_mem_dirmap_desc *
489spi_mem_dirmap_create(struct spi_mem *mem,
490 const struct spi_mem_dirmap_info *info)
491{
492 struct spi_controller *ctlr = mem->spi->controller;
493 struct spi_mem_dirmap_desc *desc;
494 int ret = -ENOTSUPP;
495
496 /* Make sure the number of address cycles is between 1 and 8 bytes. */
497 if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
498 return ERR_PTR(-EINVAL);
499
500 /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
501 if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
502 return ERR_PTR(-EINVAL);
503
504 desc = kzalloc(sizeof(*desc), GFP_KERNEL);
505 if (!desc)
506 return ERR_PTR(-ENOMEM);
507
508 desc->mem = mem;
509 desc->info = *info;
510 if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
511 ret = ctlr->mem_ops->dirmap_create(desc);
512
513 if (ret) {
514 desc->nodirmap = true;
515 if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
516 ret = -ENOTSUPP;
517 else
518 ret = 0;
519 }
520
521 if (ret) {
522 kfree(desc);
523 return ERR_PTR(ret);
524 }
525
526 return desc;
527}
528EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
529
530/**
531 * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
532 * @desc: the direct mapping descriptor to destroy
533 * @info: direct mapping information
534 *
535 * This function destroys a direct mapping descriptor previously created by
536 * spi_mem_dirmap_create().
537 */
538void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
539{
540 struct spi_controller *ctlr = desc->mem->spi->controller;
541
542 if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy)
543 ctlr->mem_ops->dirmap_destroy(desc);
544}
545EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
546
547/**
548 * spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
549 * @desc: direct mapping descriptor
550 * @offs: offset to start reading from. Note that this is not an absolute
551 * offset, but the offset within the direct mapping which already has
552 * its own offset
553 * @len: length in bytes
554 * @buf: destination buffer. This buffer must be DMA-able
555 *
556 * This function reads data from a memory device using a direct mapping
557 * previously instantiated with spi_mem_dirmap_create().
558 *
559 * Return: the amount of data read from the memory device or a negative error
560 * code. Note that the returned size might be smaller than @len, and the caller
561 * is responsible for calling spi_mem_dirmap_read() again when that happens.
562 */
563ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
564 u64 offs, size_t len, void *buf)
565{
566 struct spi_controller *ctlr = desc->mem->spi->controller;
567 ssize_t ret;
568
569 if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
570 return -EINVAL;
571
572 if (!len)
573 return 0;
574
575 if (desc->nodirmap) {
576 ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
577 } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) {
578 ret = spi_mem_access_start(desc->mem);
579 if (ret)
580 return ret;
581
582 ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf);
583
584 spi_mem_access_end(desc->mem);
585 } else {
586 ret = -ENOTSUPP;
587 }
588
589 return ret;
590}
591EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
592
593/**
594 * spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
595 * @desc: direct mapping descriptor
596 * @offs: offset to start writing from. Note that this is not an absolute
597 * offset, but the offset within the direct mapping which already has
598 * its own offset
599 * @len: length in bytes
600 * @buf: source buffer. This buffer must be DMA-able
601 *
602 * This function writes data to a memory device using a direct mapping
603 * previously instantiated with spi_mem_dirmap_create().
604 *
605 * Return: the amount of data written to the memory device or a negative error
606 * code. Note that the returned size might be smaller than @len, and the caller
607 * is responsible for calling spi_mem_dirmap_write() again when that happens.
608 */
609ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
610 u64 offs, size_t len, const void *buf)
611{
612 struct spi_controller *ctlr = desc->mem->spi->controller;
613 ssize_t ret;
614
615 if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
616 return -EINVAL;
617
618 if (!len)
619 return 0;
620
621 if (desc->nodirmap) {
622 ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
623 } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) {
624 ret = spi_mem_access_start(desc->mem);
625 if (ret)
626 return ret;
627
628 ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf);
629
630 spi_mem_access_end(desc->mem);
631 } else {
632 ret = -ENOTSUPP;
633 }
634
635 return ret;
636}
637EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
638
435static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) 639static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
436{ 640{
437 return container_of(drv, struct spi_mem_driver, spidrv.driver); 641 return container_of(drv, struct spi_mem_driver, spidrv.driver);