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 <dm.h>
10#include <log.h>
11#include <regmap.h>
12#include <sm.h>
13#include <syscon.h>
14#include <asm/arch/sm.h>
15#include <asm/cache.h>
16#include <asm/global_data.h>
17#include <asm/ptrace.h>
18#include <linux/bitops.h>
19#include <linux/err.h>
20#include <linux/kernel.h>
21#include <linux/bitfield.h>
22#include <meson/sm.h>
23
24static inline struct udevice *meson_get_sm_device(void)
25{
26	struct udevice *dev;
27	int err;
28
29	err = uclass_first_device_err(UCLASS_SM, &dev);
30	if (err) {
31		pr_err("Mesom SM device not found\n");
32		return ERR_PTR(err);
33	}
34
35	return dev;
36}
37
38ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size)
39{
40	struct udevice *dev;
41	struct pt_regs regs = { 0 };
42	int err;
43
44	dev = meson_get_sm_device();
45	if (IS_ERR(dev))
46		return PTR_ERR(dev);
47
48	regs.regs[1] = offset;
49	regs.regs[2] = size;
50
51	err = sm_call_read(dev, buffer, size,
52			   MESON_SMC_CMD_EFUSE_READ, &regs);
53	if (err < 0)
54		pr_err("Failed to read efuse memory (%d)\n", err);
55
56	return err;
57}
58
59ssize_t meson_sm_write_efuse(uintptr_t offset, void *buffer, size_t size)
60{
61	struct udevice *dev;
62	struct pt_regs regs = { 0 };
63	int err;
64
65	dev = meson_get_sm_device();
66	if (IS_ERR(dev))
67		return PTR_ERR(dev);
68
69	regs.regs[1] = offset;
70	regs.regs[2] = size;
71
72	err = sm_call_write(dev, buffer, size,
73			    MESON_SMC_CMD_EFUSE_WRITE, &regs);
74	if (err < 0)
75		pr_err("Failed to write efuse memory (%d)\n", err);
76
77	return err;
78}
79
80#define SM_CHIP_ID_LENGTH	119
81#define SM_CHIP_ID_OFFSET	4
82#define SM_CHIP_ID_SIZE		12
83
84int meson_sm_get_serial(void *buffer, size_t size)
85{
86	struct udevice *dev;
87	struct pt_regs regs = { 0 };
88	u8 id_buffer[SM_CHIP_ID_LENGTH];
89	int err;
90
91	dev = meson_get_sm_device();
92	if (IS_ERR(dev))
93		return PTR_ERR(dev);
94
95	err = sm_call_read(dev, id_buffer, SM_CHIP_ID_LENGTH,
96			   MESON_SMC_CMD_CHIP_ID_GET, &regs);
97	if (err < 0)
98		pr_err("Failed to read serial number (%d)\n", err);
99
100	memcpy(buffer, id_buffer + SM_CHIP_ID_OFFSET, size);
101
102	return 0;
103}
104
105#define AO_SEC_SD_CFG15		0xfc
106#define REBOOT_REASON_MASK	GENMASK(15, 12)
107
108int meson_sm_get_reboot_reason(void)
109{
110	struct regmap *regmap;
111	int nodeoffset;
112	ofnode node;
113	unsigned int reason;
114
115	/* find the offset of compatible node */
116	nodeoffset = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
117						   "amlogic,meson-gx-ao-secure");
118	if (nodeoffset < 0) {
119		printf("%s: failed to get amlogic,meson-gx-ao-secure\n",
120		       __func__);
121		return -ENODEV;
122	}
123
124	/* get regmap from the syscon node */
125	node = offset_to_ofnode(nodeoffset);
126	regmap = syscon_node_to_regmap(node);
127	if (IS_ERR(regmap)) {
128		printf("%s: failed to get regmap\n", __func__);
129		return -EINVAL;
130	}
131
132	regmap_read(regmap, AO_SEC_SD_CFG15, &reason);
133
134	/* The SMC call is not used, we directly use AO_SEC_SD_CFG15 */
135	return FIELD_GET(REBOOT_REASON_MASK, reason);
136}
137
138int meson_sm_pwrdm_set(size_t index, int cmd)
139{
140	struct udevice *dev;
141	struct pt_regs regs = { 0 };
142	int err;
143
144	dev = meson_get_sm_device();
145	if (IS_ERR(dev))
146		return PTR_ERR(dev);
147
148	regs.regs[1] = index;
149	regs.regs[2] = cmd;
150
151	err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, &regs);
152	if (err)
153		pr_err("Failed to %s power domain ind=%zu (%d)\n", cmd == PWRDM_ON ?
154				"enable" : "disable", index, err);
155
156	return err;
157}
158