diff options
Diffstat (limited to 'drivers/crypto/nuvoton/npcm_aes.c')
-rw-r--r-- | drivers/crypto/nuvoton/npcm_aes.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/drivers/crypto/nuvoton/npcm_aes.c b/drivers/crypto/nuvoton/npcm_aes.c new file mode 100644 index 0000000000..6493ea108e --- /dev/null +++ b/drivers/crypto/nuvoton/npcm_aes.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Nuvoton Technology Corp. + */ + +#include <common.h> +#include <dm.h> +#include <uboot_aes.h> +#include <asm/io.h> +#include <asm/arch/aes.h> +#include <asm/arch/otp.h> +#include <malloc.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(®s->aes_sw_reset) | SW_RESET_BIT, ®s->aes_sw_reset); + writel(readl(®s->aes_fifo_status) | DIN_FIFO_OVERFLOW, ®s->aes_fifo_status); + writel(readl(®s->aes_fifo_status) | DOUT_FIFO_UNDERFLOW, ®s->aes_fifo_status); + + /* Workaround to over come Errata #648 */ + orgctrlval = readl(®s->aes_control); + ctrl = (0x00002004 | dec_enc); /* AES256(CBC) */ + + if (ctrl != orgctrlval) { + writel(ctrl, ®s->aes_control); + + if (ctrl != readl(®s->aes_control)) { + u32 read_ctrl; + int intwr; + + for (wrtimeout = 0; wrtimeout < 1000; wrtimeout++) { + for (intwr = 0 ; intwr < 10; intwr++) { + writel(ctrl, ®s->aes_control); + writew(ctrl, (u16 *)®s->aes_control + 1); + /* Write configurable info in a single write operation */ + mb(); + } + + read_ctrl = readl(®s->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(®s->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], ®s->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(®s->aes_sk) | AES_SK_BIT, ®s->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], ®s->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], ®s->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(®s->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(®s->aes_busy) | AES_BUSY_BIT, ®s->aes_busy); + + /* Clear overflow and underflow */ + writel(readl(®s->aes_fifo_status) | DIN_FIFO_OVERFLOW, ®s->aes_fifo_status); + writel(readl(®s->aes_fifo_status) | DOUT_FIFO_UNDERFLOW, ®s->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(®s->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(®s->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(®s->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(®s->aes_fifo_status, DOUT_FIFO_FULL, 0); + + /* Read next block */ + npcm_aes_read(dataout); + dataout += aes_datablk; + + second_timeout(®s->aes_fifo_status, DOUT_FIFO_FULL, 0); + + /* Read next block */ + npcm_aes_read(dataout); + dataout += aes_datablk; + } else if (total_blocks > 1) { + second_timeout(®s->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, +}; |