]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/nfc/nci/spi.c
NFC: NCI: Modify NCI SPI to implement CS/INT handshake per the spec
[karo-tx-linux.git] / net / nfc / nci / spi.c
index 734c6dde7751112185ab4a41feee1b73e4d25abd..f1d426f10cceac10b0befc9f1c98caa6ad5b7793 100644 (file)
 
 #define CRC_INIT               0xFFFF
 
-static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
+static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
+                         int cs_change)
 {
        struct spi_message m;
        struct spi_transfer t;
 
        memset(&t, 0, sizeof(struct spi_transfer));
-       t.tx_buf = skb->data;
-       t.len = skb->len;
-       t.cs_change = 0;
+       /* a NULL skb means we just want the SPI chip select line to raise */
+       if (skb) {
+               t.tx_buf = skb->data;
+               t.len = skb->len;
+       } else {
+               /* still set tx_buf non NULL to make the driver happy */
+               t.tx_buf = &t;
+               t.len = 0;
+       }
+       t.cs_change = cs_change;
        t.delay_usecs = nspi->xfer_udelay;
 
        spi_message_init(&m);
@@ -55,15 +63,15 @@ static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
        return spi_sync(nspi->spi, &m);
 }
 
-int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
+int nci_spi_send(struct nci_spi *nspi,
+                struct completion *write_handshake_completion,
+                struct sk_buff *skb)
 {
        unsigned int payload_len = skb->len;
        unsigned char *hdr;
        int ret;
        long completion_rc;
 
-       nspi->ops->deassert_int(nspi);
-
        /* add the NCI SPI header to the start of the buffer */
        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
        hdr[0] = NCI_SPI_DIRECT_WRITE;
@@ -79,11 +87,21 @@ int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
                *skb_put(skb, 1) = crc & 0xFF;
        }
 
-       ret = __nci_spi_send(nspi, skb);
+       if (write_handshake_completion) {
+               /* Trick SPI driver to raise chip select */
+               ret = __nci_spi_send(nspi, NULL, 1);
+               if (ret)
+                       goto done;
 
-       kfree_skb(skb);
-       nspi->ops->assert_int(nspi);
+               /* wait for NFC chip hardware handshake to complete */
+               if (wait_for_completion_timeout(write_handshake_completion,
+                                               msecs_to_jiffies(1000)) == 0) {
+                       ret = -ETIME;
+                       goto done;
+               }
+       }
 
+       ret = __nci_spi_send(nspi, skb, 0);
        if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
                goto done;
 
@@ -96,6 +114,8 @@ int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
                ret = -EIO;
 
 done:
+       kfree_skb(skb);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(nci_spi_send);
@@ -106,26 +126,20 @@ EXPORT_SYMBOL_GPL(nci_spi_send);
  * nci_spi_allocate_spi - allocate a new nci spi
  *
  * @spi: SPI device
- * @ops: device operations
  * @acknowledge_mode: Acknowledge mode used by the NFC device
  * @delay: delay between transactions in us
  * @ndev: nci dev to send incoming nci frames to
  */
 struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
-                                    struct nci_spi_ops *ops,
                                     u8 acknowledge_mode, unsigned int delay,
                                     struct nci_dev *ndev)
 {
        struct nci_spi *nspi;
 
-       if (!ops->assert_int || !ops->deassert_int)
-               return NULL;
-
        nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
        if (!nspi)
                return NULL;
 
-       nspi->ops = ops;
        nspi->acknowledge_mode = acknowledge_mode;
        nspi->xfer_udelay = delay;
 
@@ -156,7 +170,7 @@ static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
        *skb_put(skb, 1) = crc >> 8;
        *skb_put(skb, 1) = crc & 0xFF;
 
-       ret = __nci_spi_send(nspi, skb);
+       ret = __nci_spi_send(nspi, skb, 0);
 
        kfree_skb(skb);
 
@@ -189,7 +203,6 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
        spi_message_add_tail(&rx, &m);
 
        ret = spi_sync(nspi->spi, &m);
-
        if (ret)
                return NULL;
 
@@ -213,7 +226,6 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
        spi_message_add_tail(&rx, &m);
 
        ret = spi_sync(nspi->spi, &m);
-
        if (ret)
                goto receive_error;
 
@@ -271,8 +283,6 @@ struct sk_buff *nci_spi_read(struct nci_spi *nspi)
 {
        struct sk_buff *skb;
 
-       nspi->ops->deassert_int(nspi);
-
        /* Retrieve frame from SPI */
        skb = __nci_spi_read(nspi);
        if (!skb)
@@ -305,7 +315,6 @@ struct sk_buff *nci_spi_read(struct nci_spi *nspi)
                send_acknowledge(nspi, ACKNOWLEDGE_ACK);
 
 done:
-       nspi->ops->assert_int(nspi);
 
        return skb;
 }