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 | ||
5c9a8736 AR |
102 | static ssize_t fmc_write_eeprom(struct file *file, struct kobject *kobj, |
103 | struct bin_attribute *bin_attr, | |
104 | char *buf, loff_t off, size_t count) | |
105 | { | |
106 | struct device *dev; | |
107 | struct fmc_device *fmc; | |
108 | ||
109 | dev = container_of(kobj, struct device, kobj); | |
110 | fmc = container_of(dev, struct fmc_device, dev); | |
111 | return fmc->op->write_ee(fmc, off, buf, count); | |
112 | } | |
113 | ||
77864f2e | 114 | static struct bin_attribute fmc_eeprom_attr = { |
5c9a8736 | 115 | .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, }, |
77864f2e AR |
116 | .size = 8192, /* more or less standard */ |
117 | .read = fmc_read_eeprom, | |
5c9a8736 | 118 | .write = fmc_write_eeprom, |
77864f2e AR |
119 | }; |
120 | ||
121 | /* | |
122 | * Functions for client modules follow | |
123 | */ | |
124 | ||
125 | int fmc_driver_register(struct fmc_driver *drv) | |
126 | { | |
127 | if (fmc_check_version(drv->version, drv->driver.name)) | |
128 | return -EINVAL; | |
129 | drv->driver.bus = &fmc_bus_type; | |
130 | return driver_register(&drv->driver); | |
131 | } | |
132 | EXPORT_SYMBOL(fmc_driver_register); | |
133 | ||
134 | void fmc_driver_unregister(struct fmc_driver *drv) | |
135 | { | |
136 | driver_unregister(&drv->driver); | |
137 | } | |
138 | EXPORT_SYMBOL(fmc_driver_unregister); | |
139 | ||
140 | /* | |
141 | * When a device set is registered, all eeproms must be read | |
142 | * and all FRUs must be parsed | |
143 | */ | |
144 | int fmc_device_register_n(struct fmc_device **devs, int n) | |
145 | { | |
146 | struct fmc_device *fmc, **devarray; | |
147 | uint32_t device_id; | |
148 | int i, ret = 0; | |
149 | ||
150 | if (n < 1) | |
151 | return 0; | |
152 | ||
153 | /* Check the version of the first data structure (function prints) */ | |
154 | if (fmc_check_version(devs[0]->version, devs[0]->carrier_name)) | |
155 | return -EINVAL; | |
156 | ||
157 | devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL); | |
158 | if (!devarray) | |
159 | return -ENOMEM; | |
160 | ||
161 | /* Make all other checks before continuing, for all devices */ | |
162 | for (i = 0; i < n; i++) { | |
163 | fmc = devarray[i]; | |
164 | if (!fmc->hwdev) { | |
165 | pr_err("%s: device nr. %i has no hwdev pointer\n", | |
166 | __func__, i); | |
167 | ret = -EINVAL; | |
168 | break; | |
169 | } | |
e4d6c4b7 | 170 | if (fmc->flags & FMC_DEVICE_NO_MEZZANINE) { |
77864f2e AR |
171 | dev_info(fmc->hwdev, "absent mezzanine in slot %d\n", |
172 | fmc->slot_id); | |
173 | continue; | |
174 | } | |
175 | if (!fmc->eeprom) { | |
176 | dev_err(fmc->hwdev, "no eeprom provided for slot %i\n", | |
177 | fmc->slot_id); | |
178 | ret = -EINVAL; | |
179 | } | |
180 | if (!fmc->eeprom_addr) { | |
181 | dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n", | |
182 | fmc->slot_id); | |
183 | ret = -EINVAL; | |
184 | } | |
185 | if (!fmc->carrier_name || !fmc->carrier_data || | |
186 | !fmc->device_id) { | |
187 | dev_err(fmc->hwdev, | |
188 | "deivce nr %i: carrier name, " | |
189 | "data or dev_id not set\n", i); | |
190 | ret = -EINVAL; | |
191 | } | |
192 | if (ret) | |
193 | break; | |
194 | ||
195 | } | |
196 | if (ret) { | |
197 | kfree(devarray); | |
198 | return ret; | |
199 | } | |
200 | ||
201 | /* Validation is ok. Now init and register the devices */ | |
202 | for (i = 0; i < n; i++) { | |
203 | fmc = devarray[i]; | |
204 | ||
77864f2e AR |
205 | fmc->nr_slots = n; /* each slot must know how many are there */ |
206 | fmc->devarray = devarray; | |
207 | ||
208 | device_initialize(&fmc->dev); | |
209 | fmc->dev.release = fmc_release; | |
210 | fmc->dev.parent = fmc->hwdev; | |
211 | ||
212 | /* Fill the identification stuff (may fail) */ | |
213 | fmc_fill_id_info(fmc); | |
214 | ||
215 | fmc->dev.bus = &fmc_bus_type; | |
216 | ||
217 | /* Name from mezzanine info or carrier info. Or 0,1,2.. */ | |
218 | device_id = fmc->device_id; | |
219 | if (!fmc->mezzanine_name) | |
220 | dev_set_name(&fmc->dev, "fmc-%04x", device_id); | |
221 | else | |
222 | dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, | |
223 | device_id); | |
224 | ret = device_add(&fmc->dev); | |
225 | if (ret < 0) { | |
226 | dev_err(fmc->hwdev, "Slot %i: Failed in registering " | |
227 | "\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name); | |
228 | goto out; | |
229 | } | |
230 | ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr); | |
231 | if (ret < 0) { | |
232 | dev_err(&fmc->dev, "Failed in registering eeprom\n"); | |
233 | goto out1; | |
234 | } | |
235 | /* This device went well, give information to the user */ | |
236 | fmc_dump_eeprom(fmc); | |
237 | fmc_dump_sdb(fmc); | |
238 | } | |
239 | return 0; | |
240 | ||
241 | out1: | |
242 | device_del(&fmc->dev); | |
243 | out: | |
244 | fmc_free_id_info(fmc); | |
245 | put_device(&fmc->dev); | |
246 | ||
247 | kfree(devarray); | |
248 | for (i--; i >= 0; i--) { | |
249 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); | |
250 | device_del(&devs[i]->dev); | |
251 | fmc_free_id_info(devs[i]); | |
252 | put_device(&devs[i]->dev); | |
253 | } | |
254 | return ret; | |
255 | ||
256 | } | |
257 | EXPORT_SYMBOL(fmc_device_register_n); | |
258 | ||
259 | int fmc_device_register(struct fmc_device *fmc) | |
260 | { | |
261 | return fmc_device_register_n(&fmc, 1); | |
262 | } | |
263 | EXPORT_SYMBOL(fmc_device_register); | |
264 | ||
265 | void fmc_device_unregister_n(struct fmc_device **devs, int n) | |
266 | { | |
267 | int i; | |
268 | ||
269 | if (n < 1) | |
270 | return; | |
271 | ||
272 | /* Free devarray first, not used by the later loop */ | |
273 | kfree(devs[0]->devarray); | |
274 | ||
275 | for (i = 0; i < n; i++) { | |
77864f2e AR |
276 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); |
277 | device_del(&devs[i]->dev); | |
278 | fmc_free_id_info(devs[i]); | |
279 | put_device(&devs[i]->dev); | |
280 | } | |
281 | } | |
282 | EXPORT_SYMBOL(fmc_device_unregister_n); | |
283 | ||
284 | void fmc_device_unregister(struct fmc_device *fmc) | |
285 | { | |
286 | fmc_device_unregister_n(&fmc, 1); | |
287 | } | |
288 | EXPORT_SYMBOL(fmc_device_unregister); | |
289 | ||
290 | /* Init and exit are trivial */ | |
9c9f32ed AR |
291 | static int fmc_init(void) |
292 | { | |
293 | return bus_register(&fmc_bus_type); | |
294 | } | |
295 | ||
296 | static void fmc_exit(void) | |
297 | { | |
298 | bus_unregister(&fmc_bus_type); | |
299 | } | |
300 | ||
301 | module_init(fmc_init); | |
302 | module_exit(fmc_exit); | |
303 | ||
304 | MODULE_LICENSE("GPL"); |