// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 MediaTek Inc. */ #include #include #include #include #include "user.h" #ifndef TEEPERF_DEVICE_PROPNAME #define TEEPERF_DEVICE_PROPNAME "mediatek,teeperf" #endif u32 cpu_type; u32 cpu_map; static struct { dev_t device; struct class *class; dev_t user_dev; struct cdev user_cdev; } main_ctx; static inline int teeperf_device_common_init(void) { int ret; ret = alloc_chrdev_region(&main_ctx.device, 0, 1, "teeperf"); if (ret) { pr_info(PFX "alloc_chrdev_region failed, ret %d\n", ret); return ret; } main_ctx.class = class_create(THIS_MODULE, "teeperf"); if (IS_ERR(main_ctx.class)) { ret = PTR_ERR(main_ctx.class); pr_info(PFX "class_create failed, ret %d\n", ret); unregister_chrdev_region(main_ctx.device, 1); return ret; } return 0; } static inline void teeperf_device_common_exit(void) { class_destroy(main_ctx.class); unregister_chrdev_region(main_ctx.device, 1); } static inline int teeperf_device_user_init(void) { struct device *dev; int ret = 0; main_ctx.user_dev = MKDEV(MAJOR(main_ctx.device), 1); /* Create the user node */ teeperf_user_init(&main_ctx.user_cdev); ret = cdev_add(&main_ctx.user_cdev, main_ctx.user_dev, 1); if (ret) { pr_info(PFX "user cdev_add failed, ret %d\n", ret); return ret; } main_ctx.user_cdev.owner = THIS_MODULE; dev = device_create(main_ctx.class, NULL, main_ctx.user_dev, NULL, TEEPERF_DEVNODE); if (IS_ERR(dev)) { ret = PTR_ERR(dev); cdev_del(&main_ctx.user_cdev); pr_info(PFX "user device_create failed, ret %d\n", ret); return ret; } return 0; } static inline void teeperf_device_user_exit(void) { device_destroy(main_ctx.class, main_ctx.user_dev); cdev_del(&main_ctx.user_cdev); } static int teeperf_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; int ret = 0; if (IS_ERR(node)) { pr_info(PFX "cannot find device node\n"); return -ENODEV; } ret = of_property_read_u32(node, "cpu-type", &cpu_type); if (ret || !cpu_type) { pr_info(PFX "invalid cpu type\n"); return -EINVAL; } ret = of_property_read_u32(node, "cpu-map", &cpu_map); if (ret || !cpu_map) { pr_info(PFX "invalid cpu map\n"); return -EINVAL; } ret = teeperf_device_common_init(); if (ret) goto err_common; ret = teeperf_device_user_init(); if (ret) goto err_user; return 0; err_user: teeperf_device_common_exit(); err_common: return ret; } static const struct of_device_id of_match_table[] = { { .compatible = TEEPERF_DEVICE_PROPNAME }, { } }; static struct platform_driver teeperf_plat_driver = { .probe = teeperf_probe, .driver = { .name = "teeperf", .owner = THIS_MODULE, .of_match_table = of_match_table, } }; static int __init teeperf_init(void) { return platform_driver_register(&teeperf_plat_driver); } static void __exit teeperf_exit(void) { teeperf_device_user_exit(); teeperf_device_common_exit(); platform_driver_unregister(&teeperf_plat_driver); } module_init(teeperf_init); module_exit(teeperf_exit); MODULE_AUTHOR("Jackson Chang "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TEE perf driver");