xref: /linux-6.15/arch/s390/kernel/ctlreg.c (revision cce2c860)
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 
22 void system_ctlreg_lock(void)
23 	__acquires(&system_ctl_lock)
24 {
25 	spin_lock(&system_ctl_lock);
26 }
27 
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 
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 ctl_bit_parms {
48 	unsigned long orval;
49 	unsigned long andval;
50 	int cr;
51 };
52 
53 static void ctl_bit_callback(void *info)
54 {
55 	struct ctl_bit_parms *pp = info;
56 	struct ctlreg regs[16];
57 
58 	__local_ctl_store(0, 15, regs);
59 	regs[pp->cr].val &= pp->andval;
60 	regs[pp->cr].val |= pp->orval;
61 	__local_ctl_load(0, 15, regs);
62 }
63 
64 static void system_ctl_bit_update(void *info)
65 {
66 	unsigned long flags;
67 
68 	if (system_state == SYSTEM_BOOTING) {
69 		/*
70 		 * For very early calls do not call on_each_cpu()
71 		 * since not everything might be setup.
72 		 */
73 		local_irq_save(flags);
74 		ctl_bit_callback(info);
75 		local_irq_restore(flags);
76 	} else {
77 		on_each_cpu(ctl_bit_callback, info, 1);
78 	}
79 }
80 
81 void system_ctl_set_clear_bit(unsigned int cr, unsigned int bit, bool set)
82 {
83 	struct ctl_bit_parms pp = { .cr = cr, };
84 	struct lowcore *abs_lc;
85 
86 	pp.orval  = set ? 1UL << bit : 0;
87 	pp.andval = set ? -1UL : ~(1UL << bit);
88 	if (system_ctlreg_area_init) {
89 		system_ctlreg_lock();
90 		abs_lc = get_abs_lowcore();
91 		abs_lc->cregs_save_area[cr].val &= pp.andval;
92 		abs_lc->cregs_save_area[cr].val |= pp.orval;
93 		put_abs_lowcore(abs_lc);
94 		system_ctl_bit_update(&pp);
95 		system_ctlreg_unlock();
96 	} else {
97 		system_ctl_bit_update(&pp);
98 	}
99 }
100 EXPORT_SYMBOL(system_ctl_set_clear_bit);
101