Fix typo in macros, "FIRMEWARE" -> "FIRMWARE"
[oweals/u-boot.git] / arch / arm / mach-meson / sm.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com>
4  *
5  * Secure monitor calls.
6  */
7
8 #include <common.h>
9 #include <asm/arch/sm.h>
10 #include <linux/kernel.h>
11 #include <dm.h>
12 #include <linux/bitfield.h>
13 #include <regmap.h>
14 #include <syscon.h>
15
16 #define FN_GET_SHARE_MEM_INPUT_BASE     0x82000020
17 #define FN_GET_SHARE_MEM_OUTPUT_BASE    0x82000021
18 #define FN_EFUSE_READ                   0x82000030
19 #define FN_EFUSE_WRITE                  0x82000031
20 #define FN_CHIP_ID                      0x82000044
21
22 static void *shmem_input;
23 static void *shmem_output;
24
25 static void meson_init_shmem(void)
26 {
27         struct pt_regs regs;
28
29         if (shmem_input && shmem_output)
30                 return;
31
32         regs.regs[0] = FN_GET_SHARE_MEM_INPUT_BASE;
33         smc_call(&regs);
34         shmem_input = (void *)regs.regs[0];
35
36         regs.regs[0] = FN_GET_SHARE_MEM_OUTPUT_BASE;
37         smc_call(&regs);
38         shmem_output = (void *)regs.regs[0];
39
40         debug("Secure Monitor shmem: 0x%p 0x%p\n", shmem_input, shmem_output);
41 }
42
43 ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size)
44 {
45         struct pt_regs regs;
46
47         meson_init_shmem();
48
49         regs.regs[0] = FN_EFUSE_READ;
50         regs.regs[1] = offset;
51         regs.regs[2] = size;
52
53         smc_call(&regs);
54
55         if (regs.regs[0] == 0)
56                 return -1;
57
58         memcpy(buffer, shmem_output, min(size, regs.regs[0]));
59
60         return regs.regs[0];
61 }
62
63 #define SM_CHIP_ID_LENGTH       119
64 #define SM_CHIP_ID_OFFSET       4
65 #define SM_CHIP_ID_SIZE         12
66
67 int meson_sm_get_serial(void *buffer, size_t size)
68 {
69         struct pt_regs regs;
70
71         meson_init_shmem();
72
73         regs.regs[0] = FN_CHIP_ID;
74         regs.regs[1] = 0;
75         regs.regs[2] = 0;
76
77         smc_call(&regs);
78
79         memcpy(buffer, shmem_output + SM_CHIP_ID_OFFSET,
80                min_t(size_t, size, SM_CHIP_ID_SIZE));
81
82         return 0;
83 }
84
85 #define AO_SEC_SD_CFG15         0xfc
86 #define REBOOT_REASON_MASK      GENMASK(15, 12)
87
88 int meson_sm_get_reboot_reason(void)
89 {
90         struct regmap *regmap;
91         int nodeoffset;
92         ofnode node;
93         unsigned int reason;
94
95         /* find the offset of compatible node */
96         nodeoffset = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
97                                                    "amlogic,meson-gx-ao-secure");
98         if (nodeoffset < 0) {
99                 printf("%s: failed to get amlogic,meson-gx-ao-secure\n",
100                        __func__);
101                 return -ENODEV;
102         }
103
104         /* get regmap from the syscon node */
105         node = offset_to_ofnode(nodeoffset);
106         regmap = syscon_node_to_regmap(node);
107         if (IS_ERR(regmap)) {
108                 printf("%s: failed to get regmap\n", __func__);
109                 return -EINVAL;
110         }
111
112         regmap_read(regmap, AO_SEC_SD_CFG15, &reason);
113
114         /* The SMC call is not used, we directly use AO_SEC_SD_CFG15 */
115         return FIELD_GET(REBOOT_REASON_MASK, reason);
116 }
117
118 static int do_sm_serial(cmd_tbl_t *cmdtp, int flag, int argc,
119                         char *const argv[])
120 {
121         ulong address;
122         int ret;
123
124         if (argc < 2)
125                 return CMD_RET_USAGE;
126
127         address = simple_strtoul(argv[1], NULL, 0);
128
129         ret = meson_sm_get_serial((void *)address, SM_CHIP_ID_SIZE);
130         if (ret)
131                 return CMD_RET_FAILURE;
132
133         return CMD_RET_SUCCESS;
134 }
135
136 #define MAX_REBOOT_REASONS 14
137
138 static const char *reboot_reasons[MAX_REBOOT_REASONS] = {
139         [REBOOT_REASON_COLD] = "cold_boot",
140         [REBOOT_REASON_NORMAL] = "normal",
141         [REBOOT_REASON_RECOVERY] = "recovery",
142         [REBOOT_REASON_UPDATE] = "update",
143         [REBOOT_REASON_FASTBOOT] = "fastboot",
144         [REBOOT_REASON_SUSPEND_OFF] = "suspend_off",
145         [REBOOT_REASON_HIBERNATE] = "hibernate",
146         [REBOOT_REASON_BOOTLOADER] = "bootloader",
147         [REBOOT_REASON_SHUTDOWN_REBOOT] = "shutdown_reboot",
148         [REBOOT_REASON_RPMBP] = "rpmbp",
149         [REBOOT_REASON_CRASH_DUMP] = "crash_dump",
150         [REBOOT_REASON_KERNEL_PANIC] = "kernel_panic",
151         [REBOOT_REASON_WATCHDOG_REBOOT] = "watchdog_reboot",
152 };
153
154 static int do_sm_reboot_reason(cmd_tbl_t *cmdtp, int flag, int argc,
155                         char *const argv[])
156 {
157         const char *reason_str;
158         char *destarg = NULL;
159         int reason;
160
161         if (argc > 1)
162                 destarg = argv[1];
163
164         reason = meson_sm_get_reboot_reason();
165         if (reason < 0)
166                 return CMD_RET_FAILURE;
167
168         if (reason >= MAX_REBOOT_REASONS ||
169             !reboot_reasons[reason])
170                 reason_str = "unknown";
171         else
172                 reason_str = reboot_reasons[reason];
173
174         if (destarg)
175                 env_set(destarg, reason_str);
176         else
177                 printf("reboot reason: %s (%x)\n", reason_str, reason);
178
179         return CMD_RET_SUCCESS;
180 }
181
182 static cmd_tbl_t cmd_sm_sub[] = {
183         U_BOOT_CMD_MKENT(serial, 2, 1, do_sm_serial, "", ""),
184         U_BOOT_CMD_MKENT(reboot_reason, 1, 1, do_sm_reboot_reason, "", ""),
185 };
186
187 static int do_sm(cmd_tbl_t *cmdtp, int flag, int argc,
188                  char *const argv[])
189 {
190         cmd_tbl_t *c;
191
192         if (argc < 2)
193                 return CMD_RET_USAGE;
194
195         /* Strip off leading 'sm' command argument */
196         argc--;
197         argv++;
198
199         c = find_cmd_tbl(argv[0], &cmd_sm_sub[0], ARRAY_SIZE(cmd_sm_sub));
200
201         if (c)
202                 return c->cmd(cmdtp, flag, argc, argv);
203         else
204                 return CMD_RET_USAGE;
205 }
206
207 U_BOOT_CMD(
208         sm, 5, 0, do_sm,
209         "Secure Monitor Control",
210         "serial <address> - read chip unique id to memory address\n"
211         "sm reboot_reason [name] - get reboot reason and store to to environment"
212 );