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