/* 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.
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.