#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ttwoga_pci.c,v 1.10 2021/06/25 03:49:47 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <alpha/pci/ttwogareg.h>
#include <alpha/pci/ttwogavar.h>
static int ttwoga_bus_maxdevs(void *, int);
static pcireg_t ttwoga_conf_read(void *, pcitag_t, int);
static void ttwoga_conf_write(void *, pcitag_t, int, pcireg_t);
static paddr_t ttwoga_make_type0addr(int, int);
static kmutex_t ttwoga_conf_lock;
cpuid_t ttwoga_conf_cpu;
#define TTWOGA_CONF_LOCK() \
do { \
mutex_enter(&ttwoga_conf_lock); \
ttwoga_conf_cpu = cpu_number(); \
} while (0)
#define TTWOGA_CONF_UNLOCK() \
do { \
ttwoga_conf_cpu = (cpuid_t)-1; \
mutex_exit(&ttwoga_conf_lock); \
} while (0)
void
ttwoga_pci_init(pci_chipset_tag_t pc, void *v)
{
mutex_init(&ttwoga_conf_lock, MUTEX_DEFAULT, IPL_HIGH);
pc->pc_conf_v = v;
pc->pc_bus_maxdevs = ttwoga_bus_maxdevs;
pc->pc_conf_read = ttwoga_conf_read;
pc->pc_conf_write = ttwoga_conf_write;
}
static int
ttwoga_bus_maxdevs(void *cpv, int busno)
{
return busno == 0 ? 9 : 32;
}
static paddr_t
ttwoga_make_type0addr(int d, int f)
{
KASSERT(d < 9);
return PCI_CONF_TYPE0_IDSEL(d) | __SHIFTIN(f, PCI_CONF_TYPE1_FUNCTION);
}
static pcireg_t
ttwoga_conf_read(void *cpv, pcitag_t tag, int offset)
{
struct ttwoga_config *tcp = cpv;
pcireg_t *datap, data;
int b, d, f, ba;
paddr_t addr;
uint64_t old_hae3;
if ((unsigned int)offset >= PCI_CONF_SIZE)
return (pcireg_t) -1;
pci_decompose_tag(&tcp->tc_pc, tag, &b, &d, &f);
addr = b ? tag : ttwoga_make_type0addr(d, f);
TTWOGA_CONF_LOCK();
alpha_mb();
old_hae3 = T2GA(tcp, T2_HAE0_3) & ~HAE0_3_PCA;
T2GA(tcp, T2_HAE0_3) =
old_hae3 | ((b ? 1UL : 0UL) << HAE0_3_PCA_SHIFT);
alpha_mb();
alpha_mb();
datap =
(pcireg_t *)ALPHA_PHYS_TO_K0SEG(tcp->tc_sysmap->tsmap_conf_base |
addr << 5UL |
(offset & ~0x03) << 5UL |
0x3 << 3UL);
data = (pcireg_t)-1;
if (!(ba = badaddr(datap, sizeof *datap)))
data = *datap;
alpha_mb();
T2GA(tcp, T2_HAE0_3) = old_hae3;
alpha_mb();
alpha_mb();
alpha_pal_draina();
alpha_mb();
alpha_mb();
TTWOGA_CONF_UNLOCK();
#if 0
printf("ttwoga_conf_read: tag 0x%lx, reg 0x%x -> 0x%x @ %p%s\n",
tag, offset, data, datap, ba ? " (badaddr)" : "");
#endif
return (data);
}
static void
ttwoga_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
{
struct ttwoga_config *tcp = cpv;
pcireg_t *datap;
int b, d, f;
paddr_t addr;
uint64_t old_hae3;
if ((unsigned int)offset >= PCI_CONF_SIZE)
return;
pci_decompose_tag(&tcp->tc_pc, tag, &b, &d, &f);
addr = b ? tag : ttwoga_make_type0addr(d, f);
TTWOGA_CONF_LOCK();
alpha_mb();
old_hae3 = T2GA(tcp, T2_HAE0_3) & ~HAE0_3_PCA;
T2GA(tcp, T2_HAE0_3) =
old_hae3 | ((b ? 1UL : 0UL) << HAE0_3_PCA_SHIFT);
alpha_mb();
alpha_mb();
datap =
(pcireg_t *)ALPHA_PHYS_TO_K0SEG(tcp->tc_sysmap->tsmap_conf_base |
addr << 5UL |
(offset & ~0x03) << 5UL |
0x3 << 3UL);
alpha_mb();
*datap = data;
alpha_mb();
alpha_mb();
alpha_mb();
T2GA(tcp, T2_HAE0_3) = old_hae3;
alpha_mb();
alpha_mb();
TTWOGA_CONF_UNLOCK();
#if 0
printf("ttwoga_conf_write: tag 0x%lx, reg 0x%x -> 0x%x @ %p\n",
tag, offset, data, datap);
#endif
}