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 AR |
15 | #include <linux/fmc.h> |
16 | ||
17 | static int fmc_check_version(unsigned long version, const char *name) | |
18 | { | |
19 | if (__FMC_MAJOR(version) != FMC_MAJOR) { | |
20 | pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n", | |
21 | __func__, name, __FMC_MAJOR(version), FMC_MAJOR); | |
22 | return -EINVAL; | |
23 | } | |
24 | ||
25 | if (__FMC_MINOR(version) != FMC_MINOR) | |
26 | pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n", | |
27 | __func__, name, __FMC_MINOR(version), FMC_MINOR); | |
28 | return 0; | |
29 | } | |
30 | ||
31 | static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env) | |
32 | { | |
33 | /* struct fmc_device *fdev = to_fmc_device(dev); */ | |
34 | ||
35 | /* FIXME: The MODALIAS */ | |
36 | add_uevent_var(env, "MODALIAS=%s", "fmc"); | |
37 | return 0; | |
38 | } | |
39 | ||
40 | static int fmc_probe(struct device *dev) | |
41 | { | |
42 | struct fmc_driver *fdrv = to_fmc_driver(dev->driver); | |
43 | struct fmc_device *fdev = to_fmc_device(dev); | |
44 | ||
45 | return fdrv->probe(fdev); | |
46 | } | |
47 | ||
48 | static int fmc_remove(struct device *dev) | |
49 | { | |
50 | struct fmc_driver *fdrv = to_fmc_driver(dev->driver); | |
51 | struct fmc_device *fdev = to_fmc_device(dev); | |
52 | ||
53 | return fdrv->remove(fdev); | |
54 | } | |
55 | ||
56 | static void fmc_shutdown(struct device *dev) | |
57 | { | |
58 | /* not implemented but mandatory */ | |
59 | } | |
9c9f32ed AR |
60 | |
61 | static struct bus_type fmc_bus_type = { | |
62 | .name = "fmc", | |
77864f2e AR |
63 | .match = fmc_match, |
64 | .uevent = fmc_uevent, | |
65 | .probe = fmc_probe, | |
66 | .remove = fmc_remove, | |
67 | .shutdown = fmc_shutdown, | |
9c9f32ed AR |
68 | }; |
69 | ||
77864f2e AR |
70 | static void fmc_release(struct device *dev) |
71 | { | |
72 | struct fmc_device *fmc = container_of(dev, struct fmc_device, dev); | |
73 | ||
74 | kfree(fmc); | |
75 | } | |
76 | ||
77 | /* | |
78 | * The eeprom is exported in sysfs, through a binary attribute | |
79 | */ | |
80 | ||
81 | static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj, | |
82 | struct bin_attribute *bin_attr, | |
83 | char *buf, loff_t off, size_t count) | |
84 | { | |
85 | struct device *dev; | |
86 | struct fmc_device *fmc; | |
87 | int eelen; | |
88 | ||
89 | dev = container_of(kobj, struct device, kobj); | |
90 | fmc = container_of(dev, struct fmc_device, dev); | |
91 | eelen = fmc->eeprom_len; | |
92 | if (off > eelen) | |
93 | return -ESPIPE; | |
94 | if (off == eelen) | |
95 | return 0; /* EOF */ | |
96 | if (off + count > eelen) | |
97 | count = eelen - off; | |
98 | memcpy(buf, fmc->eeprom + off, count); | |
99 | return count; | |
100 | } | |
101 | ||
102 | static struct bin_attribute fmc_eeprom_attr = { | |
103 | .attr = { .name = "eeprom", .mode = S_IRUGO, }, | |
104 | .size = 8192, /* more or less standard */ | |
105 | .read = fmc_read_eeprom, | |
106 | }; | |
107 | ||
108 | /* | |
109 | * Functions for client modules follow | |
110 | */ | |
111 | ||
112 | int fmc_driver_register(struct fmc_driver *drv) | |
113 | { | |
114 | if (fmc_check_version(drv->version, drv->driver.name)) | |
115 | return -EINVAL; | |
116 | drv->driver.bus = &fmc_bus_type; | |
117 | return driver_register(&drv->driver); | |
118 | } | |
119 | EXPORT_SYMBOL(fmc_driver_register); | |
120 | ||
121 | void fmc_driver_unregister(struct fmc_driver *drv) | |
122 | { | |
123 | driver_unregister(&drv->driver); | |
124 | } | |
125 | EXPORT_SYMBOL(fmc_driver_unregister); | |
126 | ||
127 | /* | |
128 | * When a device set is registered, all eeproms must be read | |
129 | * and all FRUs must be parsed | |
130 | */ | |
131 | int fmc_device_register_n(struct fmc_device **devs, int n) | |
132 | { | |
133 | struct fmc_device *fmc, **devarray; | |
134 | uint32_t device_id; | |
135 | int i, ret = 0; | |
136 | ||
137 | if (n < 1) | |
138 | return 0; | |
139 | ||
140 | /* Check the version of the first data structure (function prints) */ | |
141 | if (fmc_check_version(devs[0]->version, devs[0]->carrier_name)) | |
142 | return -EINVAL; | |
143 | ||
144 | devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL); | |
145 | if (!devarray) | |
146 | return -ENOMEM; | |
147 | ||
148 | /* Make all other checks before continuing, for all devices */ | |
149 | for (i = 0; i < n; i++) { | |
150 | fmc = devarray[i]; | |
151 | if (!fmc->hwdev) { | |
152 | pr_err("%s: device nr. %i has no hwdev pointer\n", | |
153 | __func__, i); | |
154 | ret = -EINVAL; | |
155 | break; | |
156 | } | |
157 | if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) { | |
158 | dev_info(fmc->hwdev, "absent mezzanine in slot %d\n", | |
159 | fmc->slot_id); | |
160 | continue; | |
161 | } | |
162 | if (!fmc->eeprom) { | |
163 | dev_err(fmc->hwdev, "no eeprom provided for slot %i\n", | |
164 | fmc->slot_id); | |
165 | ret = -EINVAL; | |
166 | } | |
167 | if (!fmc->eeprom_addr) { | |
168 | dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n", | |
169 | fmc->slot_id); | |
170 | ret = -EINVAL; | |
171 | } | |
172 | if (!fmc->carrier_name || !fmc->carrier_data || | |
173 | !fmc->device_id) { | |
174 | dev_err(fmc->hwdev, | |
175 | "deivce nr %i: carrier name, " | |
176 | "data or dev_id not set\n", i); | |
177 | ret = -EINVAL; | |
178 | } | |
179 | if (ret) | |
180 | break; | |
181 | ||
182 | } | |
183 | if (ret) { | |
184 | kfree(devarray); | |
185 | return ret; | |
186 | } | |
187 | ||
188 | /* Validation is ok. Now init and register the devices */ | |
189 | for (i = 0; i < n; i++) { | |
190 | fmc = devarray[i]; | |
191 | ||
192 | if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) | |
193 | continue; /* dev_info already done above */ | |
194 | ||
195 | fmc->nr_slots = n; /* each slot must know how many are there */ | |
196 | fmc->devarray = devarray; | |
197 | ||
198 | device_initialize(&fmc->dev); | |
199 | fmc->dev.release = fmc_release; | |
200 | fmc->dev.parent = fmc->hwdev; | |
201 | ||
202 | /* Fill the identification stuff (may fail) */ | |
203 | fmc_fill_id_info(fmc); | |
204 | ||
205 | fmc->dev.bus = &fmc_bus_type; | |
206 | ||
207 | /* Name from mezzanine info or carrier info. Or 0,1,2.. */ | |
208 | device_id = fmc->device_id; | |
209 | if (!fmc->mezzanine_name) | |
210 | dev_set_name(&fmc->dev, "fmc-%04x", device_id); | |
211 | else | |
212 | dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, | |
213 | device_id); | |
214 | ret = device_add(&fmc->dev); | |
215 | if (ret < 0) { | |
216 | dev_err(fmc->hwdev, "Slot %i: Failed in registering " | |
217 | "\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name); | |
218 | goto out; | |
219 | } | |
220 | ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr); | |
221 | if (ret < 0) { | |
222 | dev_err(&fmc->dev, "Failed in registering eeprom\n"); | |
223 | goto out1; | |
224 | } | |
225 | /* This device went well, give information to the user */ | |
226 | fmc_dump_eeprom(fmc); | |
227 | fmc_dump_sdb(fmc); | |
228 | } | |
229 | return 0; | |
230 | ||
231 | out1: | |
232 | device_del(&fmc->dev); | |
233 | out: | |
234 | fmc_free_id_info(fmc); | |
235 | put_device(&fmc->dev); | |
236 | ||
237 | kfree(devarray); | |
238 | for (i--; i >= 0; i--) { | |
239 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); | |
240 | device_del(&devs[i]->dev); | |
241 | fmc_free_id_info(devs[i]); | |
242 | put_device(&devs[i]->dev); | |
243 | } | |
244 | return ret; | |
245 | ||
246 | } | |
247 | EXPORT_SYMBOL(fmc_device_register_n); | |
248 | ||
249 | int fmc_device_register(struct fmc_device *fmc) | |
250 | { | |
251 | return fmc_device_register_n(&fmc, 1); | |
252 | } | |
253 | EXPORT_SYMBOL(fmc_device_register); | |
254 | ||
255 | void fmc_device_unregister_n(struct fmc_device **devs, int n) | |
256 | { | |
257 | int i; | |
258 | ||
259 | if (n < 1) | |
260 | return; | |
261 | ||
262 | /* Free devarray first, not used by the later loop */ | |
263 | kfree(devs[0]->devarray); | |
264 | ||
265 | for (i = 0; i < n; i++) { | |
266 | if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE) | |
267 | continue; | |
268 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); | |
269 | device_del(&devs[i]->dev); | |
270 | fmc_free_id_info(devs[i]); | |
271 | put_device(&devs[i]->dev); | |
272 | } | |
273 | } | |
274 | EXPORT_SYMBOL(fmc_device_unregister_n); | |
275 | ||
276 | void fmc_device_unregister(struct fmc_device *fmc) | |
277 | { | |
278 | fmc_device_unregister_n(&fmc, 1); | |
279 | } | |
280 | EXPORT_SYMBOL(fmc_device_unregister); | |
281 | ||
282 | /* Init and exit are trivial */ | |
9c9f32ed AR |
283 | static int fmc_init(void) |
284 | { | |
285 | return bus_register(&fmc_bus_type); | |
286 | } | |
287 | ||
288 | static void fmc_exit(void) | |
289 | { | |
290 | bus_unregister(&fmc_bus_type); | |
291 | } | |
292 | ||
293 | module_init(fmc_init); | |
294 | module_exit(fmc_exit); | |
295 | ||
296 | MODULE_LICENSE("GPL"); |