Commit | Line | Data |
---|---|---|
77864f2e AR |
1 | /* |
2 | * Copyright (C) 2012 CERN (www.cern.ch) | |
3 | * Author: Alessandro Rubini <rubini@gnudd.com> | |
4 | * | |
5 | * Released according to the GNU GPL, version 2 or any later version. | |
6 | * | |
7 | * This work is part of the White Rabbit project, a research effort led | |
8 | * by CERN, the European Institute for Nuclear Research. | |
9 | */ | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/fmc.h> | |
13 | #include <linux/ipmi-fru.h> | |
14 | ||
15 | /* The fru parser is both user and kernel capable: it needs alloc */ | |
16 | void *fru_alloc(size_t size) | |
17 | { | |
18 | return kzalloc(size, GFP_KERNEL); | |
19 | } | |
20 | ||
21 | /* The actual match function */ | |
22 | int fmc_match(struct device *dev, struct device_driver *drv) | |
23 | { | |
24 | struct fmc_driver *fdrv = to_fmc_driver(drv); | |
25 | struct fmc_device *fdev = to_fmc_device(dev); | |
26 | struct fmc_fru_id *fid; | |
27 | int i, matched = 0; | |
28 | ||
29 | /* This currently only matches the EEPROM (FRU id) */ | |
30 | fid = fdrv->id_table.fru_id; | |
31 | if (!fid) { | |
32 | dev_warn(&fdev->dev, "Driver has no ID: matches all\n"); | |
33 | matched = 1; | |
34 | } else { | |
35 | if (!fdev->id.manufacturer || !fdev->id.product_name) | |
36 | return 0; /* the device has no FRU information */ | |
37 | for (i = 0; i < fdrv->id_table.fru_id_nr; i++, fid++) { | |
38 | if (fid->manufacturer && | |
39 | strcmp(fid->manufacturer, fdev->id.manufacturer)) | |
40 | continue; | |
41 | if (fid->product_name && | |
42 | strcmp(fid->product_name, fdev->id.product_name)) | |
43 | continue; | |
44 | matched = 1; | |
45 | break; | |
46 | } | |
47 | } | |
48 | ||
49 | /* FIXME: match SDB contents */ | |
50 | return matched; | |
51 | } | |
52 | ||
53 | /* This function creates ID info for a newly registered device */ | |
54 | int fmc_fill_id_info(struct fmc_device *fmc) | |
55 | { | |
56 | struct fru_common_header *h; | |
57 | struct fru_board_info_area *bia; | |
58 | int ret, allocated = 0; | |
59 | ||
60 | /* If we know the eeprom length, try to read it off the device */ | |
61 | if (fmc->eeprom_len && !fmc->eeprom) { | |
62 | fmc->eeprom = kzalloc(fmc->eeprom_len, GFP_KERNEL); | |
63 | if (!fmc->eeprom) | |
64 | return -ENOMEM; | |
65 | allocated = 1; | |
66 | ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); | |
67 | if (ret < 0) | |
68 | goto out; | |
69 | } | |
70 | ||
71 | /* If no eeprom, continue with other matches */ | |
72 | if (!fmc->eeprom) | |
73 | return 0; | |
74 | ||
75 | dev_info(fmc->hwdev, "mezzanine %i\n", fmc->slot_id); /* header */ | |
76 | ||
77 | /* So we have the eeprom: parse the FRU part (if any) */ | |
78 | h = (void *)fmc->eeprom; | |
79 | if (h->format != 1) { | |
80 | pr_info(" EEPROM has no FRU information\n"); | |
81 | goto out; | |
82 | } | |
83 | if (!fru_header_cksum_ok(h)) { | |
84 | pr_info(" FRU: wrong header checksum\n"); | |
85 | goto out; | |
86 | } | |
87 | bia = fru_get_board_area(h); | |
88 | if (!fru_bia_cksum_ok(bia)) { | |
89 | pr_info(" FRU: wrong board area checksum\n"); | |
90 | goto out; | |
91 | } | |
92 | fmc->id.manufacturer = fru_get_board_manufacturer(h); | |
93 | fmc->id.product_name = fru_get_product_name(h); | |
94 | pr_info(" Manufacturer: %s\n", fmc->id.manufacturer); | |
95 | pr_info(" Product name: %s\n", fmc->id.product_name); | |
96 | ||
97 | /* Create the short name (FIXME: look in sdb as well) */ | |
98 | fmc->mezzanine_name = kstrdup(fmc->id.product_name, GFP_KERNEL); | |
99 | ||
100 | out: | |
101 | if (allocated) { | |
102 | kfree(fmc->eeprom); | |
103 | fmc->eeprom = NULL; | |
104 | } | |
105 | return 0; /* no error: let other identification work */ | |
106 | } | |
107 | ||
108 | /* Some ID data is allocated using fru_alloc() above, so release it */ | |
109 | void fmc_free_id_info(struct fmc_device *fmc) | |
110 | { | |
111 | kfree(fmc->mezzanine_name); | |
112 | kfree(fmc->id.manufacturer); | |
113 | kfree(fmc->id.product_name); | |
114 | } |