aboutsummaryrefslogtreecommitdiff
path: root/drivers/fmc/fmc-match.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/fmc/fmc-match.c')
-rw-r--r--drivers/fmc/fmc-match.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/drivers/fmc/fmc-match.c b/drivers/fmc/fmc-match.c
new file mode 100644
index 000000000000..104a5efc2207
--- /dev/null
+++ b/drivers/fmc/fmc-match.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fmc.h>
+#include <linux/ipmi-fru.h>
+
+/* The fru parser is both user and kernel capable: it needs alloc */
+void *fru_alloc(size_t size)
+{
+ return kzalloc(size, GFP_KERNEL);
+}
+
+/* The actual match function */
+int fmc_match(struct device *dev, struct device_driver *drv)
+{
+ struct fmc_driver *fdrv = to_fmc_driver(drv);
+ struct fmc_device *fdev = to_fmc_device(dev);
+ struct fmc_fru_id *fid;
+ int i, matched = 0;
+
+ /* This currently only matches the EEPROM (FRU id) */
+ fid = fdrv->id_table.fru_id;
+ if (!fid) {
+ dev_warn(&fdev->dev, "Driver has no ID: matches all\n");
+ matched = 1;
+ } else {
+ if (!fdev->id.manufacturer || !fdev->id.product_name)
+ return 0; /* the device has no FRU information */
+ for (i = 0; i < fdrv->id_table.fru_id_nr; i++, fid++) {
+ if (fid->manufacturer &&
+ strcmp(fid->manufacturer, fdev->id.manufacturer))
+ continue;
+ if (fid->product_name &&
+ strcmp(fid->product_name, fdev->id.product_name))
+ continue;
+ matched = 1;
+ break;
+ }
+ }
+
+ /* FIXME: match SDB contents */
+ return matched;
+}
+
+/* This function creates ID info for a newly registered device */
+int fmc_fill_id_info(struct fmc_device *fmc)
+{
+ struct fru_common_header *h;
+ struct fru_board_info_area *bia;
+ int ret, allocated = 0;
+
+ /* If we know the eeprom length, try to read it off the device */
+ if (fmc->eeprom_len && !fmc->eeprom) {
+ fmc->eeprom = kzalloc(fmc->eeprom_len, GFP_KERNEL);
+ if (!fmc->eeprom)
+ return -ENOMEM;
+ allocated = 1;
+ ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* If no eeprom, continue with other matches */
+ if (!fmc->eeprom)
+ return 0;
+
+ dev_info(fmc->hwdev, "mezzanine %i\n", fmc->slot_id); /* header */
+
+ /* So we have the eeprom: parse the FRU part (if any) */
+ h = (void *)fmc->eeprom;
+ if (h->format != 1) {
+ pr_info(" EEPROM has no FRU information\n");
+ goto out;
+ }
+ if (!fru_header_cksum_ok(h)) {
+ pr_info(" FRU: wrong header checksum\n");
+ goto out;
+ }
+ bia = fru_get_board_area(h);
+ if (!fru_bia_cksum_ok(bia)) {
+ pr_info(" FRU: wrong board area checksum\n");
+ goto out;
+ }
+ fmc->id.manufacturer = fru_get_board_manufacturer(h);
+ fmc->id.product_name = fru_get_product_name(h);
+ pr_info(" Manufacturer: %s\n", fmc->id.manufacturer);
+ pr_info(" Product name: %s\n", fmc->id.product_name);
+
+ /* Create the short name (FIXME: look in sdb as well) */
+ fmc->mezzanine_name = kstrdup(fmc->id.product_name, GFP_KERNEL);
+
+out:
+ if (allocated) {
+ kfree(fmc->eeprom);
+ fmc->eeprom = NULL;
+ }
+ return 0; /* no error: let other identification work */
+}
+
+/* Some ID data is allocated using fru_alloc() above, so release it */
+void fmc_free_id_info(struct fmc_device *fmc)
+{
+ kfree(fmc->mezzanine_name);
+ kfree(fmc->id.manufacturer);
+ kfree(fmc->id.product_name);
+}