/*
* Copyright (c) 2014 Imagination Technologies
* Authors: Will Thomas, James Hartley
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Interface structure taken from omap-sham driver
*/
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <crypto/internal/hash.h>
#include <crypto/md5.h>
#include <crypto/sha.h>
#define CR_RESET 0
#define CR_RESET_SET 1
#define CR_RESET_UNSET 0
#define CR_MESSAGE_LENGTH_H 0x4
#define CR_MESSAGE_LENGTH_L 0x8
#define CR_CONTROL 0xc
#define CR_CONTROL_BYTE_ORDER_3210 0
#define CR_CONTROL_BYTE_ORDER_0123 1
#define CR_CONTROL_BYTE_ORDER_2310 2
#define CR_CONTROL_BYTE_ORDER_1032 3
#define CR_CONTROL_BYTE_ORDER_SHIFT 8
#define CR_CONTROL_ALGO_MD5 0
#define CR_CONTROL_ALGO_SHA1 1
#define CR_CONTROL_ALGO_SHA224 2
#define CR_CONTROL_ALGO_SHA256 3
#define CR_INTSTAT 0x10
#define CR_INTENAB 0x14
#define CR_INTCLEAR 0x18
#define CR_INT_RESULTS_AVAILABLE BIT(0)
#define CR_INT_NEW_RESULTS_SET BIT(1)
#define CR_INT_RESULT_READ_ERR BIT(2)
#define CR_INT_MESSAGE_WRITE_ERROR BIT(3)
#define CR_INT_STATUS BIT(8)
#define CR_RESULT_QUEUE 0x1c
#define CR_RSD0 0x40
#define CR_CORE_REV 0x50
#define CR_CORE_DES1 0x60
#define CR_CORE_DES2 0x70
#define DRIVER_FLAGS_BUSY BIT(0)
#define DRIVER_FLAGS_FINAL BIT(1)
#define DRIVER_FLAGS_DMA_ACTIVE BIT(2)
#define DRIVER_FLAGS_OUTPUT_READY BIT(3)
#define DRIVER_FLAGS_INIT BIT(4)
#define DRIVER_FLAGS_CPU BIT(5)
#define DRIVER_FLAGS_DMA_READY BIT(6)
#define DRIVER_FLAGS_ERROR BIT(7)
#define DRIVER_FLAGS_SG BIT(8)
#define DRIVER_FLAGS_SHA1 BIT(18)
#define DRIVER_FLAGS_SHA224 BIT(19)
#define DRIVER_FLAGS_SHA256 BIT(20)
#define DRIVER_FLAGS_MD5 BIT(21)
#define IMG_HASH_QUEUE_LENGTH 20
#define IMG_HASH_DMA_THRESHOLD 64
#ifdef __LITTLE_ENDIAN
#define IMG_HASH_BYTE_ORDER CR_CONTROL_BYTE_ORDER_3210
#else
#define IMG_HASH_BYTE_ORDER CR_CONTROL_BYTE_ORDER_0123
#endif
struct img_hash_dev;
struct img_hash_request_ctx {
struct img_hash_dev *hdev;
u8 digest[SHA256_DIGEST_SIZE] __aligned(sizeof(u32));
unsigned long flags;
size_t digsize;
dma_addr_t dma_addr;
size_t dma_ct;
/* sg root */
struct scatterlist *sgfirst;
/* walk state */
struct scatterlist *sg;
size_t nents;
size_t offset;
unsigned int total;
size_t sent;
unsigned long op;
size_t bufcnt;
u8 buffer[0] __aligned(sizeof(u32));
struct ahash_request fallback_req;
};
struct img_hash_ctx {
struct img_hash_dev *hdev;
unsigned long flags;
struct crypto_ahash *fallback;
};
struct img_hash_dev {
struct list_head list;
struct device *dev;
struct clk *hash_clk;
struct clk *sys_clk;
void __iomem *io_base;
phys_addr_t bus_addr;
void __iomem *cpu_addr;
spinlock_t lock;
int err;
struct tasklet_struct done_task;
struct tasklet_struct dma_task;
unsigned long flags;
struct crypto_queue queue;
struct ahash_request *req;
struct dma_chan *dma_lch;
};
struct img_hash_drv {
struct list_head dev_list;
spinlock_t lock;
};
static struct img_hash_drv img_hash = {
.dev_list = LIST_HEAD_INIT(img_hash.dev_list),
.lock = __SPIN_LOCK_UNLOCKED(img_hash.lock),
};
static inline u32 img_hash_read(struct img_hash_dev *hdev, u32 offset)
{
return readl_relaxed(hdev->io_base + offset);
}
static inline void img_hash_write(struct img_hash_dev *hdev,
u32 offset, u32 value)
{
writel_relaxed(value, hdev->io_base + offset);
}
static inline u32 img_hash_read_result_queue(struct img_hash_dev *hdev)
{
return be32_to_cpu(img_hash_read(hdev, CR_RESULT_QUEUE));
}
static void img_hash_start(struct img_hash_dev *hdev, bool dma)
{
struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req);
u32 cr = IMG_HASH_BYTE_ORDER << CR_CONTROL_BYTE_ORDER_SHIFT;
if (ctx->flags & DRIVER_FLAGS_MD5)
cr |= CR_CONTROL_ALGO_MD5;
else if (ctx->flags & DRIVER_FLAGS_SHA1)
cr |= CR_CONTROL_ALGO_SHA1;
else if (ctx->flags & DRIVER_FLAGS_SHA224)
cr |= CR_CONTROL_ALGO_SHA224;
else if (ctx->flags & DRIVER_FLAGS_SHA256)
cr |= CR_CONTROL_ALGO_SHA256;
dev_dbg(hdev->dev, "Starting hash process\n");
img_hash_write(hdev, CR_CONTROL, cr);
/*
* The hardware block requires two cycles between writing the control
* register and writing the first word of data in non DMA mode, to
* ensure the first data write is not grouped in burst with the control
* register write a read is issued to 'flush' the bus.
*/
if (!dma)
img_hash_read(hdev, CR_CONTROL);
}
static int img_hash_xmit_cpu(struct img_hash_dev *hdev, const u8 *