Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/samsung/exynos-acpm-pmic.c
29267 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright 2020 Samsung Electronics Co., Ltd.
4
* Copyright 2020 Google LLC.
5
* Copyright 2024 Linaro Ltd.
6
*/
7
#include <linux/array_size.h>
8
#include <linux/bitfield.h>
9
#include <linux/errno.h>
10
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
11
#include <linux/ktime.h>
12
#include <linux/types.h>
13
14
#include "exynos-acpm.h"
15
#include "exynos-acpm-pmic.h"
16
17
#define ACPM_PMIC_CHANNEL GENMASK(15, 12)
18
#define ACPM_PMIC_TYPE GENMASK(11, 8)
19
#define ACPM_PMIC_REG GENMASK(7, 0)
20
21
#define ACPM_PMIC_RETURN GENMASK(31, 24)
22
#define ACPM_PMIC_MASK GENMASK(23, 16)
23
#define ACPM_PMIC_VALUE GENMASK(15, 8)
24
#define ACPM_PMIC_FUNC GENMASK(7, 0)
25
26
#define ACPM_PMIC_BULK_SHIFT 8
27
#define ACPM_PMIC_BULK_MASK GENMASK(7, 0)
28
#define ACPM_PMIC_BULK_MAX_COUNT 8
29
30
enum exynos_acpm_pmic_func {
31
ACPM_PMIC_READ,
32
ACPM_PMIC_WRITE,
33
ACPM_PMIC_UPDATE,
34
ACPM_PMIC_BULK_READ,
35
ACPM_PMIC_BULK_WRITE,
36
};
37
38
static const int acpm_pmic_linux_errmap[] = {
39
[0] = 0, /* ACPM_PMIC_SUCCESS */
40
[1] = -EACCES, /* Read register can't be accessed or issues to access it. */
41
[2] = -EACCES, /* Write register can't be accessed or issues to access it. */
42
};
43
44
static int acpm_pmic_to_linux_err(int err)
45
{
46
if (err >= 0 && err < ARRAY_SIZE(acpm_pmic_linux_errmap))
47
return acpm_pmic_linux_errmap[err];
48
return -EIO;
49
}
50
51
static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i)
52
{
53
return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i);
54
}
55
56
static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)
57
{
58
return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;
59
}
60
61
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
62
unsigned int acpm_chan_id)
63
{
64
xfer->txd = cmd;
65
xfer->rxd = cmd;
66
xfer->txlen = cmdlen;
67
xfer->rxlen = cmdlen;
68
xfer->acpm_chan_id = acpm_chan_id;
69
}
70
71
static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan)
72
{
73
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
74
FIELD_PREP(ACPM_PMIC_REG, reg) |
75
FIELD_PREP(ACPM_PMIC_CHANNEL, chan);
76
cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_READ);
77
cmd[3] = ktime_to_ms(ktime_get());
78
}
79
80
int acpm_pmic_read_reg(const struct acpm_handle *handle,
81
unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
82
u8 *buf)
83
{
84
struct acpm_xfer xfer;
85
u32 cmd[4] = {0};
86
int ret;
87
88
acpm_pmic_init_read_cmd(cmd, type, reg, chan);
89
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
90
91
ret = acpm_do_xfer(handle, &xfer);
92
if (ret)
93
return ret;
94
95
*buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]);
96
97
return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));
98
}
99
100
static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,
101
u8 count)
102
{
103
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
104
FIELD_PREP(ACPM_PMIC_REG, reg) |
105
FIELD_PREP(ACPM_PMIC_CHANNEL, chan);
106
cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_READ) |
107
FIELD_PREP(ACPM_PMIC_VALUE, count);
108
}
109
110
int acpm_pmic_bulk_read(const struct acpm_handle *handle,
111
unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
112
u8 count, u8 *buf)
113
{
114
struct acpm_xfer xfer;
115
u32 cmd[4] = {0};
116
int i, ret;
117
118
if (count > ACPM_PMIC_BULK_MAX_COUNT)
119
return -EINVAL;
120
121
acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);
122
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
123
124
ret = acpm_do_xfer(handle, &xfer);
125
if (ret)
126
return ret;
127
128
ret = acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));
129
if (ret)
130
return ret;
131
132
for (i = 0; i < count; i++) {
133
if (i < 4)
134
buf[i] = acpm_pmic_get_bulk(xfer.rxd[2], i);
135
else
136
buf[i] = acpm_pmic_get_bulk(xfer.rxd[3], i - 4);
137
}
138
139
return 0;
140
}
141
142
static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,
143
u8 value)
144
{
145
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
146
FIELD_PREP(ACPM_PMIC_REG, reg) |
147
FIELD_PREP(ACPM_PMIC_CHANNEL, chan);
148
cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_WRITE) |
149
FIELD_PREP(ACPM_PMIC_VALUE, value);
150
cmd[3] = ktime_to_ms(ktime_get());
151
}
152
153
int acpm_pmic_write_reg(const struct acpm_handle *handle,
154
unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
155
u8 value)
156
{
157
struct acpm_xfer xfer;
158
u32 cmd[4] = {0};
159
int ret;
160
161
acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);
162
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
163
164
ret = acpm_do_xfer(handle, &xfer);
165
if (ret)
166
return ret;
167
168
return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));
169
}
170
171
static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,
172
u8 count, const u8 *buf)
173
{
174
int i;
175
176
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
177
FIELD_PREP(ACPM_PMIC_REG, reg) |
178
FIELD_PREP(ACPM_PMIC_CHANNEL, chan);
179
cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_WRITE) |
180
FIELD_PREP(ACPM_PMIC_VALUE, count);
181
182
for (i = 0; i < count; i++) {
183
if (i < 4)
184
cmd[2] |= acpm_pmic_set_bulk(buf[i], i);
185
else
186
cmd[3] |= acpm_pmic_set_bulk(buf[i], i - 4);
187
}
188
}
189
190
int acpm_pmic_bulk_write(const struct acpm_handle *handle,
191
unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
192
u8 count, const u8 *buf)
193
{
194
struct acpm_xfer xfer;
195
u32 cmd[4] = {0};
196
int ret;
197
198
if (count > ACPM_PMIC_BULK_MAX_COUNT)
199
return -EINVAL;
200
201
acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);
202
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
203
204
ret = acpm_do_xfer(handle, &xfer);
205
if (ret)
206
return ret;
207
208
return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));
209
}
210
211
static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,
212
u8 value, u8 mask)
213
{
214
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
215
FIELD_PREP(ACPM_PMIC_REG, reg) |
216
FIELD_PREP(ACPM_PMIC_CHANNEL, chan);
217
cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_UPDATE) |
218
FIELD_PREP(ACPM_PMIC_VALUE, value) |
219
FIELD_PREP(ACPM_PMIC_MASK, mask);
220
cmd[3] = ktime_to_ms(ktime_get());
221
}
222
223
int acpm_pmic_update_reg(const struct acpm_handle *handle,
224
unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,
225
u8 value, u8 mask)
226
{
227
struct acpm_xfer xfer;
228
u32 cmd[4] = {0};
229
int ret;
230
231
acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);
232
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
233
234
ret = acpm_do_xfer(handle, &xfer);
235
if (ret)
236
return ret;
237
238
return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));
239
}
240
241