11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a24e5e1cSJack Steiner /*
3a24e5e1cSJack Steiner * GRU KERNEL MCS INSTRUCTIONS
4a24e5e1cSJack Steiner *
5a24e5e1cSJack Steiner * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
6a24e5e1cSJack Steiner */
7a24e5e1cSJack Steiner
8a24e5e1cSJack Steiner #include <linux/kernel.h>
9a24e5e1cSJack Steiner #include "gru.h"
10a24e5e1cSJack Steiner #include "grulib.h"
11a24e5e1cSJack Steiner #include "grutables.h"
12a24e5e1cSJack Steiner
13a24e5e1cSJack Steiner /* 10 sec */
14a24e5e1cSJack Steiner #ifdef CONFIG_IA64
15a24e5e1cSJack Steiner #include <asm/processor.h>
16a24e5e1cSJack Steiner #define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10)
17563447d7SJack Steiner #define CLKS2NSEC(c) ((c) *1000000000 / local_cpu_data->itc_freq)
18a24e5e1cSJack Steiner #else
19*9998a983SRicardo Neri #include <linux/sync_core.h>
20a24e5e1cSJack Steiner #include <asm/tsc.h>
21a24e5e1cSJack Steiner #define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
22563447d7SJack Steiner #define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz)
23a24e5e1cSJack Steiner #endif
24a24e5e1cSJack Steiner
25a24e5e1cSJack Steiner /* Extract the status field from a kernel handle */
26a24e5e1cSJack Steiner #define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3)
27a24e5e1cSJack Steiner
28e56484daSJack Steiner struct mcs_op_statistic mcs_op_statistics[mcsop_last];
29e56484daSJack Steiner
update_mcs_stats(enum mcs_op op,unsigned long clks)30e56484daSJack Steiner static void update_mcs_stats(enum mcs_op op, unsigned long clks)
31e56484daSJack Steiner {
32563447d7SJack Steiner unsigned long nsec;
33563447d7SJack Steiner
34563447d7SJack Steiner nsec = CLKS2NSEC(clks);
35e56484daSJack Steiner atomic_long_inc(&mcs_op_statistics[op].count);
36563447d7SJack Steiner atomic_long_add(nsec, &mcs_op_statistics[op].total);
37563447d7SJack Steiner if (mcs_op_statistics[op].max < nsec)
38563447d7SJack Steiner mcs_op_statistics[op].max = nsec;
39e56484daSJack Steiner }
40e56484daSJack Steiner
start_instruction(void * h)41a24e5e1cSJack Steiner static void start_instruction(void *h)
42a24e5e1cSJack Steiner {
43a24e5e1cSJack Steiner unsigned long *w0 = h;
44a24e5e1cSJack Steiner
4557ebb034SJack Steiner wmb(); /* setting CMD/STATUS bits must be last */
4657ebb034SJack Steiner *w0 = *w0 | 0x20001;
47a24e5e1cSJack Steiner gru_flush_cache(h);
48a24e5e1cSJack Steiner }
49a24e5e1cSJack Steiner
report_instruction_timeout(void * h)50648eb8e5SJack Steiner static void report_instruction_timeout(void *h)
51648eb8e5SJack Steiner {
52648eb8e5SJack Steiner unsigned long goff = GSEGPOFF((unsigned long)h);
53648eb8e5SJack Steiner char *id = "???";
54648eb8e5SJack Steiner
55648eb8e5SJack Steiner if (TYPE_IS(CCH, goff))
56648eb8e5SJack Steiner id = "CCH";
57648eb8e5SJack Steiner else if (TYPE_IS(TGH, goff))
58648eb8e5SJack Steiner id = "TGH";
59648eb8e5SJack Steiner else if (TYPE_IS(TFH, goff))
60648eb8e5SJack Steiner id = "TFH";
61648eb8e5SJack Steiner
62648eb8e5SJack Steiner panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id);
63648eb8e5SJack Steiner }
64648eb8e5SJack Steiner
wait_instruction_complete(void * h,enum mcs_op opc)65a24e5e1cSJack Steiner static int wait_instruction_complete(void *h, enum mcs_op opc)
66a24e5e1cSJack Steiner {
67a24e5e1cSJack Steiner int status;
68836ce679SJack Steiner unsigned long start_time = get_cycles();
69a24e5e1cSJack Steiner
70a24e5e1cSJack Steiner while (1) {
71a24e5e1cSJack Steiner cpu_relax();
72a24e5e1cSJack Steiner status = GET_MSEG_HANDLE_STATUS(h);
73a24e5e1cSJack Steiner if (status != CCHSTATUS_ACTIVE)
74a24e5e1cSJack Steiner break;
75648eb8e5SJack Steiner if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
76648eb8e5SJack Steiner report_instruction_timeout(h);
77648eb8e5SJack Steiner start_time = get_cycles();
78648eb8e5SJack Steiner }
79a24e5e1cSJack Steiner }
80e56484daSJack Steiner if (gru_options & OPT_STATS)
81e56484daSJack Steiner update_mcs_stats(opc, get_cycles() - start_time);
82a24e5e1cSJack Steiner return status;
83a24e5e1cSJack Steiner }
84a24e5e1cSJack Steiner
cch_allocate(struct gru_context_configuration_handle * cch)856e910074SJack Steiner int cch_allocate(struct gru_context_configuration_handle *cch)
86a24e5e1cSJack Steiner {
8767bf04a5SJack Steiner int ret;
8867bf04a5SJack Steiner
89a24e5e1cSJack Steiner cch->opc = CCHOP_ALLOCATE;
90a24e5e1cSJack Steiner start_instruction(cch);
9167bf04a5SJack Steiner ret = wait_instruction_complete(cch, cchop_allocate);
9267bf04a5SJack Steiner
9367bf04a5SJack Steiner /*
9467bf04a5SJack Steiner * Stop speculation into the GSEG being mapped by the previous ALLOCATE.
9567bf04a5SJack Steiner * The GSEG memory does not exist until the ALLOCATE completes.
9667bf04a5SJack Steiner */
9767bf04a5SJack Steiner sync_core();
9867bf04a5SJack Steiner return ret;
99a24e5e1cSJack Steiner }
100a24e5e1cSJack Steiner
cch_start(struct gru_context_configuration_handle * cch)101a24e5e1cSJack Steiner int cch_start(struct gru_context_configuration_handle *cch)
102a24e5e1cSJack Steiner {
103a24e5e1cSJack Steiner cch->opc = CCHOP_START;
104a24e5e1cSJack Steiner start_instruction(cch);
105a24e5e1cSJack Steiner return wait_instruction_complete(cch, cchop_start);
106a24e5e1cSJack Steiner }
107a24e5e1cSJack Steiner
cch_interrupt(struct gru_context_configuration_handle * cch)108a24e5e1cSJack Steiner int cch_interrupt(struct gru_context_configuration_handle *cch)
109a24e5e1cSJack Steiner {
110a24e5e1cSJack Steiner cch->opc = CCHOP_INTERRUPT;
111a24e5e1cSJack Steiner start_instruction(cch);
112a24e5e1cSJack Steiner return wait_instruction_complete(cch, cchop_interrupt);
113a24e5e1cSJack Steiner }
114a24e5e1cSJack Steiner
cch_deallocate(struct gru_context_configuration_handle * cch)115a24e5e1cSJack Steiner int cch_deallocate(struct gru_context_configuration_handle *cch)
116a24e5e1cSJack Steiner {
11767bf04a5SJack Steiner int ret;
11867bf04a5SJack Steiner
119a24e5e1cSJack Steiner cch->opc = CCHOP_DEALLOCATE;
120a24e5e1cSJack Steiner start_instruction(cch);
12167bf04a5SJack Steiner ret = wait_instruction_complete(cch, cchop_deallocate);
12267bf04a5SJack Steiner
12367bf04a5SJack Steiner /*
12467bf04a5SJack Steiner * Stop speculation into the GSEG being unmapped by the previous
12567bf04a5SJack Steiner * DEALLOCATE.
12667bf04a5SJack Steiner */
12767bf04a5SJack Steiner sync_core();
12867bf04a5SJack Steiner return ret;
129a24e5e1cSJack Steiner }
130a24e5e1cSJack Steiner
cch_interrupt_sync(struct gru_context_configuration_handle * cch)131a24e5e1cSJack Steiner int cch_interrupt_sync(struct gru_context_configuration_handle
132a24e5e1cSJack Steiner *cch)
133a24e5e1cSJack Steiner {
134a24e5e1cSJack Steiner cch->opc = CCHOP_INTERRUPT_SYNC;
135a24e5e1cSJack Steiner start_instruction(cch);
136a24e5e1cSJack Steiner return wait_instruction_complete(cch, cchop_interrupt_sync);
137a24e5e1cSJack Steiner }
138a24e5e1cSJack Steiner
tgh_invalidate(struct gru_tlb_global_handle * tgh,unsigned long vaddr,unsigned long vaddrmask,int asid,int pagesize,int global,int n,unsigned short ctxbitmap)139a24e5e1cSJack Steiner int tgh_invalidate(struct gru_tlb_global_handle *tgh,
140a24e5e1cSJack Steiner unsigned long vaddr, unsigned long vaddrmask,
141a24e5e1cSJack Steiner int asid, int pagesize, int global, int n,
142a24e5e1cSJack Steiner unsigned short ctxbitmap)
143a24e5e1cSJack Steiner {
144a24e5e1cSJack Steiner tgh->vaddr = vaddr;
145a24e5e1cSJack Steiner tgh->asid = asid;
146a24e5e1cSJack Steiner tgh->pagesize = pagesize;
147a24e5e1cSJack Steiner tgh->n = n;
148a24e5e1cSJack Steiner tgh->global = global;
149a24e5e1cSJack Steiner tgh->vaddrmask = vaddrmask;
150a24e5e1cSJack Steiner tgh->ctxbitmap = ctxbitmap;
151a24e5e1cSJack Steiner tgh->opc = TGHOP_TLBINV;
152a24e5e1cSJack Steiner start_instruction(tgh);
153a24e5e1cSJack Steiner return wait_instruction_complete(tgh, tghop_invalidate);
154a24e5e1cSJack Steiner }
155a24e5e1cSJack Steiner
tfh_write_only(struct gru_tlb_fault_handle * tfh,unsigned long paddr,int gaa,unsigned long vaddr,int asid,int dirty,int pagesize)156c550222fSJack Steiner int tfh_write_only(struct gru_tlb_fault_handle *tfh,
157c550222fSJack Steiner unsigned long paddr, int gaa,
158c550222fSJack Steiner unsigned long vaddr, int asid, int dirty,
159c550222fSJack Steiner int pagesize)
160a24e5e1cSJack Steiner {
161a24e5e1cSJack Steiner tfh->fillasid = asid;
162a24e5e1cSJack Steiner tfh->fillvaddr = vaddr;
163c550222fSJack Steiner tfh->pfn = paddr >> GRU_PADDR_SHIFT;
164c550222fSJack Steiner tfh->gaa = gaa;
165a24e5e1cSJack Steiner tfh->dirty = dirty;
166a24e5e1cSJack Steiner tfh->pagesize = pagesize;
167a24e5e1cSJack Steiner tfh->opc = TFHOP_WRITE_ONLY;
168a24e5e1cSJack Steiner start_instruction(tfh);
169c550222fSJack Steiner return wait_instruction_complete(tfh, tfhop_write_only);
170a24e5e1cSJack Steiner }
171a24e5e1cSJack Steiner
tfh_write_restart(struct gru_tlb_fault_handle * tfh,unsigned long paddr,int gaa,unsigned long vaddr,int asid,int dirty,int pagesize)172a24e5e1cSJack Steiner void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
173a24e5e1cSJack Steiner unsigned long paddr, int gaa,
174a24e5e1cSJack Steiner unsigned long vaddr, int asid, int dirty,
175a24e5e1cSJack Steiner int pagesize)
176a24e5e1cSJack Steiner {
177a24e5e1cSJack Steiner tfh->fillasid = asid;
178a24e5e1cSJack Steiner tfh->fillvaddr = vaddr;
179a24e5e1cSJack Steiner tfh->pfn = paddr >> GRU_PADDR_SHIFT;
180a24e5e1cSJack Steiner tfh->gaa = gaa;
181a24e5e1cSJack Steiner tfh->dirty = dirty;
182a24e5e1cSJack Steiner tfh->pagesize = pagesize;
183a24e5e1cSJack Steiner tfh->opc = TFHOP_WRITE_RESTART;
184a24e5e1cSJack Steiner start_instruction(tfh);
185a24e5e1cSJack Steiner }
186a24e5e1cSJack Steiner
tfh_user_polling_mode(struct gru_tlb_fault_handle * tfh)187a24e5e1cSJack Steiner void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh)
188a24e5e1cSJack Steiner {
189a24e5e1cSJack Steiner tfh->opc = TFHOP_USER_POLLING_MODE;
190a24e5e1cSJack Steiner start_instruction(tfh);
191a24e5e1cSJack Steiner }
192a24e5e1cSJack Steiner
tfh_exception(struct gru_tlb_fault_handle * tfh)193a24e5e1cSJack Steiner void tfh_exception(struct gru_tlb_fault_handle *tfh)
194a24e5e1cSJack Steiner {
195a24e5e1cSJack Steiner tfh->opc = TFHOP_EXCEPTION;
196a24e5e1cSJack Steiner start_instruction(tfh);
197a24e5e1cSJack Steiner }
198a24e5e1cSJack Steiner
199