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
acpm_pmic_set_bulk(u32 data,unsigned int i)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
acpm_pmic_get_bulk(u32 data,unsigned int i)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
acpm_pmic_set_xfer(struct acpm_xfer * xfer,u32 * cmd,size_t cmdlen,unsigned int acpm_chan_id)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
acpm_pmic_init_read_cmd(u32 cmd[4],u8 type,u8 reg,u8 chan)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
acpm_pmic_read_reg(const struct acpm_handle * handle,unsigned int acpm_chan_id,u8 type,u8 reg,u8 chan,u8 * buf)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
acpm_pmic_init_bulk_read_cmd(u32 cmd[4],u8 type,u8 reg,u8 chan,u8 count)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
acpm_pmic_bulk_read(const struct acpm_handle * handle,unsigned int acpm_chan_id,u8 type,u8 reg,u8 chan,u8 count,u8 * buf)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
acpm_pmic_init_write_cmd(u32 cmd[4],u8 type,u8 reg,u8 chan,u8 value)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
acpm_pmic_write_reg(const struct acpm_handle * handle,unsigned int acpm_chan_id,u8 type,u8 reg,u8 chan,u8 value)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
acpm_pmic_init_bulk_write_cmd(u32 cmd[4],u8 type,u8 reg,u8 chan,u8 count,const u8 * buf)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
acpm_pmic_bulk_write(const struct acpm_handle * handle,unsigned int acpm_chan_id,u8 type,u8 reg,u8 chan,u8 count,const u8 * buf)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
acpm_pmic_init_update_cmd(u32 cmd[4],u8 type,u8 reg,u8 chan,u8 value,u8 mask)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
acpm_pmic_update_reg(const struct acpm_handle * handle,unsigned int acpm_chan_id,u8 type,u8 reg,u8 chan,u8 value,u8 mask)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