/*
* Copyright (c) 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef PCI_DEBUG
int rmixl_pcix_debug = PCI_DEBUG;
# define DPRINTF(x) do { if (rmixl_pcix_debug) printf x ; } while (0)
#else
# define DPRINTF(x)
#endif
/*
* PCI-X interface exists on XLR chips only
*/
if (! cpu_rmixlr(mips_options.mips_cpu))
return 0;
/* XXX
* for now there is only one PCI-X Interface on chip
* and only one chip in the system
* this could change with furture RMI XL family designs
* or when we have multi-chip systems.
*/
if (rmixl_pcix_found)
return 0;
/* read Host Mode Control register */
r = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_HOST_MODE_CTL);
r &= PCIX_HOST_MODE_CTL_HDMSTAT;
if (r == 0)
return 0; /* strapped for Device Mode */
/*
* HBAR[0] if a 32 bit BAR, or
* HBAR[0,1] if a 64 bit BAR pair
* must cover all RAM
*/
extern u_quad_t mem_cluster_maxaddr;
uint64_t hbar_addr;
uint64_t hbar_size;
uint32_t hbar_size_lo, hbar_size_hi;
uint32_t hbar_addr_lo, hbar_addr_hi;
/*
* get PCI config space base addr from SBC PCIe CFG BAR
* initialize it if necessary
*/
bar = RMIXL_IOREG_READ(RMIXL_IO_DEV_BRIDGE + RMIXLR_SBC_PCIX_CFG_BAR);
DPRINTF(("%s: PCIX_CFG_BAR %#x\n", __func__, bar));
if ((bar & RMIXL_PCIX_CFG_BAR_ENB) == 0) {
u_long n = RMIXL_PCIX_CFG_SIZE / (1024 * 1024);
RMIXL_PCIX_BAR_INIT(CFG, bar, n, n);
}
rcp->rc_pci_cfg_pbase = (bus_addr_t)RMIXL_PCIX_CFG_BAR_TO_BA(bar);
rcp->rc_pci_cfg_size = (bus_size_t)RMIXL_PCIX_CFG_SIZE;
/*
* get PCI MEM space base [addr, size] from SBC PCIe MEM BAR
* initialize it if necessary
*/
bar = RMIXL_IOREG_READ(RMIXL_IO_DEV_BRIDGE + RMIXLR_SBC_PCIX_MEM_BAR);
DPRINTF(("%s: PCIX_MEM_BAR %#x\n", __func__, bar));
if ((bar & RMIXL_PCIX_MEM_BAR_ENB) == 0) {
u_long n = 256; /* 256 MB */
RMIXL_PCIX_BAR_INIT(MEM, bar, n, n);
}
rcp->rc_pci_mem_pbase = (bus_addr_t)RMIXL_PCIX_MEM_BAR_TO_BA(bar);
rcp->rc_pci_mem_size = (bus_size_t)RMIXL_PCIX_MEM_BAR_TO_SIZE(bar);
/*
* get PCI IO space base [addr, size] from SBC PCIe IO BAR
* initialize it if necessary
*/
bar = RMIXL_IOREG_READ(RMIXL_IO_DEV_BRIDGE + RMIXLR_SBC_PCIX_IO_BAR);
DPRINTF(("%s: PCIX_IO_BAR %#x\n", __func__, bar));
if ((bar & RMIXL_PCIX_IO_BAR_ENB) == 0) {
u_long n = 32; /* 32 MB */
RMIXL_PCIX_BAR_INIT(IO, bar, n, n);
}
rcp->rc_pci_io_pbase = (bus_addr_t)RMIXL_PCIX_IO_BAR_TO_BA(bar);
rcp->rc_pci_io_size = (bus_size_t)RMIXL_PCIX_IO_BAR_TO_SIZE(bar);
/*
* initialize the PCI CFG bus space tag
*/
rmixl_pci_cfg_bus_mem_init(&rcp->rc_pci_cfg_memt, rcp);
sc->sc_pci_cfg_memt = &rcp->rc_pci_cfg_memt;
/*
* initialize the PCI MEM and IO bus space tags
*/
rmixl_pci_bus_mem_init(&rcp->rc_pci_memt, rcp);
rmixl_pci_bus_io_init(&rcp->rc_pci_iot, rcp);
/*
* initialize the extended configuration regs
*/
rmixl_pcix_init_errors(sc);
/*
* initialize the PCI chipset tag
*/
rmixl_pcix_init(sc);
/* mask all interrupts until they are established */
RMIXL_PCIXREG_WRITE(RMIXL_PCIX_ECFG_INTR_CONTROL,
PCIX_INTR_CONTROL_MASK_ALL);
/*
* read-to-clear any pre-existing interrupts
* XXX MSI bits in STATUS are also documented as write 1 to clear in PRM
*/
(void)RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_STATUS);
(void)RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_ERR_STATUS);
int
rmixl_pcix_bus_maxdevs(void *v, int busno)
{
return (32); /* XXX depends on the family of XLS SoC */
}
/*
* XLS pci tag is a 40 bit address composed thusly:
* 39:25 (reserved)
* 24 Swap (0=little, 1=big endian)
* 23:16 Bus number
* 15:11 Device number
* 10:8 Function number
* 7:0 Register number
*
* Note: this is the "native" composition for addressing CFG space, but not for ECFG space.
*/
pcitag_t
rmixl_pcix_make_tag(void *v, int bus, int dev, int fun)
{
return ((bus << 16) | (dev << 11) | (fun << 8));
}
void
rmixl_pcix_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp)
{
if (bp != NULL)
*bp = (tag >> 16) & 0xff;
if (dp != NULL)
*dp = (tag >> 11) & 0x1f;
if (fp != NULL)
*fp = (tag >> 8) & 0x7;
}
void
rmixl_pcix_tag_print(const char *restrict s, void *v, pcitag_t tag, int offset,
vaddr_t va, u_long r)
{
int bus, dev, fun;
/*
* if no other dispatch handle is using this interrupt,
* we can disable it
*/
busy = false;
for (int i=0; i < pip->dispatch_count; i++) {
rmixl_pcix_dispatch_t *d = &pip->dispatch_data[i];
if (d == dip)
continue;
if (d->bitno == dip->bitno) {
busy = true;
break;
}
}
if (! busy) {
uint32_t bit = 1 << (dip->bitno + 2);
uint32_t r;
r = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_CONTROL);
r |= bit; /* set mask */
RMIXL_PCIXREG_WRITE(RMIXL_PCIX_ECFG_INTR_CONTROL, r);
DPRINTF(("%s: disabled pin %d\n", __func__, dip->bitno + 1));
pip->intenb &= ~(1 << dip->bitno);
if ((r & PCIX_INTR_CONTROL_MASK_ALL) == 0) {
/* tear down interrupt for this pcix */
rmixl_intr_disestablish(pip->ih);
/* commit NULL interrupt set */
sc->sc_intr = NULL;
/* schedule delayed free of the old interrupt set */
rmixl_pcix_pip_free_callout(pip);
}
}
rmixl_pcix_intr_t *
rmixl_pcix_pip_add_1(rmixl_pcix_softc_t *sc, int irq, int ipl)
{
rmixl_pcix_intr_t *pip_old = sc->sc_intr;
rmixl_pcix_intr_t *pip_new;
u_int dispatch_count;
size_t size;
dispatch_count = 1;
size = sizeof(rmixl_pcix_intr_t);
if (pip_old != NULL) {
/*
* count only those dispatch elements still in use
* unused ones will be pruned during copy
* i.e. we are "lazy" there is no rmixl_pcix_pip_sub_1
*/
for (int i=0; i < pip_old->dispatch_count; i++) {
if (pip_old->dispatch_data[i].func != NULL) {
dispatch_count++;
size += sizeof(rmixl_pcix_intr_t);
}
}
}
/*
* allocate and initialize softc intr struct
* with one or more dispatch handles
*/
pip_new = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
if (pip_old == NULL) {
/* initialize the interrupt struct */
pip_new->sc = sc;
pip_new->ipl = ipl;
pip_new->ih = rmixl_intr_establish(irq, sc->sc_tmsk,
ipl, RMIXL_TRIG_LEVEL, RMIXL_POLR_HIGH,
rmixl_pcix_intr, pip_new, false);
if (pip_new->ih == NULL)
panic("%s: cannot establish irq %d", __func__, irq);
} else {
/*
* all intrs on a softc get same ipl and sc
* first intr established sets the standard
*/
KASSERT(sc == pip_old->sc);
if (sc != pip_old->sc) {
printf("%s: sc %p mismatch\n", __func__, sc);
free(pip_new, M_DEVBUF);
return NULL;
}
KASSERT (ipl == pip_old->ipl);
if (ipl != pip_old->ipl) {
printf("%s: ipl %d mismatch\n", __func__, ipl);
free(pip_new, M_DEVBUF);
return NULL;
}
/*
* copy pip_old to pip_new, skipping unused dispatch elemets
*/
memcpy(pip_new, pip_old, sizeof(rmixl_pcix_intr_t));
for (int j=0, i=0; i < pip_old->dispatch_count; i++) {
if (pip_old->dispatch_data[i].func != NULL) {
memcpy(&pip_new->dispatch_data[j],
&pip_old->dispatch_data[i],
sizeof(rmixl_pcix_dispatch_t));
j++;
}
}
/*
* schedule delayed free of old interrupt set
*/
rmixl_pcix_pip_free_callout(pip_old);
}
pip_new->dispatch_count = dispatch_count;
return pip_new;
}
/*
* delay free of the old interrupt set
* to allow anyone still using it to do so safely
* XXX 2 seconds should be plenty?
*/
static void
rmixl_pcix_pip_free_callout(rmixl_pcix_intr_t *pip)
{
callout_init(&pip->callout, 0);
callout_reset(&pip->callout, 2 * hz, rmixl_pcix_pip_free, pip);
}
/*
* rmixl_physaddr_init_pcix:
* called from rmixl_physaddr_init to get region addrs & sizes
* from PCIX CFG, ECFG, IO, MEM BARs
*/
void
rmixl_physaddr_init_pcix(struct extent *ext)
{
u_long base;
u_long size;
uint32_t r;
#ifdef DDB
int rmixl_pcix_intr_chk(void);
int
rmixl_pcix_intr_chk(void)
{
uint32_t control, status, error_status;
control = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_CONTROL);
status = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_STATUS);
error_status = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_ERR_STATUS);
control |= PCIX_INTR_CONTROL_DIA;
RMIXL_PCIXREG_WRITE(RMIXL_PCIX_ECFG_INTR_CONTROL, control);
control = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_CONTROL);
status = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_STATUS);
error_status = RMIXL_PCIXREG_READ(RMIXL_PCIX_ECFG_INTR_ERR_STATUS);