Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/loongarch/kvm/intc/pch_pic.c
29537 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2024 Loongson Technology Corporation Limited
4
*/
5
6
#include <asm/kvm_eiointc.h>
7
#include <asm/kvm_pch_pic.h>
8
#include <asm/kvm_vcpu.h>
9
#include <linux/count_zeros.h>
10
11
/* update the isr according to irq level and route irq to eiointc */
12
static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
13
{
14
u64 mask = BIT(irq);
15
16
/*
17
* set isr and route irq to eiointc and
18
* the route table is in htmsi_vector[]
19
*/
20
if (level) {
21
if (mask & s->irr & ~s->mask) {
22
s->isr |= mask;
23
irq = s->htmsi_vector[irq];
24
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
25
}
26
} else {
27
if (mask & s->isr & ~s->irr) {
28
s->isr &= ~mask;
29
irq = s->htmsi_vector[irq];
30
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
31
}
32
}
33
}
34
35
/* update batch irqs, the irq_mask is a bitmap of irqs */
36
static void pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level)
37
{
38
unsigned int irq;
39
DECLARE_BITMAP(irqs, 64) = { BITMAP_FROM_U64(irq_mask) };
40
41
for_each_set_bit(irq, irqs, 64)
42
pch_pic_update_irq(s, irq, level);
43
}
44
45
/* called when a irq is triggered in pch pic */
46
void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level)
47
{
48
u64 mask = BIT(irq);
49
50
spin_lock(&s->lock);
51
if (level)
52
s->irr |= mask; /* set irr */
53
else {
54
/*
55
* In edge triggered mode, 0 does not mean to clear irq
56
* The irr register variable is cleared when cpu writes to the
57
* PCH_PIC_CLEAR_START address area
58
*/
59
if (s->edge & mask) {
60
spin_unlock(&s->lock);
61
return;
62
}
63
s->irr &= ~mask;
64
}
65
pch_pic_update_irq(s, irq, level);
66
spin_unlock(&s->lock);
67
}
68
69
/* msi irq handler */
70
void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
71
{
72
eiointc_set_irq(kvm->arch.eiointc, irq, level);
73
}
74
75
static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
76
{
77
int ret = 0, offset;
78
u64 data = 0;
79
void *ptemp;
80
81
offset = addr - s->pch_pic_base;
82
offset -= offset & 7;
83
84
spin_lock(&s->lock);
85
switch (offset) {
86
case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END:
87
data = s->id.data;
88
break;
89
case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
90
data = s->mask;
91
break;
92
case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
93
/* read htmsi enable reg */
94
data = s->htmsi_en;
95
break;
96
case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
97
/* read edge enable reg */
98
data = s->edge;
99
break;
100
case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
101
case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
102
/* we only use default mode: fixed interrupt distribution mode */
103
break;
104
case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
105
/* only route to int0: eiointc */
106
ptemp = s->route_entry + (offset - PCH_PIC_ROUTE_ENTRY_START);
107
data = *(u64 *)ptemp;
108
break;
109
case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
110
/* read htmsi vector */
111
ptemp = s->htmsi_vector + (offset - PCH_PIC_HTMSI_VEC_START);
112
data = *(u64 *)ptemp;
113
break;
114
case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
115
data = s->polarity;
116
break;
117
case PCH_PIC_INT_IRR_START:
118
data = s->irr;
119
break;
120
case PCH_PIC_INT_ISR_START:
121
data = s->isr;
122
break;
123
default:
124
ret = -EINVAL;
125
}
126
spin_unlock(&s->lock);
127
128
if (ret == 0) {
129
offset = (addr - s->pch_pic_base) & 7;
130
data = data >> (offset * 8);
131
memcpy(val, &data, len);
132
}
133
134
return ret;
135
}
136
137
static int kvm_pch_pic_read(struct kvm_vcpu *vcpu,
138
struct kvm_io_device *dev,
139
gpa_t addr, int len, void *val)
140
{
141
int ret;
142
struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
143
144
if (!s) {
145
kvm_err("%s: pch pic irqchip not valid!\n", __func__);
146
return -EINVAL;
147
}
148
149
if (addr & (len - 1)) {
150
kvm_err("%s: pch pic not aligned addr %llx len %d\n", __func__, addr, len);
151
return -EINVAL;
152
}
153
154
/* statistics of pch pic reading */
155
vcpu->stat.pch_pic_read_exits++;
156
ret = loongarch_pch_pic_read(s, addr, len, val);
157
158
return ret;
159
}
160
161
static int loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr,
162
int len, const void *val)
163
{
164
int ret = 0, offset;
165
u64 old, data, mask;
166
void *ptemp;
167
168
switch (len) {
169
case 1:
170
data = *(u8 *)val;
171
mask = 0xFF;
172
break;
173
case 2:
174
data = *(u16 *)val;
175
mask = USHRT_MAX;
176
break;
177
case 4:
178
data = *(u32 *)val;
179
mask = UINT_MAX;
180
break;
181
case 8:
182
default:
183
data = *(u64 *)val;
184
mask = ULONG_MAX;
185
break;
186
}
187
188
offset = (addr - s->pch_pic_base) & 7;
189
mask = mask << (offset * 8);
190
data = data << (offset * 8);
191
offset = (addr - s->pch_pic_base) - offset;
192
193
spin_lock(&s->lock);
194
switch (offset) {
195
case PCH_PIC_MASK_START:
196
old = s->mask;
197
s->mask = (old & ~mask) | data;
198
if (old & ~data)
199
pch_pic_update_batch_irqs(s, old & ~data, 1);
200
if (~old & data)
201
pch_pic_update_batch_irqs(s, ~old & data, 0);
202
break;
203
case PCH_PIC_HTMSI_EN_START:
204
s->htmsi_en = (s->htmsi_en & ~mask) | data;
205
break;
206
case PCH_PIC_EDGE_START:
207
s->edge = (s->edge & ~mask) | data;
208
break;
209
case PCH_PIC_POLARITY_START:
210
s->polarity = (s->polarity & ~mask) | data;
211
break;
212
case PCH_PIC_CLEAR_START:
213
old = s->irr & s->edge & data;
214
if (old) {
215
s->irr &= ~old;
216
pch_pic_update_batch_irqs(s, old, 0);
217
}
218
break;
219
case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
220
ptemp = s->htmsi_vector + (offset - PCH_PIC_HTMSI_VEC_START);
221
*(u64 *)ptemp = (*(u64 *)ptemp & ~mask) | data;
222
break;
223
/* Not implemented */
224
case PCH_PIC_AUTO_CTRL0_START:
225
case PCH_PIC_AUTO_CTRL1_START:
226
case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
227
break;
228
default:
229
ret = -EINVAL;
230
break;
231
}
232
spin_unlock(&s->lock);
233
234
return ret;
235
}
236
237
static int kvm_pch_pic_write(struct kvm_vcpu *vcpu,
238
struct kvm_io_device *dev,
239
gpa_t addr, int len, const void *val)
240
{
241
int ret;
242
struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
243
244
if (!s) {
245
kvm_err("%s: pch pic irqchip not valid!\n", __func__);
246
return -EINVAL;
247
}
248
249
if (addr & (len - 1)) {
250
kvm_err("%s: pch pic not aligned addr %llx len %d\n", __func__, addr, len);
251
return -EINVAL;
252
}
253
254
/* statistics of pch pic writing */
255
vcpu->stat.pch_pic_write_exits++;
256
ret = loongarch_pch_pic_write(s, addr, len, val);
257
258
return ret;
259
}
260
261
static const struct kvm_io_device_ops kvm_pch_pic_ops = {
262
.read = kvm_pch_pic_read,
263
.write = kvm_pch_pic_write,
264
};
265
266
static int kvm_pch_pic_init(struct kvm_device *dev, u64 addr)
267
{
268
int ret;
269
struct kvm *kvm = dev->kvm;
270
struct kvm_io_device *device;
271
struct loongarch_pch_pic *s = dev->kvm->arch.pch_pic;
272
273
s->pch_pic_base = addr;
274
device = &s->device;
275
/* init device by pch pic writing and reading ops */
276
kvm_iodevice_init(device, &kvm_pch_pic_ops);
277
mutex_lock(&kvm->slots_lock);
278
/* register pch pic device */
279
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, addr, PCH_PIC_SIZE, device);
280
mutex_unlock(&kvm->slots_lock);
281
282
return (ret < 0) ? -EFAULT : 0;
283
}
284
285
/* used by user space to get or set pch pic registers */
286
static int kvm_pch_pic_regs_access(struct kvm_device *dev,
287
struct kvm_device_attr *attr,
288
bool is_write)
289
{
290
char buf[8];
291
int addr, offset, len = 8, ret = 0;
292
void __user *data;
293
void *p = NULL;
294
struct loongarch_pch_pic *s;
295
296
s = dev->kvm->arch.pch_pic;
297
addr = attr->attr;
298
data = (void __user *)attr->addr;
299
300
/* get pointer to pch pic register by addr */
301
switch (addr) {
302
case PCH_PIC_MASK_START:
303
p = &s->mask;
304
break;
305
case PCH_PIC_HTMSI_EN_START:
306
p = &s->htmsi_en;
307
break;
308
case PCH_PIC_EDGE_START:
309
p = &s->edge;
310
break;
311
case PCH_PIC_AUTO_CTRL0_START:
312
p = &s->auto_ctrl0;
313
break;
314
case PCH_PIC_AUTO_CTRL1_START:
315
p = &s->auto_ctrl1;
316
break;
317
case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
318
offset = addr - PCH_PIC_ROUTE_ENTRY_START;
319
p = &s->route_entry[offset];
320
len = 1;
321
break;
322
case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
323
offset = addr - PCH_PIC_HTMSI_VEC_START;
324
p = &s->htmsi_vector[offset];
325
len = 1;
326
break;
327
case PCH_PIC_INT_IRR_START:
328
p = &s->irr;
329
break;
330
case PCH_PIC_INT_ISR_START:
331
p = &s->isr;
332
break;
333
case PCH_PIC_POLARITY_START:
334
p = &s->polarity;
335
break;
336
default:
337
return -EINVAL;
338
}
339
340
if (is_write) {
341
if (copy_from_user(buf, data, len))
342
return -EFAULT;
343
}
344
345
spin_lock(&s->lock);
346
if (is_write)
347
memcpy(p, buf, len);
348
else
349
memcpy(buf, p, len);
350
spin_unlock(&s->lock);
351
352
if (!is_write) {
353
if (copy_to_user(data, buf, len))
354
return -EFAULT;
355
}
356
357
return ret;
358
}
359
360
static int kvm_pch_pic_get_attr(struct kvm_device *dev,
361
struct kvm_device_attr *attr)
362
{
363
switch (attr->group) {
364
case KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS:
365
return kvm_pch_pic_regs_access(dev, attr, false);
366
default:
367
return -EINVAL;
368
}
369
}
370
371
static int kvm_pch_pic_set_attr(struct kvm_device *dev,
372
struct kvm_device_attr *attr)
373
{
374
u64 addr;
375
void __user *uaddr = (void __user *)(long)attr->addr;
376
377
switch (attr->group) {
378
case KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL:
379
switch (attr->attr) {
380
case KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT:
381
if (copy_from_user(&addr, uaddr, sizeof(addr)))
382
return -EFAULT;
383
384
if (!dev->kvm->arch.pch_pic) {
385
kvm_err("%s: please create pch_pic irqchip first!\n", __func__);
386
return -ENODEV;
387
}
388
389
return kvm_pch_pic_init(dev, addr);
390
default:
391
kvm_err("%s: unknown group (%d) attr (%lld)\n", __func__, attr->group,
392
attr->attr);
393
return -EINVAL;
394
}
395
case KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS:
396
return kvm_pch_pic_regs_access(dev, attr, true);
397
default:
398
return -EINVAL;
399
}
400
}
401
402
static int kvm_setup_default_irq_routing(struct kvm *kvm)
403
{
404
int i, ret;
405
u32 nr = KVM_IRQCHIP_NUM_PINS;
406
struct kvm_irq_routing_entry *entries;
407
408
entries = kcalloc(nr, sizeof(*entries), GFP_KERNEL);
409
if (!entries)
410
return -ENOMEM;
411
412
for (i = 0; i < nr; i++) {
413
entries[i].gsi = i;
414
entries[i].type = KVM_IRQ_ROUTING_IRQCHIP;
415
entries[i].u.irqchip.irqchip = 0;
416
entries[i].u.irqchip.pin = i;
417
}
418
ret = kvm_set_irq_routing(kvm, entries, nr, 0);
419
kfree(entries);
420
421
return ret;
422
}
423
424
static int kvm_pch_pic_create(struct kvm_device *dev, u32 type)
425
{
426
int i, ret, irq_num;
427
struct kvm *kvm = dev->kvm;
428
struct loongarch_pch_pic *s;
429
430
/* pch pic should not has been created */
431
if (kvm->arch.pch_pic)
432
return -EINVAL;
433
434
ret = kvm_setup_default_irq_routing(kvm);
435
if (ret)
436
return -ENOMEM;
437
438
s = kzalloc(sizeof(struct loongarch_pch_pic), GFP_KERNEL);
439
if (!s)
440
return -ENOMEM;
441
442
/*
443
* Interrupt controller identification register 1
444
* Bit 24-31 Interrupt Controller ID
445
* Interrupt controller identification register 2
446
* Bit 0-7 Interrupt Controller version number
447
* Bit 16-23 The number of interrupt sources supported
448
*/
449
irq_num = 32;
450
s->mask = -1UL;
451
s->id.desc.id = PCH_PIC_INT_ID_VAL;
452
s->id.desc.version = PCH_PIC_INT_ID_VER;
453
s->id.desc.irq_num = irq_num - 1;
454
for (i = 0; i < irq_num; i++) {
455
s->route_entry[i] = 1;
456
s->htmsi_vector[i] = i;
457
}
458
spin_lock_init(&s->lock);
459
s->kvm = kvm;
460
kvm->arch.pch_pic = s;
461
462
return 0;
463
}
464
465
static void kvm_pch_pic_destroy(struct kvm_device *dev)
466
{
467
struct kvm *kvm;
468
struct loongarch_pch_pic *s;
469
470
if (!dev || !dev->kvm || !dev->kvm->arch.pch_pic)
471
return;
472
473
kvm = dev->kvm;
474
s = kvm->arch.pch_pic;
475
/* unregister pch pic device and free it's memory */
476
kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device);
477
kfree(s);
478
}
479
480
static struct kvm_device_ops kvm_pch_pic_dev_ops = {
481
.name = "kvm-loongarch-pch-pic",
482
.create = kvm_pch_pic_create,
483
.destroy = kvm_pch_pic_destroy,
484
.set_attr = kvm_pch_pic_set_attr,
485
.get_attr = kvm_pch_pic_get_attr,
486
};
487
488
int kvm_loongarch_register_pch_pic_device(void)
489
{
490
return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC);
491
}
492
493