diff options
Diffstat (limited to 'drivers/fmc/fmc-match.c')
-rw-r--r-- | drivers/fmc/fmc-match.c | 114 |
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 00000000000..104a5efc220 --- /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); +} |