Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3764e82e JT |
2 | #include <linux/types.h> |
3 | #include <linux/ioport.h> | |
4 | #include <linux/slab.h> | |
5 | #include <linux/export.h> | |
6 | #include <linux/io.h> | |
7 | #include <linux/mcb.h> | |
8 | ||
9 | #include "mcb-internal.h" | |
10 | ||
11 | struct mcb_parse_priv { | |
12 | phys_addr_t mapbase; | |
13 | void __iomem *base; | |
14 | }; | |
15 | ||
16 | #define for_each_chameleon_cell(dtype, p) \ | |
17 | for ((dtype) = get_next_dtype((p)); \ | |
18 | (dtype) != CHAMELEON_DTYPE_END; \ | |
19 | (dtype) = get_next_dtype((p))) | |
20 | ||
21 | static inline uint32_t get_next_dtype(void __iomem *p) | |
22 | { | |
23 | uint32_t dtype; | |
24 | ||
25 | dtype = readl(p); | |
26 | return dtype >> 28; | |
27 | } | |
28 | ||
29 | static int chameleon_parse_bdd(struct mcb_bus *bus, | |
ffc7bb38 | 30 | struct chameleon_bar *cb, |
3764e82e JT |
31 | void __iomem *base) |
32 | { | |
33 | return 0; | |
34 | } | |
35 | ||
36 | static int chameleon_parse_gdd(struct mcb_bus *bus, | |
ffc7bb38 AW |
37 | struct chameleon_bar *cb, |
38 | void __iomem *base, int bar_count) | |
3764e82e JT |
39 | { |
40 | struct chameleon_gdd __iomem *gdd = | |
41 | (struct chameleon_gdd __iomem *) base; | |
42 | struct mcb_device *mdev; | |
ffc7bb38 | 43 | u32 dev_mapbase; |
3764e82e JT |
44 | u32 offset; |
45 | u32 size; | |
46 | int ret; | |
47 | __le32 reg1; | |
48 | __le32 reg2; | |
49 | ||
50 | mdev = mcb_alloc_dev(bus); | |
51 | if (!mdev) | |
52 | return -ENOMEM; | |
53 | ||
54 | reg1 = readl(&gdd->reg1); | |
55 | reg2 = readl(&gdd->reg2); | |
56 | offset = readl(&gdd->offset); | |
57 | size = readl(&gdd->size); | |
58 | ||
59 | mdev->id = GDD_DEV(reg1); | |
60 | mdev->rev = GDD_REV(reg1); | |
61 | mdev->var = GDD_VAR(reg1); | |
f75564d3 | 62 | mdev->bar = GDD_BAR(reg2); |
3764e82e JT |
63 | mdev->group = GDD_GRP(reg2); |
64 | mdev->inst = GDD_INS(reg2); | |
65 | ||
ffc7bb38 AW |
66 | /* |
67 | * If the BAR is missing, dev_mapbase is zero, or if the | |
68 | * device is IO mapped we just print a warning and go on with the | |
69 | * next device, instead of completely stop the gdd parser | |
70 | */ | |
71 | if (mdev->bar > bar_count - 1) { | |
72 | pr_info("No BAR for 16z%03d\n", mdev->id); | |
73 | ret = 0; | |
74 | goto err; | |
75 | } | |
76 | ||
77 | dev_mapbase = cb[mdev->bar].addr; | |
78 | if (!dev_mapbase) { | |
79 | pr_info("BAR not assigned for 16z%03d\n", mdev->id); | |
80 | ret = 0; | |
81 | goto err; | |
82 | } | |
83 | ||
84 | if (dev_mapbase & 0x01) { | |
85 | pr_info("IO mapped Device (16z%03d) not yet supported\n", | |
86 | mdev->id); | |
87 | ret = 0; | |
88 | goto err; | |
89 | } | |
90 | ||
3764e82e JT |
91 | pr_debug("Found a 16z%03d\n", mdev->id); |
92 | ||
93 | mdev->irq.start = GDD_IRQ(reg1); | |
94 | mdev->irq.end = GDD_IRQ(reg1); | |
95 | mdev->irq.flags = IORESOURCE_IRQ; | |
96 | ||
ffc7bb38 AW |
97 | mdev->mem.start = dev_mapbase + offset; |
98 | ||
3764e82e JT |
99 | mdev->mem.end = mdev->mem.start + size - 1; |
100 | mdev->mem.flags = IORESOURCE_MEM; | |
101 | ||
102 | mdev->is_added = false; | |
103 | ||
104 | ret = mcb_device_register(bus, mdev); | |
105 | if (ret < 0) | |
106 | goto err; | |
107 | ||
108 | return 0; | |
109 | ||
110 | err: | |
111 | mcb_free_dev(mdev); | |
112 | ||
113 | return ret; | |
114 | } | |
115 | ||
ffc7bb38 AW |
116 | static void chameleon_parse_bar(void __iomem *base, |
117 | struct chameleon_bar *cb, int bar_count) | |
118 | { | |
119 | char __iomem *p = base; | |
120 | int i; | |
121 | ||
122 | /* skip reg1 */ | |
123 | p += sizeof(__le32); | |
124 | ||
125 | for (i = 0; i < bar_count; i++) { | |
126 | cb[i].addr = readl(p); | |
127 | cb[i].size = readl(p + 4); | |
128 | ||
129 | p += sizeof(struct chameleon_bar); | |
130 | } | |
131 | } | |
132 | ||
133 | static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase, | |
134 | struct chameleon_bar **cb) | |
135 | { | |
136 | struct chameleon_bar *c; | |
137 | int bar_count; | |
138 | __le32 reg; | |
139 | u32 dtype; | |
140 | ||
141 | /* | |
142 | * For those devices which are not connected | |
143 | * to the PCI Bus (e.g. LPC) there is a bar | |
144 | * descriptor located directly after the | |
145 | * chameleon header. This header is comparable | |
146 | * to a PCI header. | |
147 | */ | |
148 | dtype = get_next_dtype(*base); | |
149 | if (dtype == CHAMELEON_DTYPE_BAR) { | |
150 | reg = readl(*base); | |
151 | ||
152 | bar_count = BAR_CNT(reg); | |
c836790a | 153 | if (bar_count <= 0 || bar_count > CHAMELEON_BAR_MAX) |
ffc7bb38 AW |
154 | return -ENODEV; |
155 | ||
156 | c = kcalloc(bar_count, sizeof(struct chameleon_bar), | |
157 | GFP_KERNEL); | |
158 | if (!c) | |
159 | return -ENOMEM; | |
160 | ||
161 | chameleon_parse_bar(*base, c, bar_count); | |
162 | *base += BAR_DESC_SIZE(bar_count); | |
163 | } else { | |
164 | c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL); | |
165 | if (!c) | |
166 | return -ENOMEM; | |
167 | ||
168 | bar_count = 1; | |
169 | c->addr = mapbase; | |
170 | } | |
171 | ||
172 | *cb = c; | |
173 | ||
174 | return bar_count; | |
175 | } | |
176 | ||
3764e82e JT |
177 | int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, |
178 | void __iomem *base) | |
179 | { | |
3764e82e | 180 | struct chameleon_fpga_header *header; |
ffc7bb38 AW |
181 | struct chameleon_bar *cb; |
182 | char __iomem *p = base; | |
3764e82e | 183 | int num_cells = 0; |
ffc7bb38 AW |
184 | uint32_t dtype; |
185 | int bar_count; | |
5ec6bff1 | 186 | int ret; |
3764e82e JT |
187 | u32 hsize; |
188 | ||
189 | hsize = sizeof(struct chameleon_fpga_header); | |
190 | ||
191 | header = kzalloc(hsize, GFP_KERNEL); | |
192 | if (!header) | |
193 | return -ENOMEM; | |
194 | ||
195 | /* Extract header information */ | |
196 | memcpy_fromio(header, p, hsize); | |
197 | /* We only support chameleon v2 at the moment */ | |
198 | header->magic = le16_to_cpu(header->magic); | |
199 | if (header->magic != CHAMELEONV2_MAGIC) { | |
200 | pr_err("Unsupported chameleon version 0x%x\n", | |
201 | header->magic); | |
ffc7bb38 AW |
202 | ret = -ENODEV; |
203 | goto free_header; | |
3764e82e JT |
204 | } |
205 | p += hsize; | |
206 | ||
803f1ca6 JT |
207 | bus->revision = header->revision; |
208 | bus->model = header->model; | |
209 | bus->minor = header->minor; | |
210 | snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", | |
211 | header->filename); | |
3764e82e | 212 | |
ffc7bb38 | 213 | bar_count = chameleon_get_bar(&p, mapbase, &cb); |
5ec6bff1 CJ |
214 | if (bar_count < 0) { |
215 | ret = bar_count; | |
ffc7bb38 | 216 | goto free_header; |
5ec6bff1 | 217 | } |
ffc7bb38 | 218 | |
3764e82e JT |
219 | for_each_chameleon_cell(dtype, p) { |
220 | switch (dtype) { | |
221 | case CHAMELEON_DTYPE_GENERAL: | |
ffc7bb38 | 222 | ret = chameleon_parse_gdd(bus, cb, p, bar_count); |
3764e82e | 223 | if (ret < 0) |
ffc7bb38 | 224 | goto free_bar; |
3764e82e JT |
225 | p += sizeof(struct chameleon_gdd); |
226 | break; | |
227 | case CHAMELEON_DTYPE_BRIDGE: | |
ffc7bb38 | 228 | chameleon_parse_bdd(bus, cb, p); |
3764e82e JT |
229 | p += sizeof(struct chameleon_bdd); |
230 | break; | |
231 | case CHAMELEON_DTYPE_END: | |
232 | break; | |
233 | default: | |
234 | pr_err("Invalid chameleon descriptor type 0x%x\n", | |
235 | dtype); | |
ffc7bb38 AW |
236 | ret = -EINVAL; |
237 | goto free_bar; | |
3764e82e JT |
238 | } |
239 | num_cells++; | |
240 | } | |
241 | ||
242 | if (num_cells == 0) | |
243 | num_cells = -EINVAL; | |
244 | ||
ffc7bb38 | 245 | kfree(cb); |
3764e82e JT |
246 | kfree(header); |
247 | return num_cells; | |
248 | ||
ffc7bb38 AW |
249 | free_bar: |
250 | kfree(cb); | |
251 | free_header: | |
3764e82e | 252 | kfree(header); |
ffc7bb38 | 253 | |
3764e82e JT |
254 | return ret; |
255 | } | |
891e6036 | 256 | EXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB); |