Commit bbe628ed authored by Cyrille Pitchen's avatar Cyrille Pitchen Committed by Herbert Xu

crypto: atmel-aes - improve performances of data transfer

This patch totally reworks data transfer.

1 - DMA

The new code now fully supports scatter-gather lists hence reducing the
number of interrupts in some cases. Also buffer alignments are better
managed to avoid useless copies.

2 - CPU

The new code allows to use PIO accesses even when transferring more than
one AES block, so futher patches could tune the DMA threshold
(ATMEL_AES_DMA_THRESHOLD).
Moreover, CPU transfers now have a chance to be processed synchronously,
hence reducing the latency by avoiding context switches when possible
(less interrupts to process, less scheduling of the 'done' task).
Indeed the 'DATA READY' bit is polled only one time in the Interrupt
Status Register before enabling then waiting for the associated interrupt.
In some condition, this single poll is enough as the data have already
been processed by the AES hardware and so are ready.
Signed-off-by: default avatarCyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 2a377828
...@@ -42,11 +42,16 @@ ...@@ -42,11 +42,16 @@
#define ATMEL_AES_PRIORITY 300 #define ATMEL_AES_PRIORITY 300
#define ATMEL_AES_BUFFER_ORDER 2
#define ATMEL_AES_BUFFER_SIZE (PAGE_SIZE << ATMEL_AES_BUFFER_ORDER)
#define CFB8_BLOCK_SIZE 1 #define CFB8_BLOCK_SIZE 1
#define CFB16_BLOCK_SIZE 2 #define CFB16_BLOCK_SIZE 2
#define CFB32_BLOCK_SIZE 4 #define CFB32_BLOCK_SIZE 4
#define CFB64_BLOCK_SIZE 8 #define CFB64_BLOCK_SIZE 8
#define SIZE_IN_WORDS(x) ((x) >> 2)
/* AES flags */ /* AES flags */
/* Reserve bits [18:16] [14:12] [0] for mode (same as for AES_MR) */ /* Reserve bits [18:16] [14:12] [0] for mode (same as for AES_MR) */
#define AES_FLAGS_ENCRYPT AES_MR_CYPHER_ENC #define AES_FLAGS_ENCRYPT AES_MR_CYPHER_ENC
...@@ -66,7 +71,6 @@ ...@@ -66,7 +71,6 @@
#define AES_FLAGS_INIT BIT(2) #define AES_FLAGS_INIT BIT(2)
#define AES_FLAGS_BUSY BIT(3) #define AES_FLAGS_BUSY BIT(3)
#define AES_FLAGS_FAST BIT(5)
#define AES_FLAGS_PERSISTENT (AES_FLAGS_INIT | AES_FLAGS_BUSY) #define AES_FLAGS_PERSISTENT (AES_FLAGS_INIT | AES_FLAGS_BUSY)
...@@ -107,7 +111,10 @@ struct atmel_aes_reqctx { ...@@ -107,7 +111,10 @@ struct atmel_aes_reqctx {
struct atmel_aes_dma { struct atmel_aes_dma {
struct dma_chan *chan; struct dma_chan *chan;
struct dma_slave_config dma_conf; struct scatterlist *sg;
int nents;
unsigned int remainder;
unsigned int sg_len;
}; };
struct atmel_aes_dev { struct atmel_aes_dev {
...@@ -120,6 +127,7 @@ struct atmel_aes_dev { ...@@ -120,6 +127,7 @@ struct atmel_aes_dev {
bool is_async; bool is_async;
atmel_aes_fn_t resume; atmel_aes_fn_t resume;
atmel_aes_fn_t cpu_transfer_complete;
struct device *dev; struct device *dev;
struct clk *iclk; struct clk *iclk;
...@@ -134,27 +142,16 @@ struct atmel_aes_dev { ...@@ -134,27 +142,16 @@ struct atmel_aes_dev {
struct tasklet_struct queue_task; struct tasklet_struct queue_task;
size_t total; size_t total;
size_t datalen;
u32 *data;
struct scatterlist *in_sg; struct atmel_aes_dma src;
unsigned int nb_in_sg; struct atmel_aes_dma dst;
size_t in_offset;
struct scatterlist *out_sg;
unsigned int nb_out_sg;
size_t out_offset;
size_t bufcnt;
size_t buflen; size_t buflen;
size_t dma_size; void *buf;
struct scatterlist aligned_sg;
void *buf_in; struct scatterlist *real_dst;
int dma_in;
dma_addr_t dma_addr_in;
struct atmel_aes_dma dma_lch_in;
void *buf_out;
int dma_out;
dma_addr_t dma_addr_out;
struct atmel_aes_dma dma_lch_out;
struct atmel_aes_caps caps; struct atmel_aes_caps caps;
...@@ -171,62 +168,6 @@ static struct atmel_aes_drv atmel_aes = { ...@@ -171,62 +168,6 @@ static struct atmel_aes_drv atmel_aes = {
.lock = __SPIN_LOCK_UNLOCKED(atmel_aes.lock), .lock = __SPIN_LOCK_UNLOCKED(atmel_aes.lock),
}; };
static int atmel_aes_sg_length(struct ablkcipher_request *req,
struct scatterlist *sg)
{
unsigned int total = req->nbytes;
int sg_nb;
unsigned int len;
struct scatterlist *sg_list;
sg_nb = 0;
sg_list = sg;
total = req->nbytes;
while (total) {
len = min(sg_list->length, total);
sg_nb++;
total -= len;
sg_list = sg_next(sg_list);
if (!sg_list)
total = 0;
}
return sg_nb;
}
static int atmel_aes_sg_copy(struct scatterlist **sg, size_t *offset,
void *buf, size_t buflen, size_t total, int out)
{
size_t count, off = 0;
while (buflen && total) {
count = min((*sg)->length - *offset, total);
count = min(count, buflen);
if (!count)
return off;
scatterwalk_map_and_copy(buf + off, *sg, *offset, count, out);
off += count;
buflen -= count;
*offset += count;
total -= count;
if (*offset == (*sg)->length) {
*sg = sg_next(*sg);
if (*sg)
*offset = 0;
else
total = 0;
}
}
return off;
}
static inline u32 atmel_aes_read(struct atmel_aes_dev *dd, u32 offset) static inline u32 atmel_aes_read(struct atmel_aes_dev *dd, u32 offset)
{ {
...@@ -253,6 +194,37 @@ static void atmel_aes_write_n(struct atmel_aes_dev *dd, u32 offset, ...@@ -253,6 +194,37 @@ static void atmel_aes_write_n(struct atmel_aes_dev *dd, u32 offset,
atmel_aes_write(dd, offset, *value); atmel_aes_write(dd, offset, *value);
} }
static inline void atmel_aes_read_block(struct atmel_aes_dev *dd, u32 offset,
u32 *value)
{
atmel_aes_read_n(dd, offset, value, SIZE_IN_WORDS(AES_BLOCK_SIZE));
}
static inline void atmel_aes_write_block(struct atmel_aes_dev *dd, u32 offset,
const u32 *value)
{
atmel_aes_write_n(dd, offset, value, SIZE_IN_WORDS(AES_BLOCK_SIZE));
}
static inline int atmel_aes_wait_for_data_ready(struct atmel_aes_dev *dd,
atmel_aes_fn_t resume)
{
u32 isr = atmel_aes_read(dd, AES_ISR);
if (unlikely(isr & AES_INT_DATARDY))
return resume(dd);
dd->resume = resume;
atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
return -EINPROGRESS;
}
static inline size_t atmel_aes_padlen(size_t len, size_t block_size)
{
len &= block_size - 1;
return len ? block_size - len : 0;
}
static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_base_ctx *ctx) static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_base_ctx *ctx)
{ {
struct atmel_aes_dev *aes_dd = NULL; struct atmel_aes_dev *aes_dd = NULL;
...@@ -332,204 +304,363 @@ static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err) ...@@ -332,204 +304,363 @@ static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
return err; return err;
} }
static void atmel_aes_dma_callback(void *data)
{
struct atmel_aes_dev *dd = data;
dd->is_async = true; /* CPU transfer */
(void)dd->resume(dd);
}
static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd, static int atmel_aes_cpu_transfer(struct atmel_aes_dev *dd)
dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, int length)
{ {
struct scatterlist sg[2]; int err = 0;
struct dma_async_tx_descriptor *in_desc, *out_desc; u32 isr;
enum dma_slave_buswidth addr_width;
u32 maxburst;
switch (dd->ctx->block_size) { for (;;) {
case CFB8_BLOCK_SIZE: atmel_aes_read_block(dd, AES_ODATAR(0), dd->data);
addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dd->data += 4;
maxburst = 1; dd->datalen -= AES_BLOCK_SIZE;
break;
case CFB16_BLOCK_SIZE: if (dd->datalen < AES_BLOCK_SIZE)
addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
maxburst = 1;
break; break;
case CFB32_BLOCK_SIZE: atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
case CFB64_BLOCK_SIZE:
addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
maxburst = 1;
break;
case AES_BLOCK_SIZE: isr = atmel_aes_read(dd, AES_ISR);
addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; if (!(isr & AES_INT_DATARDY)) {
maxburst = dd->caps.max_burst_size; dd->resume = atmel_aes_cpu_transfer;
break; atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
return -EINPROGRESS;
default: }
return -EINVAL;
} }
dd->dma_size = length; if (!sg_copy_from_buffer(dd->real_dst, sg_nents(dd->real_dst),
dd->buf, dd->total))
err = -EINVAL;
dma_sync_single_for_device(dd->dev, dma_addr_in, length, if (err)
DMA_TO_DEVICE); return atmel_aes_complete(dd, err);
dma_sync_single_for_device(dd->dev, dma_addr_out, length,
DMA_FROM_DEVICE);
dd->dma_lch_in.dma_conf.dst_addr_width = addr_width; return dd->cpu_transfer_complete(dd);
dd->dma_lch_in.dma_conf.src_maxburst = maxburst; }
dd->dma_lch_in.dma_conf.dst_maxburst = maxburst;
dd->dma_lch_out.dma_conf.src_addr_width = addr_width; static int atmel_aes_cpu_start(struct atmel_aes_dev *dd,
dd->dma_lch_out.dma_conf.src_maxburst = maxburst; struct scatterlist *src,
dd->dma_lch_out.dma_conf.dst_maxburst = maxburst; struct scatterlist *dst,
size_t len,
atmel_aes_fn_t resume)
{
size_t padlen = atmel_aes_padlen(len, AES_BLOCK_SIZE);
dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf); if (unlikely(len == 0))
dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf); return -EINVAL;
sg_init_table(&sg[0], 1); sg_copy_to_buffer(src, sg_nents(src), dd->buf, len);
sg_dma_address(&sg[0]) = dma_addr_in;
sg_dma_len(&sg[0]) = length;
sg_init_table(&sg[1], 1); dd->total = len;
sg_dma_address(&sg[1]) = dma_addr_out; dd->real_dst = dst;
sg_dma_len(&sg[1]) = length; dd->cpu_transfer_complete = resume;
dd->datalen = len + padlen;
dd->data = (u32 *)dd->buf;
atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
return atmel_aes_wait_for_data_ready(dd, atmel_aes_cpu_transfer);
}
in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, &sg[0],
1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!in_desc)
return -EINVAL;
out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, &sg[1], /* DMA transfer */
1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!out_desc)
return -EINVAL;
out_desc->callback = atmel_aes_dma_callback; static void atmel_aes_dma_callback(void *data);
out_desc->callback_param = dd;
dmaengine_submit(out_desc); static bool atmel_aes_check_aligned(struct atmel_aes_dev *dd,
dma_async_issue_pending(dd->dma_lch_out.chan); struct scatterlist *sg,
size_t len,
struct atmel_aes_dma *dma)
{
int nents;
dmaengine_submit(in_desc); if (!IS_ALIGNED(len, dd->ctx->block_size))
dma_async_issue_pending(dd->dma_lch_in.chan); return false;
return 0; for (nents = 0; sg; sg = sg_next(sg), ++nents) {
} if (!IS_ALIGNED(sg->offset, sizeof(u32)))
return false;
static int atmel_aes_cpu_complete(struct atmel_aes_dev *dd); if (len <= sg->length) {
if (!IS_ALIGNED(len, dd->ctx->block_size))
return false;
static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd) dma->nents = nents+1;
{ dma->remainder = sg->length - len;
struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq); sg->length = len;
return true;
}
dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in, if (!IS_ALIGNED(sg->length, dd->ctx->block_size))
dd->dma_size, DMA_TO_DEVICE); return false;
dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out,
dd->dma_size, DMA_FROM_DEVICE);
/* use cache buffers */ len -= sg->length;
dd->nb_in_sg = atmel_aes_sg_length(req, dd->in_sg); }
if (!dd->nb_in_sg)
return -EINVAL;
dd->nb_out_sg = atmel_aes_sg_length(req, dd->out_sg); return false;
if (!dd->nb_out_sg) }
return -EINVAL;
dd->bufcnt = sg_copy_to_buffer(dd->in_sg, dd->nb_in_sg, static inline void atmel_aes_restore_sg(const struct atmel_aes_dma *dma)
dd->buf_in, dd->total); {
struct scatterlist *sg = dma->sg;
int nents = dma->nents;
if (!dd->bufcnt) if (!dma->remainder)
return -EINVAL; return;
dd->total -= dd->bufcnt; while (--nents > 0 && sg)
sg = sg_next(sg);
atmel_aes_write(dd, AES_IER, AES_INT_DATARDY); if (!sg)
atmel_aes_write_n(dd, AES_IDATAR(0), (u32 *) dd->buf_in, return;
dd->bufcnt >> 2);
dd->resume = atmel_aes_cpu_complete; sg->length += dma->remainder;
return -EINPROGRESS;
} }
static int atmel_aes_dma_complete(struct atmel_aes_dev *dd); static int atmel_aes_map(struct atmel_aes_dev *dd,
struct scatterlist *src,
static int atmel_aes_crypt_dma_start(struct atmel_aes_dev *dd) struct scatterlist *dst,
size_t len)
{ {
int err, fast = 0, in, out; bool src_aligned, dst_aligned;
size_t count; size_t padlen;
dma_addr_t addr_in, addr_out;
dd->total = len;
dd->src.sg = src;
dd->dst.sg = dst;
dd->real_dst = dst;
if ((!dd->in_offset) && (!dd->out_offset)) { src_aligned = atmel_aes_check_aligned(dd, src, len, &dd->src);
/* check for alignment */ if (src == dst)
in = IS_ALIGNED((u32)dd->in_sg->offset, sizeof(u32)) && dst_aligned = src_aligned;
IS_ALIGNED(dd->in_sg->length, dd->ctx->block_size); else
out = IS_ALIGNED((u32)dd->out_sg->offset, sizeof(u32)) && dst_aligned = atmel_aes_check_aligned(dd, dst, len, &dd->dst);
IS_ALIGNED(dd->out_sg->length, dd->ctx->block_size); if (!src_aligned || !dst_aligned) {
fast = in && out; padlen = atmel_aes_padlen(len, dd->ctx->block_size);
if (dd->buflen < len + padlen)
return -ENOMEM;
if (!src_aligned) {
sg_copy_to_buffer(src, sg_nents(src), dd->buf, len);
dd->src.sg = &dd->aligned_sg;
dd->src.nents = 1;
dd->src.remainder = 0;
}
if (sg_dma_len(dd->in_sg) != sg_dma_len(dd->out_sg)) if (!dst_aligned) {
fast = 0; dd->dst.sg = &dd->aligned_sg;
dd->dst.nents = 1;
dd->dst.remainder = 0;
} }
sg_init_table(&dd->aligned_sg, 1);
sg_set_buf(&dd->aligned_sg, dd->buf, len + padlen);
}
if (fast) { if (dd->src.sg == dd->dst.sg) {
count = min_t(size_t, dd->total, sg_dma_len(dd->in_sg)); dd->src.sg_len = dma_map_sg(dd->dev, dd->src.sg, dd->src.nents,
count = min_t(size_t, count, sg_dma_len(dd->out_sg)); DMA_BIDIRECTIONAL);
dd->dst.sg_len = dd->src.sg_len;
if (!dd->src.sg_len)
return -EFAULT;
} else {
dd->src.sg_len = dma_map_sg(dd->dev, dd->src.sg, dd->src.nents,
DMA_TO_DEVICE);
if (!dd->src.sg_len)
return -EFAULT;
err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); dd->dst.sg_len = dma_map_sg(dd->dev, dd->dst.sg, dd->dst.nents,
if (!err) { DMA_FROM_DEVICE);
dev_err(dd->dev, "dma_map_sg() error\n"); if (!dd->dst.sg_len) {
return -EINVAL; dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
DMA_TO_DEVICE);
return -EFAULT;
} }
}
return 0;
}
static void atmel_aes_unmap(struct atmel_aes_dev *dd)
{
if (dd->src.sg == dd->dst.sg) {
dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
DMA_BIDIRECTIONAL);
err = dma_map_sg(dd->dev, dd->out_sg, 1, if (dd->src.sg != &dd->aligned_sg)
atmel_aes_restore_sg(&dd->src);
} else {
dma_unmap_sg(dd->dev, dd->dst.sg, dd->dst.nents,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
if (!err) {
dev_err(dd->dev, "dma_map_sg() error\n"); if (dd->dst.sg != &dd->aligned_sg)
dma_unmap_sg(dd->dev, dd->in_sg, 1, atmel_aes_restore_sg(&dd->dst);
dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (dd->src.sg != &dd->aligned_sg)
atmel_aes_restore_sg(&dd->src);
}
if (dd->dst.sg == &dd->aligned_sg)
sg_copy_from_buffer(dd->real_dst, sg_nents(dd->real_dst),
dd->buf, dd->total);
}
static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
enum dma_slave_buswidth addr_width,
enum dma_transfer_direction dir,
u32 maxburst)
{
struct dma_async_tx_descriptor *desc;
struct dma_slave_config config;
dma_async_tx_callback callback;
struct atmel_aes_dma *dma;
int err;
memset(&config, 0, sizeof(config));
config.direction = dir;
config.src_addr_width = addr_width;
config.dst_addr_width = addr_width;
config.src_maxburst = maxburst;
config.dst_maxburst = maxburst;
switch (dir) {
case DMA_MEM_TO_DEV:
dma = &dd->src;
callback = NULL;
config.dst_addr = dd->phys_base + AES_IDATAR(0);
break;
case DMA_DEV_TO_MEM:
dma = &dd->dst;
callback = atmel_aes_dma_callback;
config.src_addr = dd->phys_base + AES_ODATAR(0);
break;
default:
return -EINVAL; return -EINVAL;
} }
addr_in = sg_dma_address(dd->in_sg); err = dmaengine_slave_config(dma->chan, &config);
addr_out = sg_dma_address(dd->out_sg); if (err)
return err;
desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
return -ENOMEM;
dd->flags |= AES_FLAGS_FAST; desc->callback = callback;
desc->callback_param = dd;
dmaengine_submit(desc);
dma_async_issue_pending(dma->chan);
} else { return 0;
dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in, }
dd->dma_size, DMA_TO_DEVICE);
/* use cache buffers */ static void atmel_aes_dma_transfer_stop(struct atmel_aes_dev *dd,
count = atmel_aes_sg_copy(&dd->in_sg, &dd->in_offset, enum dma_transfer_direction dir)
dd->buf_in, dd->buflen, dd->total, 0); {
struct atmel_aes_dma *dma;
addr_in = dd->dma_addr_in; switch (dir) {
addr_out = dd->dma_addr_out; case DMA_MEM_TO_DEV:
dma = &dd->src;
break;
dd->flags &= ~AES_FLAGS_FAST; case DMA_DEV_TO_MEM:
dma = &dd->dst;
break;
default:
return;
} }
dd->total -= count; dmaengine_terminate_all(dma->chan);
}
static int atmel_aes_dma_start(struct atmel_aes_dev *dd,
struct scatterlist *src,
struct scatterlist *dst,
size_t len,
atmel_aes_fn_t resume)
{
enum dma_slave_buswidth addr_width;
u32 maxburst;
int err;
err = atmel_aes_crypt_dma(dd, addr_in, addr_out, count); switch (dd->ctx->block_size) {
case CFB8_BLOCK_SIZE:
addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
maxburst = 1;
break;
if (err && (dd->flags & AES_FLAGS_FAST)) { case CFB16_BLOCK_SIZE:
dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE); maxburst = 1;
break;
case CFB32_BLOCK_SIZE:
case CFB64_BLOCK_SIZE:
addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
maxburst = 1;
break;
case AES_BLOCK_SIZE:
addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
maxburst = dd->caps.max_burst_size;
break;
default:
err = -EINVAL;
goto exit;
} }
dd->resume = atmel_aes_dma_complete; err = atmel_aes_map(dd, src, dst, len);
return err ? : -EINPROGRESS; if (err)
goto exit;
dd->resume = resume;
/* Set output DMA transfer first */
err = atmel_aes_dma_transfer_start(dd, addr_width, DMA_DEV_TO_MEM,
maxburst);
if (err)
goto unmap;
/* Then set input DMA transfer */
err = atmel_aes_dma_transfer_start(dd, addr_width, DMA_MEM_TO_DEV,
maxburst);
if (err)
goto output_transfer_stop;
return -EINPROGRESS;
output_transfer_stop:
atmel_aes_dma_transfer_stop(dd, DMA_DEV_TO_MEM);
unmap:
atmel_aes_unmap(dd);
exit:
return atmel_aes_complete(dd, err);
}
static void atmel_aes_dma_stop(struct atmel_aes_dev *dd)
{
atmel_aes_dma_transfer_stop(dd, DMA_MEM_TO_DEV);
atmel_aes_dma_transfer_stop(dd, DMA_DEV_TO_MEM);
atmel_aes_unmap(dd);
}
static void atmel_aes_dma_callback(void *data)
{
struct atmel_aes_dev *dd = data;
atmel_aes_dma_stop(dd);
dd->is_async = true;
(void)dd->resume(dd);
} }
static void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma, static void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma,
...@@ -601,119 +732,52 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd, ...@@ -601,119 +732,52 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd,
return (dd->is_async) ? ret : err; return (dd->is_async) ? ret : err;
} }
static int atmel_aes_transfer_complete(struct atmel_aes_dev *dd)
{
return atmel_aes_complete(dd, 0);
}
static int atmel_aes_start(struct atmel_aes_dev *dd) static int atmel_aes_start(struct atmel_aes_dev *dd)
{ {
struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq); struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
struct atmel_aes_reqctx *rctx; struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
bool use_dma; bool use_dma = (req->nbytes >= ATMEL_AES_DMA_THRESHOLD ||
dd->ctx->block_size != AES_BLOCK_SIZE);
int err; int err;
/* assign new request to device */
dd->total = req->nbytes;
dd->in_offset = 0;
dd->in_sg = req->src;
dd->out_offset = 0;
dd->out_sg = req->dst;
rctx = ablkcipher_request_ctx(req);
atmel_aes_set_mode(dd, rctx); atmel_aes_set_mode(dd, rctx);
err = atmel_aes_hw_init(dd); err = atmel_aes_hw_init(dd);
if (!err) { if (err)
use_dma = (dd->total > ATMEL_AES_DMA_THRESHOLD);
atmel_aes_write_ctrl(dd, use_dma, req->info);
if (use_dma)
err = atmel_aes_crypt_dma_start(dd);
else
err = atmel_aes_crypt_cpu_start(dd);
}
if (err && err != -EINPROGRESS) {
/* aes_task will not finish it, so do it here */
return atmel_aes_complete(dd, err); return atmel_aes_complete(dd, err);
}
return -EINPROGRESS; atmel_aes_write_ctrl(dd, use_dma, req->info);
} if (use_dma)
return atmel_aes_dma_start(dd, req->src, req->dst, req->nbytes,
static int atmel_aes_crypt_dma_stop(struct atmel_aes_dev *dd) atmel_aes_transfer_complete);
{
int err = 0;
size_t count;
if (dd->flags & AES_FLAGS_FAST) {
dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE);
dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
} else {
dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out,
dd->dma_size, DMA_FROM_DEVICE);
/* copy data */
count = atmel_aes_sg_copy(&dd->out_sg, &dd->out_offset,
dd->buf_out, dd->buflen,
dd->dma_size, 1);
if (count != dd->dma_size) {
err = -EINVAL;
pr_err("not all data converted: %zu\n", count);
}
}
return err; return atmel_aes_cpu_start(dd, req->src, req->dst, req->nbytes,
atmel_aes_transfer_complete);
} }
static int atmel_aes_buff_init(struct atmel_aes_dev *dd) static int atmel_aes_buff_init(struct atmel_aes_dev *dd)
{ {
int err = -ENOMEM; dd->buf = (void *)__get_free_pages(GFP_KERNEL, ATMEL_AES_BUFFER_ORDER);
dd->buflen = ATMEL_AES_BUFFER_SIZE;
dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, 0);
dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, 0);
dd->buflen = PAGE_SIZE;
dd->buflen &= ~(AES_BLOCK_SIZE - 1); dd->buflen &= ~(AES_BLOCK_SIZE - 1);
if (!dd->buf_in || !dd->buf_out) { if (!dd->buf) {
dev_err(dd->dev, "unable to alloc pages.\n"); dev_err(dd->dev, "unable to alloc pages.\n");
goto err_alloc; return -ENOMEM;
}
/* MAP here */
dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in,
dd->buflen, DMA_TO_DEVICE);
if (dma_mapping_error(dd->dev, dd->dma_addr_in)) {
dev_err(dd->dev, "dma %zd bytes error\n", dd->buflen);
err = -EINVAL;
goto err_map_in;
}
dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out,
dd->buflen, DMA_FROM_DEVICE);
if (dma_mapping_error(dd->dev, dd->dma_addr_out)) {
dev_err(dd->dev, "dma %zd bytes error\n", dd->buflen);
err = -EINVAL;
goto err_map_out;
} }
return 0; return 0;
err_map_out:
dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
DMA_TO_DEVICE);
err_map_in:
err_alloc:
free_page((unsigned long)dd->buf_out);
free_page((unsigned long)dd->buf_in);
if (err)
pr_err("error: %d\n", err);
return err;
} }
static void atmel_aes_buff_cleanup(struct atmel_aes_dev *dd) static void atmel_aes_buff_cleanup(struct atmel_aes_dev *dd)
{ {
dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, free_page((unsigned long)dd->buf);
DMA_FROM_DEVICE);
dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
DMA_TO_DEVICE);
free_page((unsigned long)dd->buf_out);
free_page((unsigned long)dd->buf_in);
} }
static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode) static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
...@@ -769,6 +833,7 @@ static bool atmel_aes_filter(struct dma_chan *chan, void *slave) ...@@ -769,6 +833,7 @@ static bool atmel_aes_filter(struct dma_chan *chan, void *slave)
static int atmel_aes_dma_init(struct atmel_aes_dev *dd, static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
struct crypto_platform_data *pdata) struct crypto_platform_data *pdata)
{ {
struct at_dma_slave *slave;
int err = -ENOMEM; int err = -ENOMEM;
dma_cap_mask_t mask; dma_cap_mask_t mask;
...@@ -776,42 +841,22 @@ static int atmel_aes_dma_init(struct atmel_aes_dev *dd, ...@@ -776,42 +841,22 @@ static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_SLAVE, mask);
/* Try to grab 2 DMA channels */ /* Try to grab 2 DMA channels */
dd->dma_lch_in.chan = dma_request_slave_channel_compat(mask, slave = &pdata->dma_slave->rxdata;
atmel_aes_filter, &pdata->dma_slave->rxdata, dd->dev, "tx"); dd->src.chan = dma_request_slave_channel_compat(mask, atmel_aes_filter,
if (!dd->dma_lch_in.chan) slave, dd->dev, "tx");
if (!dd->src.chan)
goto err_dma_in; goto err_dma_in;
dd->dma_lch_in.dma_conf.direction = DMA_MEM_TO_DEV; slave = &pdata->dma_slave->txdata;
dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base + dd->dst.chan = dma_request_slave_channel_compat(mask, atmel_aes_filter,
AES_IDATAR(0); slave, dd->dev, "rx");
dd->dma_lch_in.dma_conf.src_maxburst = dd->caps.max_burst_size; if (!dd->dst.chan)
dd->dma_lch_in.dma_conf.src_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_in.dma_conf.dst_maxburst = dd->caps.max_burst_size;
dd->dma_lch_in.dma_conf.dst_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_in.dma_conf.device_fc = false;
dd->dma_lch_out.chan = dma_request_slave_channel_compat(mask,
atmel_aes_filter, &pdata->dma_slave->txdata, dd->dev, "rx");
if (!dd->dma_lch_out.chan)
goto err_dma_out; goto err_dma_out;
dd->dma_lch_out.dma_conf.direction = DMA_DEV_TO_MEM;
dd->dma_lch_out.dma_conf.src_addr = dd->phys_base +
AES_ODATAR(0);
dd->dma_lch_out.dma_conf.src_maxburst = dd->caps.max_burst_size;
dd->dma_lch_out.dma_conf.src_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_out.dma_conf.dst_maxburst = dd->caps.max_burst_size;
dd->dma_lch_out.dma_conf.dst_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_out.dma_conf.device_fc = false;
return 0; return 0;
err_dma_out: err_dma_out:
dma_release_channel(dd->dma_lch_in.chan); dma_release_channel(dd->src.chan);
err_dma_in: err_dma_in:
dev_warn(dd->dev, "no DMA channel available\n"); dev_warn(dd->dev, "no DMA channel available\n");
return err; return err;
...@@ -819,8 +864,8 @@ static int atmel_aes_dma_init(struct atmel_aes_dev *dd, ...@@ -819,8 +864,8 @@ static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
static void atmel_aes_dma_cleanup(struct atmel_aes_dev *dd) static void atmel_aes_dma_cleanup(struct atmel_aes_dev *dd)
{ {
dma_release_channel(dd->dma_lch_in.chan); dma_release_channel(dd->dst.chan);
dma_release_channel(dd->dma_lch_out.chan); dma_release_channel(dd->src.chan);
} }
static int atmel_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, static int atmel_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
...@@ -1157,43 +1202,6 @@ static void atmel_aes_done_task(unsigned long data) ...@@ -1157,43 +1202,6 @@ static void atmel_aes_done_task(unsigned long data)
(void)dd->resume(dd); (void)dd->resume(dd);
} }
static int atmel_aes_dma_complete(struct atmel_aes_dev *dd)
{
int err;
err = atmel_aes_crypt_dma_stop(dd);
if (dd->total && !err) {
if (dd->flags & AES_FLAGS_FAST) {
dd->in_sg = sg_next(dd->in_sg);
dd->out_sg = sg_next(dd->out_sg);
if (!dd->in_sg || !dd->out_sg)
err = -EINVAL;
}
if (!err)
err = atmel_aes_crypt_dma_start(dd);
if (!err || err == -EINPROGRESS)
return -EINPROGRESS; /* DMA started. Not fininishing. */
}
return atmel_aes_complete(dd, err);
}
static int atmel_aes_cpu_complete(struct atmel_aes_dev *dd)
{
int err;
atmel_aes_read_n(dd, AES_ODATAR(0), (u32 *) dd->buf_out,
dd->bufcnt >> 2);
if (sg_copy_from_buffer(dd->out_sg, dd->nb_out_sg,
dd->buf_out, dd->bufcnt))
err = 0;
else
err = -EINVAL;
return atmel_aes_complete(dd, err);
}
static irqreturn_t atmel_aes_irq(int irq, void *dev_id) static irqreturn_t atmel_aes_irq(int irq, void *dev_id)
{ {
struct atmel_aes_dev *aes_dd = dev_id; struct atmel_aes_dev *aes_dd = dev_id;
...@@ -1430,8 +1438,8 @@ static int atmel_aes_probe(struct platform_device *pdev) ...@@ -1430,8 +1438,8 @@ static int atmel_aes_probe(struct platform_device *pdev)
goto err_algs; goto err_algs;
dev_info(dev, "Atmel AES - Using %s, %s for DMA transfers\n", dev_info(dev, "Atmel AES - Using %s, %s for DMA transfers\n",
dma_chan_name(aes_dd->dma_lch_in.chan), dma_chan_name(aes_dd->src.chan),
dma_chan_name(aes_dd->dma_lch_out.chan)); dma_chan_name(aes_dd->dst.chan));
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment