root/drivers/ipack/ipack.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ipack_device_release
  2. ipack_match_one_device
  3. ipack_match_id
  4. ipack_bus_match
  5. ipack_bus_probe
  6. ipack_bus_remove
  7. ipack_uevent
  8. id_show
  9. id_vendor_show
  10. id_device_show
  11. modalias_show
  12. ipack_bus_register
  13. ipack_unregister_bus_member
  14. ipack_bus_unregister
  15. ipack_driver_register
  16. ipack_driver_unregister
  17. ipack_crc_byte
  18. ipack_calc_crc1
  19. ipack_calc_crc2
  20. ipack_parse_id1
  21. ipack_parse_id2
  22. ipack_device_read_id
  23. ipack_device_init
  24. ipack_device_add
  25. ipack_device_del
  26. ipack_get_device
  27. ipack_put_device
  28. ipack_init
  29. ipack_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Industry-pack bus support functions.
   4  *
   5  * Copyright (C) 2011-2012 CERN (www.cern.ch)
   6  * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
   7  */
   8 
   9 #include <linux/module.h>
  10 #include <linux/slab.h>
  11 #include <linux/idr.h>
  12 #include <linux/io.h>
  13 #include <linux/ipack.h>
  14 
  15 #define to_ipack_dev(device) container_of(device, struct ipack_device, dev)
  16 #define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver)
  17 
  18 static DEFINE_IDA(ipack_ida);
  19 
  20 static void ipack_device_release(struct device *dev)
  21 {
  22         struct ipack_device *device = to_ipack_dev(dev);
  23         kfree(device->id);
  24         device->release(device);
  25 }
  26 
  27 static inline const struct ipack_device_id *
  28 ipack_match_one_device(const struct ipack_device_id *id,
  29                        const struct ipack_device *device)
  30 {
  31         if ((id->format == IPACK_ANY_FORMAT ||
  32                                 id->format == device->id_format) &&
  33             (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) &&
  34             (id->device == IPACK_ANY_ID || id->device == device->id_device))
  35                 return id;
  36         return NULL;
  37 }
  38 
  39 static const struct ipack_device_id *
  40 ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev)
  41 {
  42         if (ids) {
  43                 while (ids->vendor || ids->device) {
  44                         if (ipack_match_one_device(ids, idev))
  45                                 return ids;
  46                         ids++;
  47                 }
  48         }
  49         return NULL;
  50 }
  51 
  52 static int ipack_bus_match(struct device *dev, struct device_driver *drv)
  53 {
  54         struct ipack_device *idev = to_ipack_dev(dev);
  55         struct ipack_driver *idrv = to_ipack_driver(drv);
  56         const struct ipack_device_id *found_id;
  57 
  58         found_id = ipack_match_id(idrv->id_table, idev);
  59         return found_id ? 1 : 0;
  60 }
  61 
  62 static int ipack_bus_probe(struct device *device)
  63 {
  64         struct ipack_device *dev = to_ipack_dev(device);
  65         struct ipack_driver *drv = to_ipack_driver(device->driver);
  66 
  67         if (!drv->ops->probe)
  68                 return -EINVAL;
  69 
  70         return drv->ops->probe(dev);
  71 }
  72 
  73 static int ipack_bus_remove(struct device *device)
  74 {
  75         struct ipack_device *dev = to_ipack_dev(device);
  76         struct ipack_driver *drv = to_ipack_driver(device->driver);
  77 
  78         if (!drv->ops->remove)
  79                 return -EINVAL;
  80 
  81         drv->ops->remove(dev);
  82         return 0;
  83 }
  84 
  85 static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env)
  86 {
  87         struct ipack_device *idev;
  88 
  89         if (!dev)
  90                 return -ENODEV;
  91 
  92         idev = to_ipack_dev(dev);
  93 
  94         if (add_uevent_var(env,
  95                            "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format,
  96                            idev->id_vendor, idev->id_device))
  97                 return -ENOMEM;
  98 
  99         return 0;
 100 }
 101 
 102 #define ipack_device_attr(field, format_string)                         \
 103 static ssize_t                                                          \
 104 field##_show(struct device *dev, struct device_attribute *attr,         \
 105                 char *buf)                                              \
 106 {                                                                       \
 107         struct ipack_device *idev = to_ipack_dev(dev);                  \
 108         return sprintf(buf, format_string, idev->field);                \
 109 }
 110 
 111 static ssize_t id_show(struct device *dev,
 112                        struct device_attribute *attr, char *buf)
 113 {
 114         unsigned int i, c, l, s;
 115         struct ipack_device *idev = to_ipack_dev(dev);
 116 
 117 
 118         switch (idev->id_format) {
 119         case IPACK_ID_VERSION_1:
 120                 l = 0x7; s = 1; break;
 121         case IPACK_ID_VERSION_2:
 122                 l = 0xf; s = 2; break;
 123         default:
 124                 return -EIO;
 125         }
 126         c = 0;
 127         for (i = 0; i < idev->id_avail; i++) {
 128                 if (i > 0) {
 129                         if ((i & l) == 0)
 130                                 buf[c++] = '\n';
 131                         else if ((i & s) == 0)
 132                                 buf[c++] = ' ';
 133                 }
 134                 sprintf(&buf[c], "%02x", idev->id[i]);
 135                 c += 2;
 136         }
 137         buf[c++] = '\n';
 138         return c;
 139 }
 140 
 141 static ssize_t
 142 id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
 143 {
 144         struct ipack_device *idev = to_ipack_dev(dev);
 145         switch (idev->id_format) {
 146         case IPACK_ID_VERSION_1:
 147                 return sprintf(buf, "0x%02x\n", idev->id_vendor);
 148         case IPACK_ID_VERSION_2:
 149                 return sprintf(buf, "0x%06x\n", idev->id_vendor);
 150         default:
 151                 return -EIO;
 152         }
 153 }
 154 
 155 static ssize_t
 156 id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
 157 {
 158         struct ipack_device *idev = to_ipack_dev(dev);
 159         switch (idev->id_format) {
 160         case IPACK_ID_VERSION_1:
 161                 return sprintf(buf, "0x%02x\n", idev->id_device);
 162         case IPACK_ID_VERSION_2:
 163                 return sprintf(buf, "0x%04x\n", idev->id_device);
 164         default:
 165                 return -EIO;
 166         }
 167 }
 168 
 169 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 170                              char *buf)
 171 {
 172         struct ipack_device *idev = to_ipack_dev(dev);
 173 
 174         return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format,
 175                        idev->id_vendor, idev->id_device);
 176 }
 177 
 178 ipack_device_attr(id_format, "0x%hhx\n");
 179 
 180 static DEVICE_ATTR_RO(id);
 181 static DEVICE_ATTR_RO(id_device);
 182 static DEVICE_ATTR_RO(id_format);
 183 static DEVICE_ATTR_RO(id_vendor);
 184 static DEVICE_ATTR_RO(modalias);
 185 
 186 static struct attribute *ipack_attrs[] = {
 187         &dev_attr_id.attr,
 188         &dev_attr_id_device.attr,
 189         &dev_attr_id_format.attr,
 190         &dev_attr_id_vendor.attr,
 191         &dev_attr_modalias.attr,
 192         NULL,
 193 };
 194 ATTRIBUTE_GROUPS(ipack);
 195 
 196 static struct bus_type ipack_bus_type = {
 197         .name      = "ipack",
 198         .probe     = ipack_bus_probe,
 199         .match     = ipack_bus_match,
 200         .remove    = ipack_bus_remove,
 201         .dev_groups = ipack_groups,
 202         .uevent    = ipack_uevent,
 203 };
 204 
 205 struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
 206                                             const struct ipack_bus_ops *ops,
 207                                             struct module *owner)
 208 {
 209         int bus_nr;
 210         struct ipack_bus_device *bus;
 211 
 212         bus = kzalloc(sizeof(*bus), GFP_KERNEL);
 213         if (!bus)
 214                 return NULL;
 215 
 216         bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL);
 217         if (bus_nr < 0) {
 218                 kfree(bus);
 219                 return NULL;
 220         }
 221 
 222         bus->bus_nr = bus_nr;
 223         bus->parent = parent;
 224         bus->slots = slots;
 225         bus->ops = ops;
 226         bus->owner = owner;
 227         return bus;
 228 }
 229 EXPORT_SYMBOL_GPL(ipack_bus_register);
 230 
 231 static int ipack_unregister_bus_member(struct device *dev, void *data)
 232 {
 233         struct ipack_device *idev = to_ipack_dev(dev);
 234         struct ipack_bus_device *bus = data;
 235 
 236         if (idev->bus == bus)
 237                 ipack_device_del(idev);
 238 
 239         return 1;
 240 }
 241 
 242 int ipack_bus_unregister(struct ipack_bus_device *bus)
 243 {
 244         bus_for_each_dev(&ipack_bus_type, NULL, bus,
 245                 ipack_unregister_bus_member);
 246         ida_simple_remove(&ipack_ida, bus->bus_nr);
 247         kfree(bus);
 248         return 0;
 249 }
 250 EXPORT_SYMBOL_GPL(ipack_bus_unregister);
 251 
 252 int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
 253                           const char *name)
 254 {
 255         edrv->driver.owner = owner;
 256         edrv->driver.name = name;
 257         edrv->driver.bus = &ipack_bus_type;
 258         return driver_register(&edrv->driver);
 259 }
 260 EXPORT_SYMBOL_GPL(ipack_driver_register);
 261 
 262 void ipack_driver_unregister(struct ipack_driver *edrv)
 263 {
 264         driver_unregister(&edrv->driver);
 265 }
 266 EXPORT_SYMBOL_GPL(ipack_driver_unregister);
 267 
 268 static u16 ipack_crc_byte(u16 crc, u8 c)
 269 {
 270         int i;
 271 
 272         crc ^= c << 8;
 273         for (i = 0; i < 8; i++)
 274                 crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0);
 275         return crc;
 276 }
 277 
 278 /*
 279  * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the
 280  * opposite bit ordering.
 281  */
 282 static u8 ipack_calc_crc1(struct ipack_device *dev)
 283 {
 284         u8 c;
 285         u16 crc;
 286         unsigned int i;
 287 
 288         crc = 0xffff;
 289         for (i = 0; i < dev->id_avail; i++) {
 290                 c = (i != 11) ? dev->id[i] : 0;
 291                 crc = ipack_crc_byte(crc, c);
 292         }
 293         crc = ~crc;
 294         return crc & 0xff;
 295 }
 296 
 297 static u16 ipack_calc_crc2(struct ipack_device *dev)
 298 {
 299         u8 c;
 300         u16 crc;
 301         unsigned int i;
 302 
 303         crc = 0xffff;
 304         for (i = 0; i < dev->id_avail; i++) {
 305                 c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0;
 306                 crc = ipack_crc_byte(crc, c);
 307         }
 308         crc = ~crc;
 309         return crc;
 310 }
 311 
 312 static void ipack_parse_id1(struct ipack_device *dev)
 313 {
 314         u8 *id = dev->id;
 315         u8 crc;
 316 
 317         dev->id_vendor = id[4];
 318         dev->id_device = id[5];
 319         dev->speed_8mhz = 1;
 320         dev->speed_32mhz = (id[7] == 'H');
 321         crc = ipack_calc_crc1(dev);
 322         dev->id_crc_correct = (crc == id[11]);
 323         if (!dev->id_crc_correct) {
 324                 dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
 325                                 id[11], crc);
 326         }
 327 }
 328 
 329 static void ipack_parse_id2(struct ipack_device *dev)
 330 {
 331         __be16 *id = (__be16 *) dev->id;
 332         u16 flags, crc;
 333 
 334         dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
 335                          + be16_to_cpu(id[4]);
 336         dev->id_device = be16_to_cpu(id[5]);
 337         flags = be16_to_cpu(id[10]);
 338         dev->speed_8mhz = !!(flags & 2);
 339         dev->speed_32mhz = !!(flags & 4);
 340         crc = ipack_calc_crc2(dev);
 341         dev->id_crc_correct = (crc == be16_to_cpu(id[12]));
 342         if (!dev->id_crc_correct) {
 343                 dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
 344                                 id[11], crc);
 345         }
 346 }
 347 
 348 static int ipack_device_read_id(struct ipack_device *dev)
 349 {
 350         u8 __iomem *idmem;
 351         int i;
 352         int ret = 0;
 353 
 354         idmem = ioremap(dev->region[IPACK_ID_SPACE].start,
 355                         dev->region[IPACK_ID_SPACE].size);
 356         if (!idmem) {
 357                 dev_err(&dev->dev, "error mapping memory\n");
 358                 return -ENOMEM;
 359         }
 360 
 361         /* Determine ID PROM Data Format.  If we find the ids "IPAC" or "IPAH"
 362          * we are dealing with a IndustryPack  format 1 device.  If we detect
 363          * "VITA4 " (16 bit big endian formatted) we are dealing with a
 364          * IndustryPack format 2 device */
 365         if ((ioread8(idmem + 1) == 'I') &&
 366                         (ioread8(idmem + 3) == 'P') &&
 367                         (ioread8(idmem + 5) == 'A') &&
 368                         ((ioread8(idmem + 7) == 'C') ||
 369                          (ioread8(idmem + 7) == 'H'))) {
 370                 dev->id_format = IPACK_ID_VERSION_1;
 371                 dev->id_avail = ioread8(idmem + 0x15);
 372                 if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
 373                         dev_warn(&dev->dev, "invalid id size");
 374                         dev->id_avail = 0x0c;
 375                 }
 376         } else if ((ioread8(idmem + 0) == 'I') &&
 377                         (ioread8(idmem + 1) == 'V') &&
 378                         (ioread8(idmem + 2) == 'A') &&
 379                         (ioread8(idmem + 3) == 'T') &&
 380                         (ioread8(idmem + 4) == ' ') &&
 381                         (ioread8(idmem + 5) == '4')) {
 382                 dev->id_format = IPACK_ID_VERSION_2;
 383                 dev->id_avail = ioread16be(idmem + 0x16);
 384                 if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
 385                         dev_warn(&dev->dev, "invalid id size");
 386                         dev->id_avail = 0x1a;
 387                 }
 388         } else {
 389                 dev->id_format = IPACK_ID_VERSION_INVALID;
 390                 dev->id_avail = 0;
 391         }
 392 
 393         if (!dev->id_avail) {
 394                 ret = -ENODEV;
 395                 goto out;
 396         }
 397 
 398         /* Obtain the amount of memory required to store a copy of the complete
 399          * ID ROM contents */
 400         dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
 401         if (!dev->id) {
 402                 ret = -ENOMEM;
 403                 goto out;
 404         }
 405         for (i = 0; i < dev->id_avail; i++) {
 406                 if (dev->id_format == IPACK_ID_VERSION_1)
 407                         dev->id[i] = ioread8(idmem + (i << 1) + 1);
 408                 else
 409                         dev->id[i] = ioread8(idmem + i);
 410         }
 411 
 412         /* now we can finally work with the copy */
 413         switch (dev->id_format) {
 414         case IPACK_ID_VERSION_1:
 415                 ipack_parse_id1(dev);
 416                 break;
 417         case IPACK_ID_VERSION_2:
 418                 ipack_parse_id2(dev);
 419                 break;
 420         }
 421 
 422 out:
 423         iounmap(idmem);
 424 
 425         return ret;
 426 }
 427 
 428 int ipack_device_init(struct ipack_device *dev)
 429 {
 430         int ret;
 431 
 432         dev->dev.bus = &ipack_bus_type;
 433         dev->dev.release = ipack_device_release;
 434         dev->dev.parent = dev->bus->parent;
 435         dev_set_name(&dev->dev,
 436                      "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot);
 437         device_initialize(&dev->dev);
 438 
 439         if (dev->bus->ops->set_clockrate(dev, 8))
 440                 dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n");
 441         if (dev->bus->ops->reset_timeout(dev))
 442                 dev_warn(&dev->dev, "failed to reset potential timeout.");
 443 
 444         ret = ipack_device_read_id(dev);
 445         if (ret < 0) {
 446                 dev_err(&dev->dev, "error reading device id section.\n");
 447                 return ret;
 448         }
 449 
 450         /* if the device supports 32 MHz operation, use it. */
 451         if (dev->speed_32mhz) {
 452                 ret = dev->bus->ops->set_clockrate(dev, 32);
 453                 if (ret < 0)
 454                         dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n");
 455         }
 456 
 457         return 0;
 458 }
 459 EXPORT_SYMBOL_GPL(ipack_device_init);
 460 
 461 int ipack_device_add(struct ipack_device *dev)
 462 {
 463         return device_add(&dev->dev);
 464 }
 465 EXPORT_SYMBOL_GPL(ipack_device_add);
 466 
 467 void ipack_device_del(struct ipack_device *dev)
 468 {
 469         device_del(&dev->dev);
 470         ipack_put_device(dev);
 471 }
 472 EXPORT_SYMBOL_GPL(ipack_device_del);
 473 
 474 void ipack_get_device(struct ipack_device *dev)
 475 {
 476         get_device(&dev->dev);
 477 }
 478 EXPORT_SYMBOL_GPL(ipack_get_device);
 479 
 480 void ipack_put_device(struct ipack_device *dev)
 481 {
 482         put_device(&dev->dev);
 483 }
 484 EXPORT_SYMBOL_GPL(ipack_put_device);
 485 
 486 static int __init ipack_init(void)
 487 {
 488         ida_init(&ipack_ida);
 489         return bus_register(&ipack_bus_type);
 490 }
 491 
 492 static void __exit ipack_exit(void)
 493 {
 494         bus_unregister(&ipack_bus_type);
 495         ida_destroy(&ipack_ida);
 496 }
 497 
 498 module_init(ipack_init);
 499 module_exit(ipack_exit);
 500 
 501 MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>");
 502 MODULE_LICENSE("GPL");
 503 MODULE_DESCRIPTION("Industry-pack bus core");

/* [<][>][^][v][top][bottom][index][help] */