diff options
author | Lee Jones <lee.jones@linaro.org> | 2015-10-16 03:21:30 -0400 |
---|---|---|
committer | Jassi Brar <jaswinder.singh@linaro.org> | 2015-10-17 01:06:56 -0400 |
commit | 8ea4484d0c2bb4e2152261943fa1a3522654b1c7 (patch) | |
tree | 6e3c13403f8b1a1677ba8b5aa11611f59b86f482 | |
parent | 9ef4546cbd7eed2412ec6f1161c2b92362379150 (diff) |
mailbox: Add generic mechanism for testing Mailbox Controllers
This particular Client implementation uses shared memory in order
to pass messages between Mailbox users; however, it can be easily
hacked to support any type of Controller.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
-rw-r--r-- | drivers/mailbox/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 2 | ||||
-rw-r--r-- | drivers/mailbox/mailbox-test.c | 361 |
3 files changed, 370 insertions, 0 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 7b53386f9415..546d05f4358a 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig | |||
@@ -78,4 +78,11 @@ config STI_MBOX | |||
78 | Mailbox implementation for STMicroelectonics family chips with | 78 | Mailbox implementation for STMicroelectonics family chips with |
79 | hardware for interprocessor communication. | 79 | hardware for interprocessor communication. |
80 | 80 | ||
81 | config MAILBOX_TEST | ||
82 | tristate "Mailbox Test Client" | ||
83 | depends on OF | ||
84 | help | ||
85 | Test client to help with testing new Controller driver | ||
86 | implementations. | ||
87 | |||
81 | endif | 88 | endif |
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 7cb476643833..92435ef11f26 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile | |||
@@ -2,6 +2,8 @@ | |||
2 | 2 | ||
3 | obj-$(CONFIG_MAILBOX) += mailbox.o | 3 | obj-$(CONFIG_MAILBOX) += mailbox.o |
4 | 4 | ||
5 | obj-$(CONFIG_MAILBOX_TEST) += mailbox-test.o | ||
6 | |||
5 | obj-$(CONFIG_ARM_MHU) += arm_mhu.o | 7 | obj-$(CONFIG_ARM_MHU) += arm_mhu.o |
6 | 8 | ||
7 | obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o | 9 | obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o |
diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c new file mode 100644 index 000000000000..cac1ba2f3e22 --- /dev/null +++ b/drivers/mailbox/mailbox-test.c | |||
@@ -0,0 +1,361 @@ | |||
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> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/mailbox_client.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | |||
22 | #define MBOX_MAX_SIG_LEN 8 | ||
23 | #define MBOX_MAX_MSG_LEN 128 | ||
24 | #define MBOX_BYTES_PER_LINE 16 | ||
25 | #define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2) | ||
26 | #define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \ | ||
27 | (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) | ||
28 | |||
29 | static struct dentry *root_debugfs_dir; | ||
30 | |||
31 | struct mbox_test_device { | ||
32 | struct device *dev; | ||
33 | void __iomem *mmio; | ||
34 | struct mbox_chan *tx_channel; | ||
35 | struct mbox_chan *rx_channel; | ||
36 | char *rx_buffer; | ||
37 | char *signal; | ||
38 | char *message; | ||
39 | spinlock_t lock; | ||
40 | }; | ||
41 | |||
42 | static ssize_t mbox_test_signal_write(struct file *filp, | ||
43 | const char __user *userbuf, | ||
44 | size_t count, loff_t *ppos) | ||
45 | { | ||
46 | struct mbox_test_device *tdev = filp->private_data; | ||
47 | int ret; | ||
48 | |||
49 | if (!tdev->tx_channel) { | ||
50 | dev_err(tdev->dev, "Channel cannot do Tx\n"); | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | |||
54 | if (count > MBOX_MAX_SIG_LEN) { | ||
55 | dev_err(tdev->dev, | ||
56 | "Signal length %d greater than max allowed %d\n", | ||
57 | count, MBOX_MAX_SIG_LEN); | ||
58 | return -EINVAL; | ||
59 | } | ||
60 | |||
61 | tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL); | ||
62 | if (!tdev->signal) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | ret = copy_from_user(tdev->signal, userbuf, count); | ||
66 | if (ret) { | ||
67 | kfree(tdev->signal); | ||
68 | return -EFAULT; | ||
69 | } | ||
70 | |||
71 | return ret < 0 ? ret : count; | ||
72 | } | ||
73 | |||
74 | static const struct file_operations mbox_test_signal_ops = { | ||
75 | .write = mbox_test_signal_write, | ||
76 | .open = simple_open, | ||
77 | .llseek = generic_file_llseek, | ||
78 | }; | ||
79 | |||
80 | static ssize_t mbox_test_message_write(struct file *filp, | ||
81 | const char __user *userbuf, | ||
82 | size_t count, loff_t *ppos) | ||
83 | { | ||
84 | struct mbox_test_device *tdev = filp->private_data; | ||
85 | void *data; | ||
86 | int ret; | ||
87 | |||
88 | if (!tdev->tx_channel) { | ||
89 | dev_err(tdev->dev, "Channel cannot do Tx\n"); | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | if (count > MBOX_MAX_MSG_LEN) { | ||
94 | dev_err(tdev->dev, | ||
95 | "Message length %d greater than max allowed %d\n", | ||
96 | count, MBOX_MAX_MSG_LEN); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | |||
100 | tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); | ||
101 | if (!tdev->message) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | ret = copy_from_user(tdev->message, userbuf, count); | ||
105 | if (ret) { | ||
106 | ret = -EFAULT; | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * A separate signal is only of use if there is | ||
112 | * MMIO to subsequently pass the message through | ||
113 | */ | ||
114 | if (tdev->mmio && tdev->signal) { | ||
115 | print_hex_dump(KERN_INFO, "Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, | ||
116 | MBOX_BYTES_PER_LINE, 1, tdev->signal, MBOX_MAX_SIG_LEN, true); | ||
117 | |||
118 | data = tdev->signal; | ||
119 | } else | ||
120 | data = tdev->message; | ||
121 | |||
122 | print_hex_dump(KERN_INFO, "Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, | ||
123 | MBOX_BYTES_PER_LINE, 1, tdev->message, MBOX_MAX_MSG_LEN, true); | ||
124 | |||
125 | ret = mbox_send_message(tdev->tx_channel, data); | ||
126 | if (ret < 0) | ||
127 | dev_err(tdev->dev, "Failed to send message via mailbox\n"); | ||
128 | |||
129 | out: | ||
130 | kfree(tdev->signal); | ||
131 | kfree(tdev->message); | ||
132 | |||
133 | return ret < 0 ? ret : count; | ||
134 | } | ||
135 | |||
136 | static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, | ||
137 | size_t count, loff_t *ppos) | ||
138 | { | ||
139 | struct mbox_test_device *tdev = filp->private_data; | ||
140 | unsigned long flags; | ||
141 | char *touser, *ptr; | ||
142 | int l = 0; | ||
143 | int ret; | ||
144 | |||
145 | touser = kzalloc(MBOX_HEXDUMP_MAX_LEN, GFP_KERNEL); | ||
146 | if (!touser) | ||
147 | return -ENOMEM; | ||
148 | |||
149 | if (!tdev->rx_channel) { | ||
150 | ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n"); | ||
151 | ret = simple_read_from_buffer(userbuf, count, ppos, | ||
152 | touser, ret); | ||
153 | goto out; | ||
154 | } | ||
155 | |||
156 | if (tdev->rx_buffer[0] == '\0') { | ||
157 | ret = snprintf(touser, 9, "<EMPTY>\n"); | ||
158 | ret = simple_read_from_buffer(userbuf, count, ppos, | ||
159 | touser, ret); | ||
160 | goto out; | ||
161 | } | ||
162 | |||
163 | spin_lock_irqsave(&tdev->lock, flags); | ||
164 | |||
165 | ptr = tdev->rx_buffer; | ||
166 | while (l < MBOX_HEXDUMP_MAX_LEN) { | ||
167 | hex_dump_to_buffer(ptr, | ||
168 | MBOX_BYTES_PER_LINE, | ||
169 | MBOX_BYTES_PER_LINE, 1, touser + l, | ||
170 | MBOX_HEXDUMP_LINE_LEN, true); | ||
171 | |||
172 | ptr += MBOX_BYTES_PER_LINE; | ||
173 | l += MBOX_HEXDUMP_LINE_LEN; | ||
174 | *(touser + (l - 1)) = '\n'; | ||
175 | } | ||
176 | *(touser + l) = '\0'; | ||
177 | |||
178 | memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN); | ||
179 | |||
180 | spin_unlock_irqrestore(&tdev->lock, flags); | ||
181 | |||
182 | ret = simple_read_from_buffer(userbuf, count, ppos, touser, MBOX_HEXDUMP_MAX_LEN); | ||
183 | out: | ||
184 | kfree(touser); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | static const struct file_operations mbox_test_message_ops = { | ||
189 | .write = mbox_test_message_write, | ||
190 | .read = mbox_test_message_read, | ||
191 | .open = simple_open, | ||
192 | .llseek = generic_file_llseek, | ||
193 | }; | ||
194 | |||
195 | static int mbox_test_add_debugfs(struct platform_device *pdev, | ||
196 | struct mbox_test_device *tdev) | ||
197 | { | ||
198 | if (!debugfs_initialized()) | ||
199 | return 0; | ||
200 | |||
201 | root_debugfs_dir = debugfs_create_dir("mailbox", NULL); | ||
202 | if (!root_debugfs_dir) { | ||
203 | dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | |||
207 | debugfs_create_file("message", 0600, root_debugfs_dir, | ||
208 | tdev, &mbox_test_message_ops); | ||
209 | |||
210 | debugfs_create_file("signal", 0200, root_debugfs_dir, | ||
211 | tdev, &mbox_test_signal_ops); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static void mbox_test_receive_message(struct mbox_client *client, void *message) | ||
217 | { | ||
218 | struct mbox_test_device *tdev = dev_get_drvdata(client->dev); | ||
219 | unsigned long flags; | ||
220 | |||
221 | spin_lock_irqsave(&tdev->lock, flags); | ||
222 | if (tdev->mmio) { | ||
223 | print_hex_dump(KERN_INFO, "Client: Received [MMIO]: ", | ||
224 | DUMP_PREFIX_ADDRESS, MBOX_BYTES_PER_LINE, 1, | ||
225 | tdev->mmio, MBOX_MAX_MSG_LEN, true); | ||
226 | memcpy(tdev->rx_buffer, tdev->mmio, MBOX_MAX_MSG_LEN); | ||
227 | |||
228 | } else if (message) { | ||
229 | print_hex_dump(KERN_INFO, "Client: Received [API]: ", | ||
230 | DUMP_PREFIX_ADDRESS, MBOX_BYTES_PER_LINE, 1, | ||
231 | message, MBOX_MAX_MSG_LEN, true); | ||
232 | memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); | ||
233 | } | ||
234 | spin_unlock_irqrestore(&tdev->lock, flags); | ||
235 | } | ||
236 | |||
237 | static void mbox_test_prepare_message(struct mbox_client *client, void *message) | ||
238 | { | ||
239 | struct mbox_test_device *tdev = dev_get_drvdata(client->dev); | ||
240 | |||
241 | if (tdev->mmio) { | ||
242 | if (tdev->signal) | ||
243 | memcpy(tdev->mmio, tdev->message, MBOX_MAX_MSG_LEN); | ||
244 | else | ||
245 | memcpy(tdev->mmio, message, MBOX_MAX_MSG_LEN); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void mbox_test_message_sent(struct mbox_client *client, | ||
250 | void *message, int r) | ||
251 | { | ||
252 | if (r) | ||
253 | dev_warn(client->dev, | ||
254 | "Client: Message could not be sent: %d\n", r); | ||
255 | else | ||
256 | dev_info(client->dev, | ||
257 | "Client: Message sent\n"); | ||
258 | } | ||
259 | |||
260 | static struct mbox_chan * | ||
261 | mbox_test_request_channel(struct platform_device *pdev, const char *name) | ||
262 | { | ||
263 | struct mbox_client *client; | ||
264 | struct mbox_chan *channel; | ||
265 | |||
266 | client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); | ||
267 | if (!client) | ||
268 | return ERR_PTR(-ENOMEM); | ||
269 | |||
270 | client->dev = &pdev->dev; | ||
271 | client->rx_callback = mbox_test_receive_message; | ||
272 | client->tx_prepare = mbox_test_prepare_message; | ||
273 | client->tx_done = mbox_test_message_sent; | ||
274 | client->tx_block = true; | ||
275 | client->knows_txdone = false; | ||
276 | client->tx_tout = 500; | ||
277 | |||
278 | channel = mbox_request_channel_byname(client, name); | ||
279 | if (IS_ERR(channel)) { | ||
280 | dev_warn(&pdev->dev, "Failed to request %s channel\n", name); | ||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | return channel; | ||
285 | } | ||
286 | |||
287 | static int mbox_test_probe(struct platform_device *pdev) | ||
288 | { | ||
289 | struct mbox_test_device *tdev; | ||
290 | struct resource *res; | ||
291 | int ret; | ||
292 | |||
293 | tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); | ||
294 | if (!tdev) | ||
295 | return -ENOMEM; | ||
296 | |||
297 | /* It's okay for MMIO to be NULL */ | ||
298 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
299 | tdev->mmio = devm_ioremap_resource(&pdev->dev, res); | ||
300 | if (IS_ERR(tdev->mmio)) | ||
301 | tdev->mmio = NULL; | ||
302 | |||
303 | tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); | ||
304 | tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); | ||
305 | |||
306 | if (!tdev->tx_channel && !tdev->tx_channel) | ||
307 | return -EPROBE_DEFER; | ||
308 | |||
309 | tdev->dev = &pdev->dev; | ||
310 | platform_set_drvdata(pdev, tdev); | ||
311 | |||
312 | spin_lock_init(&tdev->lock); | ||
313 | |||
314 | if (tdev->rx_channel) { | ||
315 | tdev->rx_buffer = devm_kzalloc(&pdev->dev, | ||
316 | MBOX_MAX_MSG_LEN, GFP_KERNEL); | ||
317 | if (!tdev->rx_buffer) | ||
318 | return -ENOMEM; | ||
319 | } | ||
320 | |||
321 | ret = mbox_test_add_debugfs(pdev, tdev); | ||
322 | if (ret) | ||
323 | return ret; | ||
324 | |||
325 | dev_info(&pdev->dev, "Successfully registered\n"); | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static int mbox_test_remove(struct platform_device *pdev) | ||
331 | { | ||
332 | struct mbox_test_device *tdev = platform_get_drvdata(pdev); | ||
333 | |||
334 | debugfs_remove_recursive(root_debugfs_dir); | ||
335 | |||
336 | if (tdev->tx_channel) | ||
337 | mbox_free_channel(tdev->tx_channel); | ||
338 | if (tdev->rx_channel) | ||
339 | mbox_free_channel(tdev->rx_channel); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static const struct of_device_id mbox_test_match[] = { | ||
345 | { .compatible = "mailbox_test" }, | ||
346 | {}, | ||
347 | }; | ||
348 | |||
349 | static struct platform_driver mbox_test_driver = { | ||
350 | .driver = { | ||
351 | .name = "mailbox_sti_test", | ||
352 | .of_match_table = mbox_test_match, | ||
353 | }, | ||
354 | .probe = mbox_test_probe, | ||
355 | .remove = mbox_test_remove, | ||
356 | }; | ||
357 | module_platform_driver(mbox_test_driver); | ||
358 | |||
359 | MODULE_DESCRIPTION("Generic Mailbox Testing Facility"); | ||
360 | MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org"); | ||
361 | MODULE_LICENSE("GPL v2"); | ||