Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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 */
 20static DEFINE_SPINLOCK(system_ctl_lock);
 21
 22void system_ctlreg_lock(void)
 23	__acquires(&system_ctl_lock)
 24{
 25	spin_lock(&system_ctl_lock);
 26}
 27
 28void system_ctlreg_unlock(void)
 29	__releases(&system_ctl_lock)
 30{
 31	spin_unlock(&system_ctl_lock);
 32}
 33
 34static bool system_ctlreg_area_init __ro_after_init;
 35
 36void __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
 47struct ctlreg_parms {
 48	unsigned long andval;
 49	unsigned long orval;
 50	unsigned long val;
 51	int request;
 52	int cr;
 53};
 54
 55static void ctlreg_callback(void *info)
 56{
 57	struct ctlreg_parms *pp = info;
 58	struct ctlreg regs[16];
 59
 60	__local_ctl_store(0, 15, regs);
 61	if (pp->request == CTLREG_LOAD) {
 62		regs[pp->cr].val = pp->val;
 63	} else {
 64		regs[pp->cr].val &= pp->andval;
 65		regs[pp->cr].val |= pp->orval;
 66	}
 67	__local_ctl_load(0, 15, regs);
 68}
 69
 70static void system_ctlreg_update(void *info)
 71{
 72	unsigned long flags;
 73
 74	if (system_state == SYSTEM_BOOTING) {
 75		/*
 76		 * For very early calls do not call on_each_cpu()
 77		 * since not everything might be setup.
 78		 */
 79		local_irq_save(flags);
 80		ctlreg_callback(info);
 81		local_irq_restore(flags);
 82	} else {
 83		on_each_cpu(ctlreg_callback, info, 1);
 84	}
 85}
 86
 87void system_ctlreg_modify(unsigned int cr, unsigned long data, int request)
 88{
 89	struct ctlreg_parms pp = { .cr = cr, .request = request, };
 90	struct lowcore *abs_lc;
 91
 92	switch (request) {
 93	case CTLREG_SET_BIT:
 94		pp.orval  = 1UL << data;
 95		pp.andval = -1UL;
 96		break;
 97	case CTLREG_CLEAR_BIT:
 98		pp.orval  = 0;
 99		pp.andval = ~(1UL << data);
100		break;
101	case CTLREG_LOAD:
102		pp.val = data;
103		break;
104	}
105	if (system_ctlreg_area_init) {
106		system_ctlreg_lock();
107		abs_lc = get_abs_lowcore();
108		if (request == CTLREG_LOAD) {
109			abs_lc->cregs_save_area[cr].val = pp.val;
110		} else {
111			abs_lc->cregs_save_area[cr].val &= pp.andval;
112			abs_lc->cregs_save_area[cr].val |= pp.orval;
113		}
114		put_abs_lowcore(abs_lc);
115		system_ctlreg_update(&pp);
116		system_ctlreg_unlock();
117	} else {
118		system_ctlreg_update(&pp);
119	}
120}
121EXPORT_SYMBOL(system_ctlreg_modify);