#include <sys/processor.h>
#include <sys/time.h>
#include <sys/psm.h>
#include <sys/smp_impldefs.h>
#include <sys/cram.h>
#include <sys/acpi/acpi.h>
#include <sys/acpica.h>
#include <sys/psm_common.h>
#include <sys/pit.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/pci.h>
#include <sys/promif.h>
#include <sys/x86_archext.h>
#include <sys/cpc_impl.h>
#include <sys/uadmin.h>
#include <sys/panic.h>
#include <sys/debug.h>
#include <sys/archsystm.h>
#include <sys/trap.h>
#include <sys/machsystm.h>
#include <sys/sysmacros.h>
#include <sys/cpuvar.h>
#include <sys/rm_platter.h>
#include <sys/privregs.h>
#include <sys/note.h>
#include <sys/pci_intr_lib.h>
#include <sys/spl.h>
#include <sys/clock.h>
#include <sys/dditypes.h>
#include <sys/sunddi.h>
#include <sys/x_call.h>
#include <sys/reboot.h>
#include <sys/apix.h>
#include <sys/smt.h>
static int apix_get_avail_vector_oncpu(uint32_t, int, int);
static apix_vector_t *apix_init_vector(processorid_t, uchar_t);
static void apix_cleanup_vector(apix_vector_t *);
static void apix_insert_av(apix_vector_t *, void *, avfunc, caddr_t, caddr_t,
uint64_t *, int, dev_info_t *);
static void apix_remove_av(apix_vector_t *, struct autovec *);
static void apix_clear_dev_map(dev_info_t *, int, int);
static boolean_t apix_is_cpu_enabled(processorid_t);
static void apix_wait_till_seen(processorid_t, int);
#define GET_INTR_INUM(ihdlp) \
(((ihdlp) != NULL) ? ((ddi_intr_handle_impl_t *)(ihdlp))->ih_inum : 0)
apix_rebind_info_t apix_rebindinfo = {0, 0, 0, NULL, 0, NULL};
uchar_t
apix_alloc_ipi(int ipl)
{
apix_vector_t *vecp;
uchar_t vector;
int cpun;
int nproc;
APIX_ENTER_CPU_LOCK(0);
vector = apix_get_avail_vector_oncpu(0, APIX_IPI_MIN, APIX_IPI_MAX);
if (vector == 0) {
APIX_LEAVE_CPU_LOCK(0);
cmn_err(CE_WARN, "apix: no available IPI\n");
apic_error |= APIC_ERR_GET_IPIVECT_FAIL;
return (0);
}
nproc = max(apic_nproc, apic_max_nproc);
for (cpun = 0; cpun < nproc; cpun++) {
vecp = xv_vector(cpun, vector);
if (vecp == NULL) {
vecp = kmem_zalloc(sizeof (apix_vector_t), KM_NOSLEEP);
if (vecp == NULL) {
cmn_err(CE_WARN, "apix: No memory for ipi");
goto fail;
}
xv_vector(cpun, vector) = vecp;
}
vecp->v_state = APIX_STATE_ALLOCED;
vecp->v_type = APIX_TYPE_IPI;
vecp->v_cpuid = vecp->v_bound_cpuid = cpun;
vecp->v_vector = vector;
vecp->v_pri = ipl;
}
APIX_LEAVE_CPU_LOCK(0);
return (vector);
fail:
while (--cpun >= 0)
apix_cleanup_vector(xv_vector(cpun, vector));
APIX_LEAVE_CPU_LOCK(0);
return (0);
}
static int
apix_add_ipi(int ipl, avfunc xxintr, char *name, int vector,
caddr_t arg1, caddr_t arg2)
{
int cpun;
apix_vector_t *vecp;
int nproc;
ASSERT(vector >= APIX_IPI_MIN && vector <= APIX_IPI_MAX);
nproc = max(apic_nproc, apic_max_nproc);
for (cpun = 0; cpun < nproc; cpun++) {
APIX_ENTER_CPU_LOCK(cpun);
vecp = xv_vector(cpun, vector);
apix_insert_av(vecp, NULL, xxintr, arg1, arg2, NULL, ipl, NULL);
vecp->v_state = APIX_STATE_ENABLED;
APIX_LEAVE_CPU_LOCK(cpun);
}
APIC_VERBOSE(IPI, (CE_CONT, "apix: add ipi for %s, vector %x "
"ipl %x\n", name, vector, ipl));
return (1);
}
static int
apix_get_avail_vector_oncpu(uint32_t cpuid, int start, int end)
{
int i;
apix_impl_t *apixp = apixs[cpuid];
for (i = start; i <= end; i++) {
if (APIC_CHECK_RESERVE_VECTORS(i))
continue;
if (IS_VECT_FREE(apixp->x_vectbl[i]))
return (i);
}
return (0);
}
static apix_vector_t *
apix_alloc_vector_oncpu(uint32_t cpuid, dev_info_t *dip, int inum, int type)
{
processorid_t tocpu = cpuid & ~IRQ_USER_BOUND;
apix_vector_t *vecp;
int vector;
ASSERT(APIX_CPU_LOCK_HELD(tocpu));
vector = apix_get_avail_vector_oncpu(tocpu, APIX_AVINTR_MIN,
APIX_AVINTR_MAX);
if (vector == 0)
return (NULL);
vecp = apix_init_vector(tocpu, vector);
vecp->v_type = (ushort_t)type;
vecp->v_inum = inum;
vecp->v_flags = (cpuid & IRQ_USER_BOUND) ? APIX_VECT_USER_BOUND : 0;
if (dip != NULL)
apix_set_dev_map(vecp, dip, inum);
return (vecp);
}
apix_vector_t *
apix_alloc_nvectors_oncpu(uint32_t cpuid, dev_info_t *dip, int inum,
int count, int type)
{
int i, msibits, start = 0, navail = 0;
apix_vector_t *vecp, *startp = NULL;
processorid_t tocpu = cpuid & ~IRQ_USER_BOUND;
uint_t flags;
ASSERT(APIX_CPU_LOCK_HELD(tocpu));
msibits = count - 1;
for (i = APIX_AVINTR_MIN; i <= APIX_AVINTR_MAX; i++) {
if (!IS_VECT_FREE(xv_vector(tocpu, i)))
continue;
if (msibits)
i = (i + msibits) & ~msibits;
for (navail = 0, start = i; i <= APIX_AVINTR_MAX; i++) {
if (!IS_VECT_FREE(xv_vector(tocpu, i)))
break;
if (APIC_CHECK_RESERVE_VECTORS(i))
break;
if (++navail == count)
goto done;
}
}
return (NULL);
done:
flags = (cpuid & IRQ_USER_BOUND) ? APIX_VECT_USER_BOUND : 0;
for (i = 0; i < count; i++) {
if ((vecp = apix_init_vector(tocpu, start + i)) == NULL)
goto fail;
vecp->v_type = (ushort_t)type;
vecp->v_inum = inum + i;
vecp->v_flags = flags;
if (dip != NULL)
apix_set_dev_map(vecp, dip, inum + i);
if (i == 0)
startp = vecp;
}
return (startp);
fail:
while (i-- > 0) {
vecp = xv_vector(tocpu, start + i);
apix_clear_dev_map(dip, inum + i, type);
apix_cleanup_vector(vecp);
}
return (NULL);
}
#define APIX_WRITE_MSI_DATA(_hdl, _cap, _ctrl, _v)\
do {\
if ((_ctrl) & PCI_MSI_64BIT_MASK)\
pci_config_put16((_hdl), (_cap) + PCI_MSI_64BIT_DATA, (_v));\
else\
pci_config_put16((_hdl), (_cap) + PCI_MSI_32BIT_DATA, (_v));\
_NOTE(CONSTCOND)} while (0)
static void
apix_pci_msi_enable_vector(apix_vector_t *vecp, dev_info_t *dip, int type,
int inum, int count, uchar_t vector, int target_apic_id)
{
uint64_t msi_addr, msi_data;
ushort_t msi_ctrl;
int i, cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip);
ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip);
msi_regs_t msi_regs;
void *intrmap_tbl[PCI_MSI_MAX_INTRS];
DDI_INTR_IMPLDBG((CE_CONT, "apix_pci_msi_enable_vector: dip=0x%p\n"
"\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip,
ddi_driver_name(dip), inum, vector, target_apic_id));
ASSERT((handle != NULL) && (cap_ptr != 0));
msi_regs.mr_data = vector;
msi_regs.mr_addr = target_apic_id;
for (i = 0; i < count; i++)
intrmap_tbl[i] = xv_intrmap_private(vecp->v_cpuid, vector + i);
apic_vt_ops->apic_intrmap_alloc_entry(intrmap_tbl, dip, type,
count, 0xff);
for (i = 0; i < count; i++)
xv_intrmap_private(vecp->v_cpuid, vector + i) = intrmap_tbl[i];
apic_vt_ops->apic_intrmap_map_entry(vecp->v_intrmap_private,
(void *)&msi_regs, type, count);
apic_vt_ops->apic_intrmap_record_msi(vecp->v_intrmap_private,
&msi_regs);
msi_addr = msi_regs.mr_addr;
msi_data = msi_regs.mr_data;
DDI_INTR_IMPLDBG((CE_CONT, "apix_pci_msi_enable_vector: addr=0x%lx "
"data=0x%lx\n", (long)msi_addr, (long)msi_data));
if (type == APIX_TYPE_MSI) {
msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
msi_ctrl |= ((highbit(count) - 1) << PCI_MSI_MME_SHIFT);
pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
if ((vecp->v_flags & APIX_VECT_MASKABLE) == 0)
APIX_WRITE_MSI_DATA(handle, cap_ptr, msi_ctrl,
APIX_RESV_VECTOR);
pci_config_put32(handle,
cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr);
if (msi_ctrl & PCI_MSI_64BIT_MASK)
pci_config_put32(handle,
cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32);
APIX_WRITE_MSI_DATA(handle, cap_ptr, msi_ctrl, msi_data);
} else if (type == APIX_TYPE_MSIX) {
uintptr_t off;
ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip);
off = (uintptr_t)msix_p->msix_tbl_addr +
(inum * PCI_MSIX_VECTOR_SIZE);
ddi_put32(msix_p->msix_tbl_hdl,
(uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data);
ddi_put32(msix_p->msix_tbl_hdl,
(uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr);
ddi_put32(msix_p->msix_tbl_hdl,
(uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET),
msi_addr >> 32);
}
}
static void
apix_pci_msi_enable_mode(dev_info_t *dip, int type, int inum)
{
ushort_t msi_ctrl;
int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip);
ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip);
ASSERT((handle != NULL) && (cap_ptr != 0));
if (type == APIX_TYPE_MSI) {
msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
if ((msi_ctrl & PCI_MSI_ENABLE_BIT))
return;
msi_ctrl |= PCI_MSI_ENABLE_BIT;
pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
} else if (type == DDI_INTR_TYPE_MSIX) {
uintptr_t off;
uint32_t mask;
ddi_intr_msix_t *msix_p;
msix_p = i_ddi_get_msix(dip);
off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off);
ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask & ~1));
msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL);
if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) {
msi_ctrl |= PCI_MSIX_ENABLE_BIT;
pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL,
msi_ctrl);
}
}
}
void
apix_enable_vector(apix_vector_t *vecp)
{
int tocpu = vecp->v_cpuid, type = vecp->v_type;
apic_cpus_info_t *cpu_infop;
ulong_t iflag;
ASSERT(tocpu < apic_nproc);
cpu_infop = &apic_cpus[tocpu];
if (vecp->v_flags & APIX_VECT_USER_BOUND)
cpu_infop->aci_bound++;
else
cpu_infop->aci_temp_bound++;
iflag = intr_clear();
lock_set(&apic_ioapic_lock);
if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
apix_intx_enable(vecp->v_inum);
} else {
int inum = vecp->v_inum;
dev_info_t *dip = APIX_GET_DIP(vecp);
int count = i_ddi_intr_get_current_nintrs(dip);
if (type == APIX_TYPE_MSI) {
if (inum == apix_get_max_dev_inum(dip, type)) {
uchar_t start_inum = inum + 1 - count;
uchar_t start_vect = vecp->v_vector + 1 - count;
apix_vector_t *start_vecp =
xv_vector(vecp->v_cpuid, start_vect);
APIC_VERBOSE(INTR, (CE_CONT, "apix: call "
"apix_pci_msi_enable_vector\n"));
apix_pci_msi_enable_vector(start_vecp, dip,
type, start_inum, count, start_vect,
cpu_infop->aci_local_id);
APIC_VERBOSE(INTR, (CE_CONT, "apix: call "
"apix_pci_msi_enable_mode\n"));
apix_pci_msi_enable_mode(dip, type, inum);
}
} else {
apix_pci_msi_enable_vector(vecp, dip,
type, inum, 1, vecp->v_vector,
cpu_infop->aci_local_id);
apix_pci_msi_enable_mode(dip, type, inum);
}
}
vecp->v_state = APIX_STATE_ENABLED;
apic_redist_cpu_skip &= ~(1 << tocpu);
lock_clear(&apic_ioapic_lock);
intr_restore(iflag);
}
void
apix_disable_vector(apix_vector_t *vecp)
{
struct autovec *avp = vecp->v_autovect;
ulong_t iflag;
ASSERT(avp != NULL);
iflag = intr_clear();
lock_set(&apic_ioapic_lock);
switch (vecp->v_type) {
case APIX_TYPE_MSI:
ASSERT(avp->av_vector != NULL && avp->av_dip != NULL);
if (i_ddi_intr_get_current_nenables(avp->av_dip) == 1) {
apic_pci_msi_disable_mode(avp->av_dip,
DDI_INTR_TYPE_MSI);
}
break;
case APIX_TYPE_MSIX:
ASSERT(avp->av_vector != NULL && avp->av_dip != NULL);
apic_pci_msi_unconfigure(avp->av_dip, DDI_INTR_TYPE_MSIX,
vecp->v_inum);
if (i_ddi_intr_get_current_nenables(avp->av_dip) == 1) {
apic_pci_msi_disable_mode(avp->av_dip,
DDI_INTR_TYPE_MSIX);
}
break;
default:
apix_intx_disable(vecp->v_inum);
break;
}
if (!(apic_cpus[vecp->v_cpuid].aci_status & APIC_CPU_SUSPEND))
vecp->v_state = APIX_STATE_DISABLED;
apic_vt_ops->apic_intrmap_free_entry(&vecp->v_intrmap_private);
vecp->v_intrmap_private = NULL;
lock_clear(&apic_ioapic_lock);
intr_restore(iflag);
}
#define INTR_BUSY(_avp)\
((((volatile ushort_t)(_avp)->av_flags) &\
(AV_PENTRY_PEND | AV_PENTRY_ONPROC)) != 0)
#define LOCAL_WITH_INTR_DISABLED(_cpuid)\
((_cpuid) == psm_get_cpu_id() && !interrupts_enabled())
static uint64_t dummy_tick;
int
apix_obsolete_vector(apix_vector_t *vecp)
{
struct autovec *avp = vecp->v_autovect;
int repeats, tries, ipl, busy = 0, cpuid = vecp->v_cpuid;
apix_impl_t *apixp = apixs[cpuid];
ASSERT(APIX_CPU_LOCK_HELD(cpuid));
for (avp = vecp->v_autovect; avp != NULL; avp = avp->av_link) {
if (avp->av_vector == NULL)
continue;
if (LOCAL_WITH_INTR_DISABLED(cpuid)) {
int bit, index, irr;
if (INTR_BUSY(avp)) {
busy++;
continue;
}
index = vecp->v_vector / 32;
bit = vecp->v_vector % 32;
irr = apic_reg_ops->apic_read(APIC_IRR_REG + index);
if ((irr & (1 << bit)) != 0)
busy++;
if (!busy)
apix_remove_av(vecp, avp);
continue;
}
repeats = 0;
do {
repeats++;
for (tries = 0; tries < apic_max_reps_clear_pending;
tries++)
if (!INTR_BUSY(avp))
break;
} while (INTR_BUSY(avp) &&
(repeats < apic_max_reps_clear_pending));
if (INTR_BUSY(avp))
busy++;
else {
for (ipl = 1; ipl < MIN(LOCK_LEVEL, vecp->v_pri); ipl++)
apix_wait_till_seen(cpuid, ipl);
if (INTR_BUSY(avp))
busy++;
}
if (!busy)
apix_remove_av(vecp, avp);
}
if (busy) {
apix_vector_t *tp = apixp->x_obsoletes;
if (vecp->v_state == APIX_STATE_OBSOLETED)
return (1);
vecp->v_state = APIX_STATE_OBSOLETED;
vecp->v_next = NULL;
if (tp == NULL)
apixp->x_obsoletes = vecp;
else {
while (tp->v_next != NULL)
tp = tp->v_next;
tp->v_next = vecp;
}
return (1);
}
if (vecp->v_state == APIX_STATE_OBSOLETED) {
apixp->x_obsoletes = vecp->v_next;
vecp->v_next = NULL;
}
apix_cleanup_vector(vecp);
return (0);
}
static void
apix_dup_vectors(apix_vector_t *oldp, apix_vector_t *newp, int count)
{
struct autovec *avp;
apix_vector_t *fromp, *top;
processorid_t oldcpu = oldp->v_cpuid, newcpu = newp->v_cpuid;
uchar_t oldvec = oldp->v_vector, newvec = newp->v_vector;
int i, inum;
ASSERT(oldp->v_type != APIX_TYPE_IPI);
for (i = 0; i < count; i++) {
fromp = xv_vector(oldcpu, oldvec + i);
top = xv_vector(newcpu, newvec + i);
ASSERT(fromp != NULL && top != NULL);
top->v_state = fromp->v_state;
top->v_type = fromp->v_type;
top->v_bound_cpuid = fromp->v_bound_cpuid;
top->v_inum = fromp->v_inum;
top->v_flags = fromp->v_flags;
top->v_intrmap_private = fromp->v_intrmap_private;
for (avp = fromp->v_autovect; avp != NULL; avp = avp->av_link) {
if (avp->av_vector == NULL)
continue;
apix_insert_av(top, avp->av_intr_id, avp->av_vector,
avp->av_intarg1, avp->av_intarg2, avp->av_ticksp,
avp->av_prilevel, avp->av_dip);
if (fromp->v_type == APIX_TYPE_FIXED &&
avp->av_dip != NULL) {
inum = GET_INTR_INUM(avp->av_intr_id);
apix_set_dev_map(top, avp->av_dip, inum);
}
}
if (DDI_INTR_IS_MSI_OR_MSIX(fromp->v_type) &&
fromp->v_devp != NULL)
apix_set_dev_map(top, fromp->v_devp->dv_dip,
fromp->v_devp->dv_inum);
}
}
static apix_vector_t *
apix_init_vector(processorid_t cpuid, uchar_t vector)
{
apix_impl_t *apixp = apixs[cpuid];
apix_vector_t *vecp = apixp->x_vectbl[vector];
ASSERT(IS_VECT_FREE(vecp));
if (vecp == NULL) {
vecp = kmem_zalloc(sizeof (apix_vector_t), KM_NOSLEEP);
if (vecp == NULL) {
cmn_err(CE_WARN, "apix: no memory to allocate vector");
return (NULL);
}
apixp->x_vectbl[vector] = vecp;
}
vecp->v_state = APIX_STATE_ALLOCED;
vecp->v_cpuid = vecp->v_bound_cpuid = cpuid;
vecp->v_vector = vector;
return (vecp);
}
static void
apix_cleanup_vector(apix_vector_t *vecp)
{
ASSERT(vecp->v_share == 0);
vecp->v_bound_cpuid = IRQ_UNINIT;
vecp->v_state = APIX_STATE_FREED;
vecp->v_type = 0;
vecp->v_flags = 0;
vecp->v_busy = 0;
vecp->v_intrmap_private = NULL;
}
static void
apix_dprint_vector(apix_vector_t *vecp, dev_info_t *dip, int count)
{
#ifdef DEBUG
major_t major;
char *name, *drv_name;
int instance, len, t_len;
char mesg[1024] = "apix: ";
t_len = sizeof (mesg);
len = strlen(mesg);
if (dip != NULL) {
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
drv_name = ddi_major_to_name(major);
instance = ddi_get_instance(dip);
(void) snprintf(mesg + len, t_len - len, "%s (%s) instance %d ",
name, drv_name, instance);
}
len = strlen(mesg);
switch (vecp->v_type) {
case APIX_TYPE_FIXED:
(void) snprintf(mesg + len, t_len - len, "irqno %d",
vecp->v_inum);
break;
case APIX_TYPE_MSI:
(void) snprintf(mesg + len, t_len - len,
"msi inum %d (count %d)", vecp->v_inum, count);
break;
case APIX_TYPE_MSIX:
(void) snprintf(mesg + len, t_len - len, "msi-x inum %d",
vecp->v_inum);
break;
default:
break;
}
APIC_VERBOSE(ALLOC, (CE_CONT, "%s allocated with vector 0x%x on "
"cpu %d\n", mesg, vecp->v_vector, vecp->v_cpuid));
#endif
}
#define INIT_AUTOVEC(p, intr_id, f, arg1, arg2, ticksp, ipl, dip) \
do { \
(p)->av_intr_id = intr_id; \
(p)->av_vector = f; \
(p)->av_intarg1 = arg1; \
(p)->av_intarg2 = arg2; \
(p)->av_ticksp = ticksp; \
(p)->av_prilevel = ipl; \
(p)->av_dip = dip; \
(p)->av_flags = 0; \
_NOTE(CONSTCOND)} while (0)
static void
apix_insert_av(apix_vector_t *vecp, void *intr_id, avfunc f, caddr_t arg1,
caddr_t arg2, uint64_t *ticksp, int ipl, dev_info_t *dip)
{
struct autovec *p, *prep, *mem;
APIC_VERBOSE(INTR, (CE_CONT, "apix_insert_av: dip %p, vector 0x%x, "
"cpu %d\n", (void *)dip, vecp->v_vector, vecp->v_cpuid));
mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP);
INIT_AUTOVEC(mem, intr_id, f, arg1, arg2, ticksp, ipl, dip);
if (vecp->v_type == APIX_TYPE_FIXED && apic_level_intr[vecp->v_inum])
mem->av_flags |= AV_PENTRY_LEVEL;
vecp->v_share++;
vecp->v_pri = (ipl > vecp->v_pri) ? ipl : vecp->v_pri;
smt_intr_alloc_pil(vecp->v_pri);
if (vecp->v_autovect == NULL) {
vecp->v_autovect = mem;
return;
}
if (DDI_INTR_IS_MSI_OR_MSIX(vecp->v_type)) {
ASSERT(vecp->v_share == 1);
INIT_AUTOVEC(vecp->v_autovect, intr_id, f, arg1, arg2, ticksp,
ipl, dip);
prep = vecp->v_autovect->av_link;
vecp->v_autovect->av_link = NULL;
while (prep != NULL) {
ASSERT(prep->av_vector == NULL);
p = prep;
prep = prep->av_link;
kmem_free(p, sizeof (struct autovec));
}
kmem_free(mem, sizeof (struct autovec));
return;
}
prep = NULL;
for (p = vecp->v_autovect; p != NULL; p = p->av_link) {
if (p->av_vector && p->av_prilevel <= ipl)
break;
prep = p;
}
if (prep != NULL) {
if (prep->av_vector == NULL) {
INIT_AUTOVEC(prep, intr_id, f, arg1, arg2,
ticksp, ipl, dip);
prep->av_flags = mem->av_flags;
kmem_free(mem, sizeof (struct autovec));
return;
}
mem->av_link = prep->av_link;
prep->av_link = mem;
} else {
mem->av_link = vecp->v_autovect;
vecp->v_autovect = mem;
}
}
#define APIX_CPU_ENABLED(_cp) \
(quiesce_active == 0 && \
(((_cp)->cpu_flags & (CPU_QUIESCED|CPU_OFFLINE)) == 0))
static void
apix_wait_till_seen(processorid_t cpuid, int ipl)
{
struct cpu *cp = cpu[cpuid];
if (cp == NULL || LOCAL_WITH_INTR_DISABLED(cpuid))
return;
for (;;) {
if (!INTR_ACTIVE((volatile struct cpu *)cpu[cpuid], ipl) &&
(!APIX_CPU_ENABLED(cp) ||
!INTR_PENDING((volatile apix_impl_t *)apixs[cpuid], ipl)))
return;
}
}
static void
apix_remove_av(apix_vector_t *vecp, struct autovec *target)
{
int hi_pri = 0;
struct autovec *p;
if (target == NULL)
return;
APIC_VERBOSE(INTR, (CE_CONT, "apix_remove_av: dip %p, vector 0x%x, "
"cpu %d\n", (void *)target->av_dip, vecp->v_vector, vecp->v_cpuid));
for (p = vecp->v_autovect; p; p = p->av_link) {
if (p == target || p->av_vector == NULL)
continue;
hi_pri = (p->av_prilevel > hi_pri) ? p->av_prilevel : hi_pri;
}
vecp->v_share--;
vecp->v_pri = hi_pri;
target->av_vector = NULL;
target->av_ticksp = &dummy_tick;
apix_wait_till_seen(vecp->v_cpuid, target->av_prilevel);
}
static struct autovec *
apix_find_av(apix_vector_t *vecp, void *intr_id, avfunc f)
{
struct autovec *p;
for (p = vecp->v_autovect; p; p = p->av_link) {
if ((p->av_vector == f) && (p->av_intr_id == intr_id)) {
return (p);
}
}
return (NULL);
}
static apix_vector_t *
apix_find_vector_by_avintr(void *intr_id, avfunc f)
{
apix_vector_t *vecp;
processorid_t n;
uchar_t v;
for (n = 0; n < apic_nproc; n++) {
if (!apix_is_cpu_enabled(n))
continue;
for (v = APIX_AVINTR_MIN; v <= APIX_AVINTR_MAX; v++) {
vecp = xv_vector(n, v);
if (vecp == NULL ||
vecp->v_state <= APIX_STATE_OBSOLETED)
continue;
if (apix_find_av(vecp, intr_id, f) != NULL)
return (vecp);
}
}
return (NULL);
}
int
apix_add_avintr(void *intr_id, int ipl, avfunc xxintr, char *name,
int virt_vect, caddr_t arg1, caddr_t arg2, uint64_t *ticksp,
dev_info_t *dip)
{
int cpuid;
uchar_t v = (uchar_t)APIX_VIRTVEC_VECTOR(virt_vect);
apix_vector_t *vecp;
if (xxintr == NULL) {
cmn_err(CE_WARN, "Attempt to add null for %s "
"on vector 0x%x,0x%x", name,
APIX_VIRTVEC_CPU(virt_vect),
APIX_VIRTVEC_VECTOR(virt_vect));
return (0);
}
if (v >= APIX_IPI_MIN)
return (apix_add_ipi(ipl, xxintr, name, v, arg1, arg2));
if (!APIX_IS_VIRTVEC(virt_vect)) {
int irqno = virt_vect;
int inum = GET_INTR_INUM(intr_id);
if ((vecp = apix_alloc_intx(dip, inum, irqno)) == NULL)
return (0);
cpuid = vecp->v_cpuid;
v = vecp->v_vector;
virt_vect = APIX_VIRTVECTOR(cpuid, v);
} else {
cpuid = APIX_VIRTVEC_CPU(virt_vect);
vecp = xv_vector(cpuid, v);
ASSERT(vecp != NULL);
}
lock_set(&apix_lock);
if (vecp->v_state <= APIX_STATE_OBSOLETED) {
vecp = NULL;
if (intr_id != NULL && dip != NULL) {
ddi_intr_handle_impl_t *hdlp = intr_id;
vecp = apix_get_dev_map(dip, hdlp->ih_inum,
hdlp->ih_type);
ASSERT(vecp->v_state == APIX_STATE_ALLOCED);
}
if (vecp == NULL) {
lock_clear(&apix_lock);
cmn_err(CE_WARN, "Invalid interrupt 0x%x,0x%x "
" for %p to add", cpuid, v, intr_id);
return (0);
}
cpuid = vecp->v_cpuid;
virt_vect = APIX_VIRTVECTOR(cpuid, vecp->v_vector);
}
APIX_ENTER_CPU_LOCK(cpuid);
apix_insert_av(vecp, intr_id, xxintr, arg1, arg2, ticksp, ipl, dip);
APIX_LEAVE_CPU_LOCK(cpuid);
(void) apix_addspl(virt_vect, ipl, 0, 0);
lock_clear(&apix_lock);
return (1);
}
void
apix_rem_avintr(void *intr_id, int ipl, avfunc xxintr, int virt_vect)
{
avfunc f;
apix_vector_t *vecp;
struct autovec *avp;
processorid_t cpuid;
if ((f = xxintr) == NULL)
return;
lock_set(&apix_lock);
if (!APIX_IS_VIRTVEC(virt_vect)) {
vecp = apix_intx_get_vector(virt_vect);
virt_vect = APIX_VIRTVECTOR(vecp->v_cpuid, vecp->v_vector);
} else
vecp = xv_vector(APIX_VIRTVEC_CPU(virt_vect),
APIX_VIRTVEC_VECTOR(virt_vect));
if (vecp == NULL) {
lock_clear(&apix_lock);
cmn_err(CE_CONT, "Invalid interrupt 0x%x,0x%x to remove",
APIX_VIRTVEC_CPU(virt_vect),
APIX_VIRTVEC_VECTOR(virt_vect));
return;
}
if (vecp->v_state <= APIX_STATE_OBSOLETED ||
((avp = apix_find_av(vecp, intr_id, f)) == NULL)) {
if ((vecp = apix_find_vector_by_avintr(intr_id, f))
== NULL) {
lock_clear(&apix_lock);
cmn_err(CE_CONT, "Unknown interrupt 0x%x,0x%x "
"for %p to remove", APIX_VIRTVEC_CPU(virt_vect),
APIX_VIRTVEC_VECTOR(virt_vect), intr_id);
return;
}
virt_vect = APIX_VIRTVECTOR(vecp->v_cpuid, vecp->v_vector);
avp = apix_find_av(vecp, intr_id, f);
}
cpuid = vecp->v_cpuid;
(void) apix_delspl(virt_vect, ipl, 0, 0);
APIX_ENTER_CPU_LOCK(cpuid);
apix_remove_av(vecp, avp);
APIX_LEAVE_CPU_LOCK(cpuid);
lock_clear(&apix_lock);
}
static void
apix_clear_dev_map(dev_info_t *dip, int inum, int type)
{
char *name;
major_t major;
apix_dev_vector_t *dvp, *prev = NULL;
int found = 0;
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
mutex_enter(&apix_mutex);
for (dvp = apix_dev_vector[major]; dvp != NULL;
prev = dvp, dvp = dvp->dv_next) {
if (dvp->dv_dip == dip && dvp->dv_inum == inum &&
dvp->dv_type == type) {
found++;
break;
}
}
if (!found) {
mutex_exit(&apix_mutex);
return;
}
if (prev != NULL)
prev->dv_next = dvp->dv_next;
if (apix_dev_vector[major] == dvp)
apix_dev_vector[major] = dvp->dv_next;
dvp->dv_vector->v_devp = NULL;
mutex_exit(&apix_mutex);
kmem_free(dvp, sizeof (apix_dev_vector_t));
}
void
apix_set_dev_map(apix_vector_t *vecp, dev_info_t *dip, int inum)
{
apix_dev_vector_t *dvp;
char *name;
major_t major;
uint32_t found = 0;
ASSERT(dip != NULL);
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
mutex_enter(&apix_mutex);
for (dvp = apix_dev_vector[major]; dvp != NULL;
dvp = dvp->dv_next) {
if (dvp->dv_dip == dip && dvp->dv_inum == inum &&
dvp->dv_type == vecp->v_type) {
found++;
break;
}
}
if (found == 0) {
dvp = kmem_zalloc(sizeof (apix_dev_vector_t), KM_SLEEP);
dvp->dv_dip = dip;
dvp->dv_inum = inum;
dvp->dv_type = vecp->v_type;
dvp->dv_next = apix_dev_vector[major];
apix_dev_vector[major] = dvp;
}
dvp->dv_vector = vecp;
vecp->v_devp = dvp;
mutex_exit(&apix_mutex);
DDI_INTR_IMPLDBG((CE_CONT, "apix_set_dev_map: dip=0x%p "
"inum=0x%x vector=0x%x/0x%x\n",
(void *)dip, inum, vecp->v_cpuid, vecp->v_vector));
}
apix_vector_t *
apix_get_dev_map(dev_info_t *dip, int inum, int type)
{
char *name;
major_t major;
apix_dev_vector_t *dvp;
apix_vector_t *vecp;
name = ddi_get_name(dip);
if ((major = ddi_name_to_major(name)) == DDI_MAJOR_T_NONE)
return (NULL);
mutex_enter(&apix_mutex);
for (dvp = apix_dev_vector[major]; dvp != NULL;
dvp = dvp->dv_next) {
if (dvp->dv_dip == dip && dvp->dv_inum == inum &&
dvp->dv_type == type) {
vecp = dvp->dv_vector;
mutex_exit(&apix_mutex);
return (vecp);
}
}
mutex_exit(&apix_mutex);
return (NULL);
}
int
apix_get_min_dev_inum(dev_info_t *dip, int type)
{
char *name;
major_t major;
apix_dev_vector_t *dvp;
int inum = -1;
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
mutex_enter(&apix_mutex);
for (dvp = apix_dev_vector[major]; dvp != NULL;
dvp = dvp->dv_next) {
if (dvp->dv_dip == dip && dvp->dv_type == type) {
if (inum == -1)
inum = dvp->dv_inum;
else
inum = (dvp->dv_inum < inum) ?
dvp->dv_inum : inum;
}
}
mutex_exit(&apix_mutex);
return (inum);
}
int
apix_get_max_dev_inum(dev_info_t *dip, int type)
{
char *name;
major_t major;
apix_dev_vector_t *dvp;
int inum = -1;
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
mutex_enter(&apix_mutex);
for (dvp = apix_dev_vector[major]; dvp != NULL;
dvp = dvp->dv_next) {
if (dvp->dv_dip == dip && dvp->dv_type == type) {
if (inum == -1)
inum = dvp->dv_inum;
else
inum = (dvp->dv_inum > inum) ?
dvp->dv_inum : inum;
}
}
mutex_exit(&apix_mutex);
return (inum);
}
static uint32_t
apix_get_dev_binding(dev_info_t *dip)
{
major_t major;
char *name;
uint32_t cpu = IRQ_UNINIT;
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
if (major < devcnt) {
mutex_enter(&apix_mutex);
cpu = apix_major_to_cpu[major];
mutex_exit(&apix_mutex);
}
return (cpu);
}
static void
apix_set_dev_binding(dev_info_t *dip, uint32_t cpu)
{
major_t major;
char *name;
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
if (apix_major_to_cpu[major] == IRQ_UNINIT) {
mutex_enter(&apix_mutex);
apix_major_to_cpu[major] = cpu;
mutex_exit(&apix_mutex);
}
}
uint32_t
apix_bind_cpu(dev_info_t *dip)
{
int instance, instno, prop_len, bind_cpu, count;
uint_t i, rc;
major_t major;
char *name, *drv_name, *prop_val, *cptr;
char prop_name[32];
lock_set(&apix_lock);
if (apic_intr_policy == INTR_LOWEST_PRIORITY) {
cmn_err(CE_WARN, "apix: unsupported interrupt binding policy "
"LOWEST PRIORITY, use ROUND ROBIN instead");
apic_intr_policy = INTR_ROUND_ROBIN;
}
if (apic_nproc == 1) {
lock_clear(&apix_lock);
return (0);
}
drv_name = NULL;
rc = DDI_PROP_NOT_FOUND;
major = (major_t)-1;
if (dip != NULL) {
name = ddi_get_name(dip);
major = ddi_name_to_major(name);
drv_name = ddi_major_to_name(major);
instance = ddi_get_instance(dip);
if (apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) {
bind_cpu = apix_get_dev_binding(dip);
if (bind_cpu != IRQ_UNINIT) {
lock_clear(&apix_lock);
return (bind_cpu);
}
}
(void) strcpy(prop_name, drv_name);
(void) strcat(prop_name, "_intpt_bind_cpus");
rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, prop_name,
(caddr_t)&prop_val, &prop_len);
if (rc != DDI_PROP_SUCCESS) {
rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0,
"intpt_bind_cpus", (caddr_t)&prop_val, &prop_len);
}
}
if (rc == DDI_PROP_SUCCESS) {
for (i = count = 0; i < (prop_len - 1); i++)
if (prop_val[i] == ',')
count++;
if (prop_val[i-1] != ',')
count++;
instno = instance % count;
i = 0;
cptr = prop_val;
while (i < instno)
if (*cptr++ == ',')
i++;
bind_cpu = stoi(&cptr);
if (bind_cpu >= apic_nproc) {
cmn_err(CE_WARN, "apix: %s=%s: CPU %d not present",
prop_name, prop_val, bind_cpu);
bind_cpu = 0;
} else {
bind_cpu |= IRQ_USER_BOUND;
}
kmem_free(prop_val, prop_len);
} else {
bind_cpu = apic_get_next_bind_cpu();
}
lock_clear(&apix_lock);
return ((uint32_t)bind_cpu);
}
static boolean_t
apix_is_cpu_enabled(processorid_t cpuid)
{
apic_cpus_info_t *cpu_infop;
cpu_infop = &apic_cpus[cpuid];
if ((cpu_infop->aci_status & APIC_CPU_INTR_ENABLE) == 0)
return (B_FALSE);
return (B_TRUE);
}
apix_vector_t *
apix_rebind(apix_vector_t *vecp, processorid_t newcpu, int count)
{
apix_vector_t *newp, *oldp;
processorid_t oldcpu = vecp->v_cpuid;
uchar_t newvec, oldvec = vecp->v_vector;
int i;
ASSERT(LOCK_HELD(&apix_lock) && count > 0);
if (!apix_is_cpu_enabled(newcpu))
return (NULL);
if (vecp->v_cpuid == newcpu)
return (vecp);
APIX_ENTER_CPU_LOCK(oldcpu);
APIX_ENTER_CPU_LOCK(newcpu);
if (count == 1)
newp = apix_alloc_vector_oncpu(newcpu, NULL, 0, vecp->v_type);
else {
ASSERT(vecp->v_type == APIX_TYPE_MSI);
newp = apix_alloc_nvectors_oncpu(newcpu, NULL, 0, count,
vecp->v_type);
}
if (newp == NULL) {
APIX_LEAVE_CPU_LOCK(newcpu);
APIX_LEAVE_CPU_LOCK(oldcpu);
return (NULL);
}
newvec = newp->v_vector;
apix_dup_vectors(vecp, newp, count);
APIX_LEAVE_CPU_LOCK(newcpu);
APIX_LEAVE_CPU_LOCK(oldcpu);
if (!DDI_INTR_IS_MSI_OR_MSIX(vecp->v_type)) {
ASSERT(count == 1);
if (apix_intx_rebind(vecp->v_inum, newcpu, newvec) != 0) {
struct autovec *avp;
int inum;
APIX_ENTER_CPU_LOCK(oldcpu);
APIX_ENTER_CPU_LOCK(newcpu);
for (avp = newp->v_autovect; avp != NULL;
avp = avp->av_link) {
if (avp->av_dip != NULL) {
inum = GET_INTR_INUM(avp->av_intr_id);
apix_set_dev_map(vecp, avp->av_dip,
inum);
}
apix_remove_av(newp, avp);
}
apix_cleanup_vector(newp);
APIX_LEAVE_CPU_LOCK(newcpu);
APIX_LEAVE_CPU_LOCK(oldcpu);
APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind fixed "
"interrupt 0x%x to cpu %d failed\n",
vecp->v_inum, newcpu));
return (NULL);
}
APIX_ENTER_CPU_LOCK(oldcpu);
(void) apix_obsolete_vector(vecp);
APIX_LEAVE_CPU_LOCK(oldcpu);
APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind fixed interrupt"
" 0x%x/0x%x to 0x%x/0x%x\n",
oldcpu, oldvec, newcpu, newvec));
return (newp);
}
for (i = 0; i < count; i++) {
oldp = xv_vector(oldcpu, oldvec + i);
newp = xv_vector(newcpu, newvec + i);
if (newp->v_share > 0) {
APIX_SET_REBIND_INFO(oldp, newp);
apix_enable_vector(newp);
APIX_CLR_REBIND_INFO();
}
APIX_ENTER_CPU_LOCK(oldcpu);
(void) apix_obsolete_vector(oldp);
APIX_LEAVE_CPU_LOCK(oldcpu);
}
APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind vector 0x%x/0x%x "
"to 0x%x/0x%x, count=%d\n",
oldcpu, oldvec, newcpu, newvec, count));
return (xv_vector(newcpu, newvec));
}
apix_vector_t *
apix_alloc_intx(dev_info_t *dip, int inum, int irqno)
{
apic_irq_t *irqp;
apix_vector_t *vecp;
mutex_enter(&airq_mutex);
if ((irqp = apic_irq_table[irqno]) == NULL) {
irqp = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP);
irqp->airq_mps_intr_index = FREE_INDEX;
apic_irq_table[irqno] = irqp;
}
if (irqp->airq_mps_intr_index == FREE_INDEX) {
irqp->airq_mps_intr_index = DEFAULT_INDEX;
irqp->airq_cpu = IRQ_UNINIT;
irqp->airq_origirq = (uchar_t)irqno;
}
mutex_exit(&airq_mutex);
if (irqp->airq_cpu == IRQ_UNINIT) {
uint32_t bindcpu, cpuid;
bindcpu = apix_bind_cpu(dip);
cpuid = bindcpu & ~IRQ_USER_BOUND;
APIX_ENTER_CPU_LOCK(cpuid);
if ((vecp = apix_alloc_vector_oncpu(bindcpu, dip, inum,
APIX_TYPE_FIXED)) == NULL) {
cmn_err(CE_WARN, "No interrupt vector for irq %x",
irqno);
APIX_LEAVE_CPU_LOCK(cpuid);
return (NULL);
}
vecp->v_inum = irqno;
vecp->v_flags |= APIX_VECT_MASKABLE;
apix_intx_set_vector(irqno, vecp->v_cpuid, vecp->v_vector);
APIX_LEAVE_CPU_LOCK(cpuid);
} else {
vecp = xv_vector(irqp->airq_cpu, irqp->airq_vector);
ASSERT(!IS_VECT_FREE(vecp));
if (dip != NULL)
apix_set_dev_map(vecp, dip, inum);
}
if ((dip != NULL) &&
(apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) &&
((vecp->v_flags & APIX_VECT_USER_BOUND) == 0))
apix_set_dev_binding(dip, vecp->v_cpuid);
apix_dprint_vector(vecp, dip, 1);
return (vecp);
}
int
apix_alloc_msi(dev_info_t *dip, int inum, int count, int behavior)
{
int i, cap_ptr, rcount = count;
apix_vector_t *vecp;
processorid_t bindcpu, cpuid;
ushort_t msi_ctrl;
ddi_acc_handle_t handle;
DDI_INTR_IMPLDBG((CE_CONT, "apix_alloc_msi_vectors: dip=0x%p "
"inum=0x%x count=0x%x behavior=%d\n",
(void *)dip, inum, count, behavior));
if (count > 1) {
if (behavior == DDI_INTR_ALLOC_STRICT &&
apic_multi_msi_enable == 0)
return (0);
if (apic_multi_msi_enable == 0)
count = 1;
}
cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip);
handle = i_ddi_get_pci_config_handle(dip);
msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
bindcpu = apix_bind_cpu(dip);
cpuid = bindcpu & ~IRQ_USER_BOUND;
if (!ISP2(rcount))
rcount = 1 << (highbit(rcount) - 1);
APIX_ENTER_CPU_LOCK(cpuid);
for (vecp = NULL; rcount > 0; rcount >>= 1) {
vecp = apix_alloc_nvectors_oncpu(bindcpu, dip, inum, rcount,
APIX_TYPE_MSI);
if (vecp != NULL || behavior == DDI_INTR_ALLOC_STRICT)
break;
}
for (i = 0; vecp && i < rcount; i++)
xv_vector(vecp->v_cpuid, vecp->v_vector + i)->v_flags |=
(msi_ctrl & PCI_MSI_PVM_MASK) ? APIX_VECT_MASKABLE : 0;
APIX_LEAVE_CPU_LOCK(cpuid);
if (vecp == NULL) {
APIC_VERBOSE(INTR, (CE_CONT,
"apix_alloc_msi: no %d cont vectors found on cpu 0x%x\n",
count, bindcpu));
return (0);
}
if ((apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) &&
((vecp->v_flags & APIX_VECT_USER_BOUND) == 0))
apix_set_dev_binding(dip, vecp->v_cpuid);
apix_dprint_vector(vecp, dip, rcount);
return (rcount);
}
int
apix_alloc_msix(dev_info_t *dip, int inum, int count, int behavior)
{
apix_vector_t *vecp;
processorid_t bindcpu, cpuid;
int i;
for (i = 0; i < count; i++) {
bindcpu = apix_bind_cpu(dip);
cpuid = bindcpu & ~IRQ_USER_BOUND;
APIX_ENTER_CPU_LOCK(cpuid);
if ((vecp = apix_alloc_vector_oncpu(bindcpu, dip, inum + i,
APIX_TYPE_MSIX)) == NULL) {
APIX_LEAVE_CPU_LOCK(cpuid);
APIC_VERBOSE(INTR, (CE_CONT, "apix_alloc_msix: "
"allocate msix for device dip=%p, inum=%d on"
" cpu %d failed", (void *)dip, inum + i, bindcpu));
break;
}
vecp->v_flags |= APIX_VECT_MASKABLE;
APIX_LEAVE_CPU_LOCK(cpuid);
if ((i == 0) &&
(apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) &&
((vecp->v_flags & APIX_VECT_USER_BOUND) == 0))
apix_set_dev_binding(dip, vecp->v_cpuid);
apix_dprint_vector(vecp, dip, 1);
}
if (i < count && behavior == DDI_INTR_ALLOC_STRICT) {
APIC_VERBOSE(INTR, (CE_WARN, "apix_alloc_msix: "
"strictly allocate %d vectors failed, got %d\n",
count, i));
apix_free_vectors(dip, inum, i, APIX_TYPE_MSIX);
i = 0;
}
return (i);
}
void
apix_free_vectors(dev_info_t *dip, int inum, int count, int type)
{
int i, cpuid;
apix_vector_t *vecp;
DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: dip: %p inum: %x "
"count: %x type: %x\n",
(void *)dip, inum, count, type));
lock_set(&apix_lock);
for (i = 0; i < count; i++, inum++) {
if ((vecp = apix_get_dev_map(dip, inum, type)) == NULL) {
lock_clear(&apix_lock);
DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: "
"dip=0x%p inum=0x%x type=0x%x apix_find_intr() "
"failed\n", (void *)dip, inum, type));
continue;
}
APIX_ENTER_CPU_LOCK(vecp->v_cpuid);
cpuid = vecp->v_cpuid;
DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: "
"dip=0x%p inum=0x%x type=0x%x vector 0x%x (share %d)\n",
(void *)dip, inum, type, vecp->v_vector, vecp->v_share));
apix_clear_dev_map(dip, inum, type);
if (vecp->v_type == APIX_TYPE_FIXED) {
if (vecp->v_share > 0) {
APIX_LEAVE_CPU_LOCK(cpuid);
continue;
}
apix_intx_free(vecp->v_inum);
}
apix_cleanup_vector(vecp);
APIX_LEAVE_CPU_LOCK(cpuid);
}
lock_clear(&apix_lock);
}
apix_vector_t *
apix_setup_io_intr(apix_vector_t *vecp)
{
processorid_t bindcpu;
int ret;
ASSERT(LOCK_HELD(&apix_lock));
if (apix_is_cpu_enabled(vecp->v_cpuid)) {
apix_enable_vector(vecp);
return (vecp);
}
bindcpu = apic_find_cpu(APIC_CPU_INTR_ENABLE);
if (vecp->v_type == APIX_TYPE_MSI)
return (apix_grp_set_cpu(vecp, bindcpu, &ret));
return (apix_set_cpu(vecp, bindcpu, &ret));
}
static void
ioapix_setup_intr(int irqno, iflag_t *flagp)
{
extern struct av_head autovect[];
apix_vector_t *vecp;
apic_irq_t *irqp;
uchar_t ioapicindex, ipin;
ulong_t iflag;
struct autovec *avp;
ioapicindex = acpi_find_ioapic(irqno);
ASSERT(ioapicindex != 0xFF);
ipin = irqno - apic_io_vectbase[ioapicindex];
mutex_enter(&airq_mutex);
irqp = apic_irq_table[irqno];
if (irqp != NULL) {
ASSERT(irqp->airq_mps_intr_index == ACPI_INDEX);
ASSERT(irqp->airq_intin_no == ipin &&
irqp->airq_ioapicindex == ioapicindex);
vecp = xv_vector(irqp->airq_cpu, irqp->airq_vector);
ASSERT(!IS_VECT_FREE(vecp));
mutex_exit(&airq_mutex);
} else {
irqp = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP);
irqp->airq_cpu = IRQ_UNINIT;
irqp->airq_origirq = (uchar_t)irqno;
irqp->airq_mps_intr_index = ACPI_INDEX;
irqp->airq_ioapicindex = ioapicindex;
irqp->airq_intin_no = ipin;
irqp->airq_iflag = *flagp;
irqp->airq_share++;
apic_irq_table[irqno] = irqp;
mutex_exit(&airq_mutex);
vecp = apix_alloc_intx(NULL, 0, irqno);
}
for (avp = autovect[irqno].avh_link; avp; avp = avp->av_link)
apix_insert_av(vecp, avp->av_intr_id, avp->av_vector,
avp->av_intarg1, avp->av_intarg2, avp->av_ticksp,
avp->av_prilevel, avp->av_dip);
iflag = intr_clear();
lock_set(&apix_lock);
(void) apix_setup_io_intr(vecp);
lock_clear(&apix_lock);
intr_restore(iflag);
APIC_VERBOSE_IOAPIC((CE_CONT, "apix: setup ioapic, irqno %x "
"(ioapic %x, ipin %x) is bound to cpu %x, vector %x\n",
irqno, ioapicindex, ipin, irqp->airq_cpu, irqp->airq_vector));
}
void
ioapix_init_intr(int mask_apic)
{
int ioapicindex;
int i, j;
for (j = 0; j < apic_io_max && mask_apic; j++) {
int intin_max;
ioapicindex = j;
intin_max = (ioapic_read(ioapicindex, APIC_VERS_CMD) >> 16)
& 0xff;
for (i = 0; i <= intin_max; i++)
ioapic_write(ioapicindex, APIC_RDT_CMD + 2 * i,
AV_MASK);
}
if (apic_sci_vect > 0)
ioapix_setup_intr(apic_sci_vect, &apic_sci_flags);
if (apic_hpet_vect > 0)
ioapix_setup_intr(apic_hpet_vect, &apic_hpet_flags);
}