blob: b4e65fdd196bbffd415d552564f1a4ec7a26e5a8 [file] [log] [blame]
/*
* drivers/amlogic/crypto/aml-crypto-blkmv.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/hw_random.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/crypto.h>
#include <linux/cryptohash.h>
#include <crypto/scatterwalk.h>
#include <crypto/algapi.h>
#include <crypto/internal/hash.h>
#include "aml-crypto-blkmv.h"
// ---------------------------------------------------------------------
// DMA descriptor table support
// ---------------------------------------------------------------------
unsigned long swap_ulong32(unsigned long val)
{
unsigned char *p = (unsigned char *)&val;
return ((*p)<<24)+((*(p+1))<<16)+((*(p+2))<<8)+((*(p+3))<<0);
}
// --------------------------------------------
// NDMA_set_table_position
// --------------------------------------------
void ndma_set_table_position(unsigned long thread_num,
unsigned long table_start, unsigned long size)
{
unsigned long table_end;
table_end = table_start + size;
switch (thread_num) {
case 3:
cbus_wr(NDMA_THREAD_TABLE_START3, table_start);
cbus_wr(NDMA_THREAD_TABLE_END3, table_end);
/* Pulse thread init to register the new start/end locations */
cbus_wr(NDMA_THREAD_REG,
cbus_rd(NDMA_THREAD_REG) | (1 << 27));
break;
case 2:
cbus_wr(NDMA_THREAD_TABLE_START2, table_start);
cbus_wr(NDMA_THREAD_TABLE_END2, table_end);
/* Pulse thread init to register the new start/end locations */
cbus_wr(NDMA_THREAD_REG,
cbus_rd(NDMA_THREAD_REG) | (1 << 26));
break;
case 1:
cbus_wr(NDMA_THREAD_TABLE_START1, table_start);
cbus_wr(NDMA_THREAD_TABLE_END1, table_end);
/* Pulse thread init to register the new start/end locations */
cbus_wr(NDMA_THREAD_REG,
cbus_rd(NDMA_THREAD_REG) | (1 << 25));
break;
case 0:
cbus_wr(NDMA_THREAD_TABLE_START0, table_start);
cbus_wr(NDMA_THREAD_TABLE_END0, table_end);
/* Pulse thread init to register the new start/end locations */
cbus_wr(NDMA_THREAD_REG,
cbus_rd(NDMA_THREAD_REG) | (1 << 24));
break;
}
}
/* --------------------------------------------
* ndma_add_descriptor_1d
* -------------------------------------------
*/
void ndma_add_descriptor_1d(
unsigned long table,
unsigned long irq,
unsigned long restart,
unsigned long pre_endian,
unsigned long post_endian,
unsigned long type,
unsigned long bytes_to_move,
unsigned long inlinetype,
unsigned long src_addr,
unsigned long dest_addr)
{
unsigned long *p = (unsigned long *) table;
unsigned long curr_key;
unsigned long keytype;
unsigned long cryptodir;
unsigned long mode;
(*p++) = (0x01 << 30) | /* owned by CPU */
(pre_endian << 27) |
(0 << 26) |
(0 << 25) |
(inlinetype << 22) | /* TDES in-place processing */
(irq << 21) |
(0 << 0); /* thread slice */
(*p++) = src_addr;
(*p++) = dest_addr;
(*p++) = bytes_to_move & 0x01FFFFFF;
(*p++) = 0x00000000; /* 2D entry */
(*p++) = 0x00000000; /* 2D entry */
/* Prepare the pointer for the next descriptor boundary
* inline processing + bytes to move extension
*/
switch (inlinetype) {
case INLINE_TYPE_TDES:
curr_key = (type & 0x3);
(*p++) = (restart << 6) |
(curr_key << 3) |
(post_endian << 0);
break;
case INLINE_TYPE_DIVX:
(*p++) = post_endian & 0x7;
break;
case INLINE_TYPE_CRC:
(*p++) = post_endian & 0x7;
break;
case INLINE_TYPE_AES:
keytype = (type & 0x3);
cryptodir = ((type >> 2) & 1);
mode = ((type >> 3) & 3);
(*p++) = (pre_endian << 0) |
(post_endian << 4) |
(keytype << 8) |
(cryptodir << 10)|
(((restart) ? 1 : 0) << 11)|
(mode << 12) |
(0 << 14) | /* 128 bit ctr */
(0xf << 16); /* big endian */
break;
default:
*p++ = 0;
break;
}
*p = 0;
}
void ndma_set_table_count(unsigned long thread_num, unsigned int cnt)
{
cbus_wr(NDMA_TABLE_ADD_REG, (thread_num << 8) | (cnt << 0));
}
/* --------------------------------------------
* ndma_start()
* --------------------------------------------
* Start the block move procedure
*/
void ndma_start(unsigned long thread_num)
{
/* Enable */
cbus_wr(NDMA_CNTL_REG0,
(cbus_rd(NDMA_CNTL_REG0) | (1 << NDMA_ENABLE)));
cbus_wr(NDMA_THREAD_REG,
(cbus_rd(NDMA_THREAD_REG)
| (1 << (thread_num + 8))));
}
void ndma_stop(unsigned long thread_num)
{
cbus_wr(NDMA_THREAD_REG,
cbus_rd(NDMA_THREAD_REG)
& ~(1 << (thread_num + 8)));
/* If no threads enabled, then shut down the DMA engine completely */
if (!(cbus_rd(NDMA_THREAD_REG) & (0xF << 8)))
cbus_wr(NDMA_CNTL_REG0,
cbus_rd(NDMA_CNTL_REG0)
& ~(1 << NDMA_ENABLE));
}
/* --------------------------------------------
* ndma_wait_for_completion()
* --------------------------------------------
* Wait for all block moves to complete
*/
void ndma_wait_for_completion(unsigned long thread_num)
{
while ((cbus_rd(NDMA_TABLE_ADD_REG) & (0xF << (thread_num*8))))
;
}