#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pci_6600.c,v 1.33 2021/07/04 22:42:36 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/cpu.h>
#include <machine/autoconf.h>
#define _ALPHA_BUS_DMA_PRIVATE
#include <sys/bus.h>
#include <machine/rpb.h>
#include <machine/alpha.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pciidereg.h>
#include <dev/pci/pciidevar.h>
#include <alpha/pci/tsreg.h>
#include <alpha/pci/tsvar.h>
#define pci_6600() { Generate ctags(1) key. }
#include "sio.h"
#if NSIO
#include <alpha/pci/siovar.h>
#endif
#define PCI_NIRQ 64
#define PCI_SIO_IRQ 55
#define PCI_STRAY_MAX 5
#define DEC_6600_LINE_IS_ISA(line) ((line) >= 0xe0 && (line) <= 0xef)
#define DEC_6600_LINE_ISA_IRQ(line) ((line) & 0x0f)
static struct tsp_config *sioprimary;
static void dec_6600_intr_disestablish(pci_chipset_tag_t, void *);
static void *dec_6600_intr_establish(pci_chipset_tag_t, pci_intr_handle_t,
int, int (*func)(void *), void *);
static const char *dec_6600_intr_string(pci_chipset_tag_t, pci_intr_handle_t,
char *, size_t);
static const struct evcnt *dec_6600_intr_evcnt(pci_chipset_tag_t,
pci_intr_handle_t);
static int dec_6600_intr_map(const struct pci_attach_args *,
pci_intr_handle_t *);
static void *dec_6600_pciide_compat_intr_establish(device_t,
const struct pci_attach_args *, int,
int (*)(void *), void *);
static void dec_6600_intr_enable(pci_chipset_tag_t, int irq);
static void dec_6600_intr_disable(pci_chipset_tag_t, int irq);
static void dec_6600_intr_set_affinity(pci_chipset_tag_t, int,
struct cpu_info *);
static void dec_6600_intr_program(pci_chipset_tag_t);
static void dec_6600_intr_redistribute(void);
static uint64_t dec_6600_intr_enables __read_mostly;
static uint64_t dec_6600_cpu_intr_enables[4] __read_mostly;
static void
pci_6600_pickintr(void *core, bus_space_tag_t iot, bus_space_tag_t memt,
pci_chipset_tag_t pc)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
pc->pc_intr_v = core;
pc->pc_intr_map = dec_6600_intr_map;
pc->pc_intr_string = dec_6600_intr_string;
pc->pc_intr_evcnt = dec_6600_intr_evcnt;
pc->pc_intr_establish = dec_6600_intr_establish;
pc->pc_intr_disestablish = dec_6600_intr_disestablish;
pc->pc_pciide_compat_intr_establish = NULL;
pc->pc_intr_desc = "dec 6600";
pc->pc_vecbase = 0x900;
pc->pc_nirq = PCI_NIRQ;
pc->pc_intr_enable = dec_6600_intr_enable;
pc->pc_intr_disable = dec_6600_intr_disable;
pc->pc_intr_set_affinity = dec_6600_intr_set_affinity;
alpha_intr_redistribute = dec_6600_intr_redistribute;
for (CPU_INFO_FOREACH(cii, ci)) {
KASSERT(ci->ci_cpuid < 4);
pc->pc_eligible_cpus |= __BIT(ci->ci_cpuid);
}
if (sioprimary == NULL) {
sioprimary = core;
dec_6600_cpu_intr_enables[cpu_info_primary.ci_cpuid] =
__BITS(0,63);
pc->pc_pciide_compat_intr_establish =
dec_6600_pciide_compat_intr_establish;
KASSERT(dec_6600_intr_enables == 0);
dec_6600_intr_program(pc);
alpha_pci_intr_alloc(pc, PCI_STRAY_MAX);
#if NSIO
sio_intr_setup(pc, iot);
mutex_enter(&cpu_lock);
dec_6600_intr_enable(pc, PCI_SIO_IRQ);
mutex_exit(&cpu_lock);
#endif
} else {
pc->pc_shared_intrs = sioprimary->pc_pc.pc_shared_intrs;
}
}
ALPHA_PCI_INTR_INIT(ST_DEC_6600, pci_6600_pickintr)
ALPHA_PCI_INTR_INIT(ST_DEC_TITAN, pci_6600_pickintr)
static int
dec_6600_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
pcitag_t bustag = pa->pa_intrtag;
int buspin = pa->pa_intrpin, line = pa->pa_intrline;
pci_chipset_tag_t pc = pa->pa_pc;
int bus, device, function;
if (buspin == 0) {
return 1;
}
if (buspin < 0 || buspin > 4) {
printf("intr_map: bad interrupt pin %d\n", buspin);
return 1;
}
pci_decompose_tag(pc, bustag, &bus, &device, &function);
if (line == 0xff) {
printf("dec_6600_intr_map: no mapping for %d/%d/%d\n",
bus, device, function);
return (1);
}
#if NSIO == 0
if (DEC_6600_LINE_IS_ISA(line)) {
printf("dec_6600_intr_map: ISA IRQ %d for %d/%d/%d\n",
DEC_6600_LINE_ISA_IRQ(line), bus, device, function);
return (1);
}
#endif
if (DEC_6600_LINE_IS_ISA(line) == 0 && line >= PCI_NIRQ)
panic("dec_6600_intr_map: dec 6600 irq too large (%d)",
line);
alpha_pci_intr_handle_init(ihp, line, 0);
return (0);
}
static const char *
dec_6600_intr_string(pci_chipset_tag_t const pc, pci_intr_handle_t const ih,
char * const buf, size_t const len)
{
#if NSIO
const u_int irq = alpha_pci_intr_handle_get_irq(&ih);
if (DEC_6600_LINE_IS_ISA(irq))
return (sio_intr_string(NULL ,
DEC_6600_LINE_ISA_IRQ(irq), buf, len));
#endif
return alpha_pci_generic_intr_string(pc, ih, buf, len);
}
static const struct evcnt *
dec_6600_intr_evcnt(pci_chipset_tag_t const pc, pci_intr_handle_t const ih)
{
#if NSIO
const u_int irq = alpha_pci_intr_handle_get_irq(&ih);
if (DEC_6600_LINE_IS_ISA(irq))
return (sio_intr_evcnt(NULL ,
DEC_6600_LINE_ISA_IRQ(irq)));
#endif
return alpha_pci_generic_intr_evcnt(pc, ih);
}
static void *
dec_6600_intr_establish(pci_chipset_tag_t const pc, pci_intr_handle_t const ih,
int const level, int (*func)(void *), void *arg)
{
#if NSIO
const u_int irq = alpha_pci_intr_handle_get_irq(&ih);
const u_int flags = alpha_pci_intr_handle_get_flags(&ih);
if (DEC_6600_LINE_IS_ISA(irq))
return (sio_intr_establish(NULL ,
DEC_6600_LINE_ISA_IRQ(irq), IST_LEVEL, level, flags,
func, arg));
#endif
return alpha_pci_generic_intr_establish(pc, ih, level, func, arg);
}
static void
dec_6600_intr_disestablish(pci_chipset_tag_t const pc, void * const cookie)
{
#if NSIO
struct alpha_shared_intrhand * const ih = cookie;
if (ih->ih_intrhead->intr_private != sioprimary) {
sio_intr_disestablish(NULL , cookie);
return;
}
#endif
return alpha_pci_generic_intr_disestablish(pc, cookie);
}
static void
dec_6600_intr_program(pci_chipset_tag_t const pc)
{
unsigned int irq, cpuno, cnt;
for (irq = 0; irq < PCI_NIRQ; irq++) {
if ((dec_6600_intr_enables & __BIT(irq)) == 0)
continue;
for (cpuno = 0, cnt = 0; cpuno < 4; cpuno++) {
if (dec_6600_cpu_intr_enables[cpuno] != 0 &&
(pc->pc_eligible_cpus & __BIT(cpuno)) == 0)
panic("%s: interrupts enabled on non-existent CPU %u",
__func__, cpuno);
if (dec_6600_cpu_intr_enables[cpuno] & __BIT(irq))
cnt++;
}
if (cnt != 1) {
panic("%s: irq %u enabled on %u CPUs", __func__,
irq, cnt);
}
}
const uint64_t enab0 =
dec_6600_intr_enables & dec_6600_cpu_intr_enables[0];
const uint64_t enab1 =
dec_6600_intr_enables & dec_6600_cpu_intr_enables[1];
const uint64_t enab2 =
dec_6600_intr_enables & dec_6600_cpu_intr_enables[2];
const uint64_t enab3 =
dec_6600_intr_enables & dec_6600_cpu_intr_enables[3];
uint64_t black_hole;
volatile uint64_t * const dim0 = (pc->pc_eligible_cpus & __BIT(0)) ?
(void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM0) : &black_hole;
volatile uint64_t * const dim1 = (pc->pc_eligible_cpus & __BIT(1)) ?
(void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM1) : &black_hole;
volatile uint64_t * const dim2 = (pc->pc_eligible_cpus & __BIT(2)) ?
(void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM2) : &black_hole;
volatile uint64_t * const dim3 = (pc->pc_eligible_cpus & __BIT(3)) ?
(void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM3) : &black_hole;
const unsigned long psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
alpha_mb();
*dim0 = enab0;
*dim1 = enab1;
*dim2 = enab2;
*dim3 = enab3;
alpha_mb();
(void) *dim0;
(void) *dim1;
(void) *dim2;
(void) *dim3;
alpha_mb();
alpha_pal_swpipl(psl);
}
static void
dec_6600_intr_enable(pci_chipset_tag_t const pc, int const irq)
{
KASSERT(mutex_owned(&cpu_lock));
dec_6600_intr_enables |= __BIT(irq);
dec_6600_intr_program(pc);
}
static void
dec_6600_intr_disable(pci_chipset_tag_t const pc, int const irq)
{
KASSERT(mutex_owned(&cpu_lock));
dec_6600_intr_enables &= ~__BIT(irq);
dec_6600_intr_program(pc);
}
static void
dec_6600_intr_set_affinity(pci_chipset_tag_t const pc, int const irq,
struct cpu_info * const ci)
{
const uint64_t intr_bit = __BIT(irq);
cpuid_t cpuno;
KASSERT(mutex_owned(&cpu_lock));
KASSERT(ci->ci_cpuid < 4);
KASSERT(pc->pc_eligible_cpus & __BIT(ci->ci_cpuid));
for (cpuno = 0; cpuno < 4; cpuno++) {
if (cpuno == ci->ci_cpuid)
dec_6600_cpu_intr_enables[cpuno] |= intr_bit;
else
dec_6600_cpu_intr_enables[cpuno] &= ~intr_bit;
}
if (dec_6600_intr_enables & intr_bit)
dec_6600_intr_program(pc);
}
static void
dec_6600_intr_redistribute(void)
{
KASSERT(sioprimary != NULL);
pci_chipset_tag_t const pc = &sioprimary->pc_pc;
alpha_pci_generic_intr_redistribute(pc);
}
static void *
dec_6600_pciide_compat_intr_establish(device_t dev,
const struct pci_attach_args *pa, int chan, int (*func)(void *), void *arg)
{
pci_chipset_tag_t const pc = pa->pa_pc;
if (pc->pc_intr_v != sioprimary)
return (NULL);
return sio_pciide_compat_intr_establish(dev, pa, chan, func, arg);
}