Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/ctlreg.c
26442 views
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/export.h>
9
#include <linux/kernel.h>
10
#include <linux/init.h>
11
#include <linux/smp.h>
12
#include <linux/cache.h>
13
#include <asm/abs_lowcore.h>
14
#include <asm/ctlreg.h>
15
16
/*
17
* ctl_lock guards access to global control register contents which
18
* are kept in the control register save area within absolute lowcore
19
* at physical address zero.
20
*/
21
static DEFINE_SPINLOCK(system_ctl_lock);
22
23
void system_ctlreg_lock(void)
24
__acquires(&system_ctl_lock)
25
{
26
spin_lock(&system_ctl_lock);
27
}
28
29
void system_ctlreg_unlock(void)
30
__releases(&system_ctl_lock)
31
{
32
spin_unlock(&system_ctl_lock);
33
}
34
35
static bool system_ctlreg_area_init __ro_after_init;
36
37
void __init system_ctlreg_init_save_area(struct lowcore *lc)
38
{
39
struct lowcore *abs_lc;
40
41
abs_lc = get_abs_lowcore();
42
__local_ctl_store(0, 15, lc->cregs_save_area);
43
__local_ctl_store(0, 15, abs_lc->cregs_save_area);
44
put_abs_lowcore(abs_lc);
45
system_ctlreg_area_init = true;
46
}
47
48
struct ctlreg_parms {
49
unsigned long andval;
50
unsigned long orval;
51
unsigned long val;
52
int request;
53
int cr;
54
};
55
56
static void ctlreg_callback(void *info)
57
{
58
struct ctlreg_parms *pp = info;
59
struct ctlreg regs[16];
60
61
__local_ctl_store(0, 15, regs);
62
if (pp->request == CTLREG_LOAD) {
63
regs[pp->cr].val = pp->val;
64
} else {
65
regs[pp->cr].val &= pp->andval;
66
regs[pp->cr].val |= pp->orval;
67
}
68
__local_ctl_load(0, 15, regs);
69
}
70
71
static void system_ctlreg_update(void *info)
72
{
73
unsigned long flags;
74
75
if (system_state == SYSTEM_BOOTING) {
76
/*
77
* For very early calls do not call on_each_cpu()
78
* since not everything might be setup.
79
*/
80
local_irq_save(flags);
81
ctlreg_callback(info);
82
local_irq_restore(flags);
83
} else {
84
on_each_cpu(ctlreg_callback, info, 1);
85
}
86
}
87
88
void system_ctlreg_modify(unsigned int cr, unsigned long data, int request)
89
{
90
struct ctlreg_parms pp = { .cr = cr, .request = request, };
91
struct lowcore *abs_lc;
92
93
switch (request) {
94
case CTLREG_SET_BIT:
95
pp.orval = 1UL << data;
96
pp.andval = -1UL;
97
break;
98
case CTLREG_CLEAR_BIT:
99
pp.orval = 0;
100
pp.andval = ~(1UL << data);
101
break;
102
case CTLREG_LOAD:
103
pp.val = data;
104
break;
105
}
106
if (system_ctlreg_area_init) {
107
system_ctlreg_lock();
108
abs_lc = get_abs_lowcore();
109
if (request == CTLREG_LOAD) {
110
abs_lc->cregs_save_area[cr].val = pp.val;
111
} else {
112
abs_lc->cregs_save_area[cr].val &= pp.andval;
113
abs_lc->cregs_save_area[cr].val |= pp.orval;
114
}
115
put_abs_lowcore(abs_lc);
116
system_ctlreg_update(&pp);
117
system_ctlreg_unlock();
118
} else {
119
system_ctlreg_update(&pp);
120
}
121
}
122
EXPORT_SYMBOL(system_ctlreg_modify);
123
124