18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SN Platform GRU Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * FILE OPERATIONS & DRIVER INITIALIZATION 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file supports the user system call for file open, close, mmap, etc. 88c2ecf20Sopenharmony_ci * This also incudes the driver initialization code. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP 118c2ecf20Sopenharmony_ci * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64 278c2ecf20Sopenharmony_ci#include <asm/uv/uv_irq.h> 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci#include <asm/uv/uv.h> 308c2ecf20Sopenharmony_ci#include "gru.h" 318c2ecf20Sopenharmony_ci#include "grulib.h" 328c2ecf20Sopenharmony_ci#include "grutables.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h> 358c2ecf20Sopenharmony_ci#include <asm/uv/uv_mmrs.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly; 388c2ecf20Sopenharmony_ciunsigned long gru_start_paddr __read_mostly; 398c2ecf20Sopenharmony_civoid *gru_start_vaddr __read_mostly; 408c2ecf20Sopenharmony_ciunsigned long gru_end_paddr __read_mostly; 418c2ecf20Sopenharmony_ciunsigned int gru_max_gids __read_mostly; 428c2ecf20Sopenharmony_cistruct gru_stats_s gru_stats; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Guaranteed user available resources on each node */ 458c2ecf20Sopenharmony_cistatic int max_user_cbrs, max_user_dsr_bytes; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct miscdevice gru_miscdev; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int gru_supported(void) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return is_uv_system() && 528c2ecf20Sopenharmony_ci (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * gru_vma_close 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Called when unmapping a device mapping. Frees all gru resources 598c2ecf20Sopenharmony_ci * and tables belonging to the vma. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic void gru_vma_close(struct vm_area_struct *vma) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct gru_vma_data *vdata; 648c2ecf20Sopenharmony_ci struct gru_thread_state *gts; 658c2ecf20Sopenharmony_ci struct list_head *entry, *next; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (!vma->vm_private_data) 688c2ecf20Sopenharmony_ci return; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci vdata = vma->vm_private_data; 718c2ecf20Sopenharmony_ci vma->vm_private_data = NULL; 728c2ecf20Sopenharmony_ci gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file, 738c2ecf20Sopenharmony_ci vdata); 748c2ecf20Sopenharmony_ci list_for_each_safe(entry, next, &vdata->vd_head) { 758c2ecf20Sopenharmony_ci gts = 768c2ecf20Sopenharmony_ci list_entry(entry, struct gru_thread_state, ts_next); 778c2ecf20Sopenharmony_ci list_del(>s->ts_next); 788c2ecf20Sopenharmony_ci mutex_lock(>s->ts_ctxlock); 798c2ecf20Sopenharmony_ci if (gts->ts_gru) 808c2ecf20Sopenharmony_ci gru_unload_context(gts, 0); 818c2ecf20Sopenharmony_ci mutex_unlock(>s->ts_ctxlock); 828c2ecf20Sopenharmony_ci gts_drop(gts); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci kfree(vdata); 858c2ecf20Sopenharmony_ci STAT(vdata_free); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * gru_file_mmap 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Called when mmapping the device. Initializes the vma with a fault handler 928c2ecf20Sopenharmony_ci * and private data structure necessary to allocate, track, and free the 938c2ecf20Sopenharmony_ci * underlying pages. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic int gru_file_mmap(struct file *file, struct vm_area_struct *vma) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE)) 988c2ecf20Sopenharmony_ci return -EPERM; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) || 1018c2ecf20Sopenharmony_ci vma->vm_end & (GRU_GSEG_PAGESIZE - 1)) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci vma->vm_flags |= VM_IO | VM_PFNMAP | VM_LOCKED | 1058c2ecf20Sopenharmony_ci VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP; 1068c2ecf20Sopenharmony_ci vma->vm_page_prot = PAGE_SHARED; 1078c2ecf20Sopenharmony_ci vma->vm_ops = &gru_vm_ops; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci vma->vm_private_data = gru_alloc_vma_data(vma, 0); 1108c2ecf20Sopenharmony_ci if (!vma->vm_private_data) 1118c2ecf20Sopenharmony_ci return -ENOMEM; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n", 1148c2ecf20Sopenharmony_ci file, vma->vm_start, vma, vma->vm_private_data); 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* 1198c2ecf20Sopenharmony_ci * Create a new GRU context 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic int gru_create_new_context(unsigned long arg) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct gru_create_context_req req; 1248c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 1258c2ecf20Sopenharmony_ci struct gru_vma_data *vdata; 1268c2ecf20Sopenharmony_ci int ret = -EINVAL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (copy_from_user(&req, (void __user *)arg, sizeof(req))) 1298c2ecf20Sopenharmony_ci return -EFAULT; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (req.data_segment_bytes > max_user_dsr_bytes) 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count) 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!(req.options & GRU_OPT_MISS_MASK)) 1378c2ecf20Sopenharmony_ci req.options |= GRU_OPT_MISS_FMM_INTR; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci mmap_write_lock(current->mm); 1408c2ecf20Sopenharmony_ci vma = gru_find_vma(req.gseg); 1418c2ecf20Sopenharmony_ci if (vma) { 1428c2ecf20Sopenharmony_ci vdata = vma->vm_private_data; 1438c2ecf20Sopenharmony_ci vdata->vd_user_options = req.options; 1448c2ecf20Sopenharmony_ci vdata->vd_dsr_au_count = 1458c2ecf20Sopenharmony_ci GRU_DS_BYTES_TO_AU(req.data_segment_bytes); 1468c2ecf20Sopenharmony_ci vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); 1478c2ecf20Sopenharmony_ci vdata->vd_tlb_preload_count = req.tlb_preload_count; 1488c2ecf20Sopenharmony_ci ret = 0; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci mmap_write_unlock(current->mm); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Get GRU configuration info (temp - for emulator testing) 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic long gru_get_config_info(unsigned long arg) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct gru_config_info info; 1618c2ecf20Sopenharmony_ci int nodesperblade; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (num_online_nodes() > 1 && 1648c2ecf20Sopenharmony_ci (uv_node_to_blade_id(1) == uv_node_to_blade_id(0))) 1658c2ecf20Sopenharmony_ci nodesperblade = 2; 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci nodesperblade = 1; 1688c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 1698c2ecf20Sopenharmony_ci info.cpus = num_online_cpus(); 1708c2ecf20Sopenharmony_ci info.nodes = num_online_nodes(); 1718c2ecf20Sopenharmony_ci info.blades = info.nodes / nodesperblade; 1728c2ecf20Sopenharmony_ci info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, sizeof(info))) 1758c2ecf20Sopenharmony_ci return -EFAULT; 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * gru_file_unlocked_ioctl 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Called to update file attributes via IOCTL calls. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic long gru_file_unlocked_ioctl(struct file *file, unsigned int req, 1858c2ecf20Sopenharmony_ci unsigned long arg) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int err = -EBADRQC; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci switch (req) { 1928c2ecf20Sopenharmony_ci case GRU_CREATE_CONTEXT: 1938c2ecf20Sopenharmony_ci err = gru_create_new_context(arg); 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci case GRU_SET_CONTEXT_OPTION: 1968c2ecf20Sopenharmony_ci err = gru_set_context_option(arg); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case GRU_USER_GET_EXCEPTION_DETAIL: 1998c2ecf20Sopenharmony_ci err = gru_get_exception_detail(arg); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case GRU_USER_UNLOAD_CONTEXT: 2028c2ecf20Sopenharmony_ci err = gru_user_unload_context(arg); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case GRU_USER_FLUSH_TLB: 2058c2ecf20Sopenharmony_ci err = gru_user_flush_tlb(arg); 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case GRU_USER_CALL_OS: 2088c2ecf20Sopenharmony_ci err = gru_handle_user_call_os(arg); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case GRU_GET_GSEG_STATISTICS: 2118c2ecf20Sopenharmony_ci err = gru_get_gseg_statistics(arg); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci case GRU_KTEST: 2148c2ecf20Sopenharmony_ci err = gru_ktest(arg); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case GRU_GET_CONFIG_INFO: 2178c2ecf20Sopenharmony_ci err = gru_get_config_info(arg); 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci case GRU_DUMP_CHIPLET_STATE: 2208c2ecf20Sopenharmony_ci err = gru_dump_chiplet_request(arg); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* 2278c2ecf20Sopenharmony_ci * Called at init time to build tables for all GRUs that are present in the 2288c2ecf20Sopenharmony_ci * system. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistatic void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, 2318c2ecf20Sopenharmony_ci void *vaddr, int blade_id, int chiplet_id) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci spin_lock_init(&gru->gs_lock); 2348c2ecf20Sopenharmony_ci spin_lock_init(&gru->gs_asid_lock); 2358c2ecf20Sopenharmony_ci gru->gs_gru_base_paddr = paddr; 2368c2ecf20Sopenharmony_ci gru->gs_gru_base_vaddr = vaddr; 2378c2ecf20Sopenharmony_ci gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id; 2388c2ecf20Sopenharmony_ci gru->gs_blade = gru_base[blade_id]; 2398c2ecf20Sopenharmony_ci gru->gs_blade_id = blade_id; 2408c2ecf20Sopenharmony_ci gru->gs_chiplet_id = chiplet_id; 2418c2ecf20Sopenharmony_ci gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; 2428c2ecf20Sopenharmony_ci gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; 2438c2ecf20Sopenharmony_ci gru->gs_asid_limit = MAX_ASID; 2448c2ecf20Sopenharmony_ci gru_tgh_flush_init(gru); 2458c2ecf20Sopenharmony_ci if (gru->gs_gid >= gru_max_gids) 2468c2ecf20Sopenharmony_ci gru_max_gids = gru->gs_gid + 1; 2478c2ecf20Sopenharmony_ci gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n", 2488c2ecf20Sopenharmony_ci blade_id, gru->gs_gid, gru->gs_gru_base_vaddr, 2498c2ecf20Sopenharmony_ci gru->gs_gru_base_paddr); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int pnode, nid, bid, chip; 2558c2ecf20Sopenharmony_ci int cbrs, dsrbytes, n; 2568c2ecf20Sopenharmony_ci int order = get_order(sizeof(struct gru_blade_state)); 2578c2ecf20Sopenharmony_ci struct page *page; 2588c2ecf20Sopenharmony_ci struct gru_state *gru; 2598c2ecf20Sopenharmony_ci unsigned long paddr; 2608c2ecf20Sopenharmony_ci void *vaddr; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci max_user_cbrs = GRU_NUM_CB; 2638c2ecf20Sopenharmony_ci max_user_dsr_bytes = GRU_NUM_DSR_BYTES; 2648c2ecf20Sopenharmony_ci for_each_possible_blade(bid) { 2658c2ecf20Sopenharmony_ci pnode = uv_blade_to_pnode(bid); 2668c2ecf20Sopenharmony_ci nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */ 2678c2ecf20Sopenharmony_ci page = alloc_pages_node(nid, GFP_KERNEL, order); 2688c2ecf20Sopenharmony_ci if (!page) 2698c2ecf20Sopenharmony_ci goto fail; 2708c2ecf20Sopenharmony_ci gru_base[bid] = page_address(page); 2718c2ecf20Sopenharmony_ci memset(gru_base[bid], 0, sizeof(struct gru_blade_state)); 2728c2ecf20Sopenharmony_ci gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0]; 2738c2ecf20Sopenharmony_ci spin_lock_init(&gru_base[bid]->bs_lock); 2748c2ecf20Sopenharmony_ci init_rwsem(&gru_base[bid]->bs_kgts_sema); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dsrbytes = 0; 2778c2ecf20Sopenharmony_ci cbrs = 0; 2788c2ecf20Sopenharmony_ci for (gru = gru_base[bid]->bs_grus, chip = 0; 2798c2ecf20Sopenharmony_ci chip < GRU_CHIPLETS_PER_BLADE; 2808c2ecf20Sopenharmony_ci chip++, gru++) { 2818c2ecf20Sopenharmony_ci paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); 2828c2ecf20Sopenharmony_ci vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); 2838c2ecf20Sopenharmony_ci gru_init_chiplet(gru, paddr, vaddr, bid, chip); 2848c2ecf20Sopenharmony_ci n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; 2858c2ecf20Sopenharmony_ci cbrs = max(cbrs, n); 2868c2ecf20Sopenharmony_ci n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; 2878c2ecf20Sopenharmony_ci dsrbytes = max(dsrbytes, n); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci max_user_cbrs = min(max_user_cbrs, cbrs); 2908c2ecf20Sopenharmony_ci max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cifail: 2968c2ecf20Sopenharmony_ci for (bid--; bid >= 0; bid--) 2978c2ecf20Sopenharmony_ci free_pages((unsigned long)gru_base[bid], order); 2988c2ecf20Sopenharmony_ci return -ENOMEM; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void gru_free_tables(void) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int bid; 3048c2ecf20Sopenharmony_ci int order = get_order(sizeof(struct gru_state) * 3058c2ecf20Sopenharmony_ci GRU_CHIPLETS_PER_BLADE); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (bid = 0; bid < GRU_MAX_BLADES; bid++) 3088c2ecf20Sopenharmony_ci free_pages((unsigned long)gru_base[bid], order); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci unsigned long mmr = 0; 3148c2ecf20Sopenharmony_ci int core; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * We target the cores of a blade and not the hyperthreads themselves. 3188c2ecf20Sopenharmony_ci * There is a max of 8 cores per socket and 2 sockets per blade, 3198c2ecf20Sopenharmony_ci * making for a max total of 16 cores (i.e., 16 CPUs without 3208c2ecf20Sopenharmony_ci * hyperthreading and 32 CPUs with hyperthreading). 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); 3238c2ecf20Sopenharmony_ci if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu)) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (chiplet == 0) { 3278c2ecf20Sopenharmony_ci mmr = UVH_GR0_TLB_INT0_CONFIG + 3288c2ecf20Sopenharmony_ci core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG); 3298c2ecf20Sopenharmony_ci } else if (chiplet == 1) { 3308c2ecf20Sopenharmony_ci mmr = UVH_GR1_TLB_INT0_CONFIG + 3318c2ecf20Sopenharmony_ci core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG); 3328c2ecf20Sopenharmony_ci } else { 3338c2ecf20Sopenharmony_ci BUG(); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci *corep = core; 3378c2ecf20Sopenharmony_ci return mmr; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci#ifdef CONFIG_IA64 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int gru_irq_count[GRU_CHIPLETS_PER_BLADE]; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void gru_noop(struct irq_data *d) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = { 3498c2ecf20Sopenharmony_ci [0 ... GRU_CHIPLETS_PER_BLADE - 1] { 3508c2ecf20Sopenharmony_ci .irq_mask = gru_noop, 3518c2ecf20Sopenharmony_ci .irq_unmask = gru_noop, 3528c2ecf20Sopenharmony_ci .irq_ack = gru_noop 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, 3578c2ecf20Sopenharmony_ci irq_handler_t irq_handler, int cpu, int blade) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci unsigned long mmr; 3608c2ecf20Sopenharmony_ci int irq = IRQ_GRU + chiplet; 3618c2ecf20Sopenharmony_ci int ret, core; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); 3648c2ecf20Sopenharmony_ci if (mmr == 0) 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (gru_irq_count[chiplet] == 0) { 3688c2ecf20Sopenharmony_ci gru_chip[chiplet].name = irq_name; 3698c2ecf20Sopenharmony_ci ret = irq_set_chip(irq, &gru_chip[chiplet]); 3708c2ecf20Sopenharmony_ci if (ret) { 3718c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: set_irq_chip failed, errno=%d\n", 3728c2ecf20Sopenharmony_ci GRU_DRIVER_ID_STR, -ret); 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = request_irq(irq, irq_handler, 0, irq_name, NULL); 3778c2ecf20Sopenharmony_ci if (ret) { 3788c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: request_irq failed, errno=%d\n", 3798c2ecf20Sopenharmony_ci GRU_DRIVER_ID_STR, -ret); 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci gru_irq_count[chiplet]++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci unsigned long mmr; 3918c2ecf20Sopenharmony_ci int core, irq = IRQ_GRU + chiplet; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (gru_irq_count[chiplet] == 0) 3948c2ecf20Sopenharmony_ci return; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); 3978c2ecf20Sopenharmony_ci if (mmr == 0) 3988c2ecf20Sopenharmony_ci return; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (--gru_irq_count[chiplet] == 0) 4018c2ecf20Sopenharmony_ci free_irq(irq, NULL); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci#elif defined CONFIG_X86_64 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, 4078c2ecf20Sopenharmony_ci irq_handler_t irq_handler, int cpu, int blade) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned long mmr; 4108c2ecf20Sopenharmony_ci int irq, core; 4118c2ecf20Sopenharmony_ci int ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); 4148c2ecf20Sopenharmony_ci if (mmr == 0) 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU); 4188c2ecf20Sopenharmony_ci if (irq < 0) { 4198c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n", 4208c2ecf20Sopenharmony_ci GRU_DRIVER_ID_STR, -irq); 4218c2ecf20Sopenharmony_ci return irq; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret = request_irq(irq, irq_handler, 0, irq_name, NULL); 4258c2ecf20Sopenharmony_ci if (ret) { 4268c2ecf20Sopenharmony_ci uv_teardown_irq(irq); 4278c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: request_irq failed, errno=%d\n", 4288c2ecf20Sopenharmony_ci GRU_DRIVER_ID_STR, -ret); 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq; 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int irq, core; 4388c2ecf20Sopenharmony_ci unsigned long mmr; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); 4418c2ecf20Sopenharmony_ci if (mmr) { 4428c2ecf20Sopenharmony_ci irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core]; 4438c2ecf20Sopenharmony_ci if (irq) { 4448c2ecf20Sopenharmony_ci free_irq(irq, NULL); 4458c2ecf20Sopenharmony_ci uv_teardown_irq(irq); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci#endif 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic void gru_teardown_tlb_irqs(void) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci int blade; 4558c2ecf20Sopenharmony_ci int cpu; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 4588c2ecf20Sopenharmony_ci blade = uv_cpu_to_blade_id(cpu); 4598c2ecf20Sopenharmony_ci gru_chiplet_teardown_tlb_irq(0, cpu, blade); 4608c2ecf20Sopenharmony_ci gru_chiplet_teardown_tlb_irq(1, cpu, blade); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci for_each_possible_blade(blade) { 4638c2ecf20Sopenharmony_ci if (uv_blade_nr_possible_cpus(blade)) 4648c2ecf20Sopenharmony_ci continue; 4658c2ecf20Sopenharmony_ci gru_chiplet_teardown_tlb_irq(0, 0, blade); 4668c2ecf20Sopenharmony_ci gru_chiplet_teardown_tlb_irq(1, 0, blade); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int gru_setup_tlb_irqs(void) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci int blade; 4738c2ecf20Sopenharmony_ci int cpu; 4748c2ecf20Sopenharmony_ci int ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 4778c2ecf20Sopenharmony_ci blade = uv_cpu_to_blade_id(cpu); 4788c2ecf20Sopenharmony_ci ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade); 4798c2ecf20Sopenharmony_ci if (ret != 0) 4808c2ecf20Sopenharmony_ci goto exit1; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade); 4838c2ecf20Sopenharmony_ci if (ret != 0) 4848c2ecf20Sopenharmony_ci goto exit1; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci for_each_possible_blade(blade) { 4878c2ecf20Sopenharmony_ci if (uv_blade_nr_possible_cpus(blade)) 4888c2ecf20Sopenharmony_ci continue; 4898c2ecf20Sopenharmony_ci ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade); 4908c2ecf20Sopenharmony_ci if (ret != 0) 4918c2ecf20Sopenharmony_ci goto exit1; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade); 4948c2ecf20Sopenharmony_ci if (ret != 0) 4958c2ecf20Sopenharmony_ci goto exit1; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciexit1: 5018c2ecf20Sopenharmony_ci gru_teardown_tlb_irqs(); 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* 5068c2ecf20Sopenharmony_ci * gru_init 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Called at boot or module load time to initialize the GRUs. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_cistatic int __init gru_init(void) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci int ret; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (!gru_supported()) 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci#if defined CONFIG_IA64 5188c2ecf20Sopenharmony_ci gru_start_paddr = 0xd000000000UL; /* ZZZZZZZZZZZZZZZZZZZ fixme */ 5198c2ecf20Sopenharmony_ci#else 5208c2ecf20Sopenharmony_ci gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) & 5218c2ecf20Sopenharmony_ci 0x7fffffffffffUL; 5228c2ecf20Sopenharmony_ci#endif 5238c2ecf20Sopenharmony_ci gru_start_vaddr = __va(gru_start_paddr); 5248c2ecf20Sopenharmony_ci gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; 5258c2ecf20Sopenharmony_ci printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", 5268c2ecf20Sopenharmony_ci gru_start_paddr, gru_end_paddr); 5278c2ecf20Sopenharmony_ci ret = misc_register(&gru_miscdev); 5288c2ecf20Sopenharmony_ci if (ret) { 5298c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: misc_register failed\n", 5308c2ecf20Sopenharmony_ci GRU_DRIVER_ID_STR); 5318c2ecf20Sopenharmony_ci goto exit0; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ret = gru_proc_init(); 5358c2ecf20Sopenharmony_ci if (ret) { 5368c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); 5378c2ecf20Sopenharmony_ci goto exit1; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); 5418c2ecf20Sopenharmony_ci if (ret) { 5428c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); 5438c2ecf20Sopenharmony_ci goto exit2; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ret = gru_setup_tlb_irqs(); 5478c2ecf20Sopenharmony_ci if (ret != 0) 5488c2ecf20Sopenharmony_ci goto exit3; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci gru_kservices_init(); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, 5538c2ecf20Sopenharmony_ci GRU_DRIVER_VERSION_STR); 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ciexit3: 5578c2ecf20Sopenharmony_ci gru_free_tables(); 5588c2ecf20Sopenharmony_ciexit2: 5598c2ecf20Sopenharmony_ci gru_proc_exit(); 5608c2ecf20Sopenharmony_ciexit1: 5618c2ecf20Sopenharmony_ci misc_deregister(&gru_miscdev); 5628c2ecf20Sopenharmony_ciexit0: 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void __exit gru_exit(void) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci if (!gru_supported()) 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci gru_teardown_tlb_irqs(); 5738c2ecf20Sopenharmony_ci gru_kservices_exit(); 5748c2ecf20Sopenharmony_ci gru_free_tables(); 5758c2ecf20Sopenharmony_ci misc_deregister(&gru_miscdev); 5768c2ecf20Sopenharmony_ci gru_proc_exit(); 5778c2ecf20Sopenharmony_ci mmu_notifier_synchronize(); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic const struct file_operations gru_fops = { 5818c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5828c2ecf20Sopenharmony_ci .unlocked_ioctl = gru_file_unlocked_ioctl, 5838c2ecf20Sopenharmony_ci .mmap = gru_file_mmap, 5848c2ecf20Sopenharmony_ci .llseek = noop_llseek, 5858c2ecf20Sopenharmony_ci}; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic struct miscdevice gru_miscdev = { 5888c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 5898c2ecf20Sopenharmony_ci .name = "gru", 5908c2ecf20Sopenharmony_ci .fops = &gru_fops, 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciconst struct vm_operations_struct gru_vm_ops = { 5948c2ecf20Sopenharmony_ci .close = gru_vma_close, 5958c2ecf20Sopenharmony_ci .fault = gru_fault, 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci#ifndef MODULE 5998c2ecf20Sopenharmony_cifs_initcall(gru_init); 6008c2ecf20Sopenharmony_ci#else 6018c2ecf20Sopenharmony_cimodule_init(gru_init); 6028c2ecf20Sopenharmony_ci#endif 6038c2ecf20Sopenharmony_cimodule_exit(gru_exit); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cimodule_param(gru_options, ulong, 0644); 6068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gru_options, "Various debug options"); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Silicon Graphics, Inc."); 6098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR); 6118c2ecf20Sopenharmony_ciMODULE_VERSION(GRU_DRIVER_VERSION_STR); 6128c2ecf20Sopenharmony_ci 613