Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/platform/uv/uv_irq.c
26471 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* SGI UV IRQ functions
7
*
8
* Copyright (C) 2008 Silicon Graphics, Inc. All rights reserved.
9
*/
10
11
#include <linux/export.h>
12
#include <linux/rbtree.h>
13
#include <linux/slab.h>
14
#include <linux/irq.h>
15
16
#include <asm/irqdomain.h>
17
#include <asm/apic.h>
18
#include <asm/uv/uv_irq.h>
19
#include <asm/uv/uv_hub.h>
20
21
/* MMR offset and pnode of hub sourcing interrupts for a given irq */
22
struct uv_irq_2_mmr_pnode {
23
unsigned long offset;
24
int pnode;
25
};
26
27
static void uv_program_mmr(struct irq_cfg *cfg, struct uv_irq_2_mmr_pnode *info)
28
{
29
unsigned long mmr_value;
30
struct uv_IO_APIC_route_entry *entry;
31
32
BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
33
sizeof(unsigned long));
34
35
mmr_value = 0;
36
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
37
entry->vector = cfg->vector;
38
entry->delivery_mode = APIC_DELIVERY_MODE_FIXED;
39
entry->dest_mode = apic->dest_mode_logical;
40
entry->polarity = 0;
41
entry->trigger = 0;
42
entry->mask = 0;
43
entry->dest = cfg->dest_apicid;
44
45
uv_write_global_mmr64(info->pnode, info->offset, mmr_value);
46
}
47
48
static void uv_noop(struct irq_data *data) { }
49
50
static int
51
uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
52
bool force)
53
{
54
struct irq_data *parent = data->parent_data;
55
struct irq_cfg *cfg = irqd_cfg(data);
56
int ret;
57
58
ret = parent->chip->irq_set_affinity(parent, mask, force);
59
if (ret >= 0) {
60
uv_program_mmr(cfg, data->chip_data);
61
vector_schedule_cleanup(cfg);
62
}
63
64
return ret;
65
}
66
67
static struct irq_chip uv_irq_chip = {
68
.name = "UV-CORE",
69
.irq_mask = uv_noop,
70
.irq_unmask = uv_noop,
71
.irq_eoi = apic_ack_irq,
72
.irq_set_affinity = uv_set_irq_affinity,
73
};
74
75
static int uv_domain_alloc(struct irq_domain *domain, unsigned int virq,
76
unsigned int nr_irqs, void *arg)
77
{
78
struct uv_irq_2_mmr_pnode *chip_data;
79
struct irq_alloc_info *info = arg;
80
struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
81
int ret;
82
83
if (nr_irqs > 1 || !info || info->type != X86_IRQ_ALLOC_TYPE_UV)
84
return -EINVAL;
85
86
chip_data = kmalloc_node(sizeof(*chip_data), GFP_KERNEL,
87
irq_data_get_node(irq_data));
88
if (!chip_data)
89
return -ENOMEM;
90
91
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
92
if (ret >= 0) {
93
if (info->uv.limit == UV_AFFINITY_CPU)
94
irq_set_status_flags(virq, IRQ_NO_BALANCING);
95
96
chip_data->pnode = uv_blade_to_pnode(info->uv.blade);
97
chip_data->offset = info->uv.offset;
98
irq_domain_set_info(domain, virq, virq, &uv_irq_chip, chip_data,
99
handle_percpu_irq, NULL, info->uv.name);
100
} else {
101
kfree(chip_data);
102
}
103
104
return ret;
105
}
106
107
static void uv_domain_free(struct irq_domain *domain, unsigned int virq,
108
unsigned int nr_irqs)
109
{
110
struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
111
112
BUG_ON(nr_irqs != 1);
113
kfree(irq_data->chip_data);
114
irq_clear_status_flags(virq, IRQ_NO_BALANCING);
115
irq_domain_free_irqs_top(domain, virq, nr_irqs);
116
}
117
118
/*
119
* Re-target the irq to the specified CPU and enable the specified MMR located
120
* on the specified blade to allow the sending of MSIs to the specified CPU.
121
*/
122
static int uv_domain_activate(struct irq_domain *domain,
123
struct irq_data *irq_data, bool reserve)
124
{
125
uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data);
126
return 0;
127
}
128
129
/*
130
* Disable the specified MMR located on the specified blade so that MSIs are
131
* longer allowed to be sent.
132
*/
133
static void uv_domain_deactivate(struct irq_domain *domain,
134
struct irq_data *irq_data)
135
{
136
unsigned long mmr_value;
137
struct uv_IO_APIC_route_entry *entry;
138
139
mmr_value = 0;
140
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
141
entry->mask = 1;
142
uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data);
143
}
144
145
static const struct irq_domain_ops uv_domain_ops = {
146
.alloc = uv_domain_alloc,
147
.free = uv_domain_free,
148
.activate = uv_domain_activate,
149
.deactivate = uv_domain_deactivate,
150
};
151
152
static struct irq_domain *uv_get_irq_domain(void)
153
{
154
static struct irq_domain *uv_domain;
155
static DEFINE_MUTEX(uv_lock);
156
struct fwnode_handle *fn;
157
158
mutex_lock(&uv_lock);
159
if (uv_domain)
160
goto out;
161
162
fn = irq_domain_alloc_named_fwnode("UV-CORE");
163
if (!fn)
164
goto out;
165
166
uv_domain = irq_domain_create_hierarchy(x86_vector_domain, 0, 0, fn,
167
&uv_domain_ops, NULL);
168
if (!uv_domain)
169
irq_domain_free_fwnode(fn);
170
out:
171
mutex_unlock(&uv_lock);
172
173
return uv_domain;
174
}
175
176
/*
177
* Set up a mapping of an available irq and vector, and enable the specified
178
* MMR that defines the MSI that is to be sent to the specified CPU when an
179
* interrupt is raised.
180
*/
181
int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
182
unsigned long mmr_offset, int limit)
183
{
184
struct irq_alloc_info info;
185
struct irq_domain *domain = uv_get_irq_domain();
186
187
if (!domain)
188
return -ENOMEM;
189
190
init_irq_alloc_info(&info, cpumask_of(cpu));
191
info.type = X86_IRQ_ALLOC_TYPE_UV;
192
info.uv.limit = limit;
193
info.uv.blade = mmr_blade;
194
info.uv.offset = mmr_offset;
195
info.uv.name = irq_name;
196
197
return irq_domain_alloc_irqs(domain, 1,
198
uv_blade_to_memory_nid(mmr_blade), &info);
199
}
200
EXPORT_SYMBOL_GPL(uv_setup_irq);
201
202
/*
203
* Tear down a mapping of an irq and vector, and disable the specified MMR that
204
* defined the MSI that was to be sent to the specified CPU when an interrupt
205
* was raised.
206
*
207
* Set mmr_blade and mmr_offset to what was passed in on uv_setup_irq().
208
*/
209
void uv_teardown_irq(unsigned int irq)
210
{
211
irq_domain_free_irqs(irq, 1);
212
}
213
EXPORT_SYMBOL_GPL(uv_teardown_irq);
214
215