diff options
Diffstat (limited to 'drivers/spi/spi-mem.c')
-rw-r--r-- | drivers/spi/spi-mem.c | 204 |
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 | } |
433 | EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); | 433 | EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); |
434 | 434 | ||
435 | static 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 | |||
455 | static 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 | */ | ||
488 | struct spi_mem_dirmap_desc * | ||
489 | spi_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 | } | ||
528 | EXPORT_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 | */ | ||
538 | void 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 | } | ||
545 | EXPORT_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 | */ | ||
563 | ssize_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 | } | ||
591 | EXPORT_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 | */ | ||
609 | ssize_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 | } | ||
637 | EXPORT_SYMBOL_GPL(spi_mem_dirmap_write); | ||
638 | |||
435 | static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) | 639 | static 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); |