Merge branch 'for-next-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab...
[linux-2.6-block.git] / drivers / mailbox / mailbox-test.c
CommitLineData
8ea4484d
LJ
1/*
2 * Copyright (C) 2015 ST Microelectronics
3 *
4 * Author: Lee Jones <lee.jones@linaro.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/debugfs.h>
13#include <linux/err.h>
a133f8b6 14#include <linux/io.h>
8ea4484d
LJ
15#include <linux/kernel.h>
16#include <linux/mailbox_client.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21#include <linux/uaccess.h>
22
23#define MBOX_MAX_SIG_LEN 8
24#define MBOX_MAX_MSG_LEN 128
25#define MBOX_BYTES_PER_LINE 16
6c03663f 26#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2)
8ea4484d
LJ
27#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \
28 (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
29
30static struct dentry *root_debugfs_dir;
31
32struct mbox_test_device {
33 struct device *dev;
2d74ffdc
SH
34 void __iomem *tx_mmio;
35 void __iomem *rx_mmio;
8ea4484d
LJ
36 struct mbox_chan *tx_channel;
37 struct mbox_chan *rx_channel;
38 char *rx_buffer;
39 char *signal;
40 char *message;
41 spinlock_t lock;
42};
43
44static ssize_t mbox_test_signal_write(struct file *filp,
45 const char __user *userbuf,
46 size_t count, loff_t *ppos)
47{
48 struct mbox_test_device *tdev = filp->private_data;
49 int ret;
50
51 if (!tdev->tx_channel) {
52 dev_err(tdev->dev, "Channel cannot do Tx\n");
53 return -EINVAL;
54 }
55
56 if (count > MBOX_MAX_SIG_LEN) {
57 dev_err(tdev->dev,
6c03663f 58 "Signal length %zd greater than max allowed %d\n",
8ea4484d
LJ
59 count, MBOX_MAX_SIG_LEN);
60 return -EINVAL;
61 }
62
63 tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL);
64 if (!tdev->signal)
65 return -ENOMEM;
66
67 ret = copy_from_user(tdev->signal, userbuf, count);
68 if (ret) {
69 kfree(tdev->signal);
70 return -EFAULT;
71 }
72
73 return ret < 0 ? ret : count;
74}
75
76static const struct file_operations mbox_test_signal_ops = {
77 .write = mbox_test_signal_write,
78 .open = simple_open,
79 .llseek = generic_file_llseek,
80};
81
82static ssize_t mbox_test_message_write(struct file *filp,
83 const char __user *userbuf,
84 size_t count, loff_t *ppos)
85{
86 struct mbox_test_device *tdev = filp->private_data;
87 void *data;
88 int ret;
89
90 if (!tdev->tx_channel) {
91 dev_err(tdev->dev, "Channel cannot do Tx\n");
92 return -EINVAL;
93 }
94
95 if (count > MBOX_MAX_MSG_LEN) {
96 dev_err(tdev->dev,
6c03663f 97 "Message length %zd greater than max allowed %d\n",
8ea4484d
LJ
98 count, MBOX_MAX_MSG_LEN);
99 return -EINVAL;
100 }
101
102 tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL);
103 if (!tdev->message)
104 return -ENOMEM;
105
106 ret = copy_from_user(tdev->message, userbuf, count);
107 if (ret) {
108 ret = -EFAULT;
109 goto out;
110 }
111
112 /*
113 * A separate signal is only of use if there is
114 * MMIO to subsequently pass the message through
115 */
2d74ffdc 116 if (tdev->tx_mmio && tdev->signal) {
27fa680f
SH
117 print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS,
118 tdev->signal, MBOX_MAX_SIG_LEN);
8ea4484d
LJ
119
120 data = tdev->signal;
121 } else
122 data = tdev->message;
123
27fa680f
SH
124 print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS,
125 tdev->message, MBOX_MAX_MSG_LEN);
8ea4484d
LJ
126
127 ret = mbox_send_message(tdev->tx_channel, data);
128 if (ret < 0)
129 dev_err(tdev->dev, "Failed to send message via mailbox\n");
130
131out:
132 kfree(tdev->signal);
133 kfree(tdev->message);
134
135 return ret < 0 ? ret : count;
136}
137
138static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,
139 size_t count, loff_t *ppos)
140{
141 struct mbox_test_device *tdev = filp->private_data;
142 unsigned long flags;
143 char *touser, *ptr;
144 int l = 0;
145 int ret;
146
c3ac54a6 147 touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL);
8ea4484d
LJ
148 if (!touser)
149 return -ENOMEM;
150
151 if (!tdev->rx_channel) {
152 ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n");
153 ret = simple_read_from_buffer(userbuf, count, ppos,
154 touser, ret);
155 goto out;
156 }
157
158 if (tdev->rx_buffer[0] == '\0') {
159 ret = snprintf(touser, 9, "<EMPTY>\n");
160 ret = simple_read_from_buffer(userbuf, count, ppos,
161 touser, ret);
162 goto out;
163 }
164
165 spin_lock_irqsave(&tdev->lock, flags);
166
167 ptr = tdev->rx_buffer;
168 while (l < MBOX_HEXDUMP_MAX_LEN) {
169 hex_dump_to_buffer(ptr,
170 MBOX_BYTES_PER_LINE,
171 MBOX_BYTES_PER_LINE, 1, touser + l,
172 MBOX_HEXDUMP_LINE_LEN, true);
173
174 ptr += MBOX_BYTES_PER_LINE;
175 l += MBOX_HEXDUMP_LINE_LEN;
176 *(touser + (l - 1)) = '\n';
177 }
178 *(touser + l) = '\0';
179
180 memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN);
181
182 spin_unlock_irqrestore(&tdev->lock, flags);
183
184 ret = simple_read_from_buffer(userbuf, count, ppos, touser, MBOX_HEXDUMP_MAX_LEN);
185out:
186 kfree(touser);
187 return ret;
188}
189
190static const struct file_operations mbox_test_message_ops = {
191 .write = mbox_test_message_write,
192 .read = mbox_test_message_read,
193 .open = simple_open,
194 .llseek = generic_file_llseek,
195};
196
197static int mbox_test_add_debugfs(struct platform_device *pdev,
198 struct mbox_test_device *tdev)
199{
200 if (!debugfs_initialized())
201 return 0;
202
203 root_debugfs_dir = debugfs_create_dir("mailbox", NULL);
204 if (!root_debugfs_dir) {
205 dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
206 return -EINVAL;
207 }
208
209 debugfs_create_file("message", 0600, root_debugfs_dir,
210 tdev, &mbox_test_message_ops);
211
212 debugfs_create_file("signal", 0200, root_debugfs_dir,
213 tdev, &mbox_test_signal_ops);
214
215 return 0;
216}
217
218static void mbox_test_receive_message(struct mbox_client *client, void *message)
219{
220 struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
221 unsigned long flags;
222
223 spin_lock_irqsave(&tdev->lock, flags);
2d74ffdc
SH
224 if (tdev->rx_mmio) {
225 memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN);
27fa680f
SH
226 print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS,
227 tdev->rx_buffer, MBOX_MAX_MSG_LEN);
8ea4484d 228 } else if (message) {
27fa680f
SH
229 print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS,
230 message, MBOX_MAX_MSG_LEN);
8ea4484d
LJ
231 memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN);
232 }
233 spin_unlock_irqrestore(&tdev->lock, flags);
234}
235
236static void mbox_test_prepare_message(struct mbox_client *client, void *message)
237{
238 struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
239
2d74ffdc 240 if (tdev->tx_mmio) {
8ea4484d 241 if (tdev->signal)
2d74ffdc 242 memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN);
8ea4484d 243 else
2d74ffdc 244 memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN);
8ea4484d
LJ
245 }
246}
247
248static void mbox_test_message_sent(struct mbox_client *client,
249 void *message, int r)
250{
251 if (r)
252 dev_warn(client->dev,
253 "Client: Message could not be sent: %d\n", r);
254 else
255 dev_info(client->dev,
256 "Client: Message sent\n");
257}
258
259static struct mbox_chan *
260mbox_test_request_channel(struct platform_device *pdev, const char *name)
261{
262 struct mbox_client *client;
263 struct mbox_chan *channel;
264
265 client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
266 if (!client)
267 return ERR_PTR(-ENOMEM);
268
269 client->dev = &pdev->dev;
270 client->rx_callback = mbox_test_receive_message;
271 client->tx_prepare = mbox_test_prepare_message;
272 client->tx_done = mbox_test_message_sent;
273 client->tx_block = true;
274 client->knows_txdone = false;
275 client->tx_tout = 500;
276
277 channel = mbox_request_channel_byname(client, name);
278 if (IS_ERR(channel)) {
279 dev_warn(&pdev->dev, "Failed to request %s channel\n", name);
280 return NULL;
281 }
282
283 return channel;
284}
285
286static int mbox_test_probe(struct platform_device *pdev)
287{
288 struct mbox_test_device *tdev;
289 struct resource *res;
290 int ret;
291
292 tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
293 if (!tdev)
294 return -ENOMEM;
295
296 /* It's okay for MMIO to be NULL */
297 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2d74ffdc
SH
298 tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
299 if (IS_ERR(tdev->tx_mmio))
300 tdev->tx_mmio = NULL;
301
302 /* If specified, second reg entry is Rx MMIO */
303 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
304 tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
305 if (IS_ERR(tdev->rx_mmio))
306 tdev->rx_mmio = tdev->tx_mmio;
8ea4484d
LJ
307
308 tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
309 tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
310
6c03663f 311 if (!tdev->tx_channel && !tdev->rx_channel)
8ea4484d
LJ
312 return -EPROBE_DEFER;
313
2d74ffdc
SH
314 /* If Rx is not specified but has Rx MMIO, then Rx = Tx */
315 if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio))
316 tdev->rx_channel = tdev->tx_channel;
317
8ea4484d
LJ
318 tdev->dev = &pdev->dev;
319 platform_set_drvdata(pdev, tdev);
320
321 spin_lock_init(&tdev->lock);
322
323 if (tdev->rx_channel) {
324 tdev->rx_buffer = devm_kzalloc(&pdev->dev,
325 MBOX_MAX_MSG_LEN, GFP_KERNEL);
326 if (!tdev->rx_buffer)
327 return -ENOMEM;
328 }
329
330 ret = mbox_test_add_debugfs(pdev, tdev);
331 if (ret)
332 return ret;
333
334 dev_info(&pdev->dev, "Successfully registered\n");
335
336 return 0;
337}
338
339static int mbox_test_remove(struct platform_device *pdev)
340{
341 struct mbox_test_device *tdev = platform_get_drvdata(pdev);
342
343 debugfs_remove_recursive(root_debugfs_dir);
344
345 if (tdev->tx_channel)
346 mbox_free_channel(tdev->tx_channel);
347 if (tdev->rx_channel)
348 mbox_free_channel(tdev->rx_channel);
349
350 return 0;
351}
352
353static const struct of_device_id mbox_test_match[] = {
c4280137 354 { .compatible = "mailbox-test" },
8ea4484d
LJ
355 {},
356};
357
358static struct platform_driver mbox_test_driver = {
359 .driver = {
adf06ba9 360 .name = "mailbox_test",
8ea4484d
LJ
361 .of_match_table = mbox_test_match,
362 },
363 .probe = mbox_test_probe,
364 .remove = mbox_test_remove,
365};
366module_platform_driver(mbox_test_driver);
367
368MODULE_DESCRIPTION("Generic Mailbox Testing Facility");
369MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org");
370MODULE_LICENSE("GPL v2");