Staging: ipack: Expose modalias through sysfs.
[linux-2.6.git] / drivers / staging / ipack / ipack.c
bloba5ef28f82f7aeb0cb00242b4fbe003b9260df311
1 /*
2 * Industry-pack bus support functions.
4 * (C) 2011 Samuel Iglesias Gonsalvez <siglesia@cern.ch>, CERN
5 * (C) 2012 Samuel Iglesias Gonsalvez <siglesias@igalia.com>, Igalia
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; version 2 of the License.
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/idr.h>
15 #include "ipack.h"
17 #define to_ipack_dev(device) container_of(device, struct ipack_device, dev)
18 #define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver)
20 static DEFINE_IDA(ipack_ida);
22 static void ipack_device_release(struct device *dev)
24 struct ipack_device *device = to_ipack_dev(dev);
25 kfree(device->id);
26 kfree(device);
29 static inline const struct ipack_device_id *
30 ipack_match_one_device(const struct ipack_device_id *id,
31 const struct ipack_device *device)
33 if ((id->format == IPACK_ANY_ID || id->format == device->id_format) &&
34 (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) &&
35 (id->device == IPACK_ANY_ID || id->device == device->id_device))
36 return id;
37 return NULL;
40 static const struct ipack_device_id *
41 ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev)
43 if (ids) {
44 while (ids->vendor || ids->device) {
45 if (ipack_match_one_device(ids, idev))
46 return ids;
47 ids++;
50 return NULL;
53 static int ipack_bus_match(struct device *dev, struct device_driver *drv)
55 struct ipack_device *idev = to_ipack_dev(dev);
56 struct ipack_driver *idrv = to_ipack_driver(drv);
57 const struct ipack_device_id *found_id;
59 found_id = ipack_match_id(idrv->id_table, idev);
60 if (found_id) {
61 idev->driver = idrv;
62 return 1;
65 return 0;
68 static int ipack_bus_probe(struct device *device)
70 struct ipack_device *dev = to_ipack_dev(device);
72 if (!dev->driver->ops->probe)
73 return -EINVAL;
75 return dev->driver->ops->probe(dev);
78 static int ipack_bus_remove(struct device *device)
80 struct ipack_device *dev = to_ipack_dev(device);
82 if (!dev->driver->ops->remove)
83 return -EINVAL;
85 dev->driver->ops->remove(dev);
86 return 0;
89 #ifdef CONFIG_HOTPLUG
91 static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env)
93 struct ipack_device *idev;
95 if (!dev)
96 return -ENODEV;
98 idev = to_ipack_dev(dev);
100 if (add_uevent_var(env,
101 "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format,
102 idev->id_vendor, idev->id_device))
103 return -ENOMEM;
105 return 0;
108 #else /* !CONFIG_HOTPLUG */
110 #define ipack_uevent NULL
112 #endif /* !CONFIG_HOTPLUG */
114 #define ipack_device_attr(field, format_string) \
115 static ssize_t \
116 field##_show(struct device *dev, struct device_attribute *attr, \
117 char *buf) \
119 struct ipack_device *idev = to_ipack_dev(dev); \
120 return sprintf(buf, format_string, idev->field); \
123 static ssize_t
124 id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
126 struct ipack_device *idev = to_ipack_dev(dev);
127 switch (idev->id_format) {
128 case IPACK_ID_VERSION_1:
129 return sprintf(buf, "0x%02x\n", idev->id_vendor);
130 case IPACK_ID_VERSION_2:
131 return sprintf(buf, "0x%06x\n", idev->id_vendor);
132 default:
133 return -EIO;
137 static ssize_t
138 id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
140 struct ipack_device *idev = to_ipack_dev(dev);
141 switch (idev->id_format) {
142 case IPACK_ID_VERSION_1:
143 return sprintf(buf, "0x%02x\n", idev->id_device);
144 case IPACK_ID_VERSION_2:
145 return sprintf(buf, "0x%04x\n", idev->id_device);
146 default:
147 return -EIO;
151 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
152 char *buf)
154 struct ipack_device *idev = to_ipack_dev(dev);
156 return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format,
157 idev->id_vendor, idev->id_device);
160 ipack_device_attr(id_format, "0x%hhu\n");
162 static struct device_attribute ipack_dev_attrs[] = {
163 __ATTR_RO(id_device),
164 __ATTR_RO(id_format),
165 __ATTR_RO(id_vendor),
166 __ATTR_RO(modalias),
169 static struct bus_type ipack_bus_type = {
170 .name = "ipack",
171 .probe = ipack_bus_probe,
172 .match = ipack_bus_match,
173 .remove = ipack_bus_remove,
174 .dev_attrs = ipack_dev_attrs,
175 .uevent = ipack_uevent,
178 struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
179 struct ipack_bus_ops *ops)
181 int bus_nr;
182 struct ipack_bus_device *bus;
184 bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL);
185 if (!bus)
186 return NULL;
188 bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL);
189 if (bus_nr < 0) {
190 kfree(bus);
191 return NULL;
194 bus->bus_nr = bus_nr;
195 bus->parent = parent;
196 bus->slots = slots;
197 bus->ops = ops;
198 return bus;
200 EXPORT_SYMBOL_GPL(ipack_bus_register);
202 int ipack_bus_unregister(struct ipack_bus_device *bus)
204 ida_simple_remove(&ipack_ida, bus->bus_nr);
205 kfree(bus);
206 return 0;
208 EXPORT_SYMBOL_GPL(ipack_bus_unregister);
210 int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
211 char *name)
213 edrv->driver.owner = owner;
214 edrv->driver.name = name;
215 edrv->driver.bus = &ipack_bus_type;
216 return driver_register(&edrv->driver);
218 EXPORT_SYMBOL_GPL(ipack_driver_register);
220 void ipack_driver_unregister(struct ipack_driver *edrv)
222 driver_unregister(&edrv->driver);
224 EXPORT_SYMBOL_GPL(ipack_driver_unregister);
226 static void ipack_parse_id1(struct ipack_device *dev)
228 u8 *id = dev->id;
230 dev->id_vendor = id[4];
231 dev->id_device = id[5];
234 static void ipack_parse_id2(struct ipack_device *dev)
236 __be16 *id = (__be16 *) dev->id;
238 dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
239 + be16_to_cpu(id[4]);
240 dev->id_device = be16_to_cpu(id[5]);
243 static int ipack_device_read_id(struct ipack_device *dev)
245 u8 __iomem *idmem;
246 int i;
247 int ret = 0;
249 ret = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
250 if (ret) {
251 dev_err(&dev->dev, "error mapping memory\n");
252 return ret;
254 idmem = dev->id_space.address;
256 /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH"
257 * we are dealing with a IndustryPack format 1 device. If we detect
258 * "VITA4 " (16 bit big endian formatted) we are dealing with a
259 * IndustryPack format 2 device */
260 if ((ioread8(idmem + 1) == 'I') &&
261 (ioread8(idmem + 3) == 'P') &&
262 (ioread8(idmem + 5) == 'A') &&
263 ((ioread8(idmem + 7) == 'C') ||
264 (ioread8(idmem + 7) == 'H'))) {
265 dev->id_format = IPACK_ID_VERSION_1;
266 dev->id_avail = ioread8(idmem + 0x15);
267 if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
268 dev_warn(&dev->dev, "invalid id size");
269 dev->id_avail = 0x0c;
271 } else if ((ioread8(idmem + 0) == 'I') &&
272 (ioread8(idmem + 1) == 'V') &&
273 (ioread8(idmem + 2) == 'A') &&
274 (ioread8(idmem + 3) == 'T') &&
275 (ioread8(idmem + 4) == ' ') &&
276 (ioread8(idmem + 5) == '4')) {
277 dev->id_format = IPACK_ID_VERSION_2;
278 dev->id_avail = ioread16be(idmem + 0x16);
279 if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
280 dev_warn(&dev->dev, "invalid id size");
281 dev->id_avail = 0x1a;
283 } else {
284 dev->id_format = IPACK_ID_VERSION_INVALID;
285 dev->id_avail = 0;
288 if (!dev->id_avail) {
289 ret = -ENODEV;
290 goto out;
293 /* Obtain the amount of memory required to store a copy of the complete
294 * ID ROM contents */
295 dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
296 if (!dev->id) {
297 dev_err(&dev->dev, "dev->id alloc failed.\n");
298 ret = -ENOMEM;
299 goto out;
301 for (i = 0; i < dev->id_avail; i++) {
302 if (dev->id_format == IPACK_ID_VERSION_1)
303 dev->id[i] = ioread8(idmem + (i << 1) + 1);
304 else
305 dev->id[i] = ioread8(idmem + i);
308 /* now we can finally work with the copy */
309 switch (dev->id_format) {
310 case IPACK_ID_VERSION_1:
311 ipack_parse_id1(dev);
312 break;
313 case IPACK_ID_VERSION_2:
314 ipack_parse_id2(dev);
315 break;
318 out:
319 dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
321 return ret;
324 struct ipack_device *ipack_device_register(struct ipack_bus_device *bus,
325 int slot, int irqv)
327 int ret;
328 struct ipack_device *dev;
330 dev = kzalloc(sizeof(struct ipack_device), GFP_KERNEL);
331 if (!dev)
332 return NULL;
334 dev->dev.bus = &ipack_bus_type;
335 dev->dev.release = ipack_device_release;
336 dev->dev.parent = bus->parent;
337 dev->slot = slot;
338 dev->bus_nr = bus->bus_nr;
339 dev->irq = irqv;
340 dev->bus = bus;
341 dev_set_name(&dev->dev,
342 "ipack-dev.%u.%u", dev->bus_nr, dev->slot);
344 ret = ipack_device_read_id(dev);
345 if (ret < 0) {
346 dev_err(&dev->dev, "error reading device id section.\n");
347 kfree(dev);
348 return NULL;
351 ret = device_register(&dev->dev);
352 if (ret < 0) {
353 kfree(dev->id);
354 kfree(dev);
355 return NULL;
358 return dev;
360 EXPORT_SYMBOL_GPL(ipack_device_register);
362 void ipack_device_unregister(struct ipack_device *dev)
364 device_unregister(&dev->dev);
366 EXPORT_SYMBOL_GPL(ipack_device_unregister);
368 static int __init ipack_init(void)
370 ida_init(&ipack_ida);
371 return bus_register(&ipack_bus_type);
374 static void __exit ipack_exit(void)
376 bus_unregister(&ipack_bus_type);
377 ida_destroy(&ipack_ida);
380 module_init(ipack_init);
381 module_exit(ipack_exit);
383 MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>");
384 MODULE_LICENSE("GPL");
385 MODULE_DESCRIPTION("Industry-pack bus core");