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