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