18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GRU KERNEL MCS INSTRUCTIONS 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include "gru.h" 108c2ecf20Sopenharmony_ci#include "grulib.h" 118c2ecf20Sopenharmony_ci#include "grutables.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 10 sec */ 148c2ecf20Sopenharmony_ci#ifdef CONFIG_IA64 158c2ecf20Sopenharmony_ci#include <asm/processor.h> 168c2ecf20Sopenharmony_ci#define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10) 178c2ecf20Sopenharmony_ci#define CLKS2NSEC(c) ((c) *1000000000 / local_cpu_data->itc_freq) 188c2ecf20Sopenharmony_ci#else 198c2ecf20Sopenharmony_ci#include <linux/sync_core.h> 208c2ecf20Sopenharmony_ci#include <asm/tsc.h> 218c2ecf20Sopenharmony_ci#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) 228c2ecf20Sopenharmony_ci#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz) 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Extract the status field from a kernel handle */ 268c2ecf20Sopenharmony_ci#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct mcs_op_statistic mcs_op_statistics[mcsop_last]; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void update_mcs_stats(enum mcs_op op, unsigned long clks) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci unsigned long nsec; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci nsec = CLKS2NSEC(clks); 358c2ecf20Sopenharmony_ci atomic_long_inc(&mcs_op_statistics[op].count); 368c2ecf20Sopenharmony_ci atomic_long_add(nsec, &mcs_op_statistics[op].total); 378c2ecf20Sopenharmony_ci if (mcs_op_statistics[op].max < nsec) 388c2ecf20Sopenharmony_ci mcs_op_statistics[op].max = nsec; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void start_instruction(void *h) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned long *w0 = h; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci wmb(); /* setting CMD/STATUS bits must be last */ 468c2ecf20Sopenharmony_ci *w0 = *w0 | 0x20001; 478c2ecf20Sopenharmony_ci gru_flush_cache(h); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void report_instruction_timeout(void *h) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned long goff = GSEGPOFF((unsigned long)h); 538c2ecf20Sopenharmony_ci char *id = "???"; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (TYPE_IS(CCH, goff)) 568c2ecf20Sopenharmony_ci id = "CCH"; 578c2ecf20Sopenharmony_ci else if (TYPE_IS(TGH, goff)) 588c2ecf20Sopenharmony_ci id = "TGH"; 598c2ecf20Sopenharmony_ci else if (TYPE_IS(TFH, goff)) 608c2ecf20Sopenharmony_ci id = "TFH"; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int wait_instruction_complete(void *h, enum mcs_op opc) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int status; 688c2ecf20Sopenharmony_ci unsigned long start_time = get_cycles(); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci while (1) { 718c2ecf20Sopenharmony_ci cpu_relax(); 728c2ecf20Sopenharmony_ci status = GET_MSEG_HANDLE_STATUS(h); 738c2ecf20Sopenharmony_ci if (status != CCHSTATUS_ACTIVE) 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) { 768c2ecf20Sopenharmony_ci report_instruction_timeout(h); 778c2ecf20Sopenharmony_ci start_time = get_cycles(); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci if (gru_options & OPT_STATS) 818c2ecf20Sopenharmony_ci update_mcs_stats(opc, get_cycles() - start_time); 828c2ecf20Sopenharmony_ci return status; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciint cch_allocate(struct gru_context_configuration_handle *cch) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci cch->opc = CCHOP_ALLOCATE; 908c2ecf20Sopenharmony_ci start_instruction(cch); 918c2ecf20Sopenharmony_ci ret = wait_instruction_complete(cch, cchop_allocate); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Stop speculation into the GSEG being mapped by the previous ALLOCATE. 958c2ecf20Sopenharmony_ci * The GSEG memory does not exist until the ALLOCATE completes. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci sync_core(); 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciint cch_start(struct gru_context_configuration_handle *cch) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci cch->opc = CCHOP_START; 1048c2ecf20Sopenharmony_ci start_instruction(cch); 1058c2ecf20Sopenharmony_ci return wait_instruction_complete(cch, cchop_start); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ciint cch_interrupt(struct gru_context_configuration_handle *cch) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci cch->opc = CCHOP_INTERRUPT; 1118c2ecf20Sopenharmony_ci start_instruction(cch); 1128c2ecf20Sopenharmony_ci return wait_instruction_complete(cch, cchop_interrupt); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciint cch_deallocate(struct gru_context_configuration_handle *cch) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci cch->opc = CCHOP_DEALLOCATE; 1208c2ecf20Sopenharmony_ci start_instruction(cch); 1218c2ecf20Sopenharmony_ci ret = wait_instruction_complete(cch, cchop_deallocate); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * Stop speculation into the GSEG being unmapped by the previous 1258c2ecf20Sopenharmony_ci * DEALLOCATE. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci sync_core(); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint cch_interrupt_sync(struct gru_context_configuration_handle 1328c2ecf20Sopenharmony_ci *cch) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci cch->opc = CCHOP_INTERRUPT_SYNC; 1358c2ecf20Sopenharmony_ci start_instruction(cch); 1368c2ecf20Sopenharmony_ci return wait_instruction_complete(cch, cchop_interrupt_sync); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint tgh_invalidate(struct gru_tlb_global_handle *tgh, 1408c2ecf20Sopenharmony_ci unsigned long vaddr, unsigned long vaddrmask, 1418c2ecf20Sopenharmony_ci int asid, int pagesize, int global, int n, 1428c2ecf20Sopenharmony_ci unsigned short ctxbitmap) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci tgh->vaddr = vaddr; 1458c2ecf20Sopenharmony_ci tgh->asid = asid; 1468c2ecf20Sopenharmony_ci tgh->pagesize = pagesize; 1478c2ecf20Sopenharmony_ci tgh->n = n; 1488c2ecf20Sopenharmony_ci tgh->global = global; 1498c2ecf20Sopenharmony_ci tgh->vaddrmask = vaddrmask; 1508c2ecf20Sopenharmony_ci tgh->ctxbitmap = ctxbitmap; 1518c2ecf20Sopenharmony_ci tgh->opc = TGHOP_TLBINV; 1528c2ecf20Sopenharmony_ci start_instruction(tgh); 1538c2ecf20Sopenharmony_ci return wait_instruction_complete(tgh, tghop_invalidate); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciint tfh_write_only(struct gru_tlb_fault_handle *tfh, 1578c2ecf20Sopenharmony_ci unsigned long paddr, int gaa, 1588c2ecf20Sopenharmony_ci unsigned long vaddr, int asid, int dirty, 1598c2ecf20Sopenharmony_ci int pagesize) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci tfh->fillasid = asid; 1628c2ecf20Sopenharmony_ci tfh->fillvaddr = vaddr; 1638c2ecf20Sopenharmony_ci tfh->pfn = paddr >> GRU_PADDR_SHIFT; 1648c2ecf20Sopenharmony_ci tfh->gaa = gaa; 1658c2ecf20Sopenharmony_ci tfh->dirty = dirty; 1668c2ecf20Sopenharmony_ci tfh->pagesize = pagesize; 1678c2ecf20Sopenharmony_ci tfh->opc = TFHOP_WRITE_ONLY; 1688c2ecf20Sopenharmony_ci start_instruction(tfh); 1698c2ecf20Sopenharmony_ci return wait_instruction_complete(tfh, tfhop_write_only); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_civoid tfh_write_restart(struct gru_tlb_fault_handle *tfh, 1738c2ecf20Sopenharmony_ci unsigned long paddr, int gaa, 1748c2ecf20Sopenharmony_ci unsigned long vaddr, int asid, int dirty, 1758c2ecf20Sopenharmony_ci int pagesize) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci tfh->fillasid = asid; 1788c2ecf20Sopenharmony_ci tfh->fillvaddr = vaddr; 1798c2ecf20Sopenharmony_ci tfh->pfn = paddr >> GRU_PADDR_SHIFT; 1808c2ecf20Sopenharmony_ci tfh->gaa = gaa; 1818c2ecf20Sopenharmony_ci tfh->dirty = dirty; 1828c2ecf20Sopenharmony_ci tfh->pagesize = pagesize; 1838c2ecf20Sopenharmony_ci tfh->opc = TFHOP_WRITE_RESTART; 1848c2ecf20Sopenharmony_ci start_instruction(tfh); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_civoid tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci tfh->opc = TFHOP_USER_POLLING_MODE; 1908c2ecf20Sopenharmony_ci start_instruction(tfh); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid tfh_exception(struct gru_tlb_fault_handle *tfh) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci tfh->opc = TFHOP_EXCEPTION; 1968c2ecf20Sopenharmony_ci start_instruction(tfh); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 199