.. | .. |
---|
10 | 10 | * Wu Hao <hao.wu@intel.com> |
---|
11 | 11 | * Xiao Guangrong <guangrong.xiao@linux.intel.com> |
---|
12 | 12 | */ |
---|
| 13 | +#include <linux/fpga-dfl.h> |
---|
13 | 14 | #include <linux/module.h> |
---|
| 15 | +#include <linux/uaccess.h> |
---|
14 | 16 | |
---|
15 | 17 | #include "dfl.h" |
---|
16 | 18 | |
---|
.. | .. |
---|
28 | 30 | * index to dfl_chardevs table. If no chardev support just set devt_type |
---|
29 | 31 | * as one invalid index (DFL_FPGA_DEVT_MAX). |
---|
30 | 32 | */ |
---|
31 | | -enum dfl_id_type { |
---|
32 | | - FME_ID, /* fme id allocation and mapping */ |
---|
33 | | - PORT_ID, /* port id allocation and mapping */ |
---|
34 | | - DFL_ID_MAX, |
---|
35 | | -}; |
---|
36 | | - |
---|
37 | 33 | enum dfl_fpga_devt_type { |
---|
38 | 34 | DFL_FPGA_DEVT_FME, |
---|
39 | 35 | DFL_FPGA_DEVT_PORT, |
---|
.. | .. |
---|
56 | 52 | */ |
---|
57 | 53 | struct dfl_dev_info { |
---|
58 | 54 | const char *name; |
---|
59 | | - u32 dfh_id; |
---|
| 55 | + u16 dfh_id; |
---|
60 | 56 | struct idr id; |
---|
61 | 57 | enum dfl_fpga_devt_type devt_type; |
---|
62 | 58 | }; |
---|
.. | .. |
---|
132 | 128 | return DFL_ID_MAX; |
---|
133 | 129 | } |
---|
134 | 130 | |
---|
135 | | -static enum dfl_id_type dfh_id_to_type(u32 id) |
---|
| 131 | +static enum dfl_id_type dfh_id_to_type(u16 id) |
---|
136 | 132 | { |
---|
137 | 133 | int i; |
---|
138 | 134 | |
---|
.. | .. |
---|
231 | 227 | */ |
---|
232 | 228 | int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id) |
---|
233 | 229 | { |
---|
234 | | - struct dfl_fpga_port_ops *port_ops = dfl_fpga_port_ops_get(pdev); |
---|
235 | | - int port_id; |
---|
| 230 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
---|
| 231 | + struct dfl_fpga_port_ops *port_ops; |
---|
236 | 232 | |
---|
| 233 | + if (pdata->id != FEATURE_DEV_ID_UNUSED) |
---|
| 234 | + return pdata->id == *(int *)pport_id; |
---|
| 235 | + |
---|
| 236 | + port_ops = dfl_fpga_port_ops_get(pdev); |
---|
237 | 237 | if (!port_ops || !port_ops->get_id) |
---|
238 | 238 | return 0; |
---|
239 | 239 | |
---|
240 | | - port_id = port_ops->get_id(pdev); |
---|
| 240 | + pdata->id = port_ops->get_id(pdev); |
---|
241 | 241 | dfl_fpga_port_ops_put(port_ops); |
---|
242 | 242 | |
---|
243 | | - return port_id == *(int *)pport_id; |
---|
| 243 | + return pdata->id == *(int *)pport_id; |
---|
244 | 244 | } |
---|
245 | 245 | EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id); |
---|
| 246 | + |
---|
| 247 | +static DEFINE_IDA(dfl_device_ida); |
---|
| 248 | + |
---|
| 249 | +static const struct dfl_device_id * |
---|
| 250 | +dfl_match_one_device(const struct dfl_device_id *id, struct dfl_device *ddev) |
---|
| 251 | +{ |
---|
| 252 | + if (id->type == ddev->type && id->feature_id == ddev->feature_id) |
---|
| 253 | + return id; |
---|
| 254 | + |
---|
| 255 | + return NULL; |
---|
| 256 | +} |
---|
| 257 | + |
---|
| 258 | +static int dfl_bus_match(struct device *dev, struct device_driver *drv) |
---|
| 259 | +{ |
---|
| 260 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 261 | + struct dfl_driver *ddrv = to_dfl_drv(drv); |
---|
| 262 | + const struct dfl_device_id *id_entry; |
---|
| 263 | + |
---|
| 264 | + id_entry = ddrv->id_table; |
---|
| 265 | + if (id_entry) { |
---|
| 266 | + while (id_entry->feature_id) { |
---|
| 267 | + if (dfl_match_one_device(id_entry, ddev)) { |
---|
| 268 | + ddev->id_entry = id_entry; |
---|
| 269 | + return 1; |
---|
| 270 | + } |
---|
| 271 | + id_entry++; |
---|
| 272 | + } |
---|
| 273 | + } |
---|
| 274 | + |
---|
| 275 | + return 0; |
---|
| 276 | +} |
---|
| 277 | + |
---|
| 278 | +static int dfl_bus_probe(struct device *dev) |
---|
| 279 | +{ |
---|
| 280 | + struct dfl_driver *ddrv = to_dfl_drv(dev->driver); |
---|
| 281 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 282 | + |
---|
| 283 | + return ddrv->probe(ddev); |
---|
| 284 | +} |
---|
| 285 | + |
---|
| 286 | +static int dfl_bus_remove(struct device *dev) |
---|
| 287 | +{ |
---|
| 288 | + struct dfl_driver *ddrv = to_dfl_drv(dev->driver); |
---|
| 289 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 290 | + |
---|
| 291 | + if (ddrv->remove) |
---|
| 292 | + ddrv->remove(ddev); |
---|
| 293 | + |
---|
| 294 | + return 0; |
---|
| 295 | +} |
---|
| 296 | + |
---|
| 297 | +static int dfl_bus_uevent(struct device *dev, struct kobj_uevent_env *env) |
---|
| 298 | +{ |
---|
| 299 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 300 | + |
---|
| 301 | + /* The type has 4 valid bits and feature_id has 12 valid bits */ |
---|
| 302 | + return add_uevent_var(env, "MODALIAS=dfl:t%01Xf%03X", |
---|
| 303 | + ddev->type, ddev->feature_id); |
---|
| 304 | +} |
---|
| 305 | + |
---|
| 306 | +static ssize_t |
---|
| 307 | +type_show(struct device *dev, struct device_attribute *attr, char *buf) |
---|
| 308 | +{ |
---|
| 309 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 310 | + |
---|
| 311 | + return sprintf(buf, "0x%x\n", ddev->type); |
---|
| 312 | +} |
---|
| 313 | +static DEVICE_ATTR_RO(type); |
---|
| 314 | + |
---|
| 315 | +static ssize_t |
---|
| 316 | +feature_id_show(struct device *dev, struct device_attribute *attr, char *buf) |
---|
| 317 | +{ |
---|
| 318 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 319 | + |
---|
| 320 | + return sprintf(buf, "0x%x\n", ddev->feature_id); |
---|
| 321 | +} |
---|
| 322 | +static DEVICE_ATTR_RO(feature_id); |
---|
| 323 | + |
---|
| 324 | +static struct attribute *dfl_dev_attrs[] = { |
---|
| 325 | + &dev_attr_type.attr, |
---|
| 326 | + &dev_attr_feature_id.attr, |
---|
| 327 | + NULL, |
---|
| 328 | +}; |
---|
| 329 | +ATTRIBUTE_GROUPS(dfl_dev); |
---|
| 330 | + |
---|
| 331 | +static struct bus_type dfl_bus_type = { |
---|
| 332 | + .name = "dfl", |
---|
| 333 | + .match = dfl_bus_match, |
---|
| 334 | + .probe = dfl_bus_probe, |
---|
| 335 | + .remove = dfl_bus_remove, |
---|
| 336 | + .uevent = dfl_bus_uevent, |
---|
| 337 | + .dev_groups = dfl_dev_groups, |
---|
| 338 | +}; |
---|
| 339 | + |
---|
| 340 | +static void release_dfl_dev(struct device *dev) |
---|
| 341 | +{ |
---|
| 342 | + struct dfl_device *ddev = to_dfl_dev(dev); |
---|
| 343 | + |
---|
| 344 | + if (ddev->mmio_res.parent) |
---|
| 345 | + release_resource(&ddev->mmio_res); |
---|
| 346 | + |
---|
| 347 | + ida_simple_remove(&dfl_device_ida, ddev->id); |
---|
| 348 | + kfree(ddev->irqs); |
---|
| 349 | + kfree(ddev); |
---|
| 350 | +} |
---|
| 351 | + |
---|
| 352 | +static struct dfl_device * |
---|
| 353 | +dfl_dev_add(struct dfl_feature_platform_data *pdata, |
---|
| 354 | + struct dfl_feature *feature) |
---|
| 355 | +{ |
---|
| 356 | + struct platform_device *pdev = pdata->dev; |
---|
| 357 | + struct resource *parent_res; |
---|
| 358 | + struct dfl_device *ddev; |
---|
| 359 | + int id, i, ret; |
---|
| 360 | + |
---|
| 361 | + ddev = kzalloc(sizeof(*ddev), GFP_KERNEL); |
---|
| 362 | + if (!ddev) |
---|
| 363 | + return ERR_PTR(-ENOMEM); |
---|
| 364 | + |
---|
| 365 | + id = ida_simple_get(&dfl_device_ida, 0, 0, GFP_KERNEL); |
---|
| 366 | + if (id < 0) { |
---|
| 367 | + dev_err(&pdev->dev, "unable to get id\n"); |
---|
| 368 | + kfree(ddev); |
---|
| 369 | + return ERR_PTR(id); |
---|
| 370 | + } |
---|
| 371 | + |
---|
| 372 | + /* freeing resources by put_device() after device_initialize() */ |
---|
| 373 | + device_initialize(&ddev->dev); |
---|
| 374 | + ddev->dev.parent = &pdev->dev; |
---|
| 375 | + ddev->dev.bus = &dfl_bus_type; |
---|
| 376 | + ddev->dev.release = release_dfl_dev; |
---|
| 377 | + ddev->id = id; |
---|
| 378 | + ret = dev_set_name(&ddev->dev, "dfl_dev.%d", id); |
---|
| 379 | + if (ret) |
---|
| 380 | + goto put_dev; |
---|
| 381 | + |
---|
| 382 | + ddev->type = feature_dev_id_type(pdev); |
---|
| 383 | + ddev->feature_id = feature->id; |
---|
| 384 | + ddev->cdev = pdata->dfl_cdev; |
---|
| 385 | + |
---|
| 386 | + /* add mmio resource */ |
---|
| 387 | + parent_res = &pdev->resource[feature->resource_index]; |
---|
| 388 | + ddev->mmio_res.flags = IORESOURCE_MEM; |
---|
| 389 | + ddev->mmio_res.start = parent_res->start; |
---|
| 390 | + ddev->mmio_res.end = parent_res->end; |
---|
| 391 | + ddev->mmio_res.name = dev_name(&ddev->dev); |
---|
| 392 | + ret = insert_resource(parent_res, &ddev->mmio_res); |
---|
| 393 | + if (ret) { |
---|
| 394 | + dev_err(&pdev->dev, "%s failed to claim resource: %pR\n", |
---|
| 395 | + dev_name(&ddev->dev), &ddev->mmio_res); |
---|
| 396 | + goto put_dev; |
---|
| 397 | + } |
---|
| 398 | + |
---|
| 399 | + /* then add irq resource */ |
---|
| 400 | + if (feature->nr_irqs) { |
---|
| 401 | + ddev->irqs = kcalloc(feature->nr_irqs, |
---|
| 402 | + sizeof(*ddev->irqs), GFP_KERNEL); |
---|
| 403 | + if (!ddev->irqs) { |
---|
| 404 | + ret = -ENOMEM; |
---|
| 405 | + goto put_dev; |
---|
| 406 | + } |
---|
| 407 | + |
---|
| 408 | + for (i = 0; i < feature->nr_irqs; i++) |
---|
| 409 | + ddev->irqs[i] = feature->irq_ctx[i].irq; |
---|
| 410 | + |
---|
| 411 | + ddev->num_irqs = feature->nr_irqs; |
---|
| 412 | + } |
---|
| 413 | + |
---|
| 414 | + ret = device_add(&ddev->dev); |
---|
| 415 | + if (ret) |
---|
| 416 | + goto put_dev; |
---|
| 417 | + |
---|
| 418 | + dev_dbg(&pdev->dev, "add dfl_dev: %s\n", dev_name(&ddev->dev)); |
---|
| 419 | + return ddev; |
---|
| 420 | + |
---|
| 421 | +put_dev: |
---|
| 422 | + /* calls release_dfl_dev() which does the clean up */ |
---|
| 423 | + put_device(&ddev->dev); |
---|
| 424 | + return ERR_PTR(ret); |
---|
| 425 | +} |
---|
| 426 | + |
---|
| 427 | +static void dfl_devs_remove(struct dfl_feature_platform_data *pdata) |
---|
| 428 | +{ |
---|
| 429 | + struct dfl_feature *feature; |
---|
| 430 | + |
---|
| 431 | + dfl_fpga_dev_for_each_feature(pdata, feature) { |
---|
| 432 | + if (feature->ddev) { |
---|
| 433 | + device_unregister(&feature->ddev->dev); |
---|
| 434 | + feature->ddev = NULL; |
---|
| 435 | + } |
---|
| 436 | + } |
---|
| 437 | +} |
---|
| 438 | + |
---|
| 439 | +static int dfl_devs_add(struct dfl_feature_platform_data *pdata) |
---|
| 440 | +{ |
---|
| 441 | + struct dfl_feature *feature; |
---|
| 442 | + struct dfl_device *ddev; |
---|
| 443 | + int ret; |
---|
| 444 | + |
---|
| 445 | + dfl_fpga_dev_for_each_feature(pdata, feature) { |
---|
| 446 | + if (feature->ioaddr) |
---|
| 447 | + continue; |
---|
| 448 | + |
---|
| 449 | + if (feature->ddev) { |
---|
| 450 | + ret = -EEXIST; |
---|
| 451 | + goto err; |
---|
| 452 | + } |
---|
| 453 | + |
---|
| 454 | + ddev = dfl_dev_add(pdata, feature); |
---|
| 455 | + if (IS_ERR(ddev)) { |
---|
| 456 | + ret = PTR_ERR(ddev); |
---|
| 457 | + goto err; |
---|
| 458 | + } |
---|
| 459 | + |
---|
| 460 | + feature->ddev = ddev; |
---|
| 461 | + } |
---|
| 462 | + |
---|
| 463 | + return 0; |
---|
| 464 | + |
---|
| 465 | +err: |
---|
| 466 | + dfl_devs_remove(pdata); |
---|
| 467 | + return ret; |
---|
| 468 | +} |
---|
| 469 | + |
---|
| 470 | +int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner) |
---|
| 471 | +{ |
---|
| 472 | + if (!dfl_drv || !dfl_drv->probe || !dfl_drv->id_table) |
---|
| 473 | + return -EINVAL; |
---|
| 474 | + |
---|
| 475 | + dfl_drv->drv.owner = owner; |
---|
| 476 | + dfl_drv->drv.bus = &dfl_bus_type; |
---|
| 477 | + |
---|
| 478 | + return driver_register(&dfl_drv->drv); |
---|
| 479 | +} |
---|
| 480 | +EXPORT_SYMBOL(__dfl_driver_register); |
---|
| 481 | + |
---|
| 482 | +void dfl_driver_unregister(struct dfl_driver *dfl_drv) |
---|
| 483 | +{ |
---|
| 484 | + driver_unregister(&dfl_drv->drv); |
---|
| 485 | +} |
---|
| 486 | +EXPORT_SYMBOL(dfl_driver_unregister); |
---|
| 487 | + |
---|
| 488 | +#define is_header_feature(feature) ((feature)->id == FEATURE_ID_FIU_HEADER) |
---|
246 | 489 | |
---|
247 | 490 | /** |
---|
248 | 491 | * dfl_fpga_dev_feature_uinit - uinit for sub features of dfl feature device |
---|
.. | .. |
---|
253 | 496 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
---|
254 | 497 | struct dfl_feature *feature; |
---|
255 | 498 | |
---|
256 | | - dfl_fpga_dev_for_each_feature(pdata, feature) |
---|
| 499 | + dfl_devs_remove(pdata); |
---|
| 500 | + |
---|
| 501 | + dfl_fpga_dev_for_each_feature(pdata, feature) { |
---|
257 | 502 | if (feature->ops) { |
---|
258 | | - feature->ops->uinit(pdev, feature); |
---|
| 503 | + if (feature->ops->uinit) |
---|
| 504 | + feature->ops->uinit(pdev, feature); |
---|
259 | 505 | feature->ops = NULL; |
---|
260 | 506 | } |
---|
| 507 | + } |
---|
261 | 508 | } |
---|
262 | 509 | EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit); |
---|
263 | 510 | |
---|
.. | .. |
---|
266 | 513 | struct dfl_feature *feature, |
---|
267 | 514 | struct dfl_feature_driver *drv) |
---|
268 | 515 | { |
---|
269 | | - int ret; |
---|
| 516 | + void __iomem *base; |
---|
| 517 | + int ret = 0; |
---|
270 | 518 | |
---|
271 | | - ret = drv->ops->init(pdev, feature); |
---|
272 | | - if (ret) |
---|
273 | | - return ret; |
---|
| 519 | + if (!is_header_feature(feature)) { |
---|
| 520 | + base = devm_platform_ioremap_resource(pdev, |
---|
| 521 | + feature->resource_index); |
---|
| 522 | + if (IS_ERR(base)) { |
---|
| 523 | + dev_err(&pdev->dev, |
---|
| 524 | + "ioremap failed for feature 0x%x!\n", |
---|
| 525 | + feature->id); |
---|
| 526 | + return PTR_ERR(base); |
---|
| 527 | + } |
---|
| 528 | + |
---|
| 529 | + feature->ioaddr = base; |
---|
| 530 | + } |
---|
| 531 | + |
---|
| 532 | + if (drv->ops->init) { |
---|
| 533 | + ret = drv->ops->init(pdev, feature); |
---|
| 534 | + if (ret) |
---|
| 535 | + return ret; |
---|
| 536 | + } |
---|
274 | 537 | |
---|
275 | 538 | feature->ops = drv->ops; |
---|
276 | 539 | |
---|
277 | 540 | return ret; |
---|
| 541 | +} |
---|
| 542 | + |
---|
| 543 | +static bool dfl_feature_drv_match(struct dfl_feature *feature, |
---|
| 544 | + struct dfl_feature_driver *driver) |
---|
| 545 | +{ |
---|
| 546 | + const struct dfl_feature_id *ids = driver->id_table; |
---|
| 547 | + |
---|
| 548 | + if (ids) { |
---|
| 549 | + while (ids->id) { |
---|
| 550 | + if (ids->id == feature->id) |
---|
| 551 | + return true; |
---|
| 552 | + ids++; |
---|
| 553 | + } |
---|
| 554 | + } |
---|
| 555 | + return false; |
---|
278 | 556 | } |
---|
279 | 557 | |
---|
280 | 558 | /** |
---|
.. | .. |
---|
297 | 575 | |
---|
298 | 576 | while (drv->ops) { |
---|
299 | 577 | dfl_fpga_dev_for_each_feature(pdata, feature) { |
---|
300 | | - /* match feature and drv using id */ |
---|
301 | | - if (feature->id == drv->id) { |
---|
| 578 | + if (dfl_feature_drv_match(feature, drv)) { |
---|
302 | 579 | ret = dfl_feature_instance_init(pdev, pdata, |
---|
303 | 580 | feature, drv); |
---|
304 | 581 | if (ret) |
---|
.. | .. |
---|
307 | 584 | } |
---|
308 | 585 | drv++; |
---|
309 | 586 | } |
---|
| 587 | + |
---|
| 588 | + ret = dfl_devs_add(pdata); |
---|
| 589 | + if (ret) |
---|
| 590 | + goto exit; |
---|
310 | 591 | |
---|
311 | 592 | return 0; |
---|
312 | 593 | exit: |
---|
.. | .. |
---|
322 | 603 | for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) |
---|
323 | 604 | if (MAJOR(dfl_chrdevs[i].devt)) { |
---|
324 | 605 | unregister_chrdev_region(dfl_chrdevs[i].devt, |
---|
325 | | - MINORMASK); |
---|
| 606 | + MINORMASK + 1); |
---|
326 | 607 | dfl_chrdevs[i].devt = MKDEV(0, 0); |
---|
327 | 608 | } |
---|
328 | 609 | } |
---|
.. | .. |
---|
332 | 613 | int i, ret; |
---|
333 | 614 | |
---|
334 | 615 | for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) { |
---|
335 | | - ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, MINORMASK, |
---|
336 | | - dfl_chrdevs[i].name); |
---|
| 616 | + ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, |
---|
| 617 | + MINORMASK + 1, dfl_chrdevs[i].name); |
---|
337 | 618 | if (ret) |
---|
338 | 619 | goto exit; |
---|
339 | 620 | } |
---|
.. | .. |
---|
400 | 681 | * |
---|
401 | 682 | * @dev: device to enumerate. |
---|
402 | 683 | * @cdev: the container device for all feature devices. |
---|
| 684 | + * @nr_irqs: number of irqs for all feature devices. |
---|
| 685 | + * @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of |
---|
| 686 | + * this device. |
---|
403 | 687 | * @feature_dev: current feature device. |
---|
404 | | - * @ioaddr: header register region address of feature device in enumeration. |
---|
| 688 | + * @ioaddr: header register region address of current FIU in enumeration. |
---|
| 689 | + * @start: register resource start of current FIU. |
---|
| 690 | + * @len: max register resource length of current FIU. |
---|
405 | 691 | * @sub_features: a sub features linked list for feature device in enumeration. |
---|
406 | 692 | * @feature_num: number of sub features for feature device in enumeration. |
---|
407 | 693 | */ |
---|
408 | 694 | struct build_feature_devs_info { |
---|
409 | 695 | struct device *dev; |
---|
410 | 696 | struct dfl_fpga_cdev *cdev; |
---|
| 697 | + unsigned int nr_irqs; |
---|
| 698 | + int *irq_table; |
---|
| 699 | + |
---|
411 | 700 | struct platform_device *feature_dev; |
---|
412 | 701 | void __iomem *ioaddr; |
---|
| 702 | + resource_size_t start; |
---|
| 703 | + resource_size_t len; |
---|
413 | 704 | struct list_head sub_features; |
---|
414 | 705 | int feature_num; |
---|
415 | 706 | }; |
---|
.. | .. |
---|
421 | 712 | * @mmio_res: mmio resource of this sub feature. |
---|
422 | 713 | * @ioaddr: mapped base address of mmio resource. |
---|
423 | 714 | * @node: node in sub_features linked list. |
---|
| 715 | + * @irq_base: start of irq index in this sub feature. |
---|
| 716 | + * @nr_irqs: number of irqs of this sub feature. |
---|
424 | 717 | */ |
---|
425 | 718 | struct dfl_feature_info { |
---|
426 | | - u64 fid; |
---|
| 719 | + u16 fid; |
---|
427 | 720 | struct resource mmio_res; |
---|
428 | 721 | void __iomem *ioaddr; |
---|
429 | 722 | struct list_head node; |
---|
| 723 | + unsigned int irq_base; |
---|
| 724 | + unsigned int nr_irqs; |
---|
430 | 725 | }; |
---|
431 | 726 | |
---|
432 | 727 | static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, |
---|
.. | .. |
---|
451 | 746 | struct dfl_feature_platform_data *pdata; |
---|
452 | 747 | struct dfl_feature_info *finfo, *p; |
---|
453 | 748 | enum dfl_id_type type; |
---|
454 | | - int ret, index = 0; |
---|
455 | | - |
---|
456 | | - if (!fdev) |
---|
457 | | - return 0; |
---|
| 749 | + int ret, index = 0, res_idx = 0; |
---|
458 | 750 | |
---|
459 | 751 | type = feature_dev_id_type(fdev); |
---|
460 | 752 | if (WARN_ON_ONCE(type >= DFL_ID_MAX)) |
---|
.. | .. |
---|
466 | 758 | * it will be automatically freed by device's release() callback, |
---|
467 | 759 | * platform_device_release(). |
---|
468 | 760 | */ |
---|
469 | | - pdata = kzalloc(dfl_feature_platform_data_size(binfo->feature_num), |
---|
470 | | - GFP_KERNEL); |
---|
| 761 | + pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL); |
---|
471 | 762 | if (!pdata) |
---|
472 | 763 | return -ENOMEM; |
---|
473 | 764 | |
---|
474 | 765 | pdata->dev = fdev; |
---|
475 | 766 | pdata->num = binfo->feature_num; |
---|
476 | 767 | pdata->dfl_cdev = binfo->cdev; |
---|
| 768 | + pdata->id = FEATURE_DEV_ID_UNUSED; |
---|
477 | 769 | mutex_init(&pdata->lock); |
---|
478 | 770 | lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type], |
---|
479 | 771 | dfl_pdata_key_strings[type]); |
---|
.. | .. |
---|
497 | 789 | |
---|
498 | 790 | /* fill features and resource information for feature dev */ |
---|
499 | 791 | list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) { |
---|
500 | | - struct dfl_feature *feature = &pdata->features[index]; |
---|
| 792 | + struct dfl_feature *feature = &pdata->features[index++]; |
---|
| 793 | + struct dfl_feature_irq_ctx *ctx; |
---|
| 794 | + unsigned int i; |
---|
501 | 795 | |
---|
502 | 796 | /* save resource information for each feature */ |
---|
| 797 | + feature->dev = fdev; |
---|
503 | 798 | feature->id = finfo->fid; |
---|
504 | | - feature->resource_index = index; |
---|
505 | | - feature->ioaddr = finfo->ioaddr; |
---|
506 | | - fdev->resource[index++] = finfo->mmio_res; |
---|
| 799 | + |
---|
| 800 | + /* |
---|
| 801 | + * the FIU header feature has some fundamental functions (sriov |
---|
| 802 | + * set, port enable/disable) needed for the dfl bus device and |
---|
| 803 | + * other sub features. So its mmio resource should be mapped by |
---|
| 804 | + * DFL bus device. And we should not assign it to feature |
---|
| 805 | + * devices (dfl-fme/afu) again. |
---|
| 806 | + */ |
---|
| 807 | + if (is_header_feature(feature)) { |
---|
| 808 | + feature->resource_index = -1; |
---|
| 809 | + feature->ioaddr = |
---|
| 810 | + devm_ioremap_resource(binfo->dev, |
---|
| 811 | + &finfo->mmio_res); |
---|
| 812 | + if (IS_ERR(feature->ioaddr)) |
---|
| 813 | + return PTR_ERR(feature->ioaddr); |
---|
| 814 | + } else { |
---|
| 815 | + feature->resource_index = res_idx; |
---|
| 816 | + fdev->resource[res_idx++] = finfo->mmio_res; |
---|
| 817 | + } |
---|
| 818 | + |
---|
| 819 | + if (finfo->nr_irqs) { |
---|
| 820 | + ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs, |
---|
| 821 | + sizeof(*ctx), GFP_KERNEL); |
---|
| 822 | + if (!ctx) |
---|
| 823 | + return -ENOMEM; |
---|
| 824 | + |
---|
| 825 | + for (i = 0; i < finfo->nr_irqs; i++) |
---|
| 826 | + ctx[i].irq = |
---|
| 827 | + binfo->irq_table[finfo->irq_base + i]; |
---|
| 828 | + |
---|
| 829 | + feature->irq_ctx = ctx; |
---|
| 830 | + feature->nr_irqs = finfo->nr_irqs; |
---|
| 831 | + } |
---|
507 | 832 | |
---|
508 | 833 | list_del(&finfo->node); |
---|
509 | 834 | kfree(finfo); |
---|
.. | .. |
---|
532 | 857 | |
---|
533 | 858 | static int |
---|
534 | 859 | build_info_create_dev(struct build_feature_devs_info *binfo, |
---|
535 | | - enum dfl_id_type type, void __iomem *ioaddr) |
---|
| 860 | + enum dfl_id_type type) |
---|
536 | 861 | { |
---|
537 | 862 | struct platform_device *fdev; |
---|
538 | | - int ret; |
---|
539 | 863 | |
---|
540 | 864 | if (type >= DFL_ID_MAX) |
---|
541 | 865 | return -EINVAL; |
---|
542 | | - |
---|
543 | | - /* we will create a new device, commit current device first */ |
---|
544 | | - ret = build_info_commit_dev(binfo); |
---|
545 | | - if (ret) |
---|
546 | | - return ret; |
---|
547 | 866 | |
---|
548 | 867 | /* |
---|
549 | 868 | * we use -ENODEV as the initialization indicator which indicates |
---|
.. | .. |
---|
555 | 874 | |
---|
556 | 875 | binfo->feature_dev = fdev; |
---|
557 | 876 | binfo->feature_num = 0; |
---|
558 | | - binfo->ioaddr = ioaddr; |
---|
| 877 | + |
---|
559 | 878 | INIT_LIST_HEAD(&binfo->sub_features); |
---|
560 | 879 | |
---|
561 | 880 | fdev->id = dfl_id_alloc(type, &fdev->dev); |
---|
.. | .. |
---|
599 | 918 | return ofst ? ofst : 4096; |
---|
600 | 919 | } |
---|
601 | 920 | |
---|
602 | | -static u64 feature_id(void __iomem *start) |
---|
| 921 | +static u16 feature_id(void __iomem *start) |
---|
603 | 922 | { |
---|
604 | 923 | u64 v = readq(start + DFH); |
---|
605 | 924 | u16 id = FIELD_GET(DFH_ID, v); |
---|
.. | .. |
---|
616 | 935 | return 0; |
---|
617 | 936 | } |
---|
618 | 937 | |
---|
| 938 | +static int parse_feature_irqs(struct build_feature_devs_info *binfo, |
---|
| 939 | + resource_size_t ofst, u16 fid, |
---|
| 940 | + unsigned int *irq_base, unsigned int *nr_irqs) |
---|
| 941 | +{ |
---|
| 942 | + void __iomem *base = binfo->ioaddr + ofst; |
---|
| 943 | + unsigned int i, ibase, inr = 0; |
---|
| 944 | + int virq; |
---|
| 945 | + u64 v; |
---|
| 946 | + |
---|
| 947 | + /* |
---|
| 948 | + * Ideally DFL framework should only read info from DFL header, but |
---|
| 949 | + * current version DFL only provides mmio resources information for |
---|
| 950 | + * each feature in DFL Header, no field for interrupt resources. |
---|
| 951 | + * Interrupt resource information is provided by specific mmio |
---|
| 952 | + * registers of each private feature which supports interrupt. So in |
---|
| 953 | + * order to parse and assign irq resources, DFL framework has to look |
---|
| 954 | + * into specific capability registers of these private features. |
---|
| 955 | + * |
---|
| 956 | + * Once future DFL version supports generic interrupt resource |
---|
| 957 | + * information in common DFL headers, the generic interrupt parsing |
---|
| 958 | + * code will be added. But in order to be compatible to old version |
---|
| 959 | + * DFL, the driver may still fall back to these quirks. |
---|
| 960 | + */ |
---|
| 961 | + switch (fid) { |
---|
| 962 | + case PORT_FEATURE_ID_UINT: |
---|
| 963 | + v = readq(base + PORT_UINT_CAP); |
---|
| 964 | + ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); |
---|
| 965 | + inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); |
---|
| 966 | + break; |
---|
| 967 | + case PORT_FEATURE_ID_ERROR: |
---|
| 968 | + v = readq(base + PORT_ERROR_CAP); |
---|
| 969 | + ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); |
---|
| 970 | + inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); |
---|
| 971 | + break; |
---|
| 972 | + case FME_FEATURE_ID_GLOBAL_ERR: |
---|
| 973 | + v = readq(base + FME_ERROR_CAP); |
---|
| 974 | + ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); |
---|
| 975 | + inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); |
---|
| 976 | + break; |
---|
| 977 | + } |
---|
| 978 | + |
---|
| 979 | + if (!inr) { |
---|
| 980 | + *irq_base = 0; |
---|
| 981 | + *nr_irqs = 0; |
---|
| 982 | + return 0; |
---|
| 983 | + } |
---|
| 984 | + |
---|
| 985 | + dev_dbg(binfo->dev, "feature: 0x%x, irq_base: %u, nr_irqs: %u\n", |
---|
| 986 | + fid, ibase, inr); |
---|
| 987 | + |
---|
| 988 | + if (ibase + inr > binfo->nr_irqs) { |
---|
| 989 | + dev_err(binfo->dev, |
---|
| 990 | + "Invalid interrupt number in feature 0x%x\n", fid); |
---|
| 991 | + return -EINVAL; |
---|
| 992 | + } |
---|
| 993 | + |
---|
| 994 | + for (i = 0; i < inr; i++) { |
---|
| 995 | + virq = binfo->irq_table[ibase + i]; |
---|
| 996 | + if (virq < 0 || virq > NR_IRQS) { |
---|
| 997 | + dev_err(binfo->dev, |
---|
| 998 | + "Invalid irq table entry for feature 0x%x\n", |
---|
| 999 | + fid); |
---|
| 1000 | + return -EINVAL; |
---|
| 1001 | + } |
---|
| 1002 | + } |
---|
| 1003 | + |
---|
| 1004 | + *irq_base = ibase; |
---|
| 1005 | + *nr_irqs = inr; |
---|
| 1006 | + |
---|
| 1007 | + return 0; |
---|
| 1008 | +} |
---|
| 1009 | + |
---|
619 | 1010 | /* |
---|
620 | 1011 | * when create sub feature instances, for private features, it doesn't need |
---|
621 | 1012 | * to provide resource size and feature id as they could be read from DFH |
---|
.. | .. |
---|
625 | 1016 | */ |
---|
626 | 1017 | static int |
---|
627 | 1018 | create_feature_instance(struct build_feature_devs_info *binfo, |
---|
628 | | - struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst, |
---|
629 | | - resource_size_t size, u64 fid) |
---|
| 1019 | + resource_size_t ofst, resource_size_t size, u16 fid) |
---|
630 | 1020 | { |
---|
| 1021 | + unsigned int irq_base, nr_irqs; |
---|
631 | 1022 | struct dfl_feature_info *finfo; |
---|
| 1023 | + int ret; |
---|
632 | 1024 | |
---|
633 | 1025 | /* read feature size and id if inputs are invalid */ |
---|
634 | | - size = size ? size : feature_size(dfl->ioaddr + ofst); |
---|
635 | | - fid = fid ? fid : feature_id(dfl->ioaddr + ofst); |
---|
| 1026 | + size = size ? size : feature_size(binfo->ioaddr + ofst); |
---|
| 1027 | + fid = fid ? fid : feature_id(binfo->ioaddr + ofst); |
---|
636 | 1028 | |
---|
637 | | - if (dfl->len - ofst < size) |
---|
| 1029 | + if (binfo->len - ofst < size) |
---|
638 | 1030 | return -EINVAL; |
---|
| 1031 | + |
---|
| 1032 | + ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs); |
---|
| 1033 | + if (ret) |
---|
| 1034 | + return ret; |
---|
639 | 1035 | |
---|
640 | 1036 | finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); |
---|
641 | 1037 | if (!finfo) |
---|
642 | 1038 | return -ENOMEM; |
---|
643 | 1039 | |
---|
644 | 1040 | finfo->fid = fid; |
---|
645 | | - finfo->mmio_res.start = dfl->start + ofst; |
---|
| 1041 | + finfo->mmio_res.start = binfo->start + ofst; |
---|
646 | 1042 | finfo->mmio_res.end = finfo->mmio_res.start + size - 1; |
---|
647 | 1043 | finfo->mmio_res.flags = IORESOURCE_MEM; |
---|
648 | | - finfo->ioaddr = dfl->ioaddr + ofst; |
---|
| 1044 | + finfo->irq_base = irq_base; |
---|
| 1045 | + finfo->nr_irqs = nr_irqs; |
---|
649 | 1046 | |
---|
650 | 1047 | list_add_tail(&finfo->node, &binfo->sub_features); |
---|
651 | 1048 | binfo->feature_num++; |
---|
.. | .. |
---|
654 | 1051 | } |
---|
655 | 1052 | |
---|
656 | 1053 | static int parse_feature_port_afu(struct build_feature_devs_info *binfo, |
---|
657 | | - struct dfl_fpga_enum_dfl *dfl, |
---|
658 | 1054 | resource_size_t ofst) |
---|
659 | 1055 | { |
---|
660 | 1056 | u64 v = readq(binfo->ioaddr + PORT_HDR_CAP); |
---|
.. | .. |
---|
662 | 1058 | |
---|
663 | 1059 | WARN_ON(!size); |
---|
664 | 1060 | |
---|
665 | | - return create_feature_instance(binfo, dfl, ofst, size, FEATURE_ID_AFU); |
---|
| 1061 | + return create_feature_instance(binfo, ofst, size, FEATURE_ID_AFU); |
---|
666 | 1062 | } |
---|
667 | 1063 | |
---|
| 1064 | +#define is_feature_dev_detected(binfo) (!!(binfo)->feature_dev) |
---|
| 1065 | + |
---|
668 | 1066 | static int parse_feature_afu(struct build_feature_devs_info *binfo, |
---|
669 | | - struct dfl_fpga_enum_dfl *dfl, |
---|
670 | 1067 | resource_size_t ofst) |
---|
671 | 1068 | { |
---|
672 | | - if (!binfo->feature_dev) { |
---|
| 1069 | + if (!is_feature_dev_detected(binfo)) { |
---|
673 | 1070 | dev_err(binfo->dev, "this AFU does not belong to any FIU.\n"); |
---|
674 | 1071 | return -EINVAL; |
---|
675 | 1072 | } |
---|
676 | 1073 | |
---|
677 | 1074 | switch (feature_dev_id_type(binfo->feature_dev)) { |
---|
678 | 1075 | case PORT_ID: |
---|
679 | | - return parse_feature_port_afu(binfo, dfl, ofst); |
---|
| 1076 | + return parse_feature_port_afu(binfo, ofst); |
---|
680 | 1077 | default: |
---|
681 | 1078 | dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n", |
---|
682 | 1079 | binfo->feature_dev->name); |
---|
.. | .. |
---|
685 | 1082 | return 0; |
---|
686 | 1083 | } |
---|
687 | 1084 | |
---|
| 1085 | +static int build_info_prepare(struct build_feature_devs_info *binfo, |
---|
| 1086 | + resource_size_t start, resource_size_t len) |
---|
| 1087 | +{ |
---|
| 1088 | + struct device *dev = binfo->dev; |
---|
| 1089 | + void __iomem *ioaddr; |
---|
| 1090 | + |
---|
| 1091 | + if (!devm_request_mem_region(dev, start, len, dev_name(dev))) { |
---|
| 1092 | + dev_err(dev, "request region fail, start:%pa, len:%pa\n", |
---|
| 1093 | + &start, &len); |
---|
| 1094 | + return -EBUSY; |
---|
| 1095 | + } |
---|
| 1096 | + |
---|
| 1097 | + ioaddr = devm_ioremap(dev, start, len); |
---|
| 1098 | + if (!ioaddr) { |
---|
| 1099 | + dev_err(dev, "ioremap region fail, start:%pa, len:%pa\n", |
---|
| 1100 | + &start, &len); |
---|
| 1101 | + return -ENOMEM; |
---|
| 1102 | + } |
---|
| 1103 | + |
---|
| 1104 | + binfo->start = start; |
---|
| 1105 | + binfo->len = len; |
---|
| 1106 | + binfo->ioaddr = ioaddr; |
---|
| 1107 | + |
---|
| 1108 | + return 0; |
---|
| 1109 | +} |
---|
| 1110 | + |
---|
| 1111 | +static void build_info_complete(struct build_feature_devs_info *binfo) |
---|
| 1112 | +{ |
---|
| 1113 | + devm_iounmap(binfo->dev, binfo->ioaddr); |
---|
| 1114 | + devm_release_mem_region(binfo->dev, binfo->start, binfo->len); |
---|
| 1115 | +} |
---|
| 1116 | + |
---|
688 | 1117 | static int parse_feature_fiu(struct build_feature_devs_info *binfo, |
---|
689 | | - struct dfl_fpga_enum_dfl *dfl, |
---|
690 | 1118 | resource_size_t ofst) |
---|
691 | 1119 | { |
---|
692 | | - u32 id, offset; |
---|
693 | | - u64 v; |
---|
694 | 1120 | int ret = 0; |
---|
| 1121 | + u32 offset; |
---|
| 1122 | + u16 id; |
---|
| 1123 | + u64 v; |
---|
695 | 1124 | |
---|
696 | | - v = readq(dfl->ioaddr + ofst + DFH); |
---|
| 1125 | + if (is_feature_dev_detected(binfo)) { |
---|
| 1126 | + build_info_complete(binfo); |
---|
| 1127 | + |
---|
| 1128 | + ret = build_info_commit_dev(binfo); |
---|
| 1129 | + if (ret) |
---|
| 1130 | + return ret; |
---|
| 1131 | + |
---|
| 1132 | + ret = build_info_prepare(binfo, binfo->start + ofst, |
---|
| 1133 | + binfo->len - ofst); |
---|
| 1134 | + if (ret) |
---|
| 1135 | + return ret; |
---|
| 1136 | + } |
---|
| 1137 | + |
---|
| 1138 | + v = readq(binfo->ioaddr + DFH); |
---|
697 | 1139 | id = FIELD_GET(DFH_ID, v); |
---|
698 | 1140 | |
---|
699 | 1141 | /* create platform device for dfl feature dev */ |
---|
700 | | - ret = build_info_create_dev(binfo, dfh_id_to_type(id), |
---|
701 | | - dfl->ioaddr + ofst); |
---|
| 1142 | + ret = build_info_create_dev(binfo, dfh_id_to_type(id)); |
---|
702 | 1143 | if (ret) |
---|
703 | 1144 | return ret; |
---|
704 | 1145 | |
---|
705 | | - ret = create_feature_instance(binfo, dfl, ofst, 0, 0); |
---|
| 1146 | + ret = create_feature_instance(binfo, 0, 0, 0); |
---|
706 | 1147 | if (ret) |
---|
707 | 1148 | return ret; |
---|
708 | 1149 | /* |
---|
709 | 1150 | * find and parse FIU's child AFU via its NEXT_AFU register. |
---|
710 | 1151 | * please note that only Port has valid NEXT_AFU pointer per spec. |
---|
711 | 1152 | */ |
---|
712 | | - v = readq(dfl->ioaddr + ofst + NEXT_AFU); |
---|
| 1153 | + v = readq(binfo->ioaddr + NEXT_AFU); |
---|
713 | 1154 | |
---|
714 | 1155 | offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v); |
---|
715 | 1156 | if (offset) |
---|
716 | | - return parse_feature_afu(binfo, dfl, ofst + offset); |
---|
| 1157 | + return parse_feature_afu(binfo, offset); |
---|
717 | 1158 | |
---|
718 | 1159 | dev_dbg(binfo->dev, "No AFUs detected on FIU %d\n", id); |
---|
719 | 1160 | |
---|
.. | .. |
---|
721 | 1162 | } |
---|
722 | 1163 | |
---|
723 | 1164 | static int parse_feature_private(struct build_feature_devs_info *binfo, |
---|
724 | | - struct dfl_fpga_enum_dfl *dfl, |
---|
725 | 1165 | resource_size_t ofst) |
---|
726 | 1166 | { |
---|
727 | | - if (!binfo->feature_dev) { |
---|
728 | | - dev_err(binfo->dev, "the private feature %llx does not belong to any AFU.\n", |
---|
729 | | - (unsigned long long)feature_id(dfl->ioaddr + ofst)); |
---|
| 1167 | + if (!is_feature_dev_detected(binfo)) { |
---|
| 1168 | + dev_err(binfo->dev, "the private feature 0x%x does not belong to any AFU.\n", |
---|
| 1169 | + feature_id(binfo->ioaddr + ofst)); |
---|
730 | 1170 | return -EINVAL; |
---|
731 | 1171 | } |
---|
732 | 1172 | |
---|
733 | | - return create_feature_instance(binfo, dfl, ofst, 0, 0); |
---|
| 1173 | + return create_feature_instance(binfo, ofst, 0, 0); |
---|
734 | 1174 | } |
---|
735 | 1175 | |
---|
736 | 1176 | /** |
---|
737 | 1177 | * parse_feature - parse a feature on given device feature list |
---|
738 | 1178 | * |
---|
739 | 1179 | * @binfo: build feature devices information. |
---|
740 | | - * @dfl: device feature list to parse |
---|
741 | | - * @ofst: offset to feature header on this device feature list |
---|
| 1180 | + * @ofst: offset to current FIU header |
---|
742 | 1181 | */ |
---|
743 | 1182 | static int parse_feature(struct build_feature_devs_info *binfo, |
---|
744 | | - struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst) |
---|
| 1183 | + resource_size_t ofst) |
---|
745 | 1184 | { |
---|
746 | 1185 | u64 v; |
---|
747 | 1186 | u32 type; |
---|
748 | 1187 | |
---|
749 | | - v = readq(dfl->ioaddr + ofst + DFH); |
---|
| 1188 | + v = readq(binfo->ioaddr + ofst + DFH); |
---|
750 | 1189 | type = FIELD_GET(DFH_TYPE, v); |
---|
751 | 1190 | |
---|
752 | 1191 | switch (type) { |
---|
753 | 1192 | case DFH_TYPE_AFU: |
---|
754 | | - return parse_feature_afu(binfo, dfl, ofst); |
---|
| 1193 | + return parse_feature_afu(binfo, ofst); |
---|
755 | 1194 | case DFH_TYPE_PRIVATE: |
---|
756 | | - return parse_feature_private(binfo, dfl, ofst); |
---|
| 1195 | + return parse_feature_private(binfo, ofst); |
---|
757 | 1196 | case DFH_TYPE_FIU: |
---|
758 | | - return parse_feature_fiu(binfo, dfl, ofst); |
---|
| 1197 | + return parse_feature_fiu(binfo, ofst); |
---|
759 | 1198 | default: |
---|
760 | 1199 | dev_info(binfo->dev, |
---|
761 | 1200 | "Feature Type %x is not supported.\n", type); |
---|
.. | .. |
---|
765 | 1204 | } |
---|
766 | 1205 | |
---|
767 | 1206 | static int parse_feature_list(struct build_feature_devs_info *binfo, |
---|
768 | | - struct dfl_fpga_enum_dfl *dfl) |
---|
| 1207 | + resource_size_t start, resource_size_t len) |
---|
769 | 1208 | { |
---|
770 | | - void __iomem *start = dfl->ioaddr; |
---|
771 | | - void __iomem *end = dfl->ioaddr + dfl->len; |
---|
| 1209 | + resource_size_t end = start + len; |
---|
772 | 1210 | int ret = 0; |
---|
773 | 1211 | u32 ofst = 0; |
---|
774 | 1212 | u64 v; |
---|
| 1213 | + |
---|
| 1214 | + ret = build_info_prepare(binfo, start, len); |
---|
| 1215 | + if (ret) |
---|
| 1216 | + return ret; |
---|
775 | 1217 | |
---|
776 | 1218 | /* walk through the device feature list via DFH's next DFH pointer. */ |
---|
777 | 1219 | for (; start < end; start += ofst) { |
---|
.. | .. |
---|
780 | 1222 | return -EINVAL; |
---|
781 | 1223 | } |
---|
782 | 1224 | |
---|
783 | | - ret = parse_feature(binfo, dfl, start - dfl->ioaddr); |
---|
| 1225 | + ret = parse_feature(binfo, start - binfo->start); |
---|
784 | 1226 | if (ret) |
---|
785 | 1227 | return ret; |
---|
786 | 1228 | |
---|
787 | | - v = readq(start + DFH); |
---|
| 1229 | + v = readq(binfo->ioaddr + start - binfo->start + DFH); |
---|
788 | 1230 | ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v); |
---|
789 | 1231 | |
---|
790 | 1232 | /* stop parsing if EOL(End of List) is set or offset is 0 */ |
---|
.. | .. |
---|
793 | 1235 | } |
---|
794 | 1236 | |
---|
795 | 1237 | /* commit current feature device when reach the end of list */ |
---|
796 | | - return build_info_commit_dev(binfo); |
---|
| 1238 | + build_info_complete(binfo); |
---|
| 1239 | + |
---|
| 1240 | + if (is_feature_dev_detected(binfo)) |
---|
| 1241 | + ret = build_info_commit_dev(binfo); |
---|
| 1242 | + |
---|
| 1243 | + return ret; |
---|
797 | 1244 | } |
---|
798 | 1245 | |
---|
799 | 1246 | struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev) |
---|
.. | .. |
---|
831 | 1278 | devm_kfree(dev, dfl); |
---|
832 | 1279 | } |
---|
833 | 1280 | |
---|
| 1281 | + /* remove irq table */ |
---|
| 1282 | + if (info->irq_table) |
---|
| 1283 | + devm_kfree(dev, info->irq_table); |
---|
| 1284 | + |
---|
834 | 1285 | devm_kfree(dev, info); |
---|
835 | 1286 | put_device(dev); |
---|
836 | 1287 | } |
---|
.. | .. |
---|
842 | 1293 | * @info: ptr to dfl_fpga_enum_info |
---|
843 | 1294 | * @start: mmio resource address of the device feature list. |
---|
844 | 1295 | * @len: mmio resource length of the device feature list. |
---|
845 | | - * @ioaddr: mapped mmio resource address of the device feature list. |
---|
846 | 1296 | * |
---|
847 | 1297 | * One FPGA device may have one or more Device Feature Lists (DFLs), use this |
---|
848 | 1298 | * function to add information of each DFL to common data structure for next |
---|
.. | .. |
---|
851 | 1301 | * Return: 0 on success, negative error code otherwise. |
---|
852 | 1302 | */ |
---|
853 | 1303 | int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info, |
---|
854 | | - resource_size_t start, resource_size_t len, |
---|
855 | | - void __iomem *ioaddr) |
---|
| 1304 | + resource_size_t start, resource_size_t len) |
---|
856 | 1305 | { |
---|
857 | 1306 | struct dfl_fpga_enum_dfl *dfl; |
---|
858 | 1307 | |
---|
.. | .. |
---|
862 | 1311 | |
---|
863 | 1312 | dfl->start = start; |
---|
864 | 1313 | dfl->len = len; |
---|
865 | | - dfl->ioaddr = ioaddr; |
---|
866 | 1314 | |
---|
867 | 1315 | list_add_tail(&dfl->node, &info->dfls); |
---|
868 | 1316 | |
---|
869 | 1317 | return 0; |
---|
870 | 1318 | } |
---|
871 | 1319 | EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_dfl); |
---|
| 1320 | + |
---|
| 1321 | +/** |
---|
| 1322 | + * dfl_fpga_enum_info_add_irq - add irq table to enum info |
---|
| 1323 | + * |
---|
| 1324 | + * @info: ptr to dfl_fpga_enum_info |
---|
| 1325 | + * @nr_irqs: number of irqs of the DFL fpga device to be enumerated. |
---|
| 1326 | + * @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of |
---|
| 1327 | + * this device. |
---|
| 1328 | + * |
---|
| 1329 | + * One FPGA device may have several interrupts. This function adds irq |
---|
| 1330 | + * information of the DFL fpga device to enum info for next step enumeration. |
---|
| 1331 | + * This function should be called before dfl_fpga_feature_devs_enumerate(). |
---|
| 1332 | + * As we only support one irq domain for all DFLs in the same enum info, adding |
---|
| 1333 | + * irq table a second time for the same enum info will return error. |
---|
| 1334 | + * |
---|
| 1335 | + * If we need to enumerate DFLs which belong to different irq domains, we |
---|
| 1336 | + * should fill more enum info and enumerate them one by one. |
---|
| 1337 | + * |
---|
| 1338 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1339 | + */ |
---|
| 1340 | +int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info, |
---|
| 1341 | + unsigned int nr_irqs, int *irq_table) |
---|
| 1342 | +{ |
---|
| 1343 | + if (!nr_irqs || !irq_table) |
---|
| 1344 | + return -EINVAL; |
---|
| 1345 | + |
---|
| 1346 | + if (info->irq_table) |
---|
| 1347 | + return -EEXIST; |
---|
| 1348 | + |
---|
| 1349 | + info->irq_table = devm_kmemdup(info->dev, irq_table, |
---|
| 1350 | + sizeof(int) * nr_irqs, GFP_KERNEL); |
---|
| 1351 | + if (!info->irq_table) |
---|
| 1352 | + return -ENOMEM; |
---|
| 1353 | + |
---|
| 1354 | + info->nr_irqs = nr_irqs; |
---|
| 1355 | + |
---|
| 1356 | + return 0; |
---|
| 1357 | +} |
---|
| 1358 | +EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_irq); |
---|
872 | 1359 | |
---|
873 | 1360 | static int remove_feature_dev(struct device *dev, void *data) |
---|
874 | 1361 | { |
---|
.. | .. |
---|
913 | 1400 | if (!cdev) |
---|
914 | 1401 | return ERR_PTR(-ENOMEM); |
---|
915 | 1402 | |
---|
916 | | - cdev->region = fpga_region_create(info->dev, NULL, NULL); |
---|
| 1403 | + cdev->region = devm_fpga_region_create(info->dev, NULL, NULL); |
---|
917 | 1404 | if (!cdev->region) { |
---|
918 | 1405 | ret = -ENOMEM; |
---|
919 | 1406 | goto free_cdev_exit; |
---|
.. | .. |
---|
925 | 1412 | |
---|
926 | 1413 | ret = fpga_region_register(cdev->region); |
---|
927 | 1414 | if (ret) |
---|
928 | | - goto free_region_exit; |
---|
| 1415 | + goto free_cdev_exit; |
---|
929 | 1416 | |
---|
930 | 1417 | /* create and init build info for enumeration */ |
---|
931 | 1418 | binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL); |
---|
.. | .. |
---|
937 | 1424 | binfo->dev = info->dev; |
---|
938 | 1425 | binfo->cdev = cdev; |
---|
939 | 1426 | |
---|
| 1427 | + binfo->nr_irqs = info->nr_irqs; |
---|
| 1428 | + if (info->nr_irqs) |
---|
| 1429 | + binfo->irq_table = info->irq_table; |
---|
| 1430 | + |
---|
940 | 1431 | /* |
---|
941 | 1432 | * start enumeration for all feature devices based on Device Feature |
---|
942 | 1433 | * Lists. |
---|
943 | 1434 | */ |
---|
944 | 1435 | list_for_each_entry(dfl, &info->dfls, node) { |
---|
945 | | - ret = parse_feature_list(binfo, dfl); |
---|
| 1436 | + ret = parse_feature_list(binfo, dfl->start, dfl->len); |
---|
946 | 1437 | if (ret) { |
---|
947 | 1438 | remove_feature_devs(cdev); |
---|
948 | 1439 | build_info_free(binfo); |
---|
.. | .. |
---|
956 | 1447 | |
---|
957 | 1448 | unregister_region_exit: |
---|
958 | 1449 | fpga_region_unregister(cdev->region); |
---|
959 | | -free_region_exit: |
---|
960 | | - fpga_region_free(cdev->region); |
---|
961 | 1450 | free_cdev_exit: |
---|
962 | 1451 | devm_kfree(info->dev, cdev); |
---|
963 | 1452 | return ERR_PTR(ret); |
---|
.. | .. |
---|
975 | 1464 | { |
---|
976 | 1465 | struct dfl_feature_platform_data *pdata, *ptmp; |
---|
977 | 1466 | |
---|
978 | | - remove_feature_devs(cdev); |
---|
979 | | - |
---|
980 | 1467 | mutex_lock(&cdev->lock); |
---|
981 | | - if (cdev->fme_dev) { |
---|
982 | | - /* the fme should be unregistered. */ |
---|
983 | | - WARN_ON(device_is_registered(cdev->fme_dev)); |
---|
| 1468 | + if (cdev->fme_dev) |
---|
984 | 1469 | put_device(cdev->fme_dev); |
---|
985 | | - } |
---|
986 | 1470 | |
---|
987 | 1471 | list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) { |
---|
988 | 1472 | struct platform_device *port_dev = pdata->dev; |
---|
989 | 1473 | |
---|
990 | | - /* the port should be unregistered. */ |
---|
991 | | - WARN_ON(device_is_registered(&port_dev->dev)); |
---|
| 1474 | + /* remove released ports */ |
---|
| 1475 | + if (!device_is_registered(&port_dev->dev)) { |
---|
| 1476 | + dfl_id_free(feature_dev_id_type(port_dev), |
---|
| 1477 | + port_dev->id); |
---|
| 1478 | + platform_device_put(port_dev); |
---|
| 1479 | + } |
---|
| 1480 | + |
---|
992 | 1481 | list_del(&pdata->node); |
---|
993 | 1482 | put_device(&port_dev->dev); |
---|
994 | 1483 | } |
---|
995 | 1484 | mutex_unlock(&cdev->lock); |
---|
| 1485 | + |
---|
| 1486 | + remove_feature_devs(cdev); |
---|
996 | 1487 | |
---|
997 | 1488 | fpga_region_unregister(cdev->region); |
---|
998 | 1489 | devm_kfree(cdev->parent, cdev); |
---|
.. | .. |
---|
1035 | 1526 | { |
---|
1036 | 1527 | int ret; |
---|
1037 | 1528 | |
---|
| 1529 | + ret = bus_register(&dfl_bus_type); |
---|
| 1530 | + if (ret) |
---|
| 1531 | + return ret; |
---|
| 1532 | + |
---|
1038 | 1533 | dfl_ids_init(); |
---|
1039 | 1534 | |
---|
1040 | 1535 | ret = dfl_chardev_init(); |
---|
1041 | | - if (ret) |
---|
| 1536 | + if (ret) { |
---|
1042 | 1537 | dfl_ids_destroy(); |
---|
| 1538 | + bus_unregister(&dfl_bus_type); |
---|
| 1539 | + } |
---|
1043 | 1540 | |
---|
1044 | 1541 | return ret; |
---|
1045 | 1542 | } |
---|
| 1543 | + |
---|
| 1544 | +/** |
---|
| 1545 | + * dfl_fpga_cdev_release_port - release a port platform device |
---|
| 1546 | + * |
---|
| 1547 | + * @cdev: parent container device. |
---|
| 1548 | + * @port_id: id of the port platform device. |
---|
| 1549 | + * |
---|
| 1550 | + * This function allows user to release a port platform device. This is a |
---|
| 1551 | + * mandatory step before turn a port from PF into VF for SRIOV support. |
---|
| 1552 | + * |
---|
| 1553 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1554 | + */ |
---|
| 1555 | +int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id) |
---|
| 1556 | +{ |
---|
| 1557 | + struct dfl_feature_platform_data *pdata; |
---|
| 1558 | + struct platform_device *port_pdev; |
---|
| 1559 | + int ret = -ENODEV; |
---|
| 1560 | + |
---|
| 1561 | + mutex_lock(&cdev->lock); |
---|
| 1562 | + port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, |
---|
| 1563 | + dfl_fpga_check_port_id); |
---|
| 1564 | + if (!port_pdev) |
---|
| 1565 | + goto unlock_exit; |
---|
| 1566 | + |
---|
| 1567 | + if (!device_is_registered(&port_pdev->dev)) { |
---|
| 1568 | + ret = -EBUSY; |
---|
| 1569 | + goto put_dev_exit; |
---|
| 1570 | + } |
---|
| 1571 | + |
---|
| 1572 | + pdata = dev_get_platdata(&port_pdev->dev); |
---|
| 1573 | + |
---|
| 1574 | + mutex_lock(&pdata->lock); |
---|
| 1575 | + ret = dfl_feature_dev_use_begin(pdata, true); |
---|
| 1576 | + mutex_unlock(&pdata->lock); |
---|
| 1577 | + if (ret) |
---|
| 1578 | + goto put_dev_exit; |
---|
| 1579 | + |
---|
| 1580 | + platform_device_del(port_pdev); |
---|
| 1581 | + cdev->released_port_num++; |
---|
| 1582 | +put_dev_exit: |
---|
| 1583 | + put_device(&port_pdev->dev); |
---|
| 1584 | +unlock_exit: |
---|
| 1585 | + mutex_unlock(&cdev->lock); |
---|
| 1586 | + return ret; |
---|
| 1587 | +} |
---|
| 1588 | +EXPORT_SYMBOL_GPL(dfl_fpga_cdev_release_port); |
---|
| 1589 | + |
---|
| 1590 | +/** |
---|
| 1591 | + * dfl_fpga_cdev_assign_port - assign a port platform device back |
---|
| 1592 | + * |
---|
| 1593 | + * @cdev: parent container device. |
---|
| 1594 | + * @port_id: id of the port platform device. |
---|
| 1595 | + * |
---|
| 1596 | + * This function allows user to assign a port platform device back. This is |
---|
| 1597 | + * a mandatory step after disable SRIOV support. |
---|
| 1598 | + * |
---|
| 1599 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1600 | + */ |
---|
| 1601 | +int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id) |
---|
| 1602 | +{ |
---|
| 1603 | + struct dfl_feature_platform_data *pdata; |
---|
| 1604 | + struct platform_device *port_pdev; |
---|
| 1605 | + int ret = -ENODEV; |
---|
| 1606 | + |
---|
| 1607 | + mutex_lock(&cdev->lock); |
---|
| 1608 | + port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, |
---|
| 1609 | + dfl_fpga_check_port_id); |
---|
| 1610 | + if (!port_pdev) |
---|
| 1611 | + goto unlock_exit; |
---|
| 1612 | + |
---|
| 1613 | + if (device_is_registered(&port_pdev->dev)) { |
---|
| 1614 | + ret = -EBUSY; |
---|
| 1615 | + goto put_dev_exit; |
---|
| 1616 | + } |
---|
| 1617 | + |
---|
| 1618 | + ret = platform_device_add(port_pdev); |
---|
| 1619 | + if (ret) |
---|
| 1620 | + goto put_dev_exit; |
---|
| 1621 | + |
---|
| 1622 | + pdata = dev_get_platdata(&port_pdev->dev); |
---|
| 1623 | + |
---|
| 1624 | + mutex_lock(&pdata->lock); |
---|
| 1625 | + dfl_feature_dev_use_end(pdata); |
---|
| 1626 | + mutex_unlock(&pdata->lock); |
---|
| 1627 | + |
---|
| 1628 | + cdev->released_port_num--; |
---|
| 1629 | +put_dev_exit: |
---|
| 1630 | + put_device(&port_pdev->dev); |
---|
| 1631 | +unlock_exit: |
---|
| 1632 | + mutex_unlock(&cdev->lock); |
---|
| 1633 | + return ret; |
---|
| 1634 | +} |
---|
| 1635 | +EXPORT_SYMBOL_GPL(dfl_fpga_cdev_assign_port); |
---|
| 1636 | + |
---|
| 1637 | +static void config_port_access_mode(struct device *fme_dev, int port_id, |
---|
| 1638 | + bool is_vf) |
---|
| 1639 | +{ |
---|
| 1640 | + void __iomem *base; |
---|
| 1641 | + u64 v; |
---|
| 1642 | + |
---|
| 1643 | + base = dfl_get_feature_ioaddr_by_id(fme_dev, FME_FEATURE_ID_HEADER); |
---|
| 1644 | + |
---|
| 1645 | + v = readq(base + FME_HDR_PORT_OFST(port_id)); |
---|
| 1646 | + |
---|
| 1647 | + v &= ~FME_PORT_OFST_ACC_CTRL; |
---|
| 1648 | + v |= FIELD_PREP(FME_PORT_OFST_ACC_CTRL, |
---|
| 1649 | + is_vf ? FME_PORT_OFST_ACC_VF : FME_PORT_OFST_ACC_PF); |
---|
| 1650 | + |
---|
| 1651 | + writeq(v, base + FME_HDR_PORT_OFST(port_id)); |
---|
| 1652 | +} |
---|
| 1653 | + |
---|
| 1654 | +#define config_port_vf_mode(dev, id) config_port_access_mode(dev, id, true) |
---|
| 1655 | +#define config_port_pf_mode(dev, id) config_port_access_mode(dev, id, false) |
---|
| 1656 | + |
---|
| 1657 | +/** |
---|
| 1658 | + * dfl_fpga_cdev_config_ports_pf - configure ports to PF access mode |
---|
| 1659 | + * |
---|
| 1660 | + * @cdev: parent container device. |
---|
| 1661 | + * |
---|
| 1662 | + * This function is needed in sriov configuration routine. It could be used to |
---|
| 1663 | + * configure the all released ports from VF access mode to PF. |
---|
| 1664 | + */ |
---|
| 1665 | +void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev) |
---|
| 1666 | +{ |
---|
| 1667 | + struct dfl_feature_platform_data *pdata; |
---|
| 1668 | + |
---|
| 1669 | + mutex_lock(&cdev->lock); |
---|
| 1670 | + list_for_each_entry(pdata, &cdev->port_dev_list, node) { |
---|
| 1671 | + if (device_is_registered(&pdata->dev->dev)) |
---|
| 1672 | + continue; |
---|
| 1673 | + |
---|
| 1674 | + config_port_pf_mode(cdev->fme_dev, pdata->id); |
---|
| 1675 | + } |
---|
| 1676 | + mutex_unlock(&cdev->lock); |
---|
| 1677 | +} |
---|
| 1678 | +EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_pf); |
---|
| 1679 | + |
---|
| 1680 | +/** |
---|
| 1681 | + * dfl_fpga_cdev_config_ports_vf - configure ports to VF access mode |
---|
| 1682 | + * |
---|
| 1683 | + * @cdev: parent container device. |
---|
| 1684 | + * @num_vfs: VF device number. |
---|
| 1685 | + * |
---|
| 1686 | + * This function is needed in sriov configuration routine. It could be used to |
---|
| 1687 | + * configure the released ports from PF access mode to VF. |
---|
| 1688 | + * |
---|
| 1689 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1690 | + */ |
---|
| 1691 | +int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs) |
---|
| 1692 | +{ |
---|
| 1693 | + struct dfl_feature_platform_data *pdata; |
---|
| 1694 | + int ret = 0; |
---|
| 1695 | + |
---|
| 1696 | + mutex_lock(&cdev->lock); |
---|
| 1697 | + /* |
---|
| 1698 | + * can't turn multiple ports into 1 VF device, only 1 port for 1 VF |
---|
| 1699 | + * device, so if released port number doesn't match VF device number, |
---|
| 1700 | + * then reject the request with -EINVAL error code. |
---|
| 1701 | + */ |
---|
| 1702 | + if (cdev->released_port_num != num_vfs) { |
---|
| 1703 | + ret = -EINVAL; |
---|
| 1704 | + goto done; |
---|
| 1705 | + } |
---|
| 1706 | + |
---|
| 1707 | + list_for_each_entry(pdata, &cdev->port_dev_list, node) { |
---|
| 1708 | + if (device_is_registered(&pdata->dev->dev)) |
---|
| 1709 | + continue; |
---|
| 1710 | + |
---|
| 1711 | + config_port_vf_mode(cdev->fme_dev, pdata->id); |
---|
| 1712 | + } |
---|
| 1713 | +done: |
---|
| 1714 | + mutex_unlock(&cdev->lock); |
---|
| 1715 | + return ret; |
---|
| 1716 | +} |
---|
| 1717 | +EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf); |
---|
| 1718 | + |
---|
| 1719 | +static irqreturn_t dfl_irq_handler(int irq, void *arg) |
---|
| 1720 | +{ |
---|
| 1721 | + struct eventfd_ctx *trigger = arg; |
---|
| 1722 | + |
---|
| 1723 | + eventfd_signal(trigger, 1); |
---|
| 1724 | + return IRQ_HANDLED; |
---|
| 1725 | +} |
---|
| 1726 | + |
---|
| 1727 | +static int do_set_irq_trigger(struct dfl_feature *feature, unsigned int idx, |
---|
| 1728 | + int fd) |
---|
| 1729 | +{ |
---|
| 1730 | + struct platform_device *pdev = feature->dev; |
---|
| 1731 | + struct eventfd_ctx *trigger; |
---|
| 1732 | + int irq, ret; |
---|
| 1733 | + |
---|
| 1734 | + irq = feature->irq_ctx[idx].irq; |
---|
| 1735 | + |
---|
| 1736 | + if (feature->irq_ctx[idx].trigger) { |
---|
| 1737 | + free_irq(irq, feature->irq_ctx[idx].trigger); |
---|
| 1738 | + kfree(feature->irq_ctx[idx].name); |
---|
| 1739 | + eventfd_ctx_put(feature->irq_ctx[idx].trigger); |
---|
| 1740 | + feature->irq_ctx[idx].trigger = NULL; |
---|
| 1741 | + } |
---|
| 1742 | + |
---|
| 1743 | + if (fd < 0) |
---|
| 1744 | + return 0; |
---|
| 1745 | + |
---|
| 1746 | + feature->irq_ctx[idx].name = |
---|
| 1747 | + kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%x)", idx, |
---|
| 1748 | + dev_name(&pdev->dev), feature->id); |
---|
| 1749 | + if (!feature->irq_ctx[idx].name) |
---|
| 1750 | + return -ENOMEM; |
---|
| 1751 | + |
---|
| 1752 | + trigger = eventfd_ctx_fdget(fd); |
---|
| 1753 | + if (IS_ERR(trigger)) { |
---|
| 1754 | + ret = PTR_ERR(trigger); |
---|
| 1755 | + goto free_name; |
---|
| 1756 | + } |
---|
| 1757 | + |
---|
| 1758 | + ret = request_irq(irq, dfl_irq_handler, 0, |
---|
| 1759 | + feature->irq_ctx[idx].name, trigger); |
---|
| 1760 | + if (!ret) { |
---|
| 1761 | + feature->irq_ctx[idx].trigger = trigger; |
---|
| 1762 | + return ret; |
---|
| 1763 | + } |
---|
| 1764 | + |
---|
| 1765 | + eventfd_ctx_put(trigger); |
---|
| 1766 | +free_name: |
---|
| 1767 | + kfree(feature->irq_ctx[idx].name); |
---|
| 1768 | + |
---|
| 1769 | + return ret; |
---|
| 1770 | +} |
---|
| 1771 | + |
---|
| 1772 | +/** |
---|
| 1773 | + * dfl_fpga_set_irq_triggers - set eventfd triggers for dfl feature interrupts |
---|
| 1774 | + * |
---|
| 1775 | + * @feature: dfl sub feature. |
---|
| 1776 | + * @start: start of irq index in this dfl sub feature. |
---|
| 1777 | + * @count: number of irqs. |
---|
| 1778 | + * @fds: eventfds to bind with irqs. unbind related irq if fds[n] is negative. |
---|
| 1779 | + * unbind "count" specified number of irqs if fds ptr is NULL. |
---|
| 1780 | + * |
---|
| 1781 | + * Bind given eventfds with irqs in this dfl sub feature. Unbind related irq if |
---|
| 1782 | + * fds[n] is negative. Unbind "count" specified number of irqs if fds ptr is |
---|
| 1783 | + * NULL. |
---|
| 1784 | + * |
---|
| 1785 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1786 | + */ |
---|
| 1787 | +int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start, |
---|
| 1788 | + unsigned int count, int32_t *fds) |
---|
| 1789 | +{ |
---|
| 1790 | + unsigned int i; |
---|
| 1791 | + int ret = 0; |
---|
| 1792 | + |
---|
| 1793 | + /* overflow */ |
---|
| 1794 | + if (unlikely(start + count < start)) |
---|
| 1795 | + return -EINVAL; |
---|
| 1796 | + |
---|
| 1797 | + /* exceeds nr_irqs */ |
---|
| 1798 | + if (start + count > feature->nr_irqs) |
---|
| 1799 | + return -EINVAL; |
---|
| 1800 | + |
---|
| 1801 | + for (i = 0; i < count; i++) { |
---|
| 1802 | + int fd = fds ? fds[i] : -1; |
---|
| 1803 | + |
---|
| 1804 | + ret = do_set_irq_trigger(feature, start + i, fd); |
---|
| 1805 | + if (ret) { |
---|
| 1806 | + while (i--) |
---|
| 1807 | + do_set_irq_trigger(feature, start + i, -1); |
---|
| 1808 | + break; |
---|
| 1809 | + } |
---|
| 1810 | + } |
---|
| 1811 | + |
---|
| 1812 | + return ret; |
---|
| 1813 | +} |
---|
| 1814 | +EXPORT_SYMBOL_GPL(dfl_fpga_set_irq_triggers); |
---|
| 1815 | + |
---|
| 1816 | +/** |
---|
| 1817 | + * dfl_feature_ioctl_get_num_irqs - dfl feature _GET_IRQ_NUM ioctl interface. |
---|
| 1818 | + * @pdev: the feature device which has the sub feature |
---|
| 1819 | + * @feature: the dfl sub feature |
---|
| 1820 | + * @arg: ioctl argument |
---|
| 1821 | + * |
---|
| 1822 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1823 | + */ |
---|
| 1824 | +long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev, |
---|
| 1825 | + struct dfl_feature *feature, |
---|
| 1826 | + unsigned long arg) |
---|
| 1827 | +{ |
---|
| 1828 | + return put_user(feature->nr_irqs, (__u32 __user *)arg); |
---|
| 1829 | +} |
---|
| 1830 | +EXPORT_SYMBOL_GPL(dfl_feature_ioctl_get_num_irqs); |
---|
| 1831 | + |
---|
| 1832 | +/** |
---|
| 1833 | + * dfl_feature_ioctl_set_irq - dfl feature _SET_IRQ ioctl interface. |
---|
| 1834 | + * @pdev: the feature device which has the sub feature |
---|
| 1835 | + * @feature: the dfl sub feature |
---|
| 1836 | + * @arg: ioctl argument |
---|
| 1837 | + * |
---|
| 1838 | + * Return: 0 on success, negative error code otherwise. |
---|
| 1839 | + */ |
---|
| 1840 | +long dfl_feature_ioctl_set_irq(struct platform_device *pdev, |
---|
| 1841 | + struct dfl_feature *feature, |
---|
| 1842 | + unsigned long arg) |
---|
| 1843 | +{ |
---|
| 1844 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
---|
| 1845 | + struct dfl_fpga_irq_set hdr; |
---|
| 1846 | + s32 *fds; |
---|
| 1847 | + long ret; |
---|
| 1848 | + |
---|
| 1849 | + if (!feature->nr_irqs) |
---|
| 1850 | + return -ENOENT; |
---|
| 1851 | + |
---|
| 1852 | + if (copy_from_user(&hdr, (void __user *)arg, sizeof(hdr))) |
---|
| 1853 | + return -EFAULT; |
---|
| 1854 | + |
---|
| 1855 | + if (!hdr.count || (hdr.start + hdr.count > feature->nr_irqs) || |
---|
| 1856 | + (hdr.start + hdr.count < hdr.start)) |
---|
| 1857 | + return -EINVAL; |
---|
| 1858 | + |
---|
| 1859 | + fds = memdup_user((void __user *)(arg + sizeof(hdr)), |
---|
| 1860 | + array_size(hdr.count, sizeof(s32))); |
---|
| 1861 | + if (IS_ERR(fds)) |
---|
| 1862 | + return PTR_ERR(fds); |
---|
| 1863 | + |
---|
| 1864 | + mutex_lock(&pdata->lock); |
---|
| 1865 | + ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds); |
---|
| 1866 | + mutex_unlock(&pdata->lock); |
---|
| 1867 | + |
---|
| 1868 | + kfree(fds); |
---|
| 1869 | + return ret; |
---|
| 1870 | +} |
---|
| 1871 | +EXPORT_SYMBOL_GPL(dfl_feature_ioctl_set_irq); |
---|
1046 | 1872 | |
---|
1047 | 1873 | static void __exit dfl_fpga_exit(void) |
---|
1048 | 1874 | { |
---|
1049 | 1875 | dfl_chardev_uinit(); |
---|
1050 | 1876 | dfl_ids_destroy(); |
---|
| 1877 | + bus_unregister(&dfl_bus_type); |
---|
1051 | 1878 | } |
---|
1052 | 1879 | |
---|
1053 | 1880 | module_init(dfl_fpga_init); |
---|