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(lock: &system_ctl_lock); |
26 | } |
27 | |
28 | void system_ctlreg_unlock(void) |
29 | __releases(&system_ctl_lock) |
30 | { |
31 | spin_unlock(lock: &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 ctlreg_parms { |
48 | unsigned long andval; |
49 | unsigned long orval; |
50 | unsigned long val; |
51 | int request; |
52 | int cr; |
53 | }; |
54 | |
55 | static 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 | |
70 | static 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(func: ctlreg_callback, info, wait: 1); |
84 | } |
85 | } |
86 | |
87 | void 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(info: &pp); |
116 | system_ctlreg_unlock(); |
117 | } else { |
118 | system_ctlreg_update(info: &pp); |
119 | } |
120 | } |
121 | EXPORT_SYMBOL(system_ctlreg_modify); |
122 | |