1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2023 SberDevices, Inc.
5 * Author: Alexey Romanov <avromanov@salutedevices.com>
12 #include <sm-uclass.h>
15 #include <asm/ptrace.h>
16 #include <asm/system.h>
18 #include <linux/bitfield.h>
19 #include <linux/err.h>
20 #include <linux/sizes.h>
26 #define SET_CMD(index, id) \
31 struct meson_sm_data {
33 u32 cmd_get_shmem_out;
34 unsigned int shmem_size;
35 struct meson_sm_cmd cmd[];
38 struct meson_sm_priv {
41 const struct meson_sm_data *data;
44 static unsigned long __meson_sm_call(u32 cmd, const struct pt_regs *args)
46 struct pt_regs r = *args;
54 static u32 meson_sm_get_cmd(const struct meson_sm_data *data,
57 struct meson_sm_cmd cmd;
59 if (cmd_index >= MESON_SMC_CMD_COUNT)
62 cmd = data->cmd[cmd_index];
66 static int meson_sm_call(struct udevice *dev, u32 cmd_index, s32 *retval,
69 struct meson_sm_priv *priv = dev_get_priv(dev);
72 cmd = meson_sm_get_cmd(priv->data, cmd_index);
76 ret = __meson_sm_call(cmd, args);
83 static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size,
84 u32 cmd_index, struct pt_regs *args)
86 struct meson_sm_priv *priv = dev_get_priv(dev);
90 if (!buffer || size > priv->data->shmem_size)
93 ret = meson_sm_call(dev, cmd_index, &nbytes, args);
97 if (nbytes < 0 || nbytes > size)
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.
109 memcpy(buffer, priv->sm_shmem_out, nbytes);
114 static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size,
115 u32 cmd_index, struct pt_regs *args)
117 struct meson_sm_priv *priv = dev_get_priv(dev);
121 if (!buffer || size > priv->data->shmem_size)
124 memcpy(priv->sm_shmem_in, buffer, size);
126 ret = meson_sm_call(dev, cmd_index, &nbytes, args);
130 if (nbytes <= 0 || nbytes > size)
136 static int meson_sm_probe(struct udevice *dev)
138 struct meson_sm_priv *priv = dev_get_priv(dev);
139 struct pt_regs regs = { 0 };
141 priv->data = (struct meson_sm_data *)dev_get_driver_data(dev);
146 (void *)__meson_sm_call(priv->data->cmd_get_shmem_in, ®s);
148 if (!priv->sm_shmem_in)
152 (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, ®s);
154 if (!priv->sm_shmem_out)
157 pr_debug("meson sm driver probed\n"
158 "shmem_in addr: 0x%p, shmem_out addr: 0x%p\n",
165 static const struct meson_sm_data meson_sm_gxbb_data = {
166 .cmd_get_shmem_in = 0x82000020,
167 .cmd_get_shmem_out = 0x82000021,
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),
177 static const struct udevice_id meson_sm_ids[] = {
179 .compatible = "amlogic,meson-gxbb-sm",
180 .data = (ulong)&meson_sm_gxbb_data,
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,
191 U_BOOT_DRIVER(meson_sm) = {
194 .of_match = meson_sm_ids,
195 .probe = meson_sm_probe,
196 .priv_auto = sizeof(struct meson_sm_priv),