aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEric Lapuyade <eric.lapuyade@linux.intel.com>2013-09-23 11:56:43 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-09-25 08:59:56 -0400
commit2bed27851767d93b5d2823eee110857f350a9fbe (patch)
treefb54db07a57bb7d8069eeb596b0afa9f21e2130d /net
parent22d4aae5897fb8355130b8f7d9a3af153eac9714 (diff)
NFC: NCI: Modify NCI SPI to implement CS/INT handshake per the spec
The NFC Forum NCI specification defines both a hardware and software protocol when using a SPI physical transport to connect an NFC NCI Chipset. The hardware requirement is that, after having raised the chip select line, the SPI driver must wait for an INT line from the NFC chipset to raise before it sends the data. The chip select must be raised first though, because this is the signal that the NFC chipset will detect to wake up and then raise its INT line. If the INT line doesn't raise in a timely fashion, the SPI driver should abort operation. When data is transferred from Device host (DH) to NFC Controller (NFCC), the signaling sequence is the following: Data Transfer from DH to NFCC • 1-Master asserts SPI_CSN • 2-Slave asserts SPI_INT • 3-Master sends NCI-over-SPI protocol header and payload data • 4-Slave deasserts SPI_INT • 5-Master deasserts SPI_CSN When data must be transferred from NFCC to DH, things are a little bit different. Data Transfer from NFCC to DH • 1-Slave asserts SPI_INT -> NFC chipset irq handler called -> process reading from SPI • 2-Master asserts SPI_CSN • 3-Master send 2-octet NCI-over-SPI protocol header • 4-Slave sends 2-octet NCI-over-SPI protocol payload length • 5-Slave sends NCI-over-SPI protocol payload • 6-Master deasserts SPI_CSN In this case, SPI driver should function normally as it does today. Note that the INT line can and will be lowered anytime between beginning of step 3 and end of step 5. A low INT is therefore valid after chip select has been raised. This would be easily implemented in a single driver. Unfortunately, we don't write the SPI driver and I had to imagine some workaround trick to get the SPI and NFC drivers to work in a synchronized fashion. The trick is the following: - send an empty spi message: this will raise the chip select line, and send nothing. We expect the /CS line will stay arisen because we asked for it in the spi_transfer cs_change field - wait for a completion, that will be completed by the NFC driver IRQ handler when it knows we are in the process of sending data (NFC spec says that we use SPI in a half duplex mode, so we are either sending or receiving). - when completed, proceed with the normal data send. This has been tested and verified to work very consistently on a Nexus 10 (spi-s3c64xx driver). It may not work the same with other spi drivers. The previously defined nci_spi_ops{} whose intended purpose were to address this problem are not used anymore and therefore totally removed. The nci_spi_send() takes a new optional write_handshake_completion completion pointer. If non NULL, the nci spi layer will run the above trick when sending data to the NFC Chip. If NULL, the data is sent normally all at once and it is then the NFC driver responsibility to know what it's doing. Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/nfc/nci/spi.c53
1 files changed, 31 insertions, 22 deletions
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
index 734c6dde7751..f1d426f10cce 100644
--- a/net/nfc/nci/spi.c
+++ b/net/nfc/nci/spi.c
@@ -38,15 +38,23 @@
38 38
39#define CRC_INIT 0xFFFF 39#define CRC_INIT 0xFFFF
40 40
41static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb) 41static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
42 int cs_change)
42{ 43{
43 struct spi_message m; 44 struct spi_message m;
44 struct spi_transfer t; 45 struct spi_transfer t;
45 46
46 memset(&t, 0, sizeof(struct spi_transfer)); 47 memset(&t, 0, sizeof(struct spi_transfer));
47 t.tx_buf = skb->data; 48 /* a NULL skb means we just want the SPI chip select line to raise */
48 t.len = skb->len; 49 if (skb) {
49 t.cs_change = 0; 50 t.tx_buf = skb->data;
51 t.len = skb->len;
52 } else {
53 /* still set tx_buf non NULL to make the driver happy */
54 t.tx_buf = &t;
55 t.len = 0;
56 }
57 t.cs_change = cs_change;
50 t.delay_usecs = nspi->xfer_udelay; 58 t.delay_usecs = nspi->xfer_udelay;
51 59
52 spi_message_init(&m); 60 spi_message_init(&m);
@@ -55,15 +63,15 @@ static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
55 return spi_sync(nspi->spi, &m); 63 return spi_sync(nspi->spi, &m);
56} 64}
57 65
58int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb) 66int nci_spi_send(struct nci_spi *nspi,
67 struct completion *write_handshake_completion,
68 struct sk_buff *skb)
59{ 69{
60 unsigned int payload_len = skb->len; 70 unsigned int payload_len = skb->len;
61 unsigned char *hdr; 71 unsigned char *hdr;
62 int ret; 72 int ret;
63 long completion_rc; 73 long completion_rc;
64 74
65 nspi->ops->deassert_int(nspi);
66
67 /* add the NCI SPI header to the start of the buffer */ 75 /* add the NCI SPI header to the start of the buffer */
68 hdr = skb_push(skb, NCI_SPI_HDR_LEN); 76 hdr = skb_push(skb, NCI_SPI_HDR_LEN);
69 hdr[0] = NCI_SPI_DIRECT_WRITE; 77 hdr[0] = NCI_SPI_DIRECT_WRITE;
@@ -79,11 +87,21 @@ int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
79 *skb_put(skb, 1) = crc & 0xFF; 87 *skb_put(skb, 1) = crc & 0xFF;
80 } 88 }
81 89
82 ret = __nci_spi_send(nspi, skb); 90 if (write_handshake_completion) {
91 /* Trick SPI driver to raise chip select */
92 ret = __nci_spi_send(nspi, NULL, 1);
93 if (ret)
94 goto done;
83 95
84 kfree_skb(skb); 96 /* wait for NFC chip hardware handshake to complete */
85 nspi->ops->assert_int(nspi); 97 if (wait_for_completion_timeout(write_handshake_completion,
98 msecs_to_jiffies(1000)) == 0) {
99 ret = -ETIME;
100 goto done;
101 }
102 }
86 103
104 ret = __nci_spi_send(nspi, skb, 0);
87 if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED) 105 if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
88 goto done; 106 goto done;
89 107
@@ -96,6 +114,8 @@ int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
96 ret = -EIO; 114 ret = -EIO;
97 115
98done: 116done:
117 kfree_skb(skb);
118
99 return ret; 119 return ret;
100} 120}
101EXPORT_SYMBOL_GPL(nci_spi_send); 121EXPORT_SYMBOL_GPL(nci_spi_send);
@@ -106,26 +126,20 @@ EXPORT_SYMBOL_GPL(nci_spi_send);
106 * nci_spi_allocate_spi - allocate a new nci spi 126 * nci_spi_allocate_spi - allocate a new nci spi
107 * 127 *
108 * @spi: SPI device 128 * @spi: SPI device
109 * @ops: device operations
110 * @acknowledge_mode: Acknowledge mode used by the NFC device 129 * @acknowledge_mode: Acknowledge mode used by the NFC device
111 * @delay: delay between transactions in us 130 * @delay: delay between transactions in us
112 * @ndev: nci dev to send incoming nci frames to 131 * @ndev: nci dev to send incoming nci frames to
113 */ 132 */
114struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, 133struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
115 struct nci_spi_ops *ops,
116 u8 acknowledge_mode, unsigned int delay, 134 u8 acknowledge_mode, unsigned int delay,
117 struct nci_dev *ndev) 135 struct nci_dev *ndev)
118{ 136{
119 struct nci_spi *nspi; 137 struct nci_spi *nspi;
120 138
121 if (!ops->assert_int || !ops->deassert_int)
122 return NULL;
123
124 nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL); 139 nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
125 if (!nspi) 140 if (!nspi)
126 return NULL; 141 return NULL;
127 142
128 nspi->ops = ops;
129 nspi->acknowledge_mode = acknowledge_mode; 143 nspi->acknowledge_mode = acknowledge_mode;
130 nspi->xfer_udelay = delay; 144 nspi->xfer_udelay = delay;
131 145
@@ -156,7 +170,7 @@ static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
156 *skb_put(skb, 1) = crc >> 8; 170 *skb_put(skb, 1) = crc >> 8;
157 *skb_put(skb, 1) = crc & 0xFF; 171 *skb_put(skb, 1) = crc & 0xFF;
158 172
159 ret = __nci_spi_send(nspi, skb); 173 ret = __nci_spi_send(nspi, skb, 0);
160 174
161 kfree_skb(skb); 175 kfree_skb(skb);
162 176
@@ -189,7 +203,6 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
189 spi_message_add_tail(&rx, &m); 203 spi_message_add_tail(&rx, &m);
190 204
191 ret = spi_sync(nspi->spi, &m); 205 ret = spi_sync(nspi->spi, &m);
192
193 if (ret) 206 if (ret)
194 return NULL; 207 return NULL;
195 208
@@ -213,7 +226,6 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
213 spi_message_add_tail(&rx, &m); 226 spi_message_add_tail(&rx, &m);
214 227
215 ret = spi_sync(nspi->spi, &m); 228 ret = spi_sync(nspi->spi, &m);
216
217 if (ret) 229 if (ret)
218 goto receive_error; 230 goto receive_error;
219 231
@@ -271,8 +283,6 @@ struct sk_buff *nci_spi_read(struct nci_spi *nspi)
271{ 283{
272 struct sk_buff *skb; 284 struct sk_buff *skb;
273 285
274 nspi->ops->deassert_int(nspi);
275
276 /* Retrieve frame from SPI */ 286 /* Retrieve frame from SPI */
277 skb = __nci_spi_read(nspi); 287 skb = __nci_spi_read(nspi);
278 if (!skb) 288 if (!skb)
@@ -305,7 +315,6 @@ struct sk_buff *nci_spi_read(struct nci_spi *nspi)
305 send_acknowledge(nspi, ACKNOWLEDGE_ACK); 315 send_acknowledge(nspi, ACKNOWLEDGE_ACK);
306 316
307done: 317done:
308 nspi->ops->assert_int(nspi);
309 318
310 return skb; 319 return skb;
311} 320}