1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2015 ST Microelectronics |
4 | * |
5 | * Author: Lee Jones <lee.jones@linaro.org> |
6 | */ |
7 | |
8 | #include <linux/debugfs.h> |
9 | #include <linux/err.h> |
10 | #include <linux/fs.h> |
11 | #include <linux/io.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mailbox_client.h> |
14 | #include <linux/module.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/poll.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/spinlock.h> |
21 | #include <linux/uaccess.h> |
22 | #include <linux/sched/signal.h> |
23 | |
24 | #define MBOX_MAX_SIG_LEN 8 |
25 | #define MBOX_MAX_MSG_LEN 128 |
26 | #define MBOX_BYTES_PER_LINE 16 |
27 | #define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2) |
28 | #define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \ |
29 | (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) |
30 | |
31 | static bool mbox_data_ready; |
32 | |
33 | struct mbox_test_device { |
34 | struct device *dev; |
35 | void __iomem *tx_mmio; |
36 | void __iomem *rx_mmio; |
37 | struct mbox_chan *tx_channel; |
38 | struct mbox_chan *rx_channel; |
39 | char *rx_buffer; |
40 | char *signal; |
41 | char *message; |
42 | spinlock_t lock; |
43 | struct mutex mutex; |
44 | wait_queue_head_t waitq; |
45 | struct fasync_struct *async_queue; |
46 | struct dentry *root_debugfs_dir; |
47 | }; |
48 | |
49 | static ssize_t mbox_test_signal_write(struct file *filp, |
50 | const char __user *userbuf, |
51 | size_t count, loff_t *ppos) |
52 | { |
53 | struct mbox_test_device *tdev = filp->private_data; |
54 | |
55 | if (!tdev->tx_channel) { |
56 | dev_err(tdev->dev, "Channel cannot do Tx\n" ); |
57 | return -EINVAL; |
58 | } |
59 | |
60 | if (count > MBOX_MAX_SIG_LEN) { |
61 | dev_err(tdev->dev, |
62 | "Signal length %zd greater than max allowed %d\n" , |
63 | count, MBOX_MAX_SIG_LEN); |
64 | return -EINVAL; |
65 | } |
66 | |
67 | /* Only allocate memory if we need to */ |
68 | if (!tdev->signal) { |
69 | tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL); |
70 | if (!tdev->signal) |
71 | return -ENOMEM; |
72 | } |
73 | |
74 | if (copy_from_user(to: tdev->signal, from: userbuf, n: count)) { |
75 | kfree(objp: tdev->signal); |
76 | tdev->signal = NULL; |
77 | return -EFAULT; |
78 | } |
79 | |
80 | return count; |
81 | } |
82 | |
83 | static const struct file_operations mbox_test_signal_ops = { |
84 | .write = mbox_test_signal_write, |
85 | .open = simple_open, |
86 | .llseek = generic_file_llseek, |
87 | }; |
88 | |
89 | static int mbox_test_message_fasync(int fd, struct file *filp, int on) |
90 | { |
91 | struct mbox_test_device *tdev = filp->private_data; |
92 | |
93 | return fasync_helper(fd, filp, on, &tdev->async_queue); |
94 | } |
95 | |
96 | static ssize_t mbox_test_message_write(struct file *filp, |
97 | const char __user *userbuf, |
98 | size_t count, loff_t *ppos) |
99 | { |
100 | struct mbox_test_device *tdev = filp->private_data; |
101 | char *message; |
102 | void *data; |
103 | int ret; |
104 | |
105 | if (!tdev->tx_channel) { |
106 | dev_err(tdev->dev, "Channel cannot do Tx\n" ); |
107 | return -EINVAL; |
108 | } |
109 | |
110 | if (count > MBOX_MAX_MSG_LEN) { |
111 | dev_err(tdev->dev, |
112 | "Message length %zd greater than max allowed %d\n" , |
113 | count, MBOX_MAX_MSG_LEN); |
114 | return -EINVAL; |
115 | } |
116 | |
117 | message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); |
118 | if (!message) |
119 | return -ENOMEM; |
120 | |
121 | mutex_lock(&tdev->mutex); |
122 | |
123 | tdev->message = message; |
124 | ret = copy_from_user(to: tdev->message, from: userbuf, n: count); |
125 | if (ret) { |
126 | ret = -EFAULT; |
127 | goto out; |
128 | } |
129 | |
130 | /* |
131 | * A separate signal is only of use if there is |
132 | * MMIO to subsequently pass the message through |
133 | */ |
134 | if (tdev->tx_mmio && tdev->signal) { |
135 | print_hex_dump_bytes("Client: Sending: Signal: " , DUMP_PREFIX_ADDRESS, |
136 | tdev->signal, MBOX_MAX_SIG_LEN); |
137 | |
138 | data = tdev->signal; |
139 | } else |
140 | data = tdev->message; |
141 | |
142 | print_hex_dump_bytes("Client: Sending: Message: " , DUMP_PREFIX_ADDRESS, |
143 | tdev->message, MBOX_MAX_MSG_LEN); |
144 | |
145 | ret = mbox_send_message(chan: tdev->tx_channel, mssg: data); |
146 | if (ret < 0) |
147 | dev_err(tdev->dev, "Failed to send message via mailbox\n" ); |
148 | |
149 | out: |
150 | kfree(objp: tdev->signal); |
151 | kfree(objp: tdev->message); |
152 | tdev->signal = NULL; |
153 | |
154 | mutex_unlock(lock: &tdev->mutex); |
155 | |
156 | return ret < 0 ? ret : count; |
157 | } |
158 | |
159 | static bool mbox_test_message_data_ready(struct mbox_test_device *tdev) |
160 | { |
161 | bool data_ready; |
162 | unsigned long flags; |
163 | |
164 | spin_lock_irqsave(&tdev->lock, flags); |
165 | data_ready = mbox_data_ready; |
166 | spin_unlock_irqrestore(lock: &tdev->lock, flags); |
167 | |
168 | return data_ready; |
169 | } |
170 | |
171 | static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, |
172 | size_t count, loff_t *ppos) |
173 | { |
174 | struct mbox_test_device *tdev = filp->private_data; |
175 | unsigned long flags; |
176 | char *touser, *ptr; |
177 | int l = 0; |
178 | int ret; |
179 | |
180 | DECLARE_WAITQUEUE(wait, current); |
181 | |
182 | touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL); |
183 | if (!touser) |
184 | return -ENOMEM; |
185 | |
186 | if (!tdev->rx_channel) { |
187 | ret = snprintf(buf: touser, size: 20, fmt: "<NO RX CAPABILITY>\n" ); |
188 | ret = simple_read_from_buffer(to: userbuf, count, ppos, |
189 | from: touser, available: ret); |
190 | goto kfree_err; |
191 | } |
192 | |
193 | add_wait_queue(wq_head: &tdev->waitq, wq_entry: &wait); |
194 | |
195 | do { |
196 | __set_current_state(TASK_INTERRUPTIBLE); |
197 | |
198 | if (mbox_test_message_data_ready(tdev)) |
199 | break; |
200 | |
201 | if (filp->f_flags & O_NONBLOCK) { |
202 | ret = -EAGAIN; |
203 | goto waitq_err; |
204 | } |
205 | |
206 | if (signal_pending(current)) { |
207 | ret = -ERESTARTSYS; |
208 | goto waitq_err; |
209 | } |
210 | schedule(); |
211 | |
212 | } while (1); |
213 | |
214 | spin_lock_irqsave(&tdev->lock, flags); |
215 | |
216 | ptr = tdev->rx_buffer; |
217 | while (l < MBOX_HEXDUMP_MAX_LEN) { |
218 | hex_dump_to_buffer(buf: ptr, |
219 | MBOX_BYTES_PER_LINE, |
220 | MBOX_BYTES_PER_LINE, groupsize: 1, linebuf: touser + l, |
221 | MBOX_HEXDUMP_LINE_LEN, ascii: true); |
222 | |
223 | ptr += MBOX_BYTES_PER_LINE; |
224 | l += MBOX_HEXDUMP_LINE_LEN; |
225 | *(touser + (l - 1)) = '\n'; |
226 | } |
227 | *(touser + l) = '\0'; |
228 | |
229 | memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN); |
230 | mbox_data_ready = false; |
231 | |
232 | spin_unlock_irqrestore(lock: &tdev->lock, flags); |
233 | |
234 | ret = simple_read_from_buffer(to: userbuf, count, ppos, from: touser, MBOX_HEXDUMP_MAX_LEN); |
235 | waitq_err: |
236 | __set_current_state(TASK_RUNNING); |
237 | remove_wait_queue(wq_head: &tdev->waitq, wq_entry: &wait); |
238 | kfree_err: |
239 | kfree(objp: touser); |
240 | return ret; |
241 | } |
242 | |
243 | static __poll_t |
244 | mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait) |
245 | { |
246 | struct mbox_test_device *tdev = filp->private_data; |
247 | |
248 | poll_wait(filp, wait_address: &tdev->waitq, p: wait); |
249 | |
250 | if (mbox_test_message_data_ready(tdev)) |
251 | return EPOLLIN | EPOLLRDNORM; |
252 | return 0; |
253 | } |
254 | |
255 | static const struct file_operations mbox_test_message_ops = { |
256 | .write = mbox_test_message_write, |
257 | .read = mbox_test_message_read, |
258 | .fasync = mbox_test_message_fasync, |
259 | .poll = mbox_test_message_poll, |
260 | .open = simple_open, |
261 | .llseek = generic_file_llseek, |
262 | }; |
263 | |
264 | static int mbox_test_add_debugfs(struct platform_device *pdev, |
265 | struct mbox_test_device *tdev) |
266 | { |
267 | if (!debugfs_initialized()) |
268 | return 0; |
269 | |
270 | tdev->root_debugfs_dir = debugfs_create_dir(name: dev_name(dev: &pdev->dev), NULL); |
271 | if (!tdev->root_debugfs_dir) { |
272 | dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n" ); |
273 | return -EINVAL; |
274 | } |
275 | |
276 | debugfs_create_file(name: "message" , mode: 0600, parent: tdev->root_debugfs_dir, |
277 | data: tdev, fops: &mbox_test_message_ops); |
278 | |
279 | debugfs_create_file(name: "signal" , mode: 0200, parent: tdev->root_debugfs_dir, |
280 | data: tdev, fops: &mbox_test_signal_ops); |
281 | |
282 | return 0; |
283 | } |
284 | |
285 | static void mbox_test_receive_message(struct mbox_client *client, void *message) |
286 | { |
287 | struct mbox_test_device *tdev = dev_get_drvdata(dev: client->dev); |
288 | unsigned long flags; |
289 | |
290 | spin_lock_irqsave(&tdev->lock, flags); |
291 | if (tdev->rx_mmio) { |
292 | memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN); |
293 | print_hex_dump_bytes("Client: Received [MMIO]: " , DUMP_PREFIX_ADDRESS, |
294 | tdev->rx_buffer, MBOX_MAX_MSG_LEN); |
295 | } else if (message) { |
296 | print_hex_dump_bytes("Client: Received [API]: " , DUMP_PREFIX_ADDRESS, |
297 | message, MBOX_MAX_MSG_LEN); |
298 | memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); |
299 | } |
300 | mbox_data_ready = true; |
301 | spin_unlock_irqrestore(lock: &tdev->lock, flags); |
302 | |
303 | wake_up_interruptible(&tdev->waitq); |
304 | |
305 | kill_fasync(&tdev->async_queue, SIGIO, POLL_IN); |
306 | } |
307 | |
308 | static void mbox_test_prepare_message(struct mbox_client *client, void *message) |
309 | { |
310 | struct mbox_test_device *tdev = dev_get_drvdata(dev: client->dev); |
311 | |
312 | if (tdev->tx_mmio) { |
313 | if (tdev->signal) |
314 | memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN); |
315 | else |
316 | memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN); |
317 | } |
318 | } |
319 | |
320 | static void mbox_test_message_sent(struct mbox_client *client, |
321 | void *message, int r) |
322 | { |
323 | if (r) |
324 | dev_warn(client->dev, |
325 | "Client: Message could not be sent: %d\n" , r); |
326 | else |
327 | dev_info(client->dev, |
328 | "Client: Message sent\n" ); |
329 | } |
330 | |
331 | static struct mbox_chan * |
332 | mbox_test_request_channel(struct platform_device *pdev, const char *name) |
333 | { |
334 | struct mbox_client *client; |
335 | struct mbox_chan *channel; |
336 | |
337 | client = devm_kzalloc(dev: &pdev->dev, size: sizeof(*client), GFP_KERNEL); |
338 | if (!client) |
339 | return ERR_PTR(error: -ENOMEM); |
340 | |
341 | client->dev = &pdev->dev; |
342 | client->rx_callback = mbox_test_receive_message; |
343 | client->tx_prepare = mbox_test_prepare_message; |
344 | client->tx_done = mbox_test_message_sent; |
345 | client->tx_block = true; |
346 | client->knows_txdone = false; |
347 | client->tx_tout = 500; |
348 | |
349 | channel = mbox_request_channel_byname(cl: client, name); |
350 | if (IS_ERR(ptr: channel)) { |
351 | dev_warn(&pdev->dev, "Failed to request %s channel\n" , name); |
352 | return NULL; |
353 | } |
354 | |
355 | return channel; |
356 | } |
357 | |
358 | static int mbox_test_probe(struct platform_device *pdev) |
359 | { |
360 | struct mbox_test_device *tdev; |
361 | struct resource *res; |
362 | resource_size_t size; |
363 | int ret; |
364 | |
365 | tdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*tdev), GFP_KERNEL); |
366 | if (!tdev) |
367 | return -ENOMEM; |
368 | |
369 | /* It's okay for MMIO to be NULL */ |
370 | tdev->tx_mmio = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
371 | if (PTR_ERR(ptr: tdev->tx_mmio) == -EBUSY) { |
372 | /* if reserved area in SRAM, try just ioremap */ |
373 | size = resource_size(res); |
374 | tdev->tx_mmio = devm_ioremap(dev: &pdev->dev, offset: res->start, size); |
375 | } else if (IS_ERR(ptr: tdev->tx_mmio)) { |
376 | tdev->tx_mmio = NULL; |
377 | } |
378 | |
379 | /* If specified, second reg entry is Rx MMIO */ |
380 | tdev->rx_mmio = devm_platform_get_and_ioremap_resource(pdev, index: 1, res: &res); |
381 | if (PTR_ERR(ptr: tdev->rx_mmio) == -EBUSY) { |
382 | size = resource_size(res); |
383 | tdev->rx_mmio = devm_ioremap(dev: &pdev->dev, offset: res->start, size); |
384 | } else if (IS_ERR(ptr: tdev->rx_mmio)) { |
385 | tdev->rx_mmio = tdev->tx_mmio; |
386 | } |
387 | |
388 | tdev->tx_channel = mbox_test_request_channel(pdev, name: "tx" ); |
389 | tdev->rx_channel = mbox_test_request_channel(pdev, name: "rx" ); |
390 | |
391 | if (IS_ERR_OR_NULL(ptr: tdev->tx_channel) && IS_ERR_OR_NULL(ptr: tdev->rx_channel)) |
392 | return -EPROBE_DEFER; |
393 | |
394 | /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ |
395 | if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) |
396 | tdev->rx_channel = tdev->tx_channel; |
397 | |
398 | tdev->dev = &pdev->dev; |
399 | platform_set_drvdata(pdev, data: tdev); |
400 | |
401 | spin_lock_init(&tdev->lock); |
402 | mutex_init(&tdev->mutex); |
403 | |
404 | if (tdev->rx_channel) { |
405 | tdev->rx_buffer = devm_kzalloc(dev: &pdev->dev, |
406 | MBOX_MAX_MSG_LEN, GFP_KERNEL); |
407 | if (!tdev->rx_buffer) |
408 | return -ENOMEM; |
409 | } |
410 | |
411 | ret = mbox_test_add_debugfs(pdev, tdev); |
412 | if (ret) |
413 | return ret; |
414 | |
415 | init_waitqueue_head(&tdev->waitq); |
416 | dev_info(&pdev->dev, "Successfully registered\n" ); |
417 | |
418 | return 0; |
419 | } |
420 | |
421 | static void mbox_test_remove(struct platform_device *pdev) |
422 | { |
423 | struct mbox_test_device *tdev = platform_get_drvdata(pdev); |
424 | |
425 | debugfs_remove_recursive(dentry: tdev->root_debugfs_dir); |
426 | |
427 | if (tdev->tx_channel) |
428 | mbox_free_channel(chan: tdev->tx_channel); |
429 | if (tdev->rx_channel) |
430 | mbox_free_channel(chan: tdev->rx_channel); |
431 | } |
432 | |
433 | static const struct of_device_id mbox_test_match[] = { |
434 | { .compatible = "mailbox-test" }, |
435 | {}, |
436 | }; |
437 | MODULE_DEVICE_TABLE(of, mbox_test_match); |
438 | |
439 | static struct platform_driver mbox_test_driver = { |
440 | .driver = { |
441 | .name = "mailbox_test" , |
442 | .of_match_table = mbox_test_match, |
443 | }, |
444 | .probe = mbox_test_probe, |
445 | .remove_new = mbox_test_remove, |
446 | }; |
447 | module_platform_driver(mbox_test_driver); |
448 | |
449 | MODULE_DESCRIPTION("Generic Mailbox Testing Facility" ); |
450 | MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org" ); |
451 | MODULE_LICENSE("GPL v2" ); |
452 | |