#define FLAGS_INIT             0x0100
 #define FLAGS_CPU              0x0200
 #define FLAGS_HMAC             0x0400
+#define FLAGS_ERROR            0x0800
 
 /* 3rd byte */
 #define FLAGS_BUSY             16
        int                     irq;
        struct clk              *iclk;
        spinlock_t              lock;
+       int                     err;
        int                     dma;
        int                     dma_lch;
        struct tasklet_struct   done_task;
                                SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
 
                        if (omap_sham_wait(dd, SHA_REG_SYSSTATUS,
-                                               SHA_REG_SYSSTATUS_RESETDONE))
+                                               SHA_REG_SYSSTATUS_RESETDONE)) {
+                               clk_disable(dd->iclk);
                                return -ETIMEDOUT;
-
+                       }
                        dd->flags |= FLAGS_INIT;
+                       dd->err = 0;
                }
        } else {
                omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt);
        if (err)
                return err;
 
+       /* should be non-zero before next lines to disable clocks later */
+       ctx->digcnt += length;
+
        if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY))
                return -ETIMEDOUT;
 
-       ctx->digcnt += length;
-
        if (final)
                ctx->flags |= FLAGS_FINAL; /* catch last interrupt */
 
 
        dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n",
                                                ctx->digcnt, length, final);
-
        /* flush cache entries related to our page */
        if (dma_addr == ctx->dma_addr)
                dma_sync_single_for_device(dd->dev, dma_addr, length,
 {
        struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
        unsigned int length;
+       int err;
 
        ctx->flags |= FLAGS_FAST;
 
 
        ctx->total -= length;
 
-       return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, 1);
+       err = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, 1);
+       if (err != -EINPROGRESS)
+               dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
+
+       return err;
 }
 
 static int omap_sham_update_cpu(struct omap_sham_dev *dd)
 
        ctx->bufcnt = 0;
 
-       if (err != -EINPROGRESS)
-               omap_sham_cleanup(req);
-
        dev_dbg(dd->dev, "final_req: err: %d\n", err);
 
        return err;
                omap_sham_copy_hash(ctx->dd->req, 1);
                if (ctx->flags & FLAGS_HMAC)
                        err = omap_sham_finish_req_hmac(req);
+       } else {
+               ctx->flags |= FLAGS_ERROR;
        }
 
-       if (ctx->flags & FLAGS_FINAL)
+       if ((ctx->flags & FLAGS_FINAL) || err)
                omap_sham_cleanup(req);
 
        clear_bit(FLAGS_BUSY, &ctx->dd->flags);
 
        ctx->flags |= FLAGS_FINUP;
 
-       /* OMAP HW accel works only with buffers >= 9 */
-       /* HMAC is always >= 9 because of ipad */
-       if ((ctx->digcnt + ctx->bufcnt) < 9)
-               err = omap_sham_final_shash(req);
-       else if (ctx->bufcnt)
-               return omap_sham_enqueue(req, OP_FINAL);
+       if (!(ctx->flags & FLAGS_ERROR)) {
+               /* OMAP HW accel works only with buffers >= 9 */
+               /* HMAC is always >= 9 because of ipad */
+               if ((ctx->digcnt + ctx->bufcnt) < 9)
+                       err = omap_sham_final_shash(req);
+               else if (ctx->bufcnt)
+                       return omap_sham_enqueue(req, OP_FINAL);
+       }
 
        omap_sham_cleanup(req);
 
        struct omap_sham_ctx *tctx = crypto_tfm_ctx(tfm);
        const char *alg_name = crypto_tfm_alg_name(tfm);
 
+       pr_info("enter\n");
+
        /* Allocate a fallback and abort if it failed. */
        tctx->fallback = crypto_alloc_shash(alg_name, 0,
                                            CRYPTO_ALG_NEED_FALLBACK);
        struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
        struct ahash_request *req = dd->req;
        struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
-       int ready = 1;
+       int ready = 0, err = 0;
 
        if (ctx->flags & FLAGS_OUTPUT_READY) {
                ctx->flags &= ~FLAGS_OUTPUT_READY;
        if (dd->flags & FLAGS_DMA_ACTIVE) {
                dd->flags &= ~FLAGS_DMA_ACTIVE;
                omap_sham_update_dma_stop(dd);
-               omap_sham_update_dma_slow(dd);
+               if (!dd->err)
+                       err = omap_sham_update_dma_slow(dd);
        }
 
-       if (ready && !(dd->flags & FLAGS_DMA_ACTIVE)) {
-               dev_dbg(dd->dev, "update done\n");
+       err = dd->err ? : err;
+
+       if (err != -EINPROGRESS && (ready || err)) {
+               dev_dbg(dd->dev, "update done: err: %d\n", err);
                /* finish curent request */
-               omap_sham_finish_req(req, 0);
+               omap_sham_finish_req(req, err);
                /* start new request */
                omap_sham_handle_queue(dd);
        }
        omap_sham_read(dd, SHA_REG_CTRL);
 
        ctx->flags |= FLAGS_OUTPUT_READY;
+       dd->err = 0;
        tasklet_schedule(&dd->done_task);
 
        return IRQ_HANDLED;
 {
        struct omap_sham_dev *dd = data;
 
-       if (likely(lch == dd->dma_lch))
-               tasklet_schedule(&dd->done_task);
+       if (ch_status != OMAP_DMA_BLOCK_IRQ) {
+               pr_err("omap-sham DMA error status: 0x%hx\n", ch_status);
+               dd->err = -EIO;
+               dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
+       }
+
+       tasklet_schedule(&dd->done_task);
 }
 
 static int omap_sham_dma_init(struct omap_sham_dev *dd)