18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mailbox: Common code for Mailbox controllers and users
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Linaro Ltd.
68c2ecf20Sopenharmony_ci * Author: Jassi Brar <jassisinghbrar@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/device.h>
178c2ecf20Sopenharmony_ci#include <linux/bitops.h>
188c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
198c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "mailbox.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic LIST_HEAD(mbox_cons);
248c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(con_mutex);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int add_to_rbuf(struct mbox_chan *chan, void *mssg)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int idx;
298c2ecf20Sopenharmony_ci	unsigned long flags;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* See if there is any space left */
348c2ecf20Sopenharmony_ci	if (chan->msg_count == MBOX_TX_QUEUE_LEN) {
358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
368c2ecf20Sopenharmony_ci		return -ENOBUFS;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	idx = chan->msg_free;
408c2ecf20Sopenharmony_ci	chan->msg_data[idx] = mssg;
418c2ecf20Sopenharmony_ci	chan->msg_count++;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (idx == MBOX_TX_QUEUE_LEN - 1)
448c2ecf20Sopenharmony_ci		chan->msg_free = 0;
458c2ecf20Sopenharmony_ci	else
468c2ecf20Sopenharmony_ci		chan->msg_free++;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return idx;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void msg_submit(struct mbox_chan *chan)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned count, idx;
568c2ecf20Sopenharmony_ci	unsigned long flags;
578c2ecf20Sopenharmony_ci	void *data;
588c2ecf20Sopenharmony_ci	int err = -EBUSY;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!chan->msg_count || chan->active_req)
638c2ecf20Sopenharmony_ci		goto exit;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	count = chan->msg_count;
668c2ecf20Sopenharmony_ci	idx = chan->msg_free;
678c2ecf20Sopenharmony_ci	if (idx >= count)
688c2ecf20Sopenharmony_ci		idx -= count;
698c2ecf20Sopenharmony_ci	else
708c2ecf20Sopenharmony_ci		idx += MBOX_TX_QUEUE_LEN - count;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	data = chan->msg_data[idx];
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (chan->cl->tx_prepare)
758c2ecf20Sopenharmony_ci		chan->cl->tx_prepare(chan->cl, data);
768c2ecf20Sopenharmony_ci	/* Try to submit a message to the MBOX controller */
778c2ecf20Sopenharmony_ci	err = chan->mbox->ops->send_data(chan, data);
788c2ecf20Sopenharmony_ci	if (!err) {
798c2ecf20Sopenharmony_ci		chan->active_req = data;
808c2ecf20Sopenharmony_ci		chan->msg_count--;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ciexit:
838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!err && (chan->txdone_method & TXDONE_BY_POLL)) {
868c2ecf20Sopenharmony_ci		/* kick start the timer immediately to avoid delays */
878c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chan->mbox->poll_hrt_lock, flags);
888c2ecf20Sopenharmony_ci		hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
898c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->mbox->poll_hrt_lock, flags);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void tx_tick(struct mbox_chan *chan, int r)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long flags;
968c2ecf20Sopenharmony_ci	void *mssg;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
998c2ecf20Sopenharmony_ci	mssg = chan->active_req;
1008c2ecf20Sopenharmony_ci	chan->active_req = NULL;
1018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Submit next message */
1048c2ecf20Sopenharmony_ci	msg_submit(chan);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (!mssg)
1078c2ecf20Sopenharmony_ci		return;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Notify the client */
1108c2ecf20Sopenharmony_ci	if (chan->cl->tx_done)
1118c2ecf20Sopenharmony_ci		chan->cl->tx_done(chan->cl, mssg, r);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (r != -ETIME && chan->cl->tx_block)
1148c2ecf20Sopenharmony_ci		complete(&chan->tx_complete);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct mbox_controller *mbox =
1208c2ecf20Sopenharmony_ci		container_of(hrtimer, struct mbox_controller, poll_hrt);
1218c2ecf20Sopenharmony_ci	bool txdone, resched = false;
1228c2ecf20Sopenharmony_ci	int i;
1238c2ecf20Sopenharmony_ci	unsigned long flags;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	for (i = 0; i < mbox->num_chans; i++) {
1268c2ecf20Sopenharmony_ci		struct mbox_chan *chan = &mbox->chans[i];
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		if (chan->active_req && chan->cl) {
1298c2ecf20Sopenharmony_ci			txdone = chan->mbox->ops->last_tx_done(chan);
1308c2ecf20Sopenharmony_ci			if (txdone)
1318c2ecf20Sopenharmony_ci				tx_tick(chan, 0);
1328c2ecf20Sopenharmony_ci			else
1338c2ecf20Sopenharmony_ci				resched = true;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (resched) {
1388c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mbox->poll_hrt_lock, flags);
1398c2ecf20Sopenharmony_ci		if (!hrtimer_is_queued(hrtimer))
1408c2ecf20Sopenharmony_ci			hrtimer_forward_now(hrtimer, ms_to_ktime(mbox->txpoll_period));
1418c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mbox->poll_hrt_lock, flags);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		return HRTIMER_RESTART;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/**
1498c2ecf20Sopenharmony_ci * mbox_chan_received_data - A way for controller driver to push data
1508c2ecf20Sopenharmony_ci *				received from remote to the upper layer.
1518c2ecf20Sopenharmony_ci * @chan: Pointer to the mailbox channel on which RX happened.
1528c2ecf20Sopenharmony_ci * @mssg: Client specific message typecasted as void *
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * After startup and before shutdown any data received on the chan
1558c2ecf20Sopenharmony_ci * is passed on to the API via atomic mbox_chan_received_data().
1568c2ecf20Sopenharmony_ci * The controller should ACK the RX only after this call returns.
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_civoid mbox_chan_received_data(struct mbox_chan *chan, void *mssg)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	/* No buffering the received data */
1618c2ecf20Sopenharmony_ci	if (chan->cl->rx_callback)
1628c2ecf20Sopenharmony_ci		chan->cl->rx_callback(chan->cl, mssg);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_chan_received_data);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/**
1678c2ecf20Sopenharmony_ci * mbox_chan_txdone - A way for controller driver to notify the
1688c2ecf20Sopenharmony_ci *			framework that the last TX has completed.
1698c2ecf20Sopenharmony_ci * @chan: Pointer to the mailbox chan on which TX happened.
1708c2ecf20Sopenharmony_ci * @r: Status of last TX - OK or ERROR
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * The controller that has IRQ for TX ACK calls this atomic API
1738c2ecf20Sopenharmony_ci * to tick the TX state machine. It works only if txdone_irq
1748c2ecf20Sopenharmony_ci * is set by the controller.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_civoid mbox_chan_txdone(struct mbox_chan *chan, int r)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) {
1798c2ecf20Sopenharmony_ci		dev_err(chan->mbox->dev,
1808c2ecf20Sopenharmony_ci		       "Controller can't run the TX ticker\n");
1818c2ecf20Sopenharmony_ci		return;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	tx_tick(chan, r);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_chan_txdone);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/**
1898c2ecf20Sopenharmony_ci * mbox_client_txdone - The way for a client to run the TX state machine.
1908c2ecf20Sopenharmony_ci * @chan: Mailbox channel assigned to this client.
1918c2ecf20Sopenharmony_ci * @r: Success status of last transmission.
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci * The client/protocol had received some 'ACK' packet and it notifies
1948c2ecf20Sopenharmony_ci * the API that the last packet was sent successfully. This only works
1958c2ecf20Sopenharmony_ci * if the controller can't sense TX-Done.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_civoid mbox_client_txdone(struct mbox_chan *chan, int r)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) {
2008c2ecf20Sopenharmony_ci		dev_err(chan->mbox->dev, "Client can't run the TX ticker\n");
2018c2ecf20Sopenharmony_ci		return;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	tx_tick(chan, r);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_client_txdone);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/**
2098c2ecf20Sopenharmony_ci * mbox_client_peek_data - A way for client driver to pull data
2108c2ecf20Sopenharmony_ci *			received from remote by the controller.
2118c2ecf20Sopenharmony_ci * @chan: Mailbox channel assigned to this client.
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * A poke to controller driver for any received data.
2148c2ecf20Sopenharmony_ci * The data is actually passed onto client via the
2158c2ecf20Sopenharmony_ci * mbox_chan_received_data()
2168c2ecf20Sopenharmony_ci * The call can be made from atomic context, so the controller's
2178c2ecf20Sopenharmony_ci * implementation of peek_data() must not sleep.
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * Return: True, if controller has, and is going to push after this,
2208c2ecf20Sopenharmony_ci *          some data.
2218c2ecf20Sopenharmony_ci *         False, if controller doesn't have any data to be read.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cibool mbox_client_peek_data(struct mbox_chan *chan)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	if (chan->mbox->ops->peek_data)
2268c2ecf20Sopenharmony_ci		return chan->mbox->ops->peek_data(chan);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return false;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_client_peek_data);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/**
2338c2ecf20Sopenharmony_ci * mbox_send_message -	For client to submit a message to be
2348c2ecf20Sopenharmony_ci *				sent to the remote.
2358c2ecf20Sopenharmony_ci * @chan: Mailbox channel assigned to this client.
2368c2ecf20Sopenharmony_ci * @mssg: Client specific message typecasted.
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * For client to submit data to the controller destined for a remote
2398c2ecf20Sopenharmony_ci * processor. If the client had set 'tx_block', the call will return
2408c2ecf20Sopenharmony_ci * either when the remote receives the data or when 'tx_tout' millisecs
2418c2ecf20Sopenharmony_ci * run out.
2428c2ecf20Sopenharmony_ci *  In non-blocking mode, the requests are buffered by the API and a
2438c2ecf20Sopenharmony_ci * non-negative token is returned for each queued request. If the request
2448c2ecf20Sopenharmony_ci * is not queued, a negative token is returned. Upon failure or successful
2458c2ecf20Sopenharmony_ci * TX, the API calls 'tx_done' from atomic context, from which the client
2468c2ecf20Sopenharmony_ci * could submit yet another request.
2478c2ecf20Sopenharmony_ci * The pointer to message should be preserved until it is sent
2488c2ecf20Sopenharmony_ci * over the chan, i.e, tx_done() is made.
2498c2ecf20Sopenharmony_ci * This function could be called from atomic context as it simply
2508c2ecf20Sopenharmony_ci * queues the data and returns a token against the request.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * Return: Non-negative integer for successful submission (non-blocking mode)
2538c2ecf20Sopenharmony_ci *	or transmission over chan (blocking mode).
2548c2ecf20Sopenharmony_ci *	Negative value denotes failure.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_ciint mbox_send_message(struct mbox_chan *chan, void *mssg)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int t;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (!chan || !chan->cl)
2618c2ecf20Sopenharmony_ci		return -EINVAL;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	t = add_to_rbuf(chan, mssg);
2648c2ecf20Sopenharmony_ci	if (t < 0) {
2658c2ecf20Sopenharmony_ci		dev_err(chan->mbox->dev, "Try increasing MBOX_TX_QUEUE_LEN\n");
2668c2ecf20Sopenharmony_ci		return t;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	msg_submit(chan);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (chan->cl->tx_block) {
2728c2ecf20Sopenharmony_ci		unsigned long wait;
2738c2ecf20Sopenharmony_ci		int ret;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (!chan->cl->tx_tout) /* wait forever */
2768c2ecf20Sopenharmony_ci			wait = msecs_to_jiffies(3600000);
2778c2ecf20Sopenharmony_ci		else
2788c2ecf20Sopenharmony_ci			wait = msecs_to_jiffies(chan->cl->tx_tout);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		ret = wait_for_completion_timeout(&chan->tx_complete, wait);
2818c2ecf20Sopenharmony_ci		if (ret == 0) {
2828c2ecf20Sopenharmony_ci			t = -ETIME;
2838c2ecf20Sopenharmony_ci			tx_tick(chan, t);
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return t;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_send_message);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/**
2928c2ecf20Sopenharmony_ci * mbox_flush - flush a mailbox channel
2938c2ecf20Sopenharmony_ci * @chan: mailbox channel to flush
2948c2ecf20Sopenharmony_ci * @timeout: time, in milliseconds, to allow the flush operation to succeed
2958c2ecf20Sopenharmony_ci *
2968c2ecf20Sopenharmony_ci * Mailbox controllers that need to work in atomic context can implement the
2978c2ecf20Sopenharmony_ci * ->flush() callback to busy loop until a transmission has been completed.
2988c2ecf20Sopenharmony_ci * The implementation must call mbox_chan_txdone() upon success. Clients can
2998c2ecf20Sopenharmony_ci * call the mbox_flush() function at any time after mbox_send_message() to
3008c2ecf20Sopenharmony_ci * flush the transmission. After the function returns success, the mailbox
3018c2ecf20Sopenharmony_ci * transmission is guaranteed to have completed.
3028c2ecf20Sopenharmony_ci *
3038c2ecf20Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_ciint mbox_flush(struct mbox_chan *chan, unsigned long timeout)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int ret;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!chan->mbox->ops->flush)
3108c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ret = chan->mbox->ops->flush(chan, timeout);
3138c2ecf20Sopenharmony_ci	if (ret < 0)
3148c2ecf20Sopenharmony_ci		tx_tick(chan, ret);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return ret;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_flush);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci * mbox_request_channel - Request a mailbox channel.
3228c2ecf20Sopenharmony_ci * @cl: Identity of the client requesting the channel.
3238c2ecf20Sopenharmony_ci * @index: Index of mailbox specifier in 'mboxes' property.
3248c2ecf20Sopenharmony_ci *
3258c2ecf20Sopenharmony_ci * The Client specifies its requirements and capabilities while asking for
3268c2ecf20Sopenharmony_ci * a mailbox channel. It can't be called from atomic context.
3278c2ecf20Sopenharmony_ci * The channel is exclusively allocated and can't be used by another
3288c2ecf20Sopenharmony_ci * client before the owner calls mbox_free_channel.
3298c2ecf20Sopenharmony_ci * After assignment, any packet received on this channel will be
3308c2ecf20Sopenharmony_ci * handed over to the client via the 'rx_callback'.
3318c2ecf20Sopenharmony_ci * The framework holds reference to the client, so the mbox_client
3328c2ecf20Sopenharmony_ci * structure shouldn't be modified until the mbox_free_channel returns.
3338c2ecf20Sopenharmony_ci *
3348c2ecf20Sopenharmony_ci * Return: Pointer to the channel assigned to the client if successful.
3358c2ecf20Sopenharmony_ci *		ERR_PTR for request failure.
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_cistruct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct device *dev = cl->dev;
3408c2ecf20Sopenharmony_ci	struct mbox_controller *mbox;
3418c2ecf20Sopenharmony_ci	struct of_phandle_args spec;
3428c2ecf20Sopenharmony_ci	struct mbox_chan *chan;
3438c2ecf20Sopenharmony_ci	unsigned long flags;
3448c2ecf20Sopenharmony_ci	int ret;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (!dev || !dev->of_node) {
3478c2ecf20Sopenharmony_ci		pr_debug("%s: No owner device node\n", __func__);
3488c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	mutex_lock(&con_mutex);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (of_parse_phandle_with_args(dev->of_node, "mboxes",
3548c2ecf20Sopenharmony_ci				       "#mbox-cells", index, &spec)) {
3558c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: can't parse \"mboxes\" property\n", __func__);
3568c2ecf20Sopenharmony_ci		mutex_unlock(&con_mutex);
3578c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	chan = ERR_PTR(-EPROBE_DEFER);
3618c2ecf20Sopenharmony_ci	list_for_each_entry(mbox, &mbox_cons, node)
3628c2ecf20Sopenharmony_ci		if (mbox->dev->of_node == spec.np) {
3638c2ecf20Sopenharmony_ci			chan = mbox->of_xlate(mbox, &spec);
3648c2ecf20Sopenharmony_ci			if (!IS_ERR(chan))
3658c2ecf20Sopenharmony_ci				break;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	of_node_put(spec.np);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (IS_ERR(chan)) {
3718c2ecf20Sopenharmony_ci		mutex_unlock(&con_mutex);
3728c2ecf20Sopenharmony_ci		return chan;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
3768c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: mailbox not free\n", __func__);
3778c2ecf20Sopenharmony_ci		mutex_unlock(&con_mutex);
3788c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
3828c2ecf20Sopenharmony_ci	chan->msg_free = 0;
3838c2ecf20Sopenharmony_ci	chan->msg_count = 0;
3848c2ecf20Sopenharmony_ci	chan->active_req = NULL;
3858c2ecf20Sopenharmony_ci	chan->cl = cl;
3868c2ecf20Sopenharmony_ci	init_completion(&chan->tx_complete);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (chan->txdone_method	== TXDONE_BY_POLL && cl->knows_txdone)
3898c2ecf20Sopenharmony_ci		chan->txdone_method = TXDONE_BY_ACK;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (chan->mbox->ops->startup) {
3948c2ecf20Sopenharmony_ci		ret = chan->mbox->ops->startup(chan);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (ret) {
3978c2ecf20Sopenharmony_ci			dev_err(dev, "Unable to startup the chan (%d)\n", ret);
3988c2ecf20Sopenharmony_ci			mbox_free_channel(chan);
3998c2ecf20Sopenharmony_ci			chan = ERR_PTR(ret);
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	mutex_unlock(&con_mutex);
4048c2ecf20Sopenharmony_ci	return chan;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_request_channel);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistruct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
4098c2ecf20Sopenharmony_ci					      const char *name)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct device_node *np = cl->dev->of_node;
4128c2ecf20Sopenharmony_ci	struct property *prop;
4138c2ecf20Sopenharmony_ci	const char *mbox_name;
4148c2ecf20Sopenharmony_ci	int index = 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (!np) {
4178c2ecf20Sopenharmony_ci		dev_err(cl->dev, "%s() currently only supports DT\n", __func__);
4188c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (!of_get_property(np, "mbox-names", NULL)) {
4228c2ecf20Sopenharmony_ci		dev_err(cl->dev,
4238c2ecf20Sopenharmony_ci			"%s() requires an \"mbox-names\" property\n", __func__);
4248c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	of_property_for_each_string(np, "mbox-names", prop, mbox_name) {
4288c2ecf20Sopenharmony_ci		if (!strncmp(name, mbox_name, strlen(name)))
4298c2ecf20Sopenharmony_ci			return mbox_request_channel(cl, index);
4308c2ecf20Sopenharmony_ci		index++;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	dev_err(cl->dev, "%s() could not locate channel named \"%s\"\n",
4348c2ecf20Sopenharmony_ci		__func__, name);
4358c2ecf20Sopenharmony_ci	return ERR_PTR(-EINVAL);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_request_channel_byname);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/**
4408c2ecf20Sopenharmony_ci * mbox_free_channel - The client relinquishes control of a mailbox
4418c2ecf20Sopenharmony_ci *			channel by this call.
4428c2ecf20Sopenharmony_ci * @chan: The mailbox channel to be freed.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_civoid mbox_free_channel(struct mbox_chan *chan)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	unsigned long flags;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (!chan || !chan->cl)
4498c2ecf20Sopenharmony_ci		return;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (chan->mbox->ops->shutdown)
4528c2ecf20Sopenharmony_ci		chan->mbox->ops->shutdown(chan);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* The queued TX requests are simply aborted, no callbacks are made */
4558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
4568c2ecf20Sopenharmony_ci	chan->cl = NULL;
4578c2ecf20Sopenharmony_ci	chan->active_req = NULL;
4588c2ecf20Sopenharmony_ci	if (chan->txdone_method == TXDONE_BY_ACK)
4598c2ecf20Sopenharmony_ci		chan->txdone_method = TXDONE_BY_POLL;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	module_put(chan->mbox->dev->driver->owner);
4628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_free_channel);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic struct mbox_chan *
4678c2ecf20Sopenharmony_ciof_mbox_index_xlate(struct mbox_controller *mbox,
4688c2ecf20Sopenharmony_ci		    const struct of_phandle_args *sp)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int ind = sp->args[0];
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (ind >= mbox->num_chans)
4738c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return &mbox->chans[ind];
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/**
4798c2ecf20Sopenharmony_ci * mbox_controller_register - Register the mailbox controller
4808c2ecf20Sopenharmony_ci * @mbox:	Pointer to the mailbox controller.
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * The controller driver registers its communication channels
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_ciint mbox_controller_register(struct mbox_controller *mbox)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	int i, txdone;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* Sanity check */
4898c2ecf20Sopenharmony_ci	if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans)
4908c2ecf20Sopenharmony_ci		return -EINVAL;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (mbox->txdone_irq)
4938c2ecf20Sopenharmony_ci		txdone = TXDONE_BY_IRQ;
4948c2ecf20Sopenharmony_ci	else if (mbox->txdone_poll)
4958c2ecf20Sopenharmony_ci		txdone = TXDONE_BY_POLL;
4968c2ecf20Sopenharmony_ci	else /* It has to be ACK then */
4978c2ecf20Sopenharmony_ci		txdone = TXDONE_BY_ACK;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (txdone == TXDONE_BY_POLL) {
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		if (!mbox->ops->last_tx_done) {
5028c2ecf20Sopenharmony_ci			dev_err(mbox->dev, "last_tx_done method is absent\n");
5038c2ecf20Sopenharmony_ci			return -EINVAL;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		hrtimer_init(&mbox->poll_hrt, CLOCK_MONOTONIC,
5078c2ecf20Sopenharmony_ci			     HRTIMER_MODE_REL);
5088c2ecf20Sopenharmony_ci		mbox->poll_hrt.function = txdone_hrtimer;
5098c2ecf20Sopenharmony_ci		spin_lock_init(&mbox->poll_hrt_lock);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	for (i = 0; i < mbox->num_chans; i++) {
5138c2ecf20Sopenharmony_ci		struct mbox_chan *chan = &mbox->chans[i];
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		chan->cl = NULL;
5168c2ecf20Sopenharmony_ci		chan->mbox = mbox;
5178c2ecf20Sopenharmony_ci		chan->txdone_method = txdone;
5188c2ecf20Sopenharmony_ci		spin_lock_init(&chan->lock);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!mbox->of_xlate)
5228c2ecf20Sopenharmony_ci		mbox->of_xlate = of_mbox_index_xlate;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	mutex_lock(&con_mutex);
5258c2ecf20Sopenharmony_ci	list_add_tail(&mbox->node, &mbox_cons);
5268c2ecf20Sopenharmony_ci	mutex_unlock(&con_mutex);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return 0;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_controller_register);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/**
5338c2ecf20Sopenharmony_ci * mbox_controller_unregister - Unregister the mailbox controller
5348c2ecf20Sopenharmony_ci * @mbox:	Pointer to the mailbox controller.
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_civoid mbox_controller_unregister(struct mbox_controller *mbox)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	int i;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (!mbox)
5418c2ecf20Sopenharmony_ci		return;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	mutex_lock(&con_mutex);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	list_del(&mbox->node);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	for (i = 0; i < mbox->num_chans; i++)
5488c2ecf20Sopenharmony_ci		mbox_free_channel(&mbox->chans[i]);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (mbox->txdone_poll)
5518c2ecf20Sopenharmony_ci		hrtimer_cancel(&mbox->poll_hrt);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	mutex_unlock(&con_mutex);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mbox_controller_unregister);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic void __devm_mbox_controller_unregister(struct device *dev, void *res)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct mbox_controller **mbox = res;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	mbox_controller_unregister(*mbox);
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int devm_mbox_controller_match(struct device *dev, void *res, void *data)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct mbox_controller **mbox = res;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (WARN_ON(!mbox || !*mbox))
5698c2ecf20Sopenharmony_ci		return 0;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	return *mbox == data;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci/**
5758c2ecf20Sopenharmony_ci * devm_mbox_controller_register() - managed mbox_controller_register()
5768c2ecf20Sopenharmony_ci * @dev: device owning the mailbox controller being registered
5778c2ecf20Sopenharmony_ci * @mbox: mailbox controller being registered
5788c2ecf20Sopenharmony_ci *
5798c2ecf20Sopenharmony_ci * This function adds a device-managed resource that will make sure that the
5808c2ecf20Sopenharmony_ci * mailbox controller, which is registered using mbox_controller_register()
5818c2ecf20Sopenharmony_ci * as part of this function, will be unregistered along with the rest of
5828c2ecf20Sopenharmony_ci * device-managed resources upon driver probe failure or driver removal.
5838c2ecf20Sopenharmony_ci *
5848c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error code on failure.
5858c2ecf20Sopenharmony_ci */
5868c2ecf20Sopenharmony_ciint devm_mbox_controller_register(struct device *dev,
5878c2ecf20Sopenharmony_ci				  struct mbox_controller *mbox)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct mbox_controller **ptr;
5908c2ecf20Sopenharmony_ci	int err;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	ptr = devres_alloc(__devm_mbox_controller_unregister, sizeof(*ptr),
5938c2ecf20Sopenharmony_ci			   GFP_KERNEL);
5948c2ecf20Sopenharmony_ci	if (!ptr)
5958c2ecf20Sopenharmony_ci		return -ENOMEM;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	err = mbox_controller_register(mbox);
5988c2ecf20Sopenharmony_ci	if (err < 0) {
5998c2ecf20Sopenharmony_ci		devres_free(ptr);
6008c2ecf20Sopenharmony_ci		return err;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	devres_add(dev, ptr);
6048c2ecf20Sopenharmony_ci	*ptr = mbox;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_mbox_controller_register);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/**
6118c2ecf20Sopenharmony_ci * devm_mbox_controller_unregister() - managed mbox_controller_unregister()
6128c2ecf20Sopenharmony_ci * @dev: device owning the mailbox controller being unregistered
6138c2ecf20Sopenharmony_ci * @mbox: mailbox controller being unregistered
6148c2ecf20Sopenharmony_ci *
6158c2ecf20Sopenharmony_ci * This function unregisters the mailbox controller and removes the device-
6168c2ecf20Sopenharmony_ci * managed resource that was set up to automatically unregister the mailbox
6178c2ecf20Sopenharmony_ci * controller on driver probe failure or driver removal. It's typically not
6188c2ecf20Sopenharmony_ci * necessary to call this function.
6198c2ecf20Sopenharmony_ci */
6208c2ecf20Sopenharmony_civoid devm_mbox_controller_unregister(struct device *dev, struct mbox_controller *mbox)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	WARN_ON(devres_release(dev, __devm_mbox_controller_unregister,
6238c2ecf20Sopenharmony_ci			       devm_mbox_controller_match, mbox));
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_mbox_controller_unregister);
626