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>
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
);
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
))
40 static const struct ipack_device_id
*
41 ipack_match_id(const struct ipack_device_id
*ids
, struct ipack_device
*idev
)
44 while (ids
->vendor
|| ids
->device
) {
45 if (ipack_match_one_device(ids
, idev
))
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
);
68 static int ipack_bus_probe(struct device
*device
)
70 struct ipack_device
*dev
= to_ipack_dev(device
);
72 if (!dev
->driver
->ops
->probe
)
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
)
85 dev
->driver
->ops
->remove(dev
);
91 static int ipack_uevent(struct device
*dev
, struct kobj_uevent_env
*env
)
93 struct ipack_device
*idev
;
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
))
108 #else /* !CONFIG_HOTPLUG */
110 #define ipack_uevent NULL
112 #endif /* !CONFIG_HOTPLUG */
114 #define ipack_device_attr(field, format_string) \
116 field##_show(struct device *dev, struct device_attribute *attr, \
119 struct ipack_device *idev = to_ipack_dev(dev); \
120 return sprintf(buf, format_string, idev->field); \
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
);
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
);
151 static ssize_t
modalias_show(struct device
*dev
, struct device_attribute
*attr
,
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
),
169 static struct bus_type ipack_bus_type
= {
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
)
182 struct ipack_bus_device
*bus
;
184 bus
= kzalloc(sizeof(struct ipack_bus_device
), GFP_KERNEL
);
188 bus_nr
= ida_simple_get(&ipack_ida
, 0, 0, GFP_KERNEL
);
194 bus
->bus_nr
= bus_nr
;
195 bus
->parent
= parent
;
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
);
208 EXPORT_SYMBOL_GPL(ipack_bus_unregister
);
210 int ipack_driver_register(struct ipack_driver
*edrv
, struct module
*owner
,
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
)
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
)
249 ret
= dev
->bus
->ops
->map_space(dev
, 0, IPACK_ID_SPACE
);
251 dev_err(&dev
->dev
, "error mapping memory\n");
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;
284 dev
->id_format
= IPACK_ID_VERSION_INVALID
;
288 if (!dev
->id_avail
) {
293 /* Obtain the amount of memory required to store a copy of the complete
295 dev
->id
= kmalloc(dev
->id_avail
, GFP_KERNEL
);
297 dev_err(&dev
->dev
, "dev->id alloc failed.\n");
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);
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
);
313 case IPACK_ID_VERSION_2
:
314 ipack_parse_id2(dev
);
319 dev
->bus
->ops
->unmap_space(dev
, IPACK_ID_SPACE
);
324 struct ipack_device
*ipack_device_register(struct ipack_bus_device
*bus
,
328 struct ipack_device
*dev
;
330 dev
= kzalloc(sizeof(struct ipack_device
), GFP_KERNEL
);
334 dev
->dev
.bus
= &ipack_bus_type
;
335 dev
->dev
.release
= ipack_device_release
;
336 dev
->dev
.parent
= bus
->parent
;
338 dev
->bus_nr
= bus
->bus_nr
;
341 dev_set_name(&dev
->dev
,
342 "ipack-dev.%u.%u", dev
->bus_nr
, dev
->slot
);
344 ret
= ipack_device_read_id(dev
);
346 dev_err(&dev
->dev
, "error reading device id section.\n");
351 ret
= device_register(&dev
->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");