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