mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 10:56:40 +00:00
4ea5d9998a
1) SSYNC instruction is blackfin specific and takes no effect in this driver. 2) DMA descriptor and SG middle buffer are in DMA coherent memory. No need to flush. 3) Turn kzalloc, ioremap and request_irq into managed device APIs respectively. Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
758 lines
19 KiB
C
758 lines
19 KiB
C
/*
|
|
* Cryptographic API.
|
|
*
|
|
* Support Blackfin CRC HW acceleration.
|
|
*
|
|
* Copyright 2012 Analog Devices Inc.
|
|
*
|
|
* Licensed under the GPL-2.
|
|
*/
|
|
|
|
#include <linux/err.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/io.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/unaligned/access_ok.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/cryptohash.h>
|
|
#include <crypto/scatterwalk.h>
|
|
#include <crypto/algapi.h>
|
|
#include <crypto/hash.h>
|
|
#include <crypto/internal/hash.h>
|
|
|
|
#include <asm/blackfin.h>
|
|
#include <asm/bfin_crc.h>
|
|
#include <asm/dma.h>
|
|
#include <asm/portmux.h>
|
|
|
|
#define CRC_CCRYPTO_QUEUE_LENGTH 5
|
|
|
|
#define DRIVER_NAME "bfin-hmac-crc"
|
|
#define CHKSUM_DIGEST_SIZE 4
|
|
#define CHKSUM_BLOCK_SIZE 1
|
|
|
|
#define CRC_MAX_DMA_DESC 100
|
|
|
|
#define CRC_CRYPTO_STATE_UPDATE 1
|
|
#define CRC_CRYPTO_STATE_FINALUPDATE 2
|
|
#define CRC_CRYPTO_STATE_FINISH 3
|
|
|
|
struct bfin_crypto_crc {
|
|
struct list_head list;
|
|
struct device *dev;
|
|
spinlock_t lock;
|
|
|
|
int irq;
|
|
int dma_ch;
|
|
u32 poly;
|
|
volatile struct crc_register *regs;
|
|
|
|
struct ahash_request *req; /* current request in operation */
|
|
struct dma_desc_array *sg_cpu; /* virt addr of sg dma descriptors */
|
|
dma_addr_t sg_dma; /* phy addr of sg dma descriptors */
|
|
u8 *sg_mid_buf;
|
|
|
|
struct tasklet_struct done_task;
|
|
struct crypto_queue queue; /* waiting requests */
|
|
|
|
u8 busy:1; /* crc device in operation flag */
|
|
};
|
|
|
|
static struct bfin_crypto_crc_list {
|
|
struct list_head dev_list;
|
|
spinlock_t lock;
|
|
} crc_list;
|
|
|
|
struct bfin_crypto_crc_reqctx {
|
|
struct bfin_crypto_crc *crc;
|
|
|
|
unsigned int total; /* total request bytes */
|
|
size_t sg_buflen; /* bytes for this update */
|
|
unsigned int sg_nents;
|
|
struct scatterlist *sg; /* sg list head for this update*/
|
|
struct scatterlist bufsl[2]; /* chained sg list */
|
|
|
|
size_t bufnext_len;
|
|
size_t buflast_len;
|
|
u8 bufnext[CHKSUM_DIGEST_SIZE]; /* extra bytes for next udpate */
|
|
u8 buflast[CHKSUM_DIGEST_SIZE]; /* extra bytes from last udpate */
|
|
|
|
u8 flag;
|
|
};
|
|
|
|
struct bfin_crypto_crc_ctx {
|
|
struct bfin_crypto_crc *crc;
|
|
u32 key;
|
|
};
|
|
|
|
|
|
/*
|
|
* derive number of elements in scatterlist
|
|
*/
|
|
static int sg_count(struct scatterlist *sg_list)
|
|
{
|
|
struct scatterlist *sg = sg_list;
|
|
int sg_nents = 1;
|
|
|
|
if (sg_list == NULL)
|
|
return 0;
|
|
|
|
while (!sg_is_last(sg)) {
|
|
sg_nents++;
|
|
sg = scatterwalk_sg_next(sg);
|
|
}
|
|
|
|
return sg_nents;
|
|
}
|
|
|
|
/*
|
|
* get element in scatter list by given index
|
|
*/
|
|
static struct scatterlist *sg_get(struct scatterlist *sg_list, unsigned int nents,
|
|
unsigned int index)
|
|
{
|
|
struct scatterlist *sg = NULL;
|
|
int i;
|
|
|
|
for_each_sg(sg_list, sg, nents, i)
|
|
if (i == index)
|
|
break;
|
|
|
|
return sg;
|
|
}
|
|
|
|
static int bfin_crypto_crc_init_hw(struct bfin_crypto_crc *crc, u32 key)
|
|
{
|
|
crc->regs->datacntrld = 0;
|
|
crc->regs->control = MODE_CALC_CRC << OPMODE_OFFSET;
|
|
crc->regs->curresult = key;
|
|
|
|
/* setup CRC interrupts */
|
|
crc->regs->status = CMPERRI | DCNTEXPI;
|
|
crc->regs->intrenset = CMPERRI | DCNTEXPI;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bfin_crypto_crc_init(struct ahash_request *req)
|
|
{
|
|
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
|
struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
|
|
struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
|
|
struct bfin_crypto_crc *crc;
|
|
|
|
dev_dbg(ctx->crc->dev, "crc_init\n");
|
|
spin_lock_bh(&crc_list.lock);
|
|
list_for_each_entry(crc, &crc_list.dev_list, list) {
|
|
crc_ctx->crc = crc;
|
|
break;
|
|
}
|
|
spin_unlock_bh(&crc_list.lock);
|
|
|
|
if (sg_count(req->src) > CRC_MAX_DMA_DESC) {
|
|
dev_dbg(ctx->crc->dev, "init: requested sg list is too big > %d\n",
|
|
CRC_MAX_DMA_DESC);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx->crc = crc;
|
|
ctx->bufnext_len = 0;
|
|
ctx->buflast_len = 0;
|
|
ctx->sg_buflen = 0;
|
|
ctx->total = 0;
|
|
ctx->flag = 0;
|
|
|
|
/* init crc results */
|
|
put_unaligned_le32(crc_ctx->key, req->result);
|
|
|
|
dev_dbg(ctx->crc->dev, "init: digest size: %d\n",
|
|
crypto_ahash_digestsize(tfm));
|
|
|
|
return bfin_crypto_crc_init_hw(crc, crc_ctx->key);
|
|
}
|
|
|
|
static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc)
|
|
{
|
|
struct scatterlist *sg;
|
|
struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(crc->req);
|
|
int i = 0, j = 0;
|
|
unsigned long dma_config;
|
|
unsigned int dma_count;
|
|
unsigned int dma_addr;
|
|
unsigned int mid_dma_count = 0;
|
|
int dma_mod;
|
|
|
|
dma_map_sg(crc->dev, ctx->sg, ctx->sg_nents, DMA_TO_DEVICE);
|
|
|
|
for_each_sg(ctx->sg, sg, ctx->sg_nents, j) {
|
|
dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 | DMAEN | PSIZE_32;
|
|
dma_addr = sg_dma_address(sg);
|
|
/* deduce extra bytes in last sg */
|
|
if (sg_is_last(sg))
|
|
dma_count = sg_dma_len(sg) - ctx->bufnext_len;
|
|
else
|
|
dma_count = sg_dma_len(sg);
|
|
|
|
if (mid_dma_count) {
|
|
/* Append last middle dma buffer to 4 bytes with first
|
|
bytes in current sg buffer. Move addr of current
|
|
sg and deduce the length of current sg.
|
|
*/
|
|
memcpy(crc->sg_mid_buf +((i-1) << 2) + mid_dma_count,
|
|
(void *)dma_addr,
|
|
CHKSUM_DIGEST_SIZE - mid_dma_count);
|
|
dma_addr += CHKSUM_DIGEST_SIZE - mid_dma_count;
|
|
dma_count -= CHKSUM_DIGEST_SIZE - mid_dma_count;
|
|
}
|
|
/* chop current sg dma len to multiple of 32 bits */
|
|
mid_dma_count = dma_count % 4;
|
|
dma_count &= ~0x3;
|
|
|
|
if (dma_addr % 4 == 0) {
|
|
dma_config |= WDSIZE_32;
|
|
dma_count >>= 2;
|
|
dma_mod = 4;
|
|
} else if (dma_addr % 2 == 0) {
|
|
dma_config |= WDSIZE_16;
|
|
dma_count >>= 1;
|
|
dma_mod = 2;
|
|
} else {
|
|
dma_config |= WDSIZE_8;
|
|
dma_mod = 1;
|
|
}
|
|
|
|
crc->sg_cpu[i].start_addr = dma_addr;
|
|
crc->sg_cpu[i].cfg = dma_config;
|
|
crc->sg_cpu[i].x_count = dma_count;
|
|
crc->sg_cpu[i].x_modify = dma_mod;
|
|
dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
|
|
"cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
|
|
i, crc->sg_cpu[i].start_addr,
|
|
crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
|
|
crc->sg_cpu[i].x_modify);
|
|
i++;
|
|
|
|
if (mid_dma_count) {
|
|
/* copy extra bytes to next middle dma buffer */
|
|
dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 |
|
|
DMAEN | PSIZE_32 | WDSIZE_32;
|
|
memcpy(crc->sg_mid_buf + (i << 2),
|
|
(void *)(dma_addr + (dma_count << 2)),
|
|
mid_dma_count);
|
|
/* setup new dma descriptor for next middle dma */
|
|
crc->sg_cpu[i].start_addr = dma_map_single(crc->dev,
|
|
crc->sg_mid_buf + (i << 2),
|
|
CHKSUM_DIGEST_SIZE, DMA_TO_DEVICE);
|
|
crc->sg_cpu[i].cfg = dma_config;
|
|
crc->sg_cpu[i].x_count = 1;
|
|
crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE;
|
|
dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
|
|
"cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
|
|
i, crc->sg_cpu[i].start_addr,
|
|
crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
|
|
crc->sg_cpu[i].x_modify);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 | DMAEN | PSIZE_32 | WDSIZE_32;
|
|
/* For final update req, append the buffer for next update as well*/
|
|
if (ctx->bufnext_len && (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE ||
|
|
ctx->flag == CRC_CRYPTO_STATE_FINISH)) {
|
|
crc->sg_cpu[i].start_addr = dma_map_single(crc->dev, ctx->bufnext,
|
|
CHKSUM_DIGEST_SIZE, DMA_TO_DEVICE);
|
|
crc->sg_cpu[i].cfg = dma_config;
|
|
crc->sg_cpu[i].x_count = 1;
|
|
crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE;
|
|
dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
|
|
"cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
|
|
i, crc->sg_cpu[i].start_addr,
|
|
crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
|
|
crc->sg_cpu[i].x_modify);
|
|
i++;
|
|
}
|
|
|
|
if (i == 0)
|
|
return;
|
|
|
|
/* Set the last descriptor to stop mode */
|
|
crc->sg_cpu[i - 1].cfg &= ~(DMAFLOW | NDSIZE);
|
|
crc->sg_cpu[i - 1].cfg |= DI_EN;
|
|
set_dma_curr_desc_addr(crc->dma_ch, (unsigned long *)crc->sg_dma);
|
|
set_dma_x_count(crc->dma_ch, 0);
|
|
set_dma_x_modify(crc->dma_ch, 0);
|
|
set_dma_config(crc->dma_ch, dma_config);
|
|
}
|
|
|
|
static int bfin_crypto_crc_handle_queue(struct bfin_crypto_crc *crc,
|
|
struct ahash_request *req)
|
|
{
|
|
struct crypto_async_request *async_req, *backlog;
|
|
struct bfin_crypto_crc_reqctx *ctx;
|
|
struct scatterlist *sg;
|
|
int ret = 0;
|
|
int nsg, i, j;
|
|
unsigned int nextlen;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&crc->lock, flags);
|
|
if (req)
|
|
ret = ahash_enqueue_request(&crc->queue, req);
|
|
if (crc->busy) {
|
|
spin_unlock_irqrestore(&crc->lock, flags);
|
|
return ret;
|
|
}
|
|
backlog = crypto_get_backlog(&crc->queue);
|
|
async_req = crypto_dequeue_request(&crc->queue);
|
|
if (async_req)
|
|
crc->busy = 1;
|
|
spin_unlock_irqrestore(&crc->lock, flags);
|
|
|
|
if (!async_req)
|
|
return ret;
|
|
|
|
if (backlog)
|
|
backlog->complete(backlog, -EINPROGRESS);
|
|
|
|
req = ahash_request_cast(async_req);
|
|
crc->req = req;
|
|
ctx = ahash_request_ctx(req);
|
|
ctx->sg = NULL;
|
|
ctx->sg_buflen = 0;
|
|
ctx->sg_nents = 0;
|
|
|
|
dev_dbg(crc->dev, "handling new req, flag=%u, nbytes: %d\n",
|
|
ctx->flag, req->nbytes);
|
|
|
|
if (ctx->flag == CRC_CRYPTO_STATE_FINISH) {
|
|
if (ctx->bufnext_len == 0) {
|
|
crc->busy = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Pack last crc update buffer to 32bit */
|
|
memset(ctx->bufnext + ctx->bufnext_len, 0,
|
|
CHKSUM_DIGEST_SIZE - ctx->bufnext_len);
|
|
} else {
|
|
/* Pack small data which is less than 32bit to buffer for next update. */
|
|
if (ctx->bufnext_len + req->nbytes < CHKSUM_DIGEST_SIZE) {
|
|
memcpy(ctx->bufnext + ctx->bufnext_len,
|
|
sg_virt(req->src), req->nbytes);
|
|
ctx->bufnext_len += req->nbytes;
|
|
if (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE &&
|
|
ctx->bufnext_len) {
|
|
goto finish_update;
|
|
} else {
|
|
crc->busy = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (ctx->bufnext_len) {
|
|
/* Chain in extra bytes of last update */
|
|
ctx->buflast_len = ctx->bufnext_len;
|
|
memcpy(ctx->buflast, ctx->bufnext, ctx->buflast_len);
|
|
|
|
nsg = ctx->sg_buflen ? 2 : 1;
|
|
sg_init_table(ctx->bufsl, nsg);
|
|
sg_set_buf(ctx->bufsl, ctx->buflast, ctx->buflast_len);
|
|
if (nsg > 1)
|
|
scatterwalk_sg_chain(ctx->bufsl, nsg,
|
|
req->src);
|
|
ctx->sg = ctx->bufsl;
|
|
} else
|
|
ctx->sg = req->src;
|
|
|
|
/* Chop crc buffer size to multiple of 32 bit */
|
|
nsg = ctx->sg_nents = sg_count(ctx->sg);
|
|
ctx->sg_buflen = ctx->buflast_len + req->nbytes;
|
|
ctx->bufnext_len = ctx->sg_buflen % 4;
|
|
ctx->sg_buflen &= ~0x3;
|
|
|
|
if (ctx->bufnext_len) {
|
|
/* copy extra bytes to buffer for next update */
|
|
memset(ctx->bufnext, 0, CHKSUM_DIGEST_SIZE);
|
|
nextlen = ctx->bufnext_len;
|
|
for (i = nsg - 1; i >= 0; i--) {
|
|
sg = sg_get(ctx->sg, nsg, i);
|
|
j = min(nextlen, sg_dma_len(sg));
|
|
memcpy(ctx->bufnext + nextlen - j,
|
|
sg_virt(sg) + sg_dma_len(sg) - j, j);
|
|
if (j == sg_dma_len(sg))
|
|
ctx->sg_nents--;
|
|
nextlen -= j;
|
|
if (nextlen == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
finish_update:
|
|
if (ctx->bufnext_len && (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE ||
|
|
ctx->flag == CRC_CRYPTO_STATE_FINISH))
|
|
ctx->sg_buflen += CHKSUM_DIGEST_SIZE;
|
|
|
|
/* set CRC data count before start DMA */
|
|
crc->regs->datacnt = ctx->sg_buflen >> 2;
|
|
|
|
/* setup and enable CRC DMA */
|
|
bfin_crypto_crc_config_dma(crc);
|
|
|
|
/* finally kick off CRC operation */
|
|
crc->regs->control |= BLKEN;
|
|
|
|
return -EINPROGRESS;
|
|
}
|
|
|
|
static int bfin_crypto_crc_update(struct ahash_request *req)
|
|
{
|
|
struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
|
|
|
|
if (!req->nbytes)
|
|
return 0;
|
|
|
|
dev_dbg(ctx->crc->dev, "crc_update\n");
|
|
ctx->total += req->nbytes;
|
|
ctx->flag = CRC_CRYPTO_STATE_UPDATE;
|
|
|
|
return bfin_crypto_crc_handle_queue(ctx->crc, req);
|
|
}
|
|
|
|
static int bfin_crypto_crc_final(struct ahash_request *req)
|
|
{
|
|
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
|
struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
|
|
struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
|
|
|
|
dev_dbg(ctx->crc->dev, "crc_final\n");
|
|
ctx->flag = CRC_CRYPTO_STATE_FINISH;
|
|
crc_ctx->key = 0;
|
|
|
|
return bfin_crypto_crc_handle_queue(ctx->crc, req);
|
|
}
|
|
|
|
static int bfin_crypto_crc_finup(struct ahash_request *req)
|
|
{
|
|
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
|
struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
|
|
struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
|
|
|
|
dev_dbg(ctx->crc->dev, "crc_finishupdate\n");
|
|
ctx->total += req->nbytes;
|
|
ctx->flag = CRC_CRYPTO_STATE_FINALUPDATE;
|
|
crc_ctx->key = 0;
|
|
|
|
return bfin_crypto_crc_handle_queue(ctx->crc, req);
|
|
}
|
|
|
|
static int bfin_crypto_crc_digest(struct ahash_request *req)
|
|
{
|
|
int ret;
|
|
|
|
ret = bfin_crypto_crc_init(req);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return bfin_crypto_crc_finup(req);
|
|
}
|
|
|
|
static int bfin_crypto_crc_setkey(struct crypto_ahash *tfm, const u8 *key,
|
|
unsigned int keylen)
|
|
{
|
|
struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
|
|
|
|
dev_dbg(crc_ctx->crc->dev, "crc_setkey\n");
|
|
if (keylen != CHKSUM_DIGEST_SIZE) {
|
|
crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
|
return -EINVAL;
|
|
}
|
|
|
|
crc_ctx->key = get_unaligned_le32(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bfin_crypto_crc_cra_init(struct crypto_tfm *tfm)
|
|
{
|
|
struct bfin_crypto_crc_ctx *crc_ctx = crypto_tfm_ctx(tfm);
|
|
|
|
crc_ctx->key = 0;
|
|
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
|
|
sizeof(struct bfin_crypto_crc_reqctx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bfin_crypto_crc_cra_exit(struct crypto_tfm *tfm)
|
|
{
|
|
}
|
|
|
|
static struct ahash_alg algs = {
|
|
.init = bfin_crypto_crc_init,
|
|
.update = bfin_crypto_crc_update,
|
|
.final = bfin_crypto_crc_final,
|
|
.finup = bfin_crypto_crc_finup,
|
|
.digest = bfin_crypto_crc_digest,
|
|
.setkey = bfin_crypto_crc_setkey,
|
|
.halg.digestsize = CHKSUM_DIGEST_SIZE,
|
|
.halg.base = {
|
|
.cra_name = "hmac(crc32)",
|
|
.cra_driver_name = DRIVER_NAME,
|
|
.cra_priority = 100,
|
|
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
|
|
CRYPTO_ALG_ASYNC,
|
|
.cra_blocksize = CHKSUM_BLOCK_SIZE,
|
|
.cra_ctxsize = sizeof(struct bfin_crypto_crc_ctx),
|
|
.cra_alignmask = 3,
|
|
.cra_module = THIS_MODULE,
|
|
.cra_init = bfin_crypto_crc_cra_init,
|
|
.cra_exit = bfin_crypto_crc_cra_exit,
|
|
}
|
|
};
|
|
|
|
static void bfin_crypto_crc_done_task(unsigned long data)
|
|
{
|
|
struct bfin_crypto_crc *crc = (struct bfin_crypto_crc *)data;
|
|
|
|
bfin_crypto_crc_handle_queue(crc, NULL);
|
|
}
|
|
|
|
static irqreturn_t bfin_crypto_crc_handler(int irq, void *dev_id)
|
|
{
|
|
struct bfin_crypto_crc *crc = dev_id;
|
|
|
|
if (crc->regs->status & DCNTEXP) {
|
|
crc->regs->status = DCNTEXP;
|
|
|
|
/* prepare results */
|
|
put_unaligned_le32(crc->regs->result, crc->req->result);
|
|
|
|
crc->regs->control &= ~BLKEN;
|
|
crc->busy = 0;
|
|
|
|
if (crc->req->base.complete)
|
|
crc->req->base.complete(&crc->req->base, 0);
|
|
|
|
tasklet_schedule(&crc->done_task);
|
|
|
|
return IRQ_HANDLED;
|
|
} else
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
/**
|
|
* bfin_crypto_crc_suspend - suspend crc device
|
|
* @pdev: device being suspended
|
|
* @state: requested suspend state
|
|
*/
|
|
static int bfin_crypto_crc_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
struct bfin_crypto_crc *crc = platform_get_drvdata(pdev);
|
|
int i = 100000;
|
|
|
|
while ((crc->regs->control & BLKEN) && --i)
|
|
cpu_relax();
|
|
|
|
if (i == 0)
|
|
return -EBUSY;
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
# define bfin_crypto_crc_suspend NULL
|
|
#endif
|
|
|
|
#define bfin_crypto_crc_resume NULL
|
|
|
|
/**
|
|
* bfin_crypto_crc_probe - Initialize module
|
|
*
|
|
*/
|
|
static int bfin_crypto_crc_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct resource *res;
|
|
struct bfin_crypto_crc *crc;
|
|
unsigned int timeout = 100000;
|
|
int ret;
|
|
|
|
crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL);
|
|
if (!crc) {
|
|
dev_err(&pdev->dev, "fail to malloc bfin_crypto_crc\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
crc->dev = dev;
|
|
|
|
INIT_LIST_HEAD(&crc->list);
|
|
spin_lock_init(&crc->lock);
|
|
tasklet_init(&crc->done_task, bfin_crypto_crc_done_task, (unsigned long)crc);
|
|
crypto_init_queue(&crc->queue, CRC_CCRYPTO_QUEUE_LENGTH);
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (res == NULL) {
|
|
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
crc->regs = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR((void *)crc->regs)) {
|
|
dev_err(&pdev->dev, "Cannot map CRC IO\n");
|
|
return PTR_ERR((void *)crc->regs);
|
|
}
|
|
|
|
crc->irq = platform_get_irq(pdev, 0);
|
|
if (crc->irq < 0) {
|
|
dev_err(&pdev->dev, "No CRC DCNTEXP IRQ specified\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, crc->irq, bfin_crypto_crc_handler,
|
|
IRQF_SHARED, dev_name(dev), crc);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Unable to request blackfin crc irq\n");
|
|
return ret;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
if (res == NULL) {
|
|
dev_err(&pdev->dev, "No CRC DMA channel specified\n");
|
|
return -ENOENT;
|
|
}
|
|
crc->dma_ch = res->start;
|
|
|
|
ret = request_dma(crc->dma_ch, dev_name(dev));
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Unable to attach Blackfin CRC DMA channel\n");
|
|
return ret;
|
|
}
|
|
|
|
crc->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &crc->sg_dma, GFP_KERNEL);
|
|
if (crc->sg_cpu == NULL) {
|
|
ret = -ENOMEM;
|
|
goto out_error_dma;
|
|
}
|
|
/*
|
|
* need at most CRC_MAX_DMA_DESC sg + CRC_MAX_DMA_DESC middle +
|
|
* 1 last + 1 next dma descriptors
|
|
*/
|
|
crc->sg_mid_buf = (u8 *)(crc->sg_cpu + ((CRC_MAX_DMA_DESC + 1) << 1));
|
|
|
|
crc->regs->control = 0;
|
|
crc->regs->poly = crc->poly = (u32)pdev->dev.platform_data;
|
|
|
|
while (!(crc->regs->status & LUTDONE) && (--timeout) > 0)
|
|
cpu_relax();
|
|
|
|
if (timeout == 0)
|
|
dev_info(&pdev->dev, "init crc poly timeout\n");
|
|
|
|
spin_lock(&crc_list.lock);
|
|
list_add(&crc->list, &crc_list.dev_list);
|
|
spin_unlock(&crc_list.lock);
|
|
|
|
platform_set_drvdata(pdev, crc);
|
|
|
|
ret = crypto_register_ahash(&algs);
|
|
if (ret) {
|
|
spin_lock(&crc_list.lock);
|
|
list_del(&crc->list);
|
|
spin_unlock(&crc_list.lock);
|
|
dev_err(&pdev->dev, "Cann't register crypto ahash device\n");
|
|
goto out_error_dma;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "initialized\n");
|
|
|
|
return 0;
|
|
|
|
out_error_dma:
|
|
if (crc->sg_cpu)
|
|
dma_free_coherent(&pdev->dev, PAGE_SIZE, crc->sg_cpu, crc->sg_dma);
|
|
free_dma(crc->dma_ch);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* bfin_crypto_crc_remove - Initialize module
|
|
*
|
|
*/
|
|
static int bfin_crypto_crc_remove(struct platform_device *pdev)
|
|
{
|
|
struct bfin_crypto_crc *crc = platform_get_drvdata(pdev);
|
|
|
|
if (!crc)
|
|
return -ENODEV;
|
|
|
|
spin_lock(&crc_list.lock);
|
|
list_del(&crc->list);
|
|
spin_unlock(&crc_list.lock);
|
|
|
|
crypto_unregister_ahash(&algs);
|
|
tasklet_kill(&crc->done_task);
|
|
free_dma(crc->dma_ch);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver bfin_crypto_crc_driver = {
|
|
.probe = bfin_crypto_crc_probe,
|
|
.remove = bfin_crypto_crc_remove,
|
|
.suspend = bfin_crypto_crc_suspend,
|
|
.resume = bfin_crypto_crc_resume,
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* bfin_crypto_crc_mod_init - Initialize module
|
|
*
|
|
* Checks the module params and registers the platform driver.
|
|
* Real work is in the platform probe function.
|
|
*/
|
|
static int __init bfin_crypto_crc_mod_init(void)
|
|
{
|
|
int ret;
|
|
|
|
pr_info("Blackfin hardware CRC crypto driver\n");
|
|
|
|
INIT_LIST_HEAD(&crc_list.dev_list);
|
|
spin_lock_init(&crc_list.lock);
|
|
|
|
ret = platform_driver_register(&bfin_crypto_crc_driver);
|
|
if (ret) {
|
|
pr_info(KERN_ERR "unable to register driver\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bfin_crypto_crc_mod_exit - Deinitialize module
|
|
*/
|
|
static void __exit bfin_crypto_crc_mod_exit(void)
|
|
{
|
|
platform_driver_unregister(&bfin_crypto_crc_driver);
|
|
}
|
|
|
|
module_init(bfin_crypto_crc_mod_init);
|
|
module_exit(bfin_crypto_crc_mod_exit);
|
|
|
|
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
|
|
MODULE_DESCRIPTION("Blackfin CRC hardware crypto driver");
|
|
MODULE_LICENSE("GPL");
|