2021-06-01 15:11:29 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
2021-06-25 15:27:23 +02:00
* sl3516 - ce - core . c - hardware cryptographic offloader for Storlink SL3516 SoC
2021-06-01 15:11:29 +00:00
*
* Copyright ( C ) 2021 Corentin Labbe < clabbe @ baylibre . com >
*
* Core file which registers crypto algorithms supported by the CryptoEngine
*/
# include <linux/clk.h>
# include <linux/crypto.h>
# include <linux/debugfs.h>
# include <linux/dev_printk.h>
# include <linux/dma-mapping.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/reset.h>
# include <crypto/internal/rng.h>
# include <crypto/internal/skcipher.h>
# include "sl3516-ce.h"
static int sl3516_ce_desc_init ( struct sl3516_ce_dev * ce )
{
const size_t sz = sizeof ( struct descriptor ) * MAXDESC ;
int i ;
ce - > tx = dma_alloc_coherent ( ce - > dev , sz , & ce - > dtx , GFP_KERNEL ) ;
if ( ! ce - > tx )
return - ENOMEM ;
ce - > rx = dma_alloc_coherent ( ce - > dev , sz , & ce - > drx , GFP_KERNEL ) ;
if ( ! ce - > rx )
goto err_rx ;
for ( i = 0 ; i < MAXDESC ; i + + ) {
ce - > tx [ i ] . frame_ctrl . bits . own = CE_CPU ;
ce - > tx [ i ] . next_desc . next_descriptor = ce - > dtx + ( i + 1 ) * sizeof ( struct descriptor ) ;
}
ce - > tx [ MAXDESC - 1 ] . next_desc . next_descriptor = ce - > dtx ;
for ( i = 0 ; i < MAXDESC ; i + + ) {
ce - > rx [ i ] . frame_ctrl . bits . own = CE_CPU ;
ce - > rx [ i ] . next_desc . next_descriptor = ce - > drx + ( i + 1 ) * sizeof ( struct descriptor ) ;
}
ce - > rx [ MAXDESC - 1 ] . next_desc . next_descriptor = ce - > drx ;
ce - > pctrl = dma_alloc_coherent ( ce - > dev , sizeof ( struct pkt_control_ecb ) ,
& ce - > dctrl , GFP_KERNEL ) ;
if ( ! ce - > pctrl )
goto err_pctrl ;
return 0 ;
err_pctrl :
dma_free_coherent ( ce - > dev , sz , ce - > rx , ce - > drx ) ;
err_rx :
dma_free_coherent ( ce - > dev , sz , ce - > tx , ce - > dtx ) ;
return - ENOMEM ;
}
static void sl3516_ce_free_descs ( struct sl3516_ce_dev * ce )
{
const size_t sz = sizeof ( struct descriptor ) * MAXDESC ;
dma_free_coherent ( ce - > dev , sz , ce - > tx , ce - > dtx ) ;
dma_free_coherent ( ce - > dev , sz , ce - > rx , ce - > drx ) ;
dma_free_coherent ( ce - > dev , sizeof ( struct pkt_control_ecb ) , ce - > pctrl ,
ce - > dctrl ) ;
}
static void start_dma_tx ( struct sl3516_ce_dev * ce )
{
u32 v ;
v = TXDMA_CTRL_START | TXDMA_CTRL_CHAIN_MODE | TXDMA_CTRL_CONTINUE | \
TXDMA_CTRL_INT_FAIL | TXDMA_CTRL_INT_PERR | TXDMA_CTRL_BURST_UNK ;
writel ( v , ce - > base + IPSEC_TXDMA_CTRL ) ;
}
static void start_dma_rx ( struct sl3516_ce_dev * ce )
{
u32 v ;
v = RXDMA_CTRL_START | RXDMA_CTRL_CHAIN_MODE | RXDMA_CTRL_CONTINUE | \
RXDMA_CTRL_BURST_UNK | RXDMA_CTRL_INT_FINISH | \
RXDMA_CTRL_INT_FAIL | RXDMA_CTRL_INT_PERR | \
RXDMA_CTRL_INT_EOD | RXDMA_CTRL_INT_EOF ;
writel ( v , ce - > base + IPSEC_RXDMA_CTRL ) ;
}
static struct descriptor * get_desc_tx ( struct sl3516_ce_dev * ce )
{
struct descriptor * dd ;
dd = & ce - > tx [ ce - > ctx ] ;
ce - > ctx + + ;
if ( ce - > ctx > = MAXDESC )
ce - > ctx = 0 ;
return dd ;
}
static struct descriptor * get_desc_rx ( struct sl3516_ce_dev * ce )
{
struct descriptor * rdd ;
rdd = & ce - > rx [ ce - > crx ] ;
ce - > crx + + ;
if ( ce - > crx > = MAXDESC )
ce - > crx = 0 ;
return rdd ;
}
int sl3516_ce_run_task ( struct sl3516_ce_dev * ce , struct sl3516_ce_cipher_req_ctx * rctx ,
const char * name )
{
struct descriptor * dd , * rdd = NULL ;
u32 v ;
int i , err = 0 ;
ce - > stat_req + + ;
reinit_completion ( & ce - > complete ) ;
ce - > status = 0 ;
for ( i = 0 ; i < rctx - > nr_sgd ; i + + ) {
dev_dbg ( ce - > dev , " %s handle DST SG %d/%d len=%d \n " , __func__ ,
i , rctx - > nr_sgd , rctx - > t_dst [ i ] . len ) ;
rdd = get_desc_rx ( ce ) ;
rdd - > buf_adr = rctx - > t_dst [ i ] . addr ;
rdd - > frame_ctrl . bits . buffer_size = rctx - > t_dst [ i ] . len ;
rdd - > frame_ctrl . bits . own = CE_DMA ;
}
rdd - > next_desc . bits . eofie = 1 ;
for ( i = 0 ; i < rctx - > nr_sgs ; i + + ) {
dev_dbg ( ce - > dev , " %s handle SRC SG %d/%d len=%d \n " , __func__ ,
i , rctx - > nr_sgs , rctx - > t_src [ i ] . len ) ;
rctx - > h - > algorithm_len = rctx - > t_src [ i ] . len ;
dd = get_desc_tx ( ce ) ;
dd - > frame_ctrl . raw = 0 ;
dd - > flag_status . raw = 0 ;
dd - > frame_ctrl . bits . buffer_size = rctx - > pctrllen ;
dd - > buf_adr = ce - > dctrl ;
dd - > flag_status . tx_flag . tqflag = rctx - > tqflag ;
dd - > next_desc . bits . eofie = 0 ;
dd - > next_desc . bits . dec = 0 ;
dd - > next_desc . bits . sof_eof = DESC_FIRST | DESC_LAST ;
dd - > frame_ctrl . bits . own = CE_DMA ;
dd = get_desc_tx ( ce ) ;
dd - > frame_ctrl . raw = 0 ;
dd - > flag_status . raw = 0 ;
dd - > frame_ctrl . bits . buffer_size = rctx - > t_src [ i ] . len ;
dd - > buf_adr = rctx - > t_src [ i ] . addr ;
dd - > flag_status . tx_flag . tqflag = 0 ;
dd - > next_desc . bits . eofie = 0 ;
dd - > next_desc . bits . dec = 0 ;
dd - > next_desc . bits . sof_eof = DESC_FIRST | DESC_LAST ;
dd - > frame_ctrl . bits . own = CE_DMA ;
start_dma_tx ( ce ) ;
start_dma_rx ( ce ) ;
}
wait_for_completion_interruptible_timeout ( & ce - > complete ,
msecs_to_jiffies ( 5000 ) ) ;
if ( ce - > status = = 0 ) {
dev_err ( ce - > dev , " DMA timeout for %s \n " , name ) ;
err = - EFAULT ;
}
v = readl ( ce - > base + IPSEC_STATUS_REG ) ;
if ( v & 0xFFF ) {
dev_err ( ce - > dev , " IPSEC_STATUS_REG %x \n " , v ) ;
err = - EFAULT ;
}
return err ;
}
static irqreturn_t ce_irq_handler ( int irq , void * data )
{
struct sl3516_ce_dev * ce = ( struct sl3516_ce_dev * ) data ;
u32 v ;
ce - > stat_irq + + ;
v = readl ( ce - > base + IPSEC_DMA_STATUS ) ;
writel ( v , ce - > base + IPSEC_DMA_STATUS ) ;
if ( v & DMA_STATUS_TS_DERR )
dev_err ( ce - > dev , " AHB bus Error While Tx !!! \n " ) ;
if ( v & DMA_STATUS_TS_PERR )
dev_err ( ce - > dev , " Tx Descriptor Protocol Error !!! \n " ) ;
if ( v & DMA_STATUS_RS_DERR )
dev_err ( ce - > dev , " AHB bus Error While Rx !!! \n " ) ;
if ( v & DMA_STATUS_RS_PERR )
dev_err ( ce - > dev , " Rx Descriptor Protocol Error !!! \n " ) ;
if ( v & DMA_STATUS_TS_EOFI )
ce - > stat_irq_tx + + ;
if ( v & DMA_STATUS_RS_EOFI ) {
ce - > status = 1 ;
complete ( & ce - > complete ) ;
ce - > stat_irq_rx + + ;
return IRQ_HANDLED ;
}
return IRQ_HANDLED ;
}
static struct sl3516_ce_alg_template ce_algs [ ] = {
{
. type = CRYPTO_ALG_TYPE_SKCIPHER ,
. mode = ECB_AES ,
. alg . skcipher = {
. base = {
. cra_name = " ecb(aes) " ,
. cra_driver_name = " ecb-aes-sl3516 " ,
. cra_priority = 400 ,
. cra_blocksize = AES_BLOCK_SIZE ,
. cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK ,
. cra_ctxsize = sizeof ( struct sl3516_ce_cipher_tfm_ctx ) ,
. cra_module = THIS_MODULE ,
. cra_alignmask = 0xf ,
. cra_init = sl3516_ce_cipher_init ,
. cra_exit = sl3516_ce_cipher_exit ,
} ,
. min_keysize = AES_MIN_KEY_SIZE ,
. max_keysize = AES_MAX_KEY_SIZE ,
. setkey = sl3516_ce_aes_setkey ,
. encrypt = sl3516_ce_skencrypt ,
. decrypt = sl3516_ce_skdecrypt ,
}
} ,
} ;
# ifdef CONFIG_CRYPTO_DEV_SL3516_DEBUG
static int sl3516_ce_debugfs_show ( struct seq_file * seq , void * v )
{
struct sl3516_ce_dev * ce = seq - > private ;
unsigned int i ;
seq_printf ( seq , " HWRNG %lu %lu \n " ,
ce - > hwrng_stat_req , ce - > hwrng_stat_bytes ) ;
seq_printf ( seq , " IRQ %lu \n " , ce - > stat_irq ) ;
seq_printf ( seq , " IRQ TX %lu \n " , ce - > stat_irq_tx ) ;
seq_printf ( seq , " IRQ RX %lu \n " , ce - > stat_irq_rx ) ;
seq_printf ( seq , " nreq %lu \n " , ce - > stat_req ) ;
seq_printf ( seq , " fallback SG count TX %lu \n " , ce - > fallback_sg_count_tx ) ;
seq_printf ( seq , " fallback SG count RX %lu \n " , ce - > fallback_sg_count_rx ) ;
seq_printf ( seq , " fallback modulo16 %lu \n " , ce - > fallback_mod16 ) ;
seq_printf ( seq , " fallback align16 %lu \n " , ce - > fallback_align16 ) ;
seq_printf ( seq , " fallback not same len %lu \n " , ce - > fallback_not_same_len ) ;
for ( i = 0 ; i < ARRAY_SIZE ( ce_algs ) ; i + + ) {
if ( ! ce_algs [ i ] . ce )
continue ;
switch ( ce_algs [ i ] . type ) {
case CRYPTO_ALG_TYPE_SKCIPHER :
seq_printf ( seq , " %s %s reqs=%lu fallback=%lu \n " ,
ce_algs [ i ] . alg . skcipher . base . cra_driver_name ,
ce_algs [ i ] . alg . skcipher . base . cra_name ,
ce_algs [ i ] . stat_req , ce_algs [ i ] . stat_fb ) ;
break ;
}
}
return 0 ;
}
DEFINE_SHOW_ATTRIBUTE ( sl3516_ce_debugfs ) ;
# endif
static int sl3516_ce_register_algs ( struct sl3516_ce_dev * ce )
{
int err ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ce_algs ) ; i + + ) {
ce_algs [ i ] . ce = ce ;
switch ( ce_algs [ i ] . type ) {
case CRYPTO_ALG_TYPE_SKCIPHER :
dev_info ( ce - > dev , " DEBUG: Register %s \n " ,
ce_algs [ i ] . alg . skcipher . base . cra_name ) ;
err = crypto_register_skcipher ( & ce_algs [ i ] . alg . skcipher ) ;
if ( err ) {
dev_err ( ce - > dev , " Fail to register %s \n " ,
ce_algs [ i ] . alg . skcipher . base . cra_name ) ;
ce_algs [ i ] . ce = NULL ;
return err ;
}
break ;
default :
ce_algs [ i ] . ce = NULL ;
dev_err ( ce - > dev , " ERROR: tried to register an unknown algo \n " ) ;
}
}
return 0 ;
}
static void sl3516_ce_unregister_algs ( struct sl3516_ce_dev * ce )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ce_algs ) ; i + + ) {
if ( ! ce_algs [ i ] . ce )
continue ;
switch ( ce_algs [ i ] . type ) {
case CRYPTO_ALG_TYPE_SKCIPHER :
dev_info ( ce - > dev , " Unregister %d %s \n " , i ,
ce_algs [ i ] . alg . skcipher . base . cra_name ) ;
crypto_unregister_skcipher ( & ce_algs [ i ] . alg . skcipher ) ;
break ;
}
}
}
static void sl3516_ce_start ( struct sl3516_ce_dev * ce )
{
ce - > ctx = 0 ;
ce - > crx = 0 ;
writel ( ce - > dtx , ce - > base + IPSEC_TXDMA_CURR_DESC ) ;
writel ( ce - > drx , ce - > base + IPSEC_RXDMA_CURR_DESC ) ;
writel ( 0 , ce - > base + IPSEC_DMA_STATUS ) ;
}
/*
* Power management strategy : The device is suspended unless a TFM exists for
* one of the algorithms proposed by this driver .
*/
static int sl3516_ce_pm_suspend ( struct device * dev )
{
struct sl3516_ce_dev * ce = dev_get_drvdata ( dev ) ;
reset_control_assert ( ce - > reset ) ;
clk_disable_unprepare ( ce - > clks ) ;
return 0 ;
}
static int sl3516_ce_pm_resume ( struct device * dev )
{
struct sl3516_ce_dev * ce = dev_get_drvdata ( dev ) ;
int err ;
err = clk_prepare_enable ( ce - > clks ) ;
if ( err ) {
dev_err ( ce - > dev , " Cannot prepare_enable \n " ) ;
goto error ;
}
err = reset_control_deassert ( ce - > reset ) ;
if ( err ) {
dev_err ( ce - > dev , " Cannot deassert reset control \n " ) ;
goto error ;
}
sl3516_ce_start ( ce ) ;
return 0 ;
error :
sl3516_ce_pm_suspend ( dev ) ;
return err ;
}
static const struct dev_pm_ops sl3516_ce_pm_ops = {
SET_RUNTIME_PM_OPS ( sl3516_ce_pm_suspend , sl3516_ce_pm_resume , NULL )
} ;
static int sl3516_ce_pm_init ( struct sl3516_ce_dev * ce )
{
int err ;
pm_runtime_use_autosuspend ( ce - > dev ) ;
pm_runtime_set_autosuspend_delay ( ce - > dev , 2000 ) ;
err = pm_runtime_set_suspended ( ce - > dev ) ;
if ( err )
return err ;
pm_runtime_enable ( ce - > dev ) ;
return err ;
}
static void sl3516_ce_pm_exit ( struct sl3516_ce_dev * ce )
{
pm_runtime_disable ( ce - > dev ) ;
}
static int sl3516_ce_probe ( struct platform_device * pdev )
{
struct sl3516_ce_dev * ce ;
int err , irq ;
u32 v ;
ce = devm_kzalloc ( & pdev - > dev , sizeof ( * ce ) , GFP_KERNEL ) ;
if ( ! ce )
return - ENOMEM ;
ce - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , ce ) ;
ce - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( ce - > base ) )
return PTR_ERR ( ce - > base ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
err = devm_request_irq ( & pdev - > dev , irq , ce_irq_handler , 0 , " crypto " , ce ) ;
if ( err ) {
dev_err ( ce - > dev , " Cannot request Crypto Engine IRQ (err=%d) \n " , err ) ;
return err ;
}
ce - > reset = devm_reset_control_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( ce - > reset ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( ce - > reset ) ,
" No reset control found \n " ) ;
ce - > clks = devm_clk_get ( ce - > dev , NULL ) ;
if ( IS_ERR ( ce - > clks ) ) {
err = PTR_ERR ( ce - > clks ) ;
dev_err ( ce - > dev , " Cannot get clock err=%d \n " , err ) ;
return err ;
}
err = sl3516_ce_desc_init ( ce ) ;
if ( err )
return err ;
err = sl3516_ce_pm_init ( ce ) ;
if ( err )
goto error_pm ;
init_completion ( & ce - > complete ) ;
ce - > engine = crypto_engine_alloc_init ( ce - > dev , true ) ;
if ( ! ce - > engine ) {
dev_err ( ce - > dev , " Cannot allocate engine \n " ) ;
err = - ENOMEM ;
goto error_engine ;
}
err = crypto_engine_start ( ce - > engine ) ;
if ( err ) {
dev_err ( ce - > dev , " Cannot start engine \n " ) ;
goto error_engine ;
}
err = sl3516_ce_register_algs ( ce ) ;
if ( err )
goto error_alg ;
err = sl3516_ce_rng_register ( ce ) ;
if ( err )
goto error_rng ;
err = pm_runtime_resume_and_get ( ce - > dev ) ;
if ( err < 0 )
goto error_pmuse ;
v = readl ( ce - > base + IPSEC_ID ) ;
dev_info ( ce - > dev , " SL3516 dev %lx rev %lx \n " ,
v & GENMASK ( 31 , 4 ) ,
v & GENMASK ( 3 , 0 ) ) ;
v = readl ( ce - > base + IPSEC_DMA_DEVICE_ID ) ;
dev_info ( ce - > dev , " SL3516 DMA dev %lx rev %lx \n " ,
v & GENMASK ( 15 , 4 ) ,
v & GENMASK ( 3 , 0 ) ) ;
pm_runtime_put_sync ( ce - > dev ) ;
# ifdef CONFIG_CRYPTO_DEV_SL3516_DEBUG
/* Ignore error of debugfs */
ce - > dbgfs_dir = debugfs_create_dir ( " sl3516 " , NULL ) ;
ce - > dbgfs_stats = debugfs_create_file ( " stats " , 0444 ,
ce - > dbgfs_dir , ce ,
& sl3516_ce_debugfs_fops ) ;
# endif
return 0 ;
error_pmuse :
sl3516_ce_rng_unregister ( ce ) ;
error_rng :
sl3516_ce_unregister_algs ( ce ) ;
error_alg :
crypto_engine_exit ( ce - > engine ) ;
error_engine :
sl3516_ce_pm_exit ( ce ) ;
error_pm :
sl3516_ce_free_descs ( ce ) ;
return err ;
}
static int sl3516_ce_remove ( struct platform_device * pdev )
{
struct sl3516_ce_dev * ce = platform_get_drvdata ( pdev ) ;
sl3516_ce_rng_unregister ( ce ) ;
sl3516_ce_unregister_algs ( ce ) ;
crypto_engine_exit ( ce - > engine ) ;
sl3516_ce_pm_exit ( ce ) ;
sl3516_ce_free_descs ( ce ) ;
# ifdef CONFIG_CRYPTO_DEV_SL3516_DEBUG
debugfs_remove_recursive ( ce - > dbgfs_dir ) ;
# endif
return 0 ;
}
static const struct of_device_id sl3516_ce_crypto_of_match_table [ ] = {
{ . compatible = " cortina,sl3516-crypto " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sl3516_ce_crypto_of_match_table ) ;
static struct platform_driver sl3516_ce_driver = {
. probe = sl3516_ce_probe ,
. remove = sl3516_ce_remove ,
. driver = {
. name = " sl3516-crypto " ,
. pm = & sl3516_ce_pm_ops ,
. of_match_table = sl3516_ce_crypto_of_match_table ,
} ,
} ;
module_platform_driver ( sl3516_ce_driver ) ;
MODULE_DESCRIPTION ( " SL3516 cryptographic offloader " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Corentin Labbe <clabbe@baylibre.com> " ) ;