]>
Commit | Line | Data |
---|---|---|
77864f2e AR |
1 | /* |
2 | * Copyright (C) 2012 CERN (www.cern.ch) | |
3 | * Author: Alessandro Rubini <[email protected]> | |
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); | |
e42d50ba DC |
49 | if (!arr) |
50 | return ERR_PTR(-ENOMEM); | |
51 | arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL); | |
52 | arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL); | |
53 | if (!arr->record || !arr->subtree) { | |
77864f2e AR |
54 | kfree(arr->record); |
55 | kfree(arr->subtree); | |
56 | kfree(arr); | |
57 | return ERR_PTR(-ENOMEM); | |
58 | } | |
e42d50ba | 59 | |
77864f2e AR |
60 | arr->len = n; |
61 | arr->level = level; | |
62 | arr->fmc = fmc; | |
63 | for (i = 0; i < n; i++) { | |
64 | union sdb_record *r; | |
65 | ||
66 | for (j = 0; j < sizeof(arr->record[0]); j += 4) { | |
67 | *(uint32_t *)((void *)(arr->record + i) + j) = | |
68 | __sdb_rd(fmc, sdb_addr + (i * 64) + j, convert); | |
69 | } | |
70 | r = &arr->record[i]; | |
71 | arr->subtree[i] = ERR_PTR(-ENODEV); | |
72 | if (r->empty.record_type == sdb_type_bridge) { | |
73 | struct sdb_component *c = &r->bridge.sdb_component; | |
74 | uint64_t subaddr = __be64_to_cpu(r->bridge.sdb_child); | |
75 | uint64_t newbase = __be64_to_cpu(c->addr_first); | |
76 | ||
77 | subaddr += reg_base; | |
78 | newbase += reg_base; | |
79 | sub = __fmc_scan_sdb_tree(fmc, subaddr, newbase, | |
80 | level + 1); | |
81 | arr->subtree[i] = sub; /* may be error */ | |
82 | if (IS_ERR(sub)) | |
83 | continue; | |
84 | sub->parent = arr; | |
85 | sub->baseaddr = newbase; | |
86 | } | |
87 | } | |
88 | return arr; | |
89 | } | |
90 | ||
91 | int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address) | |
92 | { | |
93 | struct sdb_array *ret; | |
94 | if (fmc->sdb) | |
95 | return -EBUSY; | |
96 | ret = __fmc_scan_sdb_tree(fmc, address, 0 /* regs */, 0); | |
97 | if (IS_ERR(ret)) | |
98 | return PTR_ERR(ret); | |
99 | fmc->sdb = ret; | |
100 | return 0; | |
101 | } | |
102 | EXPORT_SYMBOL(fmc_scan_sdb_tree); | |
103 | ||
104 | static void __fmc_sdb_free(struct sdb_array *arr) | |
105 | { | |
106 | int i, n; | |
107 | ||
108 | if (!arr) | |
109 | return; | |
110 | n = arr->len; | |
111 | for (i = 0; i < n; i++) { | |
112 | if (IS_ERR(arr->subtree[i])) | |
113 | continue; | |
114 | __fmc_sdb_free(arr->subtree[i]); | |
115 | } | |
116 | kfree(arr->record); | |
117 | kfree(arr->subtree); | |
118 | kfree(arr); | |
119 | } | |
120 | ||
121 | int fmc_free_sdb_tree(struct fmc_device *fmc) | |
122 | { | |
123 | __fmc_sdb_free(fmc->sdb); | |
124 | fmc->sdb = NULL; | |
125 | return 0; | |
126 | } | |
127 | EXPORT_SYMBOL(fmc_free_sdb_tree); | |
128 | ||
129 | /* This helper calls reprogram and inizialized sdb as well */ | |
130 | int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, | |
131 | int sdb_entry) | |
132 | { | |
133 | int ret; | |
134 | ||
135 | ret = fmc->op->reprogram(fmc, d, gw); | |
136 | if (ret < 0) | |
137 | return ret; | |
138 | if (sdb_entry < 0) | |
139 | return ret; | |
140 | ||
141 | /* We are required to find SDB at a given offset */ | |
142 | ret = fmc_scan_sdb_tree(fmc, sdb_entry); | |
143 | if (ret < 0) { | |
144 | dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n", | |
145 | sdb_entry); | |
146 | return -ENODEV; | |
147 | } | |
148 | fmc_dump_sdb(fmc); | |
149 | return 0; | |
150 | } | |
151 | EXPORT_SYMBOL(fmc_reprogram); | |
152 | ||
2e70efd9 AR |
153 | static char *__strip_trailing_space(char *buf, char *str, int len) |
154 | { | |
155 | int i = len - 1; | |
156 | ||
157 | memcpy(buf, str, len); | |
158 | while(i >= 0 && buf[i] == ' ') | |
159 | buf[i--] = '\0'; | |
160 | return buf; | |
161 | } | |
162 | ||
163 | #define __sdb_string(buf, field) ({ \ | |
164 | BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ | |
165 | __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ | |
166 | }) | |
167 | ||
77864f2e AR |
168 | static void __fmc_show_sdb_tree(const struct fmc_device *fmc, |
169 | const struct sdb_array *arr) | |
170 | { | |
83b1bfba | 171 | unsigned long base = arr->baseaddr; |
77864f2e | 172 | int i, j, n = arr->len, level = arr->level; |
2e70efd9 | 173 | char buf[64]; |
77864f2e AR |
174 | |
175 | for (i = 0; i < n; i++) { | |
77864f2e AR |
176 | union sdb_record *r; |
177 | struct sdb_product *p; | |
178 | struct sdb_component *c; | |
179 | r = &arr->record[i]; | |
180 | c = &r->dev.sdb_component; | |
181 | p = &c->product; | |
83b1bfba | 182 | |
77864f2e AR |
183 | dev_info(&fmc->dev, "SDB: "); |
184 | ||
185 | for (j = 0; j < level; j++) | |
186 | printk(KERN_CONT " "); | |
187 | switch (r->empty.record_type) { | |
188 | case sdb_type_interconnect: | |
189 | printk(KERN_CONT "%08llx:%08x %.19s\n", | |
190 | __be64_to_cpu(p->vendor_id), | |
191 | __be32_to_cpu(p->device_id), | |
192 | p->name); | |
193 | break; | |
194 | case sdb_type_device: | |
195 | printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n", | |
196 | __be64_to_cpu(p->vendor_id), | |
197 | __be32_to_cpu(p->device_id), | |
198 | p->name, | |
199 | __be64_to_cpu(c->addr_first) + base, | |
200 | __be64_to_cpu(c->addr_last) + base); | |
201 | break; | |
202 | case sdb_type_bridge: | |
203 | printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n", | |
204 | __be64_to_cpu(p->vendor_id), | |
205 | __be32_to_cpu(p->device_id), | |
206 | p->name, | |
207 | __be64_to_cpu(c->addr_first) + base); | |
208 | if (IS_ERR(arr->subtree[i])) { | |
2e70efd9 AR |
209 | dev_info(&fmc->dev, "SDB: (bridge error %li)\n", |
210 | PTR_ERR(arr->subtree[i])); | |
77864f2e AR |
211 | break; |
212 | } | |
213 | __fmc_show_sdb_tree(fmc, arr->subtree[i]); | |
214 | break; | |
215 | case sdb_type_integration: | |
216 | printk(KERN_CONT "integration\n"); | |
217 | break; | |
218 | case sdb_type_repo_url: | |
2e70efd9 AR |
219 | printk(KERN_CONT "Synthesis repository: %s\n", |
220 | __sdb_string(buf, r->repo_url.repo_url)); | |
77864f2e AR |
221 | break; |
222 | case sdb_type_synthesis: | |
2e70efd9 AR |
223 | printk(KERN_CONT "Bitstream '%s' ", |
224 | __sdb_string(buf, r->synthesis.syn_name)); | |
225 | printk(KERN_CONT "synthesized %08x by %s ", | |
226 | __be32_to_cpu(r->synthesis.date), | |
227 | __sdb_string(buf, r->synthesis.user_name)); | |
228 | printk(KERN_CONT "(%s version %x), ", | |
229 | __sdb_string(buf, r->synthesis.tool_name), | |
230 | __be32_to_cpu(r->synthesis.tool_version)); | |
231 | printk(KERN_CONT "commit %pm\n", | |
232 | r->synthesis.commit_id); | |
77864f2e AR |
233 | break; |
234 | case sdb_type_empty: | |
235 | printk(KERN_CONT "empty\n"); | |
236 | break; | |
237 | default: | |
238 | printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n", | |
239 | r->empty.record_type); | |
240 | break; | |
241 | } | |
242 | } | |
243 | } | |
244 | ||
245 | void fmc_show_sdb_tree(const struct fmc_device *fmc) | |
246 | { | |
247 | if (!fmc->sdb) | |
248 | return; | |
249 | __fmc_show_sdb_tree(fmc, fmc->sdb); | |
250 | } | |
251 | EXPORT_SYMBOL(fmc_show_sdb_tree); | |
252 | ||
253 | signed long fmc_find_sdb_device(struct sdb_array *tree, | |
254 | uint64_t vid, uint32_t did, unsigned long *sz) | |
255 | { | |
256 | signed long res = -ENODEV; | |
257 | union sdb_record *r; | |
258 | struct sdb_product *p; | |
259 | struct sdb_component *c; | |
260 | int i, n = tree->len; | |
261 | uint64_t last, first; | |
262 | ||
263 | /* FIXME: what if the first interconnect is not at zero? */ | |
264 | for (i = 0; i < n; i++) { | |
265 | r = &tree->record[i]; | |
266 | c = &r->dev.sdb_component; | |
267 | p = &c->product; | |
268 | ||
269 | if (!IS_ERR(tree->subtree[i])) | |
270 | res = fmc_find_sdb_device(tree->subtree[i], | |
271 | vid, did, sz); | |
272 | if (res >= 0) | |
273 | return res + tree->baseaddr; | |
274 | if (r->empty.record_type != sdb_type_device) | |
275 | continue; | |
276 | if (__be64_to_cpu(p->vendor_id) != vid) | |
277 | continue; | |
278 | if (__be32_to_cpu(p->device_id) != did) | |
279 | continue; | |
280 | /* found */ | |
281 | last = __be64_to_cpu(c->addr_last); | |
282 | first = __be64_to_cpu(c->addr_first); | |
283 | if (sz) | |
284 | *sz = (typeof(*sz))(last + 1 - first); | |
285 | return first + tree->baseaddr; | |
286 | } | |
287 | return res; | |
288 | } | |
289 | EXPORT_SYMBOL(fmc_find_sdb_device); |