drivers: introduce Meson Secure Monitor driver
[pandora-u-boot.git] / drivers / sm / meson-sm.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2023 SberDevices, Inc.
4  *
5  * Author: Alexey Romanov <avromanov@salutedevices.com>
6  */
7
8 #include <common.h>
9 #include <dm.h>
10 #include <regmap.h>
11 #include <sm.h>
12 #include <sm-uclass.h>
13 #include <stdlib.h>
14 #include <syscon.h>
15 #include <asm/ptrace.h>
16 #include <asm/system.h>
17 #include <meson/sm.h>
18 #include <linux/bitfield.h>
19 #include <linux/err.h>
20 #include <linux/sizes.h>
21
22 struct meson_sm_cmd {
23         u32 smc_id;
24 };
25
26 #define SET_CMD(index, id)      \
27         [index] = {             \
28                 .smc_id = (id), \
29         }
30
31 struct meson_sm_data {
32         u32 cmd_get_shmem_in;
33         u32 cmd_get_shmem_out;
34         unsigned int shmem_size;
35         struct meson_sm_cmd cmd[];
36 };
37
38 struct meson_sm_priv {
39         void *sm_shmem_in;
40         void *sm_shmem_out;
41         const struct meson_sm_data *data;
42 };
43
44 static unsigned long __meson_sm_call(u32 cmd, const struct pt_regs *args)
45 {
46         struct pt_regs r = *args;
47
48         r.regs[0] = cmd;
49         smc_call(&r);
50
51         return r.regs[0];
52 };
53
54 static u32 meson_sm_get_cmd(const struct meson_sm_data *data,
55                             u32 cmd_index)
56 {
57         struct meson_sm_cmd cmd;
58
59         if (cmd_index >= MESON_SMC_CMD_COUNT)
60                 return 0;
61
62         cmd = data->cmd[cmd_index];
63         return cmd.smc_id;
64 }
65
66 static int meson_sm_call(struct udevice *dev, u32 cmd_index, s32 *retval,
67                          struct pt_regs *args)
68 {
69         struct meson_sm_priv *priv = dev_get_priv(dev);
70         u32 cmd, ret;
71
72         cmd = meson_sm_get_cmd(priv->data, cmd_index);
73         if (!cmd)
74                 return -ENOENT;
75
76         ret = __meson_sm_call(cmd, args);
77         if (retval)
78                 *retval = ret;
79
80         return 0;
81 }
82
83 static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size,
84                               u32 cmd_index, struct pt_regs *args)
85 {
86         struct meson_sm_priv *priv = dev_get_priv(dev);
87         s32 nbytes;
88         int ret;
89
90         if (!buffer || size > priv->data->shmem_size)
91                 return -EINVAL;
92
93         ret = meson_sm_call(dev, cmd_index, &nbytes, args);
94         if (ret)
95                 return ret;
96
97         if (nbytes < 0 || nbytes > size)
98                 return -ENOBUFS;
99
100         /* In some cases (for example GET_CHIP_ID command),
101          * SMC doesn't return the number of bytes read, even
102          * though the bytes were actually read into sm_shmem_out.
103          * So this check is needed.
104          */
105         ret = nbytes;
106         if (!nbytes)
107                 nbytes = size;
108
109         memcpy(buffer, priv->sm_shmem_out, nbytes);
110
111         return ret;
112 }
113
114 static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size,
115                                u32 cmd_index, struct pt_regs *args)
116 {
117         struct meson_sm_priv *priv = dev_get_priv(dev);
118         s32 nbytes;
119         int ret;
120
121         if (!buffer || size > priv->data->shmem_size)
122                 return -EINVAL;
123
124         memcpy(priv->sm_shmem_in, buffer, size);
125
126         ret = meson_sm_call(dev, cmd_index, &nbytes, args);
127         if (ret)
128                 return ret;
129
130         if (nbytes <= 0 || nbytes > size)
131                 return -EIO;
132
133         return nbytes;
134 }
135
136 static int meson_sm_probe(struct udevice *dev)
137 {
138         struct meson_sm_priv *priv = dev_get_priv(dev);
139         struct pt_regs regs = { 0 };
140
141         priv->data = (struct meson_sm_data *)dev_get_driver_data(dev);
142         if (!priv->data)
143                 return -EINVAL;
144
145         priv->sm_shmem_in =
146                 (void *)__meson_sm_call(priv->data->cmd_get_shmem_in, &regs);
147
148         if (!priv->sm_shmem_in)
149                 return -ENOMEM;
150
151         priv->sm_shmem_out =
152                 (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, &regs);
153
154         if (!priv->sm_shmem_out)
155                 return -ENOMEM;
156
157         pr_debug("meson sm driver probed\n"
158                  "shmem_in addr: 0x%p, shmem_out addr: 0x%p\n",
159                  priv->sm_shmem_in,
160                  priv->sm_shmem_out);
161
162         return 0;
163 }
164
165 static const struct meson_sm_data meson_sm_gxbb_data = {
166         .cmd_get_shmem_in  = 0x82000020,
167         .cmd_get_shmem_out = 0x82000021,
168         .shmem_size = SZ_4K,
169         .cmd = {
170                 SET_CMD(MESON_SMC_CMD_EFUSE_READ,  0x82000030),
171                 SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031),
172                 SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044),
173                 SET_CMD(MESON_SMC_CMD_PWRDM_SET,   0x82000093),
174         },
175 };
176
177 static const struct udevice_id meson_sm_ids[] = {
178         {
179                 .compatible = "amlogic,meson-gxbb-sm",
180                 .data = (ulong)&meson_sm_gxbb_data,
181         },
182         { }
183 };
184
185 static const struct sm_ops sm_ops = {
186         .sm_call = meson_sm_call,
187         .sm_call_read = meson_sm_call_read,
188         .sm_call_write = meson_sm_call_write,
189 };
190
191 U_BOOT_DRIVER(meson_sm) = {
192         .name = "meson_sm",
193         .id = UCLASS_SM,
194         .of_match = meson_sm_ids,
195         .probe = meson_sm_probe,
196         .priv_auto = sizeof(struct meson_sm_priv),
197         .ops = &sm_ops,
198 };