xref: /linux/drivers/firmware/samsung/exynos-acpm-pmic.c (revision 297d9111e9fcf47dd1dcc6f79bba915f35378d01)
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