xref: /linux-6.15/arch/s390/kernel/ctlreg.c (revision ebe1cd53)
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(ctl_lock);
17 
18 void ctlreg_lock(void)
19 	__acquires(&ctl_lock)
20 {
21 	spin_lock(&ctl_lock);
22 }
23 
24 void ctlreg_unlock(void)
25 	__releases(&ctl_lock)
26 {
27 	spin_unlock(&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 	unsigned long regs[16];
40 
41 	__ctl_store(regs, 0, 15);
42 	regs[pp->cr] &= pp->andval;
43 	regs[pp->cr] |= pp->orval;
44 	__ctl_load(regs, 0, 15);
45 }
46 
47 void ctl_set_clear_bit(int cr, 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 	ctlreg_lock();
55 	abs_lc = get_abs_lowcore();
56 	abs_lc->cregs_save_area[cr] &= pp.andval;
57 	abs_lc->cregs_save_area[cr] |= pp.orval;
58 	put_abs_lowcore(abs_lc);
59 	on_each_cpu(ctl_bit_callback, &pp, 1);
60 	ctlreg_unlock();
61 }
62 EXPORT_SYMBOL(ctl_set_clear_bit);
63