Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[pandora-kernel.git] / drivers / crypto / mv_cesa.c
index a4faa89..21c1a87 100644 (file)
@@ -24,6 +24,7 @@
 
 #define MV_CESA        "MV-CESA:"
 #define MAX_HW_HASH_SIZE       0xFFFF
+#define MV_CESA_EXPIRE         500 /* msec */
 
 /*
  * STM:
@@ -87,6 +88,7 @@ struct crypto_priv {
        spinlock_t lock;
        struct crypto_queue queue;
        enum engine_status eng_st;
+       struct timer_list completion_timer;
        struct crypto_async_request *cur_req;
        struct req_progress p;
        int max_req_size;
@@ -138,6 +140,29 @@ struct mv_req_hash_ctx {
        int count_add;
 };
 
+static void mv_completion_timer_callback(unsigned long unused)
+{
+       int active = readl(cpg->reg + SEC_ACCEL_CMD) & SEC_CMD_EN_SEC_ACCL0;
+
+       printk(KERN_ERR MV_CESA
+              "completion timer expired (CESA %sactive), cleaning up.\n",
+              active ? "" : "in");
+
+       del_timer(&cpg->completion_timer);
+       writel(SEC_CMD_DISABLE_SEC, cpg->reg + SEC_ACCEL_CMD);
+       while(readl(cpg->reg + SEC_ACCEL_CMD) & SEC_CMD_DISABLE_SEC)
+               printk(KERN_INFO MV_CESA "%s: waiting for engine finishing\n", __func__);
+       cpg->eng_st = ENGINE_W_DEQUEUE;
+       wake_up_process(cpg->queue_th);
+}
+
+static void mv_setup_timer(void)
+{
+       setup_timer(&cpg->completion_timer, &mv_completion_timer_callback, 0);
+       mod_timer(&cpg->completion_timer,
+                       jiffies + msecs_to_jiffies(MV_CESA_EXPIRE));
+}
+
 static void compute_aes_dec_key(struct mv_ctx *ctx)
 {
        struct crypto_aes_ctx gen_aes_key;
@@ -273,12 +298,8 @@ static void mv_process_current_q(int first_block)
                        sizeof(struct sec_accel_config));
 
        /* GO */
+       mv_setup_timer();
        writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
-
-       /*
-        * XXX: add timer if the interrupt does not occur for some mystery
-        * reason
-        */
 }
 
 static void mv_crypto_algo_completion(void)
@@ -357,12 +378,8 @@ static void mv_process_hash_current(int first_block)
        memcpy(cpg->sram + SRAM_CONFIG, &op, sizeof(struct sec_accel_config));
 
        /* GO */
+       mv_setup_timer();
        writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
-
-       /*
-       * XXX: add timer if the interrupt does not occur for some mystery
-       * reason
-       */
 }
 
 static inline int mv_hash_import_sha1_ctx(const struct mv_req_hash_ctx *ctx,
@@ -406,6 +423,15 @@ out:
        return rc;
 }
 
+static void mv_save_digest_state(struct mv_req_hash_ctx *ctx)
+{
+       ctx->state[0] = readl(cpg->reg + DIGEST_INITIAL_VAL_A);
+       ctx->state[1] = readl(cpg->reg + DIGEST_INITIAL_VAL_B);
+       ctx->state[2] = readl(cpg->reg + DIGEST_INITIAL_VAL_C);
+       ctx->state[3] = readl(cpg->reg + DIGEST_INITIAL_VAL_D);
+       ctx->state[4] = readl(cpg->reg + DIGEST_INITIAL_VAL_E);
+}
+
 static void mv_hash_algo_completion(void)
 {
        struct ahash_request *req = ahash_request_cast(cpg->cur_req);
@@ -420,14 +446,12 @@ static void mv_hash_algo_completion(void)
                        memcpy(req->result, cpg->sram + SRAM_DIGEST_BUF,
                               crypto_ahash_digestsize(crypto_ahash_reqtfm
                                                       (req)));
-               } else
+               } else {
+                       mv_save_digest_state(ctx);
                        mv_hash_final_fallback(req);
+               }
        } else {
-               ctx->state[0] = readl(cpg->reg + DIGEST_INITIAL_VAL_A);
-               ctx->state[1] = readl(cpg->reg + DIGEST_INITIAL_VAL_B);
-               ctx->state[2] = readl(cpg->reg + DIGEST_INITIAL_VAL_C);
-               ctx->state[3] = readl(cpg->reg + DIGEST_INITIAL_VAL_D);
-               ctx->state[4] = readl(cpg->reg + DIGEST_INITIAL_VAL_E);
+               mv_save_digest_state(ctx);
        }
 }
 
@@ -888,6 +912,10 @@ irqreturn_t crypto_int(int irq, void *priv)
        if (!(val & SEC_INT_ACCEL0_DONE))
                return IRQ_NONE;
 
+       if (!del_timer(&cpg->completion_timer)) {
+               printk(KERN_WARNING MV_CESA
+                      "got an interrupt but no pending timer?\n");
+       }
        val &= ~SEC_INT_ACCEL0_DONE;
        writel(val, cpg->reg + FPGA_INT_STATUS);
        writel(val, cpg->reg + SEC_ACCEL_INT_STATUS);
@@ -1061,6 +1089,7 @@ static int mv_probe(struct platform_device *pdev)
        if (!IS_ERR(cp->clk))
                clk_prepare_enable(cp->clk);
 
+       writel(0, cpg->reg + SEC_ACCEL_INT_STATUS);
        writel(SEC_INT_ACCEL0_DONE, cpg->reg + SEC_ACCEL_INT_MASK);
        writel(SEC_CFG_STOP_DIG_ERR, cpg->reg + SEC_ACCEL_CFG);
        writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0);