blob: 15b3b0e2672f6e714474df8ac3b7fc43faf3948c [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
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
22struct meson_sm_cmd {
23 u32 smc_id;
24};
25
26#define SET_CMD(index, id) \
27 [index] = { \
28 .smc_id = (id), \
29 }
30
31struct 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
38struct meson_sm_priv {
39 void *sm_shmem_in;
40 void *sm_shmem_out;
41 const struct meson_sm_data *data;
42};
43
44static 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
54static 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
66static 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
83static 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
114static 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
136static 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
165static 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
177static 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
185static 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
191U_BOOT_DRIVER(meson_sm) = {
192 .name = "meson_sm",
193 .id = UCLASS_SM,
194 .of_match = meson_sm_ids,
195 .probe = meson_sm_probe,
Dmitry Rokosov23ad9522023-11-01 17:04:57 +0300196 .bind = dm_scan_fdt_dev,
Alexey Romanov0d151312023-09-21 11:13:39 +0300197 .priv_auto = sizeof(struct meson_sm_priv),
198 .ops = &sm_ops,
199};