// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2025 Rockchip Electronics Co., Ltd */ #include #include #include #include #include #include #include #include #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), };