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