blob: 9dd8a10969166345f2288a7847e8cbe58a53818a [file] [log] [blame]
Thomas Gleixnera61127c2019-05-29 16:57:49 -07001// SPDX-License-Identifier: GPL-2.0-only
Frederic Danis8a00a612013-05-29 15:35:02 +02002/*
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
Frederic Danis8a00a612013-05-29 15:35:02 +02004 */
5
6#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
7
Vincent Cuissardfcd9d042015-10-26 10:27:42 +01008#include <linux/module.h>
9
Frederic Danis8a00a612013-05-29 15:35:02 +020010#include <linux/export.h>
11#include <linux/spi/spi.h>
Frederic Danisee9596d2013-05-29 15:35:03 +020012#include <linux/crc-ccitt.h>
Frederic Danis8a00a612013-05-29 15:35:02 +020013#include <net/nfc/nci_core.h>
14
Frederic Danis391d8a22013-05-29 15:35:04 +020015#define NCI_SPI_ACK_SHIFT 6
16#define NCI_SPI_MSB_PAYLOAD_MASK 0x3F
Frederic Danis8a00a612013-05-29 15:35:02 +020017
Frederic Danisee9596d2013-05-29 15:35:03 +020018#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
Eric Lapuyade2bed2782013-09-23 17:56:43 +020030static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
31 int cs_change)
Frederic Danisee9596d2013-05-29 15:35:03 +020032{
33 struct spi_message m;
34 struct spi_transfer t;
35
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +020036 memset(&t, 0, sizeof(struct spi_transfer));
Eric Lapuyade2bed2782013-09-23 17:56:43 +020037 /* 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;
Eric Lapuyadefa544ff2013-09-05 11:02:21 +020047 t.delay_usecs = nspi->xfer_udelay;
Vincent Cuissard2bd83242015-10-26 10:27:43 +010048 t.speed_hz = nspi->xfer_speed_hz;
Frederic Danisee9596d2013-05-29 15:35:03 +020049
50 spi_message_init(&m);
51 spi_message_add_tail(&t, &m);
52
Eric Lapuyadefa544ff2013-09-05 11:02:21 +020053 return spi_sync(nspi->spi, &m);
Frederic Danisee9596d2013-05-29 15:35:03 +020054}
55
Eric Lapuyade2bed2782013-09-23 17:56:43 +020056int nci_spi_send(struct nci_spi *nspi,
57 struct completion *write_handshake_completion,
58 struct sk_buff *skb)
Frederic Danis8a00a612013-05-29 15:35:02 +020059{
Frederic Danisee9596d2013-05-29 15:35:03 +020060 unsigned int payload_len = skb->len;
61 unsigned char *hdr;
62 int ret;
63 long completion_rc;
64
Frederic Danisee9596d2013-05-29 15:35:03 +020065 /* add the NCI SPI header to the start of the buffer */
66 hdr = skb_push(skb, NCI_SPI_HDR_LEN);
67 hdr[0] = NCI_SPI_DIRECT_WRITE;
Eric Lapuyadefa544ff2013-09-05 11:02:21 +020068 hdr[1] = nspi->acknowledge_mode;
Frederic Danisee9596d2013-05-29 15:35:03 +020069 hdr[2] = payload_len >> 8;
70 hdr[3] = payload_len & 0xFF;
71
Eric Lapuyadefa544ff2013-09-05 11:02:21 +020072 if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
Frederic Danisee9596d2013-05-29 15:35:03 +020073 u16 crc;
74
75 crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
Johannes Berg634fef62017-06-16 14:29:24 +020076 skb_put_u8(skb, crc >> 8);
77 skb_put_u8(skb, crc & 0xFF);
Frederic Danisee9596d2013-05-29 15:35:03 +020078 }
79
Eric Lapuyade2bed2782013-09-23 17:56:43 +020080 if (write_handshake_completion) {
81 /* Trick SPI driver to raise chip select */
82 ret = __nci_spi_send(nspi, NULL, 1);
83 if (ret)
84 goto done;
Frederic Danisee9596d2013-05-29 15:35:03 +020085
Eric Lapuyade2bed2782013-09-23 17:56:43 +020086 /* wait for NFC chip hardware handshake to complete */
87 if (wait_for_completion_timeout(write_handshake_completion,
88 msecs_to_jiffies(1000)) == 0) {
89 ret = -ETIME;
90 goto done;
91 }
92 }
Frederic Danisee9596d2013-05-29 15:35:03 +020093
Eric Lapuyade2bed2782013-09-23 17:56:43 +020094 ret = __nci_spi_send(nspi, skb, 0);
Eric Lapuyadefa544ff2013-09-05 11:02:21 +020095 if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
Frederic Danisee9596d2013-05-29 15:35:03 +020096 goto done;
97
Axel Lin9bec44b2014-02-13 13:25:48 +080098 reinit_completion(&nspi->req_completion);
Eric Lapuyaded5937512013-09-02 12:35:39 +020099 completion_rc = wait_for_completion_interruptible_timeout(
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200100 &nspi->req_completion,
Eric Lapuyaded5937512013-09-02 12:35:39 +0200101 NCI_SPI_SEND_TIMEOUT);
Frederic Danisee9596d2013-05-29 15:35:03 +0200102
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200103 if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
Frederic Danisee9596d2013-05-29 15:35:03 +0200104 ret = -EIO;
105
106done:
Eric Lapuyade2bed2782013-09-23 17:56:43 +0200107 kfree_skb(skb);
108
Frederic Danisee9596d2013-05-29 15:35:03 +0200109 return ret;
Frederic Danis8a00a612013-05-29 15:35:02 +0200110}
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200111EXPORT_SYMBOL_GPL(nci_spi_send);
Frederic Danis8a00a612013-05-29 15:35:02 +0200112
113/* ---- Interface to NCI SPI drivers ---- */
114
115/**
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200116 * nci_spi_allocate_spi - allocate a new nci spi
Frederic Danis8a00a612013-05-29 15:35:02 +0200117 *
118 * @spi: SPI device
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200119 * @acknowledge_mode: Acknowledge mode used by the NFC device
Frederic Danis8a00a612013-05-29 15:35:02 +0200120 * @delay: delay between transactions in us
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200121 * @ndev: nci dev to send incoming nci frames to
Frederic Danis8a00a612013-05-29 15:35:02 +0200122 */
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200123struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200124 u8 acknowledge_mode, unsigned int delay,
125 struct nci_dev *ndev)
Frederic Danis8a00a612013-05-29 15:35:02 +0200126{
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200127 struct nci_spi *nspi;
Frederic Danis8a00a612013-05-29 15:35:02 +0200128
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200129 nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
130 if (!nspi)
Frederic Danis8a00a612013-05-29 15:35:02 +0200131 return NULL;
132
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200133 nspi->acknowledge_mode = acknowledge_mode;
134 nspi->xfer_udelay = delay;
Vincent Cuissard2bd83242015-10-26 10:27:43 +0100135 /* Use controller max SPI speed by default */
136 nspi->xfer_speed_hz = 0;
Eric Lapuyade645d5082013-09-19 16:52:06 +0200137 nspi->spi = spi;
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200138 nspi->ndev = ndev;
Axel Lin9bec44b2014-02-13 13:25:48 +0800139 init_completion(&nspi->req_completion);
Frederic Danis8a00a612013-05-29 15:35:02 +0200140
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200141 return nspi;
Frederic Danis8a00a612013-05-29 15:35:02 +0200142}
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200143EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
Frederic Danis8a00a612013-05-29 15:35:02 +0200144
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200145static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
Frederic Danis391d8a22013-05-29 15:35:04 +0200146{
147 struct sk_buff *skb;
148 unsigned char *hdr;
149 u16 crc;
150 int ret;
151
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200152 skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
Frederic Danis391d8a22013-05-29 15:35:04 +0200153
154 /* add the NCI SPI header to the start of the buffer */
155 hdr = skb_push(skb, NCI_SPI_HDR_LEN);
156 hdr[0] = NCI_SPI_DIRECT_WRITE;
157 hdr[1] = NCI_SPI_CRC_ENABLED;
158 hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
159 hdr[3] = 0;
160
161 crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
Johannes Berg634fef62017-06-16 14:29:24 +0200162 skb_put_u8(skb, crc >> 8);
163 skb_put_u8(skb, crc & 0xFF);
Frederic Danis391d8a22013-05-29 15:35:04 +0200164
Eric Lapuyade2bed2782013-09-23 17:56:43 +0200165 ret = __nci_spi_send(nspi, skb, 0);
Frederic Danis391d8a22013-05-29 15:35:04 +0200166
167 kfree_skb(skb);
168
169 return ret;
170}
171
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200172static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
Frederic Danis391d8a22013-05-29 15:35:04 +0200173{
174 struct sk_buff *skb;
175 struct spi_message m;
176 unsigned char req[2], resp_hdr[2];
177 struct spi_transfer tx, rx;
178 unsigned short rx_len = 0;
179 int ret;
180
181 spi_message_init(&m);
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +0200182
183 memset(&tx, 0, sizeof(struct spi_transfer));
Frederic Danis391d8a22013-05-29 15:35:04 +0200184 req[0] = NCI_SPI_DIRECT_READ;
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200185 req[1] = nspi->acknowledge_mode;
Frederic Danis391d8a22013-05-29 15:35:04 +0200186 tx.tx_buf = req;
187 tx.len = 2;
188 tx.cs_change = 0;
Vincent Cuissard2bd83242015-10-26 10:27:43 +0100189 tx.speed_hz = nspi->xfer_speed_hz;
Frederic Danis391d8a22013-05-29 15:35:04 +0200190 spi_message_add_tail(&tx, &m);
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +0200191
192 memset(&rx, 0, sizeof(struct spi_transfer));
Frederic Danis391d8a22013-05-29 15:35:04 +0200193 rx.rx_buf = resp_hdr;
194 rx.len = 2;
195 rx.cs_change = 1;
Vincent Cuissard2bd83242015-10-26 10:27:43 +0100196 rx.speed_hz = nspi->xfer_speed_hz;
Frederic Danis391d8a22013-05-29 15:35:04 +0200197 spi_message_add_tail(&rx, &m);
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +0200198
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200199 ret = spi_sync(nspi->spi, &m);
Frederic Danis391d8a22013-05-29 15:35:04 +0200200 if (ret)
201 return NULL;
202
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200203 if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
Frederic Danis391d8a22013-05-29 15:35:04 +0200204 rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
205 resp_hdr[1] + NCI_SPI_CRC_LEN;
206 else
207 rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
208
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200209 skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
Frederic Danis391d8a22013-05-29 15:35:04 +0200210 if (!skb)
211 return NULL;
212
213 spi_message_init(&m);
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +0200214
215 memset(&rx, 0, sizeof(struct spi_transfer));
Frederic Danis391d8a22013-05-29 15:35:04 +0200216 rx.rx_buf = skb_put(skb, rx_len);
217 rx.len = rx_len;
218 rx.cs_change = 0;
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200219 rx.delay_usecs = nspi->xfer_udelay;
Vincent Cuissard2bd83242015-10-26 10:27:43 +0100220 rx.speed_hz = nspi->xfer_speed_hz;
Frederic Danis391d8a22013-05-29 15:35:04 +0200221 spi_message_add_tail(&rx, &m);
Eric Lapuyadea4ada6c2013-09-23 18:02:43 +0200222
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200223 ret = spi_sync(nspi->spi, &m);
Frederic Danis391d8a22013-05-29 15:35:04 +0200224 if (ret)
225 goto receive_error;
226
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200227 if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
Johannes Bergd58ff352017-06-16 14:29:23 +0200228 *(u8 *)skb_push(skb, 1) = resp_hdr[1];
229 *(u8 *)skb_push(skb, 1) = resp_hdr[0];
Frederic Danis391d8a22013-05-29 15:35:04 +0200230 }
231
232 return skb;
233
234receive_error:
235 kfree_skb(skb);
236
237 return NULL;
238}
239
240static int nci_spi_check_crc(struct sk_buff *skb)
241{
242 u16 crc_data = (skb->data[skb->len - 2] << 8) |
243 skb->data[skb->len - 1];
244 int ret;
245
246 ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
247 == crc_data);
248
249 skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
250
251 return ret;
252}
253
254static u8 nci_spi_get_ack(struct sk_buff *skb)
255{
256 u8 ret;
257
258 ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
259
260 /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
261 skb_pull(skb, 2);
262
263 return ret;
264}
265
266/**
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200267 * nci_spi_read - read frame from NCI SPI drivers
Frederic Danis391d8a22013-05-29 15:35:04 +0200268 *
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200269 * @nspi: The nci spi
Frederic Danis391d8a22013-05-29 15:35:04 +0200270 * Context: can sleep
271 *
272 * This call may only be used from a context that may sleep. The sleep
273 * is non-interruptible, and has no timeout.
274 *
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200275 * It returns an allocated skb containing the frame on success, or NULL.
Frederic Danis391d8a22013-05-29 15:35:04 +0200276 */
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200277struct sk_buff *nci_spi_read(struct nci_spi *nspi)
Frederic Danis391d8a22013-05-29 15:35:04 +0200278{
279 struct sk_buff *skb;
Frederic Danis391d8a22013-05-29 15:35:04 +0200280
Frederic Danis391d8a22013-05-29 15:35:04 +0200281 /* Retrieve frame from SPI */
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200282 skb = __nci_spi_read(nspi);
283 if (!skb)
Frederic Danis391d8a22013-05-29 15:35:04 +0200284 goto done;
Frederic Danis391d8a22013-05-29 15:35:04 +0200285
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200286 if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
Frederic Danis391d8a22013-05-29 15:35:04 +0200287 if (!nci_spi_check_crc(skb)) {
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200288 send_acknowledge(nspi, ACKNOWLEDGE_NACK);
Frederic Danis391d8a22013-05-29 15:35:04 +0200289 goto done;
290 }
291
292 /* In case of acknowledged mode: if ACK or NACK received,
293 * unblock completion of latest frame sent.
294 */
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200295 nspi->req_result = nci_spi_get_ack(skb);
296 if (nspi->req_result)
297 complete(&nspi->req_completion);
Frederic Danis391d8a22013-05-29 15:35:04 +0200298 }
299
300 /* If there is no payload (ACK/NACK only frame),
301 * free the socket buffer
302 */
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200303 if (!skb->len) {
Frederic Danis391d8a22013-05-29 15:35:04 +0200304 kfree_skb(skb);
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200305 skb = NULL;
Frederic Danis391d8a22013-05-29 15:35:04 +0200306 goto done;
307 }
308
Eric Lapuyadefa544ff2013-09-05 11:02:21 +0200309 if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
310 send_acknowledge(nspi, ACKNOWLEDGE_ACK);
Frederic Danis391d8a22013-05-29 15:35:04 +0200311
Frederic Danis391d8a22013-05-29 15:35:04 +0200312done:
Frederic Danis391d8a22013-05-29 15:35:04 +0200313
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200314 return skb;
Frederic Danis391d8a22013-05-29 15:35:04 +0200315}
Eric Lapuyade22d4aae2013-09-23 17:56:31 +0200316EXPORT_SYMBOL_GPL(nci_spi_read);
Vincent Cuissardfcd9d042015-10-26 10:27:42 +0100317
318MODULE_LICENSE("GPL");