1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28a00a61bSFrederic Danis /*
38a00a61bSFrederic Danis * Copyright (C) 2013 Intel Corporation. All rights reserved.
48a00a61bSFrederic Danis */
58a00a61bSFrederic Danis
68a00a61bSFrederic Danis #define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
78a00a61bSFrederic Danis
8fcd9d046SVincent Cuissard #include <linux/module.h>
9fcd9d046SVincent Cuissard
108a00a61bSFrederic Danis #include <linux/export.h>
118a00a61bSFrederic Danis #include <linux/spi/spi.h>
12ee9596d4SFrederic Danis #include <linux/crc-ccitt.h>
138a00a61bSFrederic Danis #include <net/nfc/nci_core.h>
148a00a61bSFrederic Danis
15391d8a2dSFrederic Danis #define NCI_SPI_ACK_SHIFT 6
16391d8a2dSFrederic Danis #define NCI_SPI_MSB_PAYLOAD_MASK 0x3F
178a00a61bSFrederic Danis
18ee9596d4SFrederic Danis #define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
19ee9596d4SFrederic Danis NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
20ee9596d4SFrederic Danis
21ee9596d4SFrederic Danis #define NCI_SPI_DIRECT_WRITE 0x01
22ee9596d4SFrederic Danis #define NCI_SPI_DIRECT_READ 0x02
23ee9596d4SFrederic Danis
24ee9596d4SFrederic Danis #define ACKNOWLEDGE_NONE 0
25ee9596d4SFrederic Danis #define ACKNOWLEDGE_ACK 1
26ee9596d4SFrederic Danis #define ACKNOWLEDGE_NACK 2
27ee9596d4SFrederic Danis
28ee9596d4SFrederic Danis #define CRC_INIT 0xFFFF
29ee9596d4SFrederic Danis
__nci_spi_send(struct nci_spi * nspi,const struct sk_buff * skb,int cs_change)30ddecf555SKrzysztof Kozlowski static int __nci_spi_send(struct nci_spi *nspi, const struct sk_buff *skb,
312bed2785SEric Lapuyade int cs_change)
32ee9596d4SFrederic Danis {
33ee9596d4SFrederic Danis struct spi_message m;
34ee9596d4SFrederic Danis struct spi_transfer t;
35ee9596d4SFrederic Danis
36a4ada6caSEric Lapuyade memset(&t, 0, sizeof(struct spi_transfer));
372bed2785SEric Lapuyade /* a NULL skb means we just want the SPI chip select line to raise */
382bed2785SEric Lapuyade if (skb) {
39ee9596d4SFrederic Danis t.tx_buf = skb->data;
40ee9596d4SFrederic Danis t.len = skb->len;
412bed2785SEric Lapuyade } else {
422bed2785SEric Lapuyade /* still set tx_buf non NULL to make the driver happy */
432bed2785SEric Lapuyade t.tx_buf = &t;
442bed2785SEric Lapuyade t.len = 0;
452bed2785SEric Lapuyade }
462bed2785SEric Lapuyade t.cs_change = cs_change;
47099ffd7eSAlexandru Ardelean t.delay.value = nspi->xfer_udelay;
48099ffd7eSAlexandru Ardelean t.delay.unit = SPI_DELAY_UNIT_USECS;
492bd83245SVincent Cuissard t.speed_hz = nspi->xfer_speed_hz;
50ee9596d4SFrederic Danis
51ee9596d4SFrederic Danis spi_message_init(&m);
52ee9596d4SFrederic Danis spi_message_add_tail(&t, &m);
53ee9596d4SFrederic Danis
54fa544fffSEric Lapuyade return spi_sync(nspi->spi, &m);
55ee9596d4SFrederic Danis }
56ee9596d4SFrederic Danis
nci_spi_send(struct nci_spi * nspi,struct completion * write_handshake_completion,struct sk_buff * skb)572bed2785SEric Lapuyade int nci_spi_send(struct nci_spi *nspi,
582bed2785SEric Lapuyade struct completion *write_handshake_completion,
592bed2785SEric Lapuyade struct sk_buff *skb)
608a00a61bSFrederic Danis {
61ee9596d4SFrederic Danis unsigned int payload_len = skb->len;
62ee9596d4SFrederic Danis unsigned char *hdr;
63ee9596d4SFrederic Danis int ret;
64ee9596d4SFrederic Danis long completion_rc;
65ee9596d4SFrederic Danis
66ee9596d4SFrederic Danis /* add the NCI SPI header to the start of the buffer */
67ee9596d4SFrederic Danis hdr = skb_push(skb, NCI_SPI_HDR_LEN);
68ee9596d4SFrederic Danis hdr[0] = NCI_SPI_DIRECT_WRITE;
69fa544fffSEric Lapuyade hdr[1] = nspi->acknowledge_mode;
70ee9596d4SFrederic Danis hdr[2] = payload_len >> 8;
71ee9596d4SFrederic Danis hdr[3] = payload_len & 0xFF;
72ee9596d4SFrederic Danis
73fa544fffSEric Lapuyade if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
74ee9596d4SFrederic Danis u16 crc;
75ee9596d4SFrederic Danis
76ee9596d4SFrederic Danis crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
77634fef61SJohannes Berg skb_put_u8(skb, crc >> 8);
78634fef61SJohannes Berg skb_put_u8(skb, crc & 0xFF);
79ee9596d4SFrederic Danis }
80ee9596d4SFrederic Danis
812bed2785SEric Lapuyade if (write_handshake_completion) {
822bed2785SEric Lapuyade /* Trick SPI driver to raise chip select */
832bed2785SEric Lapuyade ret = __nci_spi_send(nspi, NULL, 1);
842bed2785SEric Lapuyade if (ret)
852bed2785SEric Lapuyade goto done;
86ee9596d4SFrederic Danis
872bed2785SEric Lapuyade /* wait for NFC chip hardware handshake to complete */
882bed2785SEric Lapuyade if (wait_for_completion_timeout(write_handshake_completion,
892bed2785SEric Lapuyade msecs_to_jiffies(1000)) == 0) {
902bed2785SEric Lapuyade ret = -ETIME;
912bed2785SEric Lapuyade goto done;
922bed2785SEric Lapuyade }
932bed2785SEric Lapuyade }
94ee9596d4SFrederic Danis
952bed2785SEric Lapuyade ret = __nci_spi_send(nspi, skb, 0);
96fa544fffSEric Lapuyade if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
97ee9596d4SFrederic Danis goto done;
98ee9596d4SFrederic Danis
999bec44bfSAxel Lin reinit_completion(&nspi->req_completion);
100d5937511SEric Lapuyade completion_rc = wait_for_completion_interruptible_timeout(
101fa544fffSEric Lapuyade &nspi->req_completion,
102ee9596d4SFrederic Danis NCI_SPI_SEND_TIMEOUT);
103ee9596d4SFrederic Danis
104fa544fffSEric Lapuyade if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
105ee9596d4SFrederic Danis ret = -EIO;
106ee9596d4SFrederic Danis
107ee9596d4SFrederic Danis done:
1082bed2785SEric Lapuyade kfree_skb(skb);
1092bed2785SEric Lapuyade
110ee9596d4SFrederic Danis return ret;
1118a00a61bSFrederic Danis }
112fa544fffSEric Lapuyade EXPORT_SYMBOL_GPL(nci_spi_send);
1138a00a61bSFrederic Danis
1148a00a61bSFrederic Danis /* ---- Interface to NCI SPI drivers ---- */
1158a00a61bSFrederic Danis
1168a00a61bSFrederic Danis /**
117fa544fffSEric Lapuyade * nci_spi_allocate_spi - allocate a new nci spi
1188a00a61bSFrederic Danis *
1198a00a61bSFrederic Danis * @spi: SPI device
120fa544fffSEric Lapuyade * @acknowledge_mode: Acknowledge mode used by the NFC device
1218a00a61bSFrederic Danis * @delay: delay between transactions in us
122fa544fffSEric Lapuyade * @ndev: nci dev to send incoming nci frames to
1238a00a61bSFrederic Danis */
nci_spi_allocate_spi(struct spi_device * spi,u8 acknowledge_mode,unsigned int delay,struct nci_dev * ndev)124fa544fffSEric Lapuyade struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
125fa544fffSEric Lapuyade u8 acknowledge_mode, unsigned int delay,
126fa544fffSEric Lapuyade struct nci_dev *ndev)
1278a00a61bSFrederic Danis {
128fa544fffSEric Lapuyade struct nci_spi *nspi;
1298a00a61bSFrederic Danis
130fa544fffSEric Lapuyade nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
131fa544fffSEric Lapuyade if (!nspi)
1328a00a61bSFrederic Danis return NULL;
1338a00a61bSFrederic Danis
134fa544fffSEric Lapuyade nspi->acknowledge_mode = acknowledge_mode;
135fa544fffSEric Lapuyade nspi->xfer_udelay = delay;
1362bd83245SVincent Cuissard /* Use controller max SPI speed by default */
1372bd83245SVincent Cuissard nspi->xfer_speed_hz = 0;
138645d5087SEric Lapuyade nspi->spi = spi;
139fa544fffSEric Lapuyade nspi->ndev = ndev;
1409bec44bfSAxel Lin init_completion(&nspi->req_completion);
1418a00a61bSFrederic Danis
142fa544fffSEric Lapuyade return nspi;
1438a00a61bSFrederic Danis }
144fa544fffSEric Lapuyade EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
1458a00a61bSFrederic Danis
send_acknowledge(struct nci_spi * nspi,u8 acknowledge)146fa544fffSEric Lapuyade static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
147391d8a2dSFrederic Danis {
148391d8a2dSFrederic Danis struct sk_buff *skb;
149391d8a2dSFrederic Danis unsigned char *hdr;
150391d8a2dSFrederic Danis u16 crc;
151391d8a2dSFrederic Danis int ret;
152391d8a2dSFrederic Danis
153fa544fffSEric Lapuyade skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
154*7937609cSKrzysztof Kozlowski if (!skb)
155*7937609cSKrzysztof Kozlowski return -ENOMEM;
156391d8a2dSFrederic Danis
157391d8a2dSFrederic Danis /* add the NCI SPI header to the start of the buffer */
158391d8a2dSFrederic Danis hdr = skb_push(skb, NCI_SPI_HDR_LEN);
159391d8a2dSFrederic Danis hdr[0] = NCI_SPI_DIRECT_WRITE;
160391d8a2dSFrederic Danis hdr[1] = NCI_SPI_CRC_ENABLED;
161391d8a2dSFrederic Danis hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
162391d8a2dSFrederic Danis hdr[3] = 0;
163391d8a2dSFrederic Danis
164391d8a2dSFrederic Danis crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
165634fef61SJohannes Berg skb_put_u8(skb, crc >> 8);
166634fef61SJohannes Berg skb_put_u8(skb, crc & 0xFF);
167391d8a2dSFrederic Danis
1682bed2785SEric Lapuyade ret = __nci_spi_send(nspi, skb, 0);
169391d8a2dSFrederic Danis
170391d8a2dSFrederic Danis kfree_skb(skb);
171391d8a2dSFrederic Danis
172391d8a2dSFrederic Danis return ret;
173391d8a2dSFrederic Danis }
174391d8a2dSFrederic Danis
__nci_spi_read(struct nci_spi * nspi)17522d4aae5SEric Lapuyade static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
176391d8a2dSFrederic Danis {
177391d8a2dSFrederic Danis struct sk_buff *skb;
178391d8a2dSFrederic Danis struct spi_message m;
179391d8a2dSFrederic Danis unsigned char req[2], resp_hdr[2];
180391d8a2dSFrederic Danis struct spi_transfer tx, rx;
181391d8a2dSFrederic Danis unsigned short rx_len = 0;
182391d8a2dSFrederic Danis int ret;
183391d8a2dSFrederic Danis
184391d8a2dSFrederic Danis spi_message_init(&m);
185a4ada6caSEric Lapuyade
186a4ada6caSEric Lapuyade memset(&tx, 0, sizeof(struct spi_transfer));
187391d8a2dSFrederic Danis req[0] = NCI_SPI_DIRECT_READ;
188fa544fffSEric Lapuyade req[1] = nspi->acknowledge_mode;
189391d8a2dSFrederic Danis tx.tx_buf = req;
190391d8a2dSFrederic Danis tx.len = 2;
191391d8a2dSFrederic Danis tx.cs_change = 0;
1922bd83245SVincent Cuissard tx.speed_hz = nspi->xfer_speed_hz;
193391d8a2dSFrederic Danis spi_message_add_tail(&tx, &m);
194a4ada6caSEric Lapuyade
195a4ada6caSEric Lapuyade memset(&rx, 0, sizeof(struct spi_transfer));
196391d8a2dSFrederic Danis rx.rx_buf = resp_hdr;
197391d8a2dSFrederic Danis rx.len = 2;
198391d8a2dSFrederic Danis rx.cs_change = 1;
1992bd83245SVincent Cuissard rx.speed_hz = nspi->xfer_speed_hz;
200391d8a2dSFrederic Danis spi_message_add_tail(&rx, &m);
201a4ada6caSEric Lapuyade
202fa544fffSEric Lapuyade ret = spi_sync(nspi->spi, &m);
203391d8a2dSFrederic Danis if (ret)
204391d8a2dSFrederic Danis return NULL;
205391d8a2dSFrederic Danis
206fa544fffSEric Lapuyade if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
207391d8a2dSFrederic Danis rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
208391d8a2dSFrederic Danis resp_hdr[1] + NCI_SPI_CRC_LEN;
209391d8a2dSFrederic Danis else
210391d8a2dSFrederic Danis rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
211391d8a2dSFrederic Danis
212fa544fffSEric Lapuyade skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
213391d8a2dSFrederic Danis if (!skb)
214391d8a2dSFrederic Danis return NULL;
215391d8a2dSFrederic Danis
216391d8a2dSFrederic Danis spi_message_init(&m);
217a4ada6caSEric Lapuyade
218a4ada6caSEric Lapuyade memset(&rx, 0, sizeof(struct spi_transfer));
219391d8a2dSFrederic Danis rx.rx_buf = skb_put(skb, rx_len);
220391d8a2dSFrederic Danis rx.len = rx_len;
221391d8a2dSFrederic Danis rx.cs_change = 0;
222099ffd7eSAlexandru Ardelean rx.delay.value = nspi->xfer_udelay;
223099ffd7eSAlexandru Ardelean rx.delay.unit = SPI_DELAY_UNIT_USECS;
2242bd83245SVincent Cuissard rx.speed_hz = nspi->xfer_speed_hz;
225391d8a2dSFrederic Danis spi_message_add_tail(&rx, &m);
226a4ada6caSEric Lapuyade
227fa544fffSEric Lapuyade ret = spi_sync(nspi->spi, &m);
228391d8a2dSFrederic Danis if (ret)
229391d8a2dSFrederic Danis goto receive_error;
230391d8a2dSFrederic Danis
231fa544fffSEric Lapuyade if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
232d58ff351SJohannes Berg *(u8 *)skb_push(skb, 1) = resp_hdr[1];
233d58ff351SJohannes Berg *(u8 *)skb_push(skb, 1) = resp_hdr[0];
234391d8a2dSFrederic Danis }
235391d8a2dSFrederic Danis
236391d8a2dSFrederic Danis return skb;
237391d8a2dSFrederic Danis
238391d8a2dSFrederic Danis receive_error:
239391d8a2dSFrederic Danis kfree_skb(skb);
240391d8a2dSFrederic Danis
241391d8a2dSFrederic Danis return NULL;
242391d8a2dSFrederic Danis }
243391d8a2dSFrederic Danis
nci_spi_check_crc(struct sk_buff * skb)244391d8a2dSFrederic Danis static int nci_spi_check_crc(struct sk_buff *skb)
245391d8a2dSFrederic Danis {
246391d8a2dSFrederic Danis u16 crc_data = (skb->data[skb->len - 2] << 8) |
247391d8a2dSFrederic Danis skb->data[skb->len - 1];
248391d8a2dSFrederic Danis int ret;
249391d8a2dSFrederic Danis
250391d8a2dSFrederic Danis ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
251391d8a2dSFrederic Danis == crc_data);
252391d8a2dSFrederic Danis
253391d8a2dSFrederic Danis skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
254391d8a2dSFrederic Danis
255391d8a2dSFrederic Danis return ret;
256391d8a2dSFrederic Danis }
257391d8a2dSFrederic Danis
nci_spi_get_ack(struct sk_buff * skb)258391d8a2dSFrederic Danis static u8 nci_spi_get_ack(struct sk_buff *skb)
259391d8a2dSFrederic Danis {
260391d8a2dSFrederic Danis u8 ret;
261391d8a2dSFrederic Danis
262391d8a2dSFrederic Danis ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
263391d8a2dSFrederic Danis
264391d8a2dSFrederic Danis /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
265391d8a2dSFrederic Danis skb_pull(skb, 2);
266391d8a2dSFrederic Danis
267391d8a2dSFrederic Danis return ret;
268391d8a2dSFrederic Danis }
269391d8a2dSFrederic Danis
270391d8a2dSFrederic Danis /**
27122d4aae5SEric Lapuyade * nci_spi_read - read frame from NCI SPI drivers
272391d8a2dSFrederic Danis *
273fa544fffSEric Lapuyade * @nspi: The nci spi
274391d8a2dSFrederic Danis * Context: can sleep
275391d8a2dSFrederic Danis *
276391d8a2dSFrederic Danis * This call may only be used from a context that may sleep. The sleep
277391d8a2dSFrederic Danis * is non-interruptible, and has no timeout.
278391d8a2dSFrederic Danis *
27922d4aae5SEric Lapuyade * It returns an allocated skb containing the frame on success, or NULL.
280391d8a2dSFrederic Danis */
nci_spi_read(struct nci_spi * nspi)28122d4aae5SEric Lapuyade struct sk_buff *nci_spi_read(struct nci_spi *nspi)
282391d8a2dSFrederic Danis {
283391d8a2dSFrederic Danis struct sk_buff *skb;
284391d8a2dSFrederic Danis
285391d8a2dSFrederic Danis /* Retrieve frame from SPI */
28622d4aae5SEric Lapuyade skb = __nci_spi_read(nspi);
28722d4aae5SEric Lapuyade if (!skb)
288391d8a2dSFrederic Danis goto done;
289391d8a2dSFrederic Danis
290fa544fffSEric Lapuyade if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
291391d8a2dSFrederic Danis if (!nci_spi_check_crc(skb)) {
292fa544fffSEric Lapuyade send_acknowledge(nspi, ACKNOWLEDGE_NACK);
293391d8a2dSFrederic Danis goto done;
294391d8a2dSFrederic Danis }
295391d8a2dSFrederic Danis
296391d8a2dSFrederic Danis /* In case of acknowledged mode: if ACK or NACK received,
297391d8a2dSFrederic Danis * unblock completion of latest frame sent.
298391d8a2dSFrederic Danis */
299fa544fffSEric Lapuyade nspi->req_result = nci_spi_get_ack(skb);
300fa544fffSEric Lapuyade if (nspi->req_result)
301fa544fffSEric Lapuyade complete(&nspi->req_completion);
302391d8a2dSFrederic Danis }
303391d8a2dSFrederic Danis
304391d8a2dSFrederic Danis /* If there is no payload (ACK/NACK only frame),
305391d8a2dSFrederic Danis * free the socket buffer
306391d8a2dSFrederic Danis */
30722d4aae5SEric Lapuyade if (!skb->len) {
308391d8a2dSFrederic Danis kfree_skb(skb);
30922d4aae5SEric Lapuyade skb = NULL;
310391d8a2dSFrederic Danis goto done;
311391d8a2dSFrederic Danis }
312391d8a2dSFrederic Danis
313fa544fffSEric Lapuyade if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
314fa544fffSEric Lapuyade send_acknowledge(nspi, ACKNOWLEDGE_ACK);
315391d8a2dSFrederic Danis
316391d8a2dSFrederic Danis done:
317391d8a2dSFrederic Danis
31822d4aae5SEric Lapuyade return skb;
319391d8a2dSFrederic Danis }
32022d4aae5SEric Lapuyade EXPORT_SYMBOL_GPL(nci_spi_read);
321fcd9d046SVincent Cuissard
322fcd9d046SVincent Cuissard MODULE_LICENSE("GPL");
323