#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/proc.h>
#include <uvm/uvm_extern.h>
#include <machine/cpu.h>
#include <machine/intr.h>
int mips64_ipi_intr(void *);
void mips64_ipi_nop(void);
void smp_rendezvous_action(void);
void mips64_ipi_ddb(void);
void mips64_multicast_ipi(unsigned int, unsigned int);
struct evcount ipi_count;
unsigned int ipi_irq = 0;
unsigned int ipi_mailbox[MAXCPUS];
struct mutex smp_rv_mtx;
volatile unsigned long smp_rv_map;
void (*volatile smp_rv_action_func)(void *arg);
void * volatile smp_rv_func_arg;
volatile unsigned int smp_rv_waiters[2];
typedef void (*ipifunc_t)(void);
ipifunc_t ipifuncs[MIPS64_NIPIS] = {
mips64_ipi_nop,
smp_rendezvous_action,
mips64_ipi_ddb
};
void
mips64_ipi_init(void)
{
cpuid_t cpuid = cpu_number();
int error;
if (!cpuid) {
mtx_init(&smp_rv_mtx, IPL_HIGH);
evcount_attach(&ipi_count, "ipi", &ipi_irq);
evcount_percpu(&ipi_count);
}
hw_ipi_intr_clear(cpuid);
error = hw_ipi_intr_establish(mips64_ipi_intr, cpuid);
if (error)
panic("hw_ipi_intr_establish failed:%d", error);
}
int
mips64_ipi_intr(void *arg)
{
unsigned int pending_ipis, bit;
unsigned int cpuid = (unsigned int)(unsigned long)arg;
KASSERT (cpuid == cpu_number());
hw_ipi_intr_clear(cpuid);
pending_ipis = atomic_swap_uint(&ipi_mailbox[cpuid], 0);
if (pending_ipis > 0) {
for (bit = 0; bit < MIPS64_NIPIS; bit++) {
if (pending_ipis & (1UL << bit)) {
(*ipifuncs[bit])();
evcount_inc(&ipi_count);
}
}
}
return 1;
}
static void
do_send_ipi(unsigned int cpuid, unsigned int ipimask)
{
#ifdef DEBUG
struct cpu_info *ci = get_cpu_info(cpuid);
if (ci == NULL)
panic("mips_send_ipi: bogus cpu_id");
if (!CPU_IS_RUNNING(ci))
panic("mips_send_ipi: CPU %u not running", cpuid);
#endif
atomic_setbits_int(&ipi_mailbox[cpuid], ipimask);
hw_ipi_intr_set(cpuid);
}
void
mips64_send_ipi(unsigned int cpuid, unsigned int ipimask)
{
membar_producer();
do_send_ipi(cpuid, ipimask);
}
void
mips64_multicast_ipi(unsigned int cpumask, unsigned int ipimask)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
cpumask &= ~(1 << cpu_number());
CPU_INFO_FOREACH(cii, ci) {
if (!(cpumask & (1UL << ci->ci_cpuid)) || !CPU_IS_RUNNING(ci))
continue;
do_send_ipi(ci->ci_cpuid, ipimask);
}
}
void
mips64_ipi_nop(void)
{
#ifdef DEBUG
printf("mips64_ipi_nop on cpu%lu\n", cpu_number());
#endif
}
void
smp_rendezvous_action(void)
{
unsigned int cpumask = 1 << cpu_number();
atomic_setbits_int(&smp_rv_waiters[0], cpumask);
membar_enter_after_atomic();
while (smp_rv_waiters[0] != smp_rv_map)
CPU_BUSY_CYCLE();
(*smp_rv_action_func)(smp_rv_func_arg);
membar_exit_before_atomic();
atomic_setbits_int(&smp_rv_waiters[1], cpumask);
}
void
smp_rendezvous_cpus(unsigned long map,
void (* action_func)(void *),
void *arg)
{
unsigned int cpumask = 1 << cpu_number();
if (cpumask == map) {
(*action_func)(arg);
return;
}
mtx_enter(&smp_rv_mtx);
smp_rv_map = map;
smp_rv_action_func = action_func;
smp_rv_func_arg = arg;
smp_rv_waiters[0] = 0;
smp_rv_waiters[1] = 0;
membar_exit();
mips64_multicast_ipi(map, MIPS64_IPI_RENDEZVOUS);
if (map & cpumask)
smp_rendezvous_action();
while (smp_rv_waiters[1] != smp_rv_map)
CPU_BUSY_CYCLE();
membar_sync();
smp_rv_action_func = NULL;
mtx_leave(&smp_rv_mtx);
}
void
mips64_ipi_ddb(void)
{
#ifdef DDB
db_enter();
#endif
}