Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com> * * Secure monitor calls. */ #include <dm.h> #include <log.h> #include <regmap.h> #include <sm.h> #include <syscon.h> #include <asm/arch/boot.h> #include <asm/arch/sm.h> #include <asm/cache.h> #include <asm/global_data.h> #include <asm/ptrace.h> #include <linux/bitops.h> #include <linux/compiler_attributes.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/bitfield.h> #include <meson/sm.h> static inline struct udevice *meson_get_sm_device(void) { struct udevice *dev; int err; err = uclass_first_device_err(UCLASS_SM, &dev); if (err) { pr_err("Mesom SM device not found\n"); return ERR_PTR(err); } return dev; } ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size) { struct udevice *dev; struct pt_regs regs = { 0 }; int err; dev = meson_get_sm_device(); if (IS_ERR(dev)) return PTR_ERR(dev); regs.regs[1] = offset; regs.regs[2] = size; err = sm_call_read(dev, buffer, size, MESON_SMC_CMD_EFUSE_READ, ®s); if (err < 0) pr_err("Failed to read efuse memory (%d)\n", err); return err; } ssize_t meson_sm_write_efuse(uintptr_t offset, void *buffer, size_t size) { struct udevice *dev; struct pt_regs regs = { 0 }; int err; dev = meson_get_sm_device(); if (IS_ERR(dev)) return PTR_ERR(dev); regs.regs[1] = offset; regs.regs[2] = size; err = sm_call_write(dev, buffer, size, MESON_SMC_CMD_EFUSE_WRITE, ®s); if (err < 0) pr_err("Failed to write efuse memory (%d)\n", err); return err; } /* * Helps to handle two flavors of cpu_id layouts: * * - in-register view (value read from cpu_id reg, a.k.a. socinfo): * +-----------+------------+------------+------------+ * | family_id | package_id | chip_rev | layout_rev | * +-----------+------------+------------+------------+ * | 31 24 | 23 16 | 15 8 | 7 0 | * +-----------+------------+------------+------------+ * * - in-efuse view (value, residing inside efuse/shmem data usually for * chip_id v2. Chip_id v1 does not contain cpu_id value inside efuse * data (i.e. in chip_id_efuse)): * +-----------+------------+------------+------------+ * | family_id | chip_rev | package_id | layout_rev | * +-----------+------------+------------+------------+ * | 31 24 | 23 16 | 15 8 | 7 0 | * +-----------+------------+------------+------------+ */ enum { /* In-register view of cpu_id */ CPU_ID_REG_MAJOR, /* 31-24 bits */ CPU_ID_REG_PACK, /* 23-16 bits */ CPU_ID_REG_MINOR, /* 15-8 bits */ CPU_ID_REG_MISC, /* 7-0 bits */ /* In-efuse view of cpu_id */ CPU_ID_MAJOR = CPU_ID_REG_MAJOR, CPU_ID_PACK = CPU_ID_REG_MINOR, CPU_ID_MINOR = CPU_ID_REG_PACK, CPU_ID_MISC = CPU_ID_REG_MISC, }; /* * This is a beginning chunk of the whole efuse storage area, containing * data related to chip_id only */ struct chip_id_efuse { u32 version; u8 raw[MESON_CHIP_ID_SZ]; /* payload */ } __packed; static void meson_sm_serial_reverse(u8 serial[SM_SERIAL_SIZE]) { for (int i = 0; i < SM_SERIAL_SIZE / 2; i++) { int k = SM_SERIAL_SIZE - 1 - i; swap(serial[i], serial[k]); } } int meson_sm_get_chip_id(struct meson_sm_chip_id *chip_id) { struct udevice *dev; union meson_cpu_id socinfo; struct pt_regs regs = { 0 }; struct chip_id_efuse chip_id_efuse; int err; dev = meson_get_sm_device(); if (IS_ERR(dev)) return PTR_ERR(dev); /* * Request v2. If not supported by secure monitor, then v1 should be * returned. */ regs.regs[1] = 2; err = sm_call_read(dev, &chip_id_efuse, sizeof(chip_id_efuse), MESON_SMC_CMD_CHIP_ID_GET, ®s); if (err < 0) { pr_err("Failed to read chip_id (%d)\n", err); return err; } if (chip_id_efuse.version == 2) { memcpy((u8 *)chip_id, chip_id_efuse.raw, sizeof(struct meson_sm_chip_id)); return 0; } /* * Legacy chip_id (v1) read out, transform data * to expected order format (little-endian) */ memcpy(chip_id->serial, chip_id_efuse.raw, sizeof(chip_id->serial)); meson_sm_serial_reverse(chip_id->serial); socinfo.val = meson_get_socinfo(); if (!socinfo.val) return -ENODEV; chip_id->cpu_id = (union meson_cpu_id){ .raw[CPU_ID_MAJOR] = socinfo.raw[CPU_ID_REG_MAJOR], .raw[CPU_ID_PACK] = socinfo.raw[CPU_ID_REG_PACK], .raw[CPU_ID_MINOR] = socinfo.raw[CPU_ID_REG_MINOR], .raw[CPU_ID_MISC] = socinfo.raw[CPU_ID_REG_MISC], }; return 0; } int meson_sm_get_serial(void *buffer, size_t size) { struct meson_sm_chip_id chip_id; int ret; if (size < SM_SERIAL_SIZE) return -EINVAL; ret = meson_sm_get_chip_id(&chip_id); if (ret) return ret; /* * The order of serial inside chip_id and serial which function must * return does not match: stick here to big-endian for backward * compatibility. */ meson_sm_serial_reverse(chip_id.serial); memcpy(buffer, chip_id.serial, sizeof(chip_id.serial)); return ret; } #define AO_SEC_SD_CFG15 0xfc #define REBOOT_REASON_MASK GENMASK(15, 12) int meson_sm_get_reboot_reason(void) { struct regmap *regmap; int nodeoffset; ofnode node; unsigned int reason; /* find the offset of compatible node */ nodeoffset = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "amlogic,meson-gx-ao-secure"); if (nodeoffset < 0) { printf("%s: failed to get amlogic,meson-gx-ao-secure\n", __func__); return -ENODEV; } /* get regmap from the syscon node */ node = offset_to_ofnode(nodeoffset); regmap = syscon_node_to_regmap(node); if (IS_ERR(regmap)) { printf("%s: failed to get regmap\n", __func__); return -EINVAL; } regmap_read(regmap, AO_SEC_SD_CFG15, &reason); /* The SMC call is not used, we directly use AO_SEC_SD_CFG15 */ return FIELD_GET(REBOOT_REASON_MASK, reason); } int meson_sm_pwrdm_set(size_t index, int cmd) { struct udevice *dev; struct pt_regs regs = { 0 }; int err; dev = meson_get_sm_device(); if (IS_ERR(dev)) return PTR_ERR(dev); regs.regs[1] = index; regs.regs[2] = cmd; err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, ®s); if (err) pr_err("Failed to %s power domain ind=%zu (%d)\n", cmd == PWRDM_ON ? "enable" : "disable", index, err); return err; } |