struct mv_cesa_dev *cesa_dev;
 
-static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
+static void mv_cesa_dequeue_req_locked(struct mv_cesa_engine *engine)
 {
        struct crypto_async_request *req, *backlog;
        struct mv_cesa_ctx *ctx;
 
-       spin_lock_bh(&cesa_dev->lock);
-       backlog = crypto_get_backlog(&cesa_dev->queue);
-       req = crypto_dequeue_request(&cesa_dev->queue);
+       backlog = crypto_get_backlog(&engine->queue);
+       req = crypto_dequeue_request(&engine->queue);
        engine->req = req;
-       spin_unlock_bh(&cesa_dev->lock);
 
        if (!req)
                return;
                backlog->complete(backlog, -EINPROGRESS);
 
        ctx = crypto_tfm_ctx(req->tfm);
-       ctx->ops->prepare(req, engine);
        ctx->ops->step(req);
 }
 
                        if (res != -EINPROGRESS) {
                                spin_lock_bh(&engine->lock);
                                engine->req = NULL;
-                               mv_cesa_dequeue_req_unlocked(engine);
+                               mv_cesa_dequeue_req_locked(engine);
                                spin_unlock_bh(&engine->lock);
                                ctx->ops->complete(req);
                                ctx->ops->cleanup(req);
                      struct mv_cesa_req *creq)
 {
        int ret;
-       int i;
+       struct mv_cesa_engine *engine = creq->engine;
 
-       spin_lock_bh(&cesa_dev->lock);
-       ret = crypto_enqueue_request(&cesa_dev->queue, req);
-       spin_unlock_bh(&cesa_dev->lock);
+       spin_lock_bh(&engine->lock);
+       ret = crypto_enqueue_request(&engine->queue, req);
+       spin_unlock_bh(&engine->lock);
 
        if (ret != -EINPROGRESS)
                return ret;
 
-       for (i = 0; i < cesa_dev->caps->nengines; i++) {
-               spin_lock_bh(&cesa_dev->engines[i].lock);
-               if (!cesa_dev->engines[i].req)
-                       mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
-               spin_unlock_bh(&cesa_dev->engines[i].lock);
-       }
+       spin_lock_bh(&engine->lock);
+       if (!engine->req)
+               mv_cesa_dequeue_req_locked(engine);
+       spin_unlock_bh(&engine->lock);
 
        return -EINPROGRESS;
 }
                return -ENOMEM;
 
        spin_lock_init(&cesa->lock);
-       crypto_init_queue(&cesa->queue, CESA_CRYPTO_DEFAULT_MAX_QLEN);
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
        cesa->regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(cesa->regs))
                                                engine);
                if (ret)
                        goto err_cleanup;
+
+               crypto_init_queue(&engine->queue, CESA_CRYPTO_DEFAULT_MAX_QLEN);
+               atomic_set(&engine->load, 0);
        }
 
        cesa_dev = cesa;
 
  * @regs:      device registers
  * @sram_size: usable SRAM size
  * @lock:      device lock
- * @queue:     crypto request queue
  * @engines:   array of engines
  * @dma:       dma pools
  *
        struct device *dev;
        unsigned int sram_size;
        spinlock_t lock;
-       struct crypto_queue queue;
        struct mv_cesa_engine *engines;
        struct mv_cesa_dev_dma *dma;
 };
  * @int_mask:          interrupt mask cache
  * @pool:              memory pool pointing to the memory region reserved in
  *                     SRAM
+ * @queue:             fifo of the pending crypto requests
+ * @load:              engine load counter, useful for load balancing
  *
  * Structure storing CESA engine information.
  */
        size_t max_req_len;
        u32 int_mask;
        struct gen_pool *pool;
+       struct crypto_queue queue;
+       atomic_t load;
 };
 
 /**
  * struct mv_cesa_req_ops - CESA request operations
- * @prepare:   prepare a request to be executed on the specified engine
  * @process:   process a request chunk result (should return 0 if the
  *             operation, -EINPROGRESS if it needs more steps or an error
  *             code)
  *             needed.
  */
 struct mv_cesa_req_ops {
-       void (*prepare)(struct crypto_async_request *req,
-                       struct mv_cesa_engine *engine);
        int (*process)(struct crypto_async_request *req, u32 status);
        void (*step)(struct crypto_async_request *req);
        void (*cleanup)(struct crypto_async_request *req);
 int mv_cesa_queue_req(struct crypto_async_request *req,
                      struct mv_cesa_req *creq);
 
+static inline struct mv_cesa_engine *mv_cesa_select_engine(int weight)
+{
+       int i;
+       u32 min_load = U32_MAX;
+       struct mv_cesa_engine *selected = NULL;
+
+       for (i = 0; i < cesa_dev->caps->nengines; i++) {
+               struct mv_cesa_engine *engine = cesa_dev->engines + i;
+               u32 load = atomic_read(&engine->load);
+               if (load < min_load) {
+                       min_load = load;
+                       selected = engine;
+               }
+       }
+
+       atomic_add(weight, &selected->load);
+
+       return selected;
+}
+
 /*
  * Helper function that indicates whether a crypto request needs to be
  * cleaned up or not after being enqueued using mv_cesa_queue_req().
 
        struct mv_cesa_engine *engine = creq->base.engine;
        unsigned int ivsize;
 
+       atomic_sub(ablkreq->nbytes, &engine->load);
        ivsize = crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq));
 
        if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ) {
 static const struct mv_cesa_req_ops mv_cesa_ablkcipher_req_ops = {
        .step = mv_cesa_ablkcipher_step,
        .process = mv_cesa_ablkcipher_process,
-       .prepare = mv_cesa_ablkcipher_prepare,
        .cleanup = mv_cesa_ablkcipher_req_cleanup,
        .complete = mv_cesa_ablkcipher_complete,
 };
        return ret;
 }
 
-static int mv_cesa_des_op(struct ablkcipher_request *req,
-                         struct mv_cesa_op_ctx *tmpl)
+static int mv_cesa_ablkcipher_queue_req(struct ablkcipher_request *req,
+                                       struct mv_cesa_op_ctx *tmpl)
 {
-       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
-       struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
        int ret;
-
-       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
-                             CESA_SA_DESC_CFG_CRYPTM_MSK);
-
-       memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES_KEY_SIZE);
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_engine *engine;
 
        ret = mv_cesa_ablkcipher_req_init(req, tmpl);
        if (ret)
                return ret;
 
+       engine = mv_cesa_select_engine(req->nbytes);
+       mv_cesa_ablkcipher_prepare(&req->base, engine);
+
        ret = mv_cesa_queue_req(&req->base, &creq->base);
+
        if (mv_cesa_req_needs_cleanup(&req->base, ret))
                mv_cesa_ablkcipher_cleanup(req);
 
        return ret;
 }
 
+static int mv_cesa_des_op(struct ablkcipher_request *req,
+                         struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
+                             CESA_SA_DESC_CFG_CRYPTM_MSK);
+
+       memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES_KEY_SIZE);
+
+       return mv_cesa_ablkcipher_queue_req(req, tmpl);
+}
+
 static int mv_cesa_ecb_des_encrypt(struct ablkcipher_request *req)
 {
        struct mv_cesa_op_ctx tmpl;
 static int mv_cesa_des3_op(struct ablkcipher_request *req,
                           struct mv_cesa_op_ctx *tmpl)
 {
-       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
        struct mv_cesa_des3_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       int ret;
 
        mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_3DES,
                              CESA_SA_DESC_CFG_CRYPTM_MSK);
 
        memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES3_EDE_KEY_SIZE);
 
-       ret = mv_cesa_ablkcipher_req_init(req, tmpl);
-       if (ret)
-               return ret;
-
-       ret = mv_cesa_queue_req(&req->base, &creq->base);
-       if (mv_cesa_req_needs_cleanup(&req->base, ret))
-               mv_cesa_ablkcipher_cleanup(req);
-
-       return ret;
+       return mv_cesa_ablkcipher_queue_req(req, tmpl);
 }
 
 static int mv_cesa_ecb_des3_ede_encrypt(struct ablkcipher_request *req)
 static int mv_cesa_aes_op(struct ablkcipher_request *req,
                          struct mv_cesa_op_ctx *tmpl)
 {
-       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
        struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       int ret, i;
+       int i;
        u32 *key;
        u32 cfg;
 
                              CESA_SA_DESC_CFG_CRYPTM_MSK |
                              CESA_SA_DESC_CFG_AES_LEN_MSK);
 
-       ret = mv_cesa_ablkcipher_req_init(req, tmpl);
-       if (ret)
-               return ret;
-
-       ret = mv_cesa_queue_req(&req->base, &creq->base);
-       if (mv_cesa_req_needs_cleanup(&req->base, ret))
-               mv_cesa_ablkcipher_cleanup(req);
-
-       return ret;
+       return mv_cesa_ablkcipher_queue_req(req, tmpl);
 }
 
 static int mv_cesa_ecb_aes_encrypt(struct ablkcipher_request *req)
 
                                result[i] = cpu_to_be32(creq->state[i]);
                }
        }
+
+       atomic_sub(ahashreq->nbytes, &engine->load);
 }
 
 static void mv_cesa_ahash_prepare(struct crypto_async_request *req,
 static const struct mv_cesa_req_ops mv_cesa_ahash_req_ops = {
        .step = mv_cesa_ahash_step,
        .process = mv_cesa_ahash_process,
-       .prepare = mv_cesa_ahash_prepare,
        .cleanup = mv_cesa_ahash_req_cleanup,
        .complete = mv_cesa_ahash_complete,
 };
        return ret;
 }
 
-static int mv_cesa_ahash_update(struct ahash_request *req)
+static int mv_cesa_ahash_queue_req(struct ahash_request *req)
 {
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_engine *engine;
        bool cached = false;
        int ret;
 
-       creq->len += req->nbytes;
        ret = mv_cesa_ahash_req_init(req, &cached);
        if (ret)
                return ret;
        if (cached)
                return 0;
 
+       engine = mv_cesa_select_engine(req->nbytes);
+       mv_cesa_ahash_prepare(&req->base, engine);
+
        ret = mv_cesa_queue_req(&req->base, &creq->base);
+
        if (mv_cesa_req_needs_cleanup(&req->base, ret))
                mv_cesa_ahash_cleanup(req);
 
        return ret;
 }
 
+static int mv_cesa_ahash_update(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       creq->len += req->nbytes;
+
+       return mv_cesa_ahash_queue_req(req);
+}
+
 static int mv_cesa_ahash_final(struct ahash_request *req)
 {
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
        struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
-       bool cached = false;
-       int ret;
 
        mv_cesa_set_mac_op_total_len(tmpl, creq->len);
        creq->last_req = true;
        req->nbytes = 0;
 
-       ret = mv_cesa_ahash_req_init(req, &cached);
-       if (ret)
-               return ret;
-
-       if (cached)
-               return 0;
-
-       ret = mv_cesa_queue_req(&req->base, &creq->base);
-       if (mv_cesa_req_needs_cleanup(&req->base, ret))
-               mv_cesa_ahash_cleanup(req);
-
-       return ret;
+       return mv_cesa_ahash_queue_req(req);
 }
 
 static int mv_cesa_ahash_finup(struct ahash_request *req)
 {
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
        struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
-       bool cached = false;
-       int ret;
 
        creq->len += req->nbytes;
        mv_cesa_set_mac_op_total_len(tmpl, creq->len);
        creq->last_req = true;
 
-       ret = mv_cesa_ahash_req_init(req, &cached);
-       if (ret)
-               return ret;
-
-       if (cached)
-               return 0;
-
-       ret = mv_cesa_queue_req(&req->base, &creq->base);
-       if (mv_cesa_req_needs_cleanup(&req->base, ret))
-               mv_cesa_ahash_cleanup(req);
-
-       return ret;
+       return mv_cesa_ahash_queue_req(req);
 }
 
 static int mv_cesa_ahash_export(struct ahash_request *req, void *hash,