// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2022 MediaTek Inc. */ /** * @file mkt_et.c * @brief Driver for et * */ #define __MTK_ET_C__ /* *============================================================= * Include files *============================================================= */ /* system includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __KERNEL__ #include #include #include "mtk_et.h" #endif #if IS_ENABLED(CONFIG_OF) #include #include #include #include #include #include #endif /************************************************ * Debug print ************************************************/ //#define ET_DEBUG #define ET_TAG "[ET]" #define et_err(fmt, args...) \ pr_info(ET_TAG"[ERROR][%s():%d]" fmt, __func__, __LINE__, ##args) #define et_msg(fmt, args...) \ pr_info(ET_TAG"[INFO][%s():%d]" fmt, __func__, __LINE__, ##args) #ifdef ET_DEBUG #define et_debug(fmt, args...) \ pr_debug(ET_TAG"[DEBUG][%s():%d]" fmt, __func__, __LINE__, ##args) #else #define et_debug(fmt, args...) #endif /************************************************ * SMC between kernel and atf ************************************************/ unsigned int et_smc_handle( unsigned int feature, unsigned int x2, unsigned int x3, unsigned int x4) { unsigned int ret = 0; #if defined(_MTK_SECURE_API_H_) /* update atf via smc */ ret = mt_secure_call(MTK_SIP_KERNEL_PTP3_CONTROL, feature, x2, x3, x4); #elif defined(__LINUX_ARM_SMCCC_H) /* update TFA via smc call */ struct arm_smccc_res res; arm_smccc_smc(MTK_SIP_KERNEL_PTP3_CONTROL, feature, x2, x3, x4, 0, 0, 0, &res); if (res.a0 == -1) ret = -1; else ret = (unsigned int)res.a0; #else ret = 0; #endif return ret; } /************************************************ * static function ************************************************/ /* ET_EN procfs interface */ static ssize_t et_en_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { int enable = 0; char *buf = (char *) __get_free_page(GFP_USER); if (!buf) return -ENOMEM; if (count >= PAGE_SIZE) goto out; if (copy_from_user(buf, buffer, count)) goto out; buf[count] = '\0'; if (kstrtoint(buf, 10, &enable)) { et_debug("bad argument!!\n"); goto out; } enable = enable ? 1 : 0; et_smc_handle(PTP3_FEATURE_ET, ET_W_EN, enable, 0); out: free_page((unsigned long)buf); return count; } static int et_en_proc_show(struct seq_file *m, void *v) { int status = 0; status = et_smc_handle(PTP3_FEATURE_ET, ET_R_EN, 0, 0); seq_printf(m, "ET_ENABLE = 0x%x\n", status); return 0; } /* ET_CFG procfs interface */ static ssize_t et_cfg_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { int index = 0, value = 0; char *buf = (char *) __get_free_page(GFP_USER); if (!buf) return -ENOMEM; if (count >= PAGE_SIZE) goto out; if (copy_from_user(buf, buffer, count)) goto out; buf[count] = '\0'; if (sscanf(buf, "%u %u", &index, &value) != 2) { et_debug("bad argument!! Should input 2 arguments.\n"); goto out; } if ((index < 0) || (index >= ET_INDEX_NUM)) { et_debug("index out of range!!\n"); goto out; } et_smc_handle(PTP3_FEATURE_ET, ET_W_CFG, index, value); out: free_page((unsigned long)buf); return count; } static int et_cfg_proc_show(struct seq_file *m, void *v) { int status = 0; unsigned int index = 0; for (index = 0; index <= ET_INDEX_NUM; index++) { status = et_smc_handle(PTP3_FEATURE_ET, ET_R_CFG, index, 0); seq_printf(m, "ET_index_%d = 0x%x\n", index, status); } return 0; } static int et_state_proc_show(struct seq_file *m, void *v) { int status = 0; unsigned int index = 0; for (index = 0; index < ET_NUM; index++) { status = et_smc_handle(PTP3_FEATURE_ET, ET_R_STATE, index, 0); seq_printf(m, "ET_state_%d = 0x%x\n", index, status); } return 0; } /************************************************ * Kernel driver nodes ************************************************/ PROC_FOPS_RW(et_en); PROC_FOPS_RW(et_cfg); PROC_FOPS_RO(et_state); static int create_procfs(void) { int i; const char *proc_name = "et"; struct proc_dir_entry *dir = NULL; struct pentry { const char *name; const struct proc_ops *fops; }; struct pentry et_entries[] = { PROC_ENTRY(et_en), PROC_ENTRY(et_cfg), PROC_ENTRY(et_state), }; /* create proc dir */ dir = proc_mkdir(proc_name, NULL); if (!dir) { et_err( "[%s]: mkdir /proc/%s failed\n", __func__, proc_name); return -1; } for (i = 0; i < ARRAY_SIZE(et_entries); i++) { if (!proc_create(et_entries[i].name, 0664, dir, et_entries[i].fops)) { et_debug("[%s]: create /proc/%s/iglre/%s failed\n", __func__, proc_name, et_entries[i].name); return -3; } } return 0; } static int et_probe(struct platform_device *pdev) { struct device_node *node = NULL; node = pdev->dev.of_node; if (!node) { et_debug("get et device node err\n"); return -ENODEV; } return 0; } static int et_suspend(struct platform_device *pdev, pm_message_t state) { return 0; } static int et_resume(struct platform_device *pdev) { return 0; } #if IS_ENABLED(CONFIG_OF) static const struct of_device_id et_of_match[] = { { .compatible = "mediatek,et", }, {}, }; #endif static struct platform_driver et_driver = { .remove = NULL, .shutdown = NULL, .probe = et_probe, .suspend = et_suspend, .resume = et_resume, .driver = { .name = "et", #if IS_ENABLED(CONFIG_OF) .of_match_table = et_of_match, #endif }, }; static int __init __et_init(void) { int err = 0; create_procfs(); err = platform_driver_register(&et_driver); if (err) { et_err("et driver callback register failed..\n"); return err; } return 0; } static void __exit __et_exit(void) { et_msg("et de-initialization\n"); } late_initcall(__et_init); module_exit(__et_exit); MODULE_DESCRIPTION("MediaTek et Driver v0.1"); MODULE_LICENSE("GPL"); #undef __MTK_ET_C__