Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / drivers / spi / spi-mem-nodm.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
4  */
5
6 #include <malloc.h>
7 #include <spi.h>
8 #include <spi-mem.h>
9
10 int spi_mem_exec_op(struct spi_slave *slave,
11                     const struct spi_mem_op *op)
12 {
13         unsigned int pos = 0;
14         const u8 *tx_buf = NULL;
15         u8 *rx_buf = NULL;
16         u8 *op_buf;
17         int op_len;
18         u32 flag;
19         int ret;
20         int i;
21
22         if (op->data.nbytes) {
23                 if (op->data.dir == SPI_MEM_DATA_IN)
24                         rx_buf = op->data.buf.in;
25                 else
26                         tx_buf = op->data.buf.out;
27         }
28
29         op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
30         op_buf = calloc(1, op_len);
31
32         ret = spi_claim_bus(slave);
33         if (ret < 0)
34                 return ret;
35
36         op_buf[pos++] = op->cmd.opcode;
37
38         if (op->addr.nbytes) {
39                 for (i = 0; i < op->addr.nbytes; i++)
40                         op_buf[pos + i] = op->addr.val >>
41                                 (8 * (op->addr.nbytes - i - 1));
42
43                 pos += op->addr.nbytes;
44         }
45
46         if (op->dummy.nbytes)
47                 memset(op_buf + pos, 0xff, op->dummy.nbytes);
48
49         /* 1st transfer: opcode + address + dummy cycles */
50         flag = SPI_XFER_BEGIN;
51         /* Make sure to set END bit if no tx or rx data messages follow */
52         if (!tx_buf && !rx_buf)
53                 flag |= SPI_XFER_END;
54
55         ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag);
56         if (ret)
57                 return ret;
58
59         /* 2nd transfer: rx or tx data path */
60         if (tx_buf || rx_buf) {
61                 ret = spi_xfer(slave, op->data.nbytes * 8, tx_buf,
62                                rx_buf, SPI_XFER_END);
63                 if (ret)
64                         return ret;
65         }
66
67         spi_release_bus(slave);
68
69         for (i = 0; i < pos; i++)
70                 debug("%02x ", op_buf[i]);
71         debug("| [%dB %s] ",
72               tx_buf || rx_buf ? op->data.nbytes : 0,
73               tx_buf || rx_buf ? (tx_buf ? "out" : "in") : "-");
74         for (i = 0; i < op->data.nbytes; i++)
75                 debug("%02x ", tx_buf ? tx_buf[i] : rx_buf[i]);
76         debug("[ret %d]\n", ret);
77
78         free(op_buf);
79
80         if (ret < 0)
81                 return ret;
82
83         return 0;
84 }
85
86 int spi_mem_adjust_op_size(struct spi_slave *slave,
87                            struct spi_mem_op *op)
88 {
89         unsigned int len;
90
91         len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
92         if (slave->max_write_size && len > slave->max_write_size)
93                 return -EINVAL;
94
95         if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size)
96                 op->data.nbytes = min(op->data.nbytes,
97                                       slave->max_read_size);
98         else if (slave->max_write_size)
99                 op->data.nbytes = min(op->data.nbytes,
100                                       slave->max_write_size - len);
101
102         if (!op->data.nbytes)
103                 return -EINVAL;
104
105         return 0;
106 }