| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright IBM Corp. 1999, 2023
- */
- #include <linux/irqflags.h>
- #include <linux/spinlock.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);
|