From patchwork Tue Oct 28 23:41:16 2008 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hsiao X-Patchwork-Id: 6177 X-Patchwork-Delegate: jwboyer@gmail.com Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [127.0.0.1]) by ozlabs.org (Postfix) with ESMTP id F3EEADE2F0 for ; Wed, 29 Oct 2008 10:48:29 +1100 (EST) X-Original-To: linuxppc-dev@ozlabs.org Delivered-To: linuxppc-dev@ozlabs.org Received: from sdcmail02.amcc.com (sdcmail02.amcc.com [198.137.200.73]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (Client CN "Messaging Gateway Appliance Demo Cert", Issuer "Messaging Gateway Appliance Demo Cert" (not verified)) by ozlabs.org (Postfix) with ESMTPS id E4CA7DDDEA for ; Wed, 29 Oct 2008 10:40:05 +1100 (EST) X-IronPort-AV: E=Sophos;i="4.33,502,1220252400"; d="scan'208";a="1750190" Received: from sdcexch01.amcc.com (HELO sdcexchange01.amcc.com) ([10.64.18.50]) by sdcmail02-int1.amcc.com with ESMTP; 28 Oct 2008 16:40:03 -0700 Received: from svdc-dhcp-10-66-6-209.amcc.com ([10.66.6.209]) by sdcexchange01.amcc.com with Microsoft SMTPSVC(6.0.3790.3959); Tue, 28 Oct 2008 16:40:01 -0700 Subject: [PATCH] AMCC Crypto4xx Device Driver v2] From: James Hsiao To: jwboyer@linux.vnet.ibm.com Organization: amcc Date: Tue, 28 Oct 2008 16:41:16 -0700 Message-Id: <1225237276.1850.13.camel@jhsiao-usb> Mime-Version: 1.0 X-Mailer: Evolution 2.6.0 X-OriginalArrivalTime: 28 Oct 2008 23:40:01.0966 (UTC) FILETIME=[7F21C4E0:01C93956] X-Mailman-Approved-At: Wed, 29 Oct 2008 10:47:04 +1100 Cc: linuxppc-dev@ozlabs.org X-BeenThere: linuxppc-dev@ozlabs.org X-Mailman-Version: 2.1.11 Precedence: list Reply-To: jhsiao@amcc.com List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org Hi Josh, I am reposting this patch. Thanks Kim Phillips for pointing out format of my patch. Again this patch was already reviewed by Kim Phillips on linux-crypyo. Kim suggest us submit to linuxppc-dev for review. Thanks James Signed-off-by: James Hsiao --- arch/powerpc/boot/dts/kilauea.dts | 10 +- drivers/crypto/Kconfig | 9 + drivers/crypto/Makefile | 1 + drivers/crypto/amcc/Makefile | 27 + drivers/crypto/amcc/crypto4xx_alg.c | 404 ++++++++++ drivers/crypto/amcc/crypto4xx_core.c | 1220 +++++++++++++++++++++++++++++++ drivers/crypto/amcc/crypto4xx_core.h | 200 +++++ drivers/crypto/amcc/crypto4xx_reg_def.h | 291 ++++++++ drivers/crypto/amcc/crypto4xx_sa.c | 98 +++ drivers/crypto/amcc/crypto4xx_sa.h | 223 ++++++ 10 files changed, 2482 insertions(+), 1 deletions(-) create mode 100644 drivers/crypto/amcc/Makefile create mode 100644 drivers/crypto/amcc/crypto4xx_alg.c create mode 100644 drivers/crypto/amcc/crypto4xx_core.c create mode 100644 drivers/crypto/amcc/crypto4xx_core.h create mode 100644 drivers/crypto/amcc/crypto4xx_reg_def.h create mode 100644 drivers/crypto/amcc/crypto4xx_sa.c create mode 100644 drivers/crypto/amcc/crypto4xx_sa.h diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts index dececc4..58b48a0 100644 --- a/arch/powerpc/boot/dts/kilauea.dts +++ b/arch/powerpc/boot/dts/kilauea.dts @@ -1,4 +1,4 @@ -/* +/* * Device Tree Source for AMCC Kilauea (405EX) * * Copyright 2007 DENX Software Engineering, Stefan Roese @@ -94,6 +94,14 @@ dcr-reg = <0x010 0x002>; }; + CRYPTO: crypto@ef700000 { + device_type = "crypto"; + compatible = "amcc,ppc4xx-crypto"; + reg = <0xef700000 0x80400>; + interrupt-parent = <&UIC0>; + interrupts = <0x17 0x2>; + }; + MAL0: mcmal { compatible = "ibm,mcmal-405ex", "ibm,mcmal2"; dcr-reg = <0x180 0x062>; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index e522144..d761664 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -200,4 +200,13 @@ config CRYPTO_DEV_IXP4XX help Driver for the IXP4xx NPE crypto engine. +config CRYPTO_DEV_PPC4XX + tristate "Driver AMCC PPC4XX crypto accelerator" + depends on PPC && 4xx + select CRYPTO_HASH + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + help + This option allows you to have support for AMCC crypto acceleration. + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 73557b2..9bf4a2b 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o +obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/ diff --git a/drivers/crypto/amcc/Makefile b/drivers/crypto/amcc/Makefile new file mode 100644 index 0000000..4b06655 --- /dev/null +++ b/drivers/crypto/amcc/Makefile @@ -0,0 +1,27 @@ +################################################################################ +# (C) Copyright 2007 Applied Micro Circuits Corporation +# James Hsiao, AMCC, support@amcc.com +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# +################################################################################ +# +# Makefile for the AMCC Crypto Acclerator Device Driver +# + +obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o + +crypto4xx-objs := crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c new file mode 100644 index 0000000..7dfe6a6 --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_alg.c @@ -0,0 +1,404 @@ +/***************************************************************************** + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_alg.c + * + * This file implements the Linux crypto algorithms. + * + ***************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto4xx_reg_def.h" +#include "crypto4xx_sa.h" +#include "crypto4xx_core.h" + +static inline int crypto4xx_encrypt(struct ablkcipher_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + struct crypto4xx_ctx *rctx = ablkcipher_request_ctx(req); + int rc; + + /* + * Application only provided ptr for the rctx + * we alloc memory for it. + * And along we alloc memory for the sa in it. + */ + ctx->use_rctx = 1; + ctx->direction = CRYPTO_OUTBOUND; + rc = crypto4xx_alloc_sa_rctx(ctx, rctx); + if (rc) + goto err_nomem; + memcpy((void *)(rctx->sa_out + + get_dynamic_sa_offset_state_ptr_field(rctx)), + (void *)&(rctx->state_record_dma_addr), 4); + /* copy req->iv to state_record->iv */ + if (req->info) + crypto4xx_memcpy_le(rctx->state_record, req->info, + get_dynamic_sa_iv_size(rctx)); + else + memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx)); + rctx->hash_final = 0; + rctx->is_hash = 0; + rctx->pd_ctl = 0x1; + rctx->direction = CRYPTO_OUTBOUND; + + return crypto4xx_handle_req(&req->base); + +err_nomem: + return -ENOMEM; +} + +static inline int crypto4xx_decrypt(struct ablkcipher_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + struct crypto4xx_ctx *rctx = ablkcipher_request_ctx(req); + int rc; + + /* + * Application only provided ptr for the rctx + * we alloc memory for it. + * And along we alloc memory for the sa in it + */ + ctx->use_rctx = 1; + ctx->direction = CRYPTO_INBOUND; + rc = crypto4xx_alloc_sa_rctx(ctx, rctx); + if (rc != 0) + goto err_nomem; + + memcpy((void *)(rctx->sa_in + + get_dynamic_sa_offset_state_ptr_field(rctx)), + (void *)&(rctx->state_record_dma_addr), 4); + /* copy req->iv to state_record->iv */ + if (req->info) + crypto4xx_memcpy_le(rctx->state_record, req->info, + get_dynamic_sa_iv_size(rctx)); + else + memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx)); + + rctx->hash_final = 0; + rctx->is_hash = 0; + rctx->pd_ctl = 1; + rctx->direction = CRYPTO_INBOUND; + + return crypto4xx_handle_req(&req->base); + +err_nomem: + return -ENOMEM; +} + +/** + * AES Functions + * + */ +static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher, + const u8 *key, + unsigned int keylen, + unsigned char cm, + u8 fb) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm); + struct dynamic_sa_ctl *sa; + int rc; + + if ((keylen != 256/8) && (keylen != 128/8) && (keylen != 192/8)) { + crypto_ablkcipher_set_flags(cipher, + CRYPTO_TFM_RES_BAD_KEY_LEN); + return -1; + } + + /* Create SA */ + if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr) + crypto4xx_free_sa(ctx); + + if (keylen == 256/8) + crypto4xx_alloc_sa(ctx, SA_AES256_LEN); + else if (keylen == 192/8) + crypto4xx_alloc_sa(ctx, SA_AES192_LEN); + else + crypto4xx_alloc_sa(ctx, SA_AES128_LEN); + + if (!ctx->sa_in_dma_addr || !ctx->sa_out_dma_addr) + goto err_nomem; + if (ctx->state_record_dma_addr == 0) { + rc = crypto4xx_alloc_state_record(ctx); + if (rc != 0) + goto err_nomem_sr; + } + /* Setup SA */ + sa = (struct dynamic_sa_ctl *)(ctx->sa_in); + ctx->hash_final = 0; + sa->sa_command_0.bf.hash_alg = SA_HASH_ALG_NULL; + sa->sa_command_0.bf.cipher_alg = SA_CIPHER_ALG_AES; + sa->sa_command_0.bf.opcode = SA_OPCODE_ENCRYPT; + sa->sa_command_0.bf.load_iv = 2; + + sa->sa_command_1.bf.sa_rev = 1; + sa->sa_command_1.bf.copy_payload = 0; + sa->sa_command_1.bf.crypto_mode31 = (cm & 4) >> 2; + sa->sa_command_1.bf.crypto_mode9_8 = (cm & 3); + sa->sa_command_1.bf.feedback_mode = fb; + sa->sa_command_1.bf.mutable_bit_proc = 1; + + if (keylen >= 256/8) { + crypto4xx_memcpy_le(((struct dynamic_sa_aes256 *)sa)->key, + key, keylen); + sa->sa_contents = SA_AES256_CONTENTS; + sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_256; + } else if (keylen >= 192/8) { + crypto4xx_memcpy_le(((struct dynamic_sa_aes192 *)sa)->key, + key, keylen); + sa->sa_contents = SA_AES192_CONTENTS; + sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_192; + } else { + crypto4xx_memcpy_le(((struct dynamic_sa_aes128 *)sa)->key, + key, keylen); + sa->sa_contents = SA_AES128_CONTENTS; + sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_128; + } + ctx->is_hash = 0; + ctx->direction = CRYPTO_INBOUND; + sa->sa_command_0.bf.dir = CRYPTO_INBOUND; + memcpy(ctx->sa_in + get_dynamic_sa_offset_state_ptr_field(ctx), + (void *)&(ctx->state_record_dma_addr), 4); + memcpy(ctx->sa_out, ctx->sa_in, ctx->sa_len*4); + sa = (struct dynamic_sa_ctl *)(ctx->sa_out); + sa->sa_command_0.bf.dir = CRYPTO_OUTBOUND; + + return 0; + +err_nomem_sr: + crypto4xx_free_sa(ctx); + +err_nomem: + return -ENOMEM; + +} + +static inline int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher, + const u8 *key, unsigned int keylen) +{ + return crypto4xx_setkey_aes(cipher, key, keylen, + CRYPTO_MODE_CBC, + CRYPTO_FEEDBACK_MODE_NO_FB); +} + +/** + * HASH SHA1 Functions + * + */ +static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm, + unsigned int sa_len, + unsigned char ha, + unsigned char hm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + struct crypto4xx_alg *my_alg = crypto_alg_to_crypto4xx_alg(alg); + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm); + struct dynamic_sa_ctl *sa; + + ctx->dev = my_alg->dev; + ctx->is_hash = 1; + ctx->hash_final = 0; + + /* Create SA */ + if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr) + crypto4xx_free_sa(ctx); + + crypto4xx_alloc_sa(ctx, sa_len); + if (!ctx->sa_in_dma_addr || !ctx->sa_out_dma_addr) + goto err_nomem; + + if (ctx->state_record_dma_addr == 0) { + crypto4xx_alloc_state_record(ctx); + if (!ctx->state_record_dma_addr) + goto err_nomem_sr; + } + + tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx); + sa = (struct dynamic_sa_ctl *)(ctx->sa_in); + + /* Setup hash algorithm and hash mode */ + sa->sa_command_0.w = 0; + sa->sa_command_0.bf.hash_alg = ha; + sa->sa_command_0.bf.gather = 0; + sa->sa_command_0.bf.save_hash_state = 1; + sa->sa_command_0.bf.cipher_alg = SA_CIPHER_ALG_NULL; + sa->sa_command_0.bf.opcode = SA_OPCODE_HASH; + + /* load hash state set to no load, since we don't no init idigest */ + sa->sa_command_0.bf.load_hash_state = 3; + sa->sa_command_0.bf.dir = 0; + sa->sa_command_0.bf.opcode = SA_OPCODE_HASH; + sa->sa_command_1.w = 0; + sa->sa_command_1.bf.hmac_muting = 0; + /* dynamic sa, need to set it to rev 2 */ + sa->sa_command_1.bf.sa_rev = 1; + sa->sa_command_1.bf.copy_payload = 0; + sa->sa_command_1.bf.mutable_bit_proc = 1; + + /* Need to zero hash digest in SA */ + if (ha == SA_HASH_ALG_SHA1) { + sa->sa_contents = SA_HASH160_CONTENTS; + memset(((struct dynamic_sa_hash160 *) + (ctx->sa_in))->inner_digest, 0, 20); + memset(((struct dynamic_sa_hash160 *) + (ctx->sa_in))->outer_digest, 0, 20); + ((struct dynamic_sa_hash160 *)(ctx->sa_in))->state_ptr + = ctx->state_record_dma_addr; + } else { + printk(KERN_ERR "ERROR: invalid hash" + " algorithm used \n"); + } + + return 0; + +err_nomem_sr: + crypto4xx_free_sa(ctx); +err_nomem: + return -ENOMEM; + +} + +static int crypto4xx_hash_init(struct ahash_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + int ds; + struct dynamic_sa_ctl *sa; + + ctx->use_rctx = 0; + sa = (struct dynamic_sa_ctl *)(ctx->sa_in); + ds = crypto_ahash_digestsize( + __crypto_ahash_cast(req->base.tfm)); + sa->sa_command_0.bf.digest_len = ds>>2; + sa->sa_command_0.bf.load_hash_state = SA_LOAD_HASH_FROM_SA; + ctx->is_hash = 1; + ctx->direction = CRYPTO_INBOUND; + + return 0; +} + +static int crypto4xx_hash_update(struct ahash_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + + ctx->is_hash = 1; + ctx->hash_final = 0; + ctx->use_rctx = 0; + ctx->pd_ctl = 0x11; + ctx->direction = CRYPTO_INBOUND; + return crypto4xx_handle_req(&req->base); +} + +static int crypto4xx_hash_final(struct ahash_request *req) +{ + struct crypto4xx_ctx *rctx = ahash_request_ctx(req); + + crypto4xx_free_sa_rctx(rctx); + return 0; +} + +static int crypto4xx_hash_digest(struct ahash_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm); + ctx->use_rctx = 0; + ctx->hash_final = 1; + ctx->pd_ctl = 0x11; + ctx->direction = CRYPTO_INBOUND; + return crypto4xx_handle_req(&req->base); +} + +/** + * SHA1 and SHA2 Algorithm + */ +static int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm) +{ + return crypto4xx_hash_alg_init(tfm, + SA_HASH160_LEN, + SA_HASH_ALG_SHA1, + SA_HASH_MODE_HASH); +} + +/** + * Support Crypto Algorithms + */ +struct crypto_alg crypto4xx_basic_alg[] = { + + /* Crypto AES modes */ + {.cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-ppc4xx", + .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = 16, /* 128-bits block */ + .cra_ctxsize = sizeof(struct crypto4xx_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = {.ablkcipher = { + .min_keysize = 16, /* AES min key size is 128-bits */ + .max_keysize = 32, /* AES max key size is 256-bits */ + .ivsize = 16, /* IV size is 16 bytes */ + .setkey = crypto4xx_setkey_aes_cbc, + .encrypt = crypto4xx_encrypt, + .decrypt = crypto4xx_decrypt, + } } + }, + /* Hash SHA1, SHA2 */ + {.cra_name = "sha1", + .cra_driver_name = "sha1-ppc4xx", + .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, + .cra_blocksize = 64, /* SHA1 block size is 512-bits */ + .cra_ctxsize = sizeof(struct crypto4xx_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ahash_type, + .cra_init = crypto4xx_sha1_alg_init, + .cra_module = THIS_MODULE, + .cra_u = {.ahash = { + .digestsize = 20, /* Disgest is 160-bits */ + .init = crypto4xx_hash_init, + .update = crypto4xx_hash_update, + .final = crypto4xx_hash_final, + .digest = crypto4xx_hash_digest, + } } + }, +}; + +int crypto4xx_register_basic_alg(void) +{ + return crypto4xx_register_alg(&lsec_core.dev, + crypto4xx_basic_alg, + ARRAY_SIZE(crypto4xx_basic_alg)); +} + diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c new file mode 100644 index 0000000..7845a25 --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -0,0 +1,1220 @@ +/**************************************************************************** + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_core.c + * + * This file implements AMCC crypto offload Linux device driver for use with + * Linux CryptoAPI. + * + **************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crypto4xx_reg_def.h" +#include "crypto4xx_core.h" +#include "crypto4xx_sa.h" + +#define CRYPTO4XX_CRYPTO_PRIORITY 300 +#define PPC4XX_SEC_VERSION_STR "0.1" + +struct crypto4xx_core_device lsec_core; + +u32 crypto4xx_write32(u32 reg, u32 val) +{ + writel(val, lsec_core.ce_base + reg); + return 0; +} + +u32 crypto4xx_read32(u32 reg, u32 *val) +{ + *val = readl(lsec_core.ce_base + reg); + return 0; +} + +/** + * PPC4XX Crypto Engine Initialization Routine + */ +int32_t crypto4xx_init(struct crypto4xx_device *dev) +{ + u32 rc = 0; + union ce_ring_size ring_size; + union ce_ring_contol ring_ctrl; + union ce_part_ring_size part_ring_size; + union ce_io_threshold io_threshold; + u32 rand_num; + + union ce_pe_dma_cfg pe_dma_cfg; + if ((cur_cpu_spec->pvr_value & 0xffff0000) == 0x13020000) { + mtdcri(SDR0, 0x201, mfdcri(SDR0, 0x201) | 0x08000000); + mtdcri(SDR0, 0x201, mfdcri(SDR0, 0x201) & ~0x08000000); + } else if ((cur_cpu_spec->pvr_value & 0xffff0000) == 0x12910000) { + mtdcri(SDR0, 0x200, mfdcri(SDR0, 0x200) | 0x00000008); + mtdcri(SDR0, 0x200, mfdcri(SDR0, 0x200) & ~0x00000008); + } else if ((cur_cpu_spec->pvr_value & 0xffff0000) == 0x13540000) { + mtdcri(SDR0, 0x201, mfdcri(SDR0, 0x201) | 0x20000000); + mtdcri(SDR0, 0x201, mfdcri(SDR0, 0x201) & ~0x20000000); + } else { + printk(KERN_ERR "Crypto Function Not supported!\n"); + return -EINVAL; + } + + crypto4xx_write32(CRYPTO_ENGINE_BYTE_ORDER_CFG, 0x22222); + + /* setup pe dma, include reset sg, pdr and pe, then release reset */ + pe_dma_cfg.w = 0; + + pe_dma_cfg.bf.bo_sgpd_en = 1; + pe_dma_cfg.bf.bo_data_en = 0; + pe_dma_cfg.bf.bo_sa_en = 1; + pe_dma_cfg.bf.bo_pd_en = 1; + + pe_dma_cfg.bf.dynamic_sa_en = 1; + pe_dma_cfg.bf.reset_sg = 1; + pe_dma_cfg.bf.reset_pdr = 1; + pe_dma_cfg.bf.reset_pe = 1; + + crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w); + + /* un reset pe,sg and pdr */ + pe_dma_cfg.bf.pe_mode = 0; + pe_dma_cfg.bf.reset_sg = 0; + pe_dma_cfg.bf.reset_pdr = 0; + pe_dma_cfg.bf.reset_pe = 0; + pe_dma_cfg.bf.bo_td_en = 0; + + crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w); + + crypto4xx_write32(CRYPTO_ENGINE_PDR_BASE, dev->pdr_pa); + crypto4xx_write32(CRYPTO_ENGINE_RDR_BASE, dev->pdr_pa); + + crypto4xx_write32(CRYPTO_ENGINE_PRNG_CTRL, 3); + get_random_bytes(&rand_num, sizeof(rand_num)); + crypto4xx_write32(CRYPTO_ENGINE_PRNG_SEED_L, rand_num); + get_random_bytes(&rand_num, sizeof(rand_num)); + crypto4xx_write32(CRYPTO_ENGINE_PRNG_SEED_L, rand_num); + + ring_size.w = 0; + ring_size.bf.ring_offset = PPC4XX_PD_SIZE; + ring_size.bf.ring_size = PPC4XX_NUM_PD; + crypto4xx_write32(CRYPTO_ENGINE_RING_SIZE, ring_size.w); + + ring_ctrl.w = 0; + crypto4xx_write32(CRYPTO_ENGINE_RING_CTRL, ring_ctrl.w); + crypto4xx_write32(CRYPTO_ENGINE_DC_CTRL, 1); + + crypto4xx_write32(CRYPTO_ENGINE_GATH_RING_BASE, dev->gdr_pa); + crypto4xx_write32(CRYPTO_ENGINE_SCAT_RING_BASE, dev->sdr_pa); + + part_ring_size.w = 0; + part_ring_size.bf.sdr_size = PPC4XX_SDR_SIZE; + part_ring_size.bf.gdr_size = PPC4XX_GDR_SIZE; + crypto4xx_write32(CRYPTO_ENGINE_PART_RING_SIZE, part_ring_size.w); + + crypto4xx_write32(CRYPTO_ENGINE_PART_RING_CFG, + 0x0000ffff & PPC4XX_SD_BUFFER_SIZE); + io_threshold.w = 0; + io_threshold.bf.output_threshold = PPC4XX_OUTPUT_THRESHOLD; + io_threshold.bf.input_threshold = PPC4XX_INPUT_THRESHOLD; + crypto4xx_write32(CRYPTO_ENGINE_IO_THRESHOLD, io_threshold.w); + + crypto4xx_write32(CRYPTO_ENGINE_PDR_BASE_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_RDR_BASE_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_PKT_SRC_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_PKT_DEST_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_SA_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_GATH_RING_BASE_UADDR, 0x0); + crypto4xx_write32(CRYPTO_ENGINE_SCAT_RING_BASE_UADDR, 0x0); + + /* un reset pe,sg and pdr */ + pe_dma_cfg.bf.pe_mode = 1; + pe_dma_cfg.bf.reset_sg = 0; + pe_dma_cfg.bf.reset_pdr = 0; + pe_dma_cfg.bf.reset_pe = 0; + pe_dma_cfg.bf.bo_td_en = 0; + + crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w); + /*clear all pending interrupt*/ + crypto4xx_write32(CRYPTO_ENGINE_INT_CLR, 0x3ffff); + crypto4xx_write32(CRYPTO_ENGINE_INT_DESCR_CNT, PPC4XX_INT_DESCR_CNT); + + crypto4xx_write32(CRYPTO_ENGINE_INT_TIMEOUT_CNT, + PPC4XX_INT_TIMEOUT_CNT); + crypto4xx_write32(CRYPTO_ENGINE_INT_CFG, PPC4XX_INT_CFG); + crypto4xx_write32(CRYPTO_ENGINE_INT_EN, CRYPTO_PD_DONE_INT); + + return rc; +} + +void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size) +{ + ctx->sa_in = dma_alloc_coherent(NULL, size * 4, + &ctx->sa_in_dma_addr, GFP_ATOMIC); + ctx->sa_out = dma_alloc_coherent(NULL, size * 4, + &ctx->sa_out_dma_addr, GFP_ATOMIC); + ctx->sa_len = size; +} + +void crypto4xx_free_sa(struct crypto4xx_ctx *ctx) +{ + if (ctx->sa_in != NULL) + dma_free_coherent(NULL, ctx->sa_len*4, + ctx->sa_in, ctx->sa_in_dma_addr); + if (ctx->sa_out != NULL) + dma_free_coherent(NULL, ctx->sa_len*4, + ctx->sa_out, ctx->sa_out_dma_addr); + + ctx->sa_in_dma_addr = 0; + ctx->sa_out_dma_addr = 0; + ctx->sa_len = 0; +} + +u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx) +{ + ctx->state_record = dma_alloc_coherent(NULL, + sizeof(struct dynamic_sa_state_record), + &ctx->state_record_dma_addr, GFP_ATOMIC); + if (!ctx->state_record_dma_addr) + return -ENOMEM; + memset(ctx->state_record, 0, sizeof(struct dynamic_sa_state_record)); + return 0; +} + +void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx) +{ + if (ctx->state_record != NULL) + dma_free_coherent(NULL, + sizeof(struct dynamic_sa_state_record), + ctx->state_record, + ctx->state_record_dma_addr); + ctx->state_record_dma_addr = 0; +} + +/** + * alloc memory for the gather ring + * no need to alloc buf for the ring + * gdr_tail, gdr_head and gdr_count are initialized by this function + */ +u32 crypto4xx_build_pdr(struct crypto4xx_device *dev) +{ + dev->pdr = dma_alloc_coherent(NULL, + sizeof(struct ce_pd) * PPC4XX_NUM_PD, + &dev->pdr_pa, GFP_ATOMIC); + if (!dev->pdr) + return -ENOMEM; + dev->pdr_uinfo = kzalloc(sizeof(struct pd_uinfo) * PPC4XX_NUM_PD, + GFP_KERNEL); + if (!dev->pdr_uinfo) + return -ENOMEM; + + memset(dev->pdr, 0, sizeof(struct ce_pd) * PPC4XX_NUM_PD); + return 0; +} + +void crypto4xx_destroy_pdr(struct crypto4xx_device *dev) +{ + if (dev->pdr != NULL) + dma_free_coherent(NULL, + sizeof(struct ce_pd) * PPC4XX_NUM_PD, + dev->pdr, + dev->pdr_pa); + if (dev->pdr_uinfo != NULL) + kfree(dev->pdr_uinfo); +} + +u32 crypto4xx_get_pd_from_pdr(struct crypto4xx_device *dev) +{ + u32 retval; + u32 tmp; + + retval = dev->pdr_head; + tmp = (dev->pdr_head + 1) % PPC4XX_NUM_PD; + + if (tmp == dev->pdr_tail) + return ERING_WAS_FULL; + dev->pdr_head = tmp; + + return retval; +} + + +u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx) +{ + struct pd_uinfo *pd_uinfo; + + pd_uinfo = (struct pd_uinfo *)((dev->pdr_uinfo) + + sizeof(struct pd_uinfo)*idx); + + if (dev->pdr_tail != PPC4XX_LAST_PD) + dev->pdr_tail++; + else + dev->pdr_tail = 0; + pd_uinfo->state = PD_ENTRY_FREE; + + return 0; +} + +struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev, + dma_addr_t *pd_dma, u32 idx) +{ + *pd_dma = dev->pdr_pa + sizeof(struct ce_pd) * idx; + return dev->pdr + sizeof(struct ce_pd)*idx; +} + +/** + * alloc memory for the gather ring + * no need to alloc buf for the ring + * gdr_tail, gdr_head and gdr_count are initialized by this function + */ +u32 crypto4xx_build_gdr(struct crypto4xx_device *dev) +{ + dev->gdr = dma_alloc_coherent(NULL, + sizeof(struct ce_gd) * PPC4XX_NUM_GD, + &dev->gdr_pa, GFP_ATOMIC); + if (!dev->gdr) + return -ENOMEM; + memset(dev->gdr, 0, sizeof(struct ce_gd) * PPC4XX_NUM_GD); + return 0; +} + +void crypto4xx_destroy_gdr(struct crypto4xx_device *dev) +{ + dma_free_coherent(NULL, + sizeof(struct ce_gd) * PPC4XX_NUM_GD, + dev->gdr, dev->gdr_pa); +} + +u32 crypto4xx_get_gd_from_gdr(struct crypto4xx_device *dev) +{ + u32 retval; + u32 tmp; + + retval = dev->gdr_head; + tmp = (dev->gdr_head+1) % PPC4XX_NUM_GD; + + if (tmp == dev->gdr_tail) + return ERING_WAS_FULL; + dev->gdr_head = tmp; + return retval; +} + +u32 crypto4xx_put_gd_to_gdr(struct crypto4xx_device *dev) +{ + if (dev->gdr_tail == dev->gdr_head) + return 0; + + if (dev->gdr_tail != PPC4XX_LAST_GD) + dev->gdr_tail++; + else + dev->gdr_tail = 0; + + return 0; +} + +struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev, + dma_addr_t *gd_dma, u32 idx) +{ + *gd_dma = dev->gdr_pa + sizeof(struct ce_gd)*idx; + return (struct ce_gd *) (dev->gdr + sizeof(struct ce_gd) * idx); +} + +/** + * alloc memory for the scatter ring + * need to alloc buf for the ring + * sdr_tail, sdr_head and sdr_count are initialized by this function + */ +u32 crypto4xx_build_sdr(struct crypto4xx_device *dev) +{ + int i; + struct ce_sd *sd_array; + /* alloc memory for scatter descriptor ring */ + dev->sdr = dma_alloc_coherent(NULL, + sizeof(struct ce_sd) * PPC4XX_NUM_SD, + &dev->sdr_pa, GFP_ATOMIC); + if (!dev->sdr) + return -ENOMEM; + + dev->scatter_buffer_size = PPC4XX_SD_BUFFER_SIZE; + dev->scatter_buffer_va = + dma_alloc_coherent(NULL, + dev->scatter_buffer_size * PPC4XX_NUM_SD, + &dev->scatter_buffer_pa, GFP_ATOMIC); + if (!dev->scatter_buffer_va) + return -ENOMEM; + + sd_array = dev->sdr; + + for (i = 0; i < PPC4XX_NUM_SD; i++) { + sd_array[i].ptr = dev->scatter_buffer_pa + + dev->scatter_buffer_size * i; + } + return 0; +} + +void crypto4xx_destroy_sdr(struct crypto4xx_device *dev) +{ + dma_free_coherent(NULL, + sizeof(struct ce_sd) * PPC4XX_NUM_SD, + dev->sdr, + dev->sdr_pa); + + dma_free_coherent(NULL, + dev->scatter_buffer_size * PPC4XX_NUM_SD, + dev->scatter_buffer_va, + dev->scatter_buffer_pa); +} + +u32 crypto4xx_get_sd_from_sdr(struct crypto4xx_device *dev) +{ + u32 retval; + u32 tmp; + + retval = dev->sdr_head; + tmp = (dev->sdr_head+1) % PPC4XX_NUM_SD; + + if (tmp == dev->sdr_tail) + return ERING_WAS_FULL; + + dev->sdr_head = tmp; + return retval; +} + +u32 crypto4xx_put_sd_to_sdr(struct crypto4xx_device *dev) +{ + if (dev->sdr_tail == dev->sdr_head) + return 0; + + if (dev->sdr_tail != PPC4XX_LAST_SD) + dev->sdr_tail++; + else + dev->sdr_tail = 0; + + return 0; +} + +struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev, + dma_addr_t *sd_dma, u32 idx) +{ + *sd_dma = dev->sdr_pa + sizeof(struct ce_sd) * idx; + return (struct ce_sd *)(dev->sdr + sizeof(struct ce_sd) * idx); +} + +u32 crypto4xx_fill_one_page(dma_addr_t *addr, u32 *length, + u32 *idx, u32 *offset, u32 *nbytes) + +{ + struct crypto4xx_device *dev = &(lsec_core.dev); + u32 len; + if ((*length) > dev->scatter_buffer_size) { + memcpy(phys_to_virt(*addr), + dev->scatter_buffer_va + + (*idx)*dev->scatter_buffer_size + (*offset), + dev->scatter_buffer_size); + *offset = 0; + *length -= dev->scatter_buffer_size; + *nbytes -= dev->scatter_buffer_size; + if (*idx == PPC4XX_LAST_SD) + *idx = 0; + else + (*idx)++; + *addr = *addr + dev->scatter_buffer_size; + return 1; + } else if ((*length) < dev->scatter_buffer_size) { + memcpy(phys_to_virt(*addr), + dev->scatter_buffer_va + + (*idx)*dev->scatter_buffer_size + (*offset), + *length); + if ((*offset + *length) == dev->scatter_buffer_size) { + if (*idx == PPC4XX_LAST_SD) + *idx = 0; + else + (*idx)++; + *nbytes -= *length; + *offset = 0; + } else { + *nbytes -= *length; + *offset += *length; + } + + return 0; + } else { + len = (*nbytes <= + dev->scatter_buffer_size) ? + (*nbytes) : dev->scatter_buffer_size; + memcpy(phys_to_virt(*addr), + dev->scatter_buffer_va + + (*idx)*dev->scatter_buffer_size + (*offset), + len); + *offset = 0; + *nbytes -= len; + + if (*idx == PPC4XX_LAST_SD) + *idx = 0; + else + (*idx)++; + + return 0; + } +} + +void crypto4xx_copy_pkt_to_dst(struct ce_pd *pd, + struct pd_uinfo *pd_uinfo, + u32 nbytes, + struct scatterlist *dst, + u8 type) +{ + struct crypto4xx_device *dev = &(lsec_core.dev); + dma_addr_t addr; + u32 this_sd; + u32 offset; + u32 len; + u32 i; + u32 sg_len; + struct scatterlist *sg; + this_sd = pd_uinfo->first_sd; + offset = 0; + i = 0; + + while (nbytes) { + sg = &dst[i]; + sg_len = sg->length; + addr = dma_map_page(NULL, sg_page(sg), sg->offset, + sg->length, DMA_TO_DEVICE); + + if (offset == 0) { + len = (nbytes <= sg->length) ? nbytes : sg->length; + while (crypto4xx_fill_one_page(&addr, &len, + &this_sd, &offset, &nbytes)) + ; + if (!nbytes) + return ; + i++; + + } else { + len = (nbytes <= (dev->scatter_buffer_size - offset)) ? + nbytes : (dev->scatter_buffer_size - offset); + len = (sg->length < len) ? sg->length : len; + while (crypto4xx_fill_one_page(&addr, + &len, &this_sd, &offset, &nbytes)) + ; + if (!nbytes) + return; + sg_len -= len; + if (sg_len) { + addr += len; + while (crypto4xx_fill_one_page(&addr, &sg_len, + &this_sd, &offset, &nbytes)) + ; + } + i++; + } + } +} + +u32 crypto4xx_copy_digest_to_dst(struct pd_uinfo *pd_uinfo, + struct crypto4xx_ctx *ctx) +{ + struct dynamic_sa_ctl *sa = (struct dynamic_sa_ctl *)(ctx->sa_in); + struct dynamic_sa_state_record *state_record = + (struct dynamic_sa_state_record *)(ctx->state_record); + + if (sa->sa_command_0.bf.hash_alg == SA_HASH_ALG_SHA1) { + memcpy((void *)pd_uinfo->dest_va, state_record->save_digest, + SA_HASH_ALG_SHA1_DIGEST_SIZE); + } + return 0; +} + + +void crypto4xx_ret_sg_desc(struct crypto4xx_device *dev, + struct pd_uinfo *pd_uinfo) +{ + int i; + struct ce_sd *sd = NULL; + + if (pd_uinfo->first_gd != 0xffffffff) { + if (pd_uinfo->first_gd <= pd_uinfo->last_gd) { + for (i = pd_uinfo->first_gd; + i <= pd_uinfo->last_gd; i++) + crypto4xx_put_gd_to_gdr(dev); + + } else { + for (i = pd_uinfo->first_gd; + i < PPC4XX_NUM_GD; i++) + crypto4xx_put_gd_to_gdr(dev); + for (i = 0; i <= pd_uinfo->last_gd; i++) + crypto4xx_put_gd_to_gdr(dev); + } + } + + if (pd_uinfo->first_sd != 0xffffffff) { + if (pd_uinfo->first_sd <= pd_uinfo->last_sd) { + for (i = pd_uinfo->first_sd; + i <= pd_uinfo->last_sd; i++) { + sd = (struct ce_sd *)(dev->sdr + + sizeof(struct ce_sd)*i); + sd->ctl.done = 0; + sd->ctl.rdy = 0; + crypto4xx_put_sd_to_sdr(dev); + } + } else { + for (i = pd_uinfo->first_sd; i < PPC4XX_NUM_SD; i++) { + sd = (struct ce_sd *)(dev->sdr + + sizeof(struct ce_sd)*i); + sd->ctl.done = 0; + sd->ctl.rdy = 0; + crypto4xx_put_sd_to_sdr(dev); + } + for (i = 0; i <= pd_uinfo->last_sd; i++) { + sd = (struct ce_sd *)(dev->sdr + + sizeof(struct ce_sd)*i); + sd->ctl.done = 0; + sd->ctl.rdy = 0; + crypto4xx_put_sd_to_sdr(dev); + } + } + } + + pd_uinfo->first_gd = pd_uinfo->last_gd = 0xffffffff; + pd_uinfo->first_sd = pd_uinfo->last_sd = 0xffffffff; +} + + +u32 crypto4xx_ablkcipher_done(struct pd_uinfo *pd_uinfo, struct ce_pd *pd) +{ + struct crypto4xx_ctx *ctx; + struct crypto4xx_ctx *rctx = NULL; + struct ablkcipher_request *ablk_req; + struct scatterlist *dst; + dma_addr_t addr; + + ablk_req = ablkcipher_request_cast(pd_uinfo->async_req); + ctx = crypto_tfm_ctx(ablk_req->base.tfm); + + if (ctx->use_rctx == 1) + rctx = ablkcipher_request_ctx(ablk_req); + + if (pd_uinfo->using_sd) { + crypto4xx_copy_pkt_to_dst(pd, + pd_uinfo, + ablk_req->nbytes, + ablk_req->dst, + CRYPTO_ALG_TYPE_ABLKCIPHER); + } else { + dst = pd_uinfo->dest_va; + addr = dma_map_page(NULL, sg_page(dst), dst->offset, + dst->length, DMA_FROM_DEVICE); + } + crypto4xx_ret_sg_desc(&(lsec_core.dev), pd_uinfo); + if (rctx != NULL) + crypto4xx_free_sa_rctx(rctx); + if (ablk_req->base.complete != NULL) + ablk_req->base.complete(&ablk_req->base, 0); + return 0; +} + +u32 crypto4xx_ahash_done(struct pd_uinfo *pd_uinfo) +{ + struct crypto4xx_ctx *ctx; + struct crypto4xx_ctx *rctx = NULL; + struct ahash_request *ahash_req; + + ahash_req = ahash_request_cast(pd_uinfo->async_req); + ctx = crypto_tfm_ctx(ahash_req->base.tfm); + + crypto4xx_copy_digest_to_dst(pd_uinfo, + crypto_tfm_ctx(ahash_req->base.tfm)); + crypto4xx_ret_sg_desc(&(lsec_core.dev), pd_uinfo); + + if (ctx->use_rctx == 1) { + rctx = ahash_request_ctx(ahash_req); + if (rctx != NULL) { + if (rctx->sa_in_dma_addr) + dma_free_coherent(NULL, + rctx->sa_len * 4, + rctx->sa_in, + rctx->sa_in_dma_addr); + if (rctx->sa_out_dma_addr) + dma_free_coherent(NULL, + rctx->sa_len * 4, + rctx->sa_out, + rctx->sa_out_dma_addr); + } + } + /* call user provided callback function x */ + if (ahash_req->base.complete != NULL) + ahash_req->base.complete(&ahash_req->base, 0); + return 0; +} + +u32 crypto4xx_pd_done(struct crypto4xx_core_device *lsec, u32 idx) +{ + struct ce_pd *pd; + struct pd_uinfo *pd_uinfo; + + pd = lsec->dev.pdr + sizeof(struct ce_pd)*idx; + pd_uinfo = lsec->dev.pdr_uinfo + sizeof(struct pd_uinfo)*idx; + if (crypto_tfm_alg_type(pd_uinfo->async_req->tfm) == + CRYPTO_ALG_TYPE_ABLKCIPHER) + return crypto4xx_ablkcipher_done(pd_uinfo, pd); + else + return crypto4xx_ahash_done(pd_uinfo); + return 0; +} + +u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx, + struct crypto4xx_ctx *rctx) +{ + int rc; + struct dynamic_sa_ctl *sa = NULL; + + if (ctx->direction == CRYPTO_INBOUND) { + sa = (struct dynamic_sa_ctl *)(ctx->sa_in); + rctx->sa_in = dma_alloc_coherent(NULL, + ctx->sa_len*4, + &rctx->sa_in_dma_addr, GFP_ATOMIC); + if (rctx->sa_in == NULL) + return -ENOMEM; + memcpy(rctx->sa_in, ctx->sa_in, ctx->sa_len*4); + rctx->sa_out = NULL; + rctx->sa_out_dma_addr = 0; + } else { + sa = (struct dynamic_sa_ctl *)(ctx->sa_out); + rctx->sa_out = dma_alloc_coherent(NULL, + ctx->sa_len*4, + &rctx->sa_out_dma_addr, GFP_ATOMIC); + if (rctx->sa_out == NULL) + return -ENOMEM; + + memcpy(rctx->sa_out, ctx->sa_out, ctx->sa_len*4); + rctx->sa_in = NULL; + rctx->sa_in_dma_addr = 0; + } + + if (sa->sa_contents & 0x20000000) { + rc = crypto4xx_alloc_state_record(rctx); + if (rc != 0) + return -ENOMEM; + + memcpy(rctx->state_record, ctx->state_record, 16); + } else { + rctx->state_record = NULL; + } + + rctx->direction = ctx->direction; + rctx->sa_len = ctx->sa_len; + rctx->bypass = ctx->bypass; + + return 0; +} + +void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx) +{ + if (rctx->sa_in != NULL) + dma_free_coherent(NULL, + rctx->sa_len * 4, + rctx->sa_in, + rctx->sa_in_dma_addr); + + if (rctx->sa_out != NULL) + dma_free_coherent(NULL, + rctx->sa_len * 4, + rctx->sa_out, + rctx->sa_out_dma_addr); + + crypto4xx_free_state_record(rctx); + rctx->sa_len = 0; + rctx->state_record = NULL; + rctx->state_record_dma_addr = 0; +} + +void crypto4xx_memcpy_le(unsigned int *dst, + const unsigned char *buf, + int len) +{ + /* SA is in big endian */ + for (; len; buf += 4, len -= 4) + *dst++ = cpu_to_le32(*(unsigned int *) buf); +} + +u32 crypto4xx_stop_all(void) +{ + crypto4xx_destroy_pdr(&lsec_core.dev); + crypto4xx_destroy_sdr(&lsec_core.dev); + crypto4xx_destroy_gdr(&lsec_core.dev); + + return 0; +} + +u32 crypto4xx_build_pd(struct crypto4xx_device *dev, + struct crypto_async_request *req, + u32 pd_entry, + struct crypto4xx_ctx *ctx, + struct scatterlist *src, + struct scatterlist *dst, + u16 datalen, + u8 type) +{ + dma_addr_t addr, pd_dma, sd_dma, gd_dma; + struct dynamic_sa_ctl *sa; + struct scatterlist *sg; + struct ce_pd *pd; + struct pd_uinfo *pd_uinfo; + unsigned int nbytes = datalen, idx; + struct ce_gd *gd = NULL; + u32 gd_idx = 0; + struct ce_sd *sd = NULL; + u32 sd_idx = 0; + + pd = crypto4xx_get_pdp(dev, &pd_dma, pd_entry); + pd_uinfo = (struct pd_uinfo *)((dev->pdr_uinfo) + + sizeof(struct pd_uinfo)*pd_entry); + pd_uinfo->async_req = req; + + if (ctx->direction == CRYPTO_INBOUND) { + pd->sa = ctx->sa_in_dma_addr; + sa = (struct dynamic_sa_ctl *)ctx->sa_in; + } else { + pd->sa = ctx->sa_out_dma_addr; + sa = (struct dynamic_sa_ctl *)ctx->sa_out; + } + + pd->sa_len = ctx->sa_len; + + /* If first is last then we are single */ + if (sg_is_last(src)) { + pd->src = dma_map_page(NULL, sg_page(src), + src->offset, src->length, + DMA_TO_DEVICE); + /* Disable gather in sa command */ + sa->sa_command_0.bf.gather = 0; + /* Indicate gather array is not used */ + pd_uinfo->first_gd = pd_uinfo->last_gd = 0xffffffff; + } else { + src = &src[0]; + /* get first gd we are going to use */ + gd_idx = crypto4xx_get_gd_from_gdr(dev); + if (gd_idx == ERING_WAS_FULL) { + crypto4xx_ret_sg_desc(dev, pd_uinfo); + return -EAGAIN; + } + pd_uinfo->first_gd = gd_idx; + gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx); + pd->src = gd_dma; + /* Enable gather */ + sa->sa_command_0.bf.gather = 1; + idx = 0; + + /* walk the sg, and setup gather array */ + /* Seems that CRYPTO_ENGINE DMA is byte align, + so we can use ptr directly from sg */ + while (nbytes != 0) { + sg = &src[idx]; + addr = dma_map_page(NULL, sg_page(sg), + sg->offset, sg->length, + DMA_TO_DEVICE); + gd->ptr = addr; + gd->ctl_len.len = sg->length; + gd->ctl_len.done = 0; + gd->ctl_len.ready = 1; + nbytes -= sg->length; + if (!nbytes) + break; + /* Get first gd we are going to use */ + gd_idx = crypto4xx_get_gd_from_gdr(dev); + if (gd_idx == ERING_WAS_FULL) { + crypto4xx_ret_sg_desc(dev, pd_uinfo); + return -EAGAIN; + } + gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx); + pd_uinfo->last_gd = gd_idx; + idx++; + } + } + + + if (ctx->is_hash || sg_is_last(dst)) { + /* we know application give us dst a whole piece of memory */ + /* no need to use scatter ring */ + pd_uinfo->using_sd = 0; + pd_uinfo->first_sd = pd_uinfo->last_sd = 0xffffffff; + pd_uinfo->dest_va = dst; + sa->sa_command_0.bf.scatter = 0; + if (ctx->is_hash) { + pd->dest = virt_to_phys((void *)dst); + } else { + pd->dest = dma_map_page(NULL, sg_page(dst), + dst->offset, dst->length, + DMA_TO_DEVICE); + } + + } else { + nbytes = datalen; + sa->sa_command_0.bf.scatter = 1; + pd_uinfo->using_sd = 1; + + sd_idx = crypto4xx_get_sd_from_sdr(dev); + if (sd_idx == ERING_WAS_FULL) { + crypto4xx_ret_sg_desc(dev, pd_uinfo); + return -EAGAIN; + } + pd_uinfo->first_sd = pd_uinfo->last_sd = sd_idx; + sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx); + pd->dest = sd_dma; + wmb(); + /* setup scatter descriptor */ + sd->ctl.done = 0; + sd->ctl.rdy = 1; + /* sd->ptr should be setup by sd_init routine*/ + if (nbytes >= PPC4XX_SD_BUFFER_SIZE) + nbytes -= PPC4XX_SD_BUFFER_SIZE; + else if (nbytes < PPC4XX_SD_BUFFER_SIZE) + nbytes = 0; + while (nbytes) { + sd_idx = crypto4xx_get_sd_from_sdr(dev); + if (sd_idx == ERING_WAS_FULL) { + crypto4xx_ret_sg_desc(dev, pd_uinfo); + /*Fixme implement some error code later */ + return -EAGAIN; + } + sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx); + pd_uinfo->last_sd = sd_idx; + /* setup scatter descriptor */ + sd->ctl.done = 0; + sd->ctl.rdy = 1; + if (nbytes >= PPC4XX_SD_BUFFER_SIZE) + nbytes -= PPC4XX_SD_BUFFER_SIZE; + else + nbytes = 0; + } + } + pd->pd_ctl.w = ctx->pd_ctl; + pd->pd_ctl_len.w = 0x00400000 | (ctx->bypass<<24) | datalen; + pd_uinfo->state = PD_ENTRY_INUSE; + crypto4xx_write32(CRYPTO_ENGINE_INT_DESCR_RD, 1); + + return -EINPROGRESS; + +} + +u32 crypto4xx_start_device(struct crypto4xx_device *dev) +{ + u32 rc ; + rc = crypto4xx_init(dev); + return rc; +} + +int crypto4xx_handle_req(struct crypto_async_request *req) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->tfm); + struct crypto4xx_device *dev = ctx->dev; + struct crypto4xx_ctx *rctx; + struct pd_uinfo *pd_uinfo; + + int ret = -EAGAIN; + + u32 pd_entry; + + pd_entry = crypto4xx_get_pd_from_pdr(dev); /* index to the entry */ + if (pd_entry == ERING_WAS_FULL) + return -EAGAIN; + + pd_uinfo = (struct pd_uinfo *)((dev->pdr_uinfo) + + sizeof(struct pd_uinfo)*pd_entry); + + if (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_ABLKCIPHER) { + struct ablkcipher_request *ablk_req; + ablk_req = ablkcipher_request_cast(req); + if (ctx->use_rctx) { + rctx = ablkcipher_request_ctx(ablk_req); + return crypto4xx_build_pd(dev, req, pd_entry, rctx, + ablk_req->src, ablk_req->dst, + ablk_req->nbytes, ABLK); + } else { + return crypto4xx_build_pd(dev, req, pd_entry, ctx, + ablk_req->src, ablk_req->dst, + ablk_req->nbytes, + ABLK); + } + } else { + struct ahash_request *ahash_req; + ahash_req = ahash_request_cast(req); + if (ctx->use_rctx) { + rctx = ahash_request_ctx(ahash_req); + return crypto4xx_build_pd(dev, req, pd_entry, rctx, + ahash_req->src, + (struct scatterlist *) ahash_req->result, + ahash_req->nbytes, + AHASH); + } else { + return crypto4xx_build_pd(dev, req, pd_entry, ctx, + ahash_req->src, + (struct scatterlist *) ahash_req->result, + ahash_req->nbytes, + AHASH); + } + } + return ret; +} + +int crypto4xx_setup_crypto(struct crypto_async_request *req) +{ + return crypto4xx_handle_req(req); +} + +/** + * Algorithm Registration Functions + * + */ +static int crypto4xx_alg_init(struct crypto_tfm *tfm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + struct crypto4xx_alg *amcc_alg = crypto_alg_to_crypto4xx_alg(alg); + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm); + + ctx->dev = amcc_alg->dev; + ctx->sa_in = NULL; + ctx->sa_out = NULL; + ctx->sa_in_dma_addr = 0; + ctx->sa_out_dma_addr = 0; + ctx->sa_len = 0; + + if (alg->cra_type == &crypto_ablkcipher_type) + tfm->crt_ablkcipher.reqsize = sizeof(struct crypto4xx_ctx); + else if (alg->cra_type == &crypto_ahash_type) + tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx); + return 0; +} + +void crypto4xx_alg_exit(struct crypto_tfm *tfm) +{ + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm); + crypto4xx_free_sa(ctx); + crypto4xx_free_state_record(ctx); +} + +int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, + struct crypto_alg *crypto_alg, int array_size) +{ + struct crypto4xx_alg *alg; + int i; + int rc = 0; + + for (i = 0; i < array_size; i++) { + alg = kzalloc(sizeof(struct crypto4xx_alg), GFP_KERNEL); + if (!alg) + return -ENOMEM; + + alg->alg = crypto_alg[i]; + INIT_LIST_HEAD(&alg->alg.cra_list); + if (alg->alg.cra_init == NULL) + alg->alg.cra_init = crypto4xx_alg_init; + if (alg->alg.cra_exit == NULL) + alg->alg.cra_exit = crypto4xx_alg_exit; + alg->dev = sec_dev; + list_add_tail(&alg->entry, &sec_dev->alg_list); + rc = crypto_register_alg(&alg->alg); + if (rc) { + list_del(&alg->entry); + kfree(alg); + return rc; + } + } + return rc; +} + +static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev) +{ + struct crypto4xx_alg *alg, *tmp; + + list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) { + list_del(&alg->entry); + crypto_unregister_alg(&alg->alg); + kfree(alg); + } +} + +static void crypto4xx_bh_tasklet_cb(unsigned long data) +{ + struct crypto4xx_core_device *lsec; + struct pd_uinfo *pd_uinfo; + struct ce_pd *pd; + u32 tail; + + lsec = (struct crypto4xx_core_device *) data; + + while (lsec->dev.pdr_head != lsec->dev.pdr_tail) { + tail = lsec->dev.pdr_tail; + pd_uinfo = lsec->dev.pdr_uinfo + sizeof(struct pd_uinfo)*tail; + pd = lsec->dev.pdr + sizeof(struct ce_pd)*tail; + if ((pd_uinfo->state == PD_ENTRY_INUSE) && + pd->pd_ctl.bf.pe_done && + !pd->pd_ctl.bf.host_ready) { + pd->pd_ctl.bf.pe_done = 0; + crypto4xx_pd_done(lsec, tail); + crypto4xx_put_pd_to_pdr(&(lsec->dev), tail); + pd_uinfo->state = PD_ENTRY_FREE; + } else { + /* if tail not done, break */ + break; + } + } +} + +/** + * Top Half of isr. + */ +static int crypto4xx_ce_interrupt_handler(int irq, void *id) +{ + if (lsec_core.ce_base == 0) + return 0; + + lsec_core.irq_cnt++; + crypto4xx_write32(CRYPTO_ENGINE_INT_CLR, 0x3ffff); + tasklet_schedule(&lsec_core.tasklet); + + return IRQ_HANDLED; +} + +/** + * Module Initialization Routine + * + */ +static int __init crypto4xx_crypto_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + int rc; + struct resource res; + + lsec_core.ce_base = 0; + lsec_core.irq_cnt = 0ll; + + memset(&lsec_core.dev, 0, sizeof(struct crypto4xx_device)); + + INIT_LIST_HEAD(&lsec_core.dev.alg_list); + + crypto4xx_build_pdr(&(lsec_core.dev)); + crypto4xx_build_gdr(&(lsec_core.dev)); + crypto4xx_build_sdr(&(lsec_core.dev)); + + /* Init tasklet for bottom half processing */ + tasklet_init(&lsec_core.tasklet, crypto4xx_bh_tasklet_cb, + (unsigned long)&lsec_core); + + /* Register for Crypto isr, Crypto Engine IRQ */ + lsec_core.irq = of_irq_to_resource(ofdev->node, 0, NULL); + rc = request_irq(lsec_core.irq, crypto4xx_ce_interrupt_handler, 0, + lsec_core.dev.name, NULL); + if (rc) + goto err_request_irq; + + rc = of_address_to_resource(ofdev->node, 0, &res); + if (rc) + return -ENODEV; + + lsec_core.ce_phy_address = res.start; + lsec_core.ce_base = ioremap(lsec_core.ce_phy_address, + res.end - res.start + 1); + + /* need to setup pdr, rdr, gdr and sdr */ + rc = crypto4xx_start_device(&lsec_core.dev); + if (rc) + goto err_start_device; + + /* Register security algorithms with Linux CryptoAPI */ + rc = crypto4xx_register_basic_alg(); + if (rc) + goto err_register_alg; + + printk(KERN_INFO "Loaded AMCC PPC4XX crypto " + "accelerator driver v%s\n", PPC4XX_SEC_VERSION_STR); + + return rc; + +err_register_alg: + crypto4xx_unregister_alg(&lsec_core.dev); +err_start_device: + free_irq(lsec_core.irq, &lsec_core.dev.name); +err_request_irq: + crypto4xx_stop_all(); + + return rc; +} + +static int __exit crypto4xx_crypto_remove(struct of_device *dev) +{ + free_irq(lsec_core.irq, NULL); + /* Un-register with Linux CryptoAPI */ + crypto4xx_unregister_alg(&lsec_core.dev); + /* Free all allocated memory */ + crypto4xx_stop_all(); + + printk(KERN_INFO "Unloaded AMCC PPC4XX crypto " + "accelerator driver v%s\n", PPC4XX_SEC_VERSION_STR); + + return 0; +} + +static struct of_device_id crypto4xx_crypto_match[] = { + { .compatible = "amcc,ppc4xx-crypto",}, + { }, +}; + +static struct of_platform_driver crypto4xx_crypto_driver = { + .name = "crypto4xx-crypto", + .match_table = crypto4xx_crypto_match, + .probe = crypto4xx_crypto_probe, + .remove = crypto4xx_crypto_remove, +}; + +static int __init crypto4xx_lsec_init(void) +{ + return of_register_platform_driver(&crypto4xx_crypto_driver); +} + +static void __exit crypto4xx_lsec_exit(void) +{ + of_unregister_platform_driver(&crypto4xx_crypto_driver); +} + +module_init(crypto4xx_lsec_init); +module_exit(crypto4xx_lsec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Hsiao "); +MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator"); + diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h new file mode 100644 index 0000000..b7a6191 --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -0,0 +1,200 @@ +/******************************************************************************* + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_core.h + * + * This is the header file for AMCC Crypto offload Linux device driver for + * use with Linux CryptoAPI. + * + ******************************************************************************* + */ + +#ifndef __CRYPTO4XX_CORE_H__ +#define __CRYPTO4XX_CORE_H__ + +#define CRYPTO4XX_CRYPTO_PRIORITY 300 + +#define PPC4XX_LAST_PD 63 +#define PPC4XX_NUM_PD 64 + +#define PPC4XX_LAST_GD 1023 +#define PPC4XX_NUM_GD 1024 + +#define PPC4XX_LAST_SD 63 +#define PPC4XX_NUM_SD 64 + +#define PPC4XX_SD_BUFFER_SIZE 2048 + +#define PPC4XX_INT_DESCR_CNT 4 +#define PPC4XX_INT_TIMEOUT_CNT 0 +/* FIXme arbitory number*/ +#define PPC4XX_INT_CFG 1 +/* + * These define will be used in crypto4xx_build_pd + * AHASH don't have dst scatterlist iso u8* + * with the type field it can destinguish what is + */ +#define ABLK 0 +#define AHASH 1 + +#define PD_ENTRY_INUSE 1 +#define PD_ENTRY_FREE 0 + +#define EALLOC_MEM_FAIL 0xfffffffd +#define EDOWNSEMA_FAIL 0xfffffffe +#define ERING_WAS_FULL 0xffffffff + +struct crypto4xx_device; +extern struct crypto4xx_core_device lsec_core; +extern struct crypto_alg crypto4xx_basic_alg[]; + +struct pd_uinfo { + struct crypto4xx_device *dev; + u32 state; + u32 using_sd; + void *pd_va; /* offset from pdr */ + void *rd_va; /* offset from rdr, could be + same as pdr(same as pd_va)*/ + u32 first_gd; /* first gather discriptor + used by this packet */ + u32 last_gd; /* last gather discriptor + used by this packet */ + u32 first_sd; /* first scatter discriptor + used by this packet */ + u32 last_sd; /* last scatter discriptor + used by this packet */ + u32 first_done; + u32 last_done; + struct scatterlist *dest_va; + u32 cryptype; + struct crypto_async_request *async_req; /* base crypto request + for this packet */ +}; + +struct crypto4xx_device { + u8 dev_id; /* Device ID - id of device to + send request to */ + char *name; + void *pdr; /* base address of packet + descriptor ring */ + dma_addr_t pdr_pa; /* physical address used to + program ce pdr_base_register */ + void *rdr; /* result descriptor ring, maybe same + location as pdr */ + dma_addr_t rdr_pa; /* physical address used to + program ce rdr_base_register */ + void *gdr; /* gather descriptor ring, + for inbound packet/fragments */ + /* address of particle is + from the request, src sg*/ + dma_addr_t gdr_pa; /* physical address used to + program ce gdr_base_register */ + void *sdr; /* scatter descriptor ring,for outbound + packet/fragments + must be same size, so init them + to 2k each safe for large + packets */ + dma_addr_t sdr_pa; /* physical address used to + program ce sdr_base_register */ + dma_addr_t scatter_buffer_pa; + void *scatter_buffer_va; + u32 scatter_buffer_size; + int pdr_tail; + int pdr_head; + u32 gdr_tail; + u32 gdr_head; + u32 sdr_tail; + u32 sdr_head; + void *pdr_uinfo; + struct list_head alg_list; /* List of algorithm supported + by this device */ +}; + +struct crypto4xx_core_device { + struct crypto4xx_device dev; + u32 int_status; + u32 irq; + u64 irq_cnt; + struct tasklet_struct tasklet; + u64 ce_phy_address; + void __iomem *ce_base; +}; + +struct crypto4xx_ctx { + struct crypto4xx_device *dev; + void *sa_in; + dma_addr_t sa_in_dma_addr; + void *sa_out; + dma_addr_t sa_out_dma_addr; + void *state_record; + dma_addr_t state_record_dma_addr; + u16 sa_len; + u32 direction; + u32 use_rctx; + u32 next_hdr; + u32 save_iv; + u32 pd_ctl_len; + u32 pd_ctl; + u32 bypass; + u32 is_hash; + u32 hash_final; +}; + +struct crypto4xx_req_ctx { + struct crypto4xx_device *dev; /* Device in which + operation to send to */ + void *sa; + dma_addr_t sa_dma_addr; + u16 sa_len; +}; + +struct crypto4xx_alg { + struct list_head entry; + struct crypto_alg alg; + struct crypto4xx_device *dev; +}; + +#define crypto_alg_to_crypto4xx_alg(x) \ + container_of(x, struct crypto4xx_alg, alg) + +extern void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size); +extern u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx, + struct crypto4xx_ctx *rctx); +extern void crypto4xx_free_ctx(struct crypto4xx_ctx *ctx); +extern u32 crypto4xx_pd_done(struct crypto4xx_core_device *lsec, u32 idx); +extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx); +extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx); +extern void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx); + +extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx); +extern u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx); +extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx); +extern void crypto4xx_memcpy_le(unsigned int *dst, + const unsigned char *buf, int len); +extern void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx); +extern int crypto4xx_handle_req(struct crypto_async_request *req); +extern u32 crypto4xx_build_pd(struct crypto4xx_device *dev, + struct crypto_async_request *req, + u32 pd_entry, + struct crypto4xx_ctx *ctx, + struct scatterlist *src, + struct scatterlist *dst, + u16 datalen, + u8 type); +extern int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, + struct crypto_alg *crypto_alg, int array_size); +extern int crypto4xx_register_basic_alg(void); +#endif diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h new file mode 100644 index 0000000..73003b1 --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h @@ -0,0 +1,291 @@ +/**************************************************************************** + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_reg_def.h + * + * This filr defines the register set for Security Subsystem + * + **************************************************************************** + */ + +#ifndef __CRYPTO_ENGINE_REG_DEF_H__ +#define __CRYPTO_ENGINE_REG_DEF_H__ + +/* CRYPTO_ENGINE Register offset */ +#define CRYPTO_ENGINE_DESCRIPTOR 0x00000000 +#define CRYPTO_ENGINE_CTRL_STAT 0x00000000 +#define CRYPTO_ENGINE_SOURCE 0x00000004 +#define CRYPTO_ENGINE_DEST 0x00000008 +#define CRYPTO_ENGINE_SA 0x0000000C +#define CRYPTO_ENGINE_SA_LENGTH 0x00000010 +#define CRYPTO_ENGINE_LENGTH 0x00000014 + + +#define CRYPTO_ENGINE_PE_DMA_CFG 0x00000040 +#define CRYPTO_ENGINE_PE_DMA_STAT 0x00000044 +#define CRYPTO_ENGINE_PDR_BASE 0x00000048 +#define CRYPTO_ENGINE_RDR_BASE 0x0000004c +#define CRYPTO_ENGINE_RING_SIZE 0x00000050 +#define CRYPTO_ENGINE_RING_CTRL 0x00000054 +#define CRYPTO_ENGINE_INT_RING_STAT 0x00000058 +#define CRYPTO_ENGINE_EXT_RING_STAT 0x0000005c +#define CRYPTO_ENGINE_IO_THRESHOLD 0x00000060 +#define CRYPTO_ENGINE_GATH_RING_BASE 0x00000064 +#define CRYPTO_ENGINE_SCAT_RING_BASE 0x00000068 +#define CRYPTO_ENGINE_PART_RING_SIZE 0x0000006c +#define CRYPTO_ENGINE_PART_RING_CFG 0x00000070 + +#define CRYPTO_ENGINE_PDR_BASE_UADDR 0x00000080 +#define CRYPTO_ENGINE_RDR_BASE_UADDR 0x00000084 +#define CRYPTO_ENGINE_PKT_SRC_UADDR 0x00000088 +#define CRYPTO_ENGINE_PKT_DEST_UADDR 0x0000008c +#define CRYPTO_ENGINE_SA_UADDR 0x00000090 +#define CRYPTO_ENGINE_GATH_RING_BASE_UADDR 0x000000A0 +#define CRYPTO_ENGINE_SCAT_RING_BASE_UADDR 0x000000A4 + +#define CRYPTO_ENGINE_SEQ_RD 0x00000408 +#define CRYPTO_ENGINE_SEQ_MASK_RD 0x0000040C + +#define CRYPTO_ENGINE_SA_CMD_0 0x00010600 +#define CRYPTO_ENGINE_SA_CMD_1 0x00010604 + +#define CRYPTO_ENGINE_STATE_PTR 0x000106dc +#define CRYPTO_ENGINE_STATE_IV 0x00010700 +#define CRYPTO_ENGINE_STATE_HASH_BYTE_CNT_0 0x00010710 +#define CRYPTO_ENGINE_STATE_HASH_BYTE_CNT_1 0x00010714 + +#define CRYPTO_ENGINE_STATE_IDIGEST_0 0x00010718 +#define CRYPTO_ENGINE_STATE_IDIGEST_1 0x0001071c + +#define CRYPTO_ENGINE_DATA_IN 0x00018000 +#define CRYPTO_ENGINE_DATA_OUT 0x0001c000 + + +#define CRYPTO_ENGINE_INT_UNMASK_STAT 0x000500a0 +#define CRYPTO_ENGINE_INT_MASK_STAT 0x000500a4 +#define CRYPTO_ENGINE_INT_CLR 0x000500a4 +#define CRYPTO_ENGINE_INT_EN 0x000500a8 + +#define CRYPTO_ENGINE_INT_PKA 0x00000002 +#define CRYPTO_ENGINE_INT_PDR_DONE 0x00008000 +#define CRYPTO_ENGINE_INT_MA_WR_ERR 0x00020000 +#define CRYPTO_ENGINE_INT_MA_RD_ERR 0x00010000 +#define CRYPTO_ENGINE_INT_PE_ERR 0x00000200 +#define CRYPTO_ENGINE_INT_USER_DMA_ERR 0x00000040 +#define CRYPTO_ENGINE_INT_SLAVE_ERR 0x00000010 +#define CRYPTO_ENGINE_INT_MASTER_ERR 0x00000008 +#define CRYPTO_ENGINE_INT_ERROR 0x00030258 + +#define CRYPTO_ENGINE_INT_CFG 0x000500ac +#define CRYPTO_ENGINE_INT_DESCR_RD 0x000500b0 +#define CRYPTO_ENGINE_INT_DESCR_CNT 0x000500b4 +#define CRYPTO_ENGINE_INT_TIMEOUT_CNT 0x000500b8 + +#define CRYPTO_ENGINE_DC_CTRL 0x00060080 +#define CRYPTO_ENGINE_DEVICE_ID 0x00060084 +#define CRYPTO_ENGINE_DEVICE_INFO 0x00060088 +#define CRYPTO_ENGINE_DMA_USER_SRC 0x00060094 +#define CRYPTO_ENGINE_DMA_USER_DEST 0x00060098 +#define CRYPTO_ENGINE_DMA_USER_CMD 0x0006009C + +#define CRYPTO_ENGINE_DMA_CFG 0x000600d4 +#define CRYPTO_ENGINE_BYTE_ORDER_CFG 0x000600d8 +#define CRYPTO_ENGINE_ENDIAN_CFG 0x000600d8 + +#define CRYPTO_ENGINE_PRNG_STAT 0x00070000 +#define CRYPTO_ENGINE_PRNG_CTRL 0x00070004 +#define CRYPTO_ENGINE_PRNG_SEED_L 0x00070008 +#define CRYPTO_ENGINE_PRNG_SEED_H 0x0007000c + +#define CRYPTO_ENGINE_PRNG_RES_0 0x00070020 +#define CRYPTO_ENGINE_PRNG_RES_1 0x00070024 +#define CRYPTO_ENGINE_PRNG_RES_2 0x00070028 +#define CRYPTO_ENGINE_PRNG_RES_3 0x0007002C + +#define CRYPTO_ENGINE_PRNG_LFSR_L 0x00070030 +#define CRYPTO_ENGINE_PRNG_LFSR_H 0x00070034 + +/** + * Initilize CRYPTO ENGINE registers, and memory bases. + */ + +#define PPC4XX_PDR_POLL 0x3ff +#define PPC4XX_OUTPUT_THRESHOLD 2 +#define PPC4XX_INPUT_THRESHOLD 2 +#define PPC4XX_PD_SIZE 6 +#define CRYPTO_CTX_DONE_INT 0x2000 +#define CRYPTO_PD_DONE_INT 0x8000 +/** + * all follow define are ad hoc + */ +#define PPC4XX_RING_RETRY 100 +#define PPC4XX_RING_POLL 100 +#define PPC4XX_SDR_SIZE PPC4XX_NUM_SD +#define PPC4XX_GDR_SIZE PPC4XX_NUM_GD + +/** + * + * IPE Generic Security Association (SA) with all possible fields. These will + * never likely used except for reference purpose. These structure format + * can be not changed as the hardware expects them to be layout as defined. + * Field can be removed or reduced but ordering can not be changed. + * + * + */ + +#define CRYPTO_ENGINE_DMA_CFG_OFFSET 0x40 +union ce_pe_dma_cfg { + struct { + u32 rsv:7; + u32 dir_host:1; + u32 rsv1:2; + u32 bo_td_en:1; + u32 dis_pdr_upd:1; + u32 bo_sgpd_en:1; + u32 bo_data_en:1; + u32 bo_sa_en:1; + u32 bo_pd_en:1; + u32 rsv2:4; + u32 dynamic_sa_en:1; + u32 pdr_mode:2; + u32 pe_mode:1; + u32 rsv3:5; + u32 reset_sg:1; + u32 reset_pdr:1; + u32 reset_pe:1; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_ENGINE_PDR_BASE_OFFSET 0x48 +#define CRYPTO_ENGINE_RDR_BASE_OFFSET 0x4c + +#define CRYPTO_ENGINE_RING_SIZE_OFFSET 0x50 +union ce_ring_size { + struct { + u32 ring_offset:16; + u32 rsv:6; + u32 ring_size:10; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_ENGINE_RING_CONTROL_OFFSET 0x54 +union ce_ring_contol { + struct { + u32 continuous:1; + u32 rsv:5; + u32 ring_retry_divisor:10; + u32 rsv1:4; + u32 ring_poll_divisor:10; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_ENGINE_IO_THRESHOLD_OFFSET 0x60 +union ce_io_threshold { + struct { + u32 rsv:6; + u32 output_threshold:10; + u32 rsv1:6; + u32 input_threshold:10; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_ENGINE_GATHER_RING_BASE_OFFSET 0x64 +#define CRYPTO_ENGINE_SCATTER_RING_BASE_OFFSET 0x68 + +union ce_part_ring_size { + struct { + u32 sdr_size:16; + u32 gdr_size:16; + } bf; + u32 w; +} __attribute__((packed)); + +#define MAX_BURST_SIZE_32 0 +#define MAX_BURST_SIZE_64 1 +#define MAX_BURST_SIZE_128 2 +#define MAX_BURST_SIZE_256 3 + +/* gather descriptor control length */ +struct gd_ctl_len { + u32 len:16; + u32 rsv:14; + u32 done:1; + u32 ready:1; +} __attribute__((packed)); + +struct ce_gd { + u32 ptr; + struct gd_ctl_len ctl_len; +} __attribute__((packed)); + +struct sd_ctl { + u32 ctl:30; + u32 done:1; + u32 rdy:1; +} __attribute__((packed)); + +struct ce_sd { + u32 ptr; + struct sd_ctl ctl; +} __attribute__((packed)); + +#define PD_PAD_CTL_32 0x10 +#define PD_PAD_CTL_64 0x20 +#define PD_PAD_CTL_128 0x40 +#define PD_PAD_CTL_256 0x80 +union ce_pd_ctl { + struct { + u32 pd_pad_ctl:8; + u32 status:8; + u32 next_hdr:8; + u32 rsv:2; + u32 cached_sa:1; + u32 hash_final:1; + u32 init_arc4:1; + u32 rsv1:1; + u32 pe_done:1; + u32 host_ready:1; + } bf; + u32 w; +} __attribute__((packed)); + +union ce_pd_ctl_len { + struct { + u32 bypass:8; + u32 pe_done:1; + u32 host_ready:1; + u32 rsv:2; + u32 pkt_len:20; + } bf; + u32 w; +} __attribute__((packed)); + +struct ce_pd { + union ce_pd_ctl pd_ctl; + dma_addr_t src; + dma_addr_t dest; + dma_addr_t sa; /* get from ctx->sa_dma_addr */ + u32 sa_len; /* only if dynamic sa is used */ + union ce_pd_ctl_len pd_ctl_len; + +} __attribute__((packed)); + + +#endif diff --git a/drivers/crypto/amcc/crypto4xx_sa.c b/drivers/crypto/amcc/crypto4xx_sa.c new file mode 100644 index 0000000..a7adfcf --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_sa.c @@ -0,0 +1,98 @@ +/**************************************************************************** + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_sa.c + * + * This file implements the security context + * assoicate format. + * + **************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crypto4xx_reg_def.h" +#include "crypto4xx_sa.h" +#include "crypto4xx_core.h" + +u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx) +{ + u32 offset; + union dynamic_sa_contents cts; + + if (ctx->direction == CRYPTO_INBOUND) + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents; + else + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents; + offset = cts.bf.key_size + + cts.bf.inner_size + + cts.bf.outer_size + + cts.bf.spi + + cts.bf.seq_num0 + + cts.bf.seq_num1 + + cts.bf.seq_num_mask0 + + cts.bf.seq_num_mask1 + + cts.bf.seq_num_mask2 + + cts.bf.seq_num_mask3; + + return sizeof(struct dynamic_sa_ctl) + offset * 4; +} + +u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx) +{ + u32 offset; + union dynamic_sa_contents cts; + + if (ctx->direction == CRYPTO_INBOUND) + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents; + else + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents; + offset = cts.bf.key_size + + cts.bf.inner_size + + cts.bf.outer_size + + cts.bf.spi + + cts.bf.seq_num0 + + cts.bf.seq_num1 + + cts.bf.seq_num_mask0 + + cts.bf.seq_num_mask1 + + cts.bf.seq_num_mask2 + + cts.bf.seq_num_mask3 + + cts.bf.iv0 + + cts.bf.iv1 + + cts.bf.iv2 + + cts.bf.iv3; + + return sizeof(struct dynamic_sa_ctl) + offset * 4; +} + +u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx) +{ + union dynamic_sa_contents cts; + + if (ctx->direction == CRYPTO_INBOUND) + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents; + else + cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents; + return (cts.bf.iv0 + cts.bf.iv1 + cts.bf.iv2 + cts.bf.iv3) * 4; +} diff --git a/drivers/crypto/amcc/crypto4xx_sa.h b/drivers/crypto/amcc/crypto4xx_sa.h new file mode 100644 index 0000000..f60a9d8 --- /dev/null +++ b/drivers/crypto/amcc/crypto4xx_sa.h @@ -0,0 +1,223 @@ +/**************************************************************************** + * AMCC SoC Crypto4XX Driver + * + * Copyright (c) 2008 Applied Micro Circuits Corporation. + * All rights reserved. James Hsiao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @file crypto4xx_sa.h + * + * This file defines the security context + * assoicate format. + * + **************************************************************************** + */ + +#ifndef __CRYPTO4XX_SA_H__ +#define __CRYPTO4XX_SA_H__ + +#define u32 unsigned int + +/** + * + * Contents of Dynamic Security Association (SA) with all possible fields + */ +union dynamic_sa_contents { + struct { + u32 arc4_state_ptr:1; + u32 arc4_ij_ptr:1; + u32 state_ptr:1; + u32 iv3:1; + u32 iv2:1; + u32 iv1:1; + u32 iv0:1; + u32 seq_num_mask3:1; + u32 seq_num_mask2:1; + u32 seq_num_mask1:1; + u32 seq_num_mask0:1; + u32 seq_num1:1; + u32 seq_num0:1; + u32 spi:1; + u32 outer_size:5; + u32 inner_size:5; + u32 key_size:4; + u32 cmd_size:4; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_OUTBOUND 0 +#define CRYPTO_INBOUND 1 + +#define SA_OPCODE_ENCRYPT 0 +#define SA_OPCODE_DECRYPT 0 + +#define SA_OPCODE_HASH 3 + +#define SA_CIPHER_ALG_DES 0 +#define SA_CIPHER_ALG_3DES 1 +#define SA_CIPHER_ALG_ARC4 2 +#define SA_CIPHER_ALG_AES 3 +#define SA_CIPHER_ALG_KASUMI 4 +#define SA_CIPHER_ALG_NULL 15 + +#define SA_HASH_ALG_MD5 0 +#define SA_HASH_ALG_SHA1 1 +#define SA_HASH_ALG_NULL 15 + +#define SA_HASH_ALG_SHA1_DIGEST_SIZE 20 + +#define SA_LOAD_HASH_FROM_SA 0 +#define SA_LOAD_HASH_FROM_STATE 2 +#define SA_LOAD_HASH_NO_LOAD 3 + +union sa_command_0 { + struct { + u32 scatter:1; + u32 gather:1; + u32 save_hash_state:1; + u32 save_iv:1; + u32 load_hash_state:2; + u32 load_iv:2; + u32 digest_len:4; + u32 hdr_proc:1; + u32 extend_pad:1; + u32 stream_cipher_pad:1; + u32 rsv:1; + u32 hash_alg:4; + u32 cipher_alg:4; + u32 pad_type:2; + u32 op_group:2; + u32 dir:1; + u32 opcode:3; + } bf; + u32 w; +} __attribute__((packed)); + +#define CRYPTO_MODE_ECB 0 +#define CRYPTO_MODE_CBC 1 + +#define CRYPTO_FEEDBACK_MODE_NO_FB 0 +#define CRYPTO_FEEDBACK_MODE_64BIT_OFB 0 +#define CRYPTO_FEEDBACK_MODE_8BIT_CFB 1 +#define CRYPTO_FEEDBACK_MODE_1BIT_CFB 2 +#define CRYPTO_FEEDBACK_MODE_128BIT_CFB 3 + +#define SA_AES_KEY_LEN_128 2 +#define SA_AES_KEY_LEN_192 3 +#define SA_AES_KEY_LEN_256 4 + +/** + * The follow 4 defines usage of hmac_muting bit in sa_command_1 + * In Basic hash mode this bit define simple hash or hmac. + * In IPsec mode, this bit define muting control. + */ +#define SA_HASH_MODE_HASH 0 +#define SA_HASH_MODE_HMAC 1 + +union sa_command_1 { + struct { + u32 crypto_mode31:1; + u32 save_arc4_state:1; + u32 arc4_stateful:1; + u32 key_len:5; + u32 hash_crypto_offset:8; + u32 sa_rev:2; + u32 byte_offset:1; + u32 hmac_muting:1; + u32 feedback_mode:2; + u32 crypto_mode9_8:2; + u32 extended_seq_num:1; + u32 seq_num_mask:1; + u32 mutable_bit_proc:1; + u32 ip_version:1; + u32 copy_pad:1; + u32 copy_payload:1; + u32 copy_hdr:1; + u32 rsv1:1; + } bf; + u32 w; +} __attribute__((packed)); + +struct dynamic_sa_ctl { + u32 sa_contents; + union sa_command_0 sa_command_0; + union sa_command_1 sa_command_1; + +} __attribute__((packed)); + +/** + * State Record for Security Association (SA) + */ +struct dynamic_sa_state_record { + u32 save_iv[4]; + u32 save_hash_byte_cnt[2]; + u32 save_digest[16]; +} __attribute__((packed)); + +/** + * Security Association (SA) for AES128 + * + */ +struct dynamic_sa_aes128 { + struct dynamic_sa_ctl ctrl; + u32 key[4]; + u32 iv[4]; /* for CBC, OFC, and CFB mode */ + u32 state_ptr; + u32 reserved; +} __attribute__((packed)); + +#define SA_AES128_LEN (sizeof(struct dynamic_sa_aes128)/4) +#define SA_AES128_CONTENTS 0x3e000042 + +/* + * Security Association (SA) for AES192 + */ +struct dynamic_sa_aes192 { + struct dynamic_sa_ctl ctrl; + u32 key[6]; + u32 iv[4]; /* for CBC, OFC, and CFB mode */ + u32 state_ptr; + u32 reserved; +} __attribute__((packed)); + +#define SA_AES192_LEN (sizeof(struct dynamic_sa_aes192)/4) +#define SA_AES192_CONTENTS 0x3e000062 + +/** + * Security Association (SA) for AES256 + */ +struct dynamic_sa_aes256 { + struct dynamic_sa_ctl ctrl; + u32 key[8]; + u32 iv[4]; /* for CBC, OFC, and CFB mode */ + u32 state_ptr; + u32 reserved; +} __attribute__((packed)); + +#define SA_AES256_LEN (sizeof(struct dynamic_sa_aes256)/4) +#define SA_AES256_CONTENTS 0x3e000082 + +/** + * Security Association (SA) for HASH160: HMAC-SHA1 + */ +struct dynamic_sa_hash160 { + struct dynamic_sa_ctl ctrl; + u32 inner_digest[5]; + u32 outer_digest[5]; + u32 state_ptr; + u32 reserved; +} __attribute__((packed)); +#define SA_HASH160_LEN (sizeof(struct dynamic_sa_hash160)/4) +#define SA_HASH160_CONTENTS 0x2000a502 + +#endif