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