#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: msiiep.c,v 1.54 2025/10/04 16:11:17 thorpej Exp $");
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <uvm/uvm.h>
#define _SPARC_BUS_DMA_PRIVATE
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <machine/promlib.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcivar.h>
#include <sparc/sparc/msiiepreg.h>
#include <sparc/sparc/msiiepvar.h>
#include <sparc/sparc/pci_fixup.h>
static int msiiep_match(device_t, cfdata_t, void *);
static void msiiep_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(msiiep, 0, msiiep_match, msiiep_attach, NULL, NULL);
static void msiiep_cpu_sleep(void);
volatile uint32_t *msiiep_mid = NULL;
static int mspcic_match(device_t, cfdata_t, void *);
static void mspcic_attach(device_t, device_t, void *);
static int mspcic_print(void *, const char *);
CFATTACH_DECL_NEW(mspcic, sizeof(struct mspcic_softc),
mspcic_match, mspcic_attach, NULL, NULL);
static struct sparc_pci_chipset mspcic_pc_tag = { NULL };
struct mspcic_pci_map mspcic_pci_iomap[IOMAP_SIZE] = {
{ 0x30000000, 0x0, 0x00010000 }
};
struct mspcic_pci_map mspcic_pci_memmap[MEMMAP_SIZE] = {
{ 0x30100000, 0x00100000, 0x00f00000 }
};
struct mspcic_cookie {
struct mspcic_pci_map *map;
int nmaps;
};
static struct mspcic_cookie mspcic_io_cookie = { mspcic_pci_iomap, 0 };
static struct mspcic_cookie mspcic_mem_cookie = { mspcic_pci_memmap, 0 };
static void mspcic_init_maps(void);
static void mspcic_pci_map_from_reg(struct mspcic_pci_map *,
uint8_t, uint8_t, uint8_t);
static bus_addr_t mspcic_pci_map_find(struct mspcic_pci_map *, int,
bus_addr_t, bus_size_t);
#ifdef DEBUG
static void mspcic_pci_map_print(struct mspcic_pci_map *, const char *);
#endif
static int mspcic_bus_map(bus_space_tag_t, bus_addr_t, bus_size_t,
int, vaddr_t, bus_space_handle_t *);
static paddr_t mspcic_bus_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int);
static void *mspcic_intr_establish(bus_space_tag_t, int, int,
int (*)(void *), void *, void (*)(void));
static uint16_t mspcic_bus_read_2(bus_space_tag_t, bus_space_handle_t,
bus_size_t);
static uint32_t mspcic_bus_read_4(bus_space_tag_t, bus_space_handle_t,
bus_size_t);
static uint64_t mspcic_bus_read_8(bus_space_tag_t, bus_space_handle_t,
bus_size_t);
static void mspcic_bus_write_2(bus_space_tag_t, bus_space_handle_t,
bus_size_t, uint16_t);
static void mspcic_bus_write_4(bus_space_tag_t, bus_space_handle_t,
bus_size_t, uint32_t);
static void mspcic_bus_write_8(bus_space_tag_t, bus_space_handle_t,
bus_size_t, uint64_t);
static struct sparc_bus_space_tag mspcic_io_tag;
static struct sparc_bus_space_tag mspcic_mem_tag;
static int mspcic_dmamap_load(bus_dma_tag_t, bus_dmamap_t,
void *, bus_size_t, struct proc *, int);
static void mspcic_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
static int mspcic_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *,
int, size_t, void **, int);
static struct sparc_bus_dma_tag mspcic_dma_tag = {
NULL,
_bus_dmamap_create,
_bus_dmamap_destroy,
mspcic_dmamap_load,
_bus_dmamap_load_mbuf,
_bus_dmamap_load_uio,
_bus_dmamap_load_raw,
mspcic_dmamap_unload,
_bus_dmamap_sync,
_bus_dmamem_alloc,
_bus_dmamem_free,
mspcic_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap
};
static int
msiiep_match(device_t parent, cfdata_t cf, void *aux)
{
struct mainbus_attach_args *ma = aux;
pcireg_t id;
if (strcmp(ma->ma_name, "pci") != 0)
return (0);
id = mspcic_read_4(pcic_id);
if (PCI_VENDOR(id) != PCI_VENDOR_SUN
&& PCI_PRODUCT(id) != PCI_PRODUCT_SUN_MS_IIep)
panic("msiiep_match: id %08x", id);
return (1);
}
static void
msiiep_attach(device_t parent, device_t self, void *aux)
{
struct mainbus_attach_args *ma = aux;
struct msiiep_attach_args msa;
bus_space_handle_t hmid;
struct cpu_info *cur;
aprint_normal("\n");
if (bus_space_map(ma->ma_bustag, MSIIEP_MID_PA, 4, 0, &hmid) == 0) {
#ifdef DIAGNOSTICS
uint32_t mid;
mid = bus_space_read_4(ma->ma_bustag, hmid, 0);
printf("MID: %08x\n", mid);
#endif
msiiep_mid = (volatile uint32_t *)bus_space_vaddr(ma->ma_bustag,
hmid);
cur = curcpu();
cur->idlespin = msiiep_cpu_sleep;
}
devhandle_t selfh = device_handle(self);
msa.msa_ma = ma;
KASSERT(prom_devhandle_to_node(selfh) == ma->ma_node);
msa.msa_name = "timer";
config_found(self, &msa, NULL, CFARGS_NONE);
msa.msa_name = "pcic";
config_found(self, &msa, NULL,
CFARGS(.devhandle = selfh));
}
void
msiiep_cpu_sleep(void)
{
uint32_t reg;
if (msiiep_mid == 0)
return;
reg = *msiiep_mid;
*msiiep_mid = (reg & MID_MASK) | MID_STANDBY;
}
static int
mspcic_match(device_t parent, cfdata_t cf, void *aux)
{
struct msiiep_attach_args *msa = aux;
return (strcmp(msa->msa_name, "pcic") == 0);
}
static void
mspcic_attach(device_t parent, device_t self, void *aux)
{
struct mspcic_softc *sc = device_private(self);
devhandle_t selfh = device_handle(self);
struct msiiep_attach_args *msa = aux;
struct mainbus_attach_args *ma = msa->msa_ma;
int node = ma->ma_node;
char devinfo[256], buf[32], *model;
struct pcibus_attach_args pba;
sc->sc_node = node;
KASSERT(prom_devhandle_to_node(selfh) == node);
sc->sc_bustag = ma->ma_bustag;
sc->sc_dmatag = ma->ma_dmatag;
pci_devinfo(mspcic_read_4(pcic_id), mspcic_read_4(pcic_class), 0,
devinfo, sizeof(devinfo));
printf(": %s: clock = %s MHz\n", devinfo,
clockfreq(prom_getpropint(node, "clock-frequency", 33333333)));
mspcic_init_maps();
model = prom_getpropstringA(prom_findroot(), "model", buf, sizeof(buf));
if (model != NULL && !strcmp(model, "SUNW,375-0059")) {
mspcic_write_2(pcic_intr_asgn_sel_hi, (uint16_t) 0x7502);
mspcic_write_4(pcic_cntrs, (uint32_t) 0x00808000);
}
mspcic_io_tag = *sc->sc_bustag;
mspcic_io_tag.cookie = &mspcic_io_cookie;
mspcic_io_tag.ranges = NULL;
mspcic_io_tag.nranges = 0;
mspcic_io_tag.sparc_bus_map = mspcic_bus_map;
mspcic_io_tag.sparc_bus_mmap = mspcic_bus_mmap;
mspcic_io_tag.sparc_intr_establish = mspcic_intr_establish;
mspcic_io_tag.parent = sc->sc_bustag;
mspcic_io_tag.sparc_read_2 = mspcic_bus_read_2;
mspcic_io_tag.sparc_read_4 = mspcic_bus_read_4;
mspcic_io_tag.sparc_read_8 = mspcic_bus_read_8;
mspcic_io_tag.sparc_write_2 = mspcic_bus_write_2;
mspcic_io_tag.sparc_write_4 = mspcic_bus_write_4;
mspcic_io_tag.sparc_write_8 = mspcic_bus_write_8;
mspcic_mem_tag = *sc->sc_bustag;
mspcic_mem_tag.cookie = &mspcic_mem_cookie;
mspcic_mem_tag.ranges = NULL;
mspcic_mem_tag.nranges = 0;
mspcic_mem_tag.sparc_bus_map = mspcic_bus_map;
mspcic_mem_tag.sparc_bus_mmap = mspcic_bus_mmap;
mspcic_mem_tag.sparc_intr_establish = mspcic_intr_establish;
mspcic_mem_tag.parent = sc->sc_bustag;
mspcic_mem_tag.sparc_read_2 = mspcic_bus_read_2;
mspcic_mem_tag.sparc_read_4 = mspcic_bus_read_4;
mspcic_mem_tag.sparc_read_8 = mspcic_bus_read_8;
mspcic_mem_tag.sparc_write_2 = mspcic_bus_write_2;
mspcic_mem_tag.sparc_write_4 = mspcic_bus_write_4;
mspcic_mem_tag.sparc_write_8 = mspcic_bus_write_8;
mspcic_dma_tag._cookie = sc;
mspcic_pc_tag.cookie = sc;
sc->sc_iot = &mspcic_io_tag;
sc->sc_memt = &mspcic_mem_tag;
sc->sc_dmat = &mspcic_dma_tag;
pba.pba_bus = 0;
pba.pba_bridgetag = NULL;
pba.pba_iot = sc->sc_iot;
pba.pba_memt = sc->sc_memt;
pba.pba_dmat = sc->sc_dmat;
pba.pba_dmat64 = NULL;
pba.pba_pc = &mspcic_pc_tag;
pba.pba_flags = PCI_FLAGS_IO_OKAY | PCI_FLAGS_MEM_OKAY;
mspcic_pci_scan(sc->sc_node);
config_found(self, &pba, mspcic_print,
CFARGS(.devhandle = selfh));
}
static int
mspcic_print(void *args, const char *busname)
{
if (busname == NULL)
return (UNCONF);
return (QUIET);
}
int
mspcic_assigned_interrupt(int line)
{
unsigned int intrmap;
if (line < 0 || line > 7)
return (-1);
if (line < 4) {
intrmap = mspcic_read_2(pcic_intr_asgn_sel);
} else {
intrmap = mspcic_read_2(pcic_intr_asgn_sel_hi);
line -= 4;
}
return ((intrmap >> (line * 4)) & 0xf);
}
static inline void
mspcic_pci_map_from_reg(struct mspcic_pci_map *m,
uint8_t sbar, uint8_t pbar, uint8_t sizemask)
{
m->sysbase = 0x30000000 | ((sbar & 0x0f) << 24);
m->pcibase = pbar << 24;
m->size = ~((0xf0 | sizemask) << 24) + 1;
}
#define OVERLAP(al, ar, bl, br) (((al) < (br)) && ((bl) < (ar)))
#define OVERLAP_FIXED(m) OVERLAP((m)->sysbase, (m)->sysbase + (m)->size, \
0x30000000, 0x31000000)
#define OVERLAP_MAP(ma, mb) \
((mb != NULL) && OVERLAP((ma)->sysbase, (ma)->sysbase + (ma)->size, \
(mb)->sysbase, (mb)->sysbase + (mb)->size))
static void
mspcic_init_maps(void)
{
struct mspcic_pci_map *m0, *m1, *io;
int nmem, nio;
#ifdef DEBUG
printf("mspcic0: SMBAR0 %02x PMBAR0 %02x MSIZE0 %02x\n",
mspcic_read_1(pcic_smbar0), mspcic_read_1(pcic_pmbar0),
mspcic_read_1(pcic_msize0));
printf("mspcic0: SMBAR1 %02x PMBAR1 %02x MSIZE1 %02x\n",
mspcic_read_1(pcic_smbar1), mspcic_read_1(pcic_pmbar1),
mspcic_read_1(pcic_msize1));
printf("mspcic0: SIBAR %02x PIBAR %02x IOSIZE %02x\n",
mspcic_read_1(pcic_sibar), mspcic_read_1(pcic_pibar),
mspcic_read_1(pcic_iosize));
#endif
nmem = nio = 1;
m0 = &mspcic_pci_memmap[nmem];
mspcic_pci_map_from_reg(m0,
mspcic_read_1(pcic_smbar0), mspcic_read_1(pcic_pmbar0),
mspcic_read_1(pcic_msize0));
if (OVERLAP_FIXED(m0))
m0 = NULL;
else
++nmem;
m1 = &mspcic_pci_memmap[nmem];
mspcic_pci_map_from_reg(m1,
mspcic_read_1(pcic_smbar1), mspcic_read_1(pcic_pmbar1),
mspcic_read_1(pcic_msize1));
if (OVERLAP_FIXED(m1) || OVERLAP_MAP(m1, m0))
m1 = NULL;
else
++nmem;
io = &mspcic_pci_iomap[nio];
mspcic_pci_map_from_reg(io,
mspcic_read_1(pcic_sibar), mspcic_read_1(pcic_pibar),
mspcic_read_1(pcic_iosize));
if (OVERLAP_FIXED(io) || OVERLAP_MAP(io, m0) || OVERLAP_MAP(io, m1))
io = NULL;
else
++nio;
mspcic_io_cookie.nmaps = nio;
mspcic_mem_cookie.nmaps = nmem;
#ifdef DEBUG
mspcic_pci_map_print(&mspcic_pci_iomap[0], "i/o fixed");
mspcic_pci_map_print(&mspcic_pci_memmap[0], "mem fixed");
if (m0) mspcic_pci_map_print(m0, "mem map0");
if (m1) mspcic_pci_map_print(m1, "mem map1");
if (io) mspcic_pci_map_print(io, "i/0 map");
#endif
}
#ifdef DEBUG
static void
mspcic_pci_map_print(struct mspcic_pci_map *m, const char *msg)
{
printf("mspcic0: paddr [%08x..%08x] -> pci [%08x..%08x] %s\n",
m->sysbase, m->sysbase + m->size - 1,
m->pcibase, m->pcibase + m->size - 1,
msg);
}
#endif
static bus_addr_t
mspcic_pci_map_find(struct mspcic_pci_map *m, int nmaps,
bus_addr_t pciaddr, bus_size_t size)
{
bus_size_t offset;
int i;
for (i = 0; i < nmaps; ++i, ++m) {
offset = pciaddr - m->pcibase;
if (offset + size <= m->size)
return (m->sysbase + offset);
}
return (0);
}
static int
mspcic_bus_map(bus_space_tag_t t, bus_addr_t ba, bus_size_t size,
int flags, vaddr_t va, bus_space_handle_t *hp)
{
struct mspcic_cookie *c = t->cookie;
bus_addr_t paddr;
paddr = mspcic_pci_map_find(c->map, c->nmaps, ba, size);
if (paddr == 0)
return (EINVAL);
return (bus_space_map2(t->parent, paddr, size, flags, va, hp));
}
static paddr_t
mspcic_bus_mmap(bus_space_tag_t t, bus_addr_t ba, off_t off,
int prot, int flags)
{
struct mspcic_cookie *c = t->cookie;
bus_addr_t paddr;
paddr = mspcic_pci_map_find(c->map, c->nmaps, ba + off, PAGE_SIZE);
if (paddr == 0)
return (-1);
return (bus_space_mmap(t->parent, paddr - off, off, prot, flags));
}
static void *
mspcic_intr_establish(bus_space_tag_t t, int line, int ipl,
int (*handler)(void *), void *arg,
void (*fastvec)(void))
{
struct intrhand *ih;
int pil;
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
pil = mspcic_assigned_interrupt(line);
if (pil == -1)
panic("mspcic_intr_establish: line %d", line);
ih->ih_fun = handler;
ih->ih_arg = arg;
intr_establish(pil, ipl, ih, fastvec, false);
return(ih);
}
static uint16_t
mspcic_bus_read_2(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset)
{
uint16_t val = *(volatile uint16_t *)(handle + offset);
return le16toh(val);
}
static uint32_t
mspcic_bus_read_4(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset)
{
uint32_t val = *(volatile uint32_t *)(handle + offset);
return le32toh(val);
}
static uint64_t
mspcic_bus_read_8(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset)
{
uint64_t val = *(volatile uint64_t *)(handle + offset);
return le64toh(val);
}
static void
mspcic_bus_write_2(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset, uint16_t value)
{
(*(volatile uint16_t *)(handle + offset)) = htole16(value);
}
static void
mspcic_bus_write_4(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset, uint32_t value)
{
(*(volatile uint32_t *)(handle + offset)) = htole32(value);
}
static void
mspcic_bus_write_8(bus_space_tag_t space, bus_space_handle_t handle,
bus_size_t offset, uint64_t value)
{
(*(volatile uint64_t *)(handle + offset)) = htole64(value);
}
static int
mspcic_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map,
void *buf, bus_size_t buflen,
struct proc *p, int flags)
{
pmap_t pmap;
paddr_t pa;
if (p != NULL)
pmap = p->p_vmspace->vm_map.pmap;
else
pmap = pmap_kernel();
if (!pmap_extract(pmap, (vaddr_t)buf, &pa))
panic("mspcic_dmamap_load: dma memory not mapped");
map->dm_nsegs = 1;
map->dm_segs[0].ds_addr = pa;
map->dm_segs[0].ds_len = buflen;
map->dm_mapsize = buflen;
return (0);
}
static void
mspcic_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
map->dm_mapsize = 0;
map->dm_nsegs = 0;
}
static int
mspcic_dmamem_map(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs,
size_t size, void **kvap, int flags)
{
struct pglist *mlist;
struct vm_page *m;
vaddr_t va;
int pagesz = PAGE_SIZE;
const uvm_flag_t kmflags =
(flags & BUS_DMA_NOWAIT) != 0 ? UVM_KMF_NOWAIT : 0;
if (nsegs != 1)
panic("mspcic_dmamem_map: nsegs = %d", nsegs);
size = round_page(size);
va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY | kmflags);
if (va == 0)
return (ENOMEM);
segs[0]._ds_va = va;
*kvap = (void *)va;
mlist = segs[0]._ds_mlist;
TAILQ_FOREACH(m, mlist, pageq.queue) {
paddr_t pa;
if (size == 0)
panic("mspcic_dmamem_map: size botch");
pa = VM_PAGE_TO_PHYS(m);
pmap_kenter_pa(va,
pa | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE, 0);
va += pagesz;
size -= pagesz;
}
pmap_update(pmap_kernel());
return (0);
}