18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Exceet Electronics GmbH 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Bootlin 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@bootlin.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 108c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 118c2ecf20Sopenharmony_ci#include <linux/spi/spi-mem.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "internals.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define SPI_MEM_MAX_BUSWIDTH 8 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a 198c2ecf20Sopenharmony_ci * memory operation 208c2ecf20Sopenharmony_ci * @ctlr: the SPI controller requesting this dma_map() 218c2ecf20Sopenharmony_ci * @op: the memory operation containing the buffer to map 228c2ecf20Sopenharmony_ci * @sgt: a pointer to a non-initialized sg_table that will be filled by this 238c2ecf20Sopenharmony_ci * function 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Some controllers might want to do DMA on the data buffer embedded in @op. 268c2ecf20Sopenharmony_ci * This helper prepares everything for you and provides a ready-to-use 278c2ecf20Sopenharmony_ci * sg_table. This function is not intended to be called from spi drivers. 288c2ecf20Sopenharmony_ci * Only SPI controller drivers should use it. 298c2ecf20Sopenharmony_ci * Note that the caller must ensure the memory region pointed by 308c2ecf20Sopenharmony_ci * op->data.buf.{in,out} is DMA-able before calling this function. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ciint spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, 358c2ecf20Sopenharmony_ci const struct spi_mem_op *op, 368c2ecf20Sopenharmony_ci struct sg_table *sgt) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct device *dmadev; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!op->data.nbytes) 418c2ecf20Sopenharmony_ci return -EINVAL; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) 448c2ecf20Sopenharmony_ci dmadev = ctlr->dma_tx->device->dev; 458c2ecf20Sopenharmony_ci else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) 468c2ecf20Sopenharmony_ci dmadev = ctlr->dma_rx->device->dev; 478c2ecf20Sopenharmony_ci else 488c2ecf20Sopenharmony_ci dmadev = ctlr->dev.parent; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (!dmadev) 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes, 548c2ecf20Sopenharmony_ci op->data.dir == SPI_MEM_DATA_IN ? 558c2ecf20Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/** 608c2ecf20Sopenharmony_ci * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a 618c2ecf20Sopenharmony_ci * memory operation 628c2ecf20Sopenharmony_ci * @ctlr: the SPI controller requesting this dma_unmap() 638c2ecf20Sopenharmony_ci * @op: the memory operation containing the buffer to unmap 648c2ecf20Sopenharmony_ci * @sgt: a pointer to an sg_table previously initialized by 658c2ecf20Sopenharmony_ci * spi_controller_dma_map_mem_op_data() 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Some controllers might want to do DMA on the data buffer embedded in @op. 688c2ecf20Sopenharmony_ci * This helper prepares things so that the CPU can access the 698c2ecf20Sopenharmony_ci * op->data.buf.{in,out} buffer again. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * This function is not intended to be called from SPI drivers. Only SPI 728c2ecf20Sopenharmony_ci * controller drivers should use it. 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * This function should be called after the DMA operation has finished and is 758c2ecf20Sopenharmony_ci * only valid if the previous spi_controller_dma_map_mem_op_data() call 768c2ecf20Sopenharmony_ci * returned 0. 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_civoid spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, 818c2ecf20Sopenharmony_ci const struct spi_mem_op *op, 828c2ecf20Sopenharmony_ci struct sg_table *sgt) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct device *dmadev; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!op->data.nbytes) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) 908c2ecf20Sopenharmony_ci dmadev = ctlr->dma_tx->device->dev; 918c2ecf20Sopenharmony_ci else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) 928c2ecf20Sopenharmony_ci dmadev = ctlr->dma_rx->device->dev; 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci dmadev = ctlr->dev.parent; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci spi_unmap_buf(ctlr, dmadev, sgt, 978c2ecf20Sopenharmony_ci op->data.dir == SPI_MEM_DATA_IN ? 988c2ecf20Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u32 mode = mem->spi->mode; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci switch (buswidth) { 1078c2ecf20Sopenharmony_ci case 1: 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci case 2: 1118c2ecf20Sopenharmony_ci if ((tx && 1128c2ecf20Sopenharmony_ci (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) || 1138c2ecf20Sopenharmony_ci (!tx && 1148c2ecf20Sopenharmony_ci (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))) 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci case 4: 1208c2ecf20Sopenharmony_ci if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) || 1218c2ecf20Sopenharmony_ci (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL)))) 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci case 8: 1278c2ecf20Sopenharmony_ci if ((tx && (mode & SPI_TX_OCTAL)) || 1288c2ecf20Sopenharmony_ci (!tx && (mode & SPI_RX_OCTAL))) 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci default: 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return -ENOTSUPP; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cibool spi_mem_default_supports_op(struct spi_mem *mem, 1418c2ecf20Sopenharmony_ci const struct spi_mem_op *op) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (spi_check_buswidth_req(mem, op->cmd.buswidth, true)) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (op->addr.nbytes && 1478c2ecf20Sopenharmony_ci spi_check_buswidth_req(mem, op->addr.buswidth, true)) 1488c2ecf20Sopenharmony_ci return false; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (op->dummy.nbytes && 1518c2ecf20Sopenharmony_ci spi_check_buswidth_req(mem, op->dummy.buswidth, true)) 1528c2ecf20Sopenharmony_ci return false; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (op->data.dir != SPI_MEM_NO_DATA && 1558c2ecf20Sopenharmony_ci spi_check_buswidth_req(mem, op->data.buswidth, 1568c2ecf20Sopenharmony_ci op->data.dir == SPI_MEM_DATA_OUT)) 1578c2ecf20Sopenharmony_ci return false; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) 1608c2ecf20Sopenharmony_ci return false; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (op->cmd.nbytes != 1) 1638c2ecf20Sopenharmony_ci return false; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return true; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_default_supports_op); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic bool spi_mem_buswidth_is_valid(u8 buswidth) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (hweight8(buswidth) > 1 || buswidth > SPI_MEM_MAX_BUSWIDTH) 1728c2ecf20Sopenharmony_ci return false; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return true; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int spi_mem_check_op(const struct spi_mem_op *op) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (!op->cmd.buswidth || !op->cmd.nbytes) 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if ((op->addr.nbytes && !op->addr.buswidth) || 1838c2ecf20Sopenharmony_ci (op->dummy.nbytes && !op->dummy.buswidth) || 1848c2ecf20Sopenharmony_ci (op->data.nbytes && !op->data.buswidth)) 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!spi_mem_buswidth_is_valid(op->cmd.buswidth) || 1888c2ecf20Sopenharmony_ci !spi_mem_buswidth_is_valid(op->addr.buswidth) || 1898c2ecf20Sopenharmony_ci !spi_mem_buswidth_is_valid(op->dummy.buswidth) || 1908c2ecf20Sopenharmony_ci !spi_mem_buswidth_is_valid(op->data.buswidth)) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic bool spi_mem_internal_supports_op(struct spi_mem *mem, 1978c2ecf20Sopenharmony_ci const struct spi_mem_op *op) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (ctlr->mem_ops && ctlr->mem_ops->supports_op) 2028c2ecf20Sopenharmony_ci return ctlr->mem_ops->supports_op(mem, op); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return spi_mem_default_supports_op(mem, op); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * spi_mem_supports_op() - Check if a memory device and the controller it is 2098c2ecf20Sopenharmony_ci * connected to support a specific memory operation 2108c2ecf20Sopenharmony_ci * @mem: the SPI memory 2118c2ecf20Sopenharmony_ci * @op: the memory operation to check 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Some controllers are only supporting Single or Dual IOs, others might only 2148c2ecf20Sopenharmony_ci * support specific opcodes, or it can even be that the controller and device 2158c2ecf20Sopenharmony_ci * both support Quad IOs but the hardware prevents you from using it because 2168c2ecf20Sopenharmony_ci * only 2 IO lines are connected. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * This function checks whether a specific operation is supported. 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * Return: true if @op is supported, false otherwise. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cibool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci if (spi_mem_check_op(op)) 2258c2ecf20Sopenharmony_ci return false; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return spi_mem_internal_supports_op(mem, op); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_supports_op); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int spi_mem_access_start(struct spi_mem *mem) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * Flush the message queue before executing our SPI memory 2378c2ecf20Sopenharmony_ci * operation to prevent preemption of regular SPI transfers. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci spi_flush_queue(ctlr); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (ctlr->auto_runtime_pm) { 2428c2ecf20Sopenharmony_ci int ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(ctlr->dev.parent); 2458c2ecf20Sopenharmony_ci if (ret < 0) { 2468c2ecf20Sopenharmony_ci pm_runtime_put_noidle(ctlr->dev.parent); 2478c2ecf20Sopenharmony_ci dev_err(&ctlr->dev, "Failed to power device: %d\n", 2488c2ecf20Sopenharmony_ci ret); 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci mutex_lock(&ctlr->bus_lock_mutex); 2548c2ecf20Sopenharmony_ci mutex_lock(&ctlr->io_mutex); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void spi_mem_access_end(struct spi_mem *mem) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci mutex_unlock(&ctlr->io_mutex); 2648c2ecf20Sopenharmony_ci mutex_unlock(&ctlr->bus_lock_mutex); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (ctlr->auto_runtime_pm) 2678c2ecf20Sopenharmony_ci pm_runtime_put(ctlr->dev.parent); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * spi_mem_exec_op() - Execute a memory operation 2728c2ecf20Sopenharmony_ci * @mem: the SPI memory 2738c2ecf20Sopenharmony_ci * @op: the memory operation to execute 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Executes a memory operation. 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * This function first checks that @op is supported and then tries to execute 2788c2ecf20Sopenharmony_ci * it. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ciint spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0; 2858c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 2868c2ecf20Sopenharmony_ci struct spi_transfer xfers[4] = { }; 2878c2ecf20Sopenharmony_ci struct spi_message msg; 2888c2ecf20Sopenharmony_ci u8 *tmpbuf; 2898c2ecf20Sopenharmony_ci int ret; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = spi_mem_check_op(op); 2928c2ecf20Sopenharmony_ci if (ret) 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!spi_mem_internal_supports_op(mem, op)) 2968c2ecf20Sopenharmony_ci return -ENOTSUPP; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (ctlr->mem_ops && !mem->spi->cs_gpiod) { 2998c2ecf20Sopenharmony_ci ret = spi_mem_access_start(mem); 3008c2ecf20Sopenharmony_ci if (ret) 3018c2ecf20Sopenharmony_ci return ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = ctlr->mem_ops->exec_op(mem, op); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci spi_mem_access_end(mem); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * Some controllers only optimize specific paths (typically the 3098c2ecf20Sopenharmony_ci * read path) and expect the core to use the regular SPI 3108c2ecf20Sopenharmony_ci * interface in other cases. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (!ret || ret != -ENOTSUPP) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so 3208c2ecf20Sopenharmony_ci * we're guaranteed that this buffer is DMA-able, as required by the 3218c2ecf20Sopenharmony_ci * SPI layer. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA); 3248c2ecf20Sopenharmony_ci if (!tmpbuf) 3258c2ecf20Sopenharmony_ci return -ENOMEM; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci spi_message_init(&msg); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci tmpbuf[0] = op->cmd.opcode; 3308c2ecf20Sopenharmony_ci xfers[xferpos].tx_buf = tmpbuf; 3318c2ecf20Sopenharmony_ci xfers[xferpos].len = op->cmd.nbytes; 3328c2ecf20Sopenharmony_ci xfers[xferpos].tx_nbits = op->cmd.buswidth; 3338c2ecf20Sopenharmony_ci spi_message_add_tail(&xfers[xferpos], &msg); 3348c2ecf20Sopenharmony_ci xferpos++; 3358c2ecf20Sopenharmony_ci totalxferlen++; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (op->addr.nbytes) { 3388c2ecf20Sopenharmony_ci int i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for (i = 0; i < op->addr.nbytes; i++) 3418c2ecf20Sopenharmony_ci tmpbuf[i + 1] = op->addr.val >> 3428c2ecf20Sopenharmony_ci (8 * (op->addr.nbytes - i - 1)); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci xfers[xferpos].tx_buf = tmpbuf + 1; 3458c2ecf20Sopenharmony_ci xfers[xferpos].len = op->addr.nbytes; 3468c2ecf20Sopenharmony_ci xfers[xferpos].tx_nbits = op->addr.buswidth; 3478c2ecf20Sopenharmony_ci spi_message_add_tail(&xfers[xferpos], &msg); 3488c2ecf20Sopenharmony_ci xferpos++; 3498c2ecf20Sopenharmony_ci totalxferlen += op->addr.nbytes; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (op->dummy.nbytes) { 3538c2ecf20Sopenharmony_ci memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes); 3548c2ecf20Sopenharmony_ci xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1; 3558c2ecf20Sopenharmony_ci xfers[xferpos].len = op->dummy.nbytes; 3568c2ecf20Sopenharmony_ci xfers[xferpos].tx_nbits = op->dummy.buswidth; 3578c2ecf20Sopenharmony_ci spi_message_add_tail(&xfers[xferpos], &msg); 3588c2ecf20Sopenharmony_ci xferpos++; 3598c2ecf20Sopenharmony_ci totalxferlen += op->dummy.nbytes; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (op->data.nbytes) { 3638c2ecf20Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 3648c2ecf20Sopenharmony_ci xfers[xferpos].rx_buf = op->data.buf.in; 3658c2ecf20Sopenharmony_ci xfers[xferpos].rx_nbits = op->data.buswidth; 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci xfers[xferpos].tx_buf = op->data.buf.out; 3688c2ecf20Sopenharmony_ci xfers[xferpos].tx_nbits = op->data.buswidth; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci xfers[xferpos].len = op->data.nbytes; 3728c2ecf20Sopenharmony_ci spi_message_add_tail(&xfers[xferpos], &msg); 3738c2ecf20Sopenharmony_ci xferpos++; 3748c2ecf20Sopenharmony_ci totalxferlen += op->data.nbytes; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci ret = spi_sync(mem->spi, &msg); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci kfree(tmpbuf); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (ret) 3828c2ecf20Sopenharmony_ci return ret; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (msg.actual_length != totalxferlen) 3858c2ecf20Sopenharmony_ci return -EIO; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_exec_op); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * spi_mem_get_name() - Return the SPI mem device name to be used by the 3938c2ecf20Sopenharmony_ci * upper layer if necessary 3948c2ecf20Sopenharmony_ci * @mem: the SPI memory 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * This function allows SPI mem users to retrieve the SPI mem device name. 3978c2ecf20Sopenharmony_ci * It is useful if the upper layer needs to expose a custom name for 3988c2ecf20Sopenharmony_ci * compatibility reasons. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * Return: a string containing the name of the memory device to be used 4018c2ecf20Sopenharmony_ci * by the SPI mem user 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ciconst char *spi_mem_get_name(struct spi_mem *mem) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci return mem->name; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_get_name); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/** 4108c2ecf20Sopenharmony_ci * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to 4118c2ecf20Sopenharmony_ci * match controller limitations 4128c2ecf20Sopenharmony_ci * @mem: the SPI memory 4138c2ecf20Sopenharmony_ci * @op: the operation to adjust 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * Some controllers have FIFO limitations and must split a data transfer 4168c2ecf20Sopenharmony_ci * operation into multiple ones, others require a specific alignment for 4178c2ecf20Sopenharmony_ci * optimized accesses. This function allows SPI mem drivers to split a single 4188c2ecf20Sopenharmony_ci * operation into multiple sub-operations when required. 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Return: a negative error code if the controller can't properly adjust @op, 4218c2ecf20Sopenharmony_ci * 0 otherwise. Note that @op->data.nbytes will be updated if @op 4228c2ecf20Sopenharmony_ci * can't be handled in a single step. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ciint spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 4278c2ecf20Sopenharmony_ci size_t len; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size) 4308c2ecf20Sopenharmony_ci return ctlr->mem_ops->adjust_op_size(mem, op); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) { 4338c2ecf20Sopenharmony_ci len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (len > spi_max_transfer_size(mem->spi)) 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci op->data.nbytes = min3((size_t)op->data.nbytes, 4398c2ecf20Sopenharmony_ci spi_max_transfer_size(mem->spi), 4408c2ecf20Sopenharmony_ci spi_max_message_size(mem->spi) - 4418c2ecf20Sopenharmony_ci len); 4428c2ecf20Sopenharmony_ci if (!op->data.nbytes) 4438c2ecf20Sopenharmony_ci return -EINVAL; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, 4518c2ecf20Sopenharmony_ci u64 offs, size_t len, void *buf) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct spi_mem_op op = desc->info.op_tmpl; 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci op.addr.val = desc->info.offset + offs; 4578c2ecf20Sopenharmony_ci op.data.buf.in = buf; 4588c2ecf20Sopenharmony_ci op.data.nbytes = len; 4598c2ecf20Sopenharmony_ci ret = spi_mem_adjust_op_size(desc->mem, &op); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ret = spi_mem_exec_op(desc->mem, &op); 4648c2ecf20Sopenharmony_ci if (ret) 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return op.data.nbytes; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc, 4718c2ecf20Sopenharmony_ci u64 offs, size_t len, const void *buf) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct spi_mem_op op = desc->info.op_tmpl; 4748c2ecf20Sopenharmony_ci int ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci op.addr.val = desc->info.offset + offs; 4778c2ecf20Sopenharmony_ci op.data.buf.out = buf; 4788c2ecf20Sopenharmony_ci op.data.nbytes = len; 4798c2ecf20Sopenharmony_ci ret = spi_mem_adjust_op_size(desc->mem, &op); 4808c2ecf20Sopenharmony_ci if (ret) 4818c2ecf20Sopenharmony_ci return ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = spi_mem_exec_op(desc->mem, &op); 4848c2ecf20Sopenharmony_ci if (ret) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return op.data.nbytes; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/** 4918c2ecf20Sopenharmony_ci * spi_mem_dirmap_create() - Create a direct mapping descriptor 4928c2ecf20Sopenharmony_ci * @mem: SPI mem device this direct mapping should be created for 4938c2ecf20Sopenharmony_ci * @info: direct mapping information 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * This function is creating a direct mapping descriptor which can then be used 4968c2ecf20Sopenharmony_ci * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write(). 4978c2ecf20Sopenharmony_ci * If the SPI controller driver does not support direct mapping, this function 4988c2ecf20Sopenharmony_ci * falls back to an implementation using spi_mem_exec_op(), so that the caller 4998c2ecf20Sopenharmony_ci * doesn't have to bother implementing a fallback on his own. 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Return: a valid pointer in case of success, and ERR_PTR() otherwise. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_cistruct spi_mem_dirmap_desc * 5048c2ecf20Sopenharmony_cispi_mem_dirmap_create(struct spi_mem *mem, 5058c2ecf20Sopenharmony_ci const struct spi_mem_dirmap_info *info) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct spi_controller *ctlr = mem->spi->controller; 5088c2ecf20Sopenharmony_ci struct spi_mem_dirmap_desc *desc; 5098c2ecf20Sopenharmony_ci int ret = -ENOTSUPP; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Make sure the number of address cycles is between 1 and 8 bytes. */ 5128c2ecf20Sopenharmony_ci if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8) 5138c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */ 5168c2ecf20Sopenharmony_ci if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA) 5178c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 5208c2ecf20Sopenharmony_ci if (!desc) 5218c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci desc->mem = mem; 5248c2ecf20Sopenharmony_ci desc->info = *info; 5258c2ecf20Sopenharmony_ci if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) 5268c2ecf20Sopenharmony_ci ret = ctlr->mem_ops->dirmap_create(desc); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (ret) { 5298c2ecf20Sopenharmony_ci desc->nodirmap = true; 5308c2ecf20Sopenharmony_ci if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) 5318c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci ret = 0; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (ret) { 5378c2ecf20Sopenharmony_ci kfree(desc); 5388c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return desc; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_dirmap_create); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/** 5468c2ecf20Sopenharmony_ci * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor 5478c2ecf20Sopenharmony_ci * @desc: the direct mapping descriptor to destroy 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * This function destroys a direct mapping descriptor previously created by 5508c2ecf20Sopenharmony_ci * spi_mem_dirmap_create(). 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_civoid spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct spi_controller *ctlr = desc->mem->spi->controller; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy) 5578c2ecf20Sopenharmony_ci ctlr->mem_ops->dirmap_destroy(desc); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci kfree(desc); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void devm_spi_mem_dirmap_release(struct device *dev, void *res) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct spi_mem_dirmap_desc *desc = *(struct spi_mem_dirmap_desc **)res; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci spi_mem_dirmap_destroy(desc); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/** 5718c2ecf20Sopenharmony_ci * devm_spi_mem_dirmap_create() - Create a direct mapping descriptor and attach 5728c2ecf20Sopenharmony_ci * it to a device 5738c2ecf20Sopenharmony_ci * @dev: device the dirmap desc will be attached to 5748c2ecf20Sopenharmony_ci * @mem: SPI mem device this direct mapping should be created for 5758c2ecf20Sopenharmony_ci * @info: direct mapping information 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * devm_ variant of the spi_mem_dirmap_create() function. See 5788c2ecf20Sopenharmony_ci * spi_mem_dirmap_create() for more details. 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * Return: a valid pointer in case of success, and ERR_PTR() otherwise. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistruct spi_mem_dirmap_desc * 5838c2ecf20Sopenharmony_cidevm_spi_mem_dirmap_create(struct device *dev, struct spi_mem *mem, 5848c2ecf20Sopenharmony_ci const struct spi_mem_dirmap_info *info) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct spi_mem_dirmap_desc **ptr, *desc; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_spi_mem_dirmap_release, sizeof(*ptr), 5898c2ecf20Sopenharmony_ci GFP_KERNEL); 5908c2ecf20Sopenharmony_ci if (!ptr) 5918c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci desc = spi_mem_dirmap_create(mem, info); 5948c2ecf20Sopenharmony_ci if (IS_ERR(desc)) { 5958c2ecf20Sopenharmony_ci devres_free(ptr); 5968c2ecf20Sopenharmony_ci } else { 5978c2ecf20Sopenharmony_ci *ptr = desc; 5988c2ecf20Sopenharmony_ci devres_add(dev, ptr); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return desc; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic int devm_spi_mem_dirmap_match(struct device *dev, void *res, void *data) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct spi_mem_dirmap_desc **ptr = res; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (WARN_ON(!ptr || !*ptr)) 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci return *ptr == data; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/** 6168c2ecf20Sopenharmony_ci * devm_spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor attached 6178c2ecf20Sopenharmony_ci * to a device 6188c2ecf20Sopenharmony_ci * @dev: device the dirmap desc is attached to 6198c2ecf20Sopenharmony_ci * @desc: the direct mapping descriptor to destroy 6208c2ecf20Sopenharmony_ci * 6218c2ecf20Sopenharmony_ci * devm_ variant of the spi_mem_dirmap_destroy() function. See 6228c2ecf20Sopenharmony_ci * spi_mem_dirmap_destroy() for more details. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_civoid devm_spi_mem_dirmap_destroy(struct device *dev, 6258c2ecf20Sopenharmony_ci struct spi_mem_dirmap_desc *desc) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci devres_release(dev, devm_spi_mem_dirmap_release, 6288c2ecf20Sopenharmony_ci devm_spi_mem_dirmap_match, desc); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_destroy); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/** 6338c2ecf20Sopenharmony_ci * spi_mem_dirmap_read() - Read data through a direct mapping 6348c2ecf20Sopenharmony_ci * @desc: direct mapping descriptor 6358c2ecf20Sopenharmony_ci * @offs: offset to start reading from. Note that this is not an absolute 6368c2ecf20Sopenharmony_ci * offset, but the offset within the direct mapping which already has 6378c2ecf20Sopenharmony_ci * its own offset 6388c2ecf20Sopenharmony_ci * @len: length in bytes 6398c2ecf20Sopenharmony_ci * @buf: destination buffer. This buffer must be DMA-able 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * This function reads data from a memory device using a direct mapping 6428c2ecf20Sopenharmony_ci * previously instantiated with spi_mem_dirmap_create(). 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * Return: the amount of data read from the memory device or a negative error 6458c2ecf20Sopenharmony_ci * code. Note that the returned size might be smaller than @len, and the caller 6468c2ecf20Sopenharmony_ci * is responsible for calling spi_mem_dirmap_read() again when that happens. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_cissize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, 6498c2ecf20Sopenharmony_ci u64 offs, size_t len, void *buf) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct spi_controller *ctlr = desc->mem->spi->controller; 6528c2ecf20Sopenharmony_ci ssize_t ret; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (!len) 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (desc->nodirmap) { 6618c2ecf20Sopenharmony_ci ret = spi_mem_no_dirmap_read(desc, offs, len, buf); 6628c2ecf20Sopenharmony_ci } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) { 6638c2ecf20Sopenharmony_ci ret = spi_mem_access_start(desc->mem); 6648c2ecf20Sopenharmony_ci if (ret) 6658c2ecf20Sopenharmony_ci return ret; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci spi_mem_access_end(desc->mem); 6708c2ecf20Sopenharmony_ci } else { 6718c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return ret; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_dirmap_read); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/** 6798c2ecf20Sopenharmony_ci * spi_mem_dirmap_write() - Write data through a direct mapping 6808c2ecf20Sopenharmony_ci * @desc: direct mapping descriptor 6818c2ecf20Sopenharmony_ci * @offs: offset to start writing from. Note that this is not an absolute 6828c2ecf20Sopenharmony_ci * offset, but the offset within the direct mapping which already has 6838c2ecf20Sopenharmony_ci * its own offset 6848c2ecf20Sopenharmony_ci * @len: length in bytes 6858c2ecf20Sopenharmony_ci * @buf: source buffer. This buffer must be DMA-able 6868c2ecf20Sopenharmony_ci * 6878c2ecf20Sopenharmony_ci * This function writes data to a memory device using a direct mapping 6888c2ecf20Sopenharmony_ci * previously instantiated with spi_mem_dirmap_create(). 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Return: the amount of data written to the memory device or a negative error 6918c2ecf20Sopenharmony_ci * code. Note that the returned size might be smaller than @len, and the caller 6928c2ecf20Sopenharmony_ci * is responsible for calling spi_mem_dirmap_write() again when that happens. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cissize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, 6958c2ecf20Sopenharmony_ci u64 offs, size_t len, const void *buf) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct spi_controller *ctlr = desc->mem->spi->controller; 6988c2ecf20Sopenharmony_ci ssize_t ret; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT) 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!len) 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (desc->nodirmap) { 7078c2ecf20Sopenharmony_ci ret = spi_mem_no_dirmap_write(desc, offs, len, buf); 7088c2ecf20Sopenharmony_ci } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) { 7098c2ecf20Sopenharmony_ci ret = spi_mem_access_start(desc->mem); 7108c2ecf20Sopenharmony_ci if (ret) 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci spi_mem_access_end(desc->mem); 7168c2ecf20Sopenharmony_ci } else { 7178c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci return ret; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_dirmap_write); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci return container_of(drv, struct spi_mem_driver, spidrv.driver); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int spi_mem_probe(struct spi_device *spi) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); 7328c2ecf20Sopenharmony_ci struct spi_controller *ctlr = spi->controller; 7338c2ecf20Sopenharmony_ci struct spi_mem *mem; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL); 7368c2ecf20Sopenharmony_ci if (!mem) 7378c2ecf20Sopenharmony_ci return -ENOMEM; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci mem->spi = spi; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (ctlr->mem_ops && ctlr->mem_ops->get_name) 7428c2ecf20Sopenharmony_ci mem->name = ctlr->mem_ops->get_name(mem); 7438c2ecf20Sopenharmony_ci else 7448c2ecf20Sopenharmony_ci mem->name = dev_name(&spi->dev); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(mem->name)) 7478c2ecf20Sopenharmony_ci return PTR_ERR(mem->name); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci spi_set_drvdata(spi, mem); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return memdrv->probe(mem); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int spi_mem_remove(struct spi_device *spi) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); 7578c2ecf20Sopenharmony_ci struct spi_mem *mem = spi_get_drvdata(spi); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (memdrv->remove) 7608c2ecf20Sopenharmony_ci return memdrv->remove(mem); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic void spi_mem_shutdown(struct spi_device *spi) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); 7688c2ecf20Sopenharmony_ci struct spi_mem *mem = spi_get_drvdata(spi); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (memdrv->shutdown) 7718c2ecf20Sopenharmony_ci memdrv->shutdown(mem); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/** 7758c2ecf20Sopenharmony_ci * spi_mem_driver_register_with_owner() - Register a SPI memory driver 7768c2ecf20Sopenharmony_ci * @memdrv: the SPI memory driver to register 7778c2ecf20Sopenharmony_ci * @owner: the owner of this driver 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Registers a SPI memory driver. 7808c2ecf20Sopenharmony_ci * 7818c2ecf20Sopenharmony_ci * Return: 0 in case of success, a negative error core otherwise. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ciint spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv, 7858c2ecf20Sopenharmony_ci struct module *owner) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci memdrv->spidrv.probe = spi_mem_probe; 7888c2ecf20Sopenharmony_ci memdrv->spidrv.remove = spi_mem_remove; 7898c2ecf20Sopenharmony_ci memdrv->spidrv.shutdown = spi_mem_shutdown; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return __spi_register_driver(owner, &memdrv->spidrv); 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci/** 7968c2ecf20Sopenharmony_ci * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver 7978c2ecf20Sopenharmony_ci * @memdrv: the SPI memory driver to unregister 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * Unregisters a SPI memory driver. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_civoid spi_mem_driver_unregister(struct spi_mem_driver *memdrv) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci spi_unregister_driver(&memdrv->spidrv); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_mem_driver_unregister); 806