]>
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 | */ | |
9c9f32ed AR |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> | |
77864f2e | 12 | #include <linux/slab.h> |
9c9f32ed AR |
13 | #include <linux/init.h> |
14 | #include <linux/device.h> | |
77864f2e | 15 | #include <linux/fmc.h> |
2071a3e9 FV |
16 | #include <linux/fmc-sdb.h> |
17 | ||
18 | #include "fmc-private.h" | |
77864f2e AR |
19 | |
20 | static int fmc_check_version(unsigned long version, const char *name) | |
21 | { | |
22 | if (__FMC_MAJOR(version) != FMC_MAJOR) { | |
23 | pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n", | |
24 | __func__, name, __FMC_MAJOR(version), FMC_MAJOR); | |
25 | return -EINVAL; | |
26 | } | |
27 | ||
28 | if (__FMC_MINOR(version) != FMC_MINOR) | |
29 | pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n", | |
30 | __func__, name, __FMC_MINOR(version), FMC_MINOR); | |
31 | return 0; | |
32 | } | |
33 | ||
34 | static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env) | |
35 | { | |
36 | /* struct fmc_device *fdev = to_fmc_device(dev); */ | |
37 | ||
38 | /* FIXME: The MODALIAS */ | |
39 | add_uevent_var(env, "MODALIAS=%s", "fmc"); | |
40 | return 0; | |
41 | } | |
42 | ||
43 | static int fmc_probe(struct device *dev) | |
44 | { | |
45 | struct fmc_driver *fdrv = to_fmc_driver(dev->driver); | |
46 | struct fmc_device *fdev = to_fmc_device(dev); | |
47 | ||
48 | return fdrv->probe(fdev); | |
49 | } | |
50 | ||
51 | static int fmc_remove(struct device *dev) | |
52 | { | |
53 | struct fmc_driver *fdrv = to_fmc_driver(dev->driver); | |
54 | struct fmc_device *fdev = to_fmc_device(dev); | |
55 | ||
56 | return fdrv->remove(fdev); | |
57 | } | |
58 | ||
59 | static void fmc_shutdown(struct device *dev) | |
60 | { | |
61 | /* not implemented but mandatory */ | |
62 | } | |
9c9f32ed AR |
63 | |
64 | static struct bus_type fmc_bus_type = { | |
65 | .name = "fmc", | |
77864f2e AR |
66 | .match = fmc_match, |
67 | .uevent = fmc_uevent, | |
68 | .probe = fmc_probe, | |
69 | .remove = fmc_remove, | |
70 | .shutdown = fmc_shutdown, | |
9c9f32ed AR |
71 | }; |
72 | ||
77864f2e AR |
73 | static void fmc_release(struct device *dev) |
74 | { | |
75 | struct fmc_device *fmc = container_of(dev, struct fmc_device, dev); | |
76 | ||
77 | kfree(fmc); | |
78 | } | |
79 | ||
80 | /* | |
81 | * The eeprom is exported in sysfs, through a binary attribute | |
82 | */ | |
83 | ||
84 | static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj, | |
85 | struct bin_attribute *bin_attr, | |
86 | char *buf, loff_t off, size_t count) | |
87 | { | |
88 | struct device *dev; | |
89 | struct fmc_device *fmc; | |
90 | int eelen; | |
91 | ||
92 | dev = container_of(kobj, struct device, kobj); | |
93 | fmc = container_of(dev, struct fmc_device, dev); | |
94 | eelen = fmc->eeprom_len; | |
95 | if (off > eelen) | |
96 | return -ESPIPE; | |
97 | if (off == eelen) | |
98 | return 0; /* EOF */ | |
99 | if (off + count > eelen) | |
100 | count = eelen - off; | |
101 | memcpy(buf, fmc->eeprom + off, count); | |
102 | return count; | |
103 | } | |
104 | ||
5c9a8736 AR |
105 | static ssize_t fmc_write_eeprom(struct file *file, struct kobject *kobj, |
106 | struct bin_attribute *bin_attr, | |
107 | char *buf, loff_t off, size_t count) | |
108 | { | |
109 | struct device *dev; | |
110 | struct fmc_device *fmc; | |
111 | ||
112 | dev = container_of(kobj, struct device, kobj); | |
113 | fmc = container_of(dev, struct fmc_device, dev); | |
114 | return fmc->op->write_ee(fmc, off, buf, count); | |
115 | } | |
116 | ||
77864f2e | 117 | static struct bin_attribute fmc_eeprom_attr = { |
5c9a8736 | 118 | .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, }, |
77864f2e AR |
119 | .size = 8192, /* more or less standard */ |
120 | .read = fmc_read_eeprom, | |
5c9a8736 | 121 | .write = fmc_write_eeprom, |
77864f2e AR |
122 | }; |
123 | ||
9f757f41 FV |
124 | int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h, |
125 | char *name, int flags) | |
126 | { | |
127 | if (fmc->op->irq_request) | |
128 | return fmc->op->irq_request(fmc, h, name, flags); | |
129 | return -EPERM; | |
130 | } | |
131 | EXPORT_SYMBOL(fmc_irq_request); | |
132 | ||
133 | void fmc_irq_free(struct fmc_device *fmc) | |
134 | { | |
135 | if (fmc->op->irq_free) | |
136 | fmc->op->irq_free(fmc); | |
137 | } | |
138 | EXPORT_SYMBOL(fmc_irq_free); | |
139 | ||
140 | void fmc_irq_ack(struct fmc_device *fmc) | |
141 | { | |
142 | if (likely(fmc->op->irq_ack)) | |
143 | fmc->op->irq_ack(fmc); | |
144 | } | |
145 | EXPORT_SYMBOL(fmc_irq_ack); | |
146 | ||
147 | int fmc_validate(struct fmc_device *fmc, struct fmc_driver *drv) | |
148 | { | |
149 | if (fmc->op->validate) | |
150 | return fmc->op->validate(fmc, drv); | |
151 | return -EPERM; | |
152 | } | |
153 | EXPORT_SYMBOL(fmc_validate); | |
154 | ||
155 | int fmc_gpio_config(struct fmc_device *fmc, struct fmc_gpio *gpio, int ngpio) | |
156 | { | |
157 | if (fmc->op->gpio_config) | |
158 | return fmc->op->gpio_config(fmc, gpio, ngpio); | |
159 | return -EPERM; | |
160 | } | |
161 | EXPORT_SYMBOL(fmc_gpio_config); | |
162 | ||
163 | int fmc_read_ee(struct fmc_device *fmc, int pos, void *d, int l) | |
164 | { | |
165 | if (fmc->op->read_ee) | |
166 | return fmc->op->read_ee(fmc, pos, d, l); | |
167 | return -EPERM; | |
168 | } | |
169 | EXPORT_SYMBOL(fmc_read_ee); | |
170 | ||
171 | int fmc_write_ee(struct fmc_device *fmc, int pos, const void *d, int l) | |
172 | { | |
173 | if (fmc->op->write_ee) | |
174 | return fmc->op->write_ee(fmc, pos, d, l); | |
175 | return -EPERM; | |
176 | } | |
177 | EXPORT_SYMBOL(fmc_write_ee); | |
178 | ||
77864f2e AR |
179 | /* |
180 | * Functions for client modules follow | |
181 | */ | |
182 | ||
183 | int fmc_driver_register(struct fmc_driver *drv) | |
184 | { | |
185 | if (fmc_check_version(drv->version, drv->driver.name)) | |
186 | return -EINVAL; | |
187 | drv->driver.bus = &fmc_bus_type; | |
188 | return driver_register(&drv->driver); | |
189 | } | |
190 | EXPORT_SYMBOL(fmc_driver_register); | |
191 | ||
192 | void fmc_driver_unregister(struct fmc_driver *drv) | |
193 | { | |
194 | driver_unregister(&drv->driver); | |
195 | } | |
196 | EXPORT_SYMBOL(fmc_driver_unregister); | |
197 | ||
198 | /* | |
199 | * When a device set is registered, all eeproms must be read | |
200 | * and all FRUs must be parsed | |
201 | */ | |
15b1b0f0 FV |
202 | int fmc_device_register_n_gw(struct fmc_device **devs, int n, |
203 | struct fmc_gateware *gw) | |
77864f2e AR |
204 | { |
205 | struct fmc_device *fmc, **devarray; | |
206 | uint32_t device_id; | |
207 | int i, ret = 0; | |
208 | ||
209 | if (n < 1) | |
210 | return 0; | |
211 | ||
212 | /* Check the version of the first data structure (function prints) */ | |
213 | if (fmc_check_version(devs[0]->version, devs[0]->carrier_name)) | |
214 | return -EINVAL; | |
215 | ||
216 | devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL); | |
217 | if (!devarray) | |
218 | return -ENOMEM; | |
219 | ||
220 | /* Make all other checks before continuing, for all devices */ | |
221 | for (i = 0; i < n; i++) { | |
222 | fmc = devarray[i]; | |
223 | if (!fmc->hwdev) { | |
224 | pr_err("%s: device nr. %i has no hwdev pointer\n", | |
225 | __func__, i); | |
226 | ret = -EINVAL; | |
227 | break; | |
228 | } | |
e4d6c4b7 | 229 | if (fmc->flags & FMC_DEVICE_NO_MEZZANINE) { |
77864f2e AR |
230 | dev_info(fmc->hwdev, "absent mezzanine in slot %d\n", |
231 | fmc->slot_id); | |
232 | continue; | |
233 | } | |
234 | if (!fmc->eeprom) { | |
235 | dev_err(fmc->hwdev, "no eeprom provided for slot %i\n", | |
236 | fmc->slot_id); | |
237 | ret = -EINVAL; | |
238 | } | |
239 | if (!fmc->eeprom_addr) { | |
240 | dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n", | |
241 | fmc->slot_id); | |
242 | ret = -EINVAL; | |
243 | } | |
244 | if (!fmc->carrier_name || !fmc->carrier_data || | |
245 | !fmc->device_id) { | |
246 | dev_err(fmc->hwdev, | |
247 | "deivce nr %i: carrier name, " | |
248 | "data or dev_id not set\n", i); | |
249 | ret = -EINVAL; | |
250 | } | |
251 | if (ret) | |
252 | break; | |
253 | ||
254 | } | |
255 | if (ret) { | |
256 | kfree(devarray); | |
257 | return ret; | |
258 | } | |
259 | ||
260 | /* Validation is ok. Now init and register the devices */ | |
261 | for (i = 0; i < n; i++) { | |
262 | fmc = devarray[i]; | |
263 | ||
77864f2e AR |
264 | fmc->nr_slots = n; /* each slot must know how many are there */ |
265 | fmc->devarray = devarray; | |
266 | ||
267 | device_initialize(&fmc->dev); | |
268 | fmc->dev.release = fmc_release; | |
269 | fmc->dev.parent = fmc->hwdev; | |
270 | ||
271 | /* Fill the identification stuff (may fail) */ | |
272 | fmc_fill_id_info(fmc); | |
273 | ||
274 | fmc->dev.bus = &fmc_bus_type; | |
275 | ||
276 | /* Name from mezzanine info or carrier info. Or 0,1,2.. */ | |
277 | device_id = fmc->device_id; | |
278 | if (!fmc->mezzanine_name) | |
279 | dev_set_name(&fmc->dev, "fmc-%04x", device_id); | |
280 | else | |
281 | dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, | |
282 | device_id); | |
9c0dda14 FV |
283 | |
284 | if (gw) { | |
285 | /* | |
286 | * The carrier already know the bitstream to load | |
287 | * for this set of FMC mezzanines. | |
288 | */ | |
289 | ret = fmc->op->reprogram_raw(fmc, NULL, | |
290 | gw->bitstream, gw->len); | |
291 | if (ret) { | |
292 | dev_warn(fmc->hwdev, | |
293 | "Invalid gateware for FMC mezzanine\n"); | |
294 | goto out; | |
295 | } | |
296 | } | |
297 | ||
77864f2e AR |
298 | ret = device_add(&fmc->dev); |
299 | if (ret < 0) { | |
300 | dev_err(fmc->hwdev, "Slot %i: Failed in registering " | |
301 | "\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name); | |
302 | goto out; | |
303 | } | |
304 | ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr); | |
305 | if (ret < 0) { | |
306 | dev_err(&fmc->dev, "Failed in registering eeprom\n"); | |
307 | goto out1; | |
308 | } | |
309 | /* This device went well, give information to the user */ | |
310 | fmc_dump_eeprom(fmc); | |
2071a3e9 | 311 | fmc_debug_init(fmc); |
77864f2e AR |
312 | } |
313 | return 0; | |
314 | ||
315 | out1: | |
316 | device_del(&fmc->dev); | |
317 | out: | |
77864f2e AR |
318 | kfree(devarray); |
319 | for (i--; i >= 0; i--) { | |
2071a3e9 | 320 | fmc_debug_exit(devs[i]); |
77864f2e AR |
321 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); |
322 | device_del(&devs[i]->dev); | |
323 | fmc_free_id_info(devs[i]); | |
324 | put_device(&devs[i]->dev); | |
325 | } | |
326 | return ret; | |
327 | ||
328 | } | |
15b1b0f0 FV |
329 | EXPORT_SYMBOL(fmc_device_register_n_gw); |
330 | ||
331 | int fmc_device_register_n(struct fmc_device **devs, int n) | |
332 | { | |
333 | return fmc_device_register_n_gw(devs, n, NULL); | |
334 | } | |
77864f2e AR |
335 | EXPORT_SYMBOL(fmc_device_register_n); |
336 | ||
15b1b0f0 FV |
337 | int fmc_device_register_gw(struct fmc_device *fmc, struct fmc_gateware *gw) |
338 | { | |
339 | return fmc_device_register_n_gw(&fmc, 1, gw); | |
340 | } | |
341 | EXPORT_SYMBOL(fmc_device_register_gw); | |
342 | ||
77864f2e AR |
343 | int fmc_device_register(struct fmc_device *fmc) |
344 | { | |
345 | return fmc_device_register_n(&fmc, 1); | |
346 | } | |
347 | EXPORT_SYMBOL(fmc_device_register); | |
348 | ||
349 | void fmc_device_unregister_n(struct fmc_device **devs, int n) | |
350 | { | |
351 | int i; | |
352 | ||
353 | if (n < 1) | |
354 | return; | |
355 | ||
356 | /* Free devarray first, not used by the later loop */ | |
357 | kfree(devs[0]->devarray); | |
358 | ||
359 | for (i = 0; i < n; i++) { | |
2071a3e9 | 360 | fmc_debug_exit(devs[i]); |
77864f2e AR |
361 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); |
362 | device_del(&devs[i]->dev); | |
363 | fmc_free_id_info(devs[i]); | |
364 | put_device(&devs[i]->dev); | |
365 | } | |
366 | } | |
367 | EXPORT_SYMBOL(fmc_device_unregister_n); | |
368 | ||
369 | void fmc_device_unregister(struct fmc_device *fmc) | |
370 | { | |
371 | fmc_device_unregister_n(&fmc, 1); | |
372 | } | |
373 | EXPORT_SYMBOL(fmc_device_unregister); | |
374 | ||
375 | /* Init and exit are trivial */ | |
9c9f32ed AR |
376 | static int fmc_init(void) |
377 | { | |
378 | return bus_register(&fmc_bus_type); | |
379 | } | |
380 | ||
381 | static void fmc_exit(void) | |
382 | { | |
383 | bus_unregister(&fmc_bus_type); | |
384 | } | |
385 | ||
386 | module_init(fmc_init); | |
387 | module_exit(fmc_exit); | |
388 | ||
389 | MODULE_LICENSE("GPL"); |