/* update the isr according to irq level and route irq to eiointc */ staticvoid pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
{
u64 mask = BIT(irq);
/* * set isr and route irq to eiointc and * the route table is in htmsi_vector[]
*/ if (level) { if (mask & s->irr & ~s->mask) {
s->isr |= mask;
irq = s->htmsi_vector[irq];
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
}
} else { if (mask & s->isr & ~s->irr) {
s->isr &= ~mask;
irq = s->htmsi_vector[irq];
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
}
}
}
/* update batch irqs, the irq_mask is a bitmap of irqs */ staticvoid pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level)
{ int irq, bits;
/* find each irq by irqs bitmap and update each irq */
bits = sizeof(irq_mask) * 8;
irq = find_first_bit((void *)&irq_mask, bits); while (irq < bits) {
pch_pic_update_irq(s, irq, level);
bitmap_clear((void *)&irq_mask, irq, 1);
irq = find_first_bit((void *)&irq_mask, bits);
}
}
/* called when a irq is triggered in pch pic */ void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level)
{
u64 mask = BIT(irq);
spin_lock(&s->lock); if (level)
s->irr |= mask; /* set irr */ else { /* * In edge triggered mode, 0 does not mean to clear irq * The irr register variable is cleared when cpu writes to the * PCH_PIC_CLEAR_START address area
*/ if (s->edge & mask) {
spin_unlock(&s->lock); return;
}
s->irr &= ~mask;
}
pch_pic_update_irq(s, irq, level);
spin_unlock(&s->lock);
}
/* msi irq handler */ void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
{
eiointc_set_irq(kvm->arch.eiointc, irq, level);
}
/* * pch pic register is 64-bit, but it is accessed by 32-bit, * so we use high to get whether low or high 32 bits we want * to read.
*/ static u32 pch_pic_read_reg(u64 *s, int high)
{
u64 val = *s;
/* read the high 32 bits when high is 1 */ return high ? (u32)(val >> 32) : (u32)val;
}
/* * pch pic register is 64-bit, but it is accessed by 32-bit, * so we use high to get whether low or high 32 bits we want * to write.
*/ static u32 pch_pic_write_reg(u64 *s, int high, u32 v)
{
u64 val = *s, data = v;
if (high) { /* * Clear val high 32 bits * Write the high 32 bits when the high is 1
*/
*s = (val << 32 >> 32) | (data << 32);
val >>= 32;
} else /* * Clear val low 32 bits * Write the low 32 bits when the high is 0
*/
*s = (val >> 32 << 32) | v;
return (u32)val;
}
staticint loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
{ int offset, index, ret = 0;
u32 data = 0;
u64 int_id = 0;
offset = addr - s->pch_pic_base;
spin_lock(&s->lock); switch (offset) { case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END: /* int id version */
int_id |= (u64)PCH_PIC_INT_ID_VER << 32; /* irq number */
int_id |= (u64)31 << (32 + 16); /* int id value */
int_id |= PCH_PIC_INT_ID_VAL;
*(u64 *)val = int_id; break; case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
offset -= PCH_PIC_MASK_START;
index = offset >> 2; /* read mask reg */
data = pch_pic_read_reg(&s->mask, index);
*(u32 *)val = data; break; case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
offset -= PCH_PIC_HTMSI_EN_START;
index = offset >> 2; /* read htmsi enable reg */
data = pch_pic_read_reg(&s->htmsi_en, index);
*(u32 *)val = data; break; case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
offset -= PCH_PIC_EDGE_START;
index = offset >> 2; /* read edge enable reg */
data = pch_pic_read_reg(&s->edge, index);
*(u32 *)val = data; break; case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END: case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END: /* we only use default mode: fixed interrupt distribution mode */
*(u32 *)val = 0; break; case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END: /* only route to int0: eiointc */
*(u8 *)val = 1; break; case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
offset -= PCH_PIC_HTMSI_VEC_START; /* read htmsi vector */
data = s->htmsi_vector[offset];
*(u8 *)val = data; break; case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END: /* we only use defalut value 0: high level triggered */
*(u32 *)val = 0; break; default:
ret = -EINVAL;
}
spin_unlock(&s->lock);
if (!s) {
kvm_err("%s: pch pic irqchip not valid!\n", __func__); return -EINVAL;
}
if (addr & (len - 1)) {
kvm_err("%s: pch pic not aligned addr %llx len %d\n", __func__, addr, len); return -EINVAL;
}
/* statistics of pch pic reading */
vcpu->stat.pch_pic_read_exits++;
ret = loongarch_pch_pic_read(s, addr, len, val);
return ret;
}
staticint loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr, int len, constvoid *val)
{ int ret;
u32 old, data, offset, index;
u64 irq;
ret = 0;
data = *(u32 *)val;
offset = addr - s->pch_pic_base;
spin_lock(&s->lock); switch (offset) { case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
offset -= PCH_PIC_MASK_START; /* get whether high or low 32 bits we want to write */
index = offset >> 2;
old = pch_pic_write_reg(&s->mask, index, data); /* enable irq when mask value change to 0 */
irq = (old & ~data) << (32 * index);
pch_pic_update_batch_irqs(s, irq, 1); /* disable irq when mask value change to 1 */
irq = (~old & data) << (32 * index);
pch_pic_update_batch_irqs(s, irq, 0); break; case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
offset -= PCH_PIC_HTMSI_EN_START;
index = offset >> 2;
pch_pic_write_reg(&s->htmsi_en, index, data); break; case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
offset -= PCH_PIC_EDGE_START;
index = offset >> 2; /* 1: edge triggered, 0: level triggered */
pch_pic_write_reg(&s->edge, index, data); break; case PCH_PIC_CLEAR_START ... PCH_PIC_CLEAR_END:
offset -= PCH_PIC_CLEAR_START;
index = offset >> 2; /* write 1 to clear edge irq */
old = pch_pic_read_reg(&s->irr, index); /* * get the irq bitmap which is edge triggered and * already set and to be cleared
*/
irq = old & pch_pic_read_reg(&s->edge, index) & data; /* write irr to the new state where irqs have been cleared */
pch_pic_write_reg(&s->irr, index, old & ~irq); /* update cleared irqs */
pch_pic_update_batch_irqs(s, irq, 0); break; case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
offset -= PCH_PIC_AUTO_CTRL0_START;
index = offset >> 2; /* we only use default mode: fixed interrupt distribution mode */
pch_pic_write_reg(&s->auto_ctrl0, index, 0); break; case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
offset -= PCH_PIC_AUTO_CTRL1_START;
index = offset >> 2; /* we only use default mode: fixed interrupt distribution mode */
pch_pic_write_reg(&s->auto_ctrl1, index, 0); break; case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
offset -= PCH_PIC_ROUTE_ENTRY_START; /* only route to int0: eiointc */
s->route_entry[offset] = 1; break; case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END: /* route table to eiointc */
offset -= PCH_PIC_HTMSI_VEC_START;
s->htmsi_vector[offset] = (u8)data; break; case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
offset -= PCH_PIC_POLARITY_START;
index = offset >> 2; /* we only use defalut value 0: high level triggered */
pch_pic_write_reg(&s->polarity, index, 0); break; default:
ret = -EINVAL; break;
}
spin_unlock(&s->lock);
/* used by user space to get or set pch pic registers */ staticint kvm_pch_pic_regs_access(struct kvm_device *dev, struct kvm_device_attr *attr, bool is_write)
{ char buf[8]; int addr, offset, len = 8, ret = 0; void __user *data; void *p = NULL; struct loongarch_pch_pic *s;
s = dev->kvm->arch.pch_pic;
addr = attr->attr;
data = (void __user *)attr->addr;
/* get pointer to pch pic register by addr */ switch (addr) { case PCH_PIC_MASK_START:
p = &s->mask; break; case PCH_PIC_HTMSI_EN_START:
p = &s->htmsi_en; break; case PCH_PIC_EDGE_START:
p = &s->edge; break; case PCH_PIC_AUTO_CTRL0_START:
p = &s->auto_ctrl0; break; case PCH_PIC_AUTO_CTRL1_START:
p = &s->auto_ctrl1; break; case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
offset = addr - PCH_PIC_ROUTE_ENTRY_START;
p = &s->route_entry[offset];
len = 1; break; case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
offset = addr - PCH_PIC_HTMSI_VEC_START;
p = &s->htmsi_vector[offset];
len = 1; break; case PCH_PIC_INT_IRR_START:
p = &s->irr; break; case PCH_PIC_INT_ISR_START:
p = &s->isr; break; case PCH_PIC_POLARITY_START:
p = &s->polarity; break; default: return -EINVAL;
}
if (is_write) { if (copy_from_user(buf, data, len)) return -EFAULT;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.