]>
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/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 | ||
18 | static 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 | ||
27 | static 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 | ||
90 | int 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 | } | |
101 | EXPORT_SYMBOL(fmc_scan_sdb_tree); | |
102 | ||
103 | static 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 | ||
120 | int fmc_free_sdb_tree(struct fmc_device *fmc) | |
121 | { | |
122 | __fmc_sdb_free(fmc->sdb); | |
123 | fmc->sdb = NULL; | |
124 | return 0; | |
125 | } | |
126 | EXPORT_SYMBOL(fmc_free_sdb_tree); | |
127 | ||
128 | /* This helper calls reprogram and inizialized sdb as well */ | |
129 | int 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 | } | |
150 | EXPORT_SYMBOL(fmc_reprogram); | |
151 | ||
152 | static 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 | ||
221 | void 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 | } | |
227 | EXPORT_SYMBOL(fmc_show_sdb_tree); | |
228 | ||
229 | signed 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 | } | |
265 | EXPORT_SYMBOL(fmc_find_sdb_device); |