122 lines · 2.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *	Copyright IBM Corp. 1999, 2023 */ #include <linux/irqflags.h>#include <linux/spinlock.h>#include <linux/export.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/cache.h>#include <asm/abs_lowcore.h>#include <asm/ctlreg.h> /* * ctl_lock guards access to global control register contents which * are kept in the control register save area within absolute lowcore * at physical address zero. */static DEFINE_SPINLOCK(system_ctl_lock); void system_ctlreg_lock(void)	__acquires(&system_ctl_lock){	spin_lock(&system_ctl_lock);} void system_ctlreg_unlock(void)	__releases(&system_ctl_lock){	spin_unlock(&system_ctl_lock);} static bool system_ctlreg_area_init __ro_after_init; void __init system_ctlreg_init_save_area(struct lowcore *lc){	struct lowcore *abs_lc; 	abs_lc = get_abs_lowcore();	__local_ctl_store(0, 15, lc->cregs_save_area);	__local_ctl_store(0, 15, abs_lc->cregs_save_area);	put_abs_lowcore(abs_lc);	system_ctlreg_area_init = true;} struct ctlreg_parms {	unsigned long andval;	unsigned long orval;	unsigned long val;	int request;	int cr;}; static void ctlreg_callback(void *info){	struct ctlreg_parms *pp = info;	struct ctlreg regs[16]; 	__local_ctl_store(0, 15, regs);	if (pp->request == CTLREG_LOAD) {		regs[pp->cr].val = pp->val;	} else {		regs[pp->cr].val &= pp->andval;		regs[pp->cr].val |= pp->orval;	}	__local_ctl_load(0, 15, regs);} static void system_ctlreg_update(void *info){	unsigned long flags; 	if (system_state == SYSTEM_BOOTING) {		/*		 * For very early calls do not call on_each_cpu()		 * since not everything might be setup.		 */		local_irq_save(flags);		ctlreg_callback(info);		local_irq_restore(flags);	} else {		on_each_cpu(ctlreg_callback, info, 1);	}} void system_ctlreg_modify(unsigned int cr, unsigned long data, int request){	struct ctlreg_parms pp = { .cr = cr, .request = request, };	struct lowcore *abs_lc; 	switch (request) {	case CTLREG_SET_BIT:		pp.orval  = 1UL << data;		pp.andval = -1UL;		break;	case CTLREG_CLEAR_BIT:		pp.orval  = 0;		pp.andval = ~(1UL << data);		break;	case CTLREG_LOAD:		pp.val = data;		break;	}	if (system_ctlreg_area_init) {		system_ctlreg_lock();		abs_lc = get_abs_lowcore();		if (request == CTLREG_LOAD) {			abs_lc->cregs_save_area[cr].val = pp.val;		} else {			abs_lc->cregs_save_area[cr].val &= pp.andval;			abs_lc->cregs_save_area[cr].val |= pp.orval;		}		put_abs_lowcore(abs_lc);		system_ctlreg_update(&pp);		system_ctlreg_unlock();	} else {		system_ctlreg_update(&pp);	}}EXPORT_SYMBOL(system_ctlreg_modify);