1ba1dc8d4STroy Lin // SPDX-License-Identifier: GPL-2.0
2ba1dc8d4STroy Lin /*
3ba1dc8d4STroy Lin * Copyright (c) 2025 Rockchip Electronics Co., Ltd
4ba1dc8d4STroy Lin */
5ba1dc8d4STroy Lin
6ba1dc8d4STroy Lin #include <common.h>
7ba1dc8d4STroy Lin #include <clk.h>
8ba1dc8d4STroy Lin #include <keylad.h>
9ba1dc8d4STroy Lin #include <dm.h>
1093607a96SLin Jinhan #include <misc.h>
11ba1dc8d4STroy Lin #include <asm/io.h>
12ba1dc8d4STroy Lin #include <clk-uclass.h>
13ba1dc8d4STroy Lin #include <asm/arch/hardware.h>
14ba1dc8d4STroy Lin #include <asm/arch/clock.h>
15ba1dc8d4STroy Lin
16ba1dc8d4STroy Lin #define KEYLAD_APB_CMD 0x0450
17ba1dc8d4STroy Lin #define REG_APB_CMD_EN BIT(0)
18ba1dc8d4STroy Lin #define VALUE_APB_CMD_DISABLE 0
19ba1dc8d4STroy Lin #define VALUE_APB_CMD_ENABLE BIT(0)
20ba1dc8d4STroy Lin
21ba1dc8d4STroy Lin #define KEYLAD_APB_PADDR 0x0454
22ba1dc8d4STroy Lin #define KEYLAD_APB_PWDATA 0x0458
23ba1dc8d4STroy Lin #define KEYLAD_APB_PWRITE 0x045C
24ba1dc8d4STroy Lin #define KEYLAD_DATA_CTL 0x0460
25ba1dc8d4STroy Lin #define VALUE_DATA_CTL_EN BIT(15)
26ba1dc8d4STroy Lin
27ba1dc8d4STroy Lin #define KEYLAD_KEY_SEL 0x0610
28ba1dc8d4STroy Lin #define VALUE_KEY_SEL_OUTER_KEY 0x00000000
29ba1dc8d4STroy Lin
30ba1dc8d4STroy Lin #define KEYLAD_LOCKSTEP_FLAG 0x0618
31ba1dc8d4STroy Lin #define KEYLAD_LOCKSTEP_EN 0x061C
32ba1dc8d4STroy Lin
33ba1dc8d4STroy Lin #define KEY_LADDER_OTP_KEY_REQ 0x0640
34ba1dc8d4STroy Lin #define KL_OTP_KEY_REQ_DST_ADDR(addr) ((addr) & 0x3) // 256bit algin address
35ba1dc8d4STroy Lin #define KL_OTP_KEY_REQ_BYTE_SWAP BIT(4)
36ba1dc8d4STroy Lin #define KL_OTP_KEY_REQ_WORD_SWAP BIT(5)
37ba1dc8d4STroy Lin #define KL_OTP_KEY_REQ_EN BIT(8)
38ba1dc8d4STroy Lin #define KL_OTP_KEY_ECC_ST BIT(12)
39ba1dc8d4STroy Lin #define KL_OTP_KEY_REQ_SRC_ADDR(addr) (((addr) & 0xffff) << 16)// byte address, dword align
40ba1dc8d4STroy Lin
41ba1dc8d4STroy Lin #define KEY_LADDER_KEY_LEN 0x0648
42ba1dc8d4STroy Lin #define KL_KEY_LEN(len) ((len) & 0x3f)
43ba1dc8d4STroy Lin
44ba1dc8d4STroy Lin #define KEYLAD_KEY_REG_SIZE_BYTES 4
45ba1dc8d4STroy Lin #define KEYLAD_KEY_REG_NUM 32
46ba1dc8d4STroy Lin #define KEYLAD_AREA_NUM 2
47ba1dc8d4STroy Lin
48ba1dc8d4STroy Lin #define RK_KEYLAD_TIME_OUT 10000 /* max 10ms */
49ba1dc8d4STroy Lin
50ba1dc8d4STroy Lin #define KEYLAD_POLL_TIMEOUT(condition, timeout, ret) do { \
51ba1dc8d4STroy Lin u32 time_out = timeout; \
52ba1dc8d4STroy Lin while (condition) { \
53ba1dc8d4STroy Lin if (time_out-- == 0) { \
54ba1dc8d4STroy Lin printf("[%s] %d: time out!\n", __func__, __LINE__); \
55ba1dc8d4STroy Lin ret = -ETIMEDOUT; \
56ba1dc8d4STroy Lin break; \
57ba1dc8d4STroy Lin } \
58ba1dc8d4STroy Lin udelay(1); \
59ba1dc8d4STroy Lin } \
60ba1dc8d4STroy Lin } while (0)
61ba1dc8d4STroy Lin
62ba1dc8d4STroy Lin struct rockchip_keylad_priv {
63ba1dc8d4STroy Lin fdt_addr_t reg;
6486fd7407SLin Jinhan char *clocks;
6586fd7407SLin Jinhan u32 nclocks;
66ba1dc8d4STroy Lin };
67ba1dc8d4STroy Lin
68ba1dc8d4STroy Lin fdt_addr_t keylad_base;
69ba1dc8d4STroy Lin
rk_keylad_do_enable_clk(struct udevice * dev,int enable)7086fd7407SLin Jinhan static int rk_keylad_do_enable_clk(struct udevice *dev, int enable)
7186fd7407SLin Jinhan {
7286fd7407SLin Jinhan struct rockchip_keylad_priv *priv = dev_get_priv(dev);
7386fd7407SLin Jinhan struct clk clk;
7486fd7407SLin Jinhan int i, ret;
7586fd7407SLin Jinhan
7686fd7407SLin Jinhan for (i = 0; i < priv->nclocks; i++) {
7786fd7407SLin Jinhan ret = clk_get_by_index(dev, i, &clk);
7886fd7407SLin Jinhan if (ret < 0) {
7986fd7407SLin Jinhan printf("Keylad failed to get clk index %d, ret=%d\n", i, ret);
8086fd7407SLin Jinhan return ret;
8186fd7407SLin Jinhan }
8286fd7407SLin Jinhan
8386fd7407SLin Jinhan if (enable)
8486fd7407SLin Jinhan ret = clk_enable(&clk);
8586fd7407SLin Jinhan else
8686fd7407SLin Jinhan ret = clk_disable(&clk);
8786fd7407SLin Jinhan
8886fd7407SLin Jinhan if (ret < 0 && ret != -ENOSYS) {
8986fd7407SLin Jinhan printf("Keylad failed to enable(%d) clk(%ld): ret=%d\n",
9086fd7407SLin Jinhan enable, clk.id, ret);
9186fd7407SLin Jinhan return ret;
9286fd7407SLin Jinhan }
9386fd7407SLin Jinhan }
9486fd7407SLin Jinhan
9586fd7407SLin Jinhan return 0;
9686fd7407SLin Jinhan }
9786fd7407SLin Jinhan
rk_keylad_enable_clk(struct udevice * dev)9886fd7407SLin Jinhan static int rk_keylad_enable_clk(struct udevice *dev)
9986fd7407SLin Jinhan {
10086fd7407SLin Jinhan return rk_keylad_do_enable_clk(dev, 1);
10186fd7407SLin Jinhan }
10286fd7407SLin Jinhan
rk_keylad_disable_clk(struct udevice * dev)10386fd7407SLin Jinhan static int rk_keylad_disable_clk(struct udevice *dev)
10486fd7407SLin Jinhan {
10586fd7407SLin Jinhan return rk_keylad_do_enable_clk(dev, 0);
10686fd7407SLin Jinhan }
10786fd7407SLin Jinhan
keylad_read(u32 offset)108ba1dc8d4STroy Lin static inline u32 keylad_read(u32 offset)
109ba1dc8d4STroy Lin {
110ba1dc8d4STroy Lin return readl(keylad_base + offset);
111ba1dc8d4STroy Lin }
112ba1dc8d4STroy Lin
keylad_write(u32 offset,u32 val)113ba1dc8d4STroy Lin static inline void keylad_write(u32 offset, u32 val)
114ba1dc8d4STroy Lin {
115ba1dc8d4STroy Lin writel(val, keylad_base + offset);
116ba1dc8d4STroy Lin }
117ba1dc8d4STroy Lin
rk_get_fwkey_param(u32 keyid,u32 * offset,u32 * max_len)118ba1dc8d4STroy Lin static int rk_get_fwkey_param(u32 keyid, u32 *offset, u32 *max_len)
119ba1dc8d4STroy Lin {
120ba1dc8d4STroy Lin switch (keyid) {
121ba1dc8d4STroy Lin case RK_FW_KEY0:
122190b3637SLin Jinhan *offset = OTP_FW_ENC_KEY_ADDR;
123190b3637SLin Jinhan *max_len = OTP_FW_ENC_KEY_SIZE;
124ba1dc8d4STroy Lin break;
125ba1dc8d4STroy Lin default:
126ba1dc8d4STroy Lin return -EINVAL;
127ba1dc8d4STroy Lin }
128ba1dc8d4STroy Lin
129ba1dc8d4STroy Lin return 0;
130ba1dc8d4STroy Lin }
131ba1dc8d4STroy Lin
rk_keylad_send_key(u32 key_reg,u32 n_words,ulong dst_addr)132ba1dc8d4STroy Lin static int rk_keylad_send_key(u32 key_reg, u32 n_words, ulong dst_addr)
133ba1dc8d4STroy Lin {
134ba1dc8d4STroy Lin int ret = 0;
135ba1dc8d4STroy Lin
136ba1dc8d4STroy Lin /* key_reg of 32bits can be 0-31 */
137ba1dc8d4STroy Lin if ((key_reg + n_words) > KEYLAD_KEY_REG_NUM)
138ba1dc8d4STroy Lin return -EINVAL;
139ba1dc8d4STroy Lin
140ba1dc8d4STroy Lin for (u32 i = 0; i < n_words; i++) {
141ba1dc8d4STroy Lin /* set destination addr */
142ba1dc8d4STroy Lin keylad_write(KEYLAD_APB_PADDR,
143ba1dc8d4STroy Lin (dst_addr & 0xffffffff) + (i * KEYLAD_KEY_REG_SIZE_BYTES));
144ba1dc8d4STroy Lin /* select which word of key table to be sent */
145ba1dc8d4STroy Lin keylad_write(KEYLAD_APB_PWDATA, key_reg + i);
146ba1dc8d4STroy Lin
147ba1dc8d4STroy Lin keylad_write(KEYLAD_APB_CMD, VALUE_APB_CMD_ENABLE);
148ba1dc8d4STroy Lin KEYLAD_POLL_TIMEOUT((keylad_read(KEYLAD_APB_CMD) & REG_APB_CMD_EN) ==
149ba1dc8d4STroy Lin VALUE_APB_CMD_ENABLE, RK_KEYLAD_TIME_OUT, ret);
150ba1dc8d4STroy Lin }
151ba1dc8d4STroy Lin
152ba1dc8d4STroy Lin return ret;
153ba1dc8d4STroy Lin }
154ba1dc8d4STroy Lin
rk_otp_keylad_read_init(void)15593607a96SLin Jinhan static int rk_otp_keylad_read_init(void)
15693607a96SLin Jinhan {
15793607a96SLin Jinhan struct udevice *dev;
15893607a96SLin Jinhan
15993607a96SLin Jinhan dev = misc_otp_get_device(OTP_S);
16093607a96SLin Jinhan if (!dev)
16193607a96SLin Jinhan return -ENODEV;
16293607a96SLin Jinhan
163*59a8202dSXuhui Lin misc_otp_ioctl(dev, IOCTL_REQ_START, NULL);
16493607a96SLin Jinhan misc_otp_ioctl(dev, IOCTL_REQ_KEYLAD_INIT, NULL);
16593607a96SLin Jinhan
16693607a96SLin Jinhan return 0;
16793607a96SLin Jinhan }
16893607a96SLin Jinhan
rk_otp_keylad_read_deinit(void)16993607a96SLin Jinhan static int rk_otp_keylad_read_deinit(void)
17093607a96SLin Jinhan {
17193607a96SLin Jinhan struct udevice *dev;
17293607a96SLin Jinhan
17393607a96SLin Jinhan dev = misc_otp_get_device(OTP_S);
17493607a96SLin Jinhan if (!dev)
17593607a96SLin Jinhan return -ENODEV;
17693607a96SLin Jinhan
17793607a96SLin Jinhan misc_otp_ioctl(dev, IOCTL_REQ_KEYLAD_DEINIT, NULL);
178*59a8202dSXuhui Lin misc_otp_ioctl(dev, IOCTL_REQ_STOP, NULL);
17993607a96SLin Jinhan
18093607a96SLin Jinhan return 0;
18193607a96SLin Jinhan }
18293607a96SLin Jinhan
rk_keylad_read_otp_key(u32 otp_offset,u32 keylad_area,u32 keylen)183ba1dc8d4STroy Lin static int rk_keylad_read_otp_key(u32 otp_offset, u32 keylad_area, u32 keylen)
184ba1dc8d4STroy Lin {
185ba1dc8d4STroy Lin int ret = 0;
186ba1dc8d4STroy Lin u32 val = 0;
187ba1dc8d4STroy Lin u32 nbytes = keylen;
188ba1dc8d4STroy Lin
189ba1dc8d4STroy Lin /* keylad_area of 256bits can be 0-1 */
190ba1dc8d4STroy Lin if (keylad_area >= KEYLAD_AREA_NUM)
191ba1dc8d4STroy Lin return -EINVAL;
192ba1dc8d4STroy Lin
19393607a96SLin Jinhan ret = rk_otp_keylad_read_init();
19493607a96SLin Jinhan if (ret) {
19593607a96SLin Jinhan printf("Keyladder read otp key init err: 0x%x.", ret);
19693607a96SLin Jinhan return ret;
19793607a96SLin Jinhan }
198ba1dc8d4STroy Lin
199ba1dc8d4STroy Lin /* src use byte address, dst use keytable block address */
200ba1dc8d4STroy Lin val = KL_OTP_KEY_REQ_SRC_ADDR(otp_offset / 2) |
201ba1dc8d4STroy Lin KL_OTP_KEY_REQ_DST_ADDR(keylad_area) |
202ba1dc8d4STroy Lin KL_OTP_KEY_REQ_BYTE_SWAP |
203ba1dc8d4STroy Lin KL_OTP_KEY_REQ_EN;
204ba1dc8d4STroy Lin
205ba1dc8d4STroy Lin keylad_write(KEYLAD_KEY_SEL, VALUE_KEY_SEL_OUTER_KEY);
206ba1dc8d4STroy Lin
207ba1dc8d4STroy Lin keylad_write(KEY_LADDER_KEY_LEN, KL_KEY_LEN(nbytes));
208ba1dc8d4STroy Lin
209ba1dc8d4STroy Lin keylad_write(KEY_LADDER_OTP_KEY_REQ, val);
210ba1dc8d4STroy Lin
211ba1dc8d4STroy Lin KEYLAD_POLL_TIMEOUT(keylad_read(KEY_LADDER_OTP_KEY_REQ) & KL_OTP_KEY_REQ_EN,
212ba1dc8d4STroy Lin RK_KEYLAD_TIME_OUT, ret);
213ba1dc8d4STroy Lin
214ba1dc8d4STroy Lin val = keylad_read(KEY_LADDER_OTP_KEY_REQ);
215ba1dc8d4STroy Lin if (val & KL_OTP_KEY_ECC_ST) {
216ba1dc8d4STroy Lin printf("KEYLAD transfer OTP key ECC check error!");
217ba1dc8d4STroy Lin ret = -EIO;
218ba1dc8d4STroy Lin }
219ba1dc8d4STroy Lin
22093607a96SLin Jinhan rk_otp_keylad_read_deinit();
221ba1dc8d4STroy Lin
222ba1dc8d4STroy Lin return ret;
223ba1dc8d4STroy Lin }
224ba1dc8d4STroy Lin
rockchip_keylad_transfer_fwkey(struct udevice * dev,ulong dst,u32 fw_keyid,u32 keylen)225ba1dc8d4STroy Lin static int rockchip_keylad_transfer_fwkey(struct udevice *dev, ulong dst,
226ba1dc8d4STroy Lin u32 fw_keyid, u32 keylen)
227ba1dc8d4STroy Lin {
228ba1dc8d4STroy Lin int res = 0;
229ba1dc8d4STroy Lin u32 fw_key_offset;
230ba1dc8d4STroy Lin u32 max_key_len = 0;
231ba1dc8d4STroy Lin
232ba1dc8d4STroy Lin if (keylen % 4) {
233ba1dc8d4STroy Lin printf("key_len(%u) must be multiple of 4 error.", keylen);
234ba1dc8d4STroy Lin return -EINVAL;
235ba1dc8d4STroy Lin }
236ba1dc8d4STroy Lin
237ba1dc8d4STroy Lin res = rk_get_fwkey_param(fw_keyid, &fw_key_offset, &max_key_len);
238ba1dc8d4STroy Lin if (res)
239ba1dc8d4STroy Lin return res;
240ba1dc8d4STroy Lin
241ba1dc8d4STroy Lin if (keylen > max_key_len) {
242ba1dc8d4STroy Lin printf("key_len(%u) > %u error.", keylen, max_key_len);
243ba1dc8d4STroy Lin return -EINVAL;
244ba1dc8d4STroy Lin }
245ba1dc8d4STroy Lin
24693607a96SLin Jinhan rk_keylad_enable_clk(dev);
24793607a96SLin Jinhan
248ba1dc8d4STroy Lin res = rk_keylad_read_otp_key(fw_key_offset, 0, keylen);
249ba1dc8d4STroy Lin if (res) {
250ba1dc8d4STroy Lin printf("Keyladder read otp key err: 0x%x.", res);
251ba1dc8d4STroy Lin return res;
252ba1dc8d4STroy Lin }
253ba1dc8d4STroy Lin
254ba1dc8d4STroy Lin res = rk_keylad_send_key(0, keylen / 4, dst);
25586fd7407SLin Jinhan
25686fd7407SLin Jinhan rk_keylad_disable_clk(dev);
25786fd7407SLin Jinhan
258ba1dc8d4STroy Lin if (res) {
259ba1dc8d4STroy Lin printf("Keyladder transfer key err: 0x%x.", res);
26086fd7407SLin Jinhan goto exit;
261ba1dc8d4STroy Lin }
262ba1dc8d4STroy Lin
26386fd7407SLin Jinhan exit:
264ba1dc8d4STroy Lin return res;
265ba1dc8d4STroy Lin }
266ba1dc8d4STroy Lin
267ba1dc8d4STroy Lin static const struct dm_keylad_ops rockchip_keylad_ops = {
268ba1dc8d4STroy Lin .transfer_fwkey = rockchip_keylad_transfer_fwkey,
269ba1dc8d4STroy Lin };
270ba1dc8d4STroy Lin
rockchip_keylad_ofdata_to_platdata(struct udevice * dev)271ba1dc8d4STroy Lin static int rockchip_keylad_ofdata_to_platdata(struct udevice *dev)
272ba1dc8d4STroy Lin {
273ba1dc8d4STroy Lin struct rockchip_keylad_priv *priv = dev_get_priv(dev);
27486fd7407SLin Jinhan int len = 0;
27586fd7407SLin Jinhan int ret = 0;
276ba1dc8d4STroy Lin
277ba1dc8d4STroy Lin memset(priv, 0x00, sizeof(*priv));
278ba1dc8d4STroy Lin
279ba1dc8d4STroy Lin priv->reg = (fdt_addr_t)dev_read_addr_ptr(dev);
280ba1dc8d4STroy Lin if (priv->reg == FDT_ADDR_T_NONE)
281ba1dc8d4STroy Lin return -EINVAL;
282ba1dc8d4STroy Lin
283ba1dc8d4STroy Lin keylad_base = priv->reg;
284ba1dc8d4STroy Lin
28586fd7407SLin Jinhan /* if there is no clocks in dts, just skip it */
28686fd7407SLin Jinhan if (!dev_read_prop(dev, "clocks", &len)) {
28786fd7407SLin Jinhan printf("Keylad \"clocks\" property not set.\n");
28886fd7407SLin Jinhan return 0;
28986fd7407SLin Jinhan }
29086fd7407SLin Jinhan
29186fd7407SLin Jinhan priv->clocks = malloc(len);
29286fd7407SLin Jinhan if (!priv->clocks)
29386fd7407SLin Jinhan return -ENOMEM;
29486fd7407SLin Jinhan
29586fd7407SLin Jinhan priv->nclocks = len / (2 * sizeof(u32));
29686fd7407SLin Jinhan if (dev_read_u32_array(dev, "clocks", (u32 *)priv->clocks,
29786fd7407SLin Jinhan priv->nclocks)) {
29886fd7407SLin Jinhan printf("Keylad can't read \"clocks\" property.\n");
29986fd7407SLin Jinhan ret = -EINVAL;
30086fd7407SLin Jinhan goto exit;
30186fd7407SLin Jinhan }
30286fd7407SLin Jinhan
30386fd7407SLin Jinhan return 0;
30486fd7407SLin Jinhan exit:
30586fd7407SLin Jinhan if (priv->clocks)
30686fd7407SLin Jinhan free(priv->clocks);
30786fd7407SLin Jinhan
30886fd7407SLin Jinhan return ret;
30986fd7407SLin Jinhan }
31086fd7407SLin Jinhan
rockchip_keylad_probe(struct udevice * dev)31186fd7407SLin Jinhan static int rockchip_keylad_probe(struct udevice *dev)
31286fd7407SLin Jinhan {
31386fd7407SLin Jinhan rk_keylad_disable_clk(dev);
31486fd7407SLin Jinhan
315ba1dc8d4STroy Lin return 0;
316ba1dc8d4STroy Lin }
317ba1dc8d4STroy Lin
318ba1dc8d4STroy Lin static const struct udevice_id rockchip_keylad_ids[] = {
319ba1dc8d4STroy Lin {
320ba1dc8d4STroy Lin .compatible = "rockchip,keylad",
321ba1dc8d4STroy Lin },
322ba1dc8d4STroy Lin };
323ba1dc8d4STroy Lin
324ba1dc8d4STroy Lin U_BOOT_DRIVER(rockchip_keylad) = {
325ba1dc8d4STroy Lin .name = "rockchip_keylad",
326ba1dc8d4STroy Lin .id = UCLASS_KEYLAD,
327ba1dc8d4STroy Lin .of_match = rockchip_keylad_ids,
328ba1dc8d4STroy Lin .ops = &rockchip_keylad_ops,
32986fd7407SLin Jinhan .probe = rockchip_keylad_probe,
330ba1dc8d4STroy Lin .ofdata_to_platdata = rockchip_keylad_ofdata_to_platdata,
331ba1dc8d4STroy Lin .priv_auto_alloc_size = sizeof(struct rockchip_keylad_priv),
332ba1dc8d4STroy Lin };
333