xref: /openbmc/linux/drivers/mailbox/mailbox-test.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28ea4484dSLee Jones /*
38ea4484dSLee Jones  * Copyright (C) 2015 ST Microelectronics
48ea4484dSLee Jones  *
58ea4484dSLee Jones  * Author: Lee Jones <lee.jones@linaro.org>
68ea4484dSLee Jones  */
78ea4484dSLee Jones 
88ea4484dSLee Jones #include <linux/debugfs.h>
98ea4484dSLee Jones #include <linux/err.h>
10baef9a35SSudeep Holla #include <linux/fs.h>
11a133f8b6SLee Jones #include <linux/io.h>
128ea4484dSLee Jones #include <linux/kernel.h>
138ea4484dSLee Jones #include <linux/mailbox_client.h>
148ea4484dSLee Jones #include <linux/module.h>
152d1e952aSLee Jones #include <linux/mutex.h>
168ea4484dSLee Jones #include <linux/of.h>
178ea4484dSLee Jones #include <linux/platform_device.h>
18baef9a35SSudeep Holla #include <linux/poll.h>
198ea4484dSLee Jones #include <linux/slab.h>
20be884585SLee Jones #include <linux/spinlock.h>
218ea4484dSLee Jones #include <linux/uaccess.h>
22174cd4b1SIngo Molnar #include <linux/sched/signal.h>
238ea4484dSLee Jones 
248ea4484dSLee Jones #define MBOX_MAX_SIG_LEN	8
258ea4484dSLee Jones #define MBOX_MAX_MSG_LEN	128
268ea4484dSLee Jones #define MBOX_BYTES_PER_LINE	16
278ea4484dSLee Jones #define MBOX_HEXDUMP_LINE_LEN	((MBOX_BYTES_PER_LINE * 4) + 2)
288ea4484dSLee Jones #define MBOX_HEXDUMP_MAX_LEN	(MBOX_HEXDUMP_LINE_LEN *		\
298ea4484dSLee Jones 				 (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
308ea4484dSLee Jones 
31e339c80aSSudeep Holla static bool mbox_data_ready;
328ea4484dSLee Jones 
338ea4484dSLee Jones struct mbox_test_device {
348ea4484dSLee Jones 	struct device		*dev;
352d74ffdcSSudeep Holla 	void __iomem		*tx_mmio;
362d74ffdcSSudeep Holla 	void __iomem		*rx_mmio;
378ea4484dSLee Jones 	struct mbox_chan	*tx_channel;
388ea4484dSLee Jones 	struct mbox_chan	*rx_channel;
398ea4484dSLee Jones 	char			*rx_buffer;
408ea4484dSLee Jones 	char			*signal;
418ea4484dSLee Jones 	char			*message;
428ea4484dSLee Jones 	spinlock_t		lock;
432d1e952aSLee Jones 	struct mutex		mutex;
44baef9a35SSudeep Holla 	wait_queue_head_t	waitq;
45baef9a35SSudeep Holla 	struct fasync_struct	*async_queue;
4610cfc5a9SFabien Dessenne 	struct dentry		*root_debugfs_dir;
478ea4484dSLee Jones };
488ea4484dSLee Jones 
mbox_test_signal_write(struct file * filp,const char __user * userbuf,size_t count,loff_t * ppos)498ea4484dSLee Jones static ssize_t mbox_test_signal_write(struct file *filp,
508ea4484dSLee Jones 				       const char __user *userbuf,
518ea4484dSLee Jones 				       size_t count, loff_t *ppos)
528ea4484dSLee Jones {
538ea4484dSLee Jones 	struct mbox_test_device *tdev = filp->private_data;
548ea4484dSLee Jones 
558ea4484dSLee Jones 	if (!tdev->tx_channel) {
568ea4484dSLee Jones 		dev_err(tdev->dev, "Channel cannot do Tx\n");
578ea4484dSLee Jones 		return -EINVAL;
588ea4484dSLee Jones 	}
598ea4484dSLee Jones 
608ea4484dSLee Jones 	if (count > MBOX_MAX_SIG_LEN) {
618ea4484dSLee Jones 		dev_err(tdev->dev,
626c03663fSLee Jones 			"Signal length %zd greater than max allowed %d\n",
638ea4484dSLee Jones 			count, MBOX_MAX_SIG_LEN);
648ea4484dSLee Jones 		return -EINVAL;
658ea4484dSLee Jones 	}
668ea4484dSLee Jones 
67d1c2f87cSLee Jones 	/* Only allocate memory if we need to */
68d1c2f87cSLee Jones 	if (!tdev->signal) {
698ea4484dSLee Jones 		tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL);
708ea4484dSLee Jones 		if (!tdev->signal)
718ea4484dSLee Jones 			return -ENOMEM;
72d1c2f87cSLee Jones 	}
738ea4484dSLee Jones 
7417f5f28fSLee Jones 	if (copy_from_user(tdev->signal, userbuf, count)) {
758ea4484dSLee Jones 		kfree(tdev->signal);
7617f5f28fSLee Jones 		tdev->signal = NULL;
778ea4484dSLee Jones 		return -EFAULT;
788ea4484dSLee Jones 	}
798ea4484dSLee Jones 
8017f5f28fSLee Jones 	return count;
818ea4484dSLee Jones }
828ea4484dSLee Jones 
838ea4484dSLee Jones static const struct file_operations mbox_test_signal_ops = {
848ea4484dSLee Jones 	.write	= mbox_test_signal_write,
858ea4484dSLee Jones 	.open	= simple_open,
868ea4484dSLee Jones 	.llseek	= generic_file_llseek,
878ea4484dSLee Jones };
888ea4484dSLee Jones 
mbox_test_message_fasync(int fd,struct file * filp,int on)89baef9a35SSudeep Holla static int mbox_test_message_fasync(int fd, struct file *filp, int on)
90baef9a35SSudeep Holla {
91baef9a35SSudeep Holla 	struct mbox_test_device *tdev = filp->private_data;
92baef9a35SSudeep Holla 
93baef9a35SSudeep Holla 	return fasync_helper(fd, filp, on, &tdev->async_queue);
94baef9a35SSudeep Holla }
95baef9a35SSudeep Holla 
mbox_test_message_write(struct file * filp,const char __user * userbuf,size_t count,loff_t * ppos)968ea4484dSLee Jones static ssize_t mbox_test_message_write(struct file *filp,
978ea4484dSLee Jones 				       const char __user *userbuf,
988ea4484dSLee Jones 				       size_t count, loff_t *ppos)
998ea4484dSLee Jones {
1008ea4484dSLee Jones 	struct mbox_test_device *tdev = filp->private_data;
1018fe72b76SDan Carpenter 	char *message;
1028ea4484dSLee Jones 	void *data;
1038ea4484dSLee Jones 	int ret;
1048ea4484dSLee Jones 
1058ea4484dSLee Jones 	if (!tdev->tx_channel) {
1068ea4484dSLee Jones 		dev_err(tdev->dev, "Channel cannot do Tx\n");
1078ea4484dSLee Jones 		return -EINVAL;
1088ea4484dSLee Jones 	}
1098ea4484dSLee Jones 
1108ea4484dSLee Jones 	if (count > MBOX_MAX_MSG_LEN) {
1118ea4484dSLee Jones 		dev_err(tdev->dev,
1126c03663fSLee Jones 			"Message length %zd greater than max allowed %d\n",
1138ea4484dSLee Jones 			count, MBOX_MAX_MSG_LEN);
1148ea4484dSLee Jones 		return -EINVAL;
1158ea4484dSLee Jones 	}
1168ea4484dSLee Jones 
1178fe72b76SDan Carpenter 	message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL);
1188fe72b76SDan Carpenter 	if (!message)
1198ea4484dSLee Jones 		return -ENOMEM;
1208ea4484dSLee Jones 
1218fe72b76SDan Carpenter 	mutex_lock(&tdev->mutex);
1228fe72b76SDan Carpenter 
1238fe72b76SDan Carpenter 	tdev->message = message;
1248ea4484dSLee Jones 	ret = copy_from_user(tdev->message, userbuf, count);
1258ea4484dSLee Jones 	if (ret) {
1268ea4484dSLee Jones 		ret = -EFAULT;
1278ea4484dSLee Jones 		goto out;
1288ea4484dSLee Jones 	}
1298ea4484dSLee Jones 
1308ea4484dSLee Jones 	/*
1318ea4484dSLee Jones 	 * A separate signal is only of use if there is
1328ea4484dSLee Jones 	 * MMIO to subsequently pass the message through
1338ea4484dSLee Jones 	 */
1342d74ffdcSSudeep Holla 	if (tdev->tx_mmio && tdev->signal) {
13527fa680fSSudeep Holla 		print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS,
13627fa680fSSudeep Holla 				     tdev->signal, MBOX_MAX_SIG_LEN);
1378ea4484dSLee Jones 
1388ea4484dSLee Jones 		data = tdev->signal;
1398ea4484dSLee Jones 	} else
1408ea4484dSLee Jones 		data = tdev->message;
1418ea4484dSLee Jones 
14227fa680fSSudeep Holla 	print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS,
14327fa680fSSudeep Holla 			     tdev->message, MBOX_MAX_MSG_LEN);
1448ea4484dSLee Jones 
1458ea4484dSLee Jones 	ret = mbox_send_message(tdev->tx_channel, data);
1468ea4484dSLee Jones 	if (ret < 0)
1478ea4484dSLee Jones 		dev_err(tdev->dev, "Failed to send message via mailbox\n");
1488ea4484dSLee Jones 
1498ea4484dSLee Jones out:
1508ea4484dSLee Jones 	kfree(tdev->signal);
1518ea4484dSLee Jones 	kfree(tdev->message);
1529ef3c511SSudeep Holla 	tdev->signal = NULL;
1538ea4484dSLee Jones 
1542d1e952aSLee Jones 	mutex_unlock(&tdev->mutex);
1552d1e952aSLee Jones 
1568ea4484dSLee Jones 	return ret < 0 ? ret : count;
1578ea4484dSLee Jones }
1588ea4484dSLee Jones 
mbox_test_message_data_ready(struct mbox_test_device * tdev)159baef9a35SSudeep Holla static bool mbox_test_message_data_ready(struct mbox_test_device *tdev)
160baef9a35SSudeep Holla {
161e339c80aSSudeep Holla 	bool data_ready;
162baef9a35SSudeep Holla 	unsigned long flags;
163baef9a35SSudeep Holla 
164baef9a35SSudeep Holla 	spin_lock_irqsave(&tdev->lock, flags);
165e339c80aSSudeep Holla 	data_ready = mbox_data_ready;
166baef9a35SSudeep Holla 	spin_unlock_irqrestore(&tdev->lock, flags);
167baef9a35SSudeep Holla 
168e339c80aSSudeep Holla 	return data_ready;
169baef9a35SSudeep Holla }
170baef9a35SSudeep Holla 
mbox_test_message_read(struct file * filp,char __user * userbuf,size_t count,loff_t * ppos)1718ea4484dSLee Jones static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,
1728ea4484dSLee Jones 				      size_t count, loff_t *ppos)
1738ea4484dSLee Jones {
1748ea4484dSLee Jones 	struct mbox_test_device *tdev = filp->private_data;
1758ea4484dSLee Jones 	unsigned long flags;
1768ea4484dSLee Jones 	char *touser, *ptr;
1778ea4484dSLee Jones 	int l = 0;
1788ea4484dSLee Jones 	int ret;
1798ea4484dSLee Jones 
180baef9a35SSudeep Holla 	DECLARE_WAITQUEUE(wait, current);
181baef9a35SSudeep Holla 
182c3ac54a6SDan Carpenter 	touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL);
1838ea4484dSLee Jones 	if (!touser)
1848ea4484dSLee Jones 		return -ENOMEM;
1858ea4484dSLee Jones 
1868ea4484dSLee Jones 	if (!tdev->rx_channel) {
1878ea4484dSLee Jones 		ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n");
1888ea4484dSLee Jones 		ret = simple_read_from_buffer(userbuf, count, ppos,
1898ea4484dSLee Jones 					      touser, ret);
190baef9a35SSudeep Holla 		goto kfree_err;
1918ea4484dSLee Jones 	}
1928ea4484dSLee Jones 
193baef9a35SSudeep Holla 	add_wait_queue(&tdev->waitq, &wait);
194baef9a35SSudeep Holla 
195baef9a35SSudeep Holla 	do {
196baef9a35SSudeep Holla 		__set_current_state(TASK_INTERRUPTIBLE);
197baef9a35SSudeep Holla 
198baef9a35SSudeep Holla 		if (mbox_test_message_data_ready(tdev))
199baef9a35SSudeep Holla 			break;
200baef9a35SSudeep Holla 
201baef9a35SSudeep Holla 		if (filp->f_flags & O_NONBLOCK) {
202baef9a35SSudeep Holla 			ret = -EAGAIN;
203baef9a35SSudeep Holla 			goto waitq_err;
2048ea4484dSLee Jones 		}
2058ea4484dSLee Jones 
206baef9a35SSudeep Holla 		if (signal_pending(current)) {
207baef9a35SSudeep Holla 			ret = -ERESTARTSYS;
208baef9a35SSudeep Holla 			goto waitq_err;
209baef9a35SSudeep Holla 		}
210baef9a35SSudeep Holla 		schedule();
211baef9a35SSudeep Holla 
212baef9a35SSudeep Holla 	} while (1);
213baef9a35SSudeep Holla 
2148ea4484dSLee Jones 	spin_lock_irqsave(&tdev->lock, flags);
2158ea4484dSLee Jones 
2168ea4484dSLee Jones 	ptr = tdev->rx_buffer;
2178ea4484dSLee Jones 	while (l < MBOX_HEXDUMP_MAX_LEN) {
2188ea4484dSLee Jones 		hex_dump_to_buffer(ptr,
2198ea4484dSLee Jones 				   MBOX_BYTES_PER_LINE,
2208ea4484dSLee Jones 				   MBOX_BYTES_PER_LINE, 1, touser + l,
2218ea4484dSLee Jones 				   MBOX_HEXDUMP_LINE_LEN, true);
2228ea4484dSLee Jones 
2238ea4484dSLee Jones 		ptr += MBOX_BYTES_PER_LINE;
2248ea4484dSLee Jones 		l += MBOX_HEXDUMP_LINE_LEN;
2258ea4484dSLee Jones 		*(touser + (l - 1)) = '\n';
2268ea4484dSLee Jones 	}
2278ea4484dSLee Jones 	*(touser + l) = '\0';
2288ea4484dSLee Jones 
2298ea4484dSLee Jones 	memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN);
230e339c80aSSudeep Holla 	mbox_data_ready = false;
2318ea4484dSLee Jones 
2328ea4484dSLee Jones 	spin_unlock_irqrestore(&tdev->lock, flags);
2338ea4484dSLee Jones 
2348ea4484dSLee Jones 	ret = simple_read_from_buffer(userbuf, count, ppos, touser, MBOX_HEXDUMP_MAX_LEN);
235baef9a35SSudeep Holla waitq_err:
236baef9a35SSudeep Holla 	__set_current_state(TASK_RUNNING);
237baef9a35SSudeep Holla 	remove_wait_queue(&tdev->waitq, &wait);
238baef9a35SSudeep Holla kfree_err:
2398ea4484dSLee Jones 	kfree(touser);
2408ea4484dSLee Jones 	return ret;
2418ea4484dSLee Jones }
2428ea4484dSLee Jones 
243afc9a42bSAl Viro static __poll_t
mbox_test_message_poll(struct file * filp,struct poll_table_struct * wait)244baef9a35SSudeep Holla mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait)
245baef9a35SSudeep Holla {
246baef9a35SSudeep Holla 	struct mbox_test_device *tdev = filp->private_data;
247baef9a35SSudeep Holla 
248baef9a35SSudeep Holla 	poll_wait(filp, &tdev->waitq, wait);
249baef9a35SSudeep Holla 
250baef9a35SSudeep Holla 	if (mbox_test_message_data_ready(tdev))
251a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
252baef9a35SSudeep Holla 	return 0;
253baef9a35SSudeep Holla }
254baef9a35SSudeep Holla 
2558ea4484dSLee Jones static const struct file_operations mbox_test_message_ops = {
2568ea4484dSLee Jones 	.write	= mbox_test_message_write,
2578ea4484dSLee Jones 	.read	= mbox_test_message_read,
258baef9a35SSudeep Holla 	.fasync	= mbox_test_message_fasync,
259baef9a35SSudeep Holla 	.poll	= mbox_test_message_poll,
2608ea4484dSLee Jones 	.open	= simple_open,
2618ea4484dSLee Jones 	.llseek	= generic_file_llseek,
2628ea4484dSLee Jones };
2638ea4484dSLee Jones 
mbox_test_add_debugfs(struct platform_device * pdev,struct mbox_test_device * tdev)2648ea4484dSLee Jones static int mbox_test_add_debugfs(struct platform_device *pdev,
2658ea4484dSLee Jones 				 struct mbox_test_device *tdev)
2668ea4484dSLee Jones {
2678ea4484dSLee Jones 	if (!debugfs_initialized())
2688ea4484dSLee Jones 		return 0;
2698ea4484dSLee Jones 
27010cfc5a9SFabien Dessenne 	tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL);
27110cfc5a9SFabien Dessenne 	if (!tdev->root_debugfs_dir) {
2728ea4484dSLee Jones 		dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
2738ea4484dSLee Jones 		return -EINVAL;
2748ea4484dSLee Jones 	}
2758ea4484dSLee Jones 
27610cfc5a9SFabien Dessenne 	debugfs_create_file("message", 0600, tdev->root_debugfs_dir,
2778ea4484dSLee Jones 			    tdev, &mbox_test_message_ops);
2788ea4484dSLee Jones 
27910cfc5a9SFabien Dessenne 	debugfs_create_file("signal", 0200, tdev->root_debugfs_dir,
2808ea4484dSLee Jones 			    tdev, &mbox_test_signal_ops);
2818ea4484dSLee Jones 
2828ea4484dSLee Jones 	return 0;
2838ea4484dSLee Jones }
2848ea4484dSLee Jones 
mbox_test_receive_message(struct mbox_client * client,void * message)2858ea4484dSLee Jones static void mbox_test_receive_message(struct mbox_client *client, void *message)
2868ea4484dSLee Jones {
2878ea4484dSLee Jones 	struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
2888ea4484dSLee Jones 	unsigned long flags;
2898ea4484dSLee Jones 
2908ea4484dSLee Jones 	spin_lock_irqsave(&tdev->lock, flags);
2912d74ffdcSSudeep Holla 	if (tdev->rx_mmio) {
2922d74ffdcSSudeep Holla 		memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN);
29327fa680fSSudeep Holla 		print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS,
29427fa680fSSudeep Holla 				     tdev->rx_buffer, MBOX_MAX_MSG_LEN);
2958ea4484dSLee Jones 	} else if (message) {
29627fa680fSSudeep Holla 		print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS,
29727fa680fSSudeep Holla 				     message, MBOX_MAX_MSG_LEN);
2988ea4484dSLee Jones 		memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN);
2998ea4484dSLee Jones 	}
300e339c80aSSudeep Holla 	mbox_data_ready = true;
3018ea4484dSLee Jones 	spin_unlock_irqrestore(&tdev->lock, flags);
302baef9a35SSudeep Holla 
303baef9a35SSudeep Holla 	wake_up_interruptible(&tdev->waitq);
304baef9a35SSudeep Holla 
305baef9a35SSudeep Holla 	kill_fasync(&tdev->async_queue, SIGIO, POLL_IN);
3068ea4484dSLee Jones }
3078ea4484dSLee Jones 
mbox_test_prepare_message(struct mbox_client * client,void * message)3088ea4484dSLee Jones static void mbox_test_prepare_message(struct mbox_client *client, void *message)
3098ea4484dSLee Jones {
3108ea4484dSLee Jones 	struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
3118ea4484dSLee Jones 
3122d74ffdcSSudeep Holla 	if (tdev->tx_mmio) {
3138ea4484dSLee Jones 		if (tdev->signal)
3142d74ffdcSSudeep Holla 			memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN);
3158ea4484dSLee Jones 		else
3162d74ffdcSSudeep Holla 			memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN);
3178ea4484dSLee Jones 	}
3188ea4484dSLee Jones }
3198ea4484dSLee Jones 
mbox_test_message_sent(struct mbox_client * client,void * message,int r)3208ea4484dSLee Jones static void mbox_test_message_sent(struct mbox_client *client,
3218ea4484dSLee Jones 				   void *message, int r)
3228ea4484dSLee Jones {
3238ea4484dSLee Jones 	if (r)
3248ea4484dSLee Jones 		dev_warn(client->dev,
3258ea4484dSLee Jones 			 "Client: Message could not be sent: %d\n", r);
3268ea4484dSLee Jones 	else
3278ea4484dSLee Jones 		dev_info(client->dev,
3288ea4484dSLee Jones 			 "Client: Message sent\n");
3298ea4484dSLee Jones }
3308ea4484dSLee Jones 
3318ea4484dSLee Jones static struct mbox_chan *
mbox_test_request_channel(struct platform_device * pdev,const char * name)3328ea4484dSLee Jones mbox_test_request_channel(struct platform_device *pdev, const char *name)
3338ea4484dSLee Jones {
3348ea4484dSLee Jones 	struct mbox_client *client;
3358ea4484dSLee Jones 	struct mbox_chan *channel;
3368ea4484dSLee Jones 
3378ea4484dSLee Jones 	client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
3388ea4484dSLee Jones 	if (!client)
3398ea4484dSLee Jones 		return ERR_PTR(-ENOMEM);
3408ea4484dSLee Jones 
3418ea4484dSLee Jones 	client->dev		= &pdev->dev;
3428ea4484dSLee Jones 	client->rx_callback	= mbox_test_receive_message;
3438ea4484dSLee Jones 	client->tx_prepare	= mbox_test_prepare_message;
3448ea4484dSLee Jones 	client->tx_done		= mbox_test_message_sent;
3458ea4484dSLee Jones 	client->tx_block	= true;
3468ea4484dSLee Jones 	client->knows_txdone	= false;
3478ea4484dSLee Jones 	client->tx_tout		= 500;
3488ea4484dSLee Jones 
3498ea4484dSLee Jones 	channel = mbox_request_channel_byname(client, name);
3508ea4484dSLee Jones 	if (IS_ERR(channel)) {
3518ea4484dSLee Jones 		dev_warn(&pdev->dev, "Failed to request %s channel\n", name);
3528ea4484dSLee Jones 		return NULL;
3538ea4484dSLee Jones 	}
3548ea4484dSLee Jones 
3558ea4484dSLee Jones 	return channel;
3568ea4484dSLee Jones }
3578ea4484dSLee Jones 
mbox_test_probe(struct platform_device * pdev)3588ea4484dSLee Jones static int mbox_test_probe(struct platform_device *pdev)
3598ea4484dSLee Jones {
3608ea4484dSLee Jones 	struct mbox_test_device *tdev;
3618ea4484dSLee Jones 	struct resource *res;
362db4d22c0SSudeep Holla 	resource_size_t size;
3638ea4484dSLee Jones 	int ret;
3648ea4484dSLee Jones 
3658ea4484dSLee Jones 	tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
3668ea4484dSLee Jones 	if (!tdev)
3678ea4484dSLee Jones 		return -ENOMEM;
3688ea4484dSLee Jones 
3698ea4484dSLee Jones 	/* It's okay for MMIO to be NULL */
370f7fdb53cSYangtao Li 	tdev->tx_mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
3716899b4f7SFabien Dessenne 	if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
372db4d22c0SSudeep Holla 		/* if reserved area in SRAM, try just ioremap */
3736899b4f7SFabien Dessenne 		size = resource_size(res);
374db4d22c0SSudeep Holla 		tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
3756899b4f7SFabien Dessenne 	} else if (IS_ERR(tdev->tx_mmio)) {
3762d74ffdcSSudeep Holla 		tdev->tx_mmio = NULL;
3776899b4f7SFabien Dessenne 	}
3782d74ffdcSSudeep Holla 
3792d74ffdcSSudeep Holla 	/* If specified, second reg entry is Rx MMIO */
380f7fdb53cSYangtao Li 	tdev->rx_mmio = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
3816899b4f7SFabien Dessenne 	if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
3826899b4f7SFabien Dessenne 		size = resource_size(res);
383db4d22c0SSudeep Holla 		tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
3846899b4f7SFabien Dessenne 	} else if (IS_ERR(tdev->rx_mmio)) {
3852d74ffdcSSudeep Holla 		tdev->rx_mmio = tdev->tx_mmio;
3866899b4f7SFabien Dessenne 	}
3878ea4484dSLee Jones 
3888ea4484dSLee Jones 	tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
3898ea4484dSLee Jones 	tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
3908ea4484dSLee Jones 
391*9b63a810SMinjie Du 	if (IS_ERR_OR_NULL(tdev->tx_channel) && IS_ERR_OR_NULL(tdev->rx_channel))
3928ea4484dSLee Jones 		return -EPROBE_DEFER;
3938ea4484dSLee Jones 
3942d74ffdcSSudeep Holla 	/* If Rx is not specified but has Rx MMIO, then Rx = Tx */
3952d74ffdcSSudeep Holla 	if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio))
3962d74ffdcSSudeep Holla 		tdev->rx_channel = tdev->tx_channel;
3972d74ffdcSSudeep Holla 
3988ea4484dSLee Jones 	tdev->dev = &pdev->dev;
3998ea4484dSLee Jones 	platform_set_drvdata(pdev, tdev);
4008ea4484dSLee Jones 
4018ea4484dSLee Jones 	spin_lock_init(&tdev->lock);
4022d1e952aSLee Jones 	mutex_init(&tdev->mutex);
4038ea4484dSLee Jones 
4048ea4484dSLee Jones 	if (tdev->rx_channel) {
4058ea4484dSLee Jones 		tdev->rx_buffer = devm_kzalloc(&pdev->dev,
4068ea4484dSLee Jones 					       MBOX_MAX_MSG_LEN, GFP_KERNEL);
4078ea4484dSLee Jones 		if (!tdev->rx_buffer)
4088ea4484dSLee Jones 			return -ENOMEM;
4098ea4484dSLee Jones 	}
4108ea4484dSLee Jones 
4118ea4484dSLee Jones 	ret = mbox_test_add_debugfs(pdev, tdev);
4128ea4484dSLee Jones 	if (ret)
4138ea4484dSLee Jones 		return ret;
4148ea4484dSLee Jones 
415baef9a35SSudeep Holla 	init_waitqueue_head(&tdev->waitq);
4168ea4484dSLee Jones 	dev_info(&pdev->dev, "Successfully registered\n");
4178ea4484dSLee Jones 
4188ea4484dSLee Jones 	return 0;
4198ea4484dSLee Jones }
4208ea4484dSLee Jones 
mbox_test_remove(struct platform_device * pdev)4218ea4484dSLee Jones static int mbox_test_remove(struct platform_device *pdev)
4228ea4484dSLee Jones {
4238ea4484dSLee Jones 	struct mbox_test_device *tdev = platform_get_drvdata(pdev);
4248ea4484dSLee Jones 
42510cfc5a9SFabien Dessenne 	debugfs_remove_recursive(tdev->root_debugfs_dir);
4268ea4484dSLee Jones 
4278ea4484dSLee Jones 	if (tdev->tx_channel)
4288ea4484dSLee Jones 		mbox_free_channel(tdev->tx_channel);
4298ea4484dSLee Jones 	if (tdev->rx_channel)
4308ea4484dSLee Jones 		mbox_free_channel(tdev->rx_channel);
4318ea4484dSLee Jones 
4328ea4484dSLee Jones 	return 0;
4338ea4484dSLee Jones }
4348ea4484dSLee Jones 
4358ea4484dSLee Jones static const struct of_device_id mbox_test_match[] = {
436c4280137SSudeep Holla 	{ .compatible = "mailbox-test" },
4378ea4484dSLee Jones 	{},
4388ea4484dSLee Jones };
439f42cce3cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, mbox_test_match);
4408ea4484dSLee Jones 
4418ea4484dSLee Jones static struct platform_driver mbox_test_driver = {
4428ea4484dSLee Jones 	.driver = {
443adf06ba9SSudeep Holla 		.name = "mailbox_test",
4448ea4484dSLee Jones 		.of_match_table = mbox_test_match,
4458ea4484dSLee Jones 	},
4468ea4484dSLee Jones 	.probe  = mbox_test_probe,
4478ea4484dSLee Jones 	.remove = mbox_test_remove,
4488ea4484dSLee Jones };
4498ea4484dSLee Jones module_platform_driver(mbox_test_driver);
4508ea4484dSLee Jones 
4518ea4484dSLee Jones MODULE_DESCRIPTION("Generic Mailbox Testing Facility");
4528ea4484dSLee Jones MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org");
4538ea4484dSLee Jones MODULE_LICENSE("GPL v2");
454