Commit | Line | Data |
---|---|---|
a61127c2 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8a00a61b FD |
2 | /* |
3 | * Copyright (C) 2013 Intel Corporation. All rights reserved. | |
8a00a61b FD |
4 | */ |
5 | ||
6 | #define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__ | |
7 | ||
fcd9d046 VC |
8 | #include <linux/module.h> |
9 | ||
8a00a61b FD |
10 | #include <linux/export.h> |
11 | #include <linux/spi/spi.h> | |
ee9596d4 | 12 | #include <linux/crc-ccitt.h> |
8a00a61b FD |
13 | #include <net/nfc/nci_core.h> |
14 | ||
391d8a2d FD |
15 | #define NCI_SPI_ACK_SHIFT 6 |
16 | #define NCI_SPI_MSB_PAYLOAD_MASK 0x3F | |
8a00a61b | 17 | |
ee9596d4 FD |
18 | #define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \ |
19 | NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT) | |
20 | ||
21 | #define NCI_SPI_DIRECT_WRITE 0x01 | |
22 | #define NCI_SPI_DIRECT_READ 0x02 | |
23 | ||
24 | #define ACKNOWLEDGE_NONE 0 | |
25 | #define ACKNOWLEDGE_ACK 1 | |
26 | #define ACKNOWLEDGE_NACK 2 | |
27 | ||
28 | #define CRC_INIT 0xFFFF | |
29 | ||
2bed2785 EL |
30 | static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb, |
31 | int cs_change) | |
ee9596d4 FD |
32 | { |
33 | struct spi_message m; | |
34 | struct spi_transfer t; | |
35 | ||
a4ada6ca | 36 | memset(&t, 0, sizeof(struct spi_transfer)); |
2bed2785 EL |
37 | /* a NULL skb means we just want the SPI chip select line to raise */ |
38 | if (skb) { | |
39 | t.tx_buf = skb->data; | |
40 | t.len = skb->len; | |
41 | } else { | |
42 | /* still set tx_buf non NULL to make the driver happy */ | |
43 | t.tx_buf = &t; | |
44 | t.len = 0; | |
45 | } | |
46 | t.cs_change = cs_change; | |
099ffd7e AA |
47 | t.delay.value = nspi->xfer_udelay; |
48 | t.delay.unit = SPI_DELAY_UNIT_USECS; | |
2bd83245 | 49 | t.speed_hz = nspi->xfer_speed_hz; |
ee9596d4 FD |
50 | |
51 | spi_message_init(&m); | |
52 | spi_message_add_tail(&t, &m); | |
53 | ||
fa544fff | 54 | return spi_sync(nspi->spi, &m); |
ee9596d4 FD |
55 | } |
56 | ||
2bed2785 EL |
57 | int nci_spi_send(struct nci_spi *nspi, |
58 | struct completion *write_handshake_completion, | |
59 | struct sk_buff *skb) | |
8a00a61b | 60 | { |
ee9596d4 FD |
61 | unsigned int payload_len = skb->len; |
62 | unsigned char *hdr; | |
63 | int ret; | |
64 | long completion_rc; | |
65 | ||
ee9596d4 FD |
66 | /* add the NCI SPI header to the start of the buffer */ |
67 | hdr = skb_push(skb, NCI_SPI_HDR_LEN); | |
68 | hdr[0] = NCI_SPI_DIRECT_WRITE; | |
fa544fff | 69 | hdr[1] = nspi->acknowledge_mode; |
ee9596d4 FD |
70 | hdr[2] = payload_len >> 8; |
71 | hdr[3] = payload_len & 0xFF; | |
72 | ||
fa544fff | 73 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
ee9596d4 FD |
74 | u16 crc; |
75 | ||
76 | crc = crc_ccitt(CRC_INIT, skb->data, skb->len); | |
634fef61 JB |
77 | skb_put_u8(skb, crc >> 8); |
78 | skb_put_u8(skb, crc & 0xFF); | |
ee9596d4 FD |
79 | } |
80 | ||
2bed2785 EL |
81 | if (write_handshake_completion) { |
82 | /* Trick SPI driver to raise chip select */ | |
83 | ret = __nci_spi_send(nspi, NULL, 1); | |
84 | if (ret) | |
85 | goto done; | |
ee9596d4 | 86 | |
2bed2785 EL |
87 | /* wait for NFC chip hardware handshake to complete */ |
88 | if (wait_for_completion_timeout(write_handshake_completion, | |
89 | msecs_to_jiffies(1000)) == 0) { | |
90 | ret = -ETIME; | |
91 | goto done; | |
92 | } | |
93 | } | |
ee9596d4 | 94 | |
2bed2785 | 95 | ret = __nci_spi_send(nspi, skb, 0); |
fa544fff | 96 | if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED) |
ee9596d4 FD |
97 | goto done; |
98 | ||
9bec44bf | 99 | reinit_completion(&nspi->req_completion); |
d5937511 | 100 | completion_rc = wait_for_completion_interruptible_timeout( |
fa544fff | 101 | &nspi->req_completion, |
d5937511 | 102 | NCI_SPI_SEND_TIMEOUT); |
ee9596d4 | 103 | |
fa544fff | 104 | if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK) |
ee9596d4 FD |
105 | ret = -EIO; |
106 | ||
107 | done: | |
2bed2785 EL |
108 | kfree_skb(skb); |
109 | ||
ee9596d4 | 110 | return ret; |
8a00a61b | 111 | } |
fa544fff | 112 | EXPORT_SYMBOL_GPL(nci_spi_send); |
8a00a61b FD |
113 | |
114 | /* ---- Interface to NCI SPI drivers ---- */ | |
115 | ||
116 | /** | |
fa544fff | 117 | * nci_spi_allocate_spi - allocate a new nci spi |
8a00a61b FD |
118 | * |
119 | * @spi: SPI device | |
fa544fff | 120 | * @acknowledge_mode: Acknowledge mode used by the NFC device |
8a00a61b | 121 | * @delay: delay between transactions in us |
fa544fff | 122 | * @ndev: nci dev to send incoming nci frames to |
8a00a61b | 123 | */ |
fa544fff | 124 | struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, |
fa544fff EL |
125 | u8 acknowledge_mode, unsigned int delay, |
126 | struct nci_dev *ndev) | |
8a00a61b | 127 | { |
fa544fff | 128 | struct nci_spi *nspi; |
8a00a61b | 129 | |
fa544fff EL |
130 | nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL); |
131 | if (!nspi) | |
8a00a61b FD |
132 | return NULL; |
133 | ||
fa544fff EL |
134 | nspi->acknowledge_mode = acknowledge_mode; |
135 | nspi->xfer_udelay = delay; | |
2bd83245 VC |
136 | /* Use controller max SPI speed by default */ |
137 | nspi->xfer_speed_hz = 0; | |
645d5087 | 138 | nspi->spi = spi; |
fa544fff | 139 | nspi->ndev = ndev; |
9bec44bf | 140 | init_completion(&nspi->req_completion); |
8a00a61b | 141 | |
fa544fff | 142 | return nspi; |
8a00a61b | 143 | } |
fa544fff | 144 | EXPORT_SYMBOL_GPL(nci_spi_allocate_spi); |
391d8a2d | 145 | |
fa544fff | 146 | static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge) |
391d8a2d FD |
147 | { |
148 | struct sk_buff *skb; | |
149 | unsigned char *hdr; | |
150 | u16 crc; | |
151 | int ret; | |
152 | ||
fa544fff | 153 | skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL); |
391d8a2d FD |
154 | |
155 | /* add the NCI SPI header to the start of the buffer */ | |
156 | hdr = skb_push(skb, NCI_SPI_HDR_LEN); | |
157 | hdr[0] = NCI_SPI_DIRECT_WRITE; | |
158 | hdr[1] = NCI_SPI_CRC_ENABLED; | |
159 | hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT; | |
160 | hdr[3] = 0; | |
161 | ||
162 | crc = crc_ccitt(CRC_INIT, skb->data, skb->len); | |
634fef61 JB |
163 | skb_put_u8(skb, crc >> 8); |
164 | skb_put_u8(skb, crc & 0xFF); | |
391d8a2d | 165 | |
2bed2785 | 166 | ret = __nci_spi_send(nspi, skb, 0); |
391d8a2d FD |
167 | |
168 | kfree_skb(skb); | |
169 | ||
170 | return ret; | |
171 | } | |
172 | ||
22d4aae5 | 173 | static struct sk_buff *__nci_spi_read(struct nci_spi *nspi) |
391d8a2d FD |
174 | { |
175 | struct sk_buff *skb; | |
176 | struct spi_message m; | |
177 | unsigned char req[2], resp_hdr[2]; | |
178 | struct spi_transfer tx, rx; | |
179 | unsigned short rx_len = 0; | |
180 | int ret; | |
181 | ||
182 | spi_message_init(&m); | |
a4ada6ca EL |
183 | |
184 | memset(&tx, 0, sizeof(struct spi_transfer)); | |
391d8a2d | 185 | req[0] = NCI_SPI_DIRECT_READ; |
fa544fff | 186 | req[1] = nspi->acknowledge_mode; |
391d8a2d FD |
187 | tx.tx_buf = req; |
188 | tx.len = 2; | |
189 | tx.cs_change = 0; | |
2bd83245 | 190 | tx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 191 | spi_message_add_tail(&tx, &m); |
a4ada6ca EL |
192 | |
193 | memset(&rx, 0, sizeof(struct spi_transfer)); | |
391d8a2d FD |
194 | rx.rx_buf = resp_hdr; |
195 | rx.len = 2; | |
196 | rx.cs_change = 1; | |
2bd83245 | 197 | rx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 198 | spi_message_add_tail(&rx, &m); |
a4ada6ca | 199 | |
fa544fff | 200 | ret = spi_sync(nspi->spi, &m); |
391d8a2d FD |
201 | if (ret) |
202 | return NULL; | |
203 | ||
fa544fff | 204 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) |
391d8a2d FD |
205 | rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) + |
206 | resp_hdr[1] + NCI_SPI_CRC_LEN; | |
207 | else | |
208 | rx_len = (resp_hdr[0] << 8) | resp_hdr[1]; | |
209 | ||
fa544fff | 210 | skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL); |
391d8a2d FD |
211 | if (!skb) |
212 | return NULL; | |
213 | ||
214 | spi_message_init(&m); | |
a4ada6ca EL |
215 | |
216 | memset(&rx, 0, sizeof(struct spi_transfer)); | |
391d8a2d FD |
217 | rx.rx_buf = skb_put(skb, rx_len); |
218 | rx.len = rx_len; | |
219 | rx.cs_change = 0; | |
099ffd7e AA |
220 | rx.delay.value = nspi->xfer_udelay; |
221 | rx.delay.unit = SPI_DELAY_UNIT_USECS; | |
2bd83245 | 222 | rx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 223 | spi_message_add_tail(&rx, &m); |
a4ada6ca | 224 | |
fa544fff | 225 | ret = spi_sync(nspi->spi, &m); |
391d8a2d FD |
226 | if (ret) |
227 | goto receive_error; | |
228 | ||
fa544fff | 229 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
d58ff351 JB |
230 | *(u8 *)skb_push(skb, 1) = resp_hdr[1]; |
231 | *(u8 *)skb_push(skb, 1) = resp_hdr[0]; | |
391d8a2d FD |
232 | } |
233 | ||
234 | return skb; | |
235 | ||
236 | receive_error: | |
237 | kfree_skb(skb); | |
238 | ||
239 | return NULL; | |
240 | } | |
241 | ||
242 | static int nci_spi_check_crc(struct sk_buff *skb) | |
243 | { | |
244 | u16 crc_data = (skb->data[skb->len - 2] << 8) | | |
245 | skb->data[skb->len - 1]; | |
246 | int ret; | |
247 | ||
248 | ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN) | |
249 | == crc_data); | |
250 | ||
251 | skb_trim(skb, skb->len - NCI_SPI_CRC_LEN); | |
252 | ||
253 | return ret; | |
254 | } | |
255 | ||
256 | static u8 nci_spi_get_ack(struct sk_buff *skb) | |
257 | { | |
258 | u8 ret; | |
259 | ||
260 | ret = skb->data[0] >> NCI_SPI_ACK_SHIFT; | |
261 | ||
262 | /* Remove NFCC part of the header: ACK, NACK and MSB payload len */ | |
263 | skb_pull(skb, 2); | |
264 | ||
265 | return ret; | |
266 | } | |
267 | ||
268 | /** | |
22d4aae5 | 269 | * nci_spi_read - read frame from NCI SPI drivers |
391d8a2d | 270 | * |
fa544fff | 271 | * @nspi: The nci spi |
391d8a2d FD |
272 | * Context: can sleep |
273 | * | |
274 | * This call may only be used from a context that may sleep. The sleep | |
275 | * is non-interruptible, and has no timeout. | |
276 | * | |
22d4aae5 | 277 | * It returns an allocated skb containing the frame on success, or NULL. |
391d8a2d | 278 | */ |
22d4aae5 | 279 | struct sk_buff *nci_spi_read(struct nci_spi *nspi) |
391d8a2d FD |
280 | { |
281 | struct sk_buff *skb; | |
391d8a2d | 282 | |
391d8a2d | 283 | /* Retrieve frame from SPI */ |
22d4aae5 EL |
284 | skb = __nci_spi_read(nspi); |
285 | if (!skb) | |
391d8a2d | 286 | goto done; |
391d8a2d | 287 | |
fa544fff | 288 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
391d8a2d | 289 | if (!nci_spi_check_crc(skb)) { |
fa544fff | 290 | send_acknowledge(nspi, ACKNOWLEDGE_NACK); |
391d8a2d FD |
291 | goto done; |
292 | } | |
293 | ||
294 | /* In case of acknowledged mode: if ACK or NACK received, | |
295 | * unblock completion of latest frame sent. | |
296 | */ | |
fa544fff EL |
297 | nspi->req_result = nci_spi_get_ack(skb); |
298 | if (nspi->req_result) | |
299 | complete(&nspi->req_completion); | |
391d8a2d FD |
300 | } |
301 | ||
302 | /* If there is no payload (ACK/NACK only frame), | |
303 | * free the socket buffer | |
304 | */ | |
22d4aae5 | 305 | if (!skb->len) { |
391d8a2d | 306 | kfree_skb(skb); |
22d4aae5 | 307 | skb = NULL; |
391d8a2d FD |
308 | goto done; |
309 | } | |
310 | ||
fa544fff EL |
311 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) |
312 | send_acknowledge(nspi, ACKNOWLEDGE_ACK); | |
391d8a2d | 313 | |
391d8a2d | 314 | done: |
391d8a2d | 315 | |
22d4aae5 | 316 | return skb; |
391d8a2d | 317 | } |
22d4aae5 | 318 | EXPORT_SYMBOL_GPL(nci_spi_read); |
fcd9d046 VC |
319 | |
320 | MODULE_LICENSE("GPL"); |