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