2012-06-04 08:24:47 +04:00
/*
* 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/dma.h>
# include <asm/portmux.h>
2014-04-10 10:30:56 +04:00
# include <asm/io.h>
# include "bfin_crc.h"
2012-06-04 08:24:47 +04:00
# 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 ;
2014-04-10 10:30:56 +04:00
struct crc_register * regs ;
2012-06-04 08:24:47 +04:00
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 ;
2014-04-10 12:40:59 +04:00
dma_addr_t sg_mid_dma ; /* phy addr of sg mid buffer */
2012-06-04 08:24:47 +04:00
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 )
{
2014-04-10 10:30:56 +04:00
writel ( 0 , & crc - > regs - > datacntrld ) ;
writel ( MODE_CALC_CRC < < OPMODE_OFFSET , & crc - > regs - > control ) ;
writel ( key , & crc - > regs - > curresult ) ;
2012-06-04 08:24:47 +04:00
/* setup CRC interrupts */
2014-04-10 10:30:56 +04:00
writel ( CMPERRI | DCNTEXPI , & crc - > regs - > status ) ;
writel ( CMPERRI | DCNTEXPI , & crc - > regs - > intrenset ) ;
2012-06-04 08:24:47 +04:00
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 ;
2013-02-25 02:27:39 +04:00
dev_dbg ( ctx - > crc - > dev , " crc_init \n " ) ;
2012-06-04 08:24:47 +04:00
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 ) {
2013-02-25 02:27:39 +04:00
dev_dbg ( ctx - > crc - > dev , " init: requested sg list is too big > %d \n " ,
2012-06-04 08:24:47 +04:00
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 ) ;
2013-02-25 02:27:39 +04:00
dev_dbg ( ctx - > crc - > dev , " init: digest size: %d \n " ,
2012-06-04 08:24:47 +04:00
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_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 .
*/
2014-04-10 12:40:59 +04:00
memcpy ( crc - > sg_mid_buf + ( i < < 2 ) + mid_dma_count ,
sg_virt ( sg ) ,
2012-06-04 08:24:47 +04:00
CHKSUM_DIGEST_SIZE - mid_dma_count ) ;
dma_addr + = CHKSUM_DIGEST_SIZE - mid_dma_count ;
dma_count - = CHKSUM_DIGEST_SIZE - mid_dma_count ;
2014-04-10 12:40:59 +04:00
dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 |
DMAEN | PSIZE_32 | WDSIZE_32 ;
/* setup new dma descriptor for next middle dma */
crc - > sg_cpu [ i ] . start_addr = crc - > sg_mid_dma + ( i < < 2 ) ;
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 + + ;
2012-06-04 08:24:47 +04:00
}
2014-04-10 12:40:59 +04:00
dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 | DMAEN | PSIZE_32 ;
2012-06-04 08:24:47 +04:00
/* 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 */
memcpy ( crc - > sg_mid_buf + ( i < < 2 ) ,
2014-04-10 12:40:59 +04:00
( u8 * ) sg_virt ( sg ) + ( dma_count < < 2 ) ,
2012-06-04 08:24:47 +04:00
mid_dma_count ) ;
}
}
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 ;
2014-04-10 10:30:56 +04:00
u32 reg ;
2012-06-04 08:24:47 +04:00
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 */
2014-04-10 10:30:56 +04:00
writel ( ctx - > sg_buflen > > 2 , & crc - > regs - > datacnt ) ;
2012-06-04 08:24:47 +04:00
/* setup and enable CRC DMA */
bfin_crypto_crc_config_dma ( crc ) ;
/* finally kick off CRC operation */
2014-04-10 10:30:56 +04:00
reg = readl ( & crc - > regs - > control ) ;
writel ( reg | BLKEN , & crc - > regs - > control ) ;
2012-06-04 08:24:47 +04:00
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 ;
2014-04-10 10:30:56 +04:00
u32 reg ;
2012-06-04 08:24:47 +04:00
2014-04-10 10:30:56 +04:00
if ( readl ( & crc - > regs - > status ) & DCNTEXP ) {
writel ( DCNTEXP , & crc - > regs - > status ) ;
2012-06-04 08:24:47 +04:00
/* prepare results */
2014-04-10 10:30:56 +04:00
put_unaligned_le32 ( readl ( & crc - > regs - > result ) ,
crc - > req - > result ) ;
2012-06-04 08:24:47 +04:00
2014-04-10 10:30:56 +04:00
reg = readl ( & crc - > regs - > control ) ;
writel ( reg & ~ BLKEN , & crc - > regs - > control ) ;
2012-06-04 08:24:47 +04:00
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 ;
2014-04-10 10:30:56 +04:00
while ( ( readl ( & crc - > regs - > control ) & BLKEN ) & & - - i )
2012-06-04 08:24:47 +04:00
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
*
*/
2012-12-22 01:14:09 +04:00
static int bfin_crypto_crc_probe ( struct platform_device * pdev )
2012-06-04 08:24:47 +04:00
{
struct device * dev = & pdev - > dev ;
struct resource * res ;
struct bfin_crypto_crc * crc ;
unsigned int timeout = 100000 ;
int ret ;
2014-02-26 06:39:16 +04:00
crc = devm_kzalloc ( dev , sizeof ( * crc ) , GFP_KERNEL ) ;
2012-06-04 08:24:47 +04:00
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 " ) ;
2014-02-26 06:39:16 +04:00
return - ENOENT ;
2012-06-04 08:24:47 +04:00
}
2014-02-26 06:39:16 +04:00
crc - > regs = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( ( void * ) crc - > regs ) ) {
2012-06-04 08:24:47 +04:00
dev_err ( & pdev - > dev , " Cannot map CRC IO \n " ) ;
2014-02-26 06:39:16 +04:00
return PTR_ERR ( ( void * ) crc - > regs ) ;
2012-06-04 08:24:47 +04:00
}
crc - > irq = platform_get_irq ( pdev , 0 ) ;
if ( crc - > irq < 0 ) {
dev_err ( & pdev - > dev , " No CRC DCNTEXP IRQ specified \n " ) ;
2014-02-26 06:39:16 +04:00
return - ENOENT ;
2012-06-04 08:24:47 +04:00
}
2014-02-26 06:39:16 +04:00
ret = devm_request_irq ( dev , crc - > irq , bfin_crypto_crc_handler ,
IRQF_SHARED , dev_name ( dev ) , crc ) ;
2012-06-04 08:24:47 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " Unable to request blackfin crc irq \n " ) ;
2014-02-26 06:39:16 +04:00
return ret ;
2012-06-04 08:24:47 +04:00
}
res = platform_get_resource ( pdev , IORESOURCE_DMA , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " No CRC DMA channel specified \n " ) ;
2014-02-26 06:39:16 +04:00
return - ENOENT ;
2012-06-04 08:24:47 +04:00
}
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 " ) ;
2014-02-26 06:39:16 +04:00
return ret ;
2012-06-04 08:24:47 +04:00
}
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 ) ) ;
2014-04-10 12:40:59 +04:00
crc - > sg_mid_dma = crc - > sg_dma + sizeof ( struct dma_desc_array )
* ( ( CRC_MAX_DMA_DESC + 1 ) < < 1 ) ;
2012-06-04 08:24:47 +04:00
2014-04-10 10:30:56 +04:00
writel ( 0 , & crc - > regs - > control ) ;
crc - > poly = ( u32 ) pdev - > dev . platform_data ;
writel ( crc - > poly , & crc - > regs - > poly ) ;
2012-06-04 08:24:47 +04:00
2014-04-10 10:30:56 +04:00
while ( ! ( readl ( & crc - > regs - > status ) & LUTDONE ) & & ( - - timeout ) > 0 )
2012-06-04 08:24:47 +04:00
cpu_relax ( ) ;
if ( timeout = = 0 )
dev_info ( & pdev - > dev , " init crc poly timeout \n " ) ;
2014-04-11 13:30:25 +04:00
platform_set_drvdata ( pdev , crc ) ;
2012-06-04 08:24:47 +04:00
spin_lock ( & crc_list . lock ) ;
list_add ( & crc - > list , & crc_list . dev_list ) ;
spin_unlock ( & crc_list . lock ) ;
2014-04-11 13:30:25 +04:00
if ( list_is_singular ( & crc_list . dev_list ) ) {
ret = crypto_register_ahash ( & algs ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" Can't register crypto ahash device \n " ) ;
goto out_error_dma ;
}
2012-06-04 08:24:47 +04:00
}
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
*
*/
2012-12-22 01:14:09 +04:00
static int bfin_crypto_crc_remove ( struct platform_device * pdev )
2012-06-04 08:24:47 +04:00
{
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 ,
2012-12-22 01:14:09 +04:00
. remove = bfin_crypto_crc_remove ,
2012-06-04 08:24:47 +04:00
. 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 " ) ;