Staging: ipack: Implement device matching on the bus level.
[deliverable/linux.git] / drivers / staging / ipack / ipack.c
CommitLineData
d3465872
SIG
1/*
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
416289b1 9 * Software Foundation; version 2 of the License.
d3465872
SIG
10 */
11
d3465872 12#include <linux/module.h>
ec440335 13#include <linux/slab.h>
3b86bb2e 14#include <linux/idr.h>
d3465872
SIG
15#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
3b86bb2e 20static DEFINE_IDA(ipack_ida);
d3465872 21
ec440335
SIG
22static void ipack_device_release(struct device *dev)
23{
24 struct ipack_device *device = to_ipack_dev(dev);
187e4782 25 kfree(device->id);
ec440335
SIG
26 kfree(device);
27}
28
4aa09d47
JT
29static inline const struct ipack_device_id *
30ipack_match_one_device(const struct ipack_device_id *id,
31 const struct ipack_device *device)
d3465872 32{
4aa09d47
JT
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;
38}
d3465872 39
4aa09d47
JT
40static 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}
52
53static 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;
d3465872 58
4aa09d47
JT
59 found_id = ipack_match_id(idrv->id_table, idev);
60 if (found_id) {
61 idev->driver = idrv;
62 return 1;
63 }
d3465872 64
4aa09d47 65 return 0;
d3465872
SIG
66}
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
e8ed3276
JT
89#define ipack_device_attr(field, format_string) \
90static ssize_t \
91field##_show(struct device *dev, struct device_attribute *attr, \
92 char *buf) \
93{ \
94 struct ipack_device *idev = to_ipack_dev(dev); \
95 return sprintf(buf, format_string, idev->field); \
96}
97
98static ssize_t
99id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
100{
101 struct ipack_device *idev = to_ipack_dev(dev);
102 switch (idev->id_format) {
103 case IPACK_ID_VERSION_1:
104 return sprintf(buf, "0x%02x\n", idev->id_vendor);
105 case IPACK_ID_VERSION_2:
106 return sprintf(buf, "0x%06x\n", idev->id_vendor);
107 default:
108 return -EIO;
109 }
110}
111
112static ssize_t
113id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
114{
115 struct ipack_device *idev = to_ipack_dev(dev);
116 switch (idev->id_format) {
117 case IPACK_ID_VERSION_1:
118 return sprintf(buf, "0x%02x\n", idev->id_device);
119 case IPACK_ID_VERSION_2:
120 return sprintf(buf, "0x%04x\n", idev->id_device);
121 default:
122 return -EIO;
123 }
124}
125
126ipack_device_attr(id_format, "0x%hhu\n");
127
128static struct device_attribute ipack_dev_attrs[] = {
129 __ATTR_RO(id_device),
130 __ATTR_RO(id_format),
131 __ATTR_RO(id_vendor),
132};
133
d3465872 134static struct bus_type ipack_bus_type = {
e8ed3276
JT
135 .name = "ipack",
136 .probe = ipack_bus_probe,
137 .match = ipack_bus_match,
138 .remove = ipack_bus_remove,
139 .dev_attrs = ipack_dev_attrs,
d3465872
SIG
140};
141
ec440335
SIG
142struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
143 struct ipack_bus_ops *ops)
d3465872
SIG
144{
145 int bus_nr;
ec440335
SIG
146 struct ipack_bus_device *bus;
147
148 bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL);
149 if (!bus)
150 return NULL;
d3465872 151
3b86bb2e 152 bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL);
ec440335
SIG
153 if (bus_nr < 0) {
154 kfree(bus);
155 return NULL;
156 }
d3465872
SIG
157
158 bus->bus_nr = bus_nr;
ec440335
SIG
159 bus->parent = parent;
160 bus->slots = slots;
161 bus->ops = ops;
162 return bus;
d3465872
SIG
163}
164EXPORT_SYMBOL_GPL(ipack_bus_register);
165
166int ipack_bus_unregister(struct ipack_bus_device *bus)
167{
3b86bb2e 168 ida_simple_remove(&ipack_ida, bus->bus_nr);
ec440335 169 kfree(bus);
d3465872
SIG
170 return 0;
171}
172EXPORT_SYMBOL_GPL(ipack_bus_unregister);
173
ec440335
SIG
174int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
175 char *name)
d3465872 176{
ec440335
SIG
177 edrv->driver.owner = owner;
178 edrv->driver.name = name;
d3465872
SIG
179 edrv->driver.bus = &ipack_bus_type;
180 return driver_register(&edrv->driver);
181}
182EXPORT_SYMBOL_GPL(ipack_driver_register);
183
184void ipack_driver_unregister(struct ipack_driver *edrv)
185{
186 driver_unregister(&edrv->driver);
187}
188EXPORT_SYMBOL_GPL(ipack_driver_unregister);
189
e8ed3276
JT
190static void ipack_parse_id1(struct ipack_device *dev)
191{
192 u8 *id = dev->id;
193
194 dev->id_vendor = id[4];
195 dev->id_device = id[5];
196}
197
198static void ipack_parse_id2(struct ipack_device *dev)
199{
200 __be16 *id = (__be16 *) dev->id;
201
202 dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
203 + be16_to_cpu(id[4]);
204 dev->id_device = be16_to_cpu(id[5]);
205}
206
187e4782
JT
207static int ipack_device_read_id(struct ipack_device *dev)
208{
209 u8 __iomem *idmem;
210 int i;
211 int ret = 0;
212
213 ret = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
214 if (ret) {
215 dev_err(&dev->dev, "error mapping memory\n");
216 return ret;
217 }
218 idmem = dev->id_space.address;
219
220 /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH"
221 * we are dealing with a IndustryPack format 1 device. If we detect
222 * "VITA4 " (16 bit big endian formatted) we are dealing with a
223 * IndustryPack format 2 device */
224 if ((ioread8(idmem + 1) == 'I') &&
225 (ioread8(idmem + 3) == 'P') &&
226 (ioread8(idmem + 5) == 'A') &&
227 ((ioread8(idmem + 7) == 'C') ||
228 (ioread8(idmem + 7) == 'H'))) {
229 dev->id_format = IPACK_ID_VERSION_1;
230 dev->id_avail = ioread8(idmem + 0x15);
231 if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
232 dev_warn(&dev->dev, "invalid id size");
233 dev->id_avail = 0x0c;
234 }
235 } else if ((ioread8(idmem + 0) == 'I') &&
236 (ioread8(idmem + 1) == 'V') &&
237 (ioread8(idmem + 2) == 'A') &&
238 (ioread8(idmem + 3) == 'T') &&
239 (ioread8(idmem + 4) == ' ') &&
240 (ioread8(idmem + 5) == '4')) {
241 dev->id_format = IPACK_ID_VERSION_2;
242 dev->id_avail = ioread16be(idmem + 0x16);
243 if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
244 dev_warn(&dev->dev, "invalid id size");
245 dev->id_avail = 0x1a;
246 }
247 } else {
248 dev->id_format = IPACK_ID_VERSION_INVALID;
249 dev->id_avail = 0;
250 }
251
252 if (!dev->id_avail) {
253 ret = -ENODEV;
254 goto out;
255 }
256
257 /* Obtain the amount of memory required to store a copy of the complete
258 * ID ROM contents */
259 dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
260 if (!dev->id) {
261 dev_err(&dev->dev, "dev->id alloc failed.\n");
262 ret = -ENOMEM;
263 goto out;
264 }
265 for (i = 0; i < dev->id_avail; i++) {
266 if (dev->id_format == IPACK_ID_VERSION_1)
267 dev->id[i] = ioread8(idmem + (i << 1) + 1);
268 else
269 dev->id[i] = ioread8(idmem + i);
270 }
271
e8ed3276
JT
272 /* now we can finally work with the copy */
273 switch (dev->id_format) {
274 case IPACK_ID_VERSION_1:
275 ipack_parse_id1(dev);
276 break;
277 case IPACK_ID_VERSION_2:
278 ipack_parse_id2(dev);
279 break;
280 }
281
187e4782
JT
282out:
283 dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
284
285 return ret;
286}
287
ec440335
SIG
288struct ipack_device *ipack_device_register(struct ipack_bus_device *bus,
289 int slot, int irqv)
d3465872
SIG
290{
291 int ret;
ec440335
SIG
292 struct ipack_device *dev;
293
294 dev = kzalloc(sizeof(struct ipack_device), GFP_KERNEL);
295 if (!dev)
296 return NULL;
d3465872
SIG
297
298 dev->dev.bus = &ipack_bus_type;
299 dev->dev.release = ipack_device_release;
ec440335
SIG
300 dev->dev.parent = bus->parent;
301 dev->slot = slot;
302 dev->bus_nr = bus->bus_nr;
303 dev->irq = irqv;
304 dev->bus = bus;
d3465872 305 dev_set_name(&dev->dev,
ec440335 306 "ipack-dev.%u.%u", dev->bus_nr, dev->slot);
d3465872 307
187e4782
JT
308 ret = ipack_device_read_id(dev);
309 if (ret < 0) {
310 dev_err(&dev->dev, "error reading device id section.\n");
311 kfree(dev);
312 return NULL;
313 }
314
d3465872
SIG
315 ret = device_register(&dev->dev);
316 if (ret < 0) {
187e4782 317 kfree(dev->id);
ec440335
SIG
318 kfree(dev);
319 return NULL;
d3465872
SIG
320 }
321
ec440335 322 return dev;
d3465872
SIG
323}
324EXPORT_SYMBOL_GPL(ipack_device_register);
325
326void ipack_device_unregister(struct ipack_device *dev)
327{
328 device_unregister(&dev->dev);
329}
330EXPORT_SYMBOL_GPL(ipack_device_unregister);
331
332static int __init ipack_init(void)
333{
3b86bb2e 334 ida_init(&ipack_ida);
d3465872
SIG
335 return bus_register(&ipack_bus_type);
336}
337
338static void __exit ipack_exit(void)
339{
340 bus_unregister(&ipack_bus_type);
3b86bb2e 341 ida_destroy(&ipack_ida);
d3465872
SIG
342}
343
344module_init(ipack_init);
345module_exit(ipack_exit);
346
347MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>");
348MODULE_LICENSE("GPL");
349MODULE_DESCRIPTION("Industry-pack bus core");
This page took 0.07838 seconds and 5 git commands to generate.