blob: 8d3a30ea918d6a9ec3d40e3ea869c17a9f8f3e9d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 Nuvoton Technology Corp.
*/
#include <dm.h>
#include <uboot_aes.h>
#include <asm/io.h>
#include <asm/arch/aes.h>
#include <asm/arch/otp.h>
#include <malloc.h>
#include <time.h>
#define ONE_SECOND 0xC00000
struct npcm_aes_priv {
struct npcm_aes_regs *regs;
};
static struct npcm_aes_priv *aes_priv;
static u8 fkeyind_to_set = 0xff;
static int second_timeout(u32 *addr, u32 bitmask, u32 bitpol)
{
ulong time, i = 0;
time = get_timer(0);
/* default 1 second timeout */
while (((readl(addr) & bitmask) == bitpol) && i < ONE_SECOND)
i++;
if (i == ONE_SECOND) {
printf("%xms timeout: addr = %x, mask = %x\n", (u32)get_timer(time),
*addr, bitmask);
return -1;
}
return 0;
}
int npcm_aes_select_key(u8 fkeyind)
{
if (npcm_otp_is_fuse_array_disabled(NPCM_KEY_SA)) {
printf("AES key access denied\n");
return -EACCES;
}
if (fkeyind < 4)
fkeyind_to_set = fkeyind;
return 0;
}
static int npcm_aes_init(u8 dec_enc)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 ctrl, orgctrlval, wrtimeout;
/* reset hw */
writel(readl(&regs->aes_sw_reset) | SW_RESET_BIT, &regs->aes_sw_reset);
writel(readl(&regs->aes_fifo_status) | DIN_FIFO_OVERFLOW, &regs->aes_fifo_status);
writel(readl(&regs->aes_fifo_status) | DOUT_FIFO_UNDERFLOW, &regs->aes_fifo_status);
/* Workaround to over come Errata #648 */
orgctrlval = readl(&regs->aes_control);
ctrl = (0x00002004 | dec_enc); /* AES256(CBC) */
if (ctrl != orgctrlval) {
writel(ctrl, &regs->aes_control);
if (ctrl != readl(&regs->aes_control)) {
u32 read_ctrl;
int intwr;
for (wrtimeout = 0; wrtimeout < 1000; wrtimeout++) {
for (intwr = 0 ; intwr < 10; intwr++) {
writel(ctrl, &regs->aes_control);
writew(ctrl, (u16 *)&regs->aes_control + 1);
/* Write configurable info in a single write operation */
mb();
}
read_ctrl = readl(&regs->aes_control);
if (ctrl == read_ctrl)
break;
}
if (wrtimeout == 1000) {
printf("\nTIMEOUT expected data=0x%x Actual AES_CONTROL data 0x%x\n\n",
ctrl, read_ctrl);
return -EAGAIN;
}
printf("Workaround success, wrtimeout = %d\n", wrtimeout);
}
}
if (second_timeout(&regs->aes_busy, AES_BUSY_BIT, AES_BUSY_BIT))
return -EAGAIN;
return 0;
}
static inline void npcm_aes_load_iv(u8 *iv)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 *p = (u32 *)iv;
u32 i;
/* Initialization Vector is loaded in 32-bit chunks */
for (i = 0; i < (SIZE_AES_BLOCK / sizeof(u32)); i++)
writel(p[i], &regs->aes_iv_0 + i);
}
static inline void npcm_aes_load_key(u8 *key)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 *p = (u32 *)key;
u32 i;
/* The key can be loaded either via the configuration or by using sideband
* key port (aes_select_key).
* If aes_select_key has been called ('fkeyind_to_set' was set to desired
* key index) and no key is specified (key is NULL), we should use the
* key index. Otherwise, we write the given key to the registers.
*/
if (!key && fkeyind_to_set < 4) {
npcm_otp_select_key(fkeyind_to_set);
/* Sample the new key */
writel(readl(&regs->aes_sk) | AES_SK_BIT, &regs->aes_sk);
} else {
/* Initialization Vector is loaded in 32-bit chunks */
for (i = 0; i < (2 * SIZE_AES_BLOCK / sizeof(u32)); i++)
writel(p[i], &regs->aes_key_0 + i);
fkeyind_to_set = 0xff;
}
}
static inline void npcm_aes_write(u32 *in)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 i;
/* 16 Byte AES Block is written in 32-bit chunks */
for (i = 0; i < (SIZE_AES_BLOCK / sizeof(u32)); i++)
writel(in[i], &regs->aes_fifo_data);
}
static inline void npcm_aes_read(u32 *out)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 i;
/* Data is read in 32-bit chunks */
for (i = 0; i < (SIZE_AES_BLOCK / sizeof(u32)); i++)
out[i] = readl(&regs->aes_fifo_data);
}
static void npcm_aes_feed(u32 num_aes_blocks, u32 *datain, u32 *dataout)
{
struct npcm_aes_regs *regs = aes_priv->regs;
u32 aes_datablk;
u32 total_blocks = num_aes_blocks;
u32 blocks_left = num_aes_blocks;
/* data mode */
writel(readl(&regs->aes_busy) | AES_BUSY_BIT, &regs->aes_busy);
/* Clear overflow and underflow */
writel(readl(&regs->aes_fifo_status) | DIN_FIFO_OVERFLOW, &regs->aes_fifo_status);
writel(readl(&regs->aes_fifo_status) | DOUT_FIFO_UNDERFLOW, &regs->aes_fifo_status);
/* datain/dataout is advanced in 32-bit chunks */
aes_datablk = (SIZE_AES_BLOCK / sizeof(u32));
/* Quit if there is no complete blocks */
if (total_blocks == 0)
return;
/* Write the first block */
if (total_blocks > 1) {
npcm_aes_write(datain);
datain += aes_datablk;
blocks_left--;
}
/* Write the second block */
if (total_blocks > 2) {
second_timeout(&regs->aes_fifo_status, DIN_FIFO_EMPTY, 0);
npcm_aes_write(datain);
datain += aes_datablk;
blocks_left--;
}
/* Write & read available blocks */
while (blocks_left > 0) {
second_timeout(&regs->aes_fifo_status, DIN_FIFO_FULL, DIN_FIFO_FULL);
/* Write next block */
npcm_aes_write(datain);
datain += aes_datablk;
/* Wait till DOUT FIFO is empty */
second_timeout(&regs->aes_fifo_status, DOUT_FIFO_EMPTY, DOUT_FIFO_EMPTY);
/* Read next block */
npcm_aes_read(dataout);
dataout += aes_datablk;
blocks_left--;
}
if (total_blocks > 2) {
second_timeout(&regs->aes_fifo_status, DOUT_FIFO_FULL, 0);
/* Read next block */
npcm_aes_read(dataout);
dataout += aes_datablk;
second_timeout(&regs->aes_fifo_status, DOUT_FIFO_FULL, 0);
/* Read next block */
npcm_aes_read(dataout);
dataout += aes_datablk;
} else if (total_blocks > 1) {
second_timeout(&regs->aes_fifo_status, DOUT_FIFO_FULL, 0);
/* Read next block */
npcm_aes_read(dataout);
dataout += aes_datablk;
}
}
void aes_expand_key(u8 *key, u32 key_size, u8 *expkey)
{
/* npcm hw expands the key automatically, just copy it */
memcpy(expkey, key, SIZE_AES_BLOCK * 2);
}
void aes_cbc_encrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
u32 num_aes_blocks)
{
if (npcm_aes_init(AES_OP_ENCRYPT))
return;
npcm_aes_load_iv(iv);
npcm_aes_load_key(key_exp);
npcm_aes_feed(num_aes_blocks, (u32 *)src, (u32 *)dst);
}
void aes_cbc_decrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
u32 num_aes_blocks)
{
if (npcm_aes_init(AES_OP_DECRYPT))
return;
npcm_aes_load_iv(iv);
npcm_aes_load_key(key_exp);
npcm_aes_feed(num_aes_blocks, (u32 *)src, (u32 *)dst);
}
static int npcm_aes_bind(struct udevice *dev)
{
aes_priv = calloc(1, sizeof(struct npcm_aes_priv));
if (!aes_priv) {
printf("%s: %d\n", __func__, __LINE__);
return -ENOMEM;
}
aes_priv->regs = dev_read_addr_ptr(dev);
if (!aes_priv->regs) {
printf("Cannot find aes reg address, binding failed\n");
return -EINVAL;
}
printf("AES: NPCM AES module bind OK\n");
return 0;
}
static const struct udevice_id npcm_aes_ids[] = {
{ .compatible = "nuvoton,npcm845-aes" },
{ .compatible = "nuvoton,npcm750-aes" },
{ }
};
U_BOOT_DRIVER(npcm_aes) = {
.name = "npcm_aes",
.id = UCLASS_MISC,
.of_match = npcm_aes_ids,
.priv_auto = sizeof(struct npcm_aes_priv),
.bind = npcm_aes_bind,
};