1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *	Copyright IBM Corp. 1999, 2023
4  */
5 
6 #include <linux/irqflags.h>
7 #include <linux/spinlock.h>
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/smp.h>
11 #include <linux/cache.h>
12 #include <asm/abs_lowcore.h>
13 #include <asm/ctlreg.h>
14 
15 /*
16  * ctl_lock guards access to global control register contents which
17  * are kept in the control register save area within absolute lowcore
18  * at physical address zero.
19  */
20 static DEFINE_SPINLOCK(system_ctl_lock);
21 
system_ctlreg_lock(void)22 void system_ctlreg_lock(void)
23 	__acquires(&system_ctl_lock)
24 {
25 	spin_lock(&system_ctl_lock);
26 }
27 
system_ctlreg_unlock(void)28 void system_ctlreg_unlock(void)
29 	__releases(&system_ctl_lock)
30 {
31 	spin_unlock(&system_ctl_lock);
32 }
33 
34 static bool system_ctlreg_area_init __ro_after_init;
35 
system_ctlreg_init_save_area(struct lowcore * lc)36 void __init system_ctlreg_init_save_area(struct lowcore *lc)
37 {
38 	struct lowcore *abs_lc;
39 
40 	abs_lc = get_abs_lowcore();
41 	__local_ctl_store(0, 15, lc->cregs_save_area);
42 	__local_ctl_store(0, 15, abs_lc->cregs_save_area);
43 	put_abs_lowcore(abs_lc);
44 	system_ctlreg_area_init = true;
45 }
46 
47 struct ctlreg_parms {
48 	unsigned long andval;
49 	unsigned long orval;
50 	unsigned long val;
51 	int request;
52 	int cr;
53 };
54 
ctlreg_callback(void * info)55 static void ctlreg_callback(void *info)
56 {
57 	struct ctlreg_parms *pp = info;
58 	struct ctlreg regs[16];
59 
60 	__local_ctl_store(0, 15, regs);
61 	if (pp->request == CTLREG_LOAD) {
62 		regs[pp->cr].val = pp->val;
63 	} else {
64 		regs[pp->cr].val &= pp->andval;
65 		regs[pp->cr].val |= pp->orval;
66 	}
67 	__local_ctl_load(0, 15, regs);
68 }
69 
system_ctlreg_update(void * info)70 static void system_ctlreg_update(void *info)
71 {
72 	unsigned long flags;
73 
74 	if (system_state == SYSTEM_BOOTING) {
75 		/*
76 		 * For very early calls do not call on_each_cpu()
77 		 * since not everything might be setup.
78 		 */
79 		local_irq_save(flags);
80 		ctlreg_callback(info);
81 		local_irq_restore(flags);
82 	} else {
83 		on_each_cpu(ctlreg_callback, info, 1);
84 	}
85 }
86 
system_ctlreg_modify(unsigned int cr,unsigned long data,int request)87 void system_ctlreg_modify(unsigned int cr, unsigned long data, int request)
88 {
89 	struct ctlreg_parms pp = { .cr = cr, .request = request, };
90 	struct lowcore *abs_lc;
91 
92 	switch (request) {
93 	case CTLREG_SET_BIT:
94 		pp.orval  = 1UL << data;
95 		pp.andval = -1UL;
96 		break;
97 	case CTLREG_CLEAR_BIT:
98 		pp.orval  = 0;
99 		pp.andval = ~(1UL << data);
100 		break;
101 	case CTLREG_LOAD:
102 		pp.val = data;
103 		break;
104 	}
105 	if (system_ctlreg_area_init) {
106 		system_ctlreg_lock();
107 		abs_lc = get_abs_lowcore();
108 		if (request == CTLREG_LOAD) {
109 			abs_lc->cregs_save_area[cr].val = pp.val;
110 		} else {
111 			abs_lc->cregs_save_area[cr].val &= pp.andval;
112 			abs_lc->cregs_save_area[cr].val |= pp.orval;
113 		}
114 		put_abs_lowcore(abs_lc);
115 		system_ctlreg_update(&pp);
116 		system_ctlreg_unlock();
117 	} else {
118 		system_ctlreg_update(&pp);
119 	}
120 }
121 EXPORT_SYMBOL(system_ctlreg_modify);
122