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