crypto: atmel-sha - add atmel_sha_cpu_start()
authorCyrille Pitchen <cyrille.pitchen@atmel.com>
Thu, 26 Jan 2017 16:07:52 +0000 (17:07 +0100)
committerHerbert Xu <herbert@gondor.apana.org.au>
Fri, 3 Feb 2017 10:16:13 +0000 (18:16 +0800)
This patch adds a simple function to perform data transfer with PIO, hence
handled by the CPU.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/crypto/atmel-sha.c

index be0d72cf4352f3a80510e79a587b27d4aca04f41..58d9ca8ac0f2e0d7340d248bf27ef2c4b10a44ca 100644 (file)
@@ -64,6 +64,8 @@
 #define SHA_FLAGS_ERROR                BIT(23)
 #define SHA_FLAGS_PAD          BIT(24)
 #define SHA_FLAGS_RESTORE      BIT(25)
+#define SHA_FLAGS_IDATAR0      BIT(26)
+#define SHA_FLAGS_WAIT_DATARDY BIT(27)
 
 #define SHA_OP_UPDATE  1
 #define SHA_OP_FINAL   2
@@ -141,6 +143,7 @@ struct atmel_sha_dev {
        struct ahash_request    *req;
        bool                    is_async;
        atmel_sha_fn_t          resume;
+       atmel_sha_fn_t          cpu_transfer_complete;
 
        struct atmel_sha_dma    dma_lch_in;
 
@@ -1317,6 +1320,93 @@ static irqreturn_t atmel_sha_irq(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
+
+/* CPU transfer functions */
+
+static int atmel_sha_cpu_transfer(struct atmel_sha_dev *dd)
+{
+       struct ahash_request *req = dd->req;
+       struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+       const u32 *words = (const u32 *)ctx->buffer;
+       size_t i, num_words;
+       u32 isr, din, din_inc;
+
+       din_inc = (ctx->flags & SHA_FLAGS_IDATAR0) ? 0 : 1;
+       for (;;) {
+               /* Write data into the Input Data Registers. */
+               num_words = DIV_ROUND_UP(ctx->bufcnt, sizeof(u32));
+               for (i = 0, din = 0; i < num_words; ++i, din += din_inc)
+                       atmel_sha_write(dd, SHA_REG_DIN(din), words[i]);
+
+               ctx->offset += ctx->bufcnt;
+               ctx->total -= ctx->bufcnt;
+
+               if (!ctx->total)
+                       break;
+
+               /*
+                * Prepare next block:
+                * Fill ctx->buffer now with the next data to be written into
+                * IDATARx: it gives time for the SHA hardware to process
+                * the current data so the SHA_INT_DATARDY flag might be set
+                * in SHA_ISR when polling this register at the beginning of
+                * the next loop.
+                */
+               ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total);
+               scatterwalk_map_and_copy(ctx->buffer, ctx->sg,
+                                        ctx->offset, ctx->bufcnt, 0);
+
+               /* Wait for hardware to be ready again. */
+               isr = atmel_sha_read(dd, SHA_ISR);
+               if (!(isr & SHA_INT_DATARDY)) {
+                       /* Not ready yet. */
+                       dd->resume = atmel_sha_cpu_transfer;
+                       atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY);
+                       return -EINPROGRESS;
+               }
+       }
+
+       if (unlikely(!(ctx->flags & SHA_FLAGS_WAIT_DATARDY)))
+               return dd->cpu_transfer_complete(dd);
+
+       return atmel_sha_wait_for_data_ready(dd, dd->cpu_transfer_complete);
+}
+
+static int atmel_sha_cpu_start(struct atmel_sha_dev *dd,
+                              struct scatterlist *sg,
+                              unsigned int len,
+                              bool idatar0_only,
+                              bool wait_data_ready,
+                              atmel_sha_fn_t resume)
+{
+       struct ahash_request *req = dd->req;
+       struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+
+       if (!len)
+               return resume(dd);
+
+       ctx->flags &= ~(SHA_FLAGS_IDATAR0 | SHA_FLAGS_WAIT_DATARDY);
+
+       if (idatar0_only)
+               ctx->flags |= SHA_FLAGS_IDATAR0;
+
+       if (wait_data_ready)
+               ctx->flags |= SHA_FLAGS_WAIT_DATARDY;
+
+       ctx->sg = sg;
+       ctx->total = len;
+       ctx->offset = 0;
+
+       /* Prepare the first block to be written. */
+       ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total);
+       scatterwalk_map_and_copy(ctx->buffer, ctx->sg,
+                                ctx->offset, ctx->bufcnt, 0);
+
+       dd->cpu_transfer_complete = resume;
+       return atmel_sha_cpu_transfer(dd);
+}
+
+
 static void atmel_sha_unregister_algs(struct atmel_sha_dev *dd)
 {
        int i;