// SPDX-License-Identifier: GPL-2.0-only
/*
* Support for OMAP DES and Triple DES HW acceleration.
*
* Copyright (c) 2013 Texas Instruments Incorporated
* Author: Joel Fernandes <joelf@ti.com>
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#ifdef DEBUG
#define prn(num) printk(#num "=%d\n", num)
#define prx(num) printk(#num "=%x\n", num)
#else
#define prn(num) do { } while (0)
#define prx(num) do { } while (0)
#endif
#include <crypto/engine.h>
#include <crypto/internal/des.h>
#include <crypto/internal/skcipher.h>
#include <crypto/scatterwalk.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/string.h>
#include "omap-crypto.h"
#define DST_MAXBURST 2
#define DES_BLOCK_WORDS (DES_BLOCK_SIZE >> 2)
#define _calc_walked(inout) (dd->inout##_walk.offset - dd->inout##_sg->offset)
#define DES_REG_KEY(dd, x) ((dd)->pdata->key_ofs - \
((x ^ 0x01) * 0x04))
#define DES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04))
#define DES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs)
#define DES_REG_CTRL_CBC BIT(4)
#define DES_REG_CTRL_TDES BIT(3)
#define DES_REG_CTRL_DIRECTION BIT(2)
#define DES_REG_CTRL_INPUT_READY BIT(1)
#define DES_REG_CTRL_OUTPUT_READY BIT(0)
#define DES_REG_DATA_N(dd, x) ((dd)->pdata->data_ofs + ((x) * 0x04))
#define DES_REG_REV(dd) ((dd)->pdata->rev_ofs)
#define DES_REG_MASK(dd) ((dd)->pdata->mask_ofs)
#define DES_REG_LENGTH_N(x) (0x24 + ((x) * 0x04))
#define DES_REG_IRQ_STATUS(dd) ((dd)->pdata->irq_status_ofs)
#define DES_REG_IRQ_ENABLE(dd) ((dd)->pdata->irq_enable_ofs)
#define DES_REG_IRQ_DATA_IN BIT(1)
#define DES_REG_IRQ_DATA_OUT BIT(2)
#define FLAGS_MODE_MASK 0x000f
#define FLAGS_ENCRYPT BIT(0)
#define FLAGS_CBC BIT(1)
#define FLAGS_INIT BIT(4)
#define FLAGS_BUSY BIT(6)
#define DEFAULT_AUTOSUSPEND_DELAY 1000
#define FLAGS_IN_DATA_ST_SHIFT 8
#define FLAGS_OUT_DATA_ST_SHIFT 10
struct omap_des_ctx {
struct omap_des_dev *dd;
int keylen;
__le32 key[(3 * DES_KEY_SIZE) / sizeof(u32)];
unsigned long flags;
};
struct omap_des_reqctx {
unsigned long mode;
};
#define OMAP_DES_QUEUE_LENGTH 1
#define OMAP_DES_CACHE_SIZE 0
struct omap_des_algs_info {
struct skcipher_engine_alg *algs_list;
unsigned int size;
unsigned int registered;
};
struct omap_des_pdata {
struct omap_des_algs_info *algs_info;
unsigned int algs_info_size;
void (*trigger)(struct omap_des_dev *dd, int length);
u32 key_ofs;
u32 iv_ofs;
u32 ctrl_ofs;
u32 data_ofs;
u32 rev_ofs;
u32 mask_ofs;
u32 irq_enable_ofs;
u32 irq_status_ofs;
u32 dma_enable_in;
u32 dma_enable_out;
u32 dma_start;
u32 major_mask;
u32 major_shift;
u32 minor_mask;
u32 minor_shift;
};
struct omap_des_dev {
struct list_head list;
unsigned long phys_base;
void __iomem *io_base;
struct omap_des_ctx *ctx;
struct device *dev;
unsigned long flags;
int err;
struct tasklet_struct done_task;
struct skcipher_request *req;
struct crypto_engine *engine;
/*
* total is used by PIO mode for book keeping so introduce
* variable total_save as need it to calc page_order
*/
size_t total;
size_t total_save;
struct scatterlist *in_sg;
struct scatterlist *out_sg;
/* Buffers for copying for unaligned cases */
struct scatterlist in_sgl;
struct scatterlist out_sgl;
struct scatterlist *orig_out;
struct scatter_walk in_walk;
struct scatter_walk out_walk;
struct dma_chan *dma_lch_in;
struct dma_chan *dma_lch_out;
int in_sg_len;
int out_sg_len;
int pio_only;
const struct omap_des_pdata *pdata;
};
/* keep registered devices data here */
static LIST_HEAD(dev_list);
static DEFINE_SPINLOCK(list_lock);
#ifdef DEBUG
#define omap_des_read(dd, offset) \
({ \
int _read_ret; \
_read_ret = __raw_readl(dd->io_base + offset); \
pr_err("omap_des_read(" #offset "=%#x)= %#x\n", \
offset, _read_ret); \
_read_ret; \
})
#else
static inline u32 omap_des_read(struct omap_des_dev *dd, u32 offset)
{
return __raw_readl(dd->io_base + offset);
}
#endif
#ifdef DEBUG
#define omap_des_write(dd, offset, value) \
do { \
pr_err("omap_des_write(" #offset "=%#x) value=%#x\n", \
offset, value); \
__raw_writel(value, dd->io_base + offset); \
} while (0)
#else
static inline void omap_des_write(struct omap_des_dev *dd, u32 offset,
u32 value)
{
__raw_writel(value, dd->io_base + offset);
}
#endif
static inline void omap_des_write_mask(struct omap_des_dev *dd, u32 offset,
u32 value, u32 mask)
{
u32 val;
val = omap_des_read(dd, offset);
val &= ~mask;
val |= value;
omap_des_write(dd, offset, val);
}
static void omap_des_write_n(struct omap_des_dev *dd, u32 offset,
u32 *value, int count)
{
for (; count--; value++, offset += 4)
omap_des_write(dd, offset, *value);
}
static int omap_des_hw_init(