File Index
Files and directories
Sign in
FILE

pch_pic.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kvm/intc/pch_pic.c C
  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 */
 12static 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 */
 36static 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 */
 46void 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 */
 70void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
 71{
 72	eiointc_set_irq(kvm->arch.eiointc, irq, level);
 73}
 74
 75static 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
137static 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
161static 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
237static 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
261static const struct kvm_io_device_ops kvm_pch_pic_ops = {
262	.read	= kvm_pch_pic_read,
263	.write	= kvm_pch_pic_write,
264};
265
266static 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 */
286static 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
360static 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
371static 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
402static 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
424static 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
465static 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	kfree(dev);
479}
480
481static struct kvm_device_ops kvm_pch_pic_dev_ops = {
482	.name = "kvm-loongarch-pch-pic",
483	.create = kvm_pch_pic_create,
484	.destroy = kvm_pch_pic_destroy,
485	.set_attr = kvm_pch_pic_set_attr,
486	.get_attr = kvm_pch_pic_get_attr,
487};
488
489int kvm_loongarch_register_pch_pic_device(void)
490{
491	return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC);
492}
~/ Read-only file preview.