*/
#include <linux/cleanup.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/property.h>
+#include <linux/seq_file.h>
#include <linux/spinlock.h>
static LIST_HEAD(mbox_cons);
return 0;
}
EXPORT_SYMBOL_GPL(devm_mbox_controller_register);
+
+#ifdef CONFIG_DEBUG_FS
+static void *mbox_seq_start(struct seq_file *s, loff_t *pos)
+{
+ mutex_lock(&con_mutex);
+ return seq_list_start(&mbox_cons, *pos);
+}
+
+static void *mbox_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return seq_list_next(v, &mbox_cons, pos);
+}
+
+static void mbox_seq_stop(struct seq_file *s, void *v)
+{
+ mutex_unlock(&con_mutex);
+}
+
+static int mbox_seq_show(struct seq_file *seq, void *v)
+{
+ const struct mbox_controller *mbox = list_entry(v, struct mbox_controller, node);
+
+ seq_printf(seq, "%s:\n", dev_name(mbox->dev));
+
+ for (unsigned int i = 0; i < mbox->num_chans; i++) {
+ struct mbox_chan *chan = &mbox->chans[i];
+
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+ if (chan->cl) {
+ struct device *cl_dev = chan->cl->dev;
+
+ seq_printf(seq, " %3u: %s\n", i,
+ cl_dev ? dev_name(cl_dev) : "NULL device");
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct seq_operations mbox_sops = {
+ .start = mbox_seq_start,
+ .next = mbox_seq_next,
+ .stop = mbox_seq_stop,
+ .show = mbox_seq_show,
+};
+DEFINE_SEQ_ATTRIBUTE(mbox);
+
+/*
+ * subsys_initcall() is used here but controllers may already have been
+ * registered earlier or will be later. The rationale is that debugfs is
+ * accessed only late, i.e. from userspace. So, files created here must make no
+ * assumptions about initcall ordering.
+ */
+static int __init mbox_init(void)
+{
+ struct dentry *mbox_debugfs = debugfs_create_dir("mailbox", NULL);
+
+ debugfs_create_file("mailbox_summary", 0444, mbox_debugfs, NULL, &mbox_fops);
+ return 0;
+}
+subsys_initcall(mbox_init);
+#endif /* DEBUG_FS */