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