blob: 457b4283be0ba270ec8ddadeca3cb70ba0e78702 [file] [log] [blame]
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/dst.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
#error Kernel versions lower than 2.6.32 are not supported
#else
#include <plat/hardware.h>
#include <plat/dma.h>
#include <plat/tc.h>
#endif
#include "omap3_crypto.h"
/* ========================================================================= */
/* DEBUG */
/* ========================================================================= */
static int omap3_crypto_debug = 0;
#define dprintk(a...) if (omap3_crypto_debug) printk(a);
#define KASSERT(c,p) if (!(c)) { printk p ; } else
#define __HEXDUMP(str, ptr, len) // \
{ \
int __i; \
dprintk( str ); \
dprintk( "\n" ); \
for (__i=0;__i<len;__i++) { \
dprintk("%02x%s", (((uint8_t*)ptr)[__i]), (((__i&0xF) == 0xF)?"\n":"")); \
} \
dprintk( "\n" ); \
}
/* ========================================================================= */
/* ENUMERATION FOR H/W INSTANCES */
/* ========================================================================= */
enum {
OMAP3_CRYPTO_DES1_IDX,
OMAP3_CRYPTO_DES2_IDX,
OMAP3_CRYPTO_SHA2MD5_IDX,
OMAP3_CRYPTO_SHA1MD5_IDX,
OMAP3_CRYPTO_AES1_IDX,
OMAP3_CRYPTO_AES2_IDX,
OMAP3_CRYPTO_MAX_IDX
};
/* ========================================================================= */
/* REGISTER STRUCTURES */
/* ========================================================================= */
typedef struct {
uint32_t REVISION;
uint32_t pad0;
uint32_t IRQSTATUS_L[4];
uint32_t IRQENABLE_L[4];
uint32_t SYSSTATUS;
uint32_t OCP_SYSCONFIG;
uint32_t pad1[13];
uint32_t CAPS_0;
uint32_t CAPS_1;
uint32_t CAPS_2;
uint32_t CAPS_3;
uint32_t CAPS_4;
uint32_t GCR;
uint32_t pad2;
struct {
uint32_t CCR;
uint32_t CLNK_CTRL;
uint32_t CICR;
uint32_t CSR;
uint32_t CSDP;
uint32_t CEN;
uint32_t CFN;
uint32_t CSSA;
uint32_t CDSA;
uint32_t CSEI;
int32_t CSFI;
uint32_t CDEI;
int32_t CDFI;
uint32_t CSAC;
uint32_t CDAC;
uint32_t CCEN;
uint32_t CCFN;
uint32_t COLOR;
uint32_t CDP;
uint32_t CNDP;
uint32_t CCDN;
uint32_t pad3[3];
} chan[32];
} omap3_sdma_regs;
typedef struct {
uint8_t pad[0xA10];
uint32_t CM_ICLKEN1_CORE;
uint32_t CM_ICLKEN2_CORE;
} omap3_cm_regs;
typedef struct {
uint32_t KEY4_L;
uint32_t KEY4_H;
uint32_t KEY3_L;
uint32_t KEY3_H;
uint32_t KEY2_L;
uint32_t KEY2_H;
uint32_t KEY1_L;
uint32_t KEY1_H;
uint32_t IV_1;
uint32_t IV_2;
uint32_t IV_3;
uint32_t IV_4;
uint32_t CTRL;
uint32_t DATA_1;
uint32_t DATA_2;
uint32_t DATA_3;
uint32_t DATA_4;
uint32_t REV;
uint32_t MASK;
uint32_t SYSSTATUS;
} omap3_aes_regs;
/* AES_CTRL related definitions */
#define AES_CTRL_INPUT_READY_BIT 0x2
#define AES_CTRL_OUTPUT_READY_BIT 0x1
/*AES SYS STATUS related definitions*/
#define AES_SYS_STATUS_RESET_DONE_BIT 0x1
typedef struct {
uint32_t DIGEST_A;
uint32_t DIGEST_B;
uint32_t DIGEST_C;
uint32_t DIGEST_D;
uint32_t DIGEST_E;
uint32_t DIGCNT;
uint32_t CTRL;
uint32_t DIN_0;
uint32_t DIN_1;
uint32_t DIN_2;
uint32_t DIN_3;
uint32_t DIN_4;
uint32_t DIN_5;
uint32_t DIN_6;
uint32_t DIN_7;
uint32_t DIN_8;
uint32_t DIN_9;
uint32_t DIN_10;
uint32_t DIN_11;
uint32_t DIN_12;
uint32_t DIN_13;
uint32_t DIN_14;
uint32_t DIN_15;
uint32_t REV;
uint32_t MASK;
uint32_t SYSSTATUS;
} omap3_sha1md5_regs;
#define SHA1MD5_CTRL_INPUT_READY_BIT 0x2
#define SHA1MD5_CTRL_OUTPUT_READY_BIT 0x1
#define SHA1MD5_SYS_STATUS_RESET_DONE_BIT 0x1
typedef struct {
uint32_t DIGEST_A;
uint32_t DIGEST_B;
uint32_t DIGEST_C;
uint32_t DIGEST_D;
uint32_t DIGEST_E;
uint32_t DIGEST_F;
uint32_t DIGEST_G;
uint32_t DIGEST_H;
uint32_t DIGCNT;
uint32_t BYTE;
uint32_t IRQSTAT;
uint32_t CTRL;
uint32_t DIN_0;
uint32_t DIN_1;
uint32_t DIN_2;
uint32_t DIN_3;
uint32_t DIN_4;
uint32_t DIN_5;
uint32_t DIN_6;
uint32_t DIN_7;
uint32_t DIN_8;
uint32_t DIN_9;
uint32_t DIN_10;
uint32_t DIN_11;
uint32_t DIN_12;
uint32_t DIN_13;
uint32_t DIN_14;
uint32_t DIN_15;
uint32_t REV;
uint32_t MASK;
uint32_t SYSSTATUS;
} omap3_sha2md5_regs;
#define SHA2MD5_SYS_STATUS_RESET_DONE_BIT 0x1
#define SHA2MD5_IRQSTAT_INPUT_READY_BIT 0x2
#define SHA2MD5_IRQSTAT_OUTPUT_READY_BIT 0x1
typedef struct {
uint32_t KEY3_L;
uint32_t KEY3_H;
uint32_t KEY2_L;
uint32_t KEY2_H;
uint32_t KEY1_L;
uint32_t KEY1_H;
uint32_t IV_L;
uint32_t IV_H;
uint32_t CTRL;
uint32_t DATA_L;
uint32_t DATA_H;
uint32_t REV;
uint32_t MASK;
uint32_t SYSSTATUS;
} omap3_des_regs;
/* DES_CTRL related definitions */
#define DES_CTRL_INPUT_READY_BIT 0x2
#define DES_CTRL_OUTPUT_READY_BIT 0x1
/*DES SYS STATUS related definitions*/
#define DES_SYS_STATUS_RESET_DONE_BIT 0x1
/* ========================================================================= */
/* BASE ADDRESSES FOR MMAP */
/* ========================================================================= */
#define OMAP3_CRYPTO_MMAPS_SIZE 0x1000
#define OMAP3_CRYPTO_NUM_MMAPS 8
static const uint32_t omap3_crypto_base_addresses[OMAP3_CRYPTO_NUM_MMAPS] = {
0x480A2000, // DES_1
0x480C1000, // DES_2
0x480A4000, // SHA_MD5_1
0x480C3000, // SHA_MD5_2
0x480A6000, // AES_1
0x480C5000, // AES_2
0x48004000, // Control module
0x48056000, // SDMA module
};
static void *omap3_crypto_reg_ptrs[OMAP3_CRYPTO_NUM_MMAPS] = {0};
#define SDMA_REGS ((volatile omap3_sdma_regs *)(omap3_crypto_reg_ptrs[7]))
#define CM_REGS ((volatile omap3_cm_regs *)(omap3_crypto_reg_ptrs[6]))
#define AES_REGS(x) ((volatile omap3_aes_regs *)(omap3_crypto_reg_ptrs[4+x]))
#define SHA2MD5_REGS ((volatile omap3_sha2md5_regs *)(omap3_crypto_reg_ptrs[2]))
#define SHA1MD5_REGS ((volatile omap3_sha1md5_regs *)(omap3_crypto_reg_ptrs[3]))
#define DES_REGS(x) ((volatile omap3_des_regs *)(omap3_crypto_reg_ptrs[x]))
/* ========================================================================= */
/* SCRATCH MEMORY MANAGEMENT */
/* ========================================================================= */
/* TODO : make this as module arguments */
static const uint32_t omap3_crypto_scratch_mem_len = (64*1024);
static const uint32_t omap3_crypto_scratch_mem_address = 0x40200000;
static uint8_t *omap3_crypto_scratch_mem_base_ptr;
typedef struct omap3_crypto_scratch_chunk {
struct omap3_crypto_scratch_chunk *next;
int len;
} omap3_crypto_scratch_chunk;
static omap3_crypto_scratch_chunk * omap3_crypto_scratch_free_head;
DEFINE_SPINLOCK(omap3_crypto_scratch_spinlock);
static int omap3_crypto_scratch_init(void)
{
omap3_crypto_scratch_mem_base_ptr = (uint8_t*)ioremap_nocache(
omap3_crypto_scratch_mem_address, omap3_crypto_scratch_mem_len);
if (NULL == omap3_crypto_scratch_mem_base_ptr) return -EINVAL;
omap3_crypto_scratch_free_head =
(omap3_crypto_scratch_chunk*)omap3_crypto_scratch_mem_base_ptr;
omap3_crypto_scratch_free_head->next = NULL;
omap3_crypto_scratch_free_head->len = omap3_crypto_scratch_mem_len;
return 0;
}
static void omap3_crypto_scratch_deinit(void)
{
iounmap(omap3_crypto_scratch_mem_base_ptr);
}
void *omap3_crypto_scratch_alloc(int len)
{
void *ret;
int more;
unsigned long flags;
omap3_crypto_scratch_chunk *cur, *prev, *new;
len = ((len+15)&(~15));
spin_lock_irqsave(&omap3_crypto_scratch_spinlock, flags);
if (NULL == omap3_crypto_scratch_free_head) { ret = NULL; }
else {
cur = omap3_crypto_scratch_free_head;
prev = NULL;
while (cur) {
if (cur->len >= len) break;
prev = cur;
cur = cur->next;
}
ret = (void*)cur;
more = cur->len - len;
if (more) {
new = (omap3_crypto_scratch_chunk*)(((uint8_t*)cur)+len);
new->len = more;
new->next = cur->next;
} else new = cur->next;
if (prev) prev->next = new;
else omap3_crypto_scratch_free_head = new;
}
spin_unlock_irqrestore(&omap3_crypto_scratch_spinlock, flags);
return ret;
}
void omap3_crypto_scratch_free(void *ptr, int len)
{
omap3_crypto_scratch_chunk *cur, *prev, *new;
unsigned long flags;
len = ((len+15)&(~15));
spin_lock_irqsave(&omap3_crypto_scratch_spinlock, flags);
prev = omap3_crypto_scratch_free_head;
new = (omap3_crypto_scratch_chunk*)ptr;
new->len = len;
if (prev == NULL || ((uint32_t)prev > (uint32_t)new)) {
new->next = prev;
omap3_crypto_scratch_free_head = new;
prev = new;
} else {
cur = prev->next;
while (cur && ((uint32_t)cur < (uint32_t)new)) {
prev = cur;
cur = cur->next;
}
prev->next = new;
new->next = cur;
/* Merge if contiguous */
if (((uint32_t)prev + prev->len) == (uint32_t)new) {
prev->next = new->next;
prev->len += new->len;
}
}
cur = prev->next;
/* Merge if contiguous */
if (cur && (((uint32_t)prev + prev->len) == (uint32_t)cur)) {
prev->next = cur->next;
prev->len += cur->len;
}
spin_unlock_irqrestore(&omap3_crypto_scratch_spinlock, flags);
}
uint32_t omap3_crypto_scratch_get_phys(uint8_t *ptr)
{
uint32_t ret;
ret = ((uint32_t)ptr) - ((uint32_t)omap3_crypto_scratch_mem_base_ptr);
if (ret > omap3_crypto_scratch_mem_len) return 0;
return (omap3_crypto_scratch_mem_address + ret);
}
/* ========================================================================= */
/* SDMA MANAGEMENT */
/* ========================================================================= */
static const uint32_t omap3_crypto_sdma_rx_num[OMAP3_CRYPTO_MAX_IDX] = {
11 + 1, // OMAP3_CRYPTO_DES1_IDX
67 + 1, // OMAP3_CRYPTO_DES2_IDX
0, // OMAP3_CRYPTO_SHA2MD5_IDX
0, // OMAP3_CRYPTO_SHA1MD5_IDX
9 + 1, // OMAP3_CRYPTO_AES1_IDX
65 + 1 // OMAP3_CRYPTO_AES2_IDX
};
static const uint32_t omap3_crypto_sdma_tx_num[OMAP3_CRYPTO_MAX_IDX] = {
10 + 1, // OMAP3_CRYPTO_DES1_IDX
66 + 1, // OMAP3_CRYPTO_DES2_IDX
12 + 1, // OMAP3_CRYPTO_SHA2MD5_IDX
68 + 1, // OMAP3_CRYPTO_SHA1MD5_IDX
8 + 1, // OMAP3_CRYPTO_AES1_IDX
64 + 1 // OMAP3_CRYPTO_AES2_IDX
};
static int omap3_crypto_sdma_channel_rx[OMAP3_CRYPTO_MAX_IDX];
static int omap3_crypto_sdma_channel_tx[OMAP3_CRYPTO_MAX_IDX];
#define OMAP3_CRYPTO_USE_TASKLETS 1
#ifdef OMAP3_CRYPTO_USE_TASKLETS
struct tasklet_struct omap3_crypto_sdma_tasklets[OMAP3_CRYPTO_MAX_IDX] = {
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
{ NULL, 0, ATOMIC_INIT(0), NULL, 0xFFFFFFFF },
};
#define omap3_crypto_sdma_cbs(idx) omap3_crypto_sdma_tasklets[idx].func
#define omap3_crypto_sdma_cb_data(idx) omap3_crypto_sdma_tasklets[idx].data
#else
static omap3_crypto_dma_callback _omap3_crypto_sdma_cbs[OMAP3_CRYPTO_MAX_IDX];
static unsigned long _omap3_crypto_sdma_cb_data[OMAP3_CRYPTO_MAX_IDX];
#define omap3_crypto_sdma_cbs(idx) _omap3_crypto_sdma_cbs[idx]
#define omap3_crypto_sdma_cb_data(idx) _omap3_crypto_sdma_cb_data[idx]
#endif
static void omap3_crypto_dma_tx_cb(int lch, u16 ch_status, void *data)
{
int idx = (int)data;
#ifndef OMAP3_CRYPTO_USE_TASKLETS
omap3_crypto_dma_callback cb = omap3_crypto_sdma_cbs(idx);
omap3_crypto_sdma_cbs(idx) = NULL;
if (NULL == cb) return;
#endif
// Clear the DMA start bit
switch (idx) {
case OMAP3_CRYPTO_DES1_IDX:
DES_REGS(0)->MASK = DES_REGS(0)->MASK & (~0x20);
break;
case OMAP3_CRYPTO_DES2_IDX:
DES_REGS(1)->MASK = DES_REGS(1)->MASK & (~0x20);
break;
case OMAP3_CRYPTO_SHA2MD5_IDX:
break;
case OMAP3_CRYPTO_SHA1MD5_IDX:
SHA1MD5_REGS->MASK = SHA1MD5_REGS->MASK & (~(1 << 3));
break;
case OMAP3_CRYPTO_AES1_IDX:
AES_REGS(0)->MASK = AES_REGS(0)->MASK & (~0x20);
break;
case OMAP3_CRYPTO_AES2_IDX:
AES_REGS(1)->MASK = AES_REGS(1)->MASK & (~0x20);
break;
}
#ifndef OMAP3_CRYPTO_USE_TASKLETS
cb(omap3_crypto_sdma_cb_data(idx));
#else
tasklet_schedule(&omap3_crypto_sdma_tasklets[idx]);
#endif
}
static void omap3_crypto_dma_init(void)
{
int i;
for (i=0; i<OMAP3_CRYPTO_MAX_IDX; i++) {
omap3_crypto_sdma_channel_rx[i] = -1;
omap3_crypto_sdma_channel_tx[i] = -1;
}
}
static int omap3_crypto_dma_alloc(int idx)
{
if (omap3_crypto_sdma_channel_rx[idx] >= 0) return 0;
if (omap3_crypto_sdma_channel_tx[idx] >= 0) return 0;
if (0 != omap3_crypto_sdma_rx_num[idx]) {
if (0 != omap_request_dma(OMAP_DMA_NO_DEVICE, "Crypto Driver",
NULL, NULL, &omap3_crypto_sdma_channel_rx[idx] )) {
return -ENOMEM;
}
}
if (0 != omap_request_dma(OMAP_DMA_NO_DEVICE, "Crypto Driver",
omap3_crypto_dma_tx_cb, (void *)idx, &omap3_crypto_sdma_channel_tx[idx] )) {
if (omap3_crypto_sdma_channel_rx[idx] >= 0)
omap_free_dma(omap3_crypto_sdma_channel_rx[idx]);
omap3_crypto_sdma_channel_rx[idx] = -1;
return -ENOMEM;
}
return 0;
}
static void omap3_crypto_dma_free(int idx)
{
if (omap3_crypto_sdma_channel_rx[idx] >= 0)
omap_free_dma(omap3_crypto_sdma_channel_rx[idx]);
if (omap3_crypto_sdma_channel_tx[idx] >= 0)
omap_free_dma(omap3_crypto_sdma_channel_tx[idx]);
omap3_crypto_sdma_channel_rx[idx] = -1;
omap3_crypto_sdma_channel_tx[idx] = -1;
}
/* ========================================================================= */
/* DES FUNCTIONS */
/* ========================================================================= */
static void omap3_crypto_des_reset(int id)
{
int des_inst = id - OMAP3_CRYPTO_DES1_IDX;
// Software reset
DES_REGS(des_inst)->MASK = 0x2;
// Wait for reset to complete
while (0 == (DES_REGS(des_inst)->SYSSTATUS & DES_SYS_STATUS_RESET_DONE_BIT));
}
void omap3_crypto_des_init(int id, uint8_t *key, int key_len, uint32_t mode)
{
int des_inst = id - OMAP3_CRYPTO_DES1_IDX;
uint32_t tmp_buf[6], *tmp;
DES_REGS(des_inst)->MASK = 0xC;
DES_REGS(des_inst)->CTRL &= 0x3;
DES_REGS(des_inst)->CTRL |= (mode << 3);
dprintk("keylen: %d, mode: %d\n", key_len, mode);
// Load key
if (((uint32_t)key)&3) {
tmp = tmp_buf;
memcpy(tmp, key, key_len);
} else tmp = (uint32_t*)key;
__HEXDUMP("Key", tmp, key_len);
DES_REGS(des_inst)->KEY1_L = tmp[0];
DES_REGS(des_inst)->KEY1_H = tmp[1];
if (key_len >= 24) {
DES_REGS(des_inst)->KEY2_L = tmp[2];
DES_REGS(des_inst)->KEY2_H = tmp[3];
DES_REGS(des_inst)->KEY3_L = tmp[4];
DES_REGS(des_inst)->KEY3_H = tmp[5];
}
};
void omap3_crypto_des_process(int id, int encryption, uint8_t *buf, int buf_len, uint8_t *iv)
{
int des_inst = id - OMAP3_CRYPTO_DES1_IDX;
uint32_t tmp_buf[2], *tmp;
KASSERT(((buf_len & 0x7) == 0), ("%s: buf_len [%d] is not a multiple of 8", __func__, buf_len));
encryption <<= 2;
if ((DES_REGS(des_inst)->CTRL & 0x4) != encryption)
DES_REGS(des_inst)->CTRL ^= 0x4;
if (iv) {
// Load iv
if (((uint32_t)iv)&3) {
tmp = tmp_buf;
memcpy(tmp, iv, 8);
} else tmp = (uint32_t*)iv;
__HEXDUMP("IV", tmp, 8);
DES_REGS(des_inst)->IV_L = tmp[0];
DES_REGS(des_inst)->IV_H = tmp[1];
}
if (((uint32_t)buf)&3) {
tmp = tmp_buf;
for (;buf_len>0;buf_len-=8,buf+=8) {
memcpy(tmp, buf, 8);
__HEXDUMP("INPUT", tmp, 8);
while (0 == (DES_REGS(des_inst)->CTRL & DES_CTRL_INPUT_READY_BIT));
DES_REGS(des_inst)->DATA_L = tmp[0];
DES_REGS(des_inst)->DATA_H = tmp[1];
while (0 == (DES_REGS(des_inst)->CTRL & DES_CTRL_OUTPUT_READY_BIT));
tmp[0] = DES_REGS(des_inst)->DATA_L;
tmp[1] = DES_REGS(des_inst)->DATA_H;
__HEXDUMP("OUTPUT", tmp, 8);
memcpy(buf, tmp, 8);
}
} else {
tmp = (uint32_t*)buf;
for (;buf_len>0;buf_len-=8,tmp+=2) {
__HEXDUMP("INPUT", tmp, 8);
while (0 == (DES_REGS(des_inst)->CTRL & DES_CTRL_INPUT_READY_BIT));
DES_REGS(des_inst)->DATA_L = tmp[0];
DES_REGS(des_inst)->DATA_H = tmp[1];
while (0 == (DES_REGS(des_inst)->CTRL & DES_CTRL_OUTPUT_READY_BIT));
tmp[0] = DES_REGS(des_inst)->DATA_L;
tmp[1] = DES_REGS(des_inst)->DATA_H;
__HEXDUMP("OUTPUT", tmp, 8);
}
}
}
void omap3_crypto_des_process_dma(int id, int encryption, uint8_t *buf, int buf_len, uint8_t *iv,
omap3_crypto_dma_callback cb, unsigned long data)
{
int des_inst = id - OMAP3_CRYPTO_DES1_IDX;
uint32_t tmp_buf[2], *tmp, phys;
int i;
KASSERT(((buf_len & 0x7) == 0), ("%s: buf_len [%d] is not a multiple of 8", __func__, buf_len));
phys = omap3_crypto_scratch_get_phys(buf);
if (0 == phys) {
omap3_crypto_des_process(id, encryption, buf, buf_len, iv);
cb(data);
return;
}
encryption <<= 2;
if ((DES_REGS(des_inst)->CTRL & 0x4) != encryption)
DES_REGS(des_inst)->CTRL ^= 0x4;
if (iv) {
// Load iv
if (((uint32_t)iv)&3) {
tmp = tmp_buf;
memcpy(tmp, iv, 8);
} else tmp = (uint32_t*)iv;
__HEXDUMP("IV", tmp, 8);
DES_REGS(des_inst)->IV_L = tmp[0];
DES_REGS(des_inst)->IV_H = tmp[1];
}
i = omap3_crypto_sdma_channel_rx[id];
// setup SDMA for input and output
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_rx_num[id] & 0x60) << 14) | (omap3_crypto_sdma_rx_num[id] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 2;
SDMA_REGS->chan[i].CFN = (buf_len >> 3);
SDMA_REGS->chan[i].CSSA = phys;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = 1;
SDMA_REGS->chan[i].CDSA = omap3_crypto_base_addresses[id] + 0x24;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = -7;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
i = omap3_crypto_sdma_channel_tx[id];
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 24) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_tx_num[id] & 0x60) << 14) | (omap3_crypto_sdma_tx_num[id] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 2;
SDMA_REGS->chan[i].CFN = (buf_len >> 3);
SDMA_REGS->chan[i].CSSA = omap3_crypto_base_addresses[id] + 0x24;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = -7;
SDMA_REGS->chan[i].CDSA = phys;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = 1;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
omap3_crypto_sdma_cbs(id) = cb;
omap3_crypto_sdma_cb_data(id) = data;
// set the START bit in mask register
DES_REGS(des_inst)->MASK = DES_REGS(des_inst)->MASK | 0x20;
}
/* ========================================================================= */
/* AES FUNCTIONS */
/* ========================================================================= */
static void omap3_crypto_aes_reset(int id)
{
int aes_inst = id - OMAP3_CRYPTO_AES1_IDX;
// Software reset
AES_REGS(aes_inst)->MASK = 0x2;
// Wait for reset to complete
while (0 == (AES_REGS(aes_inst)->SYSSTATUS & AES_SYS_STATUS_RESET_DONE_BIT));
}
void omap3_crypto_aes_init(int id, uint8_t *key, int key_len, uint32_t mode)
{
int aes_inst = id - OMAP3_CRYPTO_AES1_IDX;
uint32_t tmp_buf[8], *tmp;
AES_REGS(aes_inst)->MASK = 0xC;
AES_REGS(aes_inst)->CTRL &= 0x3;
AES_REGS(aes_inst)->CTRL |= ((mode << 5) | ((((key_len>>3)-1)&3) << 3));
dprintk("keylen: %d, mode: %d\n", key_len, mode);
// Load key
if (((uint32_t)key)&3) {
tmp = tmp_buf;
memcpy(tmp, key, key_len);
} else tmp = (uint32_t*)key;
__HEXDUMP("Key", tmp, key_len);
AES_REGS(aes_inst)->KEY1_L = tmp[0];
AES_REGS(aes_inst)->KEY1_H = tmp[1];
AES_REGS(aes_inst)->KEY2_L = tmp[2];
AES_REGS(aes_inst)->KEY2_H = tmp[3];
if (key_len >= 24) {
AES_REGS(aes_inst)->KEY3_L = tmp[4];
AES_REGS(aes_inst)->KEY3_H = tmp[5];
}
if (key_len >= 32) {
AES_REGS(aes_inst)->KEY4_L = tmp[6];
AES_REGS(aes_inst)->KEY4_H = tmp[7];
}
};
void omap3_crypto_aes_process(int id, int encryption, uint8_t *buf, int buf_len, uint8_t *iv)
{
int aes_inst = id - OMAP3_CRYPTO_AES1_IDX;
uint32_t tmp_buf[4], *tmp;
KASSERT(((buf_len & 0xF) == 0), ("%s: buf_len [%d] is not a multiple of 16", __func__, buf_len));
encryption <<= 2;
if ((AES_REGS(aes_inst)->CTRL & 0x4) != encryption)
AES_REGS(aes_inst)->CTRL ^= 0x4;
if (iv) {
// Load iv
if (((uint32_t)iv)&3) {
tmp = tmp_buf;
memcpy(tmp, iv, 16);
} else tmp = (uint32_t*)iv;
__HEXDUMP("IV", tmp, 16);
AES_REGS(aes_inst)->IV_1 = tmp[0];
AES_REGS(aes_inst)->IV_2 = tmp[1];
AES_REGS(aes_inst)->IV_3 = tmp[2];
AES_REGS(aes_inst)->IV_4 = tmp[3];
}
if (((uint32_t)buf)&3) {
tmp = tmp_buf;
for (;buf_len>0;buf_len-=16,buf+=16) {
memcpy(tmp, buf, 16);
__HEXDUMP("INPUT", tmp, 16);
while (0 == (AES_REGS(aes_inst)->CTRL & AES_CTRL_INPUT_READY_BIT));
AES_REGS(aes_inst)->DATA_1 = tmp[0];
AES_REGS(aes_inst)->DATA_2 = tmp[1];
AES_REGS(aes_inst)->DATA_3 = tmp[2];
AES_REGS(aes_inst)->DATA_4 = tmp[3];
while (0 == (AES_REGS(aes_inst)->CTRL & AES_CTRL_OUTPUT_READY_BIT));
tmp[0] = AES_REGS(aes_inst)->DATA_1;
tmp[1] = AES_REGS(aes_inst)->DATA_2;
tmp[2] = AES_REGS(aes_inst)->DATA_3;
tmp[3] = AES_REGS(aes_inst)->DATA_4;
__HEXDUMP("OUTPUT", tmp, 16);
memcpy(buf, tmp, 16);
}
} else {
tmp = (uint32_t*)buf;
for (;buf_len>0;buf_len-=16,tmp+=4) {
__HEXDUMP("INPUT", tmp, 16);
while (0 == (AES_REGS(aes_inst)->CTRL & AES_CTRL_INPUT_READY_BIT));
AES_REGS(aes_inst)->DATA_1 = tmp[0];
AES_REGS(aes_inst)->DATA_2 = tmp[1];
AES_REGS(aes_inst)->DATA_3 = tmp[2];
AES_REGS(aes_inst)->DATA_4 = tmp[3];
while (0 == (AES_REGS(aes_inst)->CTRL & AES_CTRL_OUTPUT_READY_BIT));
tmp[0] = AES_REGS(aes_inst)->DATA_1;
tmp[1] = AES_REGS(aes_inst)->DATA_2;
tmp[2] = AES_REGS(aes_inst)->DATA_3;
tmp[3] = AES_REGS(aes_inst)->DATA_4;
__HEXDUMP("OUTPUT", tmp, 16);
}
}
}
void omap3_crypto_aes_process_dma(int id, int encryption, uint8_t *buf, int buf_len, uint8_t *iv,
omap3_crypto_dma_callback cb, unsigned long data)
{
int aes_inst = id - OMAP3_CRYPTO_AES1_IDX;
uint32_t tmp_buf[4], *tmp, phys;
int i;
KASSERT(((buf_len & 0xF) == 0), ("%s: buf_len [%d] is not a multiple of 16", __func__, buf_len));
phys = omap3_crypto_scratch_get_phys(buf);
if (0 == phys) {
omap3_crypto_aes_process(id, encryption, buf, buf_len, iv);
cb(data);
return;
}
encryption <<= 2;
if ((AES_REGS(aes_inst)->CTRL & 0x4) != encryption)
AES_REGS(aes_inst)->CTRL ^= 0x4;
if (iv) {
// Load iv
if (((uint32_t)iv)&3) {
tmp = tmp_buf;
memcpy(tmp, iv, 16);
} else tmp = (uint32_t*)iv;
__HEXDUMP("IV", tmp, 16);
AES_REGS(aes_inst)->IV_1 = tmp[0];
AES_REGS(aes_inst)->IV_2 = tmp[1];
AES_REGS(aes_inst)->IV_3 = tmp[2];
AES_REGS(aes_inst)->IV_4 = tmp[3];
}
i = omap3_crypto_sdma_channel_rx[id];
// setup SDMA for input and output
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_rx_num[id] & 0x60) << 14) | (omap3_crypto_sdma_rx_num[id] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 4;
SDMA_REGS->chan[i].CFN = (buf_len >> 4);
SDMA_REGS->chan[i].CSSA = phys;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = 1;
SDMA_REGS->chan[i].CDSA = omap3_crypto_base_addresses[id] + 0x34;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = -15;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
i = omap3_crypto_sdma_channel_tx[id];
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 24) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_tx_num[id] & 0x60) << 14) | (omap3_crypto_sdma_tx_num[id] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 4;
SDMA_REGS->chan[i].CFN = (buf_len >> 4);
SDMA_REGS->chan[i].CSSA = omap3_crypto_base_addresses[id] + 0x34;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = -15;
SDMA_REGS->chan[i].CDSA = phys;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = 1;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
omap3_crypto_sdma_cbs(id) = cb;
omap3_crypto_sdma_cb_data(id) = data;
// set the START bit in mask register
AES_REGS(aes_inst)->MASK = AES_REGS(aes_inst)->MASK | 0x20;
}
/* ========================================================================= */
/* HASH FUNCTIONS */
/* ========================================================================= */
static void omap3_crypto_sha1md5_reset(void)
{
// Software reset
SHA1MD5_REGS->MASK = 0x2;
// Wait for reset to complete
while (0 == (SHA1MD5_REGS->SYSSTATUS & SHA1MD5_SYS_STATUS_RESET_DONE_BIT));
}
static void omap3_crypto_sha2md5_reset(void)
{
// Software reset
SHA2MD5_REGS->MASK = 0x2;
// Wait for reset to complete
while (0 == (SHA2MD5_REGS->SYSSTATUS & SHA2MD5_SYS_STATUS_RESET_DONE_BIT));
}
/* Saved state to continue a hash across process calls */
static uint32_t omap3_crypto_sha1md5_digcnt, omap3_crypto_sha2md5_digcnt;
static uint32_t omap3_crypto_sha1md5_hash[5], omap3_crypto_sha2md5_hash[8];
static uint32_t omap3_crypto_sha1md5_data[16], omap3_crypto_sha2md5_data[16];
static uint32_t omap3_crypto_sha1md5_data_len, omap3_crypto_sha2md5_data_len;
static const char * const omap3_crypto_hash_names[] = {
"", "", "", "", "MD5", "SHA1", "", "SHA2-224", "SHA2-256" };
static const uint32_t const omap3_crypto_algo[] = { 0, 0, 0, 0, 0, 1, 0, 2, 3 };
static void omap3_crypto_sha1md5_copy(uint8_t *buf, int len)
{
uint32_t tmp_buf[16];
int i;
volatile uint32_t *din = &(SHA1MD5_REGS->DIN_0);
if (((uint32_t)buf)&3) {
for (;len>0;len-=64,buf+=64) {
memcpy(tmp_buf, buf, 64);
__HEXDUMP("INPUT", tmp_buf, 64);
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = tmp_buf[i];
}
} else {
for (;len>0;len-=64,buf+=64) {
__HEXDUMP("INPUT", buf, 64);
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = ((uint32_t*)buf)[i];
}
}
}
struct semaphore omap3_crypto_sha1md5_dma_sem =
__SEMAPHORE_INITIALIZER(omap3_crypto_sha1md5_dma_sem, 0);
static void omap3_crypto_sha1md5_dma_callback(unsigned long data)
{
up(&omap3_crypto_sha1md5_dma_sem);
}
static void omap3_crypto_sha1md5_dma(uint8_t *buf, int len)
{
int i;
uint32_t phys;
phys = omap3_crypto_scratch_get_phys(buf);
if (0 == phys) {
omap3_crypto_sha1md5_copy(buf, len);
return;
}
i = omap3_crypto_sdma_channel_tx[OMAP3_CRYPTO_SHA1MD5_IDX];
omap3_crypto_sdma_cbs(OMAP3_CRYPTO_SHA1MD5_IDX) = omap3_crypto_sha1md5_dma_callback;
omap3_crypto_sdma_cb_data(OMAP3_CRYPTO_SHA1MD5_IDX) = 0;
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_tx_num[OMAP3_CRYPTO_SHA1MD5_IDX] & 0x60) << 14) |
(omap3_crypto_sdma_tx_num[OMAP3_CRYPTO_SHA1MD5_IDX] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 16;
SDMA_REGS->chan[i].CFN = (len >> 6);
SDMA_REGS->chan[i].CSSA = phys;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = 1;
SDMA_REGS->chan[i].CDSA = omap3_crypto_base_addresses[OMAP3_CRYPTO_SHA1MD5_IDX] + 0x1c;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = -63;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
// set the DMA_EN bit in mask register
SHA1MD5_REGS->MASK = (1 << 3);
if (0 != down_interruptible(&omap3_crypto_sha1md5_dma_sem)) {
printk("omap3_crypto_sha1md5_dma has hanged, bailing out!");
omap3_crypto_sha1md5_copy(buf, len);
// panic("omap3_crypto_sha1md5_dma wait interrupted!");
}
}
static void omap3_crypto_sha1md5_do(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
void (*cp_fxn)(uint8_t *, int))
{
uint32_t tmp;
int len, l, i, one_pkt;
volatile uint32_t *digest;
volatile uint32_t *din;
digest = &(SHA1MD5_REGS->DIGEST_A);
din = &(SHA1MD5_REGS->DIN_0);
len = omap3_crypto_sha1md5_data_len + buf_len;
one_pkt = (len>64)?0:1;
l = len & (~63);
if (l == len) l = (len-64);
if (0 == l) l = len;
hash_len >>= 2;
dprintk("omap3_crypto_sha1md5_do: buf_len: %d, len: %d l: %d\n", buf_len, len, l);
if (omap3_crypto_sha1md5_digcnt) { // are we continuing an earlier hash?
// reload digest and counter regs
SHA1MD5_REGS->CTRL = ((omap3_crypto_algo[hash_len] << 2) | (one_pkt << 4) | (l << 5));
for (i=0;i<hash_len;i++) digest[i] = omap3_crypto_sha1md5_hash[i];
SHA1MD5_REGS->DIGCNT = omap3_crypto_sha1md5_digcnt;
} else {
SHA1MD5_REGS->CTRL = ((omap3_crypto_algo[hash_len] << 2) | (1 << 3) | (one_pkt << 4) | (l << 5));
}
dprintk("Starting %s: %08x %08x\n", omap3_crypto_hash_names[hash_len],
SHA1MD5_REGS->CTRL, SHA1MD5_REGS->DIGCNT);
if (omap3_crypto_sha1md5_data_len) {
l = (!one_pkt)?64:len;
i = (l - omap3_crypto_sha1md5_data_len);
if (i) {
memcpy(((uint8_t*)omap3_crypto_sha1md5_data)+omap3_crypto_sha1md5_data_len, buf, i);
buf += i;
}
/* First process what is left over from the last time */
if (!one_pkt) {
// Not Last packet
len -= 64;
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = omap3_crypto_sha1md5_data[i];
omap3_crypto_sha1md5_data_len = 0;
}
}
/* Now process all except the last packet of 1-64 bytes */
l = len & (~63);
if (l == len) l = (len-64);
if (l > 0) { cp_fxn(buf, l); buf+=l; len-=l; }
if (!one_pkt) {
/* Save digest and digcnt */
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_OUTPUT_READY_BIT));
for (i=0;i<hash_len;i++) omap3_crypto_sha1md5_hash[i] = digest[i];
omap3_crypto_sha1md5_digcnt = SHA1MD5_REGS->DIGCNT;
dprintk("saving DIGCNT: %08x\n", omap3_crypto_sha1md5_digcnt);
dprintk("sha1 hash output = (%08x %08x %08x %08x %08x)\n",
omap3_crypto_sha1md5_hash[0],
omap3_crypto_sha1md5_hash[1],
omap3_crypto_sha1md5_hash[2],
omap3_crypto_sha1md5_hash[3],
omap3_crypto_sha1md5_hash[4]);
SHA1MD5_REGS->CTRL = (((hash_len==5)?1:0) << 2) | (1 << 4) | (len << 5);
SHA1MD5_REGS->DIGCNT = omap3_crypto_sha1md5_digcnt;
}
/* Last packet */
if (!omap3_crypto_sha1md5_data_len) {
memcpy(omap3_crypto_sha1md5_data, buf, len);
omap3_crypto_sha1md5_data_len = len;
}
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_INPUT_READY_BIT));
for (i=0;len>0;i++,len-=4) din[i] = omap3_crypto_sha1md5_data[i];
while (0 == (SHA1MD5_REGS->CTRL & SHA1MD5_CTRL_OUTPUT_READY_BIT));
if (hash_len == 5) { // SHA1
for (i=0;i<hash_len;i++) {
tmp = digest[i];
*hash++ = (tmp >> 24);
*hash++ = (tmp >> 16);
*hash++ = (tmp >> 8);
*hash++ = tmp;
}
}
else { // MD5
for (i=0;i<hash_len;i++) {
tmp = digest[i];
*hash++ = tmp;
*hash++ = (tmp >> 8);
*hash++ = (tmp >> 16);
*hash++ = (tmp >> 24);
}
}
}
static void omap3_crypto_sha1md5_process(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len)
{
omap3_crypto_sha1md5_do(buf,buf_len,hash,hash_len,omap3_crypto_sha1md5_copy);
}
static void omap3_crypto_sha1md5_process_dma(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
omap3_crypto_dma_callback cb, unsigned long data)
{
omap3_crypto_sha1md5_do(buf,buf_len,hash,hash_len,omap3_crypto_sha1md5_dma);
cb(data);
}
static void omap3_crypto_sha2md5_copy(uint8_t *buf, int len)
{
uint32_t tmp_buf[16];
int i;
volatile uint32_t *din = &(SHA2MD5_REGS->DIN_0);
if (((uint32_t)buf)&3) {
for (;len>0;len-=64,buf+=64) {
memcpy(tmp_buf, buf, 64);
__HEXDUMP("INPUT", tmp_buf, 64);
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = tmp_buf[i];
}
} else {
for (;len>0;len-=64,buf+=64) {
__HEXDUMP("INPUT", buf, 64);
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = ((uint32_t*)buf)[i];
}
}
}
struct semaphore omap3_crypto_sha2md5_dma_sem =
__SEMAPHORE_INITIALIZER(omap3_crypto_sha2md5_dma_sem, 0);
static void omap3_crypto_sha2md5_dma_callback(unsigned long data)
{
up(&omap3_crypto_sha2md5_dma_sem);
}
static void omap3_crypto_sha2md5_dma(uint8_t *buf, int len)
{
int i;
uint32_t phys;
phys = omap3_crypto_scratch_get_phys(buf);
if (0 == phys) {
omap3_crypto_sha2md5_copy(buf, len);
return;
}
i = omap3_crypto_sdma_channel_tx[OMAP3_CRYPTO_SHA2MD5_IDX];
omap3_crypto_sdma_cbs(OMAP3_CRYPTO_SHA2MD5_IDX) = omap3_crypto_sha2md5_dma_callback;
omap3_crypto_sdma_cb_data(OMAP3_CRYPTO_SHA2MD5_IDX) = 0;
SDMA_REGS->chan[i].CCR &= 0xFC030FC0;
SDMA_REGS->chan[i].CCR |= ((1 << 25) | (1 << 5) | (3 << 12) | (3 << 14) |
((omap3_crypto_sdma_tx_num[OMAP3_CRYPTO_SHA2MD5_IDX] & 0x60) << 14) |
(omap3_crypto_sdma_tx_num[OMAP3_CRYPTO_SHA2MD5_IDX] & 0x1F));
SDMA_REGS->chan[i].CSDP = 2;
SDMA_REGS->chan[i].CEN = 16;
SDMA_REGS->chan[i].CFN = (len >> 6);
SDMA_REGS->chan[i].CSSA = phys;
SDMA_REGS->chan[i].CSEI = 1;
SDMA_REGS->chan[i].CSFI = 1;
SDMA_REGS->chan[i].CDSA = omap3_crypto_base_addresses[OMAP3_CRYPTO_SHA2MD5_IDX] + 0x30;
SDMA_REGS->chan[i].CDEI = 1;
SDMA_REGS->chan[i].CDFI = -63;
SDMA_REGS->chan[i].CLNK_CTRL = 0;
SDMA_REGS->chan[i].CCR |= (1 << 7);
// set the DMA_EN bit in mask register
SHA2MD5_REGS->MASK = (1 << 3);
if (0 != down_interruptible(&omap3_crypto_sha2md5_dma_sem)) {
printk("omap3_crypto_sha2md5_dma has hanged, bailing out!");
omap3_crypto_sha2md5_copy(buf, len);
// panic("omap3_crypto_sha2md5_dma wait interrupted!");
}
}
static void omap3_crypto_sha2md5_do(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
void (*cp_fxn)(uint8_t *, int))
{
uint32_t tmp;
int len, l, i, one_pkt;
volatile uint32_t *digest;
volatile uint32_t *din;
digest = &(SHA2MD5_REGS->DIGEST_A);
din = &(SHA2MD5_REGS->DIN_0);
len = omap3_crypto_sha2md5_data_len + buf_len;
one_pkt = (len>64)?0:1;
l = len & (~63);
if (l == len) l = (len-64);
if (0 == l) l = len;
hash_len >>= 2;
dprintk("omap3_crypto_sha2md5_do: buf_len: %d, len: %d l: %d\n", buf_len, len, l);
if (omap3_crypto_sha2md5_digcnt) { // are we continuing an earlier hash?
// reload digest and counter regs
SHA2MD5_REGS->CTRL = ((omap3_crypto_algo[hash_len] << 1) | (one_pkt << 4) | (l << 5));
for (i=0;i<hash_len;i++) digest[i] = omap3_crypto_sha2md5_hash[i];
SHA2MD5_REGS->DIGCNT = omap3_crypto_sha2md5_digcnt;
} else {
SHA2MD5_REGS->CTRL = ((omap3_crypto_algo[hash_len] << 1) | (1 << 3) | (one_pkt << 4) | (l << 5));
}
dprintk("Starting %s: %08x %08x\n", omap3_crypto_hash_names[hash_len],
SHA2MD5_REGS->CTRL, SHA2MD5_REGS->DIGCNT);
if (omap3_crypto_sha2md5_data_len) {
l = (!one_pkt)?64:len;
i = (l - omap3_crypto_sha2md5_data_len);
if (i) {
memcpy(((uint8_t*)omap3_crypto_sha2md5_data)+omap3_crypto_sha2md5_data_len, buf, i);
buf += i;
}
/* First process what is left over from the last time */
if (!one_pkt) {
// Not Last packet
len -= 64;
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_INPUT_READY_BIT));
for (i=0;i<16;i++) din[i] = omap3_crypto_sha2md5_data[i];
omap3_crypto_sha2md5_data_len = 0;
}
}
/* Now process all except the last packet of 1-64 bytes */
l = len & (~63);
if (l == len) l = (len-64);
if (l > 0) { cp_fxn(buf, l); buf+=l; len-=l; }
if (!one_pkt) {
/* Save digest and digcnt */
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_OUTPUT_READY_BIT));
for (i=0;i<hash_len;i++) omap3_crypto_sha2md5_hash[i] = digest[i];
omap3_crypto_sha2md5_digcnt = SHA2MD5_REGS->DIGCNT;
dprintk("saving DIGCNT: %08x\n", omap3_crypto_sha2md5_digcnt);
dprintk("sha1 hash output = (%08x %08x %08x %08x %08x)\n",
omap3_crypto_sha2md5_hash[0],
omap3_crypto_sha2md5_hash[1],
omap3_crypto_sha2md5_hash[2],
omap3_crypto_sha2md5_hash[3],
omap3_crypto_sha2md5_hash[4]);
SHA2MD5_REGS->CTRL = (omap3_crypto_algo[hash_len] << 1) | (1 << 4) | (len << 5);
SHA2MD5_REGS->DIGCNT = omap3_crypto_sha2md5_digcnt;
}
/* Last packet */
if (!omap3_crypto_sha2md5_data_len) {
memcpy(omap3_crypto_sha2md5_data, buf, len);
omap3_crypto_sha2md5_data_len = len;
}
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_INPUT_READY_BIT));
for (i=0;len>0;i++,len-=4) din[i] = omap3_crypto_sha2md5_data[i];
while (0 == (SHA2MD5_REGS->IRQSTAT & SHA2MD5_IRQSTAT_OUTPUT_READY_BIT));
if (hash_len >= 5) { // SHAx works big endian
for (i=0;i<hash_len;i++) {
tmp = digest[i];
*hash++ = (tmp >> 24);
*hash++ = (tmp >> 16);
*hash++ = (tmp >> 8);
*hash++ = tmp;
}
}
else { // MD5
for (i=0;i<hash_len;i++) {
tmp = digest[i];
*hash++ = tmp;
*hash++ = (tmp >> 8);
*hash++ = (tmp >> 16);
*hash++ = (tmp >> 24);
}
}
}
static void omap3_crypto_sha2md5_process(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len)
{
omap3_crypto_sha2md5_do(buf,buf_len,hash,hash_len,omap3_crypto_sha2md5_copy);
}
static void omap3_crypto_sha2md5_process_dma(uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
omap3_crypto_dma_callback cb, unsigned long data)
{
omap3_crypto_sha2md5_do(buf,buf_len,hash,hash_len,omap3_crypto_sha2md5_dma);
cb(data);
}
#define NUM_HASH_INSTANCES 2
#define HASH_INST(id) (id-OMAP3_CRYPTO_SHA2MD5_IDX)
static void (*omap3_crypto_hash_process_fptr[NUM_HASH_INSTANCES])(
uint8_t *buf, int buf_len, uint8_t *hash, int hash_len);
static void (*omap3_crypto_hash_process_dma_fptr[NUM_HASH_INSTANCES])(
uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
omap3_crypto_dma_callback cb, unsigned long data);
void omap3_crypto_hash_init(int id, int alg, int hash_len)
{
if (id == OMAP3_CRYPTO_SHA2MD5_IDX) {
omap3_crypto_hash_process_fptr[HASH_INST(id)] = omap3_crypto_sha2md5_process;
omap3_crypto_hash_process_dma_fptr[HASH_INST(id)] = omap3_crypto_sha2md5_process_dma;
omap3_crypto_sha2md5_digcnt = 0;
omap3_crypto_sha2md5_data_len = 0;
} else {
omap3_crypto_hash_process_fptr[HASH_INST(id)] = omap3_crypto_sha1md5_process;
omap3_crypto_hash_process_dma_fptr[HASH_INST(id)] = omap3_crypto_sha1md5_process_dma;
omap3_crypto_sha1md5_digcnt = 0;
omap3_crypto_sha1md5_data_len = 0;
}
}
void omap3_crypto_hash_process(int id, uint8_t *buf, int buf_len, uint8_t *hash, int hash_len)
{
omap3_crypto_hash_process_fptr[HASH_INST(id)](buf, buf_len, hash, hash_len);
}
void omap3_crypto_hash_process_dma(int id, uint8_t *buf, int buf_len, uint8_t *hash, int hash_len,
omap3_crypto_dma_callback cb, unsigned long data)
{
omap3_crypto_hash_process_dma_fptr[HASH_INST(id)](buf, buf_len, hash, hash_len, cb, data);
}
/* ========================================================================= */
/* H/W CHANNEL MANGEMENT */
/* ========================================================================= */
static const uint32_t
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_MAX_IDX] = {
(1 << 0), // OMAP3_CRYPTO_DES1_IDX
(1 << 26), // OMAP3_CRYPTO_DES2_IDX
(1 << 1), // OMAP3_CRYPTO_SHA2MD5_IDX
(1 << 27), // OMAP3_CRYPTO_SHA1MD5_IDX
(1 << 3), // OMAP3_CRYPTO_AES1_IDX
(1 << 28) // OMAP3_CRYPTO_AES2_IDX
};
static uint32_t omap3_crypto_mod_used[OMAP3_CRYPTO_MAX_IDX];
DECLARE_MUTEX(omap3_crypto_mutex);
/* allocate algorithms of types in array alg_list (terminated by -1)
and returns ids in id_list (terminated by -1) */
int omap3_crypto_alloc(int *alg_list, int *id_list)
{
int ret = 0;
int i;
// uint32_t clken_mask = 0;
if (0 != (ret = down_interruptible(&omap3_crypto_mutex))) return ret;
// Look at requests
for (i = 0; alg_list[i] != -1; i++) {
switch (alg_list[i]) {
case OMAP3_CRYPTO_ALG_AES:
if (omap3_crypto_mod_used[OMAP3_CRYPTO_AES2_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_AES2_IDX;
else if (omap3_crypto_mod_used[OMAP3_CRYPTO_AES1_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_AES1_IDX;
else ret = -EBUSY;
break;
case OMAP3_CRYPTO_ALG_DES:
if (omap3_crypto_mod_used[OMAP3_CRYPTO_DES2_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_DES2_IDX;
else if (omap3_crypto_mod_used[OMAP3_CRYPTO_DES1_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_DES1_IDX;
else ret = -EBUSY;
break;
case OMAP3_CRYPTO_ALG_SHA1:
case OMAP3_CRYPTO_ALG_MD5:
if (omap3_crypto_mod_used[OMAP3_CRYPTO_SHA2MD5_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_SHA2MD5_IDX;
else if (omap3_crypto_mod_used[OMAP3_CRYPTO_SHA1MD5_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_SHA1MD5_IDX;
else ret = -EBUSY;
break;
case OMAP3_CRYPTO_ALG_SHA2:
if (omap3_crypto_mod_used[OMAP3_CRYPTO_SHA2MD5_IDX] == 0)
id_list[i] = OMAP3_CRYPTO_SHA2MD5_IDX;
else ret = -EBUSY;
break;
default:
// TODO: rest of the algorithms
ret = -EINVAL;
break;
}
if (0 != ret) break;
// if (0 != (ret = omap3_crypto_dma_alloc(id_list[i]))) break;
omap3_crypto_mod_used[id_list[i]] = omap3_crypto_mod_clken_bits[id_list[i]];
// clken_mask |= omap3_crypto_mod_clken_bits[id_list[i]];
}
id_list[i] = -1;
if (0 != ret) {
for (--i; i >= 0; i--) {
omap3_crypto_mod_used[id_list[i]] = 0;
// omap3_crypto_dma_free(id_list[i]);
}
} else {
#if 0
// Enable clocks to these modules
CM_REGS->CM_ICLKEN1_CORE |= (clken_mask & 0xFFFF0000);
CM_REGS->CM_ICLKEN2_CORE |= (clken_mask & 0xFFFF);
#endif
}
up(&omap3_crypto_mutex);
return ret;
}
/* free algorithm ids in id_list (terminated by -1) */
int omap3_crypto_free(int *id_list)
{
int i, ret;
uint32_t clken_mask = 0;
if (0 != (ret = down_interruptible(&omap3_crypto_mutex))) return ret;
for (i = 0; id_list[i] != -1; i++) {
clken_mask |= omap3_crypto_mod_used[id_list[i]];
omap3_crypto_mod_used[id_list[i]] = 0;
// omap3_crypto_dma_free(id_list[i]);
}
#if 0
// Disable clocks to these modules
CM_REGS->CM_ICLKEN1_CORE &= ((~clken_mask) | 0xFFFF);
CM_REGS->CM_ICLKEN2_CORE &= ((~clken_mask) | 0xFFFF0000);
#endif
up(&omap3_crypto_mutex);
return 0;
}
/* ========================================================================= */
/* MODULE INIT AND DEINIT */
/* ========================================================================= */
int omap3_crypto_init(int debug_flag)
{
int i;
uint32_t clken_mask;
omap3_crypto_debug = debug_flag;
dprintk("%s(%p)\n", __FUNCTION__, omap3_crypto_init);
for (i = 0; i < OMAP3_CRYPTO_NUM_MMAPS; i++) {
omap3_crypto_reg_ptrs[i] = ioremap_nocache(omap3_crypto_base_addresses[i],
OMAP3_CRYPTO_MMAPS_SIZE);
if (NULL == omap3_crypto_reg_ptrs[i])
panic("omap3_crypto: ioremap_nocache() failed!");
}
if (0 != omap3_crypto_scratch_init())
panic("omap3_crypto: omap3_crypto_scratch_init() failed!");
omap3_crypto_dma_init();
for (i = 0; i < OMAP3_CRYPTO_MAX_IDX; i++)
omap3_crypto_mod_used[i] = 0;
clken_mask = omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_AES1_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_AES2_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_DES1_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_DES2_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_SHA1MD5_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_SHA2MD5_IDX];
// Enable clocks to these modules
CM_REGS->CM_ICLKEN1_CORE |= (clken_mask & 0xFFFF0000);
CM_REGS->CM_ICLKEN2_CORE |= (clken_mask & 0xFFFF);
// Wait for sometime for the clocks to be enabled
{ volatile int x = 0;
for (x=0; x<10000; x++); }
omap3_crypto_aes_reset(OMAP3_CRYPTO_AES1_IDX);
omap3_crypto_aes_reset(OMAP3_CRYPTO_AES2_IDX);
omap3_crypto_des_reset(OMAP3_CRYPTO_DES1_IDX);
omap3_crypto_des_reset(OMAP3_CRYPTO_DES2_IDX);
omap3_crypto_sha1md5_reset();
omap3_crypto_sha2md5_reset();
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_AES1_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_AES2_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_DES1_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_DES2_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_SHA1MD5_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
if (0 != omap3_crypto_dma_alloc(OMAP3_CRYPTO_SHA2MD5_IDX)) {
panic("omap3_crypto: omap3_crypto_dma_alloc() failed!");
}
return 0;
}
void omap3_crypto_exit(void)
{
int i;
uint32_t clken_mask;
clken_mask = omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_AES1_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_AES2_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_DES1_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_DES2_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_SHA1MD5_IDX] |
omap3_crypto_mod_clken_bits[OMAP3_CRYPTO_SHA2MD5_IDX];
// Disable clocks to these modules
CM_REGS->CM_ICLKEN1_CORE &= ((~clken_mask) | 0xFFFF);
CM_REGS->CM_ICLKEN2_CORE &= ((~clken_mask) | 0xFFFF0000);
omap3_crypto_dma_free(OMAP3_CRYPTO_AES1_IDX);
omap3_crypto_dma_free(OMAP3_CRYPTO_AES2_IDX);
omap3_crypto_dma_free(OMAP3_CRYPTO_DES1_IDX);
omap3_crypto_dma_free(OMAP3_CRYPTO_DES2_IDX);
omap3_crypto_dma_free(OMAP3_CRYPTO_SHA1MD5_IDX);
omap3_crypto_dma_free(OMAP3_CRYPTO_SHA2MD5_IDX);
dprintk("%s()\n", __FUNCTION__);
for (i=0;i<OMAP3_CRYPTO_NUM_MMAPS;i++) {
iounmap(omap3_crypto_reg_ptrs[i]);
}
omap3_crypto_scratch_deinit();
}