File Index
Files and directories
Sign in
FILE

exynos-acpm-pmic.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/samsung/exynos-acpm-pmic.c C
  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
 30enum 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
 38static 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
 44static 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
 51static 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
 56static 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
 61static 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
 71static 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
 80int 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
100static 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
110int 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
142static 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
153int 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
171static 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
190int 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
211static 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
223int 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}
~/ Read-only file preview.