Commit c8981d92 authored by Pavitrakumar M's avatar Pavitrakumar M Committed by Herbert Xu

crypto: spacc - Add SPAcc Skcipher support

Signed-off-by: default avatarBhoomika K <bhoomikak@vayavyalabs.com>
Signed-off-by: default avatarPavitrakumar M <pavitrakumarm@vayavyalabs.com>
Acked-by: default avatarRuud Derwig <Ruud.Derwig@synopsys.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent b0cd6f4c
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include "spacc_device.h"
static struct platform_device *spacc_pdev[MAX_DEVICES];
#define VSPACC_PRIORITY_MAX 15
void spacc_cmd_process(struct spacc_device *spacc, int x)
{
struct spacc_priv *priv = container_of(spacc, struct spacc_priv, spacc);
/* run tasklet to pop jobs off fifo */
tasklet_schedule(&priv->pop_jobs);
}
void spacc_stat_process(struct spacc_device *spacc)
{
struct spacc_priv *priv = container_of(spacc, struct spacc_priv, spacc);
/* run tasklet to pop jobs off fifo */
tasklet_schedule(&priv->pop_jobs);
}
int spacc_probe(struct platform_device *pdev,
const struct of_device_id snps_spacc_id[])
{
int spacc_idx = -1;
struct resource *mem;
int spacc_endian = 0;
void __iomem *baseaddr;
struct pdu_info info;
int spacc_priority = -1;
struct spacc_priv *priv;
int x = 0, err, oldmode, irq_num;
const struct of_device_id *match, *id;
u64 oldtimer = 100000, timer = 100000;
if (pdev->dev.of_node) {
id = of_match_node(snps_spacc_id, pdev->dev.of_node);
if (!id) {
dev_err(&pdev->dev, "DT node did not match\n");
return -EINVAL;
}
}
/* Initialize DDT DMA pools based on this device's resources */
if (pdu_mem_init(&pdev->dev)) {
dev_err(&pdev->dev, "Could not initialize DMA pools\n");
return -ENOMEM;
}
match = of_match_device(of_match_ptr(snps_spacc_id), &pdev->dev);
if (!match) {
dev_err(&pdev->dev, "SPAcc dtb missing");
return -ENODEV;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no memory resource for spacc\n");
err = -ENXIO;
goto free_ddt_mem_pool;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
err = -ENOMEM;
goto free_ddt_mem_pool;
}
/* Read spacc priority and index and save inside priv.spacc.config */
if (of_property_read_u32(pdev->dev.of_node, "spacc_priority",
&spacc_priority)) {
dev_err(&pdev->dev, "No vspacc priority specified\n");
err = -EINVAL;
goto free_ddt_mem_pool;
}
if (spacc_priority < 0 && spacc_priority > VSPACC_PRIORITY_MAX) {
dev_err(&pdev->dev, "Invalid vspacc priority\n");
err = -EINVAL;
goto free_ddt_mem_pool;
}
priv->spacc.config.priority = spacc_priority;
if (of_property_read_u32(pdev->dev.of_node, "spacc_index",
&spacc_idx)) {
dev_err(&pdev->dev, "No vspacc index specified\n");
err = -EINVAL;
goto free_ddt_mem_pool;
}
priv->spacc.config.idx = spacc_idx;
if (of_property_read_u32(pdev->dev.of_node, "spacc_endian",
&spacc_endian)) {
dev_dbg(&pdev->dev, "No spacc_endian specified\n");
dev_dbg(&pdev->dev, "Default spacc Endianness (0==little)\n");
spacc_endian = 0;
}
priv->spacc.config.spacc_endian = spacc_endian;
if (of_property_read_u64(pdev->dev.of_node, "oldtimer",
&oldtimer)) {
dev_dbg(&pdev->dev, "No oldtimer specified\n");
dev_dbg(&pdev->dev, "Default oldtimer (100000)\n");
oldtimer = 100000;
}
priv->spacc.config.oldtimer = oldtimer;
if (of_property_read_u64(pdev->dev.of_node, "timer", &timer)) {
dev_dbg(&pdev->dev, "No timer specified\n");
dev_dbg(&pdev->dev, "Default timer (100000)\n");
timer = 100000;
}
priv->spacc.config.timer = timer;
baseaddr = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(baseaddr)) {
dev_err(&pdev->dev, "unable to map iomem\n");
err = PTR_ERR(baseaddr);
goto free_ddt_mem_pool;
}
pdu_get_version(baseaddr, &info);
if (pdev->dev.platform_data) {
struct pdu_info *parent_info = pdev->dev.platform_data;
memcpy(&info.pdu_config, &parent_info->pdu_config,
sizeof(info.pdu_config));
}
dev_dbg(&pdev->dev, "EPN %04X : virt [%d]\n",
info.spacc_version.project,
info.spacc_version.vspacc_idx);
/* Validate virtual spacc index with vspacc count read from
* VERSION_EXT.VSPACC_CNT. Thus vspacc count=3, gives valid index 0,1,2
*/
if (spacc_idx != info.spacc_version.vspacc_idx) {
dev_err(&pdev->dev, "DTS vspacc_idx mismatch read value\n");
err = -EINVAL;
goto free_ddt_mem_pool;
}
if (spacc_idx < 0 || spacc_idx > (info.spacc_config.num_vspacc - 1)) {
dev_err(&pdev->dev, "Invalid vspacc index specified\n");
err = -EINVAL;
goto free_ddt_mem_pool;
}
err = spacc_init(baseaddr, &priv->spacc, &info);
if (err != CRYPTO_OK) {
dev_err(&pdev->dev, "Failed to initialize device %d\n", x);
err = -ENXIO;
goto free_ddt_mem_pool;
}
spin_lock_init(&priv->hw_lock);
spacc_irq_glbl_disable(&priv->spacc);
tasklet_init(&priv->pop_jobs, spacc_pop_jobs, (unsigned long)priv);
priv->spacc.dptr = &pdev->dev;
platform_set_drvdata(pdev, priv);
irq_num = platform_get_irq(pdev, 0);
if (irq_num < 0) {
dev_err(&pdev->dev, "no irq resource for spacc\n");
err = -ENXIO;
goto free_ddt_mem_pool;
}
/* Determine configured maximum message length. */
priv->max_msg_len = priv->spacc.config.max_msg_size;
if (devm_request_irq(&pdev->dev, irq_num, spacc_irq_handler,
IRQF_SHARED, dev_name(&pdev->dev),
&pdev->dev)) {
dev_err(&pdev->dev, "failed to request IRQ\n");
err = -EBUSY;
goto err_tasklet_kill;
}
priv->spacc.irq_cb_stat = spacc_stat_process;
priv->spacc.irq_cb_cmdx = spacc_cmd_process;
oldmode = priv->spacc.op_mode;
priv->spacc.op_mode = SPACC_OP_MODE_IRQ;
spacc_irq_stat_enable(&priv->spacc, 1);
spacc_irq_cmdx_enable(&priv->spacc, 0, 1);
spacc_irq_stat_wd_disable(&priv->spacc);
spacc_irq_glbl_enable(&priv->spacc);
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_AUTODETECT)
err = spacc_autodetect(&priv->spacc);
if (err < 0) {
spacc_irq_glbl_disable(&priv->spacc);
goto err_tasklet_kill;
}
#else
err = spacc_static_config(&priv->spacc);
if (err < 0) {
spacc_irq_glbl_disable(&priv->spacc);
goto err_tasklet_kill;
}
#endif
priv->spacc.op_mode = oldmode;
if (priv->spacc.op_mode == SPACC_OP_MODE_IRQ) {
priv->spacc.irq_cb_stat = spacc_stat_process;
priv->spacc.irq_cb_cmdx = spacc_cmd_process;
spacc_irq_stat_enable(&priv->spacc, 1);
spacc_irq_cmdx_enable(&priv->spacc, 0, 1);
spacc_irq_glbl_enable(&priv->spacc);
} else {
priv->spacc.irq_cb_stat = spacc_stat_process;
priv->spacc.irq_cb_stat_wd = spacc_stat_process;
spacc_irq_stat_enable(&priv->spacc,
priv->spacc.config.ideal_stat_level);
spacc_irq_cmdx_disable(&priv->spacc, 0);
spacc_irq_stat_wd_enable(&priv->spacc);
spacc_irq_glbl_enable(&priv->spacc);
/* enable the wd by setting the wd_timer = 100000 */
spacc_set_wd_count(&priv->spacc,
priv->spacc.config.wd_timer =
priv->spacc.config.timer);
}
/* unlock normal*/
if (priv->spacc.config.is_secure_port) {
u32 t;
t = readl(baseaddr + SPACC_REG_SECURE_CTRL);
t &= ~(1UL << 31);
writel(t, baseaddr + SPACC_REG_SECURE_CTRL);
}
/* unlock device by default */
writel(0, baseaddr + SPACC_REG_SECURE_CTRL);
return err;
err_tasklet_kill:
tasklet_kill(&priv->pop_jobs);
spacc_fini(&priv->spacc);
free_ddt_mem_pool:
pdu_mem_deinit(&pdev->dev);
return err;
}
static void spacc_unregister_algs(void)
{
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_HASH)
spacc_unregister_hash_algs();
#endif
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_AEAD)
spacc_unregister_aead_algs();
#endif
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_CIPHER)
spacc_unregister_cipher_algs();
#endif
}
static const struct of_device_id snps_spacc_id[] = {
{.compatible = "snps-dwc-spacc" },
{ /*sentinel */ }
};
MODULE_DEVICE_TABLE(of, snps_spacc_id);
static int spacc_crypto_probe(struct platform_device *pdev)
{
int rc;
rc = spacc_probe(pdev, snps_spacc_id);
if (rc < 0)
goto err;
spacc_pdev[0] = pdev;
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_HASH)
rc = probe_hashes(pdev);
if (rc < 0)
goto err;
#endif
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_CIPHER)
rc = probe_ciphers(pdev);
if (rc < 0)
goto err;
#endif
#if IS_ENABLED(CONFIG_CRYPTO_DEV_SPACC_AEAD)
rc = probe_aeads(pdev);
if (rc < 0)
goto err;
#endif
return 0;
err:
spacc_unregister_algs();
return rc;
}
static void spacc_crypto_remove(struct platform_device *pdev)
{
spacc_unregister_algs();
spacc_remove(pdev);
}
static struct platform_driver spacc_driver = {
.probe = spacc_crypto_probe,
.remove = spacc_crypto_remove,
.driver = {
.name = "spacc",
.of_match_table = of_match_ptr(snps_spacc_id),
.owner = THIS_MODULE,
},
};
module_platform_driver(spacc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_DESCRIPTION("SPAcc Crypto Accelerator Driver");
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef SPACC_DEVICE_H_
#define SPACC_DEVICE_H_
#include <crypto/hash.h>
#include <crypto/ctr.h>
#include <crypto/internal/aead.h>
#include <linux/of.h>
#include "spacc_core.h"
#define MODE_TAB_AEAD(_name, _ciph, _hash, _hashlen, _ivlen, _blocklen) \
.name = _name, .aead = { .ciph = _ciph, .hash = _hash }, \
.hashlen = _hashlen, .ivlen = _ivlen, .blocklen = _blocklen
/* Helper macros for initializing the hash/cipher tables. */
#define MODE_TAB_COMMON(_name, _id_name, _blocklen) \
.name = _name, .id = CRYPTO_MODE_##_id_name, .blocklen = _blocklen
#define MODE_TAB_HASH(_name, _id_name, _hashlen, _blocklen) \
MODE_TAB_COMMON(_name, _id_name, _blocklen), \
.hashlen = _hashlen, .testlen = _hashlen
#define MODE_TAB_CIPH(_name, _id_name, _ivlen, _blocklen) \
MODE_TAB_COMMON(_name, _id_name, _blocklen), \
.ivlen = _ivlen
#define MODE_TAB_HASH_XCBC 0x8000
#define SPACC_MAX_DIGEST_SIZE 64
#define SPACC_MAX_KEY_SIZE 32
#define SPACC_MAX_IV_SIZE 16
#define SPACC_DMA_ALIGN 4
#define SPACC_DMA_BOUNDARY 0x10000
#define MAX_DEVICES 2
/* flag means the IV is computed from setkey and crypt*/
#define SPACC_MANGLE_IV_FLAG 0x8000
/* we're doing a CTR mangle (for RFC3686/IPsec)*/
#define SPACC_MANGLE_IV_RFC3686 0x0100
/* we're doing GCM */
#define SPACC_MANGLE_IV_RFC4106 0x0200
/* we're doing GMAC */
#define SPACC_MANGLE_IV_RFC4543 0x0300
/* we're doing CCM */
#define SPACC_MANGLE_IV_RFC4309 0x0400
/* we're doing SM4 GCM/CCM */
#define SPACC_MANGLE_IV_RFC8998 0x0500
#define CRYPTO_MODE_AES_CTR_RFC3686 (CRYPTO_MODE_AES_CTR \
| SPACC_MANGLE_IV_FLAG \
| SPACC_MANGLE_IV_RFC3686)
#define CRYPTO_MODE_AES_GCM_RFC4106 (CRYPTO_MODE_AES_GCM \
| SPACC_MANGLE_IV_FLAG \
| SPACC_MANGLE_IV_RFC4106)
#define CRYPTO_MODE_AES_GCM_RFC4543 (CRYPTO_MODE_AES_GCM \
| SPACC_MANGLE_IV_FLAG \
| SPACC_MANGLE_IV_RFC4543)
#define CRYPTO_MODE_AES_CCM_RFC4309 (CRYPTO_MODE_AES_CCM \
| SPACC_MANGLE_IV_FLAG \
| SPACC_MANGLE_IV_RFC4309)
#define CRYPTO_MODE_SM4_GCM_RFC8998 (CRYPTO_MODE_SM4_GCM)
#define CRYPTO_MODE_SM4_CCM_RFC8998 (CRYPTO_MODE_SM4_CCM)
struct spacc_crypto_ctx {
struct device *dev;
spinlock_t lock;
struct list_head jobs;
int handle, mode, auth_size, key_len;
unsigned char *cipher_key;
/*
* Indicates that the H/W context has been setup and can be used for
* crypto; otherwise, the software fallback will be used.
*/
bool ctx_valid;
unsigned int flag_ppp;
/* salt used for rfc3686/givencrypt mode */
unsigned char csalt[16];
u8 ipad[128] __aligned(sizeof(u32));
u8 digest_ctx_buf[128] __aligned(sizeof(u32));
u8 tmp_buffer[128] __aligned(sizeof(u32));
/* Save keylen from setkey */
int keylen;
u8 key[256];
int zero_key;
unsigned char *tmp_sgl_buff;
struct scatterlist *tmp_sgl;
union{
struct crypto_ahash *hash;
struct crypto_aead *aead;
struct crypto_skcipher *cipher;
} fb;
};
struct spacc_crypto_reqctx {
struct pdu_ddt src, dst;
void *digest_buf, *iv_buf;
dma_addr_t digest_dma;
int dst_nents, src_nents, aead_nents, total_nents;
int encrypt_op, mode, single_shot;
unsigned int spacc_cipher_cryptlen, rem_nents;
struct aead_cb_data {
int new_handle;
struct spacc_crypto_ctx *tctx;
struct spacc_crypto_reqctx *ctx;
struct aead_request *req;
struct spacc_device *spacc;
} cb;
struct ahash_cb_data {
int new_handle;
struct spacc_crypto_ctx *tctx;
struct spacc_crypto_reqctx *ctx;
struct ahash_request *req;
struct spacc_device *spacc;
} acb;
struct cipher_cb_data {
int new_handle;
struct spacc_crypto_ctx *tctx;
struct spacc_crypto_reqctx *ctx;
struct skcipher_request *req;
struct spacc_device *spacc;
} ccb;
union {
struct ahash_request hash_req;
struct skcipher_request cipher_req;
struct aead_request aead_req;
} fb;
};
struct mode_tab {
char name[128];
int valid;
/* mode ID used in hash/cipher mode but not aead*/
int id;
/* ciph/hash mode used in aead */
struct {
int ciph, hash;
} aead;
unsigned int hashlen, ivlen, blocklen, keylen[3];
unsigned int keylen_mask, testlen;
unsigned int chunksize, walksize, min_keysize, max_keysize;
bool sw_fb;
union {
unsigned char hash_test[SPACC_MAX_DIGEST_SIZE];
unsigned char ciph_test[3][2 * SPACC_MAX_IV_SIZE];
};
};
struct spacc_alg {
struct mode_tab *mode;
unsigned int keylen_mask;
struct device *dev[MAX_DEVICES];
struct list_head list;
struct crypto_alg *calg;
struct crypto_tfm *tfm;
union {
struct ahash_alg hash;
struct aead_alg aead;
struct skcipher_alg skcipher;
} alg;
};
static inline const struct spacc_alg *spacc_tfm_ahash(struct crypto_tfm *tfm)
{
const struct crypto_alg *calg = tfm->__crt_alg;
if ((calg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH)
return container_of(calg, struct spacc_alg, alg.hash.halg.base);
return NULL;
}
static inline const struct spacc_alg *spacc_tfm_skcipher(struct crypto_tfm *tfm)
{
const struct crypto_alg *calg = tfm->__crt_alg;
if ((calg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
CRYPTO_ALG_TYPE_SKCIPHER)
return container_of(calg, struct spacc_alg, alg.skcipher.base);
return NULL;
}
static inline const struct spacc_alg *spacc_tfm_aead(struct crypto_tfm *tfm)
{
const struct crypto_alg *calg = tfm->__crt_alg;
if ((calg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AEAD)
return container_of(calg, struct spacc_alg, alg.aead.base);
return NULL;
}
int probe_hashes(struct platform_device *spacc_pdev);
int spacc_unregister_hash_algs(void);
int probe_aeads(struct platform_device *spacc_pdev);
int spacc_unregister_aead_algs(void);
int probe_ciphers(struct platform_device *spacc_pdev);
int spacc_unregister_cipher_algs(void);
int spacc_probe(struct platform_device *pdev,
const struct of_device_id snps_spacc_id[]);
irqreturn_t spacc_irq_handler(int irq, void *dev);
#endif
// SPDX-License-Identifier: GPL-2.0
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include "spacc_hal.h"
static struct dma_pool *ddt_pool, *ddt16_pool, *ddt4_pool;
static struct device *ddt_device;
#define PDU_REG_SPACC_VERSION 0x00180UL
#define PDU_REG_SPACC_CONFIG 0x00184UL
#define PDU_REG_SPACC_CONFIG2 0x00190UL
#define PDU_REG_SPACC_IV_OFFSET 0x00040UL
#define PDU_REG_PDU_CONFIG 0x00188UL
#define PDU_REG_SECURE_LOCK 0x001C0UL
int pdu_get_version(void __iomem *dev, struct pdu_info *inf)
{
unsigned long tmp;
if (!inf)
return -1;
memset(inf, 0, sizeof(*inf));
tmp = readl(dev + PDU_REG_SPACC_VERSION);
/* Read the SPAcc version block this tells us the revision,
* project, and a few other feature bits
*
* layout for v6.5+
*/
inf->spacc_version = (struct spacc_version_block) {
.minor = SPACC_ID_MINOR(tmp),
.major = SPACC_ID_MAJOR(tmp),
.version = (SPACC_ID_MAJOR(tmp) << 4) | SPACC_ID_MINOR(tmp),
.qos = SPACC_ID_QOS(tmp),
.is_spacc = SPACC_ID_TYPE(tmp) == SPACC_TYPE_SPACCQOS,
.is_pdu = SPACC_ID_TYPE(tmp) == SPACC_TYPE_PDU,
.aux = SPACC_ID_AUX(tmp),
.vspacc_idx = SPACC_ID_VIDX(tmp),
.partial = SPACC_ID_PARTIAL(tmp),
.project = SPACC_ID_PROJECT(tmp),
};
/* try to autodetect */
writel(0x80000000, dev + PDU_REG_SPACC_IV_OFFSET);
if (readl(dev + PDU_REG_SPACC_IV_OFFSET) == 0x80000000)
inf->spacc_version.ivimport = 1;
else
inf->spacc_version.ivimport = 0;
/* Read the SPAcc config block (v6.5+) which tells us how many
* contexts there are and context page sizes
* this register is only available in v6.5 and up
*/
tmp = readl(dev + PDU_REG_SPACC_CONFIG);
inf->spacc_config = (struct spacc_config_block) {
SPACC_CFG_CTX_CNT(tmp),
SPACC_CFG_VSPACC_CNT(tmp),
SPACC_CFG_CIPH_CTX_SZ(tmp),
SPACC_CFG_HASH_CTX_SZ(tmp),
SPACC_CFG_DMA_TYPE(tmp),
0, 0, 0, 0
};
/* CONFIG2 only present in v6.5+ cores */
tmp = readl(dev + PDU_REG_SPACC_CONFIG2);
if (inf->spacc_version.qos) {
inf->spacc_config.cmd0_fifo_depth =
SPACC_CFG_CMD0_FIFO_QOS(tmp);
inf->spacc_config.cmd1_fifo_depth =
SPACC_CFG_CMD1_FIFO(tmp);
inf->spacc_config.cmd2_fifo_depth =
SPACC_CFG_CMD2_FIFO(tmp);
inf->spacc_config.stat_fifo_depth =
SPACC_CFG_STAT_FIFO_QOS(tmp);
} else {
inf->spacc_config.cmd0_fifo_depth =
SPACC_CFG_CMD0_FIFO(tmp);
inf->spacc_config.stat_fifo_depth =
SPACC_CFG_STAT_FIFO(tmp);
}
/* only read PDU config if it's actually a PDU engine */
if (inf->spacc_version.is_pdu) {
tmp = readl(dev + PDU_REG_PDU_CONFIG);
inf->pdu_config = (struct pdu_config_block)
{SPACC_PDU_CFG_MINOR(tmp),
SPACC_PDU_CFG_MAJOR(tmp)};
/* unlock all cores by default */
writel(0, dev + PDU_REG_SECURE_LOCK);
}
return 0;
}
void pdu_to_dev(void __iomem *addr_, uint32_t *src, unsigned long nword)
{
void __iomem *addr = addr_;
while (nword--) {
writel(*src++, addr);
addr += 4;
}
}
void pdu_from_dev(u32 *dst, void __iomem *addr_, unsigned long nword)
{
void __iomem *addr = addr_;
while (nword--) {
*dst++ = readl(addr);
addr += 4;
}
}
static void pdu_to_dev_big(void __iomem *addr_, const unsigned char *src,
unsigned long nword)
{
unsigned long v;
void __iomem *addr = addr_;
while (nword--) {
v = 0;
v = (v << 8) | ((unsigned long)*src++);
v = (v << 8) | ((unsigned long)*src++);
v = (v << 8) | ((unsigned long)*src++);
v = (v << 8) | ((unsigned long)*src++);
writel(v, addr);
addr += 4;
}
}
static void pdu_from_dev_big(unsigned char *dst, void __iomem *addr_,
unsigned long nword)
{
unsigned long v;
void __iomem *addr = addr_;
while (nword--) {
v = readl(addr);
addr += 4;
*dst++ = (v >> 24) & 0xFF; v <<= 8;
*dst++ = (v >> 24) & 0xFF; v <<= 8;
*dst++ = (v >> 24) & 0xFF; v <<= 8;
*dst++ = (v >> 24) & 0xFF; v <<= 8;
}
}
static void pdu_to_dev_little(void __iomem *addr_, const unsigned char *src,
unsigned long nword)
{
unsigned long v;
void __iomem *addr = addr_;
while (nword--) {
v = 0;
v = (v >> 8) | ((unsigned long)*src++ << 24UL);
v = (v >> 8) | ((unsigned long)*src++ << 24UL);
v = (v >> 8) | ((unsigned long)*src++ << 24UL);
v = (v >> 8) | ((unsigned long)*src++ << 24UL);
writel(v, addr);
addr += 4;
}
}
static void pdu_from_dev_little(unsigned char *dst, void __iomem *addr_,
unsigned long nword)
{
unsigned long v;
void __iomem *addr = addr_;
while (nword--) {
v = readl(addr);
addr += 4;
*dst++ = v & 0xFF; v >>= 8;
*dst++ = v & 0xFF; v >>= 8;
*dst++ = v & 0xFF; v >>= 8;
*dst++ = v & 0xFF; v >>= 8;
}
}
void pdu_to_dev_s(void __iomem *addr, const unsigned char *src,
unsigned long nword, int endian)
{
if (endian)
pdu_to_dev_big(addr, src, nword);
else
pdu_to_dev_little(addr, src, nword);
}
void pdu_from_dev_s(unsigned char *dst, void __iomem *addr,
unsigned long nword, int endian)
{
if (endian)
pdu_from_dev_big(dst, addr, nword);
else
pdu_from_dev_little(dst, addr, nword);
}
void pdu_io_cached_write(void __iomem *addr, unsigned long val,
uint32_t *cache)
{
if (*cache == val) {
#ifdef CONFIG_CRYPTO_DEV_SPACC_DEBUG_TRACE_IO
pr_debug("PDU: write %.8lx -> %p (cached)\n", val, addr);
#endif
return;
}
*cache = val;
writel(val, addr);
}
struct device *get_ddt_device(void)
{
return ddt_device;
}
/* Platform specific DDT routines */
/* create a DMA pool for DDT entries this should help from splitting
* pages for DDTs which by default are 520 bytes long meaning we would
* otherwise waste 3576 bytes per DDT allocated...
* we also maintain a smaller table of 4 entries common for simple jobs
* which uses 480 fewer bytes of DMA memory.
* and for good measure another table for 16 entries saving 384 bytes
*/
int pdu_mem_init(void *device)
{
if (ddt_device)
return 0; /* Already setup */
ddt_device = device;
ddt_pool = dma_pool_create("spaccddt", device, (PDU_MAX_DDT + 1) * 8,
8, 0); /* max of 64 DDT entries */
if (!ddt_pool)
return -1;
#if PDU_MAX_DDT > 16
/* max of 16 DDT entries */
ddt16_pool = dma_pool_create("spaccddt16", device, (16 + 1) * 8, 8, 0);
if (!ddt16_pool) {
dma_pool_destroy(ddt_pool);
return -1;
}
#else
ddt16_pool = ddt_pool;
#endif
/* max of 4 DDT entries */
ddt4_pool = dma_pool_create("spaccddt4", device, (4 + 1) * 8, 8, 0);
if (!ddt4_pool) {
dma_pool_destroy(ddt_pool);
#if PDU_MAX_DDT > 16
dma_pool_destroy(ddt16_pool);
#endif
return -1;
}
return 0;
}
/* destroy the pool */
void pdu_mem_deinit(void *device)
{
/* For now, just skip deinit except for matching device */
if (device != ddt_device)
return;
dma_pool_destroy(ddt_pool);
#if PDU_MAX_DDT > 16
dma_pool_destroy(ddt16_pool);
#endif
dma_pool_destroy(ddt4_pool);
ddt_device = NULL;
}
int pdu_ddt_init(struct pdu_ddt *ddt, unsigned long limit)
{
/* set the MSB if we want to use an ATOMIC
* allocation required for top half processing
*/
int flag = (limit & 0x80000000);
limit &= 0x7FFFFFFF;
if (limit + 1 >= SIZE_MAX / 8) {
/* Too big to even compute DDT size */
return -1;
} else if (limit > PDU_MAX_DDT) {
size_t len = 8 * ((size_t)limit + 1);
ddt->virt = dma_alloc_coherent(ddt_device, len, &ddt->phys,
flag ? GFP_ATOMIC : GFP_KERNEL);
} else if (limit > 16) {
ddt->virt = dma_pool_alloc(ddt_pool, flag ? GFP_ATOMIC :
GFP_KERNEL, &ddt->phys);
} else if (limit > 4) {
ddt->virt = dma_pool_alloc(ddt16_pool, flag ? GFP_ATOMIC :
GFP_KERNEL, &ddt->phys);
} else {
ddt->virt = dma_pool_alloc(ddt4_pool, flag ? GFP_ATOMIC :
GFP_KERNEL, &ddt->phys);
}
ddt->idx = 0;
ddt->len = 0;
ddt->limit = limit;
if (!ddt->virt)
return -1;
#ifdef CONFIG_CRYPTO_DEV_SPACC_DEBUG_TRACE_DDT
pr_debug(" DDT[%.8lx]: allocated %lu fragments\n",
(unsigned long)ddt->phys, limit);
#endif
return 0;
}
int pdu_ddt_add(struct pdu_ddt *ddt, dma_addr_t phys, unsigned long size)
{
#ifdef CONFIG_CRYPTO_DEV_SPACC_DEBUG_TRACE_DDT
pr_debug(" DDT[%.8lx]: 0x%.8lx size %lu\n",
(unsigned long)ddt->phys,
(unsigned long)phys, size);
#endif
if (ddt->idx == ddt->limit)
return -1;
ddt->virt[ddt->idx * 2 + 0] = (uint32_t)phys;
ddt->virt[ddt->idx * 2 + 1] = size;
ddt->virt[ddt->idx * 2 + 2] = 0;
ddt->virt[ddt->idx * 2 + 3] = 0;
ddt->len += size;
++(ddt->idx);
return 0;
}
int pdu_ddt_free(struct pdu_ddt *ddt)
{
if (ddt->virt) {
if (ddt->limit > PDU_MAX_DDT) {
size_t len = 8 * ((size_t)ddt->limit + 1);
dma_free_coherent(ddt_device, len, ddt->virt,
ddt->phys);
} else if (ddt->limit > 16) {
dma_pool_free(ddt_pool, ddt->virt, ddt->phys);
} else if (ddt->limit > 4) {
dma_pool_free(ddt16_pool, ddt->virt, ddt->phys);
} else {
dma_pool_free(ddt4_pool, ddt->virt, ddt->phys);
}
ddt->virt = NULL;
}
return 0;
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef SPACC_HAL_H
#define SPACC_HAL_H
/* Maximum number of DDT entries allowed*/
#ifndef PDU_MAX_DDT
#define PDU_MAX_DDT 64
#endif
/* Platform Generic */
#define PDU_IRQ_EN_GLBL BIT(31)
#define PDU_IRQ_EN_VSPACC(x) (1UL << (x))
#define PDU_IRQ_EN_RNG BIT(16)
#ifndef SPACC_ID_MINOR
#define SPACC_ID_MINOR(x) ((x) & 0x0F)
#define SPACC_ID_MAJOR(x) (((x) >> 4) & 0x0F)
#define SPACC_ID_QOS(x) (((x) >> 8) & 0x01)
#define SPACC_ID_TYPE(x) (((x) >> 9) & 0x03)
#define SPACC_ID_AUX(x) (((x) >> 11) & 0x01)
#define SPACC_ID_VIDX(x) (((x) >> 12) & 0x07)
#define SPACC_ID_PARTIAL(x) (((x) >> 15) & 0x01)
#define SPACC_ID_PROJECT(x) ((x) >> 16)
#define SPACC_TYPE_SPACCQOS 0
#define SPACC_TYPE_PDU 1
#define SPACC_CFG_CTX_CNT(x) ((x) & 0x7F)
#define SPACC_CFG_RC4_CTX_CNT(x) (((x) >> 8) & 0x7F)
#define SPACC_CFG_VSPACC_CNT(x) (((x) >> 16) & 0x0F)
#define SPACC_CFG_CIPH_CTX_SZ(x) (((x) >> 20) & 0x07)
#define SPACC_CFG_HASH_CTX_SZ(x) (((x) >> 24) & 0x0F)
#define SPACC_CFG_DMA_TYPE(x) (((x) >> 28) & 0x03)
#define SPACC_CFG_CMD0_FIFO_QOS(x) (((x) >> 0) & 0x7F)
#define SPACC_CFG_CMD0_FIFO(x) (((x) >> 0) & 0x1FF)
#define SPACC_CFG_CMD1_FIFO(x) (((x) >> 8) & 0x7F)
#define SPACC_CFG_CMD2_FIFO(x) (((x) >> 16) & 0x7F)
#define SPACC_CFG_STAT_FIFO_QOS(x) (((x) >> 24) & 0x7F)
#define SPACC_CFG_STAT_FIFO(x) (((x) >> 16) & 0x1FF)
#define SPACC_PDU_CFG_MINOR(x) ((x) & 0x0F)
#define SPACC_PDU_CFG_MAJOR(x) (((x) >> 4) & 0x0F)
#define PDU_SECURE_LOCK_SPACC(x) (x)
#define PDU_SECURE_LOCK_CFG BIT(30)
#define PDU_SECURE_LOCK_GLBL BIT(31)
#endif /* SPACC_ID_MINOR */
#define CRYPTO_OK (0)
struct spacc_version_block {
unsigned int minor,
major,
version,
qos,
is_spacc,
is_pdu,
aux,
vspacc_idx,
partial,
project,
ivimport;
};
struct spacc_config_block {
unsigned int num_ctx,
num_vspacc,
ciph_ctx_page_size,
hash_ctx_page_size,
dma_type,
cmd0_fifo_depth,
cmd1_fifo_depth,
cmd2_fifo_depth,
stat_fifo_depth;
};
struct pdu_config_block {
unsigned int minor,
major;
};
struct pdu_info {
u32 clockrate;
struct spacc_version_block spacc_version;
struct spacc_config_block spacc_config;
struct pdu_config_block pdu_config;
};
struct pdu_ddt {
dma_addr_t phys;
u32 *virt;
u32 *virt_orig;
unsigned long idx, limit, len;
};
void pdu_io_cached_write(void __iomem *addr, unsigned long val,
uint32_t *cache);
void pdu_to_dev(void __iomem *addr, uint32_t *src, unsigned long nword);
void pdu_from_dev(u32 *dst, void __iomem *addr, unsigned long nword);
void pdu_from_dev_s(unsigned char *dst, void __iomem *addr, unsigned long nword,
int endian);
void pdu_to_dev_s(void __iomem *addr, const unsigned char *src,
unsigned long nword, int endian);
struct device *get_ddt_device(void);
int pdu_mem_init(void *device);
void pdu_mem_deinit(void *device);
int pdu_ddt_init(struct pdu_ddt *ddt, unsigned long limit);
int pdu_ddt_add(struct pdu_ddt *ddt, dma_addr_t phys, unsigned long size);
int pdu_ddt_free(struct pdu_ddt *ddt);
int pdu_get_version(void __iomem *dev, struct pdu_info *inf);
#endif
// SPDX-License-Identifier: GPL-2.0
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "spacc_core.h"
/* Read the IRQ status register and process as needed */
void spacc_disable_int (struct spacc_device *spacc);
static inline uint32_t _spacc_get_stat_cnt(struct spacc_device *spacc)
{
u32 fifo;
if (spacc->config.is_qos)
fifo = SPACC_FIFO_STAT_STAT_CNT_GET_QOS(readl(spacc->regmap +
SPACC_REG_FIFO_STAT));
else
fifo = SPACC_FIFO_STAT_STAT_CNT_GET(readl(spacc->regmap +
SPACC_REG_FIFO_STAT));
return fifo;
}
static int spacc_pop_packets_ex(struct spacc_device *spacc, int *num_popped,
unsigned long *lock_flag)
{
int jobs;
int ret = -EINPROGRESS;
struct spacc_job *job = NULL;
u32 cmdstat, swid, spacc_errcode = SPACC_OK;
*num_popped = 0;
while ((jobs = _spacc_get_stat_cnt(spacc))) {
while (jobs-- > 0) {
/* write the pop register to get the next job */
writel(1, spacc->regmap + SPACC_REG_STAT_POP);
cmdstat = readl(spacc->regmap + SPACC_REG_STATUS);
swid = SPACC_STATUS_SW_ID_GET(cmdstat);
if (spacc->job_lookup[swid] == SPACC_JOB_IDX_UNUSED) {
ret = -EIO;
goto ERR;
}
/* find the associated job with popped swid */
if (swid < 0 || swid >= SPACC_MAX_JOBS)
job = NULL;
else
job = &spacc->job[spacc->job_lookup[swid]];
if (!job) {
ret = -EIO;
goto ERR;
}
/* mark job as done */
job->job_done = 1;
spacc->job_lookup[swid] = SPACC_JOB_IDX_UNUSED;
spacc_errcode = SPACC_GET_STATUS_RET_CODE(cmdstat);
switch (spacc_errcode) {
case SPACC_ICVFAIL:
ret = -EBADMSG;
break;
case SPACC_MEMERR:
ret = -EINVAL;
break;
case SPACC_BLOCKERR:
ret = -EINVAL;
break;
case SPACC_SECERR:
ret = -EIO;
break;
case SPACC_OK:
ret = CRYPTO_OK;
break;
default:
pr_debug("Invalid SPAcc Error");
}
job->job_err = ret;
/*
* We're done touching the SPAcc hw, so release the
* lock across the job callback. It must be reacquired
* before continuing to the next iteration.
*/
if (job->cb) {
spin_unlock_irqrestore(&spacc->lock,
*lock_flag);
job->cb(spacc, job->cbdata);
spin_lock_irqsave(&spacc->lock,
*lock_flag);
}
(*num_popped)++;
}
}
if (!*num_popped)
pr_debug(" Failed to pop a single job\n");
ERR:
spacc_process_jb(spacc);
/* reset the WD timer to the original value*/
if (spacc->op_mode == SPACC_OP_MODE_WD)
spacc_set_wd_count(spacc, spacc->config.wd_timer);
if (*num_popped && spacc->spacc_notify_jobs)
spacc->spacc_notify_jobs(spacc);
return ret;
}
int spacc_pop_packets(struct spacc_device *spacc, int *num_popped)
{
int err;
unsigned long lock_flag;
spin_lock_irqsave(&spacc->lock, lock_flag);
err = spacc_pop_packets_ex(spacc, num_popped, &lock_flag);
spin_unlock_irqrestore(&spacc->lock, lock_flag);
return err;
}
uint32_t spacc_process_irq(struct spacc_device *spacc)
{
u32 temp;
int x, cmd_max;
unsigned long lock_flag;
spin_lock_irqsave(&spacc->lock, lock_flag);
temp = readl(spacc->regmap + SPACC_REG_IRQ_STAT);
/* clear interrupt pin and run registered callback */
if (temp & SPACC_IRQ_STAT_STAT) {
SPACC_IRQ_STAT_CLEAR_STAT(spacc);
if (spacc->op_mode == SPACC_OP_MODE_IRQ) {
spacc->config.fifo_cnt <<= 2;
if (spacc->config.fifo_cnt >=
spacc->config.stat_fifo_depth)
spacc->config.fifo_cnt =
spacc->config.stat_fifo_depth;
/* update fifo count to allow more stati to pile up*/
spacc_irq_stat_enable(spacc, spacc->config.fifo_cnt);
/* reenable CMD0 empty interrupt*/
spacc_irq_cmdx_enable(spacc, 0, 0);
}
if (spacc->irq_cb_stat)
spacc->irq_cb_stat(spacc);
}
/* Watchdog IRQ */
if (spacc->op_mode == SPACC_OP_MODE_WD) {
if (temp & SPACC_IRQ_STAT_STAT_WD) {
if (++spacc->wdcnt == SPACC_WD_LIMIT) {
/* this happens when you get too many IRQs that
* go unanswered
*/
spacc_irq_stat_wd_disable(spacc);
/* we set the STAT CNT to 1 so that every job
* generates an IRQ now
*/
spacc_irq_stat_enable(spacc, 1);
spacc->op_mode = SPACC_OP_MODE_IRQ;
} else if (spacc->config.wd_timer < (0xFFFFFFUL >> 4)) {
/* if the timer isn't too high lets bump it up
* a bit so as to give the IRQ a chance to
* reply
*/
spacc_set_wd_count(spacc,
spacc->config.wd_timer << 4);
}
SPACC_IRQ_STAT_CLEAR_STAT_WD(spacc);
if (spacc->irq_cb_stat_wd)
spacc->irq_cb_stat_wd(spacc);
}
}
if (spacc->op_mode == SPACC_OP_MODE_IRQ) {
cmd_max = (spacc->config.is_qos ? SPACC_CMDX_MAX_QOS :
SPACC_CMDX_MAX);
for (x = 0; x < cmd_max; x++) {
if (temp & SPACC_IRQ_STAT_CMDX(x)) {
spacc->config.fifo_cnt = 1;
/* disable CMD0 interrupt since STAT=1 */
spacc_irq_cmdx_disable(spacc, x);
spacc_irq_stat_enable(spacc,
spacc->config.fifo_cnt);
SPACC_IRQ_STAT_CLEAR_CMDX(spacc, x);
/* run registered callback */
if (spacc->irq_cb_cmdx)
spacc->irq_cb_cmdx(spacc, x);
}
}
}
spin_unlock_irqrestore(&spacc->lock, lock_flag);
return temp;
}
void spacc_set_wd_count(struct spacc_device *spacc, uint32_t val)
{
writel(val, spacc->regmap + SPACC_REG_STAT_WD_CTRL);
}
/* cmdx and cmdx_cnt depend on HW config
* cmdx can be 0, 1 or 2
* cmdx_cnt must be 2^6 or less
*/
void spacc_irq_cmdx_enable(struct spacc_device *spacc, int cmdx, int cmdx_cnt)
{
u32 temp;
/* read the reg, clear the bit range and set the new value */
temp = readl(spacc->regmap + SPACC_REG_IRQ_CTRL) &
(~SPACC_IRQ_CTRL_CMDX_CNT_MASK(cmdx));
temp |= SPACC_IRQ_CTRL_CMDX_CNT_SET(cmdx, cmdx_cnt);
writel(temp | SPACC_IRQ_CTRL_CMDX_CNT_SET(cmdx, cmdx_cnt),
spacc->regmap + SPACC_REG_IRQ_CTRL);
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) | SPACC_IRQ_EN_CMD(cmdx),
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_cmdx_disable(struct spacc_device *spacc, int cmdx)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) &
(~SPACC_IRQ_EN_CMD(cmdx)), spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_stat_enable(struct spacc_device *spacc, int stat_cnt)
{
u32 temp;
temp = readl(spacc->regmap + SPACC_REG_IRQ_CTRL);
if (spacc->config.is_qos) {
temp &= (~SPACC_IRQ_CTRL_STAT_CNT_MASK_QOS);
temp |= SPACC_IRQ_CTRL_STAT_CNT_SET_QOS(stat_cnt);
} else {
temp &= (~SPACC_IRQ_CTRL_STAT_CNT_MASK);
temp |= SPACC_IRQ_CTRL_STAT_CNT_SET(stat_cnt);
}
writel(temp, spacc->regmap + SPACC_REG_IRQ_CTRL);
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) | SPACC_IRQ_EN_STAT,
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_stat_disable(struct spacc_device *spacc)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) & (~SPACC_IRQ_EN_STAT),
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_stat_wd_enable(struct spacc_device *spacc)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) | SPACC_IRQ_EN_STAT_WD,
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_stat_wd_disable(struct spacc_device *spacc)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) &
(~SPACC_IRQ_EN_STAT_WD), spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_glbl_enable(struct spacc_device *spacc)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) | SPACC_IRQ_EN_GLBL,
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_irq_glbl_disable(struct spacc_device *spacc)
{
writel(readl(spacc->regmap + SPACC_REG_IRQ_EN) & (~SPACC_IRQ_EN_GLBL),
spacc->regmap + SPACC_REG_IRQ_EN);
}
void spacc_disable_int (struct spacc_device *spacc)
{
writel(0, spacc->regmap + SPACC_REG_IRQ_EN);
}
/* a function to run callbacks in the IRQ handler */
irqreturn_t spacc_irq_handler(int irq, void *dev)
{
struct spacc_priv *priv = platform_get_drvdata(to_platform_device(dev));
struct spacc_device *spacc = &priv->spacc;
if (spacc->config.oldtimer != spacc->config.timer) {
priv->spacc.config.wd_timer = spacc->config.timer;
spacc_set_wd_count(&priv->spacc, priv->spacc.config.wd_timer);
spacc->config.oldtimer = spacc->config.timer;
}
/* check irq flags and process as required */
if (!spacc_process_irq(spacc))
return IRQ_NONE;
return IRQ_HANDLED;
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment