Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-microblaze.git] / drivers / fmc / fmc-sdb.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2012 CERN (www.cern.ch)
4  * Author: Alessandro Rubini <rubini@gnudd.com>
5  *
6  * This work is part of the White Rabbit project, a research effort led
7  * by CERN, the European Institute for Nuclear Research.
8  */
9 #include <linux/module.h>
10 #include <linux/slab.h>
11 #include <linux/fmc.h>
12 #include <linux/sdb.h>
13 #include <linux/err.h>
14 #include <linux/fmc-sdb.h>
15 #include <asm/byteorder.h>
16
17 static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
18                         int convert)
19 {
20         uint32_t res = fmc_readl(fmc, address);
21         if (convert)
22                 return __be32_to_cpu(res);
23         return res;
24 }
25
26 static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
27                                              unsigned long sdb_addr,
28                                              unsigned long reg_base, int level)
29 {
30         uint32_t onew;
31         int i, j, n, convert = 0;
32         struct sdb_array *arr, *sub;
33
34         onew = fmc_readl(fmc, sdb_addr);
35         if (onew == SDB_MAGIC) {
36                 /* Uh! If we are little-endian, we must convert */
37                 if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
38                         convert = 1;
39         } else if (onew == __be32_to_cpu(SDB_MAGIC)) {
40                 /* ok, don't convert */
41         } else {
42                 return ERR_PTR(-ENOENT);
43         }
44         /* So, the magic was there: get the count from offset 4*/
45         onew = __sdb_rd(fmc, sdb_addr + 4, convert);
46         n = __be16_to_cpu(*(uint16_t *)&onew);
47         arr = kzalloc(sizeof(*arr), GFP_KERNEL);
48         if (!arr)
49                 return ERR_PTR(-ENOMEM);
50         arr->record = kcalloc(n, sizeof(arr->record[0]), GFP_KERNEL);
51         arr->subtree = kcalloc(n, sizeof(arr->subtree[0]), GFP_KERNEL);
52         if (!arr->record || !arr->subtree) {
53                 kfree(arr->record);
54                 kfree(arr->subtree);
55                 kfree(arr);
56                 return ERR_PTR(-ENOMEM);
57         }
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_raw(struct fmc_device *fmc, struct fmc_driver *d,
130                       void *gw, unsigned long len, int sdb_entry)
131 {
132         int ret;
133
134         ret = fmc->op->reprogram_raw(fmc, d, gw, len);
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
148         return 0;
149 }
150 EXPORT_SYMBOL(fmc_reprogram_raw);
151
152 /* This helper calls reprogram and inizialized sdb as well */
153 int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw,
154                          int sdb_entry)
155 {
156         int ret;
157
158         ret = fmc->op->reprogram(fmc, d, gw);
159         if (ret < 0)
160                 return ret;
161         if (sdb_entry < 0)
162                 return ret;
163
164         /* We are required to find SDB at a given offset */
165         ret = fmc_scan_sdb_tree(fmc, sdb_entry);
166         if (ret < 0) {
167                 dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n",
168                         sdb_entry);
169                 return -ENODEV;
170         }
171
172         return 0;
173 }
174 EXPORT_SYMBOL(fmc_reprogram);
175
176 void fmc_show_sdb_tree(const struct fmc_device *fmc)
177 {
178         pr_err("%s: not supported anymore, use debugfs to dump SDB\n",
179                 __func__);
180 }
181 EXPORT_SYMBOL(fmc_show_sdb_tree);
182
183 signed long fmc_find_sdb_device(struct sdb_array *tree,
184                                 uint64_t vid, uint32_t did, unsigned long *sz)
185 {
186         signed long res = -ENODEV;
187         union  sdb_record *r;
188         struct sdb_product *p;
189         struct sdb_component *c;
190         int i, n = tree->len;
191         uint64_t last, first;
192
193         /* FIXME: what if the first interconnect is not at zero? */
194         for (i = 0; i < n; i++) {
195                 r = &tree->record[i];
196                 c = &r->dev.sdb_component;
197                 p = &c->product;
198
199                 if (!IS_ERR(tree->subtree[i]))
200                         res = fmc_find_sdb_device(tree->subtree[i],
201                                                   vid, did, sz);
202                 if (res >= 0)
203                         return res + tree->baseaddr;
204                 if (r->empty.record_type != sdb_type_device)
205                         continue;
206                 if (__be64_to_cpu(p->vendor_id) != vid)
207                         continue;
208                 if (__be32_to_cpu(p->device_id) != did)
209                         continue;
210                 /* found */
211                 last = __be64_to_cpu(c->addr_last);
212                 first = __be64_to_cpu(c->addr_first);
213                 if (sz)
214                         *sz = (typeof(*sz))(last + 1 - first);
215                 return first + tree->baseaddr;
216         }
217         return res;
218 }
219 EXPORT_SYMBOL(fmc_find_sdb_device);