2012-07-01 21:19:46 +04:00
/*
* Cryptographic API .
*
* Support for ATMEL SHA1 / SHA256 HW acceleration .
*
* Copyright ( c ) 2012 Eukréa Electromatique - ATMEL
* Author : Nicolas Royer < nicolas @ eukrea . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*
* Some ideas are from omap - sham . c drivers .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/hw_random.h>
# include <linux/platform_device.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/scatterlist.h>
# include <linux/dma-mapping.h>
2013-10-15 17:36:34 +04:00
# include <linux/of_device.h>
2012-07-01 21:19:46 +04:00
# include <linux/delay.h>
# include <linux/crypto.h>
# include <linux/cryptohash.h>
# include <crypto/scatterwalk.h>
# include <crypto/algapi.h>
# include <crypto/sha.h>
# include <crypto/hash.h>
# include <crypto/internal/hash.h>
2013-02-20 20:10:26 +04:00
# include <linux/platform_data/crypto-atmel.h>
2012-07-01 21:19:46 +04:00
# include "atmel-sha-regs.h"
/* SHA flags */
# define SHA_FLAGS_BUSY BIT(0)
# define SHA_FLAGS_FINAL BIT(1)
# define SHA_FLAGS_DMA_ACTIVE BIT(2)
# define SHA_FLAGS_OUTPUT_READY BIT(3)
# define SHA_FLAGS_INIT BIT(4)
# define SHA_FLAGS_CPU BIT(5)
# define SHA_FLAGS_DMA_READY BIT(6)
# define SHA_FLAGS_FINUP BIT(16)
# define SHA_FLAGS_SG BIT(17)
# define SHA_FLAGS_SHA1 BIT(18)
2013-02-20 20:10:26 +04:00
# define SHA_FLAGS_SHA224 BIT(19)
# define SHA_FLAGS_SHA256 BIT(20)
# define SHA_FLAGS_SHA384 BIT(21)
# define SHA_FLAGS_SHA512 BIT(22)
# define SHA_FLAGS_ERROR BIT(23)
# define SHA_FLAGS_PAD BIT(24)
2012-07-01 21:19:46 +04:00
# define SHA_OP_UPDATE 1
# define SHA_OP_FINAL 2
# define SHA_BUFFER_LEN PAGE_SIZE
# define ATMEL_SHA_DMA_THRESHOLD 56
2013-02-20 20:10:26 +04:00
struct atmel_sha_caps {
bool has_dma ;
bool has_dualbuff ;
bool has_sha224 ;
bool has_sha_384_512 ;
} ;
2012-07-01 21:19:46 +04:00
struct atmel_sha_dev ;
struct atmel_sha_reqctx {
struct atmel_sha_dev * dd ;
unsigned long flags ;
unsigned long op ;
2013-02-20 20:10:26 +04:00
u8 digest [ SHA512_DIGEST_SIZE ] __aligned ( sizeof ( u32 ) ) ;
u64 digcnt [ 2 ] ;
2012-07-01 21:19:46 +04:00
size_t bufcnt ;
size_t buflen ;
dma_addr_t dma_addr ;
/* walk state */
struct scatterlist * sg ;
unsigned int offset ; /* offset in current sg */
unsigned int total ; /* total request */
2013-02-20 20:10:26 +04:00
size_t block_size ;
2012-07-01 21:19:46 +04:00
u8 buffer [ 0 ] __aligned ( sizeof ( u32 ) ) ;
} ;
struct atmel_sha_ctx {
struct atmel_sha_dev * dd ;
unsigned long flags ;
/* fallback stuff */
struct crypto_shash * fallback ;
} ;
2013-02-20 20:10:26 +04:00
# define ATMEL_SHA_QUEUE_LENGTH 50
struct atmel_sha_dma {
struct dma_chan * chan ;
struct dma_slave_config dma_conf ;
} ;
2012-07-01 21:19:46 +04:00
struct atmel_sha_dev {
struct list_head list ;
unsigned long phys_base ;
struct device * dev ;
struct clk * iclk ;
int irq ;
void __iomem * io_base ;
spinlock_t lock ;
int err ;
struct tasklet_struct done_task ;
unsigned long flags ;
struct crypto_queue queue ;
struct ahash_request * req ;
2013-02-20 20:10:26 +04:00
struct atmel_sha_dma dma_lch_in ;
struct atmel_sha_caps caps ;
u32 hw_version ;
2012-07-01 21:19:46 +04:00
} ;
struct atmel_sha_drv {
struct list_head dev_list ;
spinlock_t lock ;
} ;
static struct atmel_sha_drv atmel_sha = {
. dev_list = LIST_HEAD_INIT ( atmel_sha . dev_list ) ,
. lock = __SPIN_LOCK_UNLOCKED ( atmel_sha . lock ) ,
} ;
static inline u32 atmel_sha_read ( struct atmel_sha_dev * dd , u32 offset )
{
return readl_relaxed ( dd - > io_base + offset ) ;
}
static inline void atmel_sha_write ( struct atmel_sha_dev * dd ,
u32 offset , u32 value )
{
writel_relaxed ( value , dd - > io_base + offset ) ;
}
static size_t atmel_sha_append_sg ( struct atmel_sha_reqctx * ctx )
{
size_t count ;
while ( ( ctx - > bufcnt < ctx - > buflen ) & & ctx - > total ) {
count = min ( ctx - > sg - > length - ctx - > offset , ctx - > total ) ;
count = min ( count , ctx - > buflen - ctx - > bufcnt ) ;
if ( count < = 0 )
break ;
scatterwalk_map_and_copy ( ctx - > buffer + ctx - > bufcnt , ctx - > sg ,
ctx - > offset , count , 0 ) ;
ctx - > bufcnt + = count ;
ctx - > offset + = count ;
ctx - > total - = count ;
if ( ctx - > offset = = ctx - > sg - > length ) {
ctx - > sg = sg_next ( ctx - > sg ) ;
if ( ctx - > sg )
ctx - > offset = 0 ;
else
ctx - > total = 0 ;
}
}
return 0 ;
}
/*
2013-02-20 20:10:26 +04:00
* The purpose of this padding is to ensure that the padded message is a
* multiple of 512 bits ( SHA1 / SHA224 / SHA256 ) or 1024 bits ( SHA384 / SHA512 ) .
* The bit " 1 " is appended at the end of the message followed by
* " padlen-1 " zero bits . Then a 64 bits block ( SHA1 / SHA224 / SHA256 ) or
* 128 bits block ( SHA384 / SHA512 ) equals to the message length in bits
* is appended .
2012-07-01 21:19:46 +04:00
*
2013-02-20 20:10:26 +04:00
* For SHA1 / SHA224 / SHA256 , padlen is calculated as followed :
2012-07-01 21:19:46 +04:00
* - if message length < 56 bytes then padlen = 56 - message length
* - else padlen = 64 + 56 - message length
2013-02-20 20:10:26 +04:00
*
* For SHA384 / SHA512 , padlen is calculated as followed :
* - if message length < 112 bytes then padlen = 112 - message length
* - else padlen = 128 + 112 - message length
2012-07-01 21:19:46 +04:00
*/
static void atmel_sha_fill_padding ( struct atmel_sha_reqctx * ctx , int length )
{
unsigned int index , padlen ;
2013-02-20 20:10:26 +04:00
u64 bits [ 2 ] ;
u64 size [ 2 ] ;
size [ 0 ] = ctx - > digcnt [ 0 ] ;
size [ 1 ] = ctx - > digcnt [ 1 ] ;
size [ 0 ] + = ctx - > bufcnt ;
if ( size [ 0 ] < ctx - > bufcnt )
size [ 1 ] + + ;
size [ 0 ] + = length ;
if ( size [ 0 ] < length )
size [ 1 ] + + ;
bits [ 1 ] = cpu_to_be64 ( size [ 0 ] < < 3 ) ;
bits [ 0 ] = cpu_to_be64 ( size [ 1 ] < < 3 | size [ 0 ] > > 61 ) ;
if ( ctx - > flags & ( SHA_FLAGS_SHA384 | SHA_FLAGS_SHA512 ) ) {
index = ctx - > bufcnt & 0x7f ;
padlen = ( index < 112 ) ? ( 112 - index ) : ( ( 128 + 112 ) - index ) ;
* ( ctx - > buffer + ctx - > bufcnt ) = 0x80 ;
memset ( ctx - > buffer + ctx - > bufcnt + 1 , 0 , padlen - 1 ) ;
memcpy ( ctx - > buffer + ctx - > bufcnt + padlen , bits , 16 ) ;
ctx - > bufcnt + = padlen + 16 ;
ctx - > flags | = SHA_FLAGS_PAD ;
} else {
index = ctx - > bufcnt & 0x3f ;
padlen = ( index < 56 ) ? ( 56 - index ) : ( ( 64 + 56 ) - index ) ;
* ( ctx - > buffer + ctx - > bufcnt ) = 0x80 ;
memset ( ctx - > buffer + ctx - > bufcnt + 1 , 0 , padlen - 1 ) ;
memcpy ( ctx - > buffer + ctx - > bufcnt + padlen , & bits [ 1 ] , 8 ) ;
ctx - > bufcnt + = padlen + 8 ;
ctx - > flags | = SHA_FLAGS_PAD ;
}
2012-07-01 21:19:46 +04:00
}
static int atmel_sha_init ( struct ahash_request * req )
{
struct crypto_ahash * tfm = crypto_ahash_reqtfm ( req ) ;
struct atmel_sha_ctx * tctx = crypto_ahash_ctx ( tfm ) ;
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
struct atmel_sha_dev * dd = NULL ;
struct atmel_sha_dev * tmp ;
spin_lock_bh ( & atmel_sha . lock ) ;
if ( ! tctx - > dd ) {
list_for_each_entry ( tmp , & atmel_sha . dev_list , list ) {
dd = tmp ;
break ;
}
tctx - > dd = dd ;
} else {
dd = tctx - > dd ;
}
spin_unlock_bh ( & atmel_sha . lock ) ;
ctx - > dd = dd ;
ctx - > flags = 0 ;
dev_dbg ( dd - > dev , " init: digest size: %d \n " ,
crypto_ahash_digestsize ( tfm ) ) ;
2013-02-20 20:10:26 +04:00
switch ( crypto_ahash_digestsize ( tfm ) ) {
case SHA1_DIGEST_SIZE :
2012-07-01 21:19:46 +04:00
ctx - > flags | = SHA_FLAGS_SHA1 ;
2013-02-20 20:10:26 +04:00
ctx - > block_size = SHA1_BLOCK_SIZE ;
break ;
case SHA224_DIGEST_SIZE :
ctx - > flags | = SHA_FLAGS_SHA224 ;
ctx - > block_size = SHA224_BLOCK_SIZE ;
break ;
case SHA256_DIGEST_SIZE :
2012-07-01 21:19:46 +04:00
ctx - > flags | = SHA_FLAGS_SHA256 ;
2013-02-20 20:10:26 +04:00
ctx - > block_size = SHA256_BLOCK_SIZE ;
break ;
case SHA384_DIGEST_SIZE :
ctx - > flags | = SHA_FLAGS_SHA384 ;
ctx - > block_size = SHA384_BLOCK_SIZE ;
break ;
case SHA512_DIGEST_SIZE :
ctx - > flags | = SHA_FLAGS_SHA512 ;
ctx - > block_size = SHA512_BLOCK_SIZE ;
break ;
default :
return - EINVAL ;
break ;
}
2012-07-01 21:19:46 +04:00
ctx - > bufcnt = 0 ;
2013-02-20 20:10:26 +04:00
ctx - > digcnt [ 0 ] = 0 ;
ctx - > digcnt [ 1 ] = 0 ;
2012-07-01 21:19:46 +04:00
ctx - > buflen = SHA_BUFFER_LEN ;
return 0 ;
}
static void atmel_sha_write_ctrl ( struct atmel_sha_dev * dd , int dma )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
u32 valcr = 0 , valmr = SHA_MR_MODE_AUTO ;
if ( likely ( dma ) ) {
2013-02-20 20:10:26 +04:00
if ( ! dd - > caps . has_dma )
atmel_sha_write ( dd , SHA_IER , SHA_INT_TXBUFE ) ;
2012-07-01 21:19:46 +04:00
valmr = SHA_MR_MODE_PDC ;
2013-02-20 20:10:26 +04:00
if ( dd - > caps . has_dualbuff )
valmr | = SHA_MR_DUALBUFF ;
2012-07-01 21:19:46 +04:00
} else {
atmel_sha_write ( dd , SHA_IER , SHA_INT_DATARDY ) ;
}
2013-02-20 20:10:26 +04:00
if ( ctx - > flags & SHA_FLAGS_SHA1 )
valmr | = SHA_MR_ALGO_SHA1 ;
else if ( ctx - > flags & SHA_FLAGS_SHA224 )
valmr | = SHA_MR_ALGO_SHA224 ;
else if ( ctx - > flags & SHA_FLAGS_SHA256 )
2012-07-01 21:19:46 +04:00
valmr | = SHA_MR_ALGO_SHA256 ;
2013-02-20 20:10:26 +04:00
else if ( ctx - > flags & SHA_FLAGS_SHA384 )
valmr | = SHA_MR_ALGO_SHA384 ;
else if ( ctx - > flags & SHA_FLAGS_SHA512 )
valmr | = SHA_MR_ALGO_SHA512 ;
2012-07-01 21:19:46 +04:00
/* Setting CR_FIRST only for the first iteration */
2013-02-20 20:10:26 +04:00
if ( ! ( ctx - > digcnt [ 0 ] | | ctx - > digcnt [ 1 ] ) )
2012-07-01 21:19:46 +04:00
valcr = SHA_CR_FIRST ;
atmel_sha_write ( dd , SHA_CR , valcr ) ;
atmel_sha_write ( dd , SHA_MR , valmr ) ;
}
static int atmel_sha_xmit_cpu ( struct atmel_sha_dev * dd , const u8 * buf ,
size_t length , int final )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
int count , len32 ;
const u32 * buffer = ( const u32 * ) buf ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " xmit_cpu: digcnt: 0x%llx 0x%llx, length: %d, final: %d \n " ,
ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] , length , final ) ;
2012-07-01 21:19:46 +04:00
atmel_sha_write_ctrl ( dd , 0 ) ;
/* should be non-zero before next lines to disable clocks later */
2013-02-20 20:10:26 +04:00
ctx - > digcnt [ 0 ] + = length ;
if ( ctx - > digcnt [ 0 ] < length )
ctx - > digcnt [ 1 ] + + ;
2012-07-01 21:19:46 +04:00
if ( final )
dd - > flags | = SHA_FLAGS_FINAL ; /* catch last interrupt */
len32 = DIV_ROUND_UP ( length , sizeof ( u32 ) ) ;
dd - > flags | = SHA_FLAGS_CPU ;
for ( count = 0 ; count < len32 ; count + + )
atmel_sha_write ( dd , SHA_REG_DIN ( count ) , buffer [ count ] ) ;
return - EINPROGRESS ;
}
static int atmel_sha_xmit_pdc ( struct atmel_sha_dev * dd , dma_addr_t dma_addr1 ,
size_t length1 , dma_addr_t dma_addr2 , size_t length2 , int final )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
int len32 ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " xmit_pdc: digcnt: 0x%llx 0x%llx, length: %d, final: %d \n " ,
ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] , length1 , final ) ;
2012-07-01 21:19:46 +04:00
len32 = DIV_ROUND_UP ( length1 , sizeof ( u32 ) ) ;
atmel_sha_write ( dd , SHA_PTCR , SHA_PTCR_TXTDIS ) ;
atmel_sha_write ( dd , SHA_TPR , dma_addr1 ) ;
atmel_sha_write ( dd , SHA_TCR , len32 ) ;
len32 = DIV_ROUND_UP ( length2 , sizeof ( u32 ) ) ;
atmel_sha_write ( dd , SHA_TNPR , dma_addr2 ) ;
atmel_sha_write ( dd , SHA_TNCR , len32 ) ;
atmel_sha_write_ctrl ( dd , 1 ) ;
/* should be non-zero before next lines to disable clocks later */
2013-02-20 20:10:26 +04:00
ctx - > digcnt [ 0 ] + = length1 ;
if ( ctx - > digcnt [ 0 ] < length1 )
ctx - > digcnt [ 1 ] + + ;
2012-07-01 21:19:46 +04:00
if ( final )
dd - > flags | = SHA_FLAGS_FINAL ; /* catch last interrupt */
dd - > flags | = SHA_FLAGS_DMA_ACTIVE ;
/* Start DMA transfer */
atmel_sha_write ( dd , SHA_PTCR , SHA_PTCR_TXTEN ) ;
return - EINPROGRESS ;
}
2013-02-20 20:10:26 +04:00
static void atmel_sha_dma_callback ( void * data )
{
struct atmel_sha_dev * dd = data ;
/* dma_lch_in - completed - wait DATRDY */
atmel_sha_write ( dd , SHA_IER , SHA_INT_DATARDY ) ;
}
static int atmel_sha_xmit_dma ( struct atmel_sha_dev * dd , dma_addr_t dma_addr1 ,
size_t length1 , dma_addr_t dma_addr2 , size_t length2 , int final )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
struct dma_async_tx_descriptor * in_desc ;
struct scatterlist sg [ 2 ] ;
dev_dbg ( dd - > dev , " xmit_dma: digcnt: 0x%llx 0x%llx, length: %d, final: %d \n " ,
ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] , length1 , final ) ;
if ( ctx - > flags & ( SHA_FLAGS_SHA1 | SHA_FLAGS_SHA224 |
SHA_FLAGS_SHA256 ) ) {
dd - > dma_lch_in . dma_conf . src_maxburst = 16 ;
dd - > dma_lch_in . dma_conf . dst_maxburst = 16 ;
} else {
dd - > dma_lch_in . dma_conf . src_maxburst = 32 ;
dd - > dma_lch_in . dma_conf . dst_maxburst = 32 ;
}
dmaengine_slave_config ( dd - > dma_lch_in . chan , & dd - > dma_lch_in . dma_conf ) ;
if ( length2 ) {
sg_init_table ( sg , 2 ) ;
sg_dma_address ( & sg [ 0 ] ) = dma_addr1 ;
sg_dma_len ( & sg [ 0 ] ) = length1 ;
sg_dma_address ( & sg [ 1 ] ) = dma_addr2 ;
sg_dma_len ( & sg [ 1 ] ) = length2 ;
in_desc = dmaengine_prep_slave_sg ( dd - > dma_lch_in . chan , sg , 2 ,
DMA_MEM_TO_DEV , DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
} else {
sg_init_table ( sg , 1 ) ;
sg_dma_address ( & sg [ 0 ] ) = dma_addr1 ;
sg_dma_len ( & sg [ 0 ] ) = length1 ;
in_desc = dmaengine_prep_slave_sg ( dd - > dma_lch_in . chan , sg , 1 ,
DMA_MEM_TO_DEV , DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
}
if ( ! in_desc )
return - EINVAL ;
in_desc - > callback = atmel_sha_dma_callback ;
in_desc - > callback_param = dd ;
atmel_sha_write_ctrl ( dd , 1 ) ;
/* should be non-zero before next lines to disable clocks later */
ctx - > digcnt [ 0 ] + = length1 ;
if ( ctx - > digcnt [ 0 ] < length1 )
ctx - > digcnt [ 1 ] + + ;
if ( final )
dd - > flags | = SHA_FLAGS_FINAL ; /* catch last interrupt */
dd - > flags | = SHA_FLAGS_DMA_ACTIVE ;
/* Start DMA transfer */
dmaengine_submit ( in_desc ) ;
dma_async_issue_pending ( dd - > dma_lch_in . chan ) ;
return - EINPROGRESS ;
}
static int atmel_sha_xmit_start ( struct atmel_sha_dev * dd , dma_addr_t dma_addr1 ,
size_t length1 , dma_addr_t dma_addr2 , size_t length2 , int final )
{
if ( dd - > caps . has_dma )
return atmel_sha_xmit_dma ( dd , dma_addr1 , length1 ,
dma_addr2 , length2 , final ) ;
else
return atmel_sha_xmit_pdc ( dd , dma_addr1 , length1 ,
dma_addr2 , length2 , final ) ;
}
2012-07-01 21:19:46 +04:00
static int atmel_sha_update_cpu ( struct atmel_sha_dev * dd )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
int bufcnt ;
atmel_sha_append_sg ( ctx ) ;
atmel_sha_fill_padding ( ctx , 0 ) ;
bufcnt = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
return atmel_sha_xmit_cpu ( dd , ctx - > buffer , bufcnt , 1 ) ;
}
static int atmel_sha_xmit_dma_map ( struct atmel_sha_dev * dd ,
struct atmel_sha_reqctx * ctx ,
size_t length , int final )
{
ctx - > dma_addr = dma_map_single ( dd - > dev , ctx - > buffer ,
2013-02-20 20:10:26 +04:00
ctx - > buflen + ctx - > block_size , DMA_TO_DEVICE ) ;
2012-07-01 21:19:46 +04:00
if ( dma_mapping_error ( dd - > dev , ctx - > dma_addr ) ) {
dev_err ( dd - > dev , " dma %u bytes error \n " , ctx - > buflen +
2013-02-20 20:10:26 +04:00
ctx - > block_size ) ;
2012-07-01 21:19:46 +04:00
return - EINVAL ;
}
ctx - > flags & = ~ SHA_FLAGS_SG ;
/* next call does not fail... so no unmap in the case of error */
2013-02-20 20:10:26 +04:00
return atmel_sha_xmit_start ( dd , ctx - > dma_addr , length , 0 , 0 , final ) ;
2012-07-01 21:19:46 +04:00
}
static int atmel_sha_update_dma_slow ( struct atmel_sha_dev * dd )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
unsigned int final ;
size_t count ;
atmel_sha_append_sg ( ctx ) ;
final = ( ctx - > flags & SHA_FLAGS_FINUP ) & & ! ctx - > total ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " slow: bufcnt: %u, digcnt: 0x%llx 0x%llx, final: %d \n " ,
ctx - > bufcnt , ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] , final ) ;
2012-07-01 21:19:46 +04:00
if ( final )
atmel_sha_fill_padding ( ctx , 0 ) ;
if ( final | | ( ctx - > bufcnt = = ctx - > buflen & & ctx - > total ) ) {
count = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
return atmel_sha_xmit_dma_map ( dd , ctx , count , final ) ;
}
return 0 ;
}
static int atmel_sha_update_dma_start ( struct atmel_sha_dev * dd )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
unsigned int length , final , tail ;
struct scatterlist * sg ;
unsigned int count ;
if ( ! ctx - > total )
return 0 ;
if ( ctx - > bufcnt | | ctx - > offset )
return atmel_sha_update_dma_slow ( dd ) ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " fast: digcnt: 0x%llx 0x%llx, bufcnt: %u, total: %u \n " ,
ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] , ctx - > bufcnt , ctx - > total ) ;
2012-07-01 21:19:46 +04:00
sg = ctx - > sg ;
if ( ! IS_ALIGNED ( sg - > offset , sizeof ( u32 ) ) )
return atmel_sha_update_dma_slow ( dd ) ;
2013-02-20 20:10:26 +04:00
if ( ! sg_is_last ( sg ) & & ! IS_ALIGNED ( sg - > length , ctx - > block_size ) )
/* size is not ctx->block_size aligned */
2012-07-01 21:19:46 +04:00
return atmel_sha_update_dma_slow ( dd ) ;
length = min ( ctx - > total , sg - > length ) ;
if ( sg_is_last ( sg ) ) {
if ( ! ( ctx - > flags & SHA_FLAGS_FINUP ) ) {
2013-02-20 20:10:26 +04:00
/* not last sg must be ctx->block_size aligned */
tail = length & ( ctx - > block_size - 1 ) ;
2012-07-01 21:19:46 +04:00
length - = tail ;
}
}
ctx - > total - = length ;
ctx - > offset = length ; /* offset where to start slow */
final = ( ctx - > flags & SHA_FLAGS_FINUP ) & & ! ctx - > total ;
/* Add padding */
if ( final ) {
2013-02-20 20:10:26 +04:00
tail = length & ( ctx - > block_size - 1 ) ;
2012-07-01 21:19:46 +04:00
length - = tail ;
ctx - > total + = tail ;
ctx - > offset = length ; /* offset where to start slow */
sg = ctx - > sg ;
atmel_sha_append_sg ( ctx ) ;
atmel_sha_fill_padding ( ctx , length ) ;
ctx - > dma_addr = dma_map_single ( dd - > dev , ctx - > buffer ,
2013-02-20 20:10:26 +04:00
ctx - > buflen + ctx - > block_size , DMA_TO_DEVICE ) ;
2012-07-01 21:19:46 +04:00
if ( dma_mapping_error ( dd - > dev , ctx - > dma_addr ) ) {
dev_err ( dd - > dev , " dma %u bytes error \n " ,
2013-02-20 20:10:26 +04:00
ctx - > buflen + ctx - > block_size ) ;
2012-07-01 21:19:46 +04:00
return - EINVAL ;
}
if ( length = = 0 ) {
ctx - > flags & = ~ SHA_FLAGS_SG ;
count = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
2013-02-20 20:10:26 +04:00
return atmel_sha_xmit_start ( dd , ctx - > dma_addr , count , 0 ,
2012-07-01 21:19:46 +04:00
0 , final ) ;
} else {
ctx - > sg = sg ;
if ( ! dma_map_sg ( dd - > dev , ctx - > sg , 1 ,
DMA_TO_DEVICE ) ) {
dev_err ( dd - > dev , " dma_map_sg error \n " ) ;
return - EINVAL ;
}
ctx - > flags | = SHA_FLAGS_SG ;
count = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
2013-02-20 20:10:26 +04:00
return atmel_sha_xmit_start ( dd , sg_dma_address ( ctx - > sg ) ,
2012-07-01 21:19:46 +04:00
length , ctx - > dma_addr , count , final ) ;
}
}
if ( ! dma_map_sg ( dd - > dev , ctx - > sg , 1 , DMA_TO_DEVICE ) ) {
dev_err ( dd - > dev , " dma_map_sg error \n " ) ;
return - EINVAL ;
}
ctx - > flags | = SHA_FLAGS_SG ;
/* next call does not fail... so no unmap in the case of error */
2013-02-20 20:10:26 +04:00
return atmel_sha_xmit_start ( dd , sg_dma_address ( ctx - > sg ) , length , 0 ,
2012-07-01 21:19:46 +04:00
0 , final ) ;
}
static int atmel_sha_update_dma_stop ( struct atmel_sha_dev * dd )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( dd - > req ) ;
if ( ctx - > flags & SHA_FLAGS_SG ) {
dma_unmap_sg ( dd - > dev , ctx - > sg , 1 , DMA_TO_DEVICE ) ;
if ( ctx - > sg - > length = = ctx - > offset ) {
ctx - > sg = sg_next ( ctx - > sg ) ;
if ( ctx - > sg )
ctx - > offset = 0 ;
}
2013-02-20 20:10:26 +04:00
if ( ctx - > flags & SHA_FLAGS_PAD ) {
2012-07-01 21:19:46 +04:00
dma_unmap_single ( dd - > dev , ctx - > dma_addr ,
2013-02-20 20:10:26 +04:00
ctx - > buflen + ctx - > block_size , DMA_TO_DEVICE ) ;
}
2012-07-01 21:19:46 +04:00
} else {
dma_unmap_single ( dd - > dev , ctx - > dma_addr , ctx - > buflen +
2013-02-20 20:10:26 +04:00
ctx - > block_size , DMA_TO_DEVICE ) ;
2012-07-01 21:19:46 +04:00
}
return 0 ;
}
static int atmel_sha_update_req ( struct atmel_sha_dev * dd )
{
struct ahash_request * req = dd - > req ;
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
int err ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " update_req: total: %u, digcnt: 0x%llx 0x%llx \n " ,
ctx - > total , ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] ) ;
2012-07-01 21:19:46 +04:00
if ( ctx - > flags & SHA_FLAGS_CPU )
err = atmel_sha_update_cpu ( dd ) ;
else
err = atmel_sha_update_dma_start ( dd ) ;
/* wait for dma completion before can take more data */
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " update: err: %d, digcnt: 0x%llx 0%llx \n " ,
err , ctx - > digcnt [ 1 ] , ctx - > digcnt [ 0 ] ) ;
2012-07-01 21:19:46 +04:00
return err ;
}
static int atmel_sha_final_req ( struct atmel_sha_dev * dd )
{
struct ahash_request * req = dd - > req ;
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
int err = 0 ;
int count ;
if ( ctx - > bufcnt > = ATMEL_SHA_DMA_THRESHOLD ) {
atmel_sha_fill_padding ( ctx , 0 ) ;
count = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
err = atmel_sha_xmit_dma_map ( dd , ctx , count , 1 ) ;
}
/* faster to handle last block with cpu */
else {
atmel_sha_fill_padding ( ctx , 0 ) ;
count = ctx - > bufcnt ;
ctx - > bufcnt = 0 ;
err = atmel_sha_xmit_cpu ( dd , ctx - > buffer , count , 1 ) ;
}
dev_dbg ( dd - > dev , " final_req: err: %d \n " , err ) ;
return err ;
}
static void atmel_sha_copy_hash ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
u32 * hash = ( u32 * ) ctx - > digest ;
int i ;
2013-02-20 20:10:26 +04:00
if ( ctx - > flags & SHA_FLAGS_SHA1 )
2012-07-01 21:19:46 +04:00
for ( i = 0 ; i < SHA1_DIGEST_SIZE / sizeof ( u32 ) ; i + + )
hash [ i ] = atmel_sha_read ( ctx - > dd , SHA_REG_DIGEST ( i ) ) ;
2013-02-20 20:10:26 +04:00
else if ( ctx - > flags & SHA_FLAGS_SHA224 )
for ( i = 0 ; i < SHA224_DIGEST_SIZE / sizeof ( u32 ) ; i + + )
hash [ i ] = atmel_sha_read ( ctx - > dd , SHA_REG_DIGEST ( i ) ) ;
else if ( ctx - > flags & SHA_FLAGS_SHA256 )
2012-07-01 21:19:46 +04:00
for ( i = 0 ; i < SHA256_DIGEST_SIZE / sizeof ( u32 ) ; i + + )
hash [ i ] = atmel_sha_read ( ctx - > dd , SHA_REG_DIGEST ( i ) ) ;
2013-02-20 20:10:26 +04:00
else if ( ctx - > flags & SHA_FLAGS_SHA384 )
for ( i = 0 ; i < SHA384_DIGEST_SIZE / sizeof ( u32 ) ; i + + )
hash [ i ] = atmel_sha_read ( ctx - > dd , SHA_REG_DIGEST ( i ) ) ;
else
for ( i = 0 ; i < SHA512_DIGEST_SIZE / sizeof ( u32 ) ; i + + )
hash [ i ] = atmel_sha_read ( ctx - > dd , SHA_REG_DIGEST ( i ) ) ;
2012-07-01 21:19:46 +04:00
}
static void atmel_sha_copy_ready_hash ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
if ( ! req - > result )
return ;
2013-02-20 20:10:26 +04:00
if ( ctx - > flags & SHA_FLAGS_SHA1 )
2012-07-01 21:19:46 +04:00
memcpy ( req - > result , ctx - > digest , SHA1_DIGEST_SIZE ) ;
2013-02-20 20:10:26 +04:00
else if ( ctx - > flags & SHA_FLAGS_SHA224 )
memcpy ( req - > result , ctx - > digest , SHA224_DIGEST_SIZE ) ;
else if ( ctx - > flags & SHA_FLAGS_SHA256 )
2012-07-01 21:19:46 +04:00
memcpy ( req - > result , ctx - > digest , SHA256_DIGEST_SIZE ) ;
2013-02-20 20:10:26 +04:00
else if ( ctx - > flags & SHA_FLAGS_SHA384 )
memcpy ( req - > result , ctx - > digest , SHA384_DIGEST_SIZE ) ;
else
memcpy ( req - > result , ctx - > digest , SHA512_DIGEST_SIZE ) ;
2012-07-01 21:19:46 +04:00
}
static int atmel_sha_finish ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
struct atmel_sha_dev * dd = ctx - > dd ;
int err = 0 ;
2013-02-20 20:10:26 +04:00
if ( ctx - > digcnt [ 0 ] | | ctx - > digcnt [ 1 ] )
2012-07-01 21:19:46 +04:00
atmel_sha_copy_ready_hash ( req ) ;
2013-02-20 20:10:26 +04:00
dev_dbg ( dd - > dev , " digcnt: 0x%llx 0x%llx, bufcnt: %d \n " , ctx - > digcnt [ 1 ] ,
ctx - > digcnt [ 0 ] , ctx - > bufcnt ) ;
2012-07-01 21:19:46 +04:00
return err ;
}
static void atmel_sha_finish_req ( struct ahash_request * req , int err )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
struct atmel_sha_dev * dd = ctx - > dd ;
if ( ! err ) {
atmel_sha_copy_hash ( req ) ;
if ( SHA_FLAGS_FINAL & dd - > flags )
err = atmel_sha_finish ( req ) ;
} else {
ctx - > flags | = SHA_FLAGS_ERROR ;
}
/* atomic operation is not needed here */
dd - > flags & = ~ ( SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU |
SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY ) ;
clk_disable_unprepare ( dd - > iclk ) ;
if ( req - > base . complete )
req - > base . complete ( & req - > base , err ) ;
/* handle new request */
tasklet_schedule ( & dd - > done_task ) ;
}
static int atmel_sha_hw_init ( struct atmel_sha_dev * dd )
{
clk_prepare_enable ( dd - > iclk ) ;
2013-02-20 20:10:26 +04:00
if ( ! ( SHA_FLAGS_INIT & dd - > flags ) ) {
2012-07-01 21:19:46 +04:00
atmel_sha_write ( dd , SHA_CR , SHA_CR_SWRST ) ;
dd - > flags | = SHA_FLAGS_INIT ;
dd - > err = 0 ;
}
return 0 ;
}
2013-02-20 20:10:26 +04:00
static inline unsigned int atmel_sha_get_version ( struct atmel_sha_dev * dd )
{
return atmel_sha_read ( dd , SHA_HW_VERSION ) & 0x00000fff ;
}
static void atmel_sha_hw_version_init ( struct atmel_sha_dev * dd )
{
atmel_sha_hw_init ( dd ) ;
dd - > hw_version = atmel_sha_get_version ( dd ) ;
dev_info ( dd - > dev ,
" version: 0x%x \n " , dd - > hw_version ) ;
clk_disable_unprepare ( dd - > iclk ) ;
}
2012-07-01 21:19:46 +04:00
static int atmel_sha_handle_queue ( struct atmel_sha_dev * dd ,
struct ahash_request * req )
{
struct crypto_async_request * async_req , * backlog ;
struct atmel_sha_reqctx * ctx ;
unsigned long flags ;
int err = 0 , ret = 0 ;
spin_lock_irqsave ( & dd - > lock , flags ) ;
if ( req )
ret = ahash_enqueue_request ( & dd - > queue , req ) ;
if ( SHA_FLAGS_BUSY & dd - > flags ) {
spin_unlock_irqrestore ( & dd - > lock , flags ) ;
return ret ;
}
backlog = crypto_get_backlog ( & dd - > queue ) ;
async_req = crypto_dequeue_request ( & dd - > queue ) ;
if ( async_req )
dd - > flags | = SHA_FLAGS_BUSY ;
spin_unlock_irqrestore ( & dd - > lock , flags ) ;
if ( ! async_req )
return ret ;
if ( backlog )
backlog - > complete ( backlog , - EINPROGRESS ) ;
req = ahash_request_cast ( async_req ) ;
dd - > req = req ;
ctx = ahash_request_ctx ( req ) ;
dev_dbg ( dd - > dev , " handling new req, op: %lu, nbytes: %d \n " ,
ctx - > op , req - > nbytes ) ;
err = atmel_sha_hw_init ( dd ) ;
if ( err )
goto err1 ;
if ( ctx - > op = = SHA_OP_UPDATE ) {
err = atmel_sha_update_req ( dd ) ;
2013-02-20 20:10:26 +04:00
if ( err ! = - EINPROGRESS & & ( ctx - > flags & SHA_FLAGS_FINUP ) )
2012-07-01 21:19:46 +04:00
/* no final() after finup() */
err = atmel_sha_final_req ( dd ) ;
} else if ( ctx - > op = = SHA_OP_FINAL ) {
err = atmel_sha_final_req ( dd ) ;
}
err1 :
if ( err ! = - EINPROGRESS )
/* done_task will not finish it, so do it here */
atmel_sha_finish_req ( req , err ) ;
dev_dbg ( dd - > dev , " exit, err: %d \n " , err ) ;
return ret ;
}
static int atmel_sha_enqueue ( struct ahash_request * req , unsigned int op )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
struct atmel_sha_ctx * tctx = crypto_tfm_ctx ( req - > base . tfm ) ;
struct atmel_sha_dev * dd = tctx - > dd ;
ctx - > op = op ;
return atmel_sha_handle_queue ( dd , req ) ;
}
static int atmel_sha_update ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
if ( ! req - > nbytes )
return 0 ;
ctx - > total = req - > nbytes ;
ctx - > sg = req - > src ;
ctx - > offset = 0 ;
if ( ctx - > flags & SHA_FLAGS_FINUP ) {
if ( ctx - > bufcnt + ctx - > total < ATMEL_SHA_DMA_THRESHOLD )
/* faster to use CPU for short transfers */
ctx - > flags | = SHA_FLAGS_CPU ;
} else if ( ctx - > bufcnt + ctx - > total < ctx - > buflen ) {
atmel_sha_append_sg ( ctx ) ;
return 0 ;
}
return atmel_sha_enqueue ( req , SHA_OP_UPDATE ) ;
}
static int atmel_sha_final ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
struct atmel_sha_ctx * tctx = crypto_tfm_ctx ( req - > base . tfm ) ;
struct atmel_sha_dev * dd = tctx - > dd ;
int err = 0 ;
ctx - > flags | = SHA_FLAGS_FINUP ;
if ( ctx - > flags & SHA_FLAGS_ERROR )
return 0 ; /* uncompleted hash is not needed */
if ( ctx - > bufcnt ) {
return atmel_sha_enqueue ( req , SHA_OP_FINAL ) ;
} else if ( ! ( ctx - > flags & SHA_FLAGS_PAD ) ) { /* add padding */
err = atmel_sha_hw_init ( dd ) ;
if ( err )
goto err1 ;
dd - > flags | = SHA_FLAGS_BUSY ;
err = atmel_sha_final_req ( dd ) ;
} else {
/* copy ready hash (+ finalize hmac) */
return atmel_sha_finish ( req ) ;
}
err1 :
if ( err ! = - EINPROGRESS )
/* done_task will not finish it, so do it here */
atmel_sha_finish_req ( req , err ) ;
return err ;
}
static int atmel_sha_finup ( struct ahash_request * req )
{
struct atmel_sha_reqctx * ctx = ahash_request_ctx ( req ) ;
int err1 , err2 ;
ctx - > flags | = SHA_FLAGS_FINUP ;
err1 = atmel_sha_update ( req ) ;
if ( err1 = = - EINPROGRESS | | err1 = = - EBUSY )
return err1 ;
/*
* final ( ) has to be always called to cleanup resources
* even if udpate ( ) failed , except EINPROGRESS
*/
err2 = atmel_sha_final ( req ) ;
return err1 ? : err2 ;
}
static int atmel_sha_digest ( struct ahash_request * req )
{
return atmel_sha_init ( req ) ? : atmel_sha_finup ( req ) ;
}
static int atmel_sha_cra_init_alg ( struct crypto_tfm * tfm , const char * alg_base )
{
struct atmel_sha_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
const char * alg_name = crypto_tfm_alg_name ( tfm ) ;
/* Allocate a fallback and abort if it failed. */
tctx - > fallback = crypto_alloc_shash ( alg_name , 0 ,
CRYPTO_ALG_NEED_FALLBACK ) ;
if ( IS_ERR ( tctx - > fallback ) ) {
pr_err ( " atmel-sha: fallback driver '%s' could not be loaded. \n " ,
alg_name ) ;
return PTR_ERR ( tctx - > fallback ) ;
}
crypto_ahash_set_reqsize ( __crypto_ahash_cast ( tfm ) ,
sizeof ( struct atmel_sha_reqctx ) +
2013-02-20 20:10:26 +04:00
SHA_BUFFER_LEN + SHA512_BLOCK_SIZE ) ;
2012-07-01 21:19:46 +04:00
return 0 ;
}
static int atmel_sha_cra_init ( struct crypto_tfm * tfm )
{
return atmel_sha_cra_init_alg ( tfm , NULL ) ;
}
static void atmel_sha_cra_exit ( struct crypto_tfm * tfm )
{
struct atmel_sha_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
crypto_free_shash ( tctx - > fallback ) ;
tctx - > fallback = NULL ;
}
2013-02-20 20:10:26 +04:00
static struct ahash_alg sha_1_256_algs [ ] = {
2012-07-01 21:19:46 +04:00
{
. init = atmel_sha_init ,
. update = atmel_sha_update ,
. final = atmel_sha_final ,
. finup = atmel_sha_finup ,
. digest = atmel_sha_digest ,
. halg = {
. digestsize = SHA1_DIGEST_SIZE ,
. base = {
. cra_name = " sha1 " ,
. cra_driver_name = " atmel-sha1 " ,
. cra_priority = 100 ,
. cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = SHA1_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct atmel_sha_ctx ) ,
. cra_alignmask = 0 ,
. cra_module = THIS_MODULE ,
. cra_init = atmel_sha_cra_init ,
. cra_exit = atmel_sha_cra_exit ,
}
}
} ,
{
. init = atmel_sha_init ,
. update = atmel_sha_update ,
. final = atmel_sha_final ,
. finup = atmel_sha_finup ,
. digest = atmel_sha_digest ,
. halg = {
. digestsize = SHA256_DIGEST_SIZE ,
. base = {
. cra_name = " sha256 " ,
. cra_driver_name = " atmel-sha256 " ,
. cra_priority = 100 ,
. cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = SHA256_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct atmel_sha_ctx ) ,
. cra_alignmask = 0 ,
. cra_module = THIS_MODULE ,
. cra_init = atmel_sha_cra_init ,
. cra_exit = atmel_sha_cra_exit ,
}
}
} ,
} ;
2013-02-20 20:10:26 +04:00
static struct ahash_alg sha_224_alg = {
. init = atmel_sha_init ,
. update = atmel_sha_update ,
. final = atmel_sha_final ,
. finup = atmel_sha_finup ,
. digest = atmel_sha_digest ,
. halg = {
. digestsize = SHA224_DIGEST_SIZE ,
. base = {
. cra_name = " sha224 " ,
. cra_driver_name = " atmel-sha224 " ,
. cra_priority = 100 ,
. cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = SHA224_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct atmel_sha_ctx ) ,
. cra_alignmask = 0 ,
. cra_module = THIS_MODULE ,
. cra_init = atmel_sha_cra_init ,
. cra_exit = atmel_sha_cra_exit ,
}
}
} ;
static struct ahash_alg sha_384_512_algs [ ] = {
{
. init = atmel_sha_init ,
. update = atmel_sha_update ,
. final = atmel_sha_final ,
. finup = atmel_sha_finup ,
. digest = atmel_sha_digest ,
. halg = {
. digestsize = SHA384_DIGEST_SIZE ,
. base = {
. cra_name = " sha384 " ,
. cra_driver_name = " atmel-sha384 " ,
. cra_priority = 100 ,
. cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = SHA384_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct atmel_sha_ctx ) ,
. cra_alignmask = 0x3 ,
. cra_module = THIS_MODULE ,
. cra_init = atmel_sha_cra_init ,
. cra_exit = atmel_sha_cra_exit ,
}
}
} ,
{
. init = atmel_sha_init ,
. update = atmel_sha_update ,
. final = atmel_sha_final ,
. finup = atmel_sha_finup ,
. digest = atmel_sha_digest ,
. halg = {
. digestsize = SHA512_DIGEST_SIZE ,
. base = {
. cra_name = " sha512 " ,
. cra_driver_name = " atmel-sha512 " ,
. cra_priority = 100 ,
. cra_flags = CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = SHA512_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct atmel_sha_ctx ) ,
. cra_alignmask = 0x3 ,
. cra_module = THIS_MODULE ,
. cra_init = atmel_sha_cra_init ,
. cra_exit = atmel_sha_cra_exit ,
}
}
} ,
} ;
2012-07-01 21:19:46 +04:00
static void atmel_sha_done_task ( unsigned long data )
{
struct atmel_sha_dev * dd = ( struct atmel_sha_dev * ) data ;
int err = 0 ;
if ( ! ( SHA_FLAGS_BUSY & dd - > flags ) ) {
atmel_sha_handle_queue ( dd , NULL ) ;
return ;
}
if ( SHA_FLAGS_CPU & dd - > flags ) {
if ( SHA_FLAGS_OUTPUT_READY & dd - > flags ) {
dd - > flags & = ~ SHA_FLAGS_OUTPUT_READY ;
goto finish ;
}
} else if ( SHA_FLAGS_DMA_READY & dd - > flags ) {
if ( SHA_FLAGS_DMA_ACTIVE & dd - > flags ) {
dd - > flags & = ~ SHA_FLAGS_DMA_ACTIVE ;
atmel_sha_update_dma_stop ( dd ) ;
if ( dd - > err ) {
err = dd - > err ;
goto finish ;
}
}
if ( SHA_FLAGS_OUTPUT_READY & dd - > flags ) {
/* hash or semi-hash ready */
dd - > flags & = ~ ( SHA_FLAGS_DMA_READY |
SHA_FLAGS_OUTPUT_READY ) ;
err = atmel_sha_update_dma_start ( dd ) ;
if ( err ! = - EINPROGRESS )
goto finish ;
}
}
return ;
finish :
/* finish curent request */
atmel_sha_finish_req ( dd - > req , err ) ;
}
static irqreturn_t atmel_sha_irq ( int irq , void * dev_id )
{
struct atmel_sha_dev * sha_dd = dev_id ;
u32 reg ;
reg = atmel_sha_read ( sha_dd , SHA_ISR ) ;
if ( reg & atmel_sha_read ( sha_dd , SHA_IMR ) ) {
atmel_sha_write ( sha_dd , SHA_IDR , reg ) ;
if ( SHA_FLAGS_BUSY & sha_dd - > flags ) {
sha_dd - > flags | = SHA_FLAGS_OUTPUT_READY ;
if ( ! ( SHA_FLAGS_CPU & sha_dd - > flags ) )
sha_dd - > flags | = SHA_FLAGS_DMA_READY ;
tasklet_schedule ( & sha_dd - > done_task ) ;
} else {
dev_warn ( sha_dd - > dev , " SHA interrupt when no active requests. \n " ) ;
}
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
static void atmel_sha_unregister_algs ( struct atmel_sha_dev * dd )
{
int i ;
2013-02-20 20:10:26 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( sha_1_256_algs ) ; i + + )
crypto_unregister_ahash ( & sha_1_256_algs [ i ] ) ;
if ( dd - > caps . has_sha224 )
crypto_unregister_ahash ( & sha_224_alg ) ;
if ( dd - > caps . has_sha_384_512 ) {
for ( i = 0 ; i < ARRAY_SIZE ( sha_384_512_algs ) ; i + + )
crypto_unregister_ahash ( & sha_384_512_algs [ i ] ) ;
}
2012-07-01 21:19:46 +04:00
}
static int atmel_sha_register_algs ( struct atmel_sha_dev * dd )
{
int err , i , j ;
2013-02-20 20:10:26 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( sha_1_256_algs ) ; i + + ) {
err = crypto_register_ahash ( & sha_1_256_algs [ i ] ) ;
2012-07-01 21:19:46 +04:00
if ( err )
2013-02-20 20:10:26 +04:00
goto err_sha_1_256_algs ;
}
if ( dd - > caps . has_sha224 ) {
err = crypto_register_ahash ( & sha_224_alg ) ;
if ( err )
goto err_sha_224_algs ;
}
if ( dd - > caps . has_sha_384_512 ) {
for ( i = 0 ; i < ARRAY_SIZE ( sha_384_512_algs ) ; i + + ) {
err = crypto_register_ahash ( & sha_384_512_algs [ i ] ) ;
if ( err )
goto err_sha_384_512_algs ;
}
2012-07-01 21:19:46 +04:00
}
return 0 ;
2013-02-20 20:10:26 +04:00
err_sha_384_512_algs :
for ( j = 0 ; j < i ; j + + )
crypto_unregister_ahash ( & sha_384_512_algs [ j ] ) ;
crypto_unregister_ahash ( & sha_224_alg ) ;
err_sha_224_algs :
i = ARRAY_SIZE ( sha_1_256_algs ) ;
err_sha_1_256_algs :
2012-07-01 21:19:46 +04:00
for ( j = 0 ; j < i ; j + + )
2013-02-20 20:10:26 +04:00
crypto_unregister_ahash ( & sha_1_256_algs [ j ] ) ;
2012-07-01 21:19:46 +04:00
return err ;
}
2013-02-20 20:10:26 +04:00
static bool atmel_sha_filter ( struct dma_chan * chan , void * slave )
{
struct at_dma_slave * sl = slave ;
if ( sl & & sl - > dma_dev = = chan - > device - > dev ) {
chan - > private = sl ;
return true ;
} else {
return false ;
}
}
static int atmel_sha_dma_init ( struct atmel_sha_dev * dd ,
struct crypto_platform_data * pdata )
{
int err = - ENOMEM ;
dma_cap_mask_t mask_in ;
2013-10-15 17:36:34 +04:00
/* Try to grab DMA channel */
dma_cap_zero ( mask_in ) ;
dma_cap_set ( DMA_SLAVE , mask_in ) ;
2013-02-20 20:10:26 +04:00
2013-10-15 17:36:34 +04:00
dd - > dma_lch_in . chan = dma_request_slave_channel_compat ( mask_in ,
atmel_sha_filter , & pdata - > dma_slave - > rxdata , dd - > dev , " tx " ) ;
if ( ! dd - > dma_lch_in . chan ) {
dev_warn ( dd - > dev , " no DMA channel available \n " ) ;
return err ;
2013-02-20 20:10:26 +04:00
}
2013-10-15 17:36:34 +04:00
dd - > dma_lch_in . dma_conf . direction = DMA_MEM_TO_DEV ;
dd - > dma_lch_in . dma_conf . dst_addr = dd - > phys_base +
SHA_REG_DIN ( 0 ) ;
dd - > dma_lch_in . dma_conf . src_maxburst = 1 ;
dd - > dma_lch_in . dma_conf . src_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES ;
dd - > dma_lch_in . dma_conf . dst_maxburst = 1 ;
dd - > dma_lch_in . dma_conf . dst_addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES ;
dd - > dma_lch_in . dma_conf . device_fc = false ;
return 0 ;
2013-02-20 20:10:26 +04:00
}
static void atmel_sha_dma_cleanup ( struct atmel_sha_dev * dd )
{
dma_release_channel ( dd - > dma_lch_in . chan ) ;
}
static void atmel_sha_get_cap ( struct atmel_sha_dev * dd )
{
dd - > caps . has_dma = 0 ;
dd - > caps . has_dualbuff = 0 ;
dd - > caps . has_sha224 = 0 ;
dd - > caps . has_sha_384_512 = 0 ;
/* keep only major version number */
switch ( dd - > hw_version & 0xff0 ) {
case 0x410 :
dd - > caps . has_dma = 1 ;
dd - > caps . has_dualbuff = 1 ;
dd - > caps . has_sha224 = 1 ;
dd - > caps . has_sha_384_512 = 1 ;
break ;
case 0x400 :
dd - > caps . has_dma = 1 ;
dd - > caps . has_dualbuff = 1 ;
dd - > caps . has_sha224 = 1 ;
break ;
case 0x320 :
break ;
default :
dev_warn ( dd - > dev ,
" Unmanaged sha version, set minimum capabilities \n " ) ;
break ;
}
}
2013-10-15 17:36:34 +04:00
# if defined(CONFIG_OF)
static const struct of_device_id atmel_sha_dt_ids [ ] = {
{ . compatible = " atmel,at91sam9g46-sha " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , atmel_sha_dt_ids ) ;
static struct crypto_platform_data * atmel_sha_of_init ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct crypto_platform_data * pdata ;
if ( ! np ) {
dev_err ( & pdev - > dev , " device node not found \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " could not allocate memory for pdata \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
pdata - > dma_slave = devm_kzalloc ( & pdev - > dev ,
sizeof ( * ( pdata - > dma_slave ) ) ,
GFP_KERNEL ) ;
if ( ! pdata - > dma_slave ) {
dev_err ( & pdev - > dev , " could not allocate memory for dma_slave \n " ) ;
devm_kfree ( & pdev - > dev , pdata ) ;
return ERR_PTR ( - ENOMEM ) ;
}
return pdata ;
}
# else /* CONFIG_OF */
static inline struct crypto_platform_data * atmel_sha_of_init ( struct platform_device * dev )
{
return ERR_PTR ( - EINVAL ) ;
}
# endif
2012-12-22 01:14:09 +04:00
static int atmel_sha_probe ( struct platform_device * pdev )
2012-07-01 21:19:46 +04:00
{
struct atmel_sha_dev * sha_dd ;
2013-02-20 20:10:26 +04:00
struct crypto_platform_data * pdata ;
2012-07-01 21:19:46 +04:00
struct device * dev = & pdev - > dev ;
struct resource * sha_res ;
unsigned long sha_phys_size ;
int err ;
sha_dd = kzalloc ( sizeof ( struct atmel_sha_dev ) , GFP_KERNEL ) ;
if ( sha_dd = = NULL ) {
dev_err ( dev , " unable to alloc data struct. \n " ) ;
err = - ENOMEM ;
goto sha_dd_err ;
}
sha_dd - > dev = dev ;
platform_set_drvdata ( pdev , sha_dd ) ;
INIT_LIST_HEAD ( & sha_dd - > list ) ;
tasklet_init ( & sha_dd - > done_task , atmel_sha_done_task ,
( unsigned long ) sha_dd ) ;
crypto_init_queue ( & sha_dd - > queue , ATMEL_SHA_QUEUE_LENGTH ) ;
sha_dd - > irq = - 1 ;
/* Get the base address */
sha_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! sha_res ) {
dev_err ( dev , " no MEM resource info \n " ) ;
err = - ENODEV ;
goto res_err ;
}
sha_dd - > phys_base = sha_res - > start ;
sha_phys_size = resource_size ( sha_res ) ;
/* Get the IRQ */
sha_dd - > irq = platform_get_irq ( pdev , 0 ) ;
if ( sha_dd - > irq < 0 ) {
dev_err ( dev , " no IRQ resource info \n " ) ;
err = sha_dd - > irq ;
goto res_err ;
}
err = request_irq ( sha_dd - > irq , atmel_sha_irq , IRQF_SHARED , " atmel-sha " ,
sha_dd ) ;
if ( err ) {
dev_err ( dev , " unable to request sha irq. \n " ) ;
goto res_err ;
}
/* Initializing the clock */
2013-02-20 20:10:26 +04:00
sha_dd - > iclk = clk_get ( & pdev - > dev , " sha_clk " ) ;
2012-07-01 21:19:46 +04:00
if ( IS_ERR ( sha_dd - > iclk ) ) {
dev_err ( dev , " clock intialization failed. \n " ) ;
err = PTR_ERR ( sha_dd - > iclk ) ;
goto clk_err ;
}
sha_dd - > io_base = ioremap ( sha_dd - > phys_base , sha_phys_size ) ;
if ( ! sha_dd - > io_base ) {
dev_err ( dev , " can't ioremap \n " ) ;
err = - ENOMEM ;
goto sha_io_err ;
}
2013-02-20 20:10:26 +04:00
atmel_sha_hw_version_init ( sha_dd ) ;
atmel_sha_get_cap ( sha_dd ) ;
if ( sha_dd - > caps . has_dma ) {
pdata = pdev - > dev . platform_data ;
if ( ! pdata ) {
2013-10-15 17:36:34 +04:00
pdata = atmel_sha_of_init ( pdev ) ;
if ( IS_ERR ( pdata ) ) {
dev_err ( & pdev - > dev , " platform data not available \n " ) ;
err = PTR_ERR ( pdata ) ;
goto err_pdata ;
}
}
if ( ! pdata - > dma_slave ) {
2013-02-20 20:10:26 +04:00
err = - ENXIO ;
goto err_pdata ;
}
err = atmel_sha_dma_init ( sha_dd , pdata ) ;
if ( err )
goto err_sha_dma ;
2013-10-15 17:36:34 +04:00
dev_info ( dev , " using %s for DMA transfers \n " ,
dma_chan_name ( sha_dd - > dma_lch_in . chan ) ) ;
2013-02-20 20:10:26 +04:00
}
2012-07-01 21:19:46 +04:00
spin_lock ( & atmel_sha . lock ) ;
list_add_tail ( & sha_dd - > list , & atmel_sha . dev_list ) ;
spin_unlock ( & atmel_sha . lock ) ;
err = atmel_sha_register_algs ( sha_dd ) ;
if ( err )
goto err_algs ;
2013-10-15 18:37:44 +04:00
dev_info ( dev , " Atmel SHA1/SHA256%s%s \n " ,
sha_dd - > caps . has_sha224 ? " /SHA224 " : " " ,
sha_dd - > caps . has_sha_384_512 ? " /SHA384/SHA512 " : " " ) ;
2012-07-01 21:19:46 +04:00
return 0 ;
err_algs :
spin_lock ( & atmel_sha . lock ) ;
list_del ( & sha_dd - > list ) ;
spin_unlock ( & atmel_sha . lock ) ;
2013-02-20 20:10:26 +04:00
if ( sha_dd - > caps . has_dma )
atmel_sha_dma_cleanup ( sha_dd ) ;
err_sha_dma :
err_pdata :
2012-07-01 21:19:46 +04:00
iounmap ( sha_dd - > io_base ) ;
sha_io_err :
clk_put ( sha_dd - > iclk ) ;
clk_err :
free_irq ( sha_dd - > irq , sha_dd ) ;
res_err :
tasklet_kill ( & sha_dd - > done_task ) ;
kfree ( sha_dd ) ;
sha_dd = NULL ;
sha_dd_err :
dev_err ( dev , " initialization failed. \n " ) ;
return err ;
}
2012-12-22 01:14:09 +04:00
static int atmel_sha_remove ( struct platform_device * pdev )
2012-07-01 21:19:46 +04:00
{
static struct atmel_sha_dev * sha_dd ;
sha_dd = platform_get_drvdata ( pdev ) ;
if ( ! sha_dd )
return - ENODEV ;
spin_lock ( & atmel_sha . lock ) ;
list_del ( & sha_dd - > list ) ;
spin_unlock ( & atmel_sha . lock ) ;
atmel_sha_unregister_algs ( sha_dd ) ;
tasklet_kill ( & sha_dd - > done_task ) ;
2013-02-20 20:10:26 +04:00
if ( sha_dd - > caps . has_dma )
atmel_sha_dma_cleanup ( sha_dd ) ;
2012-07-01 21:19:46 +04:00
iounmap ( sha_dd - > io_base ) ;
clk_put ( sha_dd - > iclk ) ;
if ( sha_dd - > irq > = 0 )
free_irq ( sha_dd - > irq , sha_dd ) ;
kfree ( sha_dd ) ;
sha_dd = NULL ;
return 0 ;
}
static struct platform_driver atmel_sha_driver = {
. probe = atmel_sha_probe ,
2012-12-22 01:14:09 +04:00
. remove = atmel_sha_remove ,
2012-07-01 21:19:46 +04:00
. driver = {
. name = " atmel_sha " ,
. owner = THIS_MODULE ,
2013-10-15 17:36:34 +04:00
. of_match_table = of_match_ptr ( atmel_sha_dt_ids ) ,
2012-07-01 21:19:46 +04:00
} ,
} ;
module_platform_driver ( atmel_sha_driver ) ;
2013-02-20 20:10:26 +04:00
MODULE_DESCRIPTION ( " Atmel SHA (1/256/224/384/512) hw acceleration support. " ) ;
2012-07-01 21:19:46 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Nicolas Royer - Eukréa Electromatique " ) ;