| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2015, Linaro Limited |
|---|
| 3 | | - * |
|---|
| 4 | | - * This software is licensed under the terms of the GNU General Public |
|---|
| 5 | | - * License version 2, as published by the Free Software Foundation, and |
|---|
| 6 | | - * may be copied, distributed, and modified under those terms. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 9 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | | - * GNU General Public License for more details. |
|---|
| 12 | | - * |
|---|
| 13 | 4 | */ |
|---|
| 14 | 5 | |
|---|
| 15 | 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 16 | 7 | |
|---|
| 17 | 8 | #include <linux/arm-smccc.h> |
|---|
| 9 | +#include <linux/crash_dump.h> |
|---|
| 18 | 10 | #include <linux/errno.h> |
|---|
| 19 | 11 | #include <linux/io.h> |
|---|
| 20 | 12 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 26 | 18 | #include <linux/tee_drv.h> |
|---|
| 27 | 19 | #include <linux/types.h> |
|---|
| 28 | 20 | #include <linux/uaccess.h> |
|---|
| 21 | +#include <linux/workqueue.h> |
|---|
| 29 | 22 | #include "optee_private.h" |
|---|
| 30 | 23 | #include "optee_smc.h" |
|---|
| 31 | 24 | #include "shm_pool.h" |
|---|
| .. | .. |
|---|
| 214 | 207 | |
|---|
| 215 | 208 | if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) |
|---|
| 216 | 209 | v.gen_caps |= TEE_GEN_CAP_REG_MEM; |
|---|
| 210 | + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) |
|---|
| 211 | + v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; |
|---|
| 217 | 212 | *vers = v; |
|---|
| 213 | +} |
|---|
| 214 | + |
|---|
| 215 | +static void optee_bus_scan(struct work_struct *work) |
|---|
| 216 | +{ |
|---|
| 217 | + WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); |
|---|
| 218 | 218 | } |
|---|
| 219 | 219 | |
|---|
| 220 | 220 | static int optee_open(struct tee_context *ctx) |
|---|
| .. | .. |
|---|
| 240 | 240 | kfree(ctxdata); |
|---|
| 241 | 241 | return -EBUSY; |
|---|
| 242 | 242 | } |
|---|
| 243 | | - } |
|---|
| 244 | 243 | |
|---|
| 244 | + if (!optee->scan_bus_done) { |
|---|
| 245 | + INIT_WORK(&optee->scan_bus_work, optee_bus_scan); |
|---|
| 246 | + optee->scan_bus_wq = create_workqueue("optee_bus_scan"); |
|---|
| 247 | + if (!optee->scan_bus_wq) { |
|---|
| 248 | + kfree(ctxdata); |
|---|
| 249 | + return -ECHILD; |
|---|
| 250 | + } |
|---|
| 251 | + queue_work(optee->scan_bus_wq, &optee->scan_bus_work); |
|---|
| 252 | + optee->scan_bus_done = true; |
|---|
| 253 | + } |
|---|
| 254 | + } |
|---|
| 245 | 255 | mutex_init(&ctxdata->mutex); |
|---|
| 246 | 256 | INIT_LIST_HEAD(&ctxdata->sess_list); |
|---|
| 257 | + |
|---|
| 258 | + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) |
|---|
| 259 | + ctx->cap_memref_null = true; |
|---|
| 260 | + else |
|---|
| 261 | + ctx->cap_memref_null = false; |
|---|
| 247 | 262 | |
|---|
| 248 | 263 | ctx->data = ctxdata; |
|---|
| 249 | 264 | return 0; |
|---|
| .. | .. |
|---|
| 263 | 278 | if (!ctxdata) |
|---|
| 264 | 279 | return; |
|---|
| 265 | 280 | |
|---|
| 266 | | - shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); |
|---|
| 281 | + shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), |
|---|
| 282 | + TEE_SHM_MAPPED | TEE_SHM_PRIV); |
|---|
| 267 | 283 | if (!IS_ERR(shm)) { |
|---|
| 268 | 284 | arg = tee_shm_get_va(shm, 0); |
|---|
| 269 | 285 | /* |
|---|
| .. | .. |
|---|
| 295 | 311 | |
|---|
| 296 | 312 | ctx->data = NULL; |
|---|
| 297 | 313 | |
|---|
| 298 | | - if (teedev == optee->supp_teedev) |
|---|
| 314 | + if (teedev == optee->supp_teedev) { |
|---|
| 315 | + if (optee->scan_bus_wq) { |
|---|
| 316 | + destroy_workqueue(optee->scan_bus_wq); |
|---|
| 317 | + optee->scan_bus_wq = NULL; |
|---|
| 318 | + } |
|---|
| 299 | 319 | optee_supp_release(&optee->supp); |
|---|
| 320 | + } |
|---|
| 300 | 321 | } |
|---|
| 301 | 322 | |
|---|
| 302 | 323 | static const struct tee_driver_ops optee_ops = { |
|---|
| .. | .. |
|---|
| 409 | 430 | return true; |
|---|
| 410 | 431 | } |
|---|
| 411 | 432 | |
|---|
| 433 | +static struct tee_shm_pool *optee_config_dyn_shm(void) |
|---|
| 434 | +{ |
|---|
| 435 | + struct tee_shm_pool_mgr *priv_mgr; |
|---|
| 436 | + struct tee_shm_pool_mgr *dmabuf_mgr; |
|---|
| 437 | + void *rc; |
|---|
| 438 | + |
|---|
| 439 | + rc = optee_shm_pool_alloc_pages(); |
|---|
| 440 | + if (IS_ERR(rc)) |
|---|
| 441 | + return rc; |
|---|
| 442 | + priv_mgr = rc; |
|---|
| 443 | + |
|---|
| 444 | + rc = optee_shm_pool_alloc_pages(); |
|---|
| 445 | + if (IS_ERR(rc)) { |
|---|
| 446 | + tee_shm_pool_mgr_destroy(priv_mgr); |
|---|
| 447 | + return rc; |
|---|
| 448 | + } |
|---|
| 449 | + dmabuf_mgr = rc; |
|---|
| 450 | + |
|---|
| 451 | + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); |
|---|
| 452 | + if (IS_ERR(rc)) { |
|---|
| 453 | + tee_shm_pool_mgr_destroy(priv_mgr); |
|---|
| 454 | + tee_shm_pool_mgr_destroy(dmabuf_mgr); |
|---|
| 455 | + } |
|---|
| 456 | + |
|---|
| 457 | + return rc; |
|---|
| 458 | +} |
|---|
| 459 | + |
|---|
| 412 | 460 | static struct tee_shm_pool * |
|---|
| 413 | | -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm, |
|---|
| 414 | | - u32 sec_caps) |
|---|
| 461 | +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) |
|---|
| 415 | 462 | { |
|---|
| 416 | 463 | union { |
|---|
| 417 | 464 | struct arm_smccc_res smccc; |
|---|
| .. | .. |
|---|
| 426 | 473 | struct tee_shm_pool_mgr *priv_mgr; |
|---|
| 427 | 474 | struct tee_shm_pool_mgr *dmabuf_mgr; |
|---|
| 428 | 475 | void *rc; |
|---|
| 476 | + const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; |
|---|
| 429 | 477 | |
|---|
| 430 | 478 | invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); |
|---|
| 431 | 479 | if (res.result.status != OPTEE_SMC_RETURN_OK) { |
|---|
| 432 | | - pr_info("shm service not available\n"); |
|---|
| 480 | + pr_err("static shm service not available\n"); |
|---|
| 433 | 481 | return ERR_PTR(-ENOENT); |
|---|
| 434 | 482 | } |
|---|
| 435 | 483 | |
|---|
| .. | .. |
|---|
| 455 | 503 | } |
|---|
| 456 | 504 | vaddr = (unsigned long)va; |
|---|
| 457 | 505 | |
|---|
| 458 | | - /* |
|---|
| 459 | | - * If OP-TEE can work with unregistered SHM, we will use own pool |
|---|
| 460 | | - * for private shm |
|---|
| 461 | | - */ |
|---|
| 462 | | - if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) { |
|---|
| 463 | | - rc = optee_shm_pool_alloc_pages(); |
|---|
| 464 | | - if (IS_ERR(rc)) |
|---|
| 465 | | - goto err_memunmap; |
|---|
| 466 | | - priv_mgr = rc; |
|---|
| 467 | | - } else { |
|---|
| 468 | | - const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; |
|---|
| 506 | + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, |
|---|
| 507 | + 3 /* 8 bytes aligned */); |
|---|
| 508 | + if (IS_ERR(rc)) |
|---|
| 509 | + goto err_memunmap; |
|---|
| 510 | + priv_mgr = rc; |
|---|
| 469 | 511 | |
|---|
| 470 | | - rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, |
|---|
| 471 | | - 3 /* 8 bytes aligned */); |
|---|
| 472 | | - if (IS_ERR(rc)) |
|---|
| 473 | | - goto err_memunmap; |
|---|
| 474 | | - priv_mgr = rc; |
|---|
| 475 | | - |
|---|
| 476 | | - vaddr += sz; |
|---|
| 477 | | - paddr += sz; |
|---|
| 478 | | - size -= sz; |
|---|
| 479 | | - } |
|---|
| 512 | + vaddr += sz; |
|---|
| 513 | + paddr += sz; |
|---|
| 514 | + size -= sz; |
|---|
| 480 | 515 | |
|---|
| 481 | 516 | rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); |
|---|
| 482 | 517 | if (IS_ERR(rc)) |
|---|
| .. | .. |
|---|
| 519 | 554 | arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); |
|---|
| 520 | 555 | } |
|---|
| 521 | 556 | |
|---|
| 522 | | -static optee_invoke_fn *get_invoke_func(struct device_node *np) |
|---|
| 557 | +static optee_invoke_fn *get_invoke_func(struct device *dev) |
|---|
| 523 | 558 | { |
|---|
| 524 | 559 | const char *method; |
|---|
| 525 | 560 | |
|---|
| 526 | | - pr_info("probing for conduit method from DT.\n"); |
|---|
| 561 | + pr_info("probing for conduit method.\n"); |
|---|
| 527 | 562 | |
|---|
| 528 | | - if (of_property_read_string(np, "method", &method)) { |
|---|
| 563 | + if (device_property_read_string(dev, "method", &method)) { |
|---|
| 529 | 564 | pr_warn("missing \"method\" property\n"); |
|---|
| 530 | 565 | return ERR_PTR(-ENXIO); |
|---|
| 531 | 566 | } |
|---|
| .. | .. |
|---|
| 539 | 574 | return ERR_PTR(-EINVAL); |
|---|
| 540 | 575 | } |
|---|
| 541 | 576 | |
|---|
| 542 | | -static struct optee *optee_probe(struct device_node *np) |
|---|
| 577 | +/* optee_remove - Device Removal Routine |
|---|
| 578 | + * @pdev: platform device information struct |
|---|
| 579 | + * |
|---|
| 580 | + * optee_remove is called by platform subsystem to alert the driver |
|---|
| 581 | + * that it should release the device |
|---|
| 582 | + */ |
|---|
| 583 | + |
|---|
| 584 | +static int optee_remove(struct platform_device *pdev) |
|---|
| 585 | +{ |
|---|
| 586 | + struct optee *optee = platform_get_drvdata(pdev); |
|---|
| 587 | + |
|---|
| 588 | + /* Unregister OP-TEE specific client devices on TEE bus */ |
|---|
| 589 | + optee_unregister_devices(); |
|---|
| 590 | + |
|---|
| 591 | + teedev_close_context(optee->ctx); |
|---|
| 592 | + /* |
|---|
| 593 | + * Ask OP-TEE to free all cached shared memory objects to decrease |
|---|
| 594 | + * reference counters and also avoid wild pointers in secure world |
|---|
| 595 | + * into the old shared memory range. |
|---|
| 596 | + */ |
|---|
| 597 | + optee_disable_shm_cache(optee); |
|---|
| 598 | + |
|---|
| 599 | + /* |
|---|
| 600 | + * The two devices have to be unregistered before we can free the |
|---|
| 601 | + * other resources. |
|---|
| 602 | + */ |
|---|
| 603 | + tee_device_unregister(optee->supp_teedev); |
|---|
| 604 | + tee_device_unregister(optee->teedev); |
|---|
| 605 | + |
|---|
| 606 | + tee_shm_pool_free(optee->pool); |
|---|
| 607 | + if (optee->memremaped_shm) |
|---|
| 608 | + memunmap(optee->memremaped_shm); |
|---|
| 609 | + optee_wait_queue_exit(&optee->wait_queue); |
|---|
| 610 | + optee_supp_uninit(&optee->supp); |
|---|
| 611 | + mutex_destroy(&optee->call_queue.mutex); |
|---|
| 612 | + |
|---|
| 613 | + kfree(optee); |
|---|
| 614 | + |
|---|
| 615 | + return 0; |
|---|
| 616 | +} |
|---|
| 617 | + |
|---|
| 618 | +/* optee_shutdown - Device Removal Routine |
|---|
| 619 | + * @pdev: platform device information struct |
|---|
| 620 | + * |
|---|
| 621 | + * platform_shutdown is called by the platform subsystem to alert |
|---|
| 622 | + * the driver that a shutdown, reboot, or kexec is happening and |
|---|
| 623 | + * device must be disabled. |
|---|
| 624 | + */ |
|---|
| 625 | +static void optee_shutdown(struct platform_device *pdev) |
|---|
| 626 | +{ |
|---|
| 627 | + optee_disable_shm_cache(platform_get_drvdata(pdev)); |
|---|
| 628 | +} |
|---|
| 629 | + |
|---|
| 630 | +static int optee_probe(struct platform_device *pdev) |
|---|
| 543 | 631 | { |
|---|
| 544 | 632 | optee_invoke_fn *invoke_fn; |
|---|
| 545 | | - struct tee_shm_pool *pool; |
|---|
| 633 | + struct tee_shm_pool *pool = ERR_PTR(-EINVAL); |
|---|
| 546 | 634 | struct optee *optee = NULL; |
|---|
| 547 | 635 | void *memremaped_shm = NULL; |
|---|
| 548 | 636 | struct tee_device *teedev; |
|---|
| 637 | + struct tee_context *ctx; |
|---|
| 549 | 638 | u32 sec_caps; |
|---|
| 550 | 639 | int rc; |
|---|
| 551 | 640 | |
|---|
| 552 | | - invoke_fn = get_invoke_func(np); |
|---|
| 641 | + /* |
|---|
| 642 | + * The kernel may have crashed at the same time that all available |
|---|
| 643 | + * secure world threads were suspended and we cannot reschedule the |
|---|
| 644 | + * suspended threads without access to the crashed kernel's wait_queue. |
|---|
| 645 | + * Therefore, we cannot reliably initialize the OP-TEE driver in the |
|---|
| 646 | + * kdump kernel. |
|---|
| 647 | + */ |
|---|
| 648 | + if (is_kdump_kernel()) |
|---|
| 649 | + return -ENODEV; |
|---|
| 650 | + |
|---|
| 651 | + invoke_fn = get_invoke_func(&pdev->dev); |
|---|
| 553 | 652 | if (IS_ERR(invoke_fn)) |
|---|
| 554 | | - return (void *)invoke_fn; |
|---|
| 653 | + return PTR_ERR(invoke_fn); |
|---|
| 555 | 654 | |
|---|
| 556 | 655 | if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { |
|---|
| 557 | 656 | pr_warn("api uid mismatch\n"); |
|---|
| 558 | | - return ERR_PTR(-EINVAL); |
|---|
| 657 | + return -EINVAL; |
|---|
| 559 | 658 | } |
|---|
| 560 | 659 | |
|---|
| 561 | 660 | optee_msg_get_os_revision(invoke_fn); |
|---|
| 562 | 661 | |
|---|
| 563 | 662 | if (!optee_msg_api_revision_is_compatible(invoke_fn)) { |
|---|
| 564 | 663 | pr_warn("api revision mismatch\n"); |
|---|
| 565 | | - return ERR_PTR(-EINVAL); |
|---|
| 664 | + return -EINVAL; |
|---|
| 566 | 665 | } |
|---|
| 567 | 666 | |
|---|
| 568 | 667 | if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { |
|---|
| 569 | 668 | pr_warn("capabilities mismatch\n"); |
|---|
| 570 | | - return ERR_PTR(-EINVAL); |
|---|
| 669 | + return -EINVAL; |
|---|
| 571 | 670 | } |
|---|
| 572 | 671 | |
|---|
| 573 | 672 | /* |
|---|
| 574 | | - * We have no other option for shared memory, if secure world |
|---|
| 575 | | - * doesn't have any reserved memory we can use we can't continue. |
|---|
| 673 | + * Try to use dynamic shared memory if possible |
|---|
| 576 | 674 | */ |
|---|
| 577 | | - if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) |
|---|
| 578 | | - return ERR_PTR(-EINVAL); |
|---|
| 675 | + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) |
|---|
| 676 | + pool = optee_config_dyn_shm(); |
|---|
| 579 | 677 | |
|---|
| 580 | | - pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps); |
|---|
| 678 | + /* |
|---|
| 679 | + * If dynamic shared memory is not available or failed - try static one |
|---|
| 680 | + */ |
|---|
| 681 | + if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) |
|---|
| 682 | + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); |
|---|
| 683 | + |
|---|
| 581 | 684 | if (IS_ERR(pool)) |
|---|
| 582 | | - return (void *)pool; |
|---|
| 685 | + return PTR_ERR(pool); |
|---|
| 583 | 686 | |
|---|
| 584 | 687 | optee = kzalloc(sizeof(*optee), GFP_KERNEL); |
|---|
| 585 | 688 | if (!optee) { |
|---|
| .. | .. |
|---|
| 618 | 721 | optee_supp_init(&optee->supp); |
|---|
| 619 | 722 | optee->memremaped_shm = memremaped_shm; |
|---|
| 620 | 723 | optee->pool = pool; |
|---|
| 724 | + ctx = teedev_open(optee->teedev); |
|---|
| 725 | + if (IS_ERR(ctx)) { |
|---|
| 726 | + rc = PTR_ERR(ctx); |
|---|
| 727 | + goto err; |
|---|
| 728 | + } |
|---|
| 729 | + optee->ctx = ctx; |
|---|
| 621 | 730 | |
|---|
| 622 | 731 | /* |
|---|
| 623 | 732 | * Ensure that there are no pre-existing shm objects before enabling |
|---|
| .. | .. |
|---|
| 630 | 739 | |
|---|
| 631 | 740 | optee_enable_shm_cache(optee); |
|---|
| 632 | 741 | |
|---|
| 742 | + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) |
|---|
| 743 | + pr_info("dynamic shared memory is enabled\n"); |
|---|
| 744 | + |
|---|
| 745 | + platform_set_drvdata(pdev, optee); |
|---|
| 746 | + |
|---|
| 747 | + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); |
|---|
| 748 | + if (rc) { |
|---|
| 749 | + optee_remove(pdev); |
|---|
| 750 | + return rc; |
|---|
| 751 | + } |
|---|
| 752 | + |
|---|
| 633 | 753 | pr_info("initialized driver\n"); |
|---|
| 634 | | - return optee; |
|---|
| 754 | + return 0; |
|---|
| 635 | 755 | err: |
|---|
| 636 | 756 | if (optee) { |
|---|
| 637 | 757 | /* |
|---|
| .. | .. |
|---|
| 647 | 767 | tee_shm_pool_free(pool); |
|---|
| 648 | 768 | if (memremaped_shm) |
|---|
| 649 | 769 | memunmap(memremaped_shm); |
|---|
| 650 | | - return ERR_PTR(rc); |
|---|
| 770 | + return rc; |
|---|
| 651 | 771 | } |
|---|
| 652 | 772 | |
|---|
| 653 | | -static void optee_remove(struct optee *optee) |
|---|
| 654 | | -{ |
|---|
| 655 | | - /* |
|---|
| 656 | | - * Ask OP-TEE to free all cached shared memory objects to decrease |
|---|
| 657 | | - * reference counters and also avoid wild pointers in secure world |
|---|
| 658 | | - * into the old shared memory range. |
|---|
| 659 | | - */ |
|---|
| 660 | | - optee_disable_shm_cache(optee); |
|---|
| 661 | | - |
|---|
| 662 | | - /* |
|---|
| 663 | | - * The two devices has to be unregistered before we can free the |
|---|
| 664 | | - * other resources. |
|---|
| 665 | | - */ |
|---|
| 666 | | - tee_device_unregister(optee->supp_teedev); |
|---|
| 667 | | - tee_device_unregister(optee->teedev); |
|---|
| 668 | | - |
|---|
| 669 | | - tee_shm_pool_free(optee->pool); |
|---|
| 670 | | - if (optee->memremaped_shm) |
|---|
| 671 | | - memunmap(optee->memremaped_shm); |
|---|
| 672 | | - optee_wait_queue_exit(&optee->wait_queue); |
|---|
| 673 | | - optee_supp_uninit(&optee->supp); |
|---|
| 674 | | - mutex_destroy(&optee->call_queue.mutex); |
|---|
| 675 | | - |
|---|
| 676 | | - kfree(optee); |
|---|
| 677 | | -} |
|---|
| 678 | | - |
|---|
| 679 | | -static const struct of_device_id optee_match[] = { |
|---|
| 773 | +static const struct of_device_id optee_dt_match[] = { |
|---|
| 680 | 774 | { .compatible = "linaro,optee-tz" }, |
|---|
| 681 | 775 | {}, |
|---|
| 682 | 776 | }; |
|---|
| 777 | +MODULE_DEVICE_TABLE(of, optee_dt_match); |
|---|
| 683 | 778 | |
|---|
| 684 | | -static struct optee *optee_svc; |
|---|
| 685 | | - |
|---|
| 686 | | -static int __init optee_driver_init(void) |
|---|
| 687 | | -{ |
|---|
| 688 | | - struct device_node *fw_np; |
|---|
| 689 | | - struct device_node *np; |
|---|
| 690 | | - struct optee *optee; |
|---|
| 691 | | - |
|---|
| 692 | | - /* Node is supposed to be below /firmware */ |
|---|
| 693 | | - fw_np = of_find_node_by_name(NULL, "firmware"); |
|---|
| 694 | | - if (!fw_np) |
|---|
| 695 | | - return -ENODEV; |
|---|
| 696 | | - |
|---|
| 697 | | - np = of_find_matching_node(fw_np, optee_match); |
|---|
| 698 | | - if (!np || !of_device_is_available(np)) { |
|---|
| 699 | | - of_node_put(np); |
|---|
| 700 | | - return -ENODEV; |
|---|
| 701 | | - } |
|---|
| 702 | | - |
|---|
| 703 | | - optee = optee_probe(np); |
|---|
| 704 | | - of_node_put(np); |
|---|
| 705 | | - |
|---|
| 706 | | - if (IS_ERR(optee)) |
|---|
| 707 | | - return PTR_ERR(optee); |
|---|
| 708 | | - |
|---|
| 709 | | - optee_svc = optee; |
|---|
| 710 | | - |
|---|
| 711 | | - return 0; |
|---|
| 712 | | -} |
|---|
| 713 | | -module_init(optee_driver_init); |
|---|
| 714 | | - |
|---|
| 715 | | -static void __exit optee_driver_exit(void) |
|---|
| 716 | | -{ |
|---|
| 717 | | - struct optee *optee = optee_svc; |
|---|
| 718 | | - |
|---|
| 719 | | - optee_svc = NULL; |
|---|
| 720 | | - if (optee) |
|---|
| 721 | | - optee_remove(optee); |
|---|
| 722 | | -} |
|---|
| 723 | | -module_exit(optee_driver_exit); |
|---|
| 779 | +static struct platform_driver optee_driver = { |
|---|
| 780 | + .probe = optee_probe, |
|---|
| 781 | + .remove = optee_remove, |
|---|
| 782 | + .shutdown = optee_shutdown, |
|---|
| 783 | + .driver = { |
|---|
| 784 | + .name = "optee", |
|---|
| 785 | + .of_match_table = optee_dt_match, |
|---|
| 786 | + }, |
|---|
| 787 | +}; |
|---|
| 788 | +module_platform_driver(optee_driver); |
|---|
| 724 | 789 | |
|---|
| 725 | 790 | MODULE_AUTHOR("Linaro"); |
|---|
| 726 | 791 | MODULE_DESCRIPTION("OP-TEE driver"); |
|---|
| 727 | 792 | MODULE_SUPPORTED_DEVICE(""); |
|---|
| 728 | 793 | MODULE_VERSION("1.0"); |
|---|
| 729 | 794 | MODULE_LICENSE("GPL v2"); |
|---|
| 795 | +MODULE_ALIAS("platform:optee"); |
|---|