From 61446875cb796cf4314c50df235cf8a6d9a57ffe Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Wed, 1 Aug 2012 22:58:43 +0200
Subject: [PATCH] sdb: keep baseaddr, print absolute address; implement
 free_sdb

---
 kernel/fmc-sdb.c               | 50 ++++++++++++++++++++++++++++++----
 kernel/include/linux/fmc-sdb.h |  1 +
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/kernel/fmc-sdb.c b/kernel/fmc-sdb.c
index 6479b1f..4ebaae4 100644
--- a/kernel/fmc-sdb.c
+++ b/kernel/fmc-sdb.c
@@ -61,17 +61,26 @@ static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
 	arr->level = level;
 	arr->fmc = fmc;
 	for (i = 0; i < n; i++) {
+		union  sdb_record *r;
+
 		for (j = 0; j < sizeof(arr->record[0]); j += 4) {
 			*(uint32_t *)((void *)(arr->record + i) + j) =
 				__sdb_rd(fmc, address + (i * 64) + j, convert);
 		}
+		r = &arr->record[i];
 		arr->subtree[i] = ERR_PTR(-ENODEV);
-		if (arr->record[i].empty.record_type == sdb_type_bridge) {
-			uint64_t subaddr = arr->record[i].bridge.sdb_child;
+		if (r->empty.record_type == sdb_type_bridge) {
+			uint64_t subaddr = r->bridge.sdb_child;
+			struct sdb_component *c;
 
+			c = &r->bridge.sdb_component;
 			subaddr = __be64_to_cpu(subaddr);
 			sub = __fmc_scan_sdb_tree(fmc, subaddr, level + 1);
 			arr->subtree[i] = sub; /* may be error */
+			if (IS_ERR(sub))
+				continue;
+			sub->parent = arr;
+			sub->baseaddr = __be64_to_cpu(c->addr_first);
 		}
 	}
 	return arr;
@@ -90,12 +99,37 @@ int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
 }
 EXPORT_SYMBOL(fmc_scan_sdb_tree);
 
+static void __fmc_sdb_free(struct sdb_array *arr)
+{
+	int i, n;
+
+	if (!arr) return;
+	n = arr->len;
+	for (i = 0; i < n; i++) {
+		if (IS_ERR(arr->subtree[i]))
+			continue;
+		__fmc_sdb_free(arr->subtree[i]);
+	}
+	kfree(arr->record);
+	kfree(arr->subtree);
+	kfree(arr);
+}
+
+int fmc_free_sdb_tree(struct fmc_device *fmc)
+{
+	__fmc_sdb_free(fmc->sdb);
+	fmc->sdb = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(fmc_free_sdb_tree);
 
 static void __fmc_show_sdb_tree(struct sdb_array *arr)
 {
 	int i, j, n = arr->len, level = arr->level;
+	struct sdb_array *ap;
 
 	for (i = 0; i < n; i++) {
+		unsigned long base;
 		union  sdb_record *r;
 		struct sdb_product *p;
 		struct sdb_component *c;
@@ -104,6 +138,9 @@ static void __fmc_show_sdb_tree(struct sdb_array *arr)
 		r = &arr->record[i];
 		c = &r->dev.sdb_component;
 		p = &c->product;
+		base = 0;
+		for (ap = arr; ap; ap = ap->parent)
+			base += ap->baseaddr;
 
 		switch(r->empty.record_type) {
 		case sdb_type_interconnect:
@@ -117,14 +154,15 @@ static void __fmc_show_sdb_tree(struct sdb_array *arr)
 			       __be64_to_cpu(p->vendor_id),
 			       __be32_to_cpu(p->device_id),
 			       p->name,
-			       __be64_to_cpu(c->addr_first),
-			       __be64_to_cpu(c->addr_last));
+			       __be64_to_cpu(c->addr_first) + base,
+			       __be64_to_cpu(c->addr_last) + base);
 			break;
 		case sdb_type_bridge:
-			printk("%08llx:%08x %.19s (bridge)\n",
+			printk("%08llx:%08x %.19s (bridge: %08llx)\n",
 			       __be64_to_cpu(p->vendor_id),
 			       __be32_to_cpu(p->device_id),
-			       p->name);
+			       p->name,
+			       __be64_to_cpu(c->addr_first) + base);
 			if (IS_ERR(arr->subtree[i])) {
 				printk("(bridge error %li)\n",
 				       PTR_ERR(arr->subtree[i]));
diff --git a/kernel/include/linux/fmc-sdb.h b/kernel/include/linux/fmc-sdb.h
index 9400af7..456bc14 100644
--- a/kernel/include/linux/fmc-sdb.h
+++ b/kernel/include/linux/fmc-sdb.h
@@ -22,6 +22,7 @@ struct fmc_device;
 struct sdb_array {
 	int len;
 	int level;
+	unsigned long baseaddr;
 	struct fmc_device *fmc;		/* the device that hosts it */
 	struct sdb_array *parent;	/* NULL at root */
 	union sdb_record *record;	/* copies of the struct */
-- 
GitLab