fmc-y += fmc-sdb.o
fmc-y += fru-parse.o
fmc-y += fmc-dump.o
+fmc-y += fmc-debug.o
obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fmc.h>
+#include <linux/fmc-sdb.h>
+
+#include "fmc-private.h"
static int fmc_check_version(unsigned long version, const char *name)
{
}
/* This device went well, give information to the user */
fmc_dump_eeprom(fmc);
- fmc_dump_sdb(fmc);
+ fmc_debug_init(fmc);
}
return 0;
kfree(devarray);
for (i--; i >= 0; i--) {
+ fmc_debug_exit(devs[i]);
sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
device_del(&devs[i]->dev);
fmc_free_id_info(devs[i]);
kfree(devs[0]->devarray);
for (i = 0; i < n; i++) {
+ fmc_debug_exit(devs[i]);
sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
device_del(&devs[i]->dev);
fmc_free_id_info(devs[i]);
--- /dev/null
+/*
+ * Copyright (C) 2015 CERN (www.cern.ch)
+ * Author: Federico Vaga <federico.vaga@cern.ch>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <asm/byteorder.h>
+
+#include <linux/fmc.h>
+#include <linux/sdb.h>
+#include <linux/fmc-sdb.h>
+
+#define FMC_DBG_SDB_DUMP "dump_sdb"
+
+static char *__strip_trailing_space(char *buf, char *str, int len)
+{
+ int i = len - 1;
+
+ memcpy(buf, str, len);
+ buf[len] = '\0';
+ while (i >= 0 && buf[i] == ' ')
+ buf[i--] = '\0';
+ return buf;
+}
+
+#define __sdb_string(buf, field) ({ \
+ BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \
+ __strip_trailing_space(buf, (void *)(field), sizeof(field)); \
+ })
+
+/**
+ * We do not check seq_printf() errors because we want to see things in any case
+ */
+static void fmc_sdb_dump_recursive(struct fmc_device *fmc, struct seq_file *s,
+ const struct sdb_array *arr)
+{
+ unsigned long base = arr->baseaddr;
+ int i, j, n = arr->len, level = arr->level;
+ char tmp[64];
+
+ for (i = 0; i < n; i++) {
+ union sdb_record *r;
+ struct sdb_product *p;
+ struct sdb_component *c;
+
+ r = &arr->record[i];
+ c = &r->dev.sdb_component;
+ p = &c->product;
+
+ for (j = 0; j < level; j++)
+ seq_printf(s, " ");
+ switch (r->empty.record_type) {
+ case sdb_type_interconnect:
+ seq_printf(s, "%08llx:%08x %.19s\n",
+ __be64_to_cpu(p->vendor_id),
+ __be32_to_cpu(p->device_id),
+ p->name);
+ break;
+ case sdb_type_device:
+ seq_printf(s, "%08llx:%08x %.19s (%08llx-%08llx)\n",
+ __be64_to_cpu(p->vendor_id),
+ __be32_to_cpu(p->device_id),
+ p->name,
+ __be64_to_cpu(c->addr_first) + base,
+ __be64_to_cpu(c->addr_last) + base);
+ break;
+ case sdb_type_bridge:
+ seq_printf(s, "%08llx:%08x %.19s (bridge: %08llx)\n",
+ __be64_to_cpu(p->vendor_id),
+ __be32_to_cpu(p->device_id),
+ p->name,
+ __be64_to_cpu(c->addr_first) + base);
+ if (IS_ERR(arr->subtree[i])) {
+ seq_printf(s, "SDB: (bridge error %li)\n",
+ PTR_ERR(arr->subtree[i]));
+ break;
+ }
+ fmc_sdb_dump_recursive(fmc, s, arr->subtree[i]);
+ break;
+ case sdb_type_integration:
+ seq_printf(s, "integration\n");
+ break;
+ case sdb_type_repo_url:
+ seq_printf(s, "Synthesis repository: %s\n",
+ __sdb_string(tmp, r->repo_url.repo_url));
+ break;
+ case sdb_type_synthesis:
+ seq_printf(s, "Bitstream '%s' ",
+ __sdb_string(tmp, r->synthesis.syn_name));
+ seq_printf(s, "synthesized %08x by %s ",
+ __be32_to_cpu(r->synthesis.date),
+ __sdb_string(tmp, r->synthesis.user_name));
+ seq_printf(s, "(%s version %x), ",
+ __sdb_string(tmp, r->synthesis.tool_name),
+ __be32_to_cpu(r->synthesis.tool_version));
+ seq_printf(s, "commit %pm\n",
+ r->synthesis.commit_id);
+ break;
+ case sdb_type_empty:
+ seq_printf(s, "empty\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN TYPE 0x%02x\n",
+ r->empty.record_type);
+ break;
+ }
+ }
+}
+
+static int fmc_sdb_dump(struct seq_file *s, void *offset)
+{
+ struct fmc_device *fmc = s->private;
+
+ if (!fmc->sdb) {
+ seq_printf(s, "no SDB information\n");
+ return 0;
+ }
+
+ seq_printf(s, "FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
+ fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
+ /* Dump SDB information */
+ fmc_sdb_dump_recursive(fmc, s, fmc->sdb);
+
+ return 0;
+}
+
+
+static int fmc_sdb_dump_open(struct inode *inode, struct file *file)
+{
+ struct fmc_device *fmc = inode->i_private;
+
+ return single_open(file, fmc_sdb_dump, fmc);
+}
+
+
+const struct file_operations fmc_dbgfs_sdb_dump = {
+ .owner = THIS_MODULE,
+ .open = fmc_sdb_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int fmc_debug_init(struct fmc_device *fmc)
+{
+ fmc->dbg_dir = debugfs_create_dir(dev_name(&fmc->dev), NULL);
+ if (IS_ERR_OR_NULL(fmc->dbg_dir)) {
+ pr_err("FMC: Cannot create debugfs\n");
+ return PTR_ERR(fmc->dbg_dir);
+ }
+
+ fmc->dbg_sdb_dump = debugfs_create_file(FMC_DBG_SDB_DUMP, 0444,
+ fmc->dbg_dir, fmc,
+ &fmc_dbgfs_sdb_dump);
+ if (IS_ERR_OR_NULL(fmc->dbg_sdb_dump))
+ pr_err("FMC: Cannot create debugfs file %s\n",
+ FMC_DBG_SDB_DUMP);
+
+ return 0;
+}
+
+void fmc_debug_exit(struct fmc_device *fmc)
+{
+ if (fmc->dbg_dir)
+ debugfs_remove_recursive(fmc->dbg_dir);
+}
static int fmc_must_dump_eeprom;
module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644);
-static int fmc_must_dump_sdb;
-module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644);
#define LINELEN 16
for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN)
prev = dump_line(i, line, prev);
}
-
-void fmc_dump_sdb(const struct fmc_device *fmc)
-{
- const uint8_t *line, *prev;
- int i, len;
-
- if (!fmc->sdb)
- return;
- if (!fmc_must_dump_sdb)
- return;
-
- /* If the argument is not-zero, do simple dump (== show) */
- if (fmc_must_dump_sdb > 0)
- fmc_show_sdb_tree(fmc);
-
- if (fmc_must_dump_sdb == 1)
- return;
-
- /* If bigger than 1, dump it seriously, to help debugging */
-
- /*
- * Here we should really use libsdbfs (which is designed to
- * work in kernel space as well) , but it doesn't support
- * directories yet, and it requires better intergration (it
- * should be used instead of fmc-specific code).
- *
- * So, lazily, just dump the top-level array
- */
- pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
- fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
- pr_info("FMC: poor dump of sdb first level:\n");
-
- len = fmc->sdb->len * sizeof(union sdb_record);
- line = (void *)fmc->sdb->record;
- prev = NULL;
- for (i = 0; i < len; i += LINELEN, line += LINELEN)
- prev = dump_line(i, line, prev);
- return;
-}
--- /dev/null
+/*
+ * Copyright (C) 2015 CERN (www.cern.ch)
+ * Author: Federico Vaga <federico.vaga@cern.ch>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ */
+
+extern int fmc_debug_init(struct fmc_device *fmc);
+extern void fmc_debug_exit(struct fmc_device *fmc);
sdb_entry);
return -ENODEV;
}
- fmc_dump_sdb(fmc);
+
return 0;
}
EXPORT_SYMBOL(fmc_reprogram);
-static char *__strip_trailing_space(char *buf, char *str, int len)
-{
- int i = len - 1;
-
- memcpy(buf, str, len);
- while(i >= 0 && buf[i] == ' ')
- buf[i--] = '\0';
- return buf;
-}
-
-#define __sdb_string(buf, field) ({ \
- BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \
- __strip_trailing_space(buf, (void *)(field), sizeof(field)); \
- })
-
-static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
- const struct sdb_array *arr)
-{
- unsigned long base = arr->baseaddr;
- int i, j, n = arr->len, level = arr->level;
- char buf[64];
-
- for (i = 0; i < n; i++) {
- union sdb_record *r;
- struct sdb_product *p;
- struct sdb_component *c;
- r = &arr->record[i];
- c = &r->dev.sdb_component;
- p = &c->product;
-
- dev_info(&fmc->dev, "SDB: ");
-
- for (j = 0; j < level; j++)
- printk(KERN_CONT " ");
- switch (r->empty.record_type) {
- case sdb_type_interconnect:
- printk(KERN_CONT "%08llx:%08x %.19s\n",
- __be64_to_cpu(p->vendor_id),
- __be32_to_cpu(p->device_id),
- p->name);
- break;
- case sdb_type_device:
- printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n",
- __be64_to_cpu(p->vendor_id),
- __be32_to_cpu(p->device_id),
- p->name,
- __be64_to_cpu(c->addr_first) + base,
- __be64_to_cpu(c->addr_last) + base);
- break;
- case sdb_type_bridge:
- printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n",
- __be64_to_cpu(p->vendor_id),
- __be32_to_cpu(p->device_id),
- p->name,
- __be64_to_cpu(c->addr_first) + base);
- if (IS_ERR(arr->subtree[i])) {
- dev_info(&fmc->dev, "SDB: (bridge error %li)\n",
- PTR_ERR(arr->subtree[i]));
- break;
- }
- __fmc_show_sdb_tree(fmc, arr->subtree[i]);
- break;
- case sdb_type_integration:
- printk(KERN_CONT "integration\n");
- break;
- case sdb_type_repo_url:
- printk(KERN_CONT "Synthesis repository: %s\n",
- __sdb_string(buf, r->repo_url.repo_url));
- break;
- case sdb_type_synthesis:
- printk(KERN_CONT "Bitstream '%s' ",
- __sdb_string(buf, r->synthesis.syn_name));
- printk(KERN_CONT "synthesized %08x by %s ",
- __be32_to_cpu(r->synthesis.date),
- __sdb_string(buf, r->synthesis.user_name));
- printk(KERN_CONT "(%s version %x), ",
- __sdb_string(buf, r->synthesis.tool_name),
- __be32_to_cpu(r->synthesis.tool_version));
- printk(KERN_CONT "commit %pm\n",
- r->synthesis.commit_id);
- break;
- case sdb_type_empty:
- printk(KERN_CONT "empty\n");
- break;
- default:
- printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n",
- r->empty.record_type);
- break;
- }
- }
-}
-
void fmc_show_sdb_tree(const struct fmc_device *fmc)
{
- if (!fmc->sdb)
- return;
- __fmc_show_sdb_tree(fmc, fmc->sdb);
+ pr_err("%s: not supported anymore, use debugfs to dump SDB\n",
+ __func__);
}
EXPORT_SYMBOL(fmc_show_sdb_tree);
uint32_t device_id; /* Filled by the device */
char *mezzanine_name; /* Defaults to ``fmc'' */
void *mezzanine_data;
+
+ struct dentry *dbg_dir;
+ struct dentry *dbg_sdb_dump;
};
#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
extern int fmc_fill_id_info(struct fmc_device *fmc);
extern void fmc_free_id_info(struct fmc_device *fmc);
extern void fmc_dump_eeprom(const struct fmc_device *fmc);
-extern void fmc_dump_sdb(const struct fmc_device *fmc);
/* helpers for FMC operations */
extern int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h,