/* * mods_krnl.c - This file is part of NVIDIA MODS kernel driver. * * Copyright (c) 2008-2018, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * NVIDIA MODS kernel driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NVIDIA MODS kernel driver. * If not, see . */ #include "mods_internal.h" #include #include #include #include #include #include #include #include #include #ifdef MODS_HAS_CONSOLE_LOCK # include # include # include # include # include #endif /*********************************************************************** * mods_krnl_* functions, driver interfaces called by the Linux kernel * ***********************************************************************/ static int mods_krnl_open(struct inode *, struct file *); static int mods_krnl_close(struct inode *, struct file *); static unsigned int mods_krnl_poll(struct file *, poll_table *); static int mods_krnl_mmap(struct file *, struct vm_area_struct *); static long mods_krnl_ioctl(struct file *, unsigned int, unsigned long); #ifdef MODS_HAS_SRIOV static int mods_pci_sriov_configure(struct pci_dev *dev, int numvfs); #endif /* character driver entry points */ static const struct file_operations mods_fops = { .owner = THIS_MODULE, .open = mods_krnl_open, .release = mods_krnl_close, .poll = mods_krnl_poll, .mmap = mods_krnl_mmap, .unlocked_ioctl = mods_krnl_ioctl, #if defined(HAVE_COMPAT_IOCTL) .compat_ioctl = mods_krnl_ioctl, #endif }; #define DEVICE_NAME "mods" struct miscdevice mods_dev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &mods_fops }; #if defined(MODS_CAN_REGISTER_PCI_DEV) static pci_ers_result_t mods_pci_error_detected(struct pci_dev *, enum pci_channel_state); static pci_ers_result_t mods_pci_mmio_enabled(struct pci_dev *); static void mods_pci_resume(struct pci_dev *); struct pci_error_handlers mods_pci_error_handlers = { .error_detected = mods_pci_error_detected, .mmio_enabled = mods_pci_mmio_enabled, .resume = mods_pci_resume, }; static const struct pci_device_id mods_pci_table[] = { { .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = (PCI_CLASS_DISPLAY_VGA << 8), .class_mask = ~0 }, { .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = (PCI_CLASS_DISPLAY_3D << 8), .class_mask = ~0 }, { } }; static int mods_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { mods_debug_printk(DEBUG_PCI, "probed vendor %x device %x devfn %x\n", dev->vendor, dev->device, dev->devfn); return 0; } struct pci_driver mods_pci_driver = { .name = DEVICE_NAME, .id_table = mods_pci_table, .probe = mods_pci_probe, .err_handler = &mods_pci_error_handlers, #ifdef MODS_HAS_SRIOV .sriov_configure = mods_pci_sriov_configure, #endif }; #endif /*********************************************** * module wide parameters and access functions * * used to avoid globalization of variables * ***********************************************/ static int debug; static int multi_instance = MODS_MULTI_INSTANCE_DEFAULT_VALUE; static u32 access_token = MODS_ACCESS_TOKEN_NONE; #ifdef MODS_HAS_SRIOV static int mods_pci_sriov_configure(struct pci_dev *dev, int numvfs) { int rv = 0; LOG_ENT(); mods_debug_printk(DEBUG_PCI, "(numvfs=%d, totalvfs=%d, dev->is_physfn=%d)\n", numvfs, pci_sriov_get_totalvfs(dev), dev->is_physfn); if (numvfs > 0) { rv = pci_enable_sriov(dev, numvfs); if (rv) { mods_error_printk( "pci_enable_sriov failed error %d\n", rv); return rv; } rv = numvfs; } else { pci_disable_sriov(dev); rv = 0; } LOG_EXT(); return rv; } static int esc_mods_set_num_vf(struct file *pfile, struct MODS_SET_NUM_VF *p) { int rv = 0; struct pci_dev *dev; unsigned int devfn; LOG_ENT(); /* Get the PCI device structure for the specified device from kernel */ devfn = PCI_DEVFN(p->dev.device, p->dev.function); dev = MODS_PCI_GET_SLOT(p->dev.domain, p->dev.bus, devfn); if (!dev) { mods_error_printk( "unknown dev %04x:%x:%02x.%x\n", (unsigned int)p->dev.domain, (unsigned int)p->dev.bus, (unsigned int)p->dev.device, (unsigned int)p->dev.function); LOG_EXT(); return -EINVAL; } rv = mods_pci_sriov_configure(dev, p->numvfs); LOG_EXT(); return rv; } static int esc_mods_set_total_vf(struct file *pfile, struct MODS_SET_NUM_VF *p) { int rv = 0; struct pci_dev *dev; unsigned int devfn; LOG_ENT(); mods_debug_printk(DEBUG_PCI, "pci_sriov_set_totalvfs(totalvfs=%d)\n", p->numvfs); /* Get the PCI device structure for the specified device from kernel */ devfn = PCI_DEVFN(p->dev.device, p->dev.function); dev = MODS_PCI_GET_SLOT(p->dev.domain, p->dev.bus, devfn); if (!dev) { mods_error_printk( "unknown dev %04x:%x:%02x.%x\n", (unsigned int)p->dev.domain, (unsigned int)p->dev.bus, (unsigned int)p->dev.device, (unsigned int)p->dev.function); LOG_EXT(); return -EINVAL; } rv = pci_sriov_set_totalvfs(dev, p->numvfs); if (rv) { mods_error_printk( "pci_sriov_set_totalvfs failed error %d\n", rv); return rv; } LOG_EXT(); return rv; } #endif #if defined(CONFIG_PPC64) static int ppc_tce_bypass = MODS_PPC_TCE_BYPASS_ON; void mods_set_ppc_tce_bypass(int bypass) { ppc_tce_bypass = bypass; } int mods_get_ppc_tce_bypass(void) { return ppc_tce_bypass; } #endif void mods_set_debug_level(int mask) { debug = mask; } int mods_get_debug_level(void) { return debug; } int mods_check_debug_level(int mask) { return ((debug & mask) == mask) ? 1 : 0; } void mods_set_multi_instance(int mi) { multi_instance = (mi > 0) ? 1 : -1; } int mods_get_multi_instance(void) { return multi_instance > 0; } u32 mods_get_access_token(void) { return access_token; } static int mods_set_access_token(u32 tok) { /* When setting a null token, the existing token must match the * provided token, when setting a non-null token the existing token * must be null, use atomic compare/exchange to set it */ u32 req_old_token = (tok == MODS_ACCESS_TOKEN_NONE) ? access_token : MODS_ACCESS_TOKEN_NONE; if (cmpxchg(&access_token, req_old_token, tok) != req_old_token) return -EFAULT; return OK; } static int mods_check_access_token(struct file *fp) { struct mods_client *client = fp->private_data; if (client->access_token != mods_get_access_token()) return -EFAULT; return OK; } /****************************** * INIT/EXIT MODULE FUNCTIONS * ******************************/ static int __init mods_init_module(void) { int rc; LOG_ENT(); mods_init_irq(); rc = misc_register(&mods_dev); if (rc < 0) return -EBUSY; #if defined(MODS_CAN_REGISTER_PCI_DEV) rc = pci_register_driver(&mods_pci_driver); if (rc < 0) return -EBUSY; #endif #if defined(MODS_HAS_CLOCK) mods_init_clock_api(); #endif rc = mods_create_debugfs(&mods_dev); if (rc < 0) return rc; rc = mods_init_dmabuf(); if (rc < 0) return rc; #if defined(MODS_TEGRA) /* tegra prod */ mods_tegra_prod_init(&mods_dev); #endif mods_info_printk("*** WARNING: DIAGNOSTIC DRIVER LOADED ***\n"); mods_info_printk("driver loaded, version %x.%02x\n", (MODS_DRIVER_VERSION>>8), (MODS_DRIVER_VERSION&0xFF)); if (debug) mods_info_printk("debug level 0x%x\n", debug); LOG_EXT(); return OK; } static void __exit mods_exit_module(void) { LOG_ENT(); mods_exit_dmabuf(); mods_remove_debugfs(); mods_cleanup_irq(); #if defined(MODS_CAN_REGISTER_PCI_DEV) pci_unregister_driver(&mods_pci_driver); #endif misc_deregister(&mods_dev); #if defined(MODS_HAS_CLOCK) mods_shutdown_clock_api(); #endif mods_info_printk("driver unloaded\n"); LOG_EXT(); } /*************************** * KERNEL INTERFACE SET UP * ***************************/ module_init(mods_init_module); module_exit(mods_exit_module); MODULE_LICENSE("GPL"); #define STRING_VALUE(x) #x #define MAKE_MODULE_VERSION(x, y) STRING_VALUE(x) "." STRING_VALUE(y) MODULE_VERSION(MAKE_MODULE_VERSION(MODS_DRIVER_VERSION_MAJOR, MODS_DRIVER_VERSION_MINOR)); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug bitflags (2=ioctl 4=pci 8=acpi 16=irq 32=mem 64=fun +256=detailed)"); module_param(multi_instance, int, 0644); MODULE_PARM_DESC(multi_instance, "allows more than one client to simultaneously open the driver"); #if defined(CONFIG_PPC64) module_param(ppc_tce_bypass, int, 0644); MODULE_PARM_DESC(ppc_tce_bypass, "PPC TCE bypass (0=sys default, 1=force bypass, 2=force non bypass)"); #endif /******************** * HELPER FUNCTIONS * ********************/ static void mods_disable_all_devices(struct mods_client *client) { #ifdef CONFIG_PCI if (unlikely(mutex_lock_interruptible(mods_get_irq_mutex()))) return; while (client->enabled_devices != 0) { struct en_dev_entry *old = client->enabled_devices; mods_disable_device(old->dev); client->enabled_devices = old->next; kfree(old); } mutex_unlock(mods_get_irq_mutex()); #else WARN_ON(client->enabled_devices != 0); #endif } static int mods_resume_console(struct file *pfile); /********************* * MAPPING FUNCTIONS * *********************/ static int mods_register_mapping( struct file *fp, struct MODS_MEM_INFO *p_mem_info, u64 dma_addr, u64 virtual_address, u64 mapping_length) { struct SYS_MAP_MEMORY *p_map_mem; struct mods_client *client = fp->private_data; LOG_ENT(); p_map_mem = kmalloc(sizeof(*p_map_mem), GFP_KERNEL | __GFP_NORETRY); if (unlikely(!p_map_mem)) { LOG_EXT(); return -ENOMEM; } memset(p_map_mem, 0, sizeof(*p_map_mem)); p_map_mem->dma_addr = dma_addr; p_map_mem->virtual_addr = virtual_address; p_map_mem->mapping_length = mapping_length; p_map_mem->p_mem_info = p_mem_info; list_add(&p_map_mem->list, &client->mem_map_list); mods_debug_printk(DEBUG_MEM_DETAILED, "map alloc %p as %p: phys 0x%llx, virt 0x%llx, size 0x%llx\n", p_mem_info, p_map_mem, dma_addr, virtual_address, mapping_length); LOG_EXT(); return OK; } static void mods_unregister_mapping(struct file *fp, u64 virtual_address) { struct SYS_MAP_MEMORY *p_map_mem; struct mods_client *client = fp->private_data; struct list_head *head = &client->mem_map_list; struct list_head *iter; LOG_ENT(); list_for_each(iter, head) { p_map_mem = list_entry(iter, struct SYS_MAP_MEMORY, list); if (p_map_mem->virtual_addr == virtual_address) { /* remove from the list */ list_del(iter); /* free our data struct which keeps track of mapping */ kfree(p_map_mem); return; } } LOG_EXT(); } static void mods_unregister_all_mappings(struct file *fp) { struct SYS_MAP_MEMORY *p_map_mem; struct mods_client *client = fp->private_data; struct list_head *head = &client->mem_map_list; struct list_head *iter; struct list_head *tmp; LOG_ENT(); list_for_each_safe(iter, tmp, head) { p_map_mem = list_entry(iter, struct SYS_MAP_MEMORY, list); mods_unregister_mapping(fp, p_map_mem->virtual_addr); } LOG_EXT(); } static pgprot_t mods_get_prot(u32 mem_type, pgprot_t prot) { switch (mem_type) { case MODS_MEMORY_CACHED: return prot; case MODS_MEMORY_UNCACHED: return MODS_PGPROT_UC(prot); case MODS_MEMORY_WRITECOMBINE: return MODS_PGPROT_WC(prot); default: mods_warning_printk("unsupported memory type: %u\n", mem_type); return prot; } } static pgprot_t mods_get_prot_for_range(struct file *fp, u64 dma_addr, u64 size, pgprot_t prot) { struct mods_client *client = fp->private_data; if ((dma_addr == client->mem_type.dma_addr) && (size == client->mem_type.size)) { return mods_get_prot(client->mem_type.type, prot); } return prot; } const char *mods_get_prot_str(u32 mem_type) { switch (mem_type) { case MODS_MEMORY_CACHED: return "WB"; case MODS_MEMORY_UNCACHED: return "UC"; case MODS_MEMORY_WRITECOMBINE: return "WC"; default: return "unknown"; } } static const char *mods_get_prot_str_for_range(struct file *fp, u64 dma_addr, u64 size) { struct mods_client *client = fp->private_data; if ((dma_addr == client->mem_type.dma_addr) && (size == client->mem_type.size)) { return mods_get_prot_str(client->mem_type.type); } return "default"; } /******************** * PCI ERROR FUNCTIONS * ********************/ #if defined(MODS_CAN_REGISTER_PCI_DEV) static pci_ers_result_t mods_pci_error_detected(struct pci_dev *dev, enum pci_channel_state state) { mods_debug_printk(DEBUG_PCI, "pci_error_detected %04x:%x:%02x.%x\n", pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); return PCI_ERS_RESULT_CAN_RECOVER; } static pci_ers_result_t mods_pci_mmio_enabled(struct pci_dev *dev) { mods_debug_printk(DEBUG_PCI, "pci_mmio_enabled %04x:%x:%02x.%x\n", pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); return PCI_ERS_RESULT_NEED_RESET; } static void mods_pci_resume(struct pci_dev *dev) { mods_debug_printk(DEBUG_PCI, "pci_resume %04x:%x:%02x.%x\n", pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); } #endif /******************** * KERNEL FUNCTIONS * ********************/ static void mods_krnl_vma_open(struct vm_area_struct *vma) { struct mods_vm_private_data *vma_private_data; LOG_ENT(); mods_debug_printk(DEBUG_MEM_DETAILED, "open vma, virt 0x%lx, phys 0x%llx\n", vma->vm_start, (u64)(MODS_VMA_PGOFF(vma) << PAGE_SHIFT)); if (MODS_VMA_PRIVATE(vma)) { vma_private_data = MODS_VMA_PRIVATE(vma); atomic_inc(&vma_private_data->usage_count); } LOG_EXT(); } static void mods_krnl_vma_close(struct vm_area_struct *vma) { LOG_ENT(); if (MODS_VMA_PRIVATE(vma)) { struct mods_vm_private_data *vma_private_data = MODS_VMA_PRIVATE(vma); if (atomic_dec_and_test(&vma_private_data->usage_count)) { struct mods_client *client = vma_private_data->fp->private_data; if (unlikely(mutex_lock_interruptible( &client->mtx))) { LOG_EXT(); return; } /* we need to unregister the mapping */ mods_unregister_mapping(vma_private_data->fp, vma->vm_start); mods_debug_printk(DEBUG_MEM_DETAILED, "closed vma, virt 0x%lx\n", vma->vm_start); MODS_VMA_PRIVATE(vma) = NULL; kfree(vma_private_data); mutex_unlock(&client->mtx); } } LOG_EXT(); } static const struct vm_operations_struct mods_krnl_vm_ops = { .open = mods_krnl_vma_open, .close = mods_krnl_vma_close }; static int mods_krnl_open(struct inode *ip, struct file *fp) { struct mods_client *client; LOG_ENT(); client = mods_alloc_client(); if (client == NULL) { mods_error_printk("too many clients\n"); LOG_EXT(); return -EBUSY; } fp->private_data = client; mods_info_printk("driver opened\n"); LOG_EXT(); return OK; } static int mods_krnl_close(struct inode *ip, struct file *fp) { struct mods_client *client = fp->private_data; u8 client_id = client->client_id; int ret = OK; LOG_ENT(); WARN_ON(!is_client_id_valid(client_id)); if (!is_client_id_valid(client_id)) { LOG_EXT(); return -EINVAL; } mods_free_client_interrupts(client); mods_resume_console(fp); mods_unregister_all_mappings(fp); ret = mods_unregister_all_alloc(fp); if (ret) mods_error_printk("failed to free all memory\n"); #if defined(CONFIG_PPC64) ret = mods_unregister_all_ppc_tce_bypass(fp); if (ret) mods_error_printk("failed to restore dma bypass\n"); ret = mods_unregister_all_nvlink_sysmem_trained(fp); if (ret) mods_error_printk("failed to free nvlink trained\n"); #endif mods_disable_all_devices(client); mods_free_client(client_id); mods_info_printk("driver closed\n"); LOG_EXT(); return ret; } static unsigned int mods_krnl_poll(struct file *fp, poll_table *wait) { unsigned int mask = 0; struct mods_client *client = fp->private_data; u8 client_id = get_client_id(fp); int access_tok_ret = mods_check_access_token(fp); if (access_tok_ret < 0) return access_tok_ret; if (!(fp->f_flags & O_NONBLOCK)) { mods_debug_printk(DEBUG_ISR_DETAILED, "poll wait\n"); poll_wait(fp, &client->interrupt_event, wait); } /* if any interrupts pending then check intr, POLLIN on irq */ mask |= mods_irq_event_check(client_id); mods_debug_printk(DEBUG_ISR_DETAILED, "poll mask 0x%x\n", mask); return mask; } static int mods_krnl_map_inner(struct file *fp, struct vm_area_struct *vma); static int mods_krnl_mmap(struct file *fp, struct vm_area_struct *vma) { struct mods_vm_private_data *vma_private_data; int access_tok_ret; LOG_ENT(); access_tok_ret = mods_check_access_token(fp); if (access_tok_ret < 0) { LOG_EXT(); return access_tok_ret; } vma->vm_ops = &mods_krnl_vm_ops; vma_private_data = kmalloc(sizeof(*vma_private_data), GFP_KERNEL | __GFP_NORETRY); if (unlikely(!vma_private_data)) { LOG_EXT(); return -ENOMEM; } /* set private data for vm_area_struct */ atomic_set(&vma_private_data->usage_count, 0); vma_private_data->fp = fp; MODS_VMA_PRIVATE(vma) = vma_private_data; /* call for the first time open function */ mods_krnl_vma_open(vma); { int ret = OK; struct mods_client *client = fp->private_data; if (unlikely(mutex_lock_interruptible(&client->mtx))) ret = -EINTR; else { ret = mods_krnl_map_inner(fp, vma); mutex_unlock(&client->mtx); } LOG_EXT(); return ret; } } static int mods_krnl_map_inner(struct file *fp, struct vm_area_struct *vma) { u64 req_pa = MODS_VMA_OFFSET(vma); struct MODS_MEM_INFO *p_mem_info = mods_find_alloc(fp, req_pa); u32 req_pages = MODS_VMA_SIZE(vma) >> PAGE_SHIFT; if ((req_pa & ~PAGE_MASK) != 0 || (MODS_VMA_SIZE(vma) & ~PAGE_MASK) != 0) { mods_error_printk("requested mapping is not page-aligned\n"); return -EINVAL; } /* system memory */ if (p_mem_info) { u32 first, i; struct MODS_PHYS_CHUNK *pt = p_mem_info->pages; u32 have_pages = 0; unsigned long map_va = 0; const pgprot_t prot = mods_get_prot(p_mem_info->cache_type, vma->vm_page_prot); /* Find the beginning of the requested range */ for (first = 0; first < p_mem_info->max_chunks; first++) { u64 dma_addr; if (!pt[first].allocated) continue; dma_addr = pt[first].dma_addr; if ((req_pa >= dma_addr) && (req_pa < dma_addr + (PAGE_SIZE << pt->order))) { break; } } if (first == p_mem_info->max_chunks) { mods_error_printk("can't satisfy requested mapping\n"); return -EINVAL; } /* Count how many remaining pages we have in the allocation */ for (i = first; i < p_mem_info->max_chunks; i++) { if (!pt[i].allocated) break; if (i == first) { u64 aoffs = req_pa - pt[i].dma_addr; u32 skip_pages = aoffs >> PAGE_SHIFT; have_pages -= skip_pages; } have_pages += 1U << pt[i].order; } if (have_pages < req_pages) { mods_error_printk("requested mapping exceeds bounds\n"); return -EINVAL; } /* Map pages into VA space */ map_va = vma->vm_start; have_pages = req_pages; for (i = first; have_pages > 0; i++) { u64 map_pa = MODS_DMA_TO_PHYS(pt[i].dma_addr); u32 map_size = PAGE_SIZE << pt[i].order; u32 map_pages = 1U << pt[i].order; if (!pt[i].allocated) break; if (i == first) { u64 aoffs = req_pa - pt[i].dma_addr; map_pa += aoffs; map_size -= aoffs; map_pages -= aoffs >> PAGE_SHIFT; } if (map_pages > have_pages) { map_size = have_pages << PAGE_SHIFT; map_pages = have_pages; } mods_debug_printk(DEBUG_MEM_DETAILED, "remap va 0x%lx pfn 0x%x size 0x%x pages 0x%x\n", map_va, (unsigned int)(map_pa>>PAGE_SHIFT), map_size, map_pages); if (remap_pfn_range(vma, map_va, map_pa>>PAGE_SHIFT, map_size, prot)) { mods_error_printk("failed to map memory\n"); return -EAGAIN; } map_va += map_size; have_pages -= map_pages; } /* MODS_VMA_OFFSET(vma) can change so it can't be used * to register the mapping */ mods_register_mapping(fp, p_mem_info, pt[first].dma_addr, vma->vm_start, MODS_VMA_SIZE(vma)); } else { /* device memory */ mods_debug_printk(DEBUG_MEM, "map dev: phys 0x%llx, virt 0x%lx, size 0x%lx, %s\n", req_pa, (unsigned long)vma->vm_start, (unsigned long)MODS_VMA_SIZE(vma), mods_get_prot_str_for_range(fp, req_pa, MODS_VMA_SIZE(vma))); if (io_remap_pfn_range( vma, vma->vm_start, req_pa>>PAGE_SHIFT, MODS_VMA_SIZE(vma), mods_get_prot_for_range( fp, req_pa, MODS_VMA_SIZE(vma), vma->vm_page_prot))) { mods_error_printk("failed to map device memory\n"); return -EAGAIN; } mods_register_mapping(fp, NULL, req_pa, vma->vm_start, MODS_VMA_SIZE(vma)); } return OK; } #if !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) && !defined(CONFIG_PPC64) static int mods_get_screen_info(struct MODS_SCREEN_INFO *p) { p->orig_video_mode = screen_info.orig_video_mode; p->orig_video_is_vga = screen_info.orig_video_isVGA; p->lfb_width = screen_info.lfb_width; p->lfb_height = screen_info.lfb_height; p->lfb_depth = screen_info.lfb_depth; p->lfb_base = screen_info.lfb_base; p->lfb_size = screen_info.lfb_size; p->lfb_linelength = screen_info.lfb_linelength; return OK; } #endif /************************* * ESCAPE CALL FUNCTIONS * *************************/ static int esc_mods_get_api_version(struct file *pfile, struct MODS_GET_VERSION *p) { p->version = MODS_DRIVER_VERSION; return OK; } static int esc_mods_get_kernel_version(struct file *pfile, struct MODS_GET_VERSION *p) { p->version = MODS_KERNEL_VERSION; return OK; } static int esc_mods_set_driver_para(struct file *pfile, struct MODS_SET_PARA *p) { int rc = OK; return rc; } static int esc_mods_get_screen_info(struct file *pfile, struct MODS_SCREEN_INFO *p) { #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC64) return -EINVAL; #else int rc = mods_get_screen_info(p); #if defined(VIDEO_CAPABILITY_64BIT_BASE) if (screen_info.ext_lfb_base) return -EOVERFLOW; #endif return rc; #endif } static int esc_mods_get_screen_info_2(struct file *pfile, struct MODS_SCREEN_INFO_2 *p) { #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC64) return -EINVAL; #else int rc = mods_get_screen_info(&p->info); #if defined(VIDEO_CAPABILITY_64BIT_BASE) p->ext_lfb_base = screen_info.ext_lfb_base; #else p->ext_lfb_base = 0; #endif return rc; #endif } static int esc_mods_lock_console(struct file *pfile) { #if defined(MODS_HAS_CONSOLE_LOCK) console_lock(); return OK; #else return -EINVAL; #endif } static int esc_mods_unlock_console(struct file *pfile) { #if defined(MODS_HAS_CONSOLE_LOCK) console_unlock(); return OK; #else return -EINVAL; #endif } static int esc_mods_suspend_console(struct file *pfile) { int ret = -EINVAL; LOG_ENT(); #if defined(CONFIG_FB) && defined(MODS_HAS_CONSOLE_LOCK) if (num_registered_fb) { /* tell the os to block fb accesses */ struct mods_client *client = pfile->private_data; int i = 0; for (i = 0; i < num_registered_fb; i++) { console_lock(); if (registered_fb[i]->state != FBINFO_STATE_SUSPENDED) { fb_set_suspend(registered_fb[i], 1); client->mods_fb_suspended[i] = 1; } console_unlock(); } ret = OK; } #endif #if defined(MODS_HAS_CONSOLE_BINDING) && defined(MODS_HAS_CONSOLE_LOCK) if (&vga_con == vc_cons[fg_console].d->vc_sw) { /* if the current console is the vga console driver, * have the dummy driver take over. */ console_lock(); do_take_over_console(&dummy_con, 0, 0, 0); console_unlock(); ret = OK; } #endif LOG_EXT(); return ret; } static int esc_mods_resume_console(struct file *pfile) { return mods_resume_console(pfile); } static int mods_resume_console(struct file *pfile) { int ret = -EINVAL; LOG_ENT(); #if defined(CONFIG_FB) && defined(MODS_HAS_CONSOLE_LOCK) if (num_registered_fb) { struct mods_client *client = pfile->private_data; int i = 0; for (i = 0; i < num_registered_fb; i++) { console_lock(); if (client->mods_fb_suspended[i]) { fb_set_suspend(registered_fb[i], 0); client->mods_fb_suspended[i] = 0; } console_unlock(); } ret = OK; } #endif #if defined(MODS_HAS_CONSOLE_BINDING) && defined(MODS_HAS_CONSOLE_LOCK) if (&dummy_con == vc_cons[fg_console].d->vc_sw) { /* try to unbind the dummy driver, * the system driver should take over. */ console_lock(); do_unbind_con_driver(vc_cons[fg_console].d->vc_sw, 0, 0, 0); console_unlock(); ret = OK; } #endif LOG_EXT(); return ret; } static int esc_mods_acquire_access_token(struct file *pfile, struct MODS_ACCESS_TOKEN *ptoken) { int ret = -EINVAL; LOG_ENT(); if (mods_get_multi_instance()) { LOG_EXT(); mods_error_printk( "access token ops not supported with multi_instance=1!\n"); return ret; } get_random_bytes(&ptoken->token, sizeof(ptoken->token)); ret = mods_set_access_token(ptoken->token); if (ret < 0) { mods_error_printk("unable to set access token!\n"); } else { struct mods_client *client = pfile->private_data; client->access_token = ptoken->token; } LOG_EXT(); return ret; } static int esc_mods_release_access_token(struct file *pfile, struct MODS_ACCESS_TOKEN *ptoken) { int ret = -EINVAL; LOG_ENT(); if (mods_get_multi_instance()) { LOG_EXT(); mods_error_printk( "access token ops not supported with multi_instance=1!\n"); return ret; } ret = mods_set_access_token(MODS_ACCESS_TOKEN_NONE); if (ret < 0) { mods_error_printk("unable to clear access token!\n"); } else { struct mods_client *client = pfile->private_data; client->access_token = MODS_ACCESS_TOKEN_NONE; } LOG_EXT(); return ret; } static int esc_mods_verify_access_token(struct file *pfile, struct MODS_ACCESS_TOKEN *ptoken) { int ret = -EINVAL; LOG_ENT(); if (ptoken->token == mods_get_access_token()) { struct mods_client *client = pfile->private_data; client->access_token = ptoken->token; ret = OK; } else mods_error_printk("invalid access token\n"); LOG_EXT(); return ret; } struct mods_sysfs_work { struct work_struct work; struct MODS_SYSFS_NODE *pdata; int ret; }; #ifdef MODS_OLD_INIT_WORK static void sysfs_write_task(void *w) #else static void sysfs_write_task(struct work_struct *w) #endif { struct mods_sysfs_work *task = container_of(w, struct mods_sysfs_work, work); struct file *f; mm_segment_t old_fs; LOG_ENT(); task->ret = -EINVAL; old_fs = get_fs(); set_fs(KERNEL_DS); f = filp_open(task->pdata->path, O_WRONLY, 0); if (IS_ERR(f)) task->ret = PTR_ERR(f); else { f->f_pos = 0; if (task->pdata->size <= MODS_MAX_SYSFS_FILE_SIZE) task->ret = f->f_op->write(f, task->pdata->contents, task->pdata->size, &f->f_pos); filp_close(f, NULL); } set_fs(old_fs); LOG_EXT(); } static int esc_mods_write_sysfs_node(struct file *pfile, struct MODS_SYSFS_NODE *pdata) { int ret = -EINVAL; struct mods_sysfs_work task; struct workqueue_struct *wq; LOG_ENT(); memmove(&pdata->path[5], pdata->path, MODS_MAX_SYSFS_PATH_LEN); memcpy(pdata->path, "/sys/", 5); pdata->path[MODS_MAX_SYSFS_PATH_BUF_SIZE - 1] = 0; task.pdata = pdata; wq = create_singlethread_workqueue("mods_sysfs_write"); if (!wq) { LOG_EXT(); return ret; } #ifdef MODS_OLD_INIT_WORK INIT_WORK(&task.work, sysfs_write_task, &task); #else INIT_WORK(&task.work, sysfs_write_task); #endif queue_work(wq, &task.work); flush_workqueue(wq); destroy_workqueue(wq); ret = task.ret; if (ret > 0) ret = OK; LOG_EXT(); return ret; } /************** * IO control * **************/ static long mods_krnl_ioctl(struct file *fp, unsigned int cmd, unsigned long i_arg) { int ret = 0; void *arg_copy = 0; void *arg = (void *) i_arg; int arg_size; char buf[64]; LOG_ENT(); if ((cmd != MODS_ESC_VERIFY_ACCESS_TOKEN) && (cmd != MODS_ESC_GET_API_VERSION)) { ret = mods_check_access_token(fp); if (ret < 0) { LOG_EXT(); return ret; } } arg_size = _IOC_SIZE(cmd); if (arg_size > (int)sizeof(buf)) { arg_copy = kmalloc(arg_size, GFP_KERNEL | __GFP_NORETRY); if (unlikely(!arg_copy)) { LOG_EXT(); return -ENOMEM; } } else if (arg_size > 0) arg_copy = buf; if ((arg_size > 0) && copy_from_user(arg_copy, arg, arg_size)) { mods_error_printk("failed to copy ioctl data\n"); kfree(arg_copy); LOG_EXT(); return -EFAULT; } #define MODS_IOCTL(code, function, argtype)\ ({\ do {\ mods_debug_printk(DEBUG_IOCTL, "ioctl(" #code ")\n");\ if (arg_size != sizeof(struct argtype)) {\ ret = -EINVAL;\ mods_error_printk( \ "invalid parameter passed to ioctl " #code \ "\n");\ } else {\ ret = function(fp, (struct argtype *)arg_copy);\ if ((ret == OK) && \ copy_to_user(arg, arg_copy, arg_size)) {\ ret = -EFAULT;\ mods_error_printk( \ "copying return value for ioctl " \ #code " to user space failed\n");\ } \ } \ } while (0);\ }) #define MODS_IOCTL_NORETVAL(code, function, argtype)\ ({\ do {\ mods_debug_printk(DEBUG_IOCTL, "ioctl(" #code ")\n");\ if (arg_size != sizeof(struct argtype)) {\ ret = -EINVAL;\ mods_error_printk( \ "invalid parameter passed to ioctl " #code \ "\n");\ } else {\ ret = function(fp, (struct argtype *)arg_copy);\ } \ } while (0);\ }) #define MODS_IOCTL_VOID(code, function)\ ({\ do {\ mods_debug_printk(DEBUG_IOCTL, "ioctl(" #code ")\n");\ if (arg_size != 0) {\ ret = -EINVAL;\ mods_error_printk( \ "invalid parameter passed to ioctl " #code \ "\n");\ } else {\ ret = function(fp);\ } \ } while (0);\ }) switch (cmd) { #ifdef CONFIG_PCI case MODS_ESC_FIND_PCI_DEVICE: MODS_IOCTL(MODS_ESC_FIND_PCI_DEVICE, esc_mods_find_pci_dev, MODS_FIND_PCI_DEVICE); break; case MODS_ESC_FIND_PCI_DEVICE_2: MODS_IOCTL(MODS_ESC_FIND_PCI_DEVICE_2, esc_mods_find_pci_dev_2, MODS_FIND_PCI_DEVICE_2); break; case MODS_ESC_FIND_PCI_CLASS_CODE: MODS_IOCTL(MODS_ESC_FIND_PCI_CLASS_CODE, esc_mods_find_pci_class_code, MODS_FIND_PCI_CLASS_CODE); break; case MODS_ESC_FIND_PCI_CLASS_CODE_2: MODS_IOCTL(MODS_ESC_FIND_PCI_CLASS_CODE_2, esc_mods_find_pci_class_code_2, MODS_FIND_PCI_CLASS_CODE_2); break; case MODS_ESC_PCI_GET_BAR_INFO: MODS_IOCTL(MODS_ESC_PCI_GET_BAR_INFO, esc_mods_pci_get_bar_info, MODS_PCI_GET_BAR_INFO); break; case MODS_ESC_PCI_GET_BAR_INFO_2: MODS_IOCTL(MODS_ESC_PCI_GET_BAR_INFO_2, esc_mods_pci_get_bar_info_2, MODS_PCI_GET_BAR_INFO_2); break; case MODS_ESC_PCI_GET_IRQ: MODS_IOCTL(MODS_ESC_PCI_GET_IRQ, esc_mods_pci_get_irq, MODS_PCI_GET_IRQ); break; case MODS_ESC_PCI_GET_IRQ_2: MODS_IOCTL(MODS_ESC_PCI_GET_IRQ_2, esc_mods_pci_get_irq_2, MODS_PCI_GET_IRQ_2); break; case MODS_ESC_PCI_READ: MODS_IOCTL(MODS_ESC_PCI_READ, esc_mods_pci_read, MODS_PCI_READ); break; case MODS_ESC_PCI_READ_2: MODS_IOCTL(MODS_ESC_PCI_READ_2, esc_mods_pci_read_2, MODS_PCI_READ_2); break; case MODS_ESC_PCI_WRITE: MODS_IOCTL_NORETVAL(MODS_ESC_PCI_WRITE, esc_mods_pci_write, MODS_PCI_WRITE); break; case MODS_ESC_PCI_WRITE_2: MODS_IOCTL_NORETVAL(MODS_ESC_PCI_WRITE_2, esc_mods_pci_write_2, MODS_PCI_WRITE_2); break; case MODS_ESC_PCI_BUS_ADD_DEVICES: MODS_IOCTL_NORETVAL(MODS_ESC_PCI_BUS_ADD_DEVICES, esc_mods_pci_bus_add_dev, MODS_PCI_BUS_ADD_DEVICES); break; case MODS_ESC_PCI_HOT_RESET: MODS_IOCTL_NORETVAL(MODS_ESC_PCI_HOT_RESET, esc_mods_pci_hot_reset, MODS_PCI_HOT_RESET); break; case MODS_ESC_PIO_READ: MODS_IOCTL(MODS_ESC_PIO_READ, esc_mods_pio_read, MODS_PIO_READ); break; case MODS_ESC_PIO_WRITE: MODS_IOCTL_NORETVAL(MODS_ESC_PIO_WRITE, esc_mods_pio_write, MODS_PIO_WRITE); break; case MODS_ESC_DEVICE_NUMA_INFO: MODS_IOCTL(MODS_ESC_DEVICE_NUMA_INFO, esc_mods_device_numa_info, MODS_DEVICE_NUMA_INFO); break; case MODS_ESC_DEVICE_NUMA_INFO_2: MODS_IOCTL(MODS_ESC_DEVICE_NUMA_INFO_2, esc_mods_device_numa_info_2, MODS_DEVICE_NUMA_INFO_2); break; case MODS_ESC_GET_IOMMU_STATE: MODS_IOCTL(MODS_ESC_GET_IOMMU_STATE, esc_mods_get_iommu_state, MODS_GET_IOMMU_STATE); break; case MODS_ESC_GET_IOMMU_STATE_2: MODS_IOCTL(MODS_ESC_GET_IOMMU_STATE_2, esc_mods_get_iommu_state_2, MODS_GET_IOMMU_STATE); break; case MODS_ESC_PCI_SET_DMA_MASK: MODS_IOCTL(MODS_ESC_PCI_SET_DMA_MASK, esc_mods_pci_set_dma_mask, MODS_PCI_DMA_MASK); break; #endif case MODS_ESC_ALLOC_PAGES: MODS_IOCTL(MODS_ESC_ALLOC_PAGES, esc_mods_alloc_pages, MODS_ALLOC_PAGES); break; case MODS_ESC_DEVICE_ALLOC_PAGES: MODS_IOCTL(MODS_ESC_DEVICE_ALLOC_PAGES, esc_mods_device_alloc_pages, MODS_DEVICE_ALLOC_PAGES); break; case MODS_ESC_DEVICE_ALLOC_PAGES_2: MODS_IOCTL(MODS_ESC_DEVICE_ALLOC_PAGES_2, esc_mods_device_alloc_pages_2, MODS_DEVICE_ALLOC_PAGES_2); break; case MODS_ESC_FREE_PAGES: MODS_IOCTL(MODS_ESC_FREE_PAGES, esc_mods_free_pages, MODS_FREE_PAGES); break; case MODS_ESC_GET_PHYSICAL_ADDRESS: MODS_IOCTL(MODS_ESC_GET_PHYSICAL_ADDRESS, esc_mods_get_phys_addr, MODS_GET_PHYSICAL_ADDRESS); break; case MODS_ESC_GET_PHYSICAL_ADDRESS_2: MODS_IOCTL(MODS_ESC_GET_PHYSICAL_ADDRESS_2, esc_mods_get_phys_addr_2, MODS_GET_PHYSICAL_ADDRESS_3); break; case MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS: MODS_IOCTL(MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS, esc_mods_get_mapped_phys_addr, MODS_GET_PHYSICAL_ADDRESS); break; case MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS_2: MODS_IOCTL(MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS_2, esc_mods_get_mapped_phys_addr_2, MODS_GET_PHYSICAL_ADDRESS_2); break; case MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS_3: MODS_IOCTL(MODS_ESC_GET_MAPPED_PHYSICAL_ADDRESS_3, esc_mods_get_mapped_phys_addr_3, MODS_GET_PHYSICAL_ADDRESS_3); break; case MODS_ESC_SET_MEMORY_TYPE: MODS_IOCTL_NORETVAL(MODS_ESC_SET_MEMORY_TYPE, esc_mods_set_mem_type, MODS_MEMORY_TYPE); break; case MODS_ESC_VIRTUAL_TO_PHYSICAL: MODS_IOCTL(MODS_ESC_VIRTUAL_TO_PHYSICAL, esc_mods_virtual_to_phys, MODS_VIRTUAL_TO_PHYSICAL); break; case MODS_ESC_PHYSICAL_TO_VIRTUAL: MODS_IOCTL(MODS_ESC_PHYSICAL_TO_VIRTUAL, esc_mods_phys_to_virtual, MODS_PHYSICAL_TO_VIRTUAL); break; #if defined(CONFIG_PPC64) case MODS_ESC_SET_PPC_TCE_BYPASS: MODS_IOCTL(MODS_ESC_SET_PPC_TCE_BYPASS, esc_mods_set_ppc_tce_bypass, MODS_SET_PPC_TCE_BYPASS); break; case MODS_ESC_GET_ATS_ADDRESS_RANGE: MODS_IOCTL(MODS_ESC_GET_ATS_ADDRESS_RANGE, esc_mods_get_ats_address_range, MODS_GET_ATS_ADDRESS_RANGE); break; case MODS_ESC_SET_NVLINK_SYSMEM_TRAINED: MODS_IOCTL(MODS_ESC_SET_NVLINK_SYSMEM_TRAINED, esc_mods_set_nvlink_sysmem_trained, MODS_SET_NVLINK_SYSMEM_TRAINED); break; case MODS_ESC_GET_NVLINK_LINE_RATE: MODS_IOCTL(MODS_ESC_GET_NVLINK_LINE_RATE, esc_mods_get_nvlink_line_rate, MODS_GET_NVLINK_LINE_RATE); break; #endif case MODS_ESC_DMA_MAP_MEMORY: MODS_IOCTL(MODS_ESC_DMA_MAP_MEMORY, esc_mods_dma_map_memory, MODS_DMA_MAP_MEMORY); break; case MODS_ESC_DMA_UNMAP_MEMORY: MODS_IOCTL(MODS_ESC_DMA_UNMAP_MEMORY, esc_mods_dma_unmap_memory, MODS_DMA_MAP_MEMORY); break; case MODS_ESC_IRQ_REGISTER: case MODS_ESC_MSI_REGISTER: ret = -EINVAL; break; case MODS_ESC_MAP_INTERRUPT: #if defined(MODS_TEGRA) && defined(CONFIG_OF_IRQ) && defined(CONFIG_OF) MODS_IOCTL(MODS_ESC_MAP_INTERRUPT, esc_mods_map_irq, MODS_DT_INFO); #endif break; case MODS_ESC_REGISTER_IRQ: MODS_IOCTL_NORETVAL(MODS_ESC_REGISTER_IRQ, esc_mods_register_irq, MODS_REGISTER_IRQ); break; case MODS_ESC_REGISTER_IRQ_2: MODS_IOCTL_NORETVAL(MODS_ESC_REGISTER_IRQ_2, esc_mods_register_irq_2, MODS_REGISTER_IRQ_2); break; case MODS_ESC_REGISTER_IRQ_3: MODS_IOCTL_NORETVAL(MODS_ESC_REGISTER_IRQ_3, esc_mods_register_irq_3, MODS_REGISTER_IRQ_3); break; case MODS_ESC_UNREGISTER_IRQ: MODS_IOCTL_NORETVAL(MODS_ESC_UNREGISTER_IRQ, esc_mods_unregister_irq, MODS_REGISTER_IRQ); break; case MODS_ESC_UNREGISTER_IRQ_2: MODS_IOCTL_NORETVAL(MODS_ESC_UNREGISTER_IRQ_2, esc_mods_unregister_irq_2, MODS_REGISTER_IRQ_2); break; case MODS_ESC_QUERY_IRQ: MODS_IOCTL(MODS_ESC_QUERY_IRQ, esc_mods_query_irq, MODS_QUERY_IRQ); break; case MODS_ESC_QUERY_IRQ_2: MODS_IOCTL(MODS_ESC_QUERY_IRQ_2, esc_mods_query_irq_2, MODS_QUERY_IRQ_2); break; case MODS_ESC_IRQ_HANDLED: MODS_IOCTL_NORETVAL(MODS_ESC_IRQ_HANDLED, esc_mods_irq_handled, MODS_REGISTER_IRQ); break; case MODS_ESC_IRQ_HANDLED_2: MODS_IOCTL_NORETVAL(MODS_ESC_IRQ_HANDLED_2, esc_mods_irq_handled_2, MODS_REGISTER_IRQ_2); break; #ifdef CONFIG_ACPI case MODS_ESC_EVAL_ACPI_METHOD: MODS_IOCTL(MODS_ESC_EVAL_ACPI_METHOD, esc_mods_eval_acpi_method, MODS_EVAL_ACPI_METHOD); break; case MODS_ESC_EVAL_DEV_ACPI_METHOD: MODS_IOCTL(MODS_ESC_EVAL_DEV_ACPI_METHOD, esc_mods_eval_dev_acpi_method, MODS_EVAL_DEV_ACPI_METHOD); break; case MODS_ESC_EVAL_DEV_ACPI_METHOD_2: MODS_IOCTL(MODS_ESC_EVAL_DEV_ACPI_METHOD_2, esc_mods_eval_dev_acpi_method_2, MODS_EVAL_DEV_ACPI_METHOD_2); break; case MODS_ESC_ACPI_GET_DDC: MODS_IOCTL(MODS_ESC_ACPI_GET_DDC, esc_mods_acpi_get_ddc, MODS_ACPI_GET_DDC); break; case MODS_ESC_ACPI_GET_DDC_2: MODS_IOCTL(MODS_ESC_ACPI_GET_DDC_2, esc_mods_acpi_get_ddc_2, MODS_ACPI_GET_DDC_2); break; #else case MODS_ESC_EVAL_ACPI_METHOD: /* fallthrough */ case MODS_ESC_EVAL_DEV_ACPI_METHOD: /* fallthrough */ case MODS_ESC_EVAL_DEV_ACPI_METHOD_2: /* fallthrough */ case MODS_ESC_ACPI_GET_DDC: /* fallthrough */ case MODS_ESC_ACPI_GET_DDC_2: /* Silent failure to avoid clogging kernel log */ ret = -EINVAL; break; #endif case MODS_ESC_GET_API_VERSION: MODS_IOCTL(MODS_ESC_GET_API_VERSION, esc_mods_get_api_version, MODS_GET_VERSION); break; case MODS_ESC_GET_KERNEL_VERSION: MODS_IOCTL(MODS_ESC_GET_KERNEL_VERSION, esc_mods_get_kernel_version, MODS_GET_VERSION); break; case MODS_ESC_SET_DRIVER_PARA: MODS_IOCTL_NORETVAL(MODS_ESC_SET_DRIVER_PARA, esc_mods_set_driver_para, MODS_SET_PARA); break; #if defined(MODS_HAS_CLOCK) case MODS_ESC_GET_CLOCK_HANDLE: MODS_IOCTL(MODS_ESC_GET_CLOCK_HANDLE, esc_mods_get_clock_handle, MODS_GET_CLOCK_HANDLE); break; case MODS_ESC_SET_CLOCK_RATE: MODS_IOCTL_NORETVAL(MODS_ESC_SET_CLOCK_RATE, esc_mods_set_clock_rate, MODS_CLOCK_RATE); break; case MODS_ESC_GET_CLOCK_RATE: MODS_IOCTL(MODS_ESC_GET_CLOCK_RATE, esc_mods_get_clock_rate, MODS_CLOCK_RATE); break; case MODS_ESC_GET_CLOCK_MAX_RATE: MODS_IOCTL(MODS_ESC_GET_CLOCK_MAX_RATE, esc_mods_get_clock_max_rate, MODS_CLOCK_RATE); break; case MODS_ESC_SET_CLOCK_MAX_RATE: MODS_IOCTL_NORETVAL(MODS_ESC_SET_CLOCK_MAX_RATE, esc_mods_set_clock_max_rate, MODS_CLOCK_RATE); break; case MODS_ESC_SET_CLOCK_PARENT: MODS_IOCTL_NORETVAL(MODS_ESC_SET_CLOCK_PARENT, esc_mods_set_clock_parent, MODS_CLOCK_PARENT); break; case MODS_ESC_GET_CLOCK_PARENT: MODS_IOCTL(MODS_ESC_GET_CLOCK_PARENT, esc_mods_get_clock_parent, MODS_CLOCK_PARENT); break; case MODS_ESC_ENABLE_CLOCK: MODS_IOCTL_NORETVAL(MODS_ESC_ENABLE_CLOCK, esc_mods_enable_clock, MODS_CLOCK_HANDLE); break; case MODS_ESC_DISABLE_CLOCK: MODS_IOCTL_NORETVAL(MODS_ESC_DISABLE_CLOCK, esc_mods_disable_clock, MODS_CLOCK_HANDLE); break; case MODS_ESC_IS_CLOCK_ENABLED: MODS_IOCTL(MODS_ESC_IS_CLOCK_ENABLED, esc_mods_is_clock_enabled, MODS_CLOCK_ENABLED); break; case MODS_ESC_CLOCK_RESET_ASSERT: MODS_IOCTL_NORETVAL(MODS_ESC_CLOCK_RESET_ASSERT, esc_mods_clock_reset_assert, MODS_CLOCK_HANDLE); break; case MODS_ESC_CLOCK_RESET_DEASSERT: MODS_IOCTL_NORETVAL(MODS_ESC_CLOCK_RESET_DEASSERT, esc_mods_clock_reset_deassert, MODS_CLOCK_HANDLE); break; #endif #if defined(MODS_TEGRA) case MODS_ESC_FLUSH_CPU_CACHE_RANGE: MODS_IOCTL_NORETVAL(MODS_ESC_FLUSH_CPU_CACHE_RANGE, esc_mods_flush_cpu_cache_range, MODS_FLUSH_CPU_CACHE_RANGE); break; case MODS_ESC_DMA_ALLOC_COHERENT: MODS_IOCTL(MODS_ESC_DMA_ALLOC_COHERENT, esc_mods_dma_alloc_coherent, MODS_DMA_COHERENT_MEM_HANDLE); break; case MODS_ESC_DMA_FREE_COHERENT: MODS_IOCTL(MODS_ESC_DMA_FREE_COHERENT, esc_mods_dma_free_coherent, MODS_DMA_COHERENT_MEM_HANDLE); break; case MODS_ESC_DMA_COPY_TO_USER: MODS_IOCTL(MODS_ESC_DMA_COPY_TO_USER, esc_mods_dma_copy_to_user, MODS_DMA_COPY_TO_USER); break; #if defined(CONFIG_DMA_ENGINE) case MODS_ESC_DMA_REQUEST_HANDLE: MODS_IOCTL(MODS_ESC_DMA_REQUEST_HANDLE, esc_mods_dma_request_channel, MODS_DMA_HANDLE); break; case MODS_ESC_DMA_RELEASE_HANDLE: MODS_IOCTL_NORETVAL(MODS_ESC_DMA_RELEASE_HANDLE, esc_mods_dma_release_channel, MODS_DMA_HANDLE); break; case MODS_ESC_DMA_ISSUE_PENDING: MODS_IOCTL_NORETVAL(MODS_ESC_DMA_ISSUE_PENDING, esc_mods_dma_async_issue_pending, MODS_DMA_HANDLE); break; case MODS_ESC_DMA_SET_CONFIG: MODS_IOCTL_NORETVAL(MODS_ESC_DMA_SET_CONFIG, esc_mods_dma_set_config, MODS_DMA_CHANNEL_CONFIG); break; case MODS_ESC_DMA_TX_SUBMIT: MODS_IOCTL(MODS_ESC_DMA_TX_SUBMIT, esc_mods_dma_submit_request, MODS_DMA_TX_DESC); break; case MODS_ESC_DMA_TX_WAIT: MODS_IOCTL(MODS_MODS_ESC_DMA_TX_WAIT, esc_mods_dma_wait, MODS_DMA_WAIT_DESC); break; #endif #ifdef CONFIG_TEGRA_DC case MODS_ESC_TEGRA_DC_CONFIG_POSSIBLE: MODS_IOCTL(MODS_ESC_TEGRA_DC_CONFIG_POSSIBLE, esc_mods_tegra_dc_config_possible, MODS_TEGRA_DC_CONFIG_POSSIBLE); break; #endif #ifdef MODS_HAS_NET case MODS_ESC_NET_FORCE_LINK: MODS_IOCTL(MODS_ESC_NET_FORCE_LINK, esc_mods_net_force_link, MODS_NET_DEVICE_NAME); break; #endif #endif case MODS_ESC_MEMORY_BARRIER: MODS_IOCTL_VOID(MODS_ESC_MEMORY_BARRIER, esc_mods_memory_barrier); break; #ifdef MODS_TEGRA case MODS_ESC_DMABUF_GET_PHYSICAL_ADDRESS: MODS_IOCTL(MODS_ESC_DMABUF_GET_PHYSICAL_ADDRESS, esc_mods_dmabuf_get_phys_addr, MODS_DMABUF_GET_PHYSICAL_ADDRESS); break; #endif #ifdef CONFIG_TEGRA_NVADSP case MODS_ESC_ADSP_LOAD: MODS_IOCTL_VOID(MODS_ESC_ADSP_LOAD, esc_mods_adsp_load); break; case MODS_ESC_ADSP_START: MODS_IOCTL_VOID(MODS_ESC_ADSP_START, esc_mods_adsp_start); break; case MODS_ESC_ADSP_STOP: MODS_IOCTL_VOID(MODS_ESC_ADSP_STOP, esc_mods_adsp_stop); break; case MODS_ESC_ADSP_RUN_APP: MODS_IOCTL_NORETVAL(MODS_ESC_ADSP_RUN_APP, esc_mods_adsp_run_app, MODS_ADSP_RUN_APP_INFO); break; #endif case MODS_ESC_GET_SCREEN_INFO: MODS_IOCTL(MODS_ESC_GET_SCREEN_INFO, esc_mods_get_screen_info, MODS_SCREEN_INFO); break; case MODS_ESC_GET_SCREEN_INFO_2: MODS_IOCTL(MODS_ESC_GET_SCREEN_INFO_2, esc_mods_get_screen_info_2, MODS_SCREEN_INFO_2); break; case MODS_ESC_LOCK_CONSOLE: MODS_IOCTL_VOID(MODS_ESC_LOCK_CONSOLE, esc_mods_lock_console); break; case MODS_ESC_UNLOCK_CONSOLE: MODS_IOCTL_VOID(MODS_ESC_UNLOCK_CONSOLE, esc_mods_unlock_console); break; case MODS_ESC_SUSPEND_CONSOLE: MODS_IOCTL_VOID(MODS_ESC_SUSPEND_CONSOLE, esc_mods_suspend_console); break; case MODS_ESC_RESUME_CONSOLE: MODS_IOCTL_VOID(MODS_ESC_RESUME_CONSOLE, esc_mods_resume_console); break; #if defined(MODS_TEGRA) case MODS_ESC_TEGRA_PROD_IS_SUPPORTED: MODS_IOCTL(MODS_ESC_TEGRA_PROD_IS_SUPPORTED, esc_mods_tegra_prod_is_supported, MODS_TEGRA_PROD_IS_SUPPORTED); break; case MODS_ESC_TEGRA_PROD_SET_PROD_ALL: MODS_IOCTL_NORETVAL(MODS_ESC_TEGRA_PROD_SET_PROD_ALL, esc_mods_tegra_prod_set_prod_all, MODS_TEGRA_PROD_SET_TUPLE); break; case MODS_ESC_TEGRA_PROD_SET_PROD_BOOT: MODS_IOCTL_NORETVAL(MODS_ESC_TEGRA_PROD_SET_PROD_BOOT, esc_mods_tegra_prod_set_prod_boot, MODS_TEGRA_PROD_SET_TUPLE); break; case MODS_ESC_TEGRA_PROD_SET_PROD_BY_NAME: MODS_IOCTL_NORETVAL(MODS_ESC_TEGRA_PROD_SET_PROD_BY_NAME, esc_mods_tegra_prod_set_prod_by_name, MODS_TEGRA_PROD_SET_TUPLE); break; case MODS_ESC_TEGRA_PROD_SET_PROD_EXACT: MODS_IOCTL_NORETVAL(MODS_ESC_TEGRA_PROD_SET_PROD_EXACT, esc_mods_tegra_prod_set_prod_exact, MODS_TEGRA_PROD_SET_TUPLE); break; case MODS_ESC_TEGRA_PROD_ITERATE_DT: MODS_IOCTL(MODS_ESC_TEGRA_PROD_ITERATE_DT, esc_mods_tegra_prod_iterate_dt, MODS_TEGRA_PROD_ITERATOR); break; #endif case MODS_ESC_ACQUIRE_ACCESS_TOKEN: MODS_IOCTL(MODS_ESC_ACQUIRE_ACCESS_TOKEN, esc_mods_acquire_access_token, MODS_ACCESS_TOKEN); break; case MODS_ESC_RELEASE_ACCESS_TOKEN: MODS_IOCTL_NORETVAL(MODS_ESC_RELEASE_ACCESS_TOKEN, esc_mods_release_access_token, MODS_ACCESS_TOKEN); break; case MODS_ESC_VERIFY_ACCESS_TOKEN: MODS_IOCTL_NORETVAL(MODS_ESC_VERIFY_ACCESS_TOKEN, esc_mods_verify_access_token, MODS_ACCESS_TOKEN); break; case MODS_ESC_WRITE_SYSFS_NODE: MODS_IOCTL_NORETVAL(MODS_ESC_WRITE_SYSFS_NODE, esc_mods_write_sysfs_node, MODS_SYSFS_NODE); break; case MODS_ESC_REGISTER_IRQ_4: MODS_IOCTL_NORETVAL(MODS_ESC_REGISTER_IRQ_4, esc_mods_register_irq_4, MODS_REGISTER_IRQ_4); break; case MODS_ESC_QUERY_IRQ_3: MODS_IOCTL(MODS_ESC_QUERY_IRQ_3, esc_mods_query_irq_3, MODS_QUERY_IRQ_3); break; #ifdef MODS_HAS_SRIOV case MODS_ESC_SET_NUM_VF: MODS_IOCTL_NORETVAL(MODS_ESC_SET_NUM_VF, esc_mods_set_num_vf, MODS_SET_NUM_VF); break; case MODS_ESC_SET_TOTAL_VF: MODS_IOCTL_NORETVAL(MODS_ESC_SET_TOTAL_VF, esc_mods_set_total_vf, MODS_SET_NUM_VF); break; #endif default: mods_error_printk("unrecognized ioctl (0x%x) dir(0x%x) type (0x%x) nr (0x%x) size (0x%x)\n", cmd, _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); ret = -EINVAL; break; } if (arg_size > (int)sizeof(buf)) kfree(arg_copy); LOG_EXT(); return ret; }