aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlessandro Rubini <rubini@gnudd.com>2013-06-18 17:47:13 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-06-18 18:36:56 -0400
commit77864f2e0a824a92bd93b4c9ad22c31d28ff55a6 (patch)
tree04748bc59eebcb9c127c33b897a7abb216044a3b
parent7b175c46720f8e6b92801bb634c93d1016f80c62 (diff)
FMC: add core bus driver
This module offers registration services for both carriers (i.e. devices) and mezzanines (i.e. drivers). The matching for devices and drivers is performed according to the IPMI standard for FRU devices (Field Replaceable Units). The code includes support for parsing an SDB tree if present in the FPGA, and dumping it for diagnostics. SDB is not mandatory. Files in this commit correspond to commit ab23167f in the master branch of the project hosted on ohwr.org. Signed-off-by: Alessandro Rubini <rubini@gnudd.com> Acked-by: Juan David Gonzalez Cobas <dcobas@cern.ch> Acked-by: Emilio G. Cota <cota@braap.org> Acked-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/fmc/Makefile4
-rw-r--r--drivers/fmc/fmc-core.c274
-rw-r--r--drivers/fmc/fmc-dump.c100
-rw-r--r--drivers/fmc/fmc-match.c114
-rw-r--r--drivers/fmc/fmc-sdb.c265
-rw-r--r--drivers/fmc/fru-parse.c82
6 files changed, 838 insertions, 1 deletions
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index a2784d8b5306..df9893972a62 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -2,3 +2,7 @@
2obj-$(CONFIG_FMC) += fmc.o 2obj-$(CONFIG_FMC) += fmc.o
3 3
4fmc-y = fmc-core.o 4fmc-y = fmc-core.o
5fmc-y += fmc-match.o
6fmc-y += fmc-sdb.o
7fmc-y += fru-parse.o
8fmc-y += fmc-dump.o
diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c
index fc3547f32d5e..24d52497524d 100644
--- a/drivers/fmc/fmc-core.c
+++ b/drivers/fmc/fmc-core.c
@@ -1,13 +1,285 @@
1/* Temporary placeholder so the empty code can build */ 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 */
2#include <linux/kernel.h> 10#include <linux/kernel.h>
3#include <linux/module.h> 11#include <linux/module.h>
12#include <linux/slab.h>
4#include <linux/init.h> 13#include <linux/init.h>
5#include <linux/device.h> 14#include <linux/device.h>
15#include <linux/fmc.h>
16
17static int fmc_check_version(unsigned long version, const char *name)
18{
19 if (__FMC_MAJOR(version) != FMC_MAJOR) {
20 pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n",
21 __func__, name, __FMC_MAJOR(version), FMC_MAJOR);
22 return -EINVAL;
23 }
24
25 if (__FMC_MINOR(version) != FMC_MINOR)
26 pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n",
27 __func__, name, __FMC_MINOR(version), FMC_MINOR);
28 return 0;
29}
30
31static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
32{
33 /* struct fmc_device *fdev = to_fmc_device(dev); */
34
35 /* FIXME: The MODALIAS */
36 add_uevent_var(env, "MODALIAS=%s", "fmc");
37 return 0;
38}
39
40static int fmc_probe(struct device *dev)
41{
42 struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
43 struct fmc_device *fdev = to_fmc_device(dev);
44
45 return fdrv->probe(fdev);
46}
47
48static int fmc_remove(struct device *dev)
49{
50 struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
51 struct fmc_device *fdev = to_fmc_device(dev);
52
53 return fdrv->remove(fdev);
54}
55
56static void fmc_shutdown(struct device *dev)
57{
58 /* not implemented but mandatory */
59}
6 60
7static struct bus_type fmc_bus_type = { 61static struct bus_type fmc_bus_type = {
8 .name = "fmc", 62 .name = "fmc",
63 .match = fmc_match,
64 .uevent = fmc_uevent,
65 .probe = fmc_probe,
66 .remove = fmc_remove,
67 .shutdown = fmc_shutdown,
9}; 68};
10 69
70static void fmc_release(struct device *dev)
71{
72 struct fmc_device *fmc = container_of(dev, struct fmc_device, dev);
73
74 kfree(fmc);
75}
76
77/*
78 * The eeprom is exported in sysfs, through a binary attribute
79 */
80
81static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj,
82 struct bin_attribute *bin_attr,
83 char *buf, loff_t off, size_t count)
84{
85 struct device *dev;
86 struct fmc_device *fmc;
87 int eelen;
88
89 dev = container_of(kobj, struct device, kobj);
90 fmc = container_of(dev, struct fmc_device, dev);
91 eelen = fmc->eeprom_len;
92 if (off > eelen)
93 return -ESPIPE;
94 if (off == eelen)
95 return 0; /* EOF */
96 if (off + count > eelen)
97 count = eelen - off;
98 memcpy(buf, fmc->eeprom + off, count);
99 return count;
100}
101
102static struct bin_attribute fmc_eeprom_attr = {
103 .attr = { .name = "eeprom", .mode = S_IRUGO, },
104 .size = 8192, /* more or less standard */
105 .read = fmc_read_eeprom,
106};
107
108/*
109 * Functions for client modules follow
110 */
111
112int fmc_driver_register(struct fmc_driver *drv)
113{
114 if (fmc_check_version(drv->version, drv->driver.name))
115 return -EINVAL;
116 drv->driver.bus = &fmc_bus_type;
117 return driver_register(&drv->driver);
118}
119EXPORT_SYMBOL(fmc_driver_register);
120
121void fmc_driver_unregister(struct fmc_driver *drv)
122{
123 driver_unregister(&drv->driver);
124}
125EXPORT_SYMBOL(fmc_driver_unregister);
126
127/*
128 * When a device set is registered, all eeproms must be read
129 * and all FRUs must be parsed
130 */
131int fmc_device_register_n(struct fmc_device **devs, int n)
132{
133 struct fmc_device *fmc, **devarray;
134 uint32_t device_id;
135 int i, ret = 0;
136
137 if (n < 1)
138 return 0;
139
140 /* Check the version of the first data structure (function prints) */
141 if (fmc_check_version(devs[0]->version, devs[0]->carrier_name))
142 return -EINVAL;
143
144 devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL);
145 if (!devarray)
146 return -ENOMEM;
147
148 /* Make all other checks before continuing, for all devices */
149 for (i = 0; i < n; i++) {
150 fmc = devarray[i];
151 if (!fmc->hwdev) {
152 pr_err("%s: device nr. %i has no hwdev pointer\n",
153 __func__, i);
154 ret = -EINVAL;
155 break;
156 }
157 if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) {
158 dev_info(fmc->hwdev, "absent mezzanine in slot %d\n",
159 fmc->slot_id);
160 continue;
161 }
162 if (!fmc->eeprom) {
163 dev_err(fmc->hwdev, "no eeprom provided for slot %i\n",
164 fmc->slot_id);
165 ret = -EINVAL;
166 }
167 if (!fmc->eeprom_addr) {
168 dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n",
169 fmc->slot_id);
170 ret = -EINVAL;
171 }
172 if (!fmc->carrier_name || !fmc->carrier_data ||
173 !fmc->device_id) {
174 dev_err(fmc->hwdev,
175 "deivce nr %i: carrier name, "
176 "data or dev_id not set\n", i);
177 ret = -EINVAL;
178 }
179 if (ret)
180 break;
181
182 }
183 if (ret) {
184 kfree(devarray);
185 return ret;
186 }
187
188 /* Validation is ok. Now init and register the devices */
189 for (i = 0; i < n; i++) {
190 fmc = devarray[i];
191
192 if (fmc->flags == FMC_DEVICE_NO_MEZZANINE)
193 continue; /* dev_info already done above */
194
195 fmc->nr_slots = n; /* each slot must know how many are there */
196 fmc->devarray = devarray;
197
198 device_initialize(&fmc->dev);
199 fmc->dev.release = fmc_release;
200 fmc->dev.parent = fmc->hwdev;
201
202 /* Fill the identification stuff (may fail) */
203 fmc_fill_id_info(fmc);
204
205 fmc->dev.bus = &fmc_bus_type;
206
207 /* Name from mezzanine info or carrier info. Or 0,1,2.. */
208 device_id = fmc->device_id;
209 if (!fmc->mezzanine_name)
210 dev_set_name(&fmc->dev, "fmc-%04x", device_id);
211 else
212 dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name,
213 device_id);
214 ret = device_add(&fmc->dev);
215 if (ret < 0) {
216 dev_err(fmc->hwdev, "Slot %i: Failed in registering "
217 "\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name);
218 goto out;
219 }
220 ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr);
221 if (ret < 0) {
222 dev_err(&fmc->dev, "Failed in registering eeprom\n");
223 goto out1;
224 }
225 /* This device went well, give information to the user */
226 fmc_dump_eeprom(fmc);
227 fmc_dump_sdb(fmc);
228 }
229 return 0;
230
231out1:
232 device_del(&fmc->dev);
233out:
234 fmc_free_id_info(fmc);
235 put_device(&fmc->dev);
236
237 kfree(devarray);
238 for (i--; i >= 0; i--) {
239 sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
240 device_del(&devs[i]->dev);
241 fmc_free_id_info(devs[i]);
242 put_device(&devs[i]->dev);
243 }
244 return ret;
245
246}
247EXPORT_SYMBOL(fmc_device_register_n);
248
249int fmc_device_register(struct fmc_device *fmc)
250{
251 return fmc_device_register_n(&fmc, 1);
252}
253EXPORT_SYMBOL(fmc_device_register);
254
255void fmc_device_unregister_n(struct fmc_device **devs, int n)
256{
257 int i;
258
259 if (n < 1)
260 return;
261
262 /* Free devarray first, not used by the later loop */
263 kfree(devs[0]->devarray);
264
265 for (i = 0; i < n; i++) {
266 if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE)
267 continue;
268 sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
269 device_del(&devs[i]->dev);
270 fmc_free_id_info(devs[i]);
271 put_device(&devs[i]->dev);
272 }
273}
274EXPORT_SYMBOL(fmc_device_unregister_n);
275
276void fmc_device_unregister(struct fmc_device *fmc)
277{
278 fmc_device_unregister_n(&fmc, 1);
279}
280EXPORT_SYMBOL(fmc_device_unregister);
281
282/* Init and exit are trivial */
11static int fmc_init(void) 283static int fmc_init(void)
12{ 284{
13 return bus_register(&fmc_bus_type); 285 return bus_register(&fmc_bus_type);
diff --git a/drivers/fmc/fmc-dump.c b/drivers/fmc/fmc-dump.c
new file mode 100644
index 000000000000..c91afd6388f6
--- /dev/null
+++ b/drivers/fmc/fmc-dump.c
@@ -0,0 +1,100 @@
1/*
2 * Copyright (C) 2013 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/moduleparam.h>
12#include <linux/device.h>
13#include <linux/fmc.h>
14#include <linux/fmc-sdb.h>
15
16static int fmc_must_dump_eeprom;
17module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644);
18static int fmc_must_dump_sdb;
19module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644);
20
21#define LINELEN 16
22
23/* Dumping 8k takes oh so much: avoid duplicate lines */
24static const uint8_t *dump_line(int addr, const uint8_t *line,
25 const uint8_t *prev)
26{
27 int i;
28
29 if (!prev || memcmp(line, prev, LINELEN)) {
30 pr_info("%04x: ", addr);
31 for (i = 0; i < LINELEN; ) {
32 printk(KERN_CONT "%02x", line[i]);
33 i++;
34 printk(i & 3 ? " " : i & (LINELEN - 1) ? " " : "\n");
35 }
36 return line;
37 }
38 /* repeated line */
39 if (line == prev + LINELEN)
40 pr_info("[...]\n");
41 return prev;
42}
43
44void fmc_dump_eeprom(const struct fmc_device *fmc)
45{
46 const uint8_t *line, *prev;
47 int i;
48
49 if (!fmc_must_dump_eeprom)
50 return;
51
52 pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
53 fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
54 pr_info("FMC: dumping eeprom 0x%x (%i) bytes\n", fmc->eeprom_len,
55 fmc->eeprom_len);
56
57 line = fmc->eeprom;
58 prev = NULL;
59 for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN)
60 prev = dump_line(i, line, prev);
61}
62
63void fmc_dump_sdb(const struct fmc_device *fmc)
64{
65 const uint8_t *line, *prev;
66 int i, len;
67
68 if (!fmc->sdb)
69 return;
70 if (!fmc_must_dump_sdb)
71 return;
72
73 /* If the argument is not-zero, do simple dump (== show) */
74 if (fmc_must_dump_sdb > 0)
75 fmc_show_sdb_tree(fmc);
76
77 if (fmc_must_dump_sdb == 1)
78 return;
79
80 /* If bigger than 1, dump it seriously, to help debugging */
81
82 /*
83 * Here we should really use libsdbfs (which is designed to
84 * work in kernel space as well) , but it doesn't support
85 * directories yet, and it requires better intergration (it
86 * should be used instead of fmc-specific code).
87 *
88 * So, lazily, just dump the top-level array
89 */
90 pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
91 fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
92 pr_info("FMC: poor dump of sdb first level:\n");
93
94 len = fmc->sdb->len * sizeof(union sdb_record);
95 line = (void *)fmc->sdb->record;
96 prev = NULL;
97 for (i = 0; i < len; i += LINELEN, line += LINELEN)
98 prev = dump_line(i, line, prev);
99 return;
100}
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 @@
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 */
16void *fru_alloc(size_t size)
17{
18 return kzalloc(size, GFP_KERNEL);
19}
20
21/* The actual match function */
22int 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 */
54int 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
100out:
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 */
109void 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}
diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c
new file mode 100644
index 000000000000..74fb326f4af1
--- /dev/null
+++ b/drivers/fmc/fmc-sdb.c
@@ -0,0 +1,265 @@
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/module.h>
11#include <linux/slab.h>
12#include <linux/fmc.h>
13#include <linux/sdb.h>
14#include <linux/err.h>
15#include <linux/fmc-sdb.h>
16#include <asm/byteorder.h>
17
18static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
19 int convert)
20{
21 uint32_t res = fmc_readl(fmc, address);
22 if (convert)
23 return __be32_to_cpu(res);
24 return res;
25}
26
27static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
28 unsigned long sdb_addr,
29 unsigned long reg_base, int level)
30{
31 uint32_t onew;
32 int i, j, n, convert = 0;
33 struct sdb_array *arr, *sub;
34
35 onew = fmc_readl(fmc, sdb_addr);
36 if (onew == SDB_MAGIC) {
37 /* Uh! If we are little-endian, we must convert */
38 if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
39 convert = 1;
40 } else if (onew == __be32_to_cpu(SDB_MAGIC)) {
41 /* ok, don't convert */
42 } else {
43 return ERR_PTR(-ENOENT);
44 }
45 /* So, the magic was there: get the count from offset 4*/
46 onew = __sdb_rd(fmc, sdb_addr + 4, convert);
47 n = __be16_to_cpu(*(uint16_t *)&onew);
48 arr = kzalloc(sizeof(*arr), GFP_KERNEL);
49 if (arr) {
50 arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL);
51 arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL);
52 }
53 if (!arr || !arr->record || !arr->subtree) {
54 kfree(arr->record);
55 kfree(arr->subtree);
56 kfree(arr);
57 return ERR_PTR(-ENOMEM);
58 }
59 arr->len = n;
60 arr->level = level;
61 arr->fmc = fmc;
62 for (i = 0; i < n; i++) {
63 union sdb_record *r;
64
65 for (j = 0; j < sizeof(arr->record[0]); j += 4) {
66 *(uint32_t *)((void *)(arr->record + i) + j) =
67 __sdb_rd(fmc, sdb_addr + (i * 64) + j, convert);
68 }
69 r = &arr->record[i];
70 arr->subtree[i] = ERR_PTR(-ENODEV);
71 if (r->empty.record_type == sdb_type_bridge) {
72 struct sdb_component *c = &r->bridge.sdb_component;
73 uint64_t subaddr = __be64_to_cpu(r->bridge.sdb_child);
74 uint64_t newbase = __be64_to_cpu(c->addr_first);
75
76 subaddr += reg_base;
77 newbase += reg_base;
78 sub = __fmc_scan_sdb_tree(fmc, subaddr, newbase,
79 level + 1);
80 arr->subtree[i] = sub; /* may be error */
81 if (IS_ERR(sub))
82 continue;
83 sub->parent = arr;
84 sub->baseaddr = newbase;
85 }
86 }
87 return arr;
88}
89
90int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
91{
92 struct sdb_array *ret;
93 if (fmc->sdb)
94 return -EBUSY;
95 ret = __fmc_scan_sdb_tree(fmc, address, 0 /* regs */, 0);
96 if (IS_ERR(ret))
97 return PTR_ERR(ret);
98 fmc->sdb = ret;
99 return 0;
100}
101EXPORT_SYMBOL(fmc_scan_sdb_tree);
102
103static void __fmc_sdb_free(struct sdb_array *arr)
104{
105 int i, n;
106
107 if (!arr)
108 return;
109 n = arr->len;
110 for (i = 0; i < n; i++) {
111 if (IS_ERR(arr->subtree[i]))
112 continue;
113 __fmc_sdb_free(arr->subtree[i]);
114 }
115 kfree(arr->record);
116 kfree(arr->subtree);
117 kfree(arr);
118}
119
120int fmc_free_sdb_tree(struct fmc_device *fmc)
121{
122 __fmc_sdb_free(fmc->sdb);
123 fmc->sdb = NULL;
124 return 0;
125}
126EXPORT_SYMBOL(fmc_free_sdb_tree);
127
128/* This helper calls reprogram and inizialized sdb as well */
129int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw,
130 int sdb_entry)
131{
132 int ret;
133
134 ret = fmc->op->reprogram(fmc, d, gw);
135 if (ret < 0)
136 return ret;
137 if (sdb_entry < 0)
138 return ret;
139
140 /* We are required to find SDB at a given offset */
141 ret = fmc_scan_sdb_tree(fmc, sdb_entry);
142 if (ret < 0) {
143 dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n",
144 sdb_entry);
145 return -ENODEV;
146 }
147 fmc_dump_sdb(fmc);
148 return 0;
149}
150EXPORT_SYMBOL(fmc_reprogram);
151
152static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
153 const struct sdb_array *arr)
154{
155 int i, j, n = arr->len, level = arr->level;
156 const struct sdb_array *ap;
157
158 for (i = 0; i < n; i++) {
159 unsigned long base;
160 union sdb_record *r;
161 struct sdb_product *p;
162 struct sdb_component *c;
163 r = &arr->record[i];
164 c = &r->dev.sdb_component;
165 p = &c->product;
166 base = 0;
167 for (ap = arr; ap; ap = ap->parent)
168 base += ap->baseaddr;
169 dev_info(&fmc->dev, "SDB: ");
170
171 for (j = 0; j < level; j++)
172 printk(KERN_CONT " ");
173 switch (r->empty.record_type) {
174 case sdb_type_interconnect:
175 printk(KERN_CONT "%08llx:%08x %.19s\n",
176 __be64_to_cpu(p->vendor_id),
177 __be32_to_cpu(p->device_id),
178 p->name);
179 break;
180 case sdb_type_device:
181 printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n",
182 __be64_to_cpu(p->vendor_id),
183 __be32_to_cpu(p->device_id),
184 p->name,
185 __be64_to_cpu(c->addr_first) + base,
186 __be64_to_cpu(c->addr_last) + base);
187 break;
188 case sdb_type_bridge:
189 printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n",
190 __be64_to_cpu(p->vendor_id),
191 __be32_to_cpu(p->device_id),
192 p->name,
193 __be64_to_cpu(c->addr_first) + base);
194 if (IS_ERR(arr->subtree[i])) {
195 printk(KERN_CONT "(bridge error %li)\n",
196 PTR_ERR(arr->subtree[i]));
197 break;
198 }
199 __fmc_show_sdb_tree(fmc, arr->subtree[i]);
200 break;
201 case sdb_type_integration:
202 printk(KERN_CONT "integration\n");
203 break;
204 case sdb_type_repo_url:
205 printk(KERN_CONT "repo-url\n");
206 break;
207 case sdb_type_synthesis:
208 printk(KERN_CONT "synthesis-info\n");
209 break;
210 case sdb_type_empty:
211 printk(KERN_CONT "empty\n");
212 break;
213 default:
214 printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n",
215 r->empty.record_type);
216 break;
217 }
218 }
219}
220
221void fmc_show_sdb_tree(const struct fmc_device *fmc)
222{
223 if (!fmc->sdb)
224 return;
225 __fmc_show_sdb_tree(fmc, fmc->sdb);
226}
227EXPORT_SYMBOL(fmc_show_sdb_tree);
228
229signed long fmc_find_sdb_device(struct sdb_array *tree,
230 uint64_t vid, uint32_t did, unsigned long *sz)
231{
232 signed long res = -ENODEV;
233 union sdb_record *r;
234 struct sdb_product *p;
235 struct sdb_component *c;
236 int i, n = tree->len;
237 uint64_t last, first;
238
239 /* FIXME: what if the first interconnect is not at zero? */
240 for (i = 0; i < n; i++) {
241 r = &tree->record[i];
242 c = &r->dev.sdb_component;
243 p = &c->product;
244
245 if (!IS_ERR(tree->subtree[i]))
246 res = fmc_find_sdb_device(tree->subtree[i],
247 vid, did, sz);
248 if (res >= 0)
249 return res + tree->baseaddr;
250 if (r->empty.record_type != sdb_type_device)
251 continue;
252 if (__be64_to_cpu(p->vendor_id) != vid)
253 continue;
254 if (__be32_to_cpu(p->device_id) != did)
255 continue;
256 /* found */
257 last = __be64_to_cpu(c->addr_last);
258 first = __be64_to_cpu(c->addr_first);
259 if (sz)
260 *sz = (typeof(*sz))(last + 1 - first);
261 return first + tree->baseaddr;
262 }
263 return res;
264}
265EXPORT_SYMBOL(fmc_find_sdb_device);
diff --git a/drivers/fmc/fru-parse.c b/drivers/fmc/fru-parse.c
new file mode 100644
index 000000000000..cb46263c5da2
--- /dev/null
+++ b/drivers/fmc/fru-parse.c
@@ -0,0 +1,82 @@
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/ipmi-fru.h>
11
12/* Some internal helpers */
13static struct fru_type_length *
14__fru_get_board_tl(struct fru_common_header *header, int nr)
15{
16 struct fru_board_info_area *bia;
17 struct fru_type_length *tl;
18
19 bia = fru_get_board_area(header);
20 tl = bia->tl;
21 while (nr > 0 && !fru_is_eof(tl)) {
22 tl = fru_next_tl(tl);
23 nr--;
24 }
25 if (fru_is_eof(tl))
26 return NULL;
27 return tl;
28}
29
30static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr)
31{
32 struct fru_type_length *tl;
33 char *res;
34 int len;
35
36 tl = __fru_get_board_tl(header, nr);
37 if (!tl)
38 return NULL;
39 len = fru_strlen(tl);
40 res = fru_alloc(fru_strlen(tl) + 1);
41 if (!res)
42 return NULL;
43 return fru_strcpy(res, tl);
44}
45
46/* Public checksum verifiers */
47int fru_header_cksum_ok(struct fru_common_header *header)
48{
49 uint8_t *ptr = (void *)header;
50 int i, sum;
51
52 for (i = sum = 0; i < sizeof(*header); i++)
53 sum += ptr[i];
54 return (sum & 0xff) == 0;
55}
56int fru_bia_cksum_ok(struct fru_board_info_area *bia)
57{
58 uint8_t *ptr = (void *)bia;
59 int i, sum;
60
61 for (i = sum = 0; i < 8 * bia->area_len; i++)
62 sum += ptr[i];
63 return (sum & 0xff) == 0;
64}
65
66/* Get various stuff, trivial */
67char *fru_get_board_manufacturer(struct fru_common_header *header)
68{
69 return __fru_alloc_get_tl(header, 0);
70}
71char *fru_get_product_name(struct fru_common_header *header)
72{
73 return __fru_alloc_get_tl(header, 1);
74}
75char *fru_get_serial_number(struct fru_common_header *header)
76{
77 return __fru_alloc_get_tl(header, 2);
78}
79char *fru_get_part_number(struct fru_common_header *header)
80{
81 return __fru_alloc_get_tl(header, 3);
82}