You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
5.1 KiB
222 lines
5.1 KiB
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2025 Rockchip Electronics Co., Ltd
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <clk.h>
|
|
#include <keylad.h>
|
|
#include <dm.h>
|
|
#include <asm/io.h>
|
|
#include <clk-uclass.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/arch/clock.h>
|
|
|
|
#define KEYLAD_APB_CMD 0x0450
|
|
#define REG_APB_CMD_EN BIT(0)
|
|
#define VALUE_APB_CMD_DISABLE 0
|
|
#define VALUE_APB_CMD_ENABLE BIT(0)
|
|
|
|
#define KEYLAD_APB_PADDR 0x0454
|
|
#define KEYLAD_APB_PWDATA 0x0458
|
|
#define KEYLAD_APB_PWRITE 0x045C
|
|
#define KEYLAD_DATA_CTL 0x0460
|
|
#define VALUE_DATA_CTL_EN BIT(15)
|
|
|
|
#define KEYLAD_KEY_SEL 0x0610
|
|
#define VALUE_KEY_SEL_OUTER_KEY 0x00000000
|
|
|
|
#define KEYLAD_LOCKSTEP_FLAG 0x0618
|
|
#define KEYLAD_LOCKSTEP_EN 0x061C
|
|
|
|
#define KEY_LADDER_OTP_KEY_REQ 0x0640
|
|
#define KL_OTP_KEY_REQ_DST_ADDR(addr) ((addr) & 0x3) // 256bit algin address
|
|
#define KL_OTP_KEY_REQ_BYTE_SWAP BIT(4)
|
|
#define KL_OTP_KEY_REQ_WORD_SWAP BIT(5)
|
|
#define KL_OTP_KEY_REQ_EN BIT(8)
|
|
#define KL_OTP_KEY_ECC_ST BIT(12)
|
|
#define KL_OTP_KEY_REQ_SRC_ADDR(addr) (((addr) & 0xffff) << 16)// byte address, dword align
|
|
|
|
#define KEY_LADDER_KEY_LEN 0x0648
|
|
#define KL_KEY_LEN(len) ((len) & 0x3f)
|
|
|
|
#define KEYLAD_KEY_REG_SIZE_BYTES 4
|
|
#define KEYLAD_KEY_REG_NUM 32
|
|
#define KEYLAD_AREA_NUM 2
|
|
|
|
#define RK_KEYLAD_TIME_OUT 10000 /* max 10ms */
|
|
|
|
#define KEYLAD_POLL_TIMEOUT(condition, timeout, ret) do { \
|
|
u32 time_out = timeout; \
|
|
while (condition) { \
|
|
if (time_out-- == 0) { \
|
|
printf("[%s] %d: time out!\n", __func__, __LINE__); \
|
|
ret = -ETIMEDOUT; \
|
|
break; \
|
|
} \
|
|
udelay(1); \
|
|
} \
|
|
} while (0)
|
|
|
|
struct rockchip_keylad_priv {
|
|
fdt_addr_t reg;
|
|
};
|
|
|
|
fdt_addr_t keylad_base;
|
|
|
|
static inline u32 keylad_read(u32 offset)
|
|
{
|
|
return readl(keylad_base + offset);
|
|
}
|
|
|
|
static inline void keylad_write(u32 offset, u32 val)
|
|
{
|
|
writel(val, keylad_base + offset);
|
|
}
|
|
|
|
static int rk_get_fwkey_param(u32 keyid, u32 *offset, u32 *max_len)
|
|
{
|
|
switch (keyid) {
|
|
case RK_FW_KEY0:
|
|
*offset = OEM_CIPHER_KEY_FW_ADDR;
|
|
*max_len = OEM_CIPHER_KEY_FW_LEN;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_keylad_send_key(u32 key_reg, u32 n_words, ulong dst_addr)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* key_reg of 32bits can be 0-31 */
|
|
if ((key_reg + n_words) > KEYLAD_KEY_REG_NUM)
|
|
return -EINVAL;
|
|
|
|
for (u32 i = 0; i < n_words; i++) {
|
|
/* set destination addr */
|
|
keylad_write(KEYLAD_APB_PADDR,
|
|
(dst_addr & 0xffffffff) + (i * KEYLAD_KEY_REG_SIZE_BYTES));
|
|
/* select which word of key table to be sent */
|
|
keylad_write(KEYLAD_APB_PWDATA, key_reg + i);
|
|
|
|
keylad_write(KEYLAD_APB_CMD, VALUE_APB_CMD_ENABLE);
|
|
KEYLAD_POLL_TIMEOUT((keylad_read(KEYLAD_APB_CMD) & REG_APB_CMD_EN) ==
|
|
VALUE_APB_CMD_ENABLE, RK_KEYLAD_TIME_OUT, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rk_keylad_read_otp_key(u32 otp_offset, u32 keylad_area, u32 keylen)
|
|
{
|
|
int ret = 0;
|
|
u32 val = 0;
|
|
u32 nbytes = keylen;
|
|
|
|
/* keylad_area of 256bits can be 0-1 */
|
|
if (keylad_area >= KEYLAD_AREA_NUM)
|
|
return -EINVAL;
|
|
|
|
// rk_otp_keylad_read_init();
|
|
|
|
/* src use byte address, dst use keytable block address */
|
|
val = KL_OTP_KEY_REQ_SRC_ADDR(otp_offset / 2) |
|
|
KL_OTP_KEY_REQ_DST_ADDR(keylad_area) |
|
|
KL_OTP_KEY_REQ_BYTE_SWAP |
|
|
KL_OTP_KEY_REQ_EN;
|
|
|
|
keylad_write(KEYLAD_KEY_SEL, VALUE_KEY_SEL_OUTER_KEY);
|
|
|
|
keylad_write(KEY_LADDER_KEY_LEN, KL_KEY_LEN(nbytes));
|
|
|
|
keylad_write(KEY_LADDER_OTP_KEY_REQ, val);
|
|
|
|
KEYLAD_POLL_TIMEOUT(keylad_read(KEY_LADDER_OTP_KEY_REQ) & KL_OTP_KEY_REQ_EN,
|
|
RK_KEYLAD_TIME_OUT, ret);
|
|
|
|
val = keylad_read(KEY_LADDER_OTP_KEY_REQ);
|
|
if (val & KL_OTP_KEY_ECC_ST) {
|
|
printf("KEYLAD transfer OTP key ECC check error!");
|
|
ret = -EIO;
|
|
}
|
|
|
|
// rk_otp_keylad_read_deinit();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_keylad_transfer_fwkey(struct udevice *dev, ulong dst,
|
|
u32 fw_keyid, u32 keylen)
|
|
{
|
|
int res = 0;
|
|
u32 fw_key_offset;
|
|
u32 max_key_len = 0;
|
|
|
|
if (keylen % 4) {
|
|
printf("key_len(%u) must be multiple of 4 error.", keylen);
|
|
return -EINVAL;
|
|
}
|
|
|
|
res = rk_get_fwkey_param(fw_keyid, &fw_key_offset, &max_key_len);
|
|
if (res)
|
|
return res;
|
|
|
|
if (keylen > max_key_len) {
|
|
printf("key_len(%u) > %u error.", keylen, max_key_len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
res = rk_keylad_read_otp_key(fw_key_offset, 0, keylen);
|
|
if (res) {
|
|
printf("Keyladder read otp key err: 0x%x.", res);
|
|
return res;
|
|
}
|
|
|
|
/// TODO: enable clock
|
|
res = rk_keylad_send_key(0, keylen / 4, dst);
|
|
if (res) {
|
|
printf("Keyladder transfer key err: 0x%x.", res);
|
|
return res;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static const struct dm_keylad_ops rockchip_keylad_ops = {
|
|
.transfer_fwkey = rockchip_keylad_transfer_fwkey,
|
|
};
|
|
|
|
static int rockchip_keylad_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct rockchip_keylad_priv *priv = dev_get_priv(dev);
|
|
|
|
memset(priv, 0x00, sizeof(*priv));
|
|
|
|
priv->reg = (fdt_addr_t)dev_read_addr_ptr(dev);
|
|
if (priv->reg == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
keylad_base = priv->reg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id rockchip_keylad_ids[] = {
|
|
{
|
|
.compatible = "rockchip,keylad",
|
|
},
|
|
};
|
|
|
|
U_BOOT_DRIVER(rockchip_keylad) = {
|
|
.name = "rockchip_keylad",
|
|
.id = UCLASS_KEYLAD,
|
|
.of_match = rockchip_keylad_ids,
|
|
.ops = &rockchip_keylad_ops,
|
|
.ofdata_to_platdata = rockchip_keylad_ofdata_to_platdata,
|
|
.priv_auto_alloc_size = sizeof(struct rockchip_keylad_priv),
|
|
};
|