xref: /linux/drivers/firmware/samsung/exynos-acpm.c (revision b1bf2ef6259e6aedc3f952ebd9bc056605563b74)
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 
8a88927b5STudor Ambarus #include <linux/bitfield.h>
9a88927b5STudor Ambarus #include <linux/bitmap.h>
10a88927b5STudor Ambarus #include <linux/bits.h>
11a88927b5STudor Ambarus #include <linux/cleanup.h>
12a88927b5STudor Ambarus #include <linux/container_of.h>
13a88927b5STudor Ambarus #include <linux/delay.h>
14a88927b5STudor Ambarus #include <linux/device.h>
15a88927b5STudor Ambarus #include <linux/firmware/samsung/exynos-acpm-protocol.h>
16a88927b5STudor Ambarus #include <linux/io.h>
17a88927b5STudor Ambarus #include <linux/iopoll.h>
18d2098981SAndré Draszik #include <linux/ktime.h>
19a88927b5STudor Ambarus #include <linux/mailbox/exynos-message.h>
20a88927b5STudor Ambarus #include <linux/mailbox_client.h>
21a88927b5STudor Ambarus #include <linux/module.h>
22a88927b5STudor Ambarus #include <linux/mutex.h>
23a88927b5STudor Ambarus #include <linux/math.h>
24a88927b5STudor Ambarus #include <linux/of.h>
25a88927b5STudor Ambarus #include <linux/of_address.h>
26a88927b5STudor Ambarus #include <linux/of_platform.h>
27a88927b5STudor Ambarus #include <linux/platform_device.h>
28a88927b5STudor Ambarus #include <linux/slab.h>
29a88927b5STudor Ambarus #include <linux/types.h>
30a88927b5STudor Ambarus 
31a88927b5STudor Ambarus #include "exynos-acpm.h"
32a88927b5STudor Ambarus #include "exynos-acpm-pmic.h"
33a88927b5STudor Ambarus 
34a88927b5STudor Ambarus #define ACPM_PROTOCOL_SEQNUM		GENMASK(21, 16)
35a88927b5STudor Ambarus 
36d2098981SAndré Draszik #define ACPM_POLL_TIMEOUT_US		(100 * USEC_PER_MSEC)
37a88927b5STudor Ambarus #define ACPM_TX_TIMEOUT_US		500000
38a88927b5STudor Ambarus 
39a88927b5STudor Ambarus #define ACPM_GS101_INITDATA_BASE	0xa000
40a88927b5STudor Ambarus 
41a88927b5STudor Ambarus /**
42a88927b5STudor Ambarus  * struct acpm_shmem - shared memory configuration information.
43a88927b5STudor Ambarus  * @reserved:	unused fields.
44a88927b5STudor Ambarus  * @chans:	offset to array of struct acpm_chan_shmem.
45a88927b5STudor Ambarus  * @reserved1:	unused fields.
46a88927b5STudor Ambarus  * @num_chans:	number of channels.
47a88927b5STudor Ambarus  */
48a88927b5STudor Ambarus struct acpm_shmem {
49a88927b5STudor Ambarus 	u32 reserved[2];
50a88927b5STudor Ambarus 	u32 chans;
51a88927b5STudor Ambarus 	u32 reserved1[3];
52a88927b5STudor Ambarus 	u32 num_chans;
53a88927b5STudor Ambarus };
54a88927b5STudor Ambarus 
55a88927b5STudor Ambarus /**
56a88927b5STudor Ambarus  * struct acpm_chan_shmem - descriptor of a shared memory channel.
57a88927b5STudor Ambarus  *
58a88927b5STudor Ambarus  * @id:			channel ID.
59a88927b5STudor Ambarus  * @reserved:		unused fields.
60a88927b5STudor Ambarus  * @rx_rear:		rear pointer of APM RX queue (TX for AP).
61a88927b5STudor Ambarus  * @rx_front:		front pointer of APM RX queue (TX for AP).
62a88927b5STudor Ambarus  * @rx_base:		base address of APM RX queue (TX for AP).
63a88927b5STudor Ambarus  * @reserved1:		unused fields.
64a88927b5STudor Ambarus  * @tx_rear:		rear pointer of APM TX queue (RX for AP).
65a88927b5STudor Ambarus  * @tx_front:		front pointer of APM TX queue (RX for AP).
66a88927b5STudor Ambarus  * @tx_base:		base address of APM TX queue (RX for AP).
67a88927b5STudor Ambarus  * @qlen:		queue length. Applies to both TX/RX queues.
68a88927b5STudor Ambarus  * @mlen:		message length. Applies to both TX/RX queues.
69a88927b5STudor Ambarus  * @reserved2:		unused fields.
70a88927b5STudor Ambarus  * @poll_completion:	true when the channel works on polling.
71a88927b5STudor Ambarus  */
72a88927b5STudor Ambarus struct acpm_chan_shmem {
73a88927b5STudor Ambarus 	u32 id;
74a88927b5STudor Ambarus 	u32 reserved[3];
75a88927b5STudor Ambarus 	u32 rx_rear;
76a88927b5STudor Ambarus 	u32 rx_front;
77a88927b5STudor Ambarus 	u32 rx_base;
78a88927b5STudor Ambarus 	u32 reserved1[3];
79a88927b5STudor Ambarus 	u32 tx_rear;
80a88927b5STudor Ambarus 	u32 tx_front;
81a88927b5STudor Ambarus 	u32 tx_base;
82a88927b5STudor Ambarus 	u32 qlen;
83a88927b5STudor Ambarus 	u32 mlen;
84a88927b5STudor Ambarus 	u32 reserved2[2];
85a88927b5STudor Ambarus 	u32 poll_completion;
86a88927b5STudor Ambarus };
87a88927b5STudor Ambarus 
88a88927b5STudor Ambarus /**
89a88927b5STudor Ambarus  * struct acpm_queue - exynos acpm queue.
90a88927b5STudor Ambarus  *
91a88927b5STudor Ambarus  * @rear:	rear address of the queue.
92a88927b5STudor Ambarus  * @front:	front address of the queue.
93a88927b5STudor Ambarus  * @base:	base address of the queue.
94a88927b5STudor Ambarus  */
95a88927b5STudor Ambarus struct acpm_queue {
96a88927b5STudor Ambarus 	void __iomem *rear;
97a88927b5STudor Ambarus 	void __iomem *front;
98a88927b5STudor Ambarus 	void __iomem *base;
99a88927b5STudor Ambarus };
100a88927b5STudor Ambarus 
101a88927b5STudor Ambarus /**
102a88927b5STudor Ambarus  * struct acpm_rx_data - RX queue data.
103a88927b5STudor Ambarus  *
104a88927b5STudor Ambarus  * @cmd:	pointer to where the data shall be saved.
105a88927b5STudor Ambarus  * @n_cmd:	number of 32-bit commands.
106a88927b5STudor Ambarus  * @response:	true if the client expects the RX data.
107a88927b5STudor Ambarus  */
108a88927b5STudor Ambarus struct acpm_rx_data {
109a88927b5STudor Ambarus 	u32 *cmd;
110a88927b5STudor Ambarus 	size_t n_cmd;
111a88927b5STudor Ambarus 	bool response;
112a88927b5STudor Ambarus };
113a88927b5STudor Ambarus 
114a88927b5STudor Ambarus #define ACPM_SEQNUM_MAX    64
115a88927b5STudor Ambarus 
116a88927b5STudor Ambarus /**
117a88927b5STudor Ambarus  * struct acpm_chan - driver internal representation of a channel.
118a88927b5STudor Ambarus  * @cl:		mailbox client.
119a88927b5STudor Ambarus  * @chan:	mailbox channel.
120a88927b5STudor Ambarus  * @acpm:	pointer to driver private data.
121a88927b5STudor Ambarus  * @tx:		TX queue. The enqueue is done by the host.
122a88927b5STudor Ambarus  *			- front index is written by the host.
123a88927b5STudor Ambarus  *			- rear index is written by the firmware.
124a88927b5STudor Ambarus  *
125a88927b5STudor Ambarus  * @rx:		RX queue. The enqueue is done by the firmware.
126a88927b5STudor Ambarus  *			- front index is written by the firmware.
127a88927b5STudor Ambarus  *			- rear index is written by the host.
128a88927b5STudor Ambarus  * @tx_lock:	protects TX queue.
129a88927b5STudor Ambarus  * @rx_lock:	protects RX queue.
130a88927b5STudor Ambarus  * @qlen:	queue length. Applies to both TX/RX queues.
131a88927b5STudor Ambarus  * @mlen:	message length. Applies to both TX/RX queues.
132a88927b5STudor Ambarus  * @seqnum:	sequence number of the last message enqueued on TX queue.
133a88927b5STudor Ambarus  * @id:		channel ID.
134a88927b5STudor Ambarus  * @poll_completion:	indicates if the transfer needs to be polled for
135a88927b5STudor Ambarus  *			completion or interrupt mode is used.
136a88927b5STudor Ambarus  * @bitmap_seqnum: bitmap that tracks the messages on the TX/RX queues.
137a88927b5STudor Ambarus  * @rx_data:	internal buffer used to drain the RX queue.
138a88927b5STudor Ambarus  */
139a88927b5STudor Ambarus struct acpm_chan {
140a88927b5STudor Ambarus 	struct mbox_client cl;
141a88927b5STudor Ambarus 	struct mbox_chan *chan;
142a88927b5STudor Ambarus 	struct acpm_info *acpm;
143a88927b5STudor Ambarus 	struct acpm_queue tx;
144a88927b5STudor Ambarus 	struct acpm_queue rx;
145a88927b5STudor Ambarus 	struct mutex tx_lock;
146a88927b5STudor Ambarus 	struct mutex rx_lock;
147a88927b5STudor Ambarus 
148a88927b5STudor Ambarus 	unsigned int qlen;
149a88927b5STudor Ambarus 	unsigned int mlen;
150a88927b5STudor Ambarus 	u8 seqnum;
151a88927b5STudor Ambarus 	u8 id;
152a88927b5STudor Ambarus 	bool poll_completion;
153a88927b5STudor Ambarus 
154a88927b5STudor Ambarus 	DECLARE_BITMAP(bitmap_seqnum, ACPM_SEQNUM_MAX - 1);
155a88927b5STudor Ambarus 	struct acpm_rx_data rx_data[ACPM_SEQNUM_MAX];
156a88927b5STudor Ambarus };
157a88927b5STudor Ambarus 
158a88927b5STudor Ambarus /**
159a88927b5STudor Ambarus  * struct acpm_info - driver's private data.
160a88927b5STudor Ambarus  * @shmem:	pointer to the SRAM configuration data.
161a88927b5STudor Ambarus  * @sram_base:	base address of SRAM.
162a88927b5STudor Ambarus  * @chans:	pointer to the ACPM channel parameters retrieved from SRAM.
163a88927b5STudor Ambarus  * @dev:	pointer to the exynos-acpm device.
164a88927b5STudor Ambarus  * @handle:	instance of acpm_handle to send to clients.
165a88927b5STudor Ambarus  * @num_chans:	number of channels available for this controller.
166a88927b5STudor Ambarus  */
167a88927b5STudor Ambarus struct acpm_info {
168a88927b5STudor Ambarus 	struct acpm_shmem __iomem *shmem;
169a88927b5STudor Ambarus 	void __iomem *sram_base;
170a88927b5STudor Ambarus 	struct acpm_chan *chans;
171a88927b5STudor Ambarus 	struct device *dev;
172a88927b5STudor Ambarus 	struct acpm_handle handle;
173a88927b5STudor Ambarus 	u32 num_chans;
174a88927b5STudor Ambarus };
175a88927b5STudor Ambarus 
176a88927b5STudor Ambarus /**
177a88927b5STudor Ambarus  * struct acpm_match_data - of_device_id data.
178a88927b5STudor Ambarus  * @initdata_base:	offset in SRAM where the channels configuration resides.
179a88927b5STudor Ambarus  */
180a88927b5STudor Ambarus struct acpm_match_data {
181a88927b5STudor Ambarus 	loff_t initdata_base;
182a88927b5STudor Ambarus };
183a88927b5STudor Ambarus 
184a88927b5STudor Ambarus #define client_to_acpm_chan(c) container_of(c, struct acpm_chan, cl)
185a88927b5STudor Ambarus #define handle_to_acpm_info(h) container_of(h, struct acpm_info, handle)
186a88927b5STudor Ambarus 
187a88927b5STudor Ambarus /**
1882908ffa5STudor Ambarus  * acpm_get_saved_rx() - get the response if it was already saved.
1892908ffa5STudor Ambarus  * @achan:	ACPM channel info.
1902908ffa5STudor Ambarus  * @xfer:	reference to the transfer to get response for.
1912908ffa5STudor Ambarus  * @tx_seqnum:	xfer TX sequence number.
1922908ffa5STudor Ambarus  */
acpm_get_saved_rx(struct acpm_chan * achan,const struct acpm_xfer * xfer,u32 tx_seqnum)1932908ffa5STudor Ambarus static void acpm_get_saved_rx(struct acpm_chan *achan,
1942908ffa5STudor Ambarus 			      const struct acpm_xfer *xfer, u32 tx_seqnum)
1952908ffa5STudor Ambarus {
1962908ffa5STudor Ambarus 	const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1];
1972908ffa5STudor Ambarus 	u32 rx_seqnum;
1982908ffa5STudor Ambarus 
1992908ffa5STudor Ambarus 	if (!rx_data->response)
2002908ffa5STudor Ambarus 		return;
2012908ffa5STudor Ambarus 
2022908ffa5STudor Ambarus 	rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
2032908ffa5STudor Ambarus 
2042908ffa5STudor Ambarus 	if (rx_seqnum == tx_seqnum) {
2052908ffa5STudor Ambarus 		memcpy(xfer->rxd, rx_data->cmd, xfer->rxlen);
2062908ffa5STudor Ambarus 		clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
2072908ffa5STudor Ambarus 	}
2082908ffa5STudor Ambarus }
2092908ffa5STudor Ambarus 
2102908ffa5STudor Ambarus /**
211a88927b5STudor Ambarus  * acpm_get_rx() - get response from RX queue.
212a88927b5STudor Ambarus  * @achan:	ACPM channel info.
213a88927b5STudor Ambarus  * @xfer:	reference to the transfer to get response for.
214a88927b5STudor Ambarus  *
215a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
216a88927b5STudor Ambarus  */
acpm_get_rx(struct acpm_chan * achan,const struct acpm_xfer * xfer)217a88927b5STudor Ambarus static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
218a88927b5STudor Ambarus {
219a88927b5STudor Ambarus 	u32 rx_front, rx_seqnum, tx_seqnum, seqnum;
220a88927b5STudor Ambarus 	const void __iomem *base, *addr;
221a88927b5STudor Ambarus 	struct acpm_rx_data *rx_data;
222a88927b5STudor Ambarus 	u32 i, val, mlen;
223a88927b5STudor Ambarus 	bool rx_set = false;
224a88927b5STudor Ambarus 
225a88927b5STudor Ambarus 	guard(mutex)(&achan->rx_lock);
226a88927b5STudor Ambarus 
227a88927b5STudor Ambarus 	rx_front = readl(achan->rx.front);
228a88927b5STudor Ambarus 	i = readl(achan->rx.rear);
229a88927b5STudor Ambarus 
2302908ffa5STudor Ambarus 	tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
2312908ffa5STudor Ambarus 
2322908ffa5STudor Ambarus 	if (i == rx_front) {
2332908ffa5STudor Ambarus 		acpm_get_saved_rx(achan, xfer, tx_seqnum);
234a88927b5STudor Ambarus 		return 0;
2352908ffa5STudor Ambarus 	}
236a88927b5STudor Ambarus 
237a88927b5STudor Ambarus 	base = achan->rx.base;
238a88927b5STudor Ambarus 	mlen = achan->mlen;
239a88927b5STudor Ambarus 
240a88927b5STudor Ambarus 	/* Drain RX queue. */
241a88927b5STudor Ambarus 	do {
242a88927b5STudor Ambarus 		/* Read RX seqnum. */
243a88927b5STudor Ambarus 		addr = base + mlen * i;
244a88927b5STudor Ambarus 		val = readl(addr);
245a88927b5STudor Ambarus 
246a88927b5STudor Ambarus 		rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, val);
247a88927b5STudor Ambarus 		if (!rx_seqnum)
248a88927b5STudor Ambarus 			return -EIO;
249a88927b5STudor Ambarus 		/*
250a88927b5STudor Ambarus 		 * mssg seqnum starts with value 1, whereas the driver considers
251a88927b5STudor Ambarus 		 * the first mssg at index 0.
252a88927b5STudor Ambarus 		 */
253a88927b5STudor Ambarus 		seqnum = rx_seqnum - 1;
254a88927b5STudor Ambarus 		rx_data = &achan->rx_data[seqnum];
255a88927b5STudor Ambarus 
256a88927b5STudor Ambarus 		if (rx_data->response) {
257a88927b5STudor Ambarus 			if (rx_seqnum == tx_seqnum) {
258a88927b5STudor Ambarus 				__ioread32_copy(xfer->rxd, addr,
259a88927b5STudor Ambarus 						xfer->rxlen / 4);
260a88927b5STudor Ambarus 				rx_set = true;
261a88927b5STudor Ambarus 				clear_bit(seqnum, achan->bitmap_seqnum);
262a88927b5STudor Ambarus 			} else {
263a88927b5STudor Ambarus 				/*
264a88927b5STudor Ambarus 				 * The RX data corresponds to another request.
265a88927b5STudor Ambarus 				 * Save the data to drain the queue, but don't
266a88927b5STudor Ambarus 				 * clear yet the bitmap. It will be cleared
267a88927b5STudor Ambarus 				 * after the response is copied to the request.
268a88927b5STudor Ambarus 				 */
269a88927b5STudor Ambarus 				__ioread32_copy(rx_data->cmd, addr,
270a88927b5STudor Ambarus 						xfer->rxlen / 4);
271a88927b5STudor Ambarus 			}
272a88927b5STudor Ambarus 		} else {
273a88927b5STudor Ambarus 			clear_bit(seqnum, achan->bitmap_seqnum);
274a88927b5STudor Ambarus 		}
275a88927b5STudor Ambarus 
276a88927b5STudor Ambarus 		i = (i + 1) % achan->qlen;
277a88927b5STudor Ambarus 	} while (i != rx_front);
278a88927b5STudor Ambarus 
279a88927b5STudor Ambarus 	/* We saved all responses, mark RX empty. */
280a88927b5STudor Ambarus 	writel(rx_front, achan->rx.rear);
281a88927b5STudor Ambarus 
282a88927b5STudor Ambarus 	/*
283a88927b5STudor Ambarus 	 * If the response was not in this iteration of the queue, check if the
284a88927b5STudor Ambarus 	 * RX data was previously saved.
285a88927b5STudor Ambarus 	 */
2862908ffa5STudor Ambarus 	if (!rx_set)
2872908ffa5STudor Ambarus 		acpm_get_saved_rx(achan, xfer, tx_seqnum);
288a88927b5STudor Ambarus 
289a88927b5STudor Ambarus 	return 0;
290a88927b5STudor Ambarus }
291a88927b5STudor Ambarus 
292a88927b5STudor Ambarus /**
293a88927b5STudor Ambarus  * acpm_dequeue_by_polling() - RX dequeue by polling.
294a88927b5STudor Ambarus  * @achan:	ACPM channel info.
295a88927b5STudor Ambarus  * @xfer:	reference to the transfer being waited for.
296a88927b5STudor Ambarus  *
297a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
298a88927b5STudor Ambarus  */
acpm_dequeue_by_polling(struct acpm_chan * achan,const struct acpm_xfer * xfer)299a88927b5STudor Ambarus static int acpm_dequeue_by_polling(struct acpm_chan *achan,
300a88927b5STudor Ambarus 				   const struct acpm_xfer *xfer)
301a88927b5STudor Ambarus {
302a88927b5STudor Ambarus 	struct device *dev = achan->acpm->dev;
303d2098981SAndré Draszik 	ktime_t timeout;
304a88927b5STudor Ambarus 	u32 seqnum;
305a88927b5STudor Ambarus 	int ret;
306a88927b5STudor Ambarus 
307a88927b5STudor Ambarus 	seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
308a88927b5STudor Ambarus 
309d2098981SAndré Draszik 	timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US);
310a88927b5STudor Ambarus 	do {
311a88927b5STudor Ambarus 		ret = acpm_get_rx(achan, xfer);
312a88927b5STudor Ambarus 		if (ret)
313a88927b5STudor Ambarus 			return ret;
314a88927b5STudor Ambarus 
315a88927b5STudor Ambarus 		if (!test_bit(seqnum - 1, achan->bitmap_seqnum))
316a88927b5STudor Ambarus 			return 0;
317a88927b5STudor Ambarus 
318a88927b5STudor Ambarus 		/* Determined experimentally. */
3192d14c680SAndré Draszik 		udelay(20);
320d2098981SAndré Draszik 	} while (ktime_before(ktime_get(), timeout));
321a88927b5STudor Ambarus 
322d2098981SAndré Draszik 	dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx.\n",
323d2098981SAndré Draszik 		achan->id, seqnum, achan->bitmap_seqnum[0]);
324a88927b5STudor Ambarus 
325a88927b5STudor Ambarus 	return -ETIME;
326a88927b5STudor Ambarus }
327a88927b5STudor Ambarus 
328a88927b5STudor Ambarus /**
329a88927b5STudor Ambarus  * acpm_wait_for_queue_slots() - wait for queue slots.
330a88927b5STudor Ambarus  *
331a88927b5STudor Ambarus  * @achan:		ACPM channel info.
332a88927b5STudor Ambarus  * @next_tx_front:	next front index of the TX queue.
333a88927b5STudor Ambarus  *
334a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
335a88927b5STudor Ambarus  */
acpm_wait_for_queue_slots(struct acpm_chan * achan,u32 next_tx_front)336a88927b5STudor Ambarus static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front)
337a88927b5STudor Ambarus {
338a88927b5STudor Ambarus 	u32 val, ret;
339a88927b5STudor Ambarus 
340a88927b5STudor Ambarus 	/*
341a88927b5STudor Ambarus 	 * Wait for RX front to keep up with TX front. Make sure there's at
342a88927b5STudor Ambarus 	 * least one element between them.
343a88927b5STudor Ambarus 	 */
344a88927b5STudor Ambarus 	ret = readl_poll_timeout(achan->rx.front, val, next_tx_front != val, 0,
345a88927b5STudor Ambarus 				 ACPM_TX_TIMEOUT_US);
346a88927b5STudor Ambarus 	if (ret) {
347a88927b5STudor Ambarus 		dev_err(achan->acpm->dev, "RX front can not keep up with TX front.\n");
348a88927b5STudor Ambarus 		return ret;
349a88927b5STudor Ambarus 	}
350a88927b5STudor Ambarus 
351a88927b5STudor Ambarus 	ret = readl_poll_timeout(achan->tx.rear, val, next_tx_front != val, 0,
352a88927b5STudor Ambarus 				 ACPM_TX_TIMEOUT_US);
353a88927b5STudor Ambarus 	if (ret)
354a88927b5STudor Ambarus 		dev_err(achan->acpm->dev, "TX queue is full.\n");
355a88927b5STudor Ambarus 
356a88927b5STudor Ambarus 	return ret;
357a88927b5STudor Ambarus }
358a88927b5STudor Ambarus 
359a88927b5STudor Ambarus /**
360a88927b5STudor Ambarus  * acpm_prepare_xfer() - prepare a transfer before writing the message to the
361a88927b5STudor Ambarus  * TX queue.
362a88927b5STudor Ambarus  * @achan:	ACPM channel info.
363a88927b5STudor Ambarus  * @xfer:	reference to the transfer being prepared.
364a88927b5STudor Ambarus  */
acpm_prepare_xfer(struct acpm_chan * achan,const struct acpm_xfer * xfer)365a88927b5STudor Ambarus static void acpm_prepare_xfer(struct acpm_chan *achan,
366a88927b5STudor Ambarus 			      const struct acpm_xfer *xfer)
367a88927b5STudor Ambarus {
368a88927b5STudor Ambarus 	struct acpm_rx_data *rx_data;
369a88927b5STudor Ambarus 	u32 *txd = (u32 *)xfer->txd;
370a88927b5STudor Ambarus 
371a88927b5STudor Ambarus 	/* Prevent chan->seqnum from being re-used */
372a88927b5STudor Ambarus 	do {
373a88927b5STudor Ambarus 		if (++achan->seqnum == ACPM_SEQNUM_MAX)
374a88927b5STudor Ambarus 			achan->seqnum = 1;
375a88927b5STudor Ambarus 	} while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum));
376a88927b5STudor Ambarus 
377a88927b5STudor Ambarus 	txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum);
378a88927b5STudor Ambarus 
379a88927b5STudor Ambarus 	/* Clear data for upcoming responses */
380a88927b5STudor Ambarus 	rx_data = &achan->rx_data[achan->seqnum - 1];
381a88927b5STudor Ambarus 	memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
382a88927b5STudor Ambarus 	if (xfer->rxd)
383a88927b5STudor Ambarus 		rx_data->response = true;
384a88927b5STudor Ambarus 
385a88927b5STudor Ambarus 	/* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
386a88927b5STudor Ambarus 	set_bit(achan->seqnum - 1, achan->bitmap_seqnum);
387a88927b5STudor Ambarus }
388a88927b5STudor Ambarus 
389a88927b5STudor Ambarus /**
390a88927b5STudor Ambarus  * acpm_wait_for_message_response - an helper to group all possible ways of
391a88927b5STudor Ambarus  * waiting for a synchronous message response.
392a88927b5STudor Ambarus  *
393a88927b5STudor Ambarus  * @achan:	ACPM channel info.
394a88927b5STudor Ambarus  * @xfer:	reference to the transfer being waited for.
395a88927b5STudor Ambarus  *
396a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
397a88927b5STudor Ambarus  */
acpm_wait_for_message_response(struct acpm_chan * achan,const struct acpm_xfer * xfer)398a88927b5STudor Ambarus static int acpm_wait_for_message_response(struct acpm_chan *achan,
399a88927b5STudor Ambarus 					  const struct acpm_xfer *xfer)
400a88927b5STudor Ambarus {
401a88927b5STudor Ambarus 	/* Just polling mode supported for now. */
402a88927b5STudor Ambarus 	return acpm_dequeue_by_polling(achan, xfer);
403a88927b5STudor Ambarus }
404a88927b5STudor Ambarus 
405a88927b5STudor Ambarus /**
406a88927b5STudor Ambarus  * acpm_do_xfer() - do one transfer.
407a88927b5STudor Ambarus  * @handle:	pointer to the acpm handle.
408a88927b5STudor Ambarus  * @xfer:	transfer to initiate and wait for response.
409a88927b5STudor Ambarus  *
410a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
411a88927b5STudor Ambarus  */
acpm_do_xfer(const struct acpm_handle * handle,const struct acpm_xfer * xfer)412a88927b5STudor Ambarus int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer)
413a88927b5STudor Ambarus {
414a88927b5STudor Ambarus 	struct acpm_info *acpm = handle_to_acpm_info(handle);
415a88927b5STudor Ambarus 	struct exynos_mbox_msg msg;
416a88927b5STudor Ambarus 	struct acpm_chan *achan;
417a88927b5STudor Ambarus 	u32 idx, tx_front;
418a88927b5STudor Ambarus 	int ret;
419a88927b5STudor Ambarus 
420a88927b5STudor Ambarus 	if (xfer->acpm_chan_id >= acpm->num_chans)
421a88927b5STudor Ambarus 		return -EINVAL;
422a88927b5STudor Ambarus 
423a88927b5STudor Ambarus 	achan = &acpm->chans[xfer->acpm_chan_id];
424a88927b5STudor Ambarus 
425a88927b5STudor Ambarus 	if (!xfer->txd || xfer->txlen > achan->mlen || xfer->rxlen > achan->mlen)
426a88927b5STudor Ambarus 		return -EINVAL;
427a88927b5STudor Ambarus 
428a88927b5STudor Ambarus 	if (!achan->poll_completion) {
429a88927b5STudor Ambarus 		dev_err(achan->acpm->dev, "Interrupt mode not supported\n");
430a88927b5STudor Ambarus 		return -EOPNOTSUPP;
431a88927b5STudor Ambarus 	}
432a88927b5STudor Ambarus 
433*8d2c2fa2STudor Ambarus 	msg.chan_id = xfer->acpm_chan_id;
434*8d2c2fa2STudor Ambarus 	msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL;
435*8d2c2fa2STudor Ambarus 
436a88927b5STudor Ambarus 	scoped_guard(mutex, &achan->tx_lock) {
437a88927b5STudor Ambarus 		tx_front = readl(achan->tx.front);
438a88927b5STudor Ambarus 		idx = (tx_front + 1) % achan->qlen;
439a88927b5STudor Ambarus 
440a88927b5STudor Ambarus 		ret = acpm_wait_for_queue_slots(achan, idx);
441a88927b5STudor Ambarus 		if (ret)
442a88927b5STudor Ambarus 			return ret;
443a88927b5STudor Ambarus 
444a88927b5STudor Ambarus 		acpm_prepare_xfer(achan, xfer);
445a88927b5STudor Ambarus 
446a88927b5STudor Ambarus 		/* Write TX command. */
447a88927b5STudor Ambarus 		__iowrite32_copy(achan->tx.base + achan->mlen * tx_front,
448a88927b5STudor Ambarus 				 xfer->txd, xfer->txlen / 4);
449a88927b5STudor Ambarus 
450a88927b5STudor Ambarus 		/* Advance TX front. */
451a88927b5STudor Ambarus 		writel(idx, achan->tx.front);
452a88927b5STudor Ambarus 
453a88927b5STudor Ambarus 		ret = mbox_send_message(achan->chan, (void *)&msg);
454a88927b5STudor Ambarus 		if (ret < 0)
455a88927b5STudor Ambarus 			return ret;
456a88927b5STudor Ambarus 
457*8d2c2fa2STudor Ambarus 		mbox_client_txdone(achan->chan, 0);
458*8d2c2fa2STudor Ambarus 	}
459a88927b5STudor Ambarus 
460*8d2c2fa2STudor Ambarus 	return acpm_wait_for_message_response(achan, xfer);
461a88927b5STudor Ambarus }
462a88927b5STudor Ambarus 
463a88927b5STudor Ambarus /**
464a88927b5STudor Ambarus  * acpm_chan_shmem_get_params() - get channel parameters and addresses of the
465a88927b5STudor Ambarus  * TX/RX queues.
466a88927b5STudor Ambarus  * @achan:	ACPM channel info.
467a88927b5STudor Ambarus  * @chan_shmem:	__iomem pointer to a channel described in shared memory.
468a88927b5STudor Ambarus  */
acpm_chan_shmem_get_params(struct acpm_chan * achan,struct acpm_chan_shmem __iomem * chan_shmem)469a88927b5STudor Ambarus static void acpm_chan_shmem_get_params(struct acpm_chan *achan,
470a88927b5STudor Ambarus 				struct acpm_chan_shmem __iomem *chan_shmem)
471a88927b5STudor Ambarus {
472a88927b5STudor Ambarus 	void __iomem *base = achan->acpm->sram_base;
473a88927b5STudor Ambarus 	struct acpm_queue *rx = &achan->rx;
474a88927b5STudor Ambarus 	struct acpm_queue *tx = &achan->tx;
475a88927b5STudor Ambarus 
476a88927b5STudor Ambarus 	achan->mlen = readl(&chan_shmem->mlen);
477a88927b5STudor Ambarus 	achan->poll_completion = readl(&chan_shmem->poll_completion);
478a88927b5STudor Ambarus 	achan->id = readl(&chan_shmem->id);
479a88927b5STudor Ambarus 	achan->qlen = readl(&chan_shmem->qlen);
480a88927b5STudor Ambarus 
481a88927b5STudor Ambarus 	tx->base = base + readl(&chan_shmem->rx_base);
482a88927b5STudor Ambarus 	tx->rear = base + readl(&chan_shmem->rx_rear);
483a88927b5STudor Ambarus 	tx->front = base + readl(&chan_shmem->rx_front);
484a88927b5STudor Ambarus 
485a88927b5STudor Ambarus 	rx->base = base + readl(&chan_shmem->tx_base);
486a88927b5STudor Ambarus 	rx->rear = base + readl(&chan_shmem->tx_rear);
487a88927b5STudor Ambarus 	rx->front = base + readl(&chan_shmem->tx_front);
488a88927b5STudor Ambarus 
489a88927b5STudor Ambarus 	dev_vdbg(achan->acpm->dev, "ID = %d poll = %d, mlen = %d, qlen = %d\n",
490a88927b5STudor Ambarus 		 achan->id, achan->poll_completion, achan->mlen, achan->qlen);
491a88927b5STudor Ambarus }
492a88927b5STudor Ambarus 
493a88927b5STudor Ambarus /**
494a88927b5STudor Ambarus  * acpm_achan_alloc_cmds() - allocate buffers for retrieving data from the ACPM
495a88927b5STudor Ambarus  * firmware.
496a88927b5STudor Ambarus  * @achan:	ACPM channel info.
497a88927b5STudor Ambarus  *
498a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
499a88927b5STudor Ambarus  */
acpm_achan_alloc_cmds(struct acpm_chan * achan)500a88927b5STudor Ambarus static int acpm_achan_alloc_cmds(struct acpm_chan *achan)
501a88927b5STudor Ambarus {
502a88927b5STudor Ambarus 	struct device *dev = achan->acpm->dev;
503a88927b5STudor Ambarus 	struct acpm_rx_data *rx_data;
504a88927b5STudor Ambarus 	size_t cmd_size, n_cmd;
505a88927b5STudor Ambarus 	int i;
506a88927b5STudor Ambarus 
507a88927b5STudor Ambarus 	if (achan->mlen == 0)
508a88927b5STudor Ambarus 		return 0;
509a88927b5STudor Ambarus 
510a88927b5STudor Ambarus 	cmd_size = sizeof(*(achan->rx_data[0].cmd));
511a88927b5STudor Ambarus 	n_cmd = DIV_ROUND_UP_ULL(achan->mlen, cmd_size);
512a88927b5STudor Ambarus 
513a88927b5STudor Ambarus 	for (i = 0; i < ACPM_SEQNUM_MAX; i++) {
514a88927b5STudor Ambarus 		rx_data = &achan->rx_data[i];
515a88927b5STudor Ambarus 		rx_data->n_cmd = n_cmd;
516a88927b5STudor Ambarus 		rx_data->cmd = devm_kcalloc(dev, n_cmd, cmd_size, GFP_KERNEL);
517a88927b5STudor Ambarus 		if (!rx_data->cmd)
518a88927b5STudor Ambarus 			return -ENOMEM;
519a88927b5STudor Ambarus 	}
520a88927b5STudor Ambarus 
521a88927b5STudor Ambarus 	return 0;
522a88927b5STudor Ambarus }
523a88927b5STudor Ambarus 
524a88927b5STudor Ambarus /**
525a88927b5STudor Ambarus  * acpm_free_mbox_chans() - free mailbox channels.
526a88927b5STudor Ambarus  * @acpm:	pointer to driver data.
527a88927b5STudor Ambarus  */
acpm_free_mbox_chans(struct acpm_info * acpm)528a88927b5STudor Ambarus static void acpm_free_mbox_chans(struct acpm_info *acpm)
529a88927b5STudor Ambarus {
530a88927b5STudor Ambarus 	int i;
531a88927b5STudor Ambarus 
532a88927b5STudor Ambarus 	for (i = 0; i < acpm->num_chans; i++)
533a88927b5STudor Ambarus 		if (!IS_ERR_OR_NULL(acpm->chans[i].chan))
534a88927b5STudor Ambarus 			mbox_free_channel(acpm->chans[i].chan);
535a88927b5STudor Ambarus }
536a88927b5STudor Ambarus 
537a88927b5STudor Ambarus /**
538a88927b5STudor Ambarus  * acpm_channels_init() - initialize channels based on the configuration data in
539a88927b5STudor Ambarus  * the shared memory.
540a88927b5STudor Ambarus  * @acpm:	pointer to driver data.
541a88927b5STudor Ambarus  *
542a88927b5STudor Ambarus  * Return: 0 on success, -errno otherwise.
543a88927b5STudor Ambarus  */
acpm_channels_init(struct acpm_info * acpm)544a88927b5STudor Ambarus static int acpm_channels_init(struct acpm_info *acpm)
545a88927b5STudor Ambarus {
546a88927b5STudor Ambarus 	struct acpm_shmem __iomem *shmem = acpm->shmem;
547a88927b5STudor Ambarus 	struct acpm_chan_shmem __iomem *chans_shmem;
548a88927b5STudor Ambarus 	struct device *dev = acpm->dev;
549a88927b5STudor Ambarus 	int i, ret;
550a88927b5STudor Ambarus 
551a88927b5STudor Ambarus 	acpm->num_chans = readl(&shmem->num_chans);
552a88927b5STudor Ambarus 	acpm->chans = devm_kcalloc(dev, acpm->num_chans, sizeof(*acpm->chans),
553a88927b5STudor Ambarus 				   GFP_KERNEL);
554a88927b5STudor Ambarus 	if (!acpm->chans)
555a88927b5STudor Ambarus 		return -ENOMEM;
556a88927b5STudor Ambarus 
557a88927b5STudor Ambarus 	chans_shmem = acpm->sram_base + readl(&shmem->chans);
558a88927b5STudor Ambarus 
559a88927b5STudor Ambarus 	for (i = 0; i < acpm->num_chans; i++) {
560a88927b5STudor Ambarus 		struct acpm_chan_shmem __iomem *chan_shmem = &chans_shmem[i];
561a88927b5STudor Ambarus 		struct acpm_chan *achan = &acpm->chans[i];
562a88927b5STudor Ambarus 		struct mbox_client *cl = &achan->cl;
563a88927b5STudor Ambarus 
564a88927b5STudor Ambarus 		achan->acpm = acpm;
565a88927b5STudor Ambarus 
566a88927b5STudor Ambarus 		acpm_chan_shmem_get_params(achan, chan_shmem);
567a88927b5STudor Ambarus 
568a88927b5STudor Ambarus 		ret = acpm_achan_alloc_cmds(achan);
569a88927b5STudor Ambarus 		if (ret)
570a88927b5STudor Ambarus 			return ret;
571a88927b5STudor Ambarus 
572a88927b5STudor Ambarus 		mutex_init(&achan->rx_lock);
573a88927b5STudor Ambarus 		mutex_init(&achan->tx_lock);
574a88927b5STudor Ambarus 
575a88927b5STudor Ambarus 		cl->dev = dev;
576a88927b5STudor Ambarus 
577a88927b5STudor Ambarus 		achan->chan = mbox_request_channel(cl, 0);
578a88927b5STudor Ambarus 		if (IS_ERR(achan->chan)) {
579a88927b5STudor Ambarus 			acpm_free_mbox_chans(acpm);
580a88927b5STudor Ambarus 			return PTR_ERR(achan->chan);
581a88927b5STudor Ambarus 		}
582a88927b5STudor Ambarus 	}
583a88927b5STudor Ambarus 
584a88927b5STudor Ambarus 	return 0;
585a88927b5STudor Ambarus }
586a88927b5STudor Ambarus 
587a88927b5STudor Ambarus /**
588a88927b5STudor Ambarus  * acpm_setup_ops() - setup the operations structures.
589a88927b5STudor Ambarus  * @acpm:	pointer to the driver data.
590a88927b5STudor Ambarus  */
acpm_setup_ops(struct acpm_info * acpm)591a88927b5STudor Ambarus static void acpm_setup_ops(struct acpm_info *acpm)
592a88927b5STudor Ambarus {
593a88927b5STudor Ambarus 	struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops;
594a88927b5STudor Ambarus 
595a88927b5STudor Ambarus 	pmic_ops->read_reg = acpm_pmic_read_reg;
596a88927b5STudor Ambarus 	pmic_ops->bulk_read = acpm_pmic_bulk_read;
597a88927b5STudor Ambarus 	pmic_ops->write_reg = acpm_pmic_write_reg;
598a88927b5STudor Ambarus 	pmic_ops->bulk_write = acpm_pmic_bulk_write;
599a88927b5STudor Ambarus 	pmic_ops->update_reg = acpm_pmic_update_reg;
600a88927b5STudor Ambarus }
601a88927b5STudor Ambarus 
acpm_probe(struct platform_device * pdev)602a88927b5STudor Ambarus static int acpm_probe(struct platform_device *pdev)
603a88927b5STudor Ambarus {
604a88927b5STudor Ambarus 	const struct acpm_match_data *match_data;
605a88927b5STudor Ambarus 	struct device *dev = &pdev->dev;
606a88927b5STudor Ambarus 	struct device_node *shmem;
607a88927b5STudor Ambarus 	struct acpm_info *acpm;
608a88927b5STudor Ambarus 	resource_size_t size;
609a88927b5STudor Ambarus 	struct resource res;
610a88927b5STudor Ambarus 	int ret;
611a88927b5STudor Ambarus 
612a88927b5STudor Ambarus 	acpm = devm_kzalloc(dev, sizeof(*acpm), GFP_KERNEL);
613a88927b5STudor Ambarus 	if (!acpm)
614a88927b5STudor Ambarus 		return -ENOMEM;
615a88927b5STudor Ambarus 
616a88927b5STudor Ambarus 	shmem = of_parse_phandle(dev->of_node, "shmem", 0);
617a88927b5STudor Ambarus 	ret = of_address_to_resource(shmem, 0, &res);
618a88927b5STudor Ambarus 	of_node_put(shmem);
619a88927b5STudor Ambarus 	if (ret)
620a88927b5STudor Ambarus 		return dev_err_probe(dev, ret,
621a88927b5STudor Ambarus 				     "Failed to get shared memory.\n");
622a88927b5STudor Ambarus 
623a88927b5STudor Ambarus 	size = resource_size(&res);
624a88927b5STudor Ambarus 	acpm->sram_base = devm_ioremap(dev, res.start, size);
625a88927b5STudor Ambarus 	if (!acpm->sram_base)
626a88927b5STudor Ambarus 		return dev_err_probe(dev, -ENOMEM,
627a88927b5STudor Ambarus 				     "Failed to ioremap shared memory.\n");
628a88927b5STudor Ambarus 
629a88927b5STudor Ambarus 	match_data = of_device_get_match_data(dev);
630a88927b5STudor Ambarus 	if (!match_data)
631a88927b5STudor Ambarus 		return dev_err_probe(dev, -EINVAL,
6328c47b744SColin Ian King 				     "Failed to get match data.\n");
633a88927b5STudor Ambarus 
634a88927b5STudor Ambarus 	acpm->shmem = acpm->sram_base + match_data->initdata_base;
635a88927b5STudor Ambarus 	acpm->dev = dev;
636a88927b5STudor Ambarus 
637a88927b5STudor Ambarus 	ret = acpm_channels_init(acpm);
638a88927b5STudor Ambarus 	if (ret)
639a88927b5STudor Ambarus 		return ret;
640a88927b5STudor Ambarus 
641a88927b5STudor Ambarus 	acpm_setup_ops(acpm);
642a88927b5STudor Ambarus 
643a88927b5STudor Ambarus 	platform_set_drvdata(pdev, acpm);
644a88927b5STudor Ambarus 
645636baba9STudor Ambarus 	return devm_of_platform_populate(dev);
646a88927b5STudor Ambarus }
647a88927b5STudor Ambarus 
648a88927b5STudor Ambarus /**
649a88927b5STudor Ambarus  * acpm_handle_put() - release the handle acquired by acpm_get_by_phandle.
650a88927b5STudor Ambarus  * @handle:	Handle acquired by acpm_get_by_phandle.
651a88927b5STudor Ambarus  */
acpm_handle_put(const struct acpm_handle * handle)652a88927b5STudor Ambarus static void acpm_handle_put(const struct acpm_handle *handle)
653a88927b5STudor Ambarus {
654a88927b5STudor Ambarus 	struct acpm_info *acpm = handle_to_acpm_info(handle);
655a88927b5STudor Ambarus 	struct device *dev = acpm->dev;
656a88927b5STudor Ambarus 
657a88927b5STudor Ambarus 	module_put(dev->driver->owner);
658a88927b5STudor Ambarus 	/* Drop reference taken with of_find_device_by_node(). */
659a88927b5STudor Ambarus 	put_device(dev);
660a88927b5STudor Ambarus }
661a88927b5STudor Ambarus 
662a88927b5STudor Ambarus /**
663a88927b5STudor Ambarus  * devm_acpm_release() - devres release method.
664a88927b5STudor Ambarus  * @dev: pointer to device.
665a88927b5STudor Ambarus  * @res: pointer to resource.
666a88927b5STudor Ambarus  */
devm_acpm_release(struct device * dev,void * res)667a88927b5STudor Ambarus static void devm_acpm_release(struct device *dev, void *res)
668a88927b5STudor Ambarus {
669a88927b5STudor Ambarus 	acpm_handle_put(*(struct acpm_handle **)res);
670a88927b5STudor Ambarus }
671a88927b5STudor Ambarus 
672a88927b5STudor Ambarus /**
6732c2e5e90SKrzysztof Kozlowski  * acpm_get_by_node() - get the ACPM handle using node pointer.
674a88927b5STudor Ambarus  * @dev:	device pointer requesting ACPM handle.
6752c2e5e90SKrzysztof Kozlowski  * @np:		ACPM device tree node.
676a88927b5STudor Ambarus  *
677a88927b5STudor Ambarus  * Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
678a88927b5STudor Ambarus  */
acpm_get_by_node(struct device * dev,struct device_node * np)679a8dc26a0SAndré Draszik static const struct acpm_handle *acpm_get_by_node(struct device *dev,
6802c2e5e90SKrzysztof Kozlowski 						  struct device_node *np)
681a88927b5STudor Ambarus {
682a88927b5STudor Ambarus 	struct platform_device *pdev;
683a88927b5STudor Ambarus 	struct device_link *link;
684a88927b5STudor Ambarus 	struct acpm_info *acpm;
685a88927b5STudor Ambarus 
6862c2e5e90SKrzysztof Kozlowski 	pdev = of_find_device_by_node(np);
68753734383SAndré Draszik 	if (!pdev)
688a88927b5STudor Ambarus 		return ERR_PTR(-EPROBE_DEFER);
689a88927b5STudor Ambarus 
690a88927b5STudor Ambarus 	acpm = platform_get_drvdata(pdev);
691a88927b5STudor Ambarus 	if (!acpm) {
692a88927b5STudor Ambarus 		platform_device_put(pdev);
693a88927b5STudor Ambarus 		return ERR_PTR(-EPROBE_DEFER);
694a88927b5STudor Ambarus 	}
695a88927b5STudor Ambarus 
696a88927b5STudor Ambarus 	if (!try_module_get(pdev->dev.driver->owner)) {
697a88927b5STudor Ambarus 		platform_device_put(pdev);
698a88927b5STudor Ambarus 		return ERR_PTR(-EPROBE_DEFER);
699a88927b5STudor Ambarus 	}
700a88927b5STudor Ambarus 
701a88927b5STudor Ambarus 	link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
702a88927b5STudor Ambarus 	if (!link) {
703a88927b5STudor Ambarus 		dev_err(&pdev->dev,
704a88927b5STudor Ambarus 			"Failed to create device link to consumer %s.\n",
705a88927b5STudor Ambarus 			dev_name(dev));
706a88927b5STudor Ambarus 		platform_device_put(pdev);
707a88927b5STudor Ambarus 		module_put(pdev->dev.driver->owner);
708a88927b5STudor Ambarus 		return ERR_PTR(-EINVAL);
709a88927b5STudor Ambarus 	}
710a88927b5STudor Ambarus 
711a88927b5STudor Ambarus 	return &acpm->handle;
712a88927b5STudor Ambarus }
713a88927b5STudor Ambarus 
714a88927b5STudor Ambarus /**
715a8dc26a0SAndré Draszik  * devm_acpm_get_by_node() - managed get handle using node pointer.
716a88927b5STudor Ambarus  * @dev: device pointer requesting ACPM handle.
717a8dc26a0SAndré Draszik  * @np:  ACPM device tree node.
718a88927b5STudor Ambarus  *
719a88927b5STudor Ambarus  * Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
720a88927b5STudor Ambarus  */
devm_acpm_get_by_node(struct device * dev,struct device_node * np)721a8dc26a0SAndré Draszik const struct acpm_handle *devm_acpm_get_by_node(struct device *dev,
722a8dc26a0SAndré Draszik 						struct device_node *np)
723a88927b5STudor Ambarus {
724a88927b5STudor Ambarus 	const struct acpm_handle **ptr, *handle;
725a88927b5STudor Ambarus 
726a88927b5STudor Ambarus 	ptr = devres_alloc(devm_acpm_release, sizeof(*ptr), GFP_KERNEL);
727a88927b5STudor Ambarus 	if (!ptr)
728a88927b5STudor Ambarus 		return ERR_PTR(-ENOMEM);
729a88927b5STudor Ambarus 
730a8dc26a0SAndré Draszik 	handle = acpm_get_by_node(dev, np);
731a88927b5STudor Ambarus 	if (!IS_ERR(handle)) {
732a88927b5STudor Ambarus 		*ptr = handle;
733a88927b5STudor Ambarus 		devres_add(dev, ptr);
734a88927b5STudor Ambarus 	} else {
735a88927b5STudor Ambarus 		devres_free(ptr);
736a88927b5STudor Ambarus 	}
737a88927b5STudor Ambarus 
738a88927b5STudor Ambarus 	return handle;
739a88927b5STudor Ambarus }
740a8dc26a0SAndré Draszik EXPORT_SYMBOL_GPL(devm_acpm_get_by_node);
741a88927b5STudor Ambarus 
742a88927b5STudor Ambarus static const struct acpm_match_data acpm_gs101 = {
743a88927b5STudor Ambarus 	.initdata_base = ACPM_GS101_INITDATA_BASE,
744a88927b5STudor Ambarus };
745a88927b5STudor Ambarus 
746a88927b5STudor Ambarus static const struct of_device_id acpm_match[] = {
747a88927b5STudor Ambarus 	{
748a88927b5STudor Ambarus 		.compatible = "google,gs101-acpm-ipc",
749a88927b5STudor Ambarus 		.data = &acpm_gs101,
750a88927b5STudor Ambarus 	},
751a88927b5STudor Ambarus 	{},
752a88927b5STudor Ambarus };
753a88927b5STudor Ambarus MODULE_DEVICE_TABLE(of, acpm_match);
754a88927b5STudor Ambarus 
755a88927b5STudor Ambarus static struct platform_driver acpm_driver = {
756a88927b5STudor Ambarus 	.probe	= acpm_probe,
757a88927b5STudor Ambarus 	.driver	= {
758a88927b5STudor Ambarus 		.name = "exynos-acpm-protocol",
759a88927b5STudor Ambarus 		.of_match_table	= acpm_match,
760a88927b5STudor Ambarus 	},
761a88927b5STudor Ambarus };
762a88927b5STudor Ambarus module_platform_driver(acpm_driver);
763a88927b5STudor Ambarus 
764a88927b5STudor Ambarus MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
765a88927b5STudor Ambarus MODULE_DESCRIPTION("Samsung Exynos ACPM mailbox protocol driver");
766a88927b5STudor Ambarus MODULE_LICENSE("GPL");
767