blob: ff907facc005d316fe7dd7e8e07f527c300467b4 [file] [log] [blame]
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +02001/*
2 * Industry-pack bus support functions.
3 *
4 * (C) 2011 Samuel Iglesias Gonsalvez <siglesia@cern.ch>, CERN
5 * (C) 2012 Samuel Iglesias Gonsalvez <siglesias@igalia.com>, Igalia
6 *
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
Samuel Iglesias Gonsalvez416289b2012-05-11 10:17:13 +02009 * Software Foundation; version 2 of the License.
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020010 */
11
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020012#include <linux/module.h>
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +020013#include <linux/slab.h>
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +020014#include <linux/idr.h>
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020015#include "ipack.h"
16
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)
19
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +020020static DEFINE_IDA(ipack_ida);
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020021
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +020022static void ipack_device_release(struct device *dev)
23{
24 struct ipack_device *device = to_ipack_dev(dev);
Jens Taprogge187e4782012-09-04 17:01:14 +020025 kfree(device->id);
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +020026 kfree(device);
27}
28
Jens Taprogge4aa09d42012-09-04 17:01:19 +020029static inline const struct ipack_device_id *
30ipack_match_one_device(const struct ipack_device_id *id,
31 const struct ipack_device *device)
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020032{
Jens Taprogge4aa09d42012-09-04 17:01:19 +020033 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;
38}
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020039
Jens Taprogge4aa09d42012-09-04 17:01:19 +020040static const struct ipack_device_id *
41ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev)
42{
43 if (ids) {
44 while (ids->vendor || ids->device) {
45 if (ipack_match_one_device(ids, idev))
46 return ids;
47 ids++;
48 }
49 }
50 return NULL;
51}
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020052
Jens Taprogge4aa09d42012-09-04 17:01:19 +020053static int ipack_bus_match(struct device *dev, struct device_driver *drv)
54{
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;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020058
Jens Taprogge4aa09d42012-09-04 17:01:19 +020059 found_id = ipack_match_id(idrv->id_table, idev);
60 if (found_id) {
61 idev->driver = idrv;
62 return 1;
63 }
64
65 return 0;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +020066}
67
68static int ipack_bus_probe(struct device *device)
69{
70 struct ipack_device *dev = to_ipack_dev(device);
71
72 if (!dev->driver->ops->probe)
73 return -EINVAL;
74
75 return dev->driver->ops->probe(dev);
76}
77
78static int ipack_bus_remove(struct device *device)
79{
80 struct ipack_device *dev = to_ipack_dev(device);
81
82 if (!dev->driver->ops->remove)
83 return -EINVAL;
84
85 dev->driver->ops->remove(dev);
86 return 0;
87}
88
Jens Taprogge35eb97b2012-09-04 17:01:20 +020089#ifdef CONFIG_HOTPLUG
90
91static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env)
92{
93 struct ipack_device *idev;
94
95 if (!dev)
96 return -ENODEV;
97
98 idev = to_ipack_dev(dev);
99
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;
104
105 return 0;
106}
107
108#else /* !CONFIG_HOTPLUG */
109
110#define ipack_uevent NULL
111
112#endif /* !CONFIG_HOTPLUG */
113
Jens Taproggee8ed3272012-09-04 17:01:15 +0200114#define ipack_device_attr(field, format_string) \
115static ssize_t \
116field##_show(struct device *dev, struct device_attribute *attr, \
117 char *buf) \
118{ \
119 struct ipack_device *idev = to_ipack_dev(dev); \
120 return sprintf(buf, format_string, idev->field); \
121}
122
Jens Taprogge5d72c8482012-09-04 17:01:21 +0200123static ssize_t id_show(struct device *dev,
124 struct device_attribute *attr, char *buf)
125{
126 unsigned int i, c, l, s;
127 struct ipack_device *idev = to_ipack_dev(dev);
128
129
130 switch (idev->id_format) {
131 case IPACK_ID_VERSION_1:
132 l = 0x7; s = 1; break;
133 case IPACK_ID_VERSION_2:
134 l = 0xf; s = 2; break;
135 default:
136 return -EIO;
137 }
138 c = 0;
139 for (i = 0; i < idev->id_avail; i++) {
140 if (i > 0) {
141 if ((i & l) == 0)
142 buf[c++] = '\n';
143 else if ((i & s) == 0)
144 buf[c++] = ' ';
145 }
146 sprintf(&buf[c], "%02x", idev->id[i]);
147 c += 2;
148 }
149 buf[c++] = '\n';
150 return c;
151}
152
Jens Taproggee8ed3272012-09-04 17:01:15 +0200153static ssize_t
154id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
155{
156 struct ipack_device *idev = to_ipack_dev(dev);
157 switch (idev->id_format) {
158 case IPACK_ID_VERSION_1:
159 return sprintf(buf, "0x%02x\n", idev->id_vendor);
160 case IPACK_ID_VERSION_2:
161 return sprintf(buf, "0x%06x\n", idev->id_vendor);
162 default:
163 return -EIO;
164 }
165}
166
167static ssize_t
168id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
169{
170 struct ipack_device *idev = to_ipack_dev(dev);
171 switch (idev->id_format) {
172 case IPACK_ID_VERSION_1:
173 return sprintf(buf, "0x%02x\n", idev->id_device);
174 case IPACK_ID_VERSION_2:
175 return sprintf(buf, "0x%04x\n", idev->id_device);
176 default:
177 return -EIO;
178 }
179}
180
Jens Taprogge35eb97b2012-09-04 17:01:20 +0200181static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
182 char *buf)
183{
184 struct ipack_device *idev = to_ipack_dev(dev);
185
186 return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format,
187 idev->id_vendor, idev->id_device);
188}
189
Jens Taproggee8ed3272012-09-04 17:01:15 +0200190ipack_device_attr(id_format, "0x%hhu\n");
191
192static struct device_attribute ipack_dev_attrs[] = {
Jens Taprogge5d72c8482012-09-04 17:01:21 +0200193 __ATTR_RO(id),
Jens Taproggee8ed3272012-09-04 17:01:15 +0200194 __ATTR_RO(id_device),
195 __ATTR_RO(id_format),
196 __ATTR_RO(id_vendor),
Jens Taprogge35eb97b2012-09-04 17:01:20 +0200197 __ATTR_RO(modalias),
Jens Taproggee8ed3272012-09-04 17:01:15 +0200198};
199
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200200static struct bus_type ipack_bus_type = {
Jens Taproggee8ed3272012-09-04 17:01:15 +0200201 .name = "ipack",
202 .probe = ipack_bus_probe,
203 .match = ipack_bus_match,
204 .remove = ipack_bus_remove,
205 .dev_attrs = ipack_dev_attrs,
Jens Taprogge35eb97b2012-09-04 17:01:20 +0200206 .uevent = ipack_uevent,
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200207};
208
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200209struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
210 struct ipack_bus_ops *ops)
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200211{
212 int bus_nr;
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200213 struct ipack_bus_device *bus;
214
215 bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL);
216 if (!bus)
217 return NULL;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200218
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +0200219 bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL);
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200220 if (bus_nr < 0) {
221 kfree(bus);
222 return NULL;
223 }
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200224
225 bus->bus_nr = bus_nr;
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200226 bus->parent = parent;
227 bus->slots = slots;
228 bus->ops = ops;
229 return bus;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200230}
231EXPORT_SYMBOL_GPL(ipack_bus_register);
232
233int ipack_bus_unregister(struct ipack_bus_device *bus)
234{
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +0200235 ida_simple_remove(&ipack_ida, bus->bus_nr);
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200236 kfree(bus);
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200237 return 0;
238}
239EXPORT_SYMBOL_GPL(ipack_bus_unregister);
240
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200241int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
242 char *name)
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200243{
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200244 edrv->driver.owner = owner;
245 edrv->driver.name = name;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200246 edrv->driver.bus = &ipack_bus_type;
247 return driver_register(&edrv->driver);
248}
249EXPORT_SYMBOL_GPL(ipack_driver_register);
250
251void ipack_driver_unregister(struct ipack_driver *edrv)
252{
253 driver_unregister(&edrv->driver);
254}
255EXPORT_SYMBOL_GPL(ipack_driver_unregister);
256
Jens Taproggee8ed3272012-09-04 17:01:15 +0200257static void ipack_parse_id1(struct ipack_device *dev)
258{
259 u8 *id = dev->id;
260
261 dev->id_vendor = id[4];
262 dev->id_device = id[5];
263}
264
265static void ipack_parse_id2(struct ipack_device *dev)
266{
267 __be16 *id = (__be16 *) dev->id;
268
269 dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
270 + be16_to_cpu(id[4]);
271 dev->id_device = be16_to_cpu(id[5]);
272}
273
Jens Taprogge187e4782012-09-04 17:01:14 +0200274static int ipack_device_read_id(struct ipack_device *dev)
275{
276 u8 __iomem *idmem;
277 int i;
278 int ret = 0;
279
280 ret = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
281 if (ret) {
282 dev_err(&dev->dev, "error mapping memory\n");
283 return ret;
284 }
285 idmem = dev->id_space.address;
286
287 /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH"
288 * we are dealing with a IndustryPack format 1 device. If we detect
289 * "VITA4 " (16 bit big endian formatted) we are dealing with a
290 * IndustryPack format 2 device */
291 if ((ioread8(idmem + 1) == 'I') &&
292 (ioread8(idmem + 3) == 'P') &&
293 (ioread8(idmem + 5) == 'A') &&
294 ((ioread8(idmem + 7) == 'C') ||
295 (ioread8(idmem + 7) == 'H'))) {
296 dev->id_format = IPACK_ID_VERSION_1;
297 dev->id_avail = ioread8(idmem + 0x15);
298 if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
299 dev_warn(&dev->dev, "invalid id size");
300 dev->id_avail = 0x0c;
301 }
302 } else if ((ioread8(idmem + 0) == 'I') &&
303 (ioread8(idmem + 1) == 'V') &&
304 (ioread8(idmem + 2) == 'A') &&
305 (ioread8(idmem + 3) == 'T') &&
306 (ioread8(idmem + 4) == ' ') &&
307 (ioread8(idmem + 5) == '4')) {
308 dev->id_format = IPACK_ID_VERSION_2;
309 dev->id_avail = ioread16be(idmem + 0x16);
310 if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
311 dev_warn(&dev->dev, "invalid id size");
312 dev->id_avail = 0x1a;
313 }
314 } else {
315 dev->id_format = IPACK_ID_VERSION_INVALID;
316 dev->id_avail = 0;
317 }
318
319 if (!dev->id_avail) {
320 ret = -ENODEV;
321 goto out;
322 }
323
324 /* Obtain the amount of memory required to store a copy of the complete
325 * ID ROM contents */
326 dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
327 if (!dev->id) {
328 dev_err(&dev->dev, "dev->id alloc failed.\n");
329 ret = -ENOMEM;
330 goto out;
331 }
332 for (i = 0; i < dev->id_avail; i++) {
333 if (dev->id_format == IPACK_ID_VERSION_1)
334 dev->id[i] = ioread8(idmem + (i << 1) + 1);
335 else
336 dev->id[i] = ioread8(idmem + i);
337 }
338
Jens Taproggee8ed3272012-09-04 17:01:15 +0200339 /* now we can finally work with the copy */
340 switch (dev->id_format) {
341 case IPACK_ID_VERSION_1:
342 ipack_parse_id1(dev);
343 break;
344 case IPACK_ID_VERSION_2:
345 ipack_parse_id2(dev);
346 break;
347 }
348
Jens Taprogge187e4782012-09-04 17:01:14 +0200349out:
350 dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
351
352 return ret;
353}
354
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200355struct ipack_device *ipack_device_register(struct ipack_bus_device *bus,
356 int slot, int irqv)
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200357{
358 int ret;
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200359 struct ipack_device *dev;
360
361 dev = kzalloc(sizeof(struct ipack_device), GFP_KERNEL);
362 if (!dev)
363 return NULL;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200364
365 dev->dev.bus = &ipack_bus_type;
366 dev->dev.release = ipack_device_release;
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200367 dev->dev.parent = bus->parent;
368 dev->slot = slot;
369 dev->bus_nr = bus->bus_nr;
370 dev->irq = irqv;
371 dev->bus = bus;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200372 dev_set_name(&dev->dev,
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200373 "ipack-dev.%u.%u", dev->bus_nr, dev->slot);
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200374
Jens Taprogge187e4782012-09-04 17:01:14 +0200375 ret = ipack_device_read_id(dev);
376 if (ret < 0) {
377 dev_err(&dev->dev, "error reading device id section.\n");
378 kfree(dev);
379 return NULL;
380 }
381
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200382 ret = device_register(&dev->dev);
383 if (ret < 0) {
Jens Taprogge187e4782012-09-04 17:01:14 +0200384 kfree(dev->id);
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200385 kfree(dev);
386 return NULL;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200387 }
388
Samuel Iglesias Gonsalvezec440332012-05-18 11:10:05 +0200389 return dev;
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200390}
391EXPORT_SYMBOL_GPL(ipack_device_register);
392
393void ipack_device_unregister(struct ipack_device *dev)
394{
395 device_unregister(&dev->dev);
396}
397EXPORT_SYMBOL_GPL(ipack_device_unregister);
398
399static int __init ipack_init(void)
400{
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +0200401 ida_init(&ipack_ida);
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200402 return bus_register(&ipack_bus_type);
403}
404
405static void __exit ipack_exit(void)
406{
407 bus_unregister(&ipack_bus_type);
Samuel Iglesias Gonsalvez3b86bb22012-05-25 10:03:01 +0200408 ida_destroy(&ipack_ida);
Samuel Iglesias Gonsalvezd3465872012-05-09 15:27:19 +0200409}
410
411module_init(ipack_init);
412module_exit(ipack_exit);
413
414MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>");
415MODULE_LICENSE("GPL");
416MODULE_DESCRIPTION("Industry-pack bus core");