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