| // 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(®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, |
| }; |