xref: /linux-6.15/arch/s390/kernel/ctlreg.c (revision 527618ab)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *	Copyright IBM Corp. 1999, 2023
4  */
5 
6 #include <linux/spinlock.h>
7 #include <linux/smp.h>
8 #include <asm/abs_lowcore.h>
9 #include <asm/ctlreg.h>
10 
11 /*
12  * ctl_lock guards access to global control register contents which
13  * are kept in the control register save area within absolute lowcore
14  * at physical address zero.
15  */
16 static DEFINE_SPINLOCK(system_ctl_lock);
17 
18 void system_ctlreg_lock(void)
19 	__acquires(&system_ctl_lock)
20 {
21 	spin_lock(&system_ctl_lock);
22 }
23 
24 void system_ctlreg_unlock(void)
25 	__releases(&system_ctl_lock)
26 {
27 	spin_unlock(&system_ctl_lock);
28 }
29 
30 struct ctl_bit_parms {
31 	unsigned long orval;
32 	unsigned long andval;
33 	int cr;
34 };
35 
36 static void ctl_bit_callback(void *info)
37 {
38 	struct ctl_bit_parms *pp = info;
39 	struct ctlreg regs[16];
40 
41 	__local_ctl_store(0, 15, regs);
42 	regs[pp->cr].val &= pp->andval;
43 	regs[pp->cr].val |= pp->orval;
44 	__local_ctl_load(0, 15, regs);
45 }
46 
47 void system_ctl_set_clear_bit(unsigned int cr, unsigned int bit, bool set)
48 {
49 	struct ctl_bit_parms pp = { .cr = cr, };
50 	struct lowcore *abs_lc;
51 
52 	pp.orval  = set ? 1UL << bit : 0;
53 	pp.andval = set ? -1UL : ~(1UL << bit);
54 	system_ctlreg_lock();
55 	abs_lc = get_abs_lowcore();
56 	abs_lc->cregs_save_area[cr].val &= pp.andval;
57 	abs_lc->cregs_save_area[cr].val |= pp.orval;
58 	put_abs_lowcore(abs_lc);
59 	on_each_cpu(ctl_bit_callback, &pp, 1);
60 	system_ctlreg_unlock();
61 }
62 EXPORT_SYMBOL(system_ctl_set_clear_bit);
63