xref: /linux/arch/loongarch/kvm/intc/pch_pic.c (revision f5f31efa3c2d51c03afab5ab6e3f004d2d529013)
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 
11*f5f31efaSXianglai Li /* update the isr according to irq level and route irq to eiointc */
12*f5f31efaSXianglai Li static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
13*f5f31efaSXianglai Li {
14*f5f31efaSXianglai Li 	u64 mask = BIT(irq);
15*f5f31efaSXianglai Li 
16*f5f31efaSXianglai Li 	/*
17*f5f31efaSXianglai Li 	 * set isr and route irq to eiointc and
18*f5f31efaSXianglai Li 	 * the route table is in htmsi_vector[]
19*f5f31efaSXianglai Li 	 */
20*f5f31efaSXianglai Li 	if (level) {
21*f5f31efaSXianglai Li 		if (mask & s->irr & ~s->mask) {
22*f5f31efaSXianglai Li 			s->isr |= mask;
23*f5f31efaSXianglai Li 			irq = s->htmsi_vector[irq];
24*f5f31efaSXianglai Li 			eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
25*f5f31efaSXianglai Li 		}
26*f5f31efaSXianglai Li 	} else {
27*f5f31efaSXianglai Li 		if (mask & s->isr & ~s->irr) {
28*f5f31efaSXianglai Li 			s->isr &= ~mask;
29*f5f31efaSXianglai Li 			irq = s->htmsi_vector[irq];
30*f5f31efaSXianglai Li 			eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
31*f5f31efaSXianglai Li 		}
32*f5f31efaSXianglai Li 	}
33*f5f31efaSXianglai Li }
34*f5f31efaSXianglai Li 
35*f5f31efaSXianglai Li /* update batch irqs, the irq_mask is a bitmap of irqs */
36*f5f31efaSXianglai Li static void pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level)
37*f5f31efaSXianglai Li {
38*f5f31efaSXianglai Li 	int irq, bits;
39*f5f31efaSXianglai Li 
40*f5f31efaSXianglai Li 	/* find each irq by irqs bitmap and update each irq */
41*f5f31efaSXianglai Li 	bits = sizeof(irq_mask) * 8;
42*f5f31efaSXianglai Li 	irq = find_first_bit((void *)&irq_mask, bits);
43*f5f31efaSXianglai Li 	while (irq < bits) {
44*f5f31efaSXianglai Li 		pch_pic_update_irq(s, irq, level);
45*f5f31efaSXianglai Li 		bitmap_clear((void *)&irq_mask, irq, 1);
46*f5f31efaSXianglai Li 		irq = find_first_bit((void *)&irq_mask, bits);
47*f5f31efaSXianglai Li 	}
48*f5f31efaSXianglai Li }
49*f5f31efaSXianglai Li 
50*f5f31efaSXianglai Li /* called when a irq is triggered in pch pic */
51*f5f31efaSXianglai Li void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level)
52*f5f31efaSXianglai Li {
53*f5f31efaSXianglai Li 	u64 mask = BIT(irq);
54*f5f31efaSXianglai Li 
55*f5f31efaSXianglai Li 	spin_lock(&s->lock);
56*f5f31efaSXianglai Li 	if (level)
57*f5f31efaSXianglai Li 		s->irr |= mask; /* set irr */
58*f5f31efaSXianglai Li 	else {
59*f5f31efaSXianglai Li 		/*
60*f5f31efaSXianglai Li 		 * In edge triggered mode, 0 does not mean to clear irq
61*f5f31efaSXianglai Li 		 * The irr register variable is cleared when cpu writes to the
62*f5f31efaSXianglai Li 		 * PCH_PIC_CLEAR_START address area
63*f5f31efaSXianglai Li 		 */
64*f5f31efaSXianglai Li 		if (s->edge & mask) {
65*f5f31efaSXianglai Li 			spin_unlock(&s->lock);
66*f5f31efaSXianglai Li 			return;
67*f5f31efaSXianglai Li 		}
68*f5f31efaSXianglai Li 		s->irr &= ~mask;
69*f5f31efaSXianglai Li 	}
70*f5f31efaSXianglai Li 	pch_pic_update_irq(s, irq, level);
71*f5f31efaSXianglai Li 	spin_unlock(&s->lock);
72*f5f31efaSXianglai Li }
73*f5f31efaSXianglai Li 
74*f5f31efaSXianglai Li /* msi irq handler */
75*f5f31efaSXianglai Li void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
76*f5f31efaSXianglai Li {
77*f5f31efaSXianglai Li 	eiointc_set_irq(kvm->arch.eiointc, irq, level);
78*f5f31efaSXianglai Li }
79*f5f31efaSXianglai Li 
80*f5f31efaSXianglai Li /*
81*f5f31efaSXianglai Li  * pch pic register is 64-bit, but it is accessed by 32-bit,
82*f5f31efaSXianglai Li  * so we use high to get whether low or high 32 bits we want
83*f5f31efaSXianglai Li  * to read.
84*f5f31efaSXianglai Li  */
85*f5f31efaSXianglai Li static u32 pch_pic_read_reg(u64 *s, int high)
86*f5f31efaSXianglai Li {
87*f5f31efaSXianglai Li 	u64 val = *s;
88*f5f31efaSXianglai Li 
89*f5f31efaSXianglai Li 	/* read the high 32 bits when high is 1 */
90*f5f31efaSXianglai Li 	return high ? (u32)(val >> 32) : (u32)val;
91*f5f31efaSXianglai Li }
92*f5f31efaSXianglai Li 
93*f5f31efaSXianglai Li /*
94*f5f31efaSXianglai Li  * pch pic register is 64-bit, but it is accessed by 32-bit,
95*f5f31efaSXianglai Li  * so we use high to get whether low or high 32 bits we want
96*f5f31efaSXianglai Li  * to write.
97*f5f31efaSXianglai Li  */
98*f5f31efaSXianglai Li static u32 pch_pic_write_reg(u64 *s, int high, u32 v)
99*f5f31efaSXianglai Li {
100*f5f31efaSXianglai Li 	u64 val = *s, data = v;
101*f5f31efaSXianglai Li 
102*f5f31efaSXianglai Li 	if (high) {
103*f5f31efaSXianglai Li 		/*
104*f5f31efaSXianglai Li 		 * Clear val high 32 bits
105*f5f31efaSXianglai Li 		 * Write the high 32 bits when the high is 1
106*f5f31efaSXianglai Li 		 */
107*f5f31efaSXianglai Li 		*s = (val << 32 >> 32) | (data << 32);
108*f5f31efaSXianglai Li 		val >>= 32;
109*f5f31efaSXianglai Li 	} else
110*f5f31efaSXianglai Li 		/*
111*f5f31efaSXianglai Li 		 * Clear val low 32 bits
112*f5f31efaSXianglai Li 		 * Write the low 32 bits when the high is 0
113*f5f31efaSXianglai Li 		 */
114*f5f31efaSXianglai Li 		*s = (val >> 32 << 32) | v;
115*f5f31efaSXianglai Li 
116*f5f31efaSXianglai Li 	return (u32)val;
117*f5f31efaSXianglai Li }
118*f5f31efaSXianglai Li 
119*f5f31efaSXianglai Li static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
120*f5f31efaSXianglai Li {
121*f5f31efaSXianglai Li 	int offset, index, ret = 0;
122*f5f31efaSXianglai Li 	u32 data = 0;
123*f5f31efaSXianglai Li 	u64 int_id = 0;
124*f5f31efaSXianglai Li 
125*f5f31efaSXianglai Li 	offset = addr - s->pch_pic_base;
126*f5f31efaSXianglai Li 
127*f5f31efaSXianglai Li 	spin_lock(&s->lock);
128*f5f31efaSXianglai Li 	switch (offset) {
129*f5f31efaSXianglai Li 	case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END:
130*f5f31efaSXianglai Li 		/* int id version */
131*f5f31efaSXianglai Li 		int_id |= (u64)PCH_PIC_INT_ID_VER << 32;
132*f5f31efaSXianglai Li 		/* irq number */
133*f5f31efaSXianglai Li 		int_id |= (u64)31 << (32 + 16);
134*f5f31efaSXianglai Li 		/* int id value */
135*f5f31efaSXianglai Li 		int_id |= PCH_PIC_INT_ID_VAL;
136*f5f31efaSXianglai Li 		*(u64 *)val = int_id;
137*f5f31efaSXianglai Li 		break;
138*f5f31efaSXianglai Li 	case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
139*f5f31efaSXianglai Li 		offset -= PCH_PIC_MASK_START;
140*f5f31efaSXianglai Li 		index = offset >> 2;
141*f5f31efaSXianglai Li 		/* read mask reg */
142*f5f31efaSXianglai Li 		data = pch_pic_read_reg(&s->mask, index);
143*f5f31efaSXianglai Li 		*(u32 *)val = data;
144*f5f31efaSXianglai Li 		break;
145*f5f31efaSXianglai Li 	case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
146*f5f31efaSXianglai Li 		offset -= PCH_PIC_HTMSI_EN_START;
147*f5f31efaSXianglai Li 		index = offset >> 2;
148*f5f31efaSXianglai Li 		/* read htmsi enable reg */
149*f5f31efaSXianglai Li 		data = pch_pic_read_reg(&s->htmsi_en, index);
150*f5f31efaSXianglai Li 		*(u32 *)val = data;
151*f5f31efaSXianglai Li 		break;
152*f5f31efaSXianglai Li 	case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
153*f5f31efaSXianglai Li 		offset -= PCH_PIC_EDGE_START;
154*f5f31efaSXianglai Li 		index = offset >> 2;
155*f5f31efaSXianglai Li 		/* read edge enable reg */
156*f5f31efaSXianglai Li 		data = pch_pic_read_reg(&s->edge, index);
157*f5f31efaSXianglai Li 		*(u32 *)val = data;
158*f5f31efaSXianglai Li 		break;
159*f5f31efaSXianglai Li 	case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
160*f5f31efaSXianglai Li 	case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
161*f5f31efaSXianglai Li 		/* we only use default mode: fixed interrupt distribution mode */
162*f5f31efaSXianglai Li 		*(u32 *)val = 0;
163*f5f31efaSXianglai Li 		break;
164*f5f31efaSXianglai Li 	case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
165*f5f31efaSXianglai Li 		/* only route to int0: eiointc */
166*f5f31efaSXianglai Li 		*(u8 *)val = 1;
167*f5f31efaSXianglai Li 		break;
168*f5f31efaSXianglai Li 	case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
169*f5f31efaSXianglai Li 		offset -= PCH_PIC_HTMSI_VEC_START;
170*f5f31efaSXianglai Li 		/* read htmsi vector */
171*f5f31efaSXianglai Li 		data = s->htmsi_vector[offset];
172*f5f31efaSXianglai Li 		*(u8 *)val = data;
173*f5f31efaSXianglai Li 		break;
174*f5f31efaSXianglai Li 	case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
175*f5f31efaSXianglai Li 		/* we only use defalut value 0: high level triggered */
176*f5f31efaSXianglai Li 		*(u32 *)val = 0;
177*f5f31efaSXianglai Li 		break;
178*f5f31efaSXianglai Li 	default:
179*f5f31efaSXianglai Li 		ret = -EINVAL;
180*f5f31efaSXianglai Li 	}
181*f5f31efaSXianglai Li 	spin_unlock(&s->lock);
182*f5f31efaSXianglai Li 
183*f5f31efaSXianglai Li 	return ret;
184*f5f31efaSXianglai Li }
185*f5f31efaSXianglai 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 {
190*f5f31efaSXianglai Li 	int ret;
191*f5f31efaSXianglai Li 	struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
192*f5f31efaSXianglai Li 
193*f5f31efaSXianglai Li 	if (!s) {
194*f5f31efaSXianglai Li 		kvm_err("%s: pch pic irqchip not valid!\n", __func__);
195*f5f31efaSXianglai Li 		return -EINVAL;
196*f5f31efaSXianglai Li 	}
197*f5f31efaSXianglai Li 
198*f5f31efaSXianglai Li 	/* statistics of pch pic reading */
199*f5f31efaSXianglai Li 	vcpu->kvm->stat.pch_pic_read_exits++;
200*f5f31efaSXianglai Li 	ret = loongarch_pch_pic_read(s, addr, len, val);
201*f5f31efaSXianglai Li 
202*f5f31efaSXianglai Li 	return ret;
203*f5f31efaSXianglai Li }
204*f5f31efaSXianglai Li 
205*f5f31efaSXianglai Li static int loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr,
206*f5f31efaSXianglai Li 					int len, const void *val)
207*f5f31efaSXianglai Li {
208*f5f31efaSXianglai Li 	int ret;
209*f5f31efaSXianglai Li 	u32 old, data, offset, index;
210*f5f31efaSXianglai Li 	u64 irq;
211*f5f31efaSXianglai Li 
212*f5f31efaSXianglai Li 	ret = 0;
213*f5f31efaSXianglai Li 	data = *(u32 *)val;
214*f5f31efaSXianglai Li 	offset = addr - s->pch_pic_base;
215*f5f31efaSXianglai Li 
216*f5f31efaSXianglai Li 	spin_lock(&s->lock);
217*f5f31efaSXianglai Li 	switch (offset) {
218*f5f31efaSXianglai Li 	case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
219*f5f31efaSXianglai Li 		offset -= PCH_PIC_MASK_START;
220*f5f31efaSXianglai Li 		/* get whether high or low 32 bits we want to write */
221*f5f31efaSXianglai Li 		index = offset >> 2;
222*f5f31efaSXianglai Li 		old = pch_pic_write_reg(&s->mask, index, data);
223*f5f31efaSXianglai Li 		/* enable irq when mask value change to 0 */
224*f5f31efaSXianglai Li 		irq = (old & ~data) << (32 * index);
225*f5f31efaSXianglai Li 		pch_pic_update_batch_irqs(s, irq, 1);
226*f5f31efaSXianglai Li 		/* disable irq when mask value change to 1 */
227*f5f31efaSXianglai Li 		irq = (~old & data) << (32 * index);
228*f5f31efaSXianglai Li 		pch_pic_update_batch_irqs(s, irq, 0);
229*f5f31efaSXianglai Li 		break;
230*f5f31efaSXianglai Li 	case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
231*f5f31efaSXianglai Li 		offset -= PCH_PIC_HTMSI_EN_START;
232*f5f31efaSXianglai Li 		index = offset >> 2;
233*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->htmsi_en, index, data);
234*f5f31efaSXianglai Li 		break;
235*f5f31efaSXianglai Li 	case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
236*f5f31efaSXianglai Li 		offset -= PCH_PIC_EDGE_START;
237*f5f31efaSXianglai Li 		index = offset >> 2;
238*f5f31efaSXianglai Li 		/* 1: edge triggered, 0: level triggered */
239*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->edge, index, data);
240*f5f31efaSXianglai Li 		break;
241*f5f31efaSXianglai Li 	case PCH_PIC_CLEAR_START ... PCH_PIC_CLEAR_END:
242*f5f31efaSXianglai Li 		offset -= PCH_PIC_CLEAR_START;
243*f5f31efaSXianglai Li 		index = offset >> 2;
244*f5f31efaSXianglai Li 		/* write 1 to clear edge irq */
245*f5f31efaSXianglai Li 		old = pch_pic_read_reg(&s->irr, index);
246*f5f31efaSXianglai Li 		/*
247*f5f31efaSXianglai Li 		 * get the irq bitmap which is edge triggered and
248*f5f31efaSXianglai Li 		 * already set and to be cleared
249*f5f31efaSXianglai Li 		 */
250*f5f31efaSXianglai Li 		irq = old & pch_pic_read_reg(&s->edge, index) & data;
251*f5f31efaSXianglai Li 		/* write irr to the new state where irqs have been cleared */
252*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->irr, index, old & ~irq);
253*f5f31efaSXianglai Li 		/* update cleared irqs */
254*f5f31efaSXianglai Li 		pch_pic_update_batch_irqs(s, irq, 0);
255*f5f31efaSXianglai Li 		break;
256*f5f31efaSXianglai Li 	case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
257*f5f31efaSXianglai Li 		offset -= PCH_PIC_AUTO_CTRL0_START;
258*f5f31efaSXianglai Li 		index = offset >> 2;
259*f5f31efaSXianglai Li 		/* we only use default mode: fixed interrupt distribution mode */
260*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->auto_ctrl0, index, 0);
261*f5f31efaSXianglai Li 		break;
262*f5f31efaSXianglai Li 	case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
263*f5f31efaSXianglai Li 		offset -= PCH_PIC_AUTO_CTRL1_START;
264*f5f31efaSXianglai Li 		index = offset >> 2;
265*f5f31efaSXianglai Li 		/* we only use default mode: fixed interrupt distribution mode */
266*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->auto_ctrl1, index, 0);
267*f5f31efaSXianglai Li 		break;
268*f5f31efaSXianglai Li 	case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
269*f5f31efaSXianglai Li 		offset -= PCH_PIC_ROUTE_ENTRY_START;
270*f5f31efaSXianglai Li 		/* only route to int0: eiointc */
271*f5f31efaSXianglai Li 		s->route_entry[offset] = 1;
272*f5f31efaSXianglai Li 		break;
273*f5f31efaSXianglai Li 	case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
274*f5f31efaSXianglai Li 		/* route table to eiointc */
275*f5f31efaSXianglai Li 		offset -= PCH_PIC_HTMSI_VEC_START;
276*f5f31efaSXianglai Li 		s->htmsi_vector[offset] = (u8)data;
277*f5f31efaSXianglai Li 		break;
278*f5f31efaSXianglai Li 	case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
279*f5f31efaSXianglai Li 		offset -= PCH_PIC_POLARITY_START;
280*f5f31efaSXianglai Li 		index = offset >> 2;
281*f5f31efaSXianglai Li 		/* we only use defalut value 0: high level triggered */
282*f5f31efaSXianglai Li 		pch_pic_write_reg(&s->polarity, index, 0);
283*f5f31efaSXianglai Li 		break;
284*f5f31efaSXianglai Li 	default:
285*f5f31efaSXianglai Li 		ret = -EINVAL;
286*f5f31efaSXianglai Li 		break;
287*f5f31efaSXianglai Li 	}
288*f5f31efaSXianglai Li 	spin_unlock(&s->lock);
289*f5f31efaSXianglai Li 
290*f5f31efaSXianglai 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 {
297*f5f31efaSXianglai Li 	int ret;
298*f5f31efaSXianglai Li 	struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
299*f5f31efaSXianglai Li 
300*f5f31efaSXianglai Li 	if (!s) {
301*f5f31efaSXianglai Li 		kvm_err("%s: pch pic irqchip not valid!\n", __func__);
302*f5f31efaSXianglai Li 		return -EINVAL;
303*f5f31efaSXianglai Li 	}
304*f5f31efaSXianglai Li 
305*f5f31efaSXianglai Li 	/* statistics of pch pic writing */
306*f5f31efaSXianglai Li 	vcpu->kvm->stat.pch_pic_write_exits++;
307*f5f31efaSXianglai Li 	ret = loongarch_pch_pic_write(s, addr, len, val);
308*f5f31efaSXianglai Li 
309*f5f31efaSXianglai 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 
317e785dfacSXianglai Li static int kvm_pch_pic_get_attr(struct kvm_device *dev,
318e785dfacSXianglai Li 				struct kvm_device_attr *attr)
319e785dfacSXianglai Li {
320e785dfacSXianglai Li 	return 0;
321e785dfacSXianglai Li }
322e785dfacSXianglai Li 
323e785dfacSXianglai Li static int kvm_pch_pic_set_attr(struct kvm_device *dev,
324e785dfacSXianglai Li 				struct kvm_device_attr *attr)
325e785dfacSXianglai Li {
326e785dfacSXianglai Li 	return 0;
327e785dfacSXianglai Li }
328e785dfacSXianglai Li 
329e785dfacSXianglai Li static int kvm_pch_pic_create(struct kvm_device *dev, u32 type)
330e785dfacSXianglai Li {
331e785dfacSXianglai Li 	struct kvm *kvm = dev->kvm;
332e785dfacSXianglai Li 	struct loongarch_pch_pic *s;
333e785dfacSXianglai Li 
334e785dfacSXianglai Li 	/* pch pic should not has been created */
335e785dfacSXianglai Li 	if (kvm->arch.pch_pic)
336e785dfacSXianglai Li 		return -EINVAL;
337e785dfacSXianglai Li 
338e785dfacSXianglai Li 	s = kzalloc(sizeof(struct loongarch_pch_pic), GFP_KERNEL);
339e785dfacSXianglai Li 	if (!s)
340e785dfacSXianglai Li 		return -ENOMEM;
341e785dfacSXianglai Li 
342e785dfacSXianglai Li 	spin_lock_init(&s->lock);
343e785dfacSXianglai Li 	s->kvm = kvm;
344e785dfacSXianglai Li 	kvm->arch.pch_pic = s;
345e785dfacSXianglai Li 
346e785dfacSXianglai Li 	return 0;
347e785dfacSXianglai Li }
348e785dfacSXianglai Li 
349e785dfacSXianglai Li static void kvm_pch_pic_destroy(struct kvm_device *dev)
350e785dfacSXianglai Li {
351e785dfacSXianglai Li 	struct kvm *kvm;
352e785dfacSXianglai Li 	struct loongarch_pch_pic *s;
353e785dfacSXianglai Li 
354e785dfacSXianglai Li 	if (!dev || !dev->kvm || !dev->kvm->arch.pch_pic)
355e785dfacSXianglai Li 		return;
356e785dfacSXianglai Li 
357e785dfacSXianglai Li 	kvm = dev->kvm;
358e785dfacSXianglai Li 	s = kvm->arch.pch_pic;
359e785dfacSXianglai Li 	/* unregister pch pic device and free it's memory */
360e785dfacSXianglai Li 	kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device);
361e785dfacSXianglai Li 	kfree(s);
362e785dfacSXianglai Li }
363e785dfacSXianglai Li 
364e785dfacSXianglai Li static struct kvm_device_ops kvm_pch_pic_dev_ops = {
365e785dfacSXianglai Li 	.name = "kvm-loongarch-pch-pic",
366e785dfacSXianglai Li 	.create = kvm_pch_pic_create,
367e785dfacSXianglai Li 	.destroy = kvm_pch_pic_destroy,
368e785dfacSXianglai Li 	.set_attr = kvm_pch_pic_set_attr,
369e785dfacSXianglai Li 	.get_attr = kvm_pch_pic_get_attr,
370e785dfacSXianglai Li };
371e785dfacSXianglai Li 
372e785dfacSXianglai Li int kvm_loongarch_register_pch_pic_device(void)
373e785dfacSXianglai Li {
374e785dfacSXianglai Li 	return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC);
375e785dfacSXianglai Li }
376