d4da990081
do not set the virtual address to the bus address when the bus doesn't have either of the PCI_RF_DENSE or PCI_RF_BWX flags set. The TGA driver uses the virtual address to access the registers, which on some machines can cause a memory management fault. Map the bus address as K0SEG virtual memory instead. Note that with some hardware combinations involving the TGA2 adapter this change merely results that the memory management fault is replaced by a machine check.
373 lines
8.5 KiB
C
373 lines
8.5 KiB
C
/*
|
|
* Copyright (c) 1997, Stefan Esser <se@freebsd.org>
|
|
* All rights reserved.
|
|
*
|
|
* 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 unmodified, 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_isa.h"
|
|
|
|
#define __RMAN_RESOURCE_VISIBLE
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/bus.h>
|
|
#include <machine/bus.h>
|
|
#include <sys/interrupt.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
#include <machine/chipset.h>
|
|
#include <machine/cpuconf.h>
|
|
#include <machine/resource.h>
|
|
#include <machine/md_var.h>
|
|
#include <alpha/pci/pcibus.h>
|
|
#include <alpha/isa/isavar.h>
|
|
|
|
#include "alphapci_if.h"
|
|
|
|
#define ISA_IRQ_OFFSET 0xe0
|
|
#define ISA_IRQ_LEN 0x10
|
|
|
|
struct alpha_busspace *busspace_isa_io;
|
|
struct alpha_busspace *busspace_isa_mem;
|
|
|
|
char chipset_type[10];
|
|
int chipset_bwx = 0;
|
|
long chipset_ports = 0;
|
|
long chipset_memory = 0;
|
|
long chipset_dense = 0;
|
|
long chipset_hae_mask = 0;
|
|
|
|
SYSCTL_NODE(_hw, OID_AUTO, chipset, CTLFLAG_RW, 0, "PCI chipset information");
|
|
SYSCTL_STRING(_hw_chipset, OID_AUTO, type, CTLFLAG_RD, chipset_type, 0,
|
|
"PCI chipset type");
|
|
SYSCTL_INT(_hw_chipset, OID_AUTO, bwx, CTLFLAG_RD, &chipset_bwx, 0,
|
|
"PCI chipset supports BWX access");
|
|
SYSCTL_LONG(_hw_chipset, OID_AUTO, ports, CTLFLAG_RD, &chipset_ports, 0,
|
|
"PCI chipset port address");
|
|
SYSCTL_LONG(_hw_chipset, OID_AUTO, memory, CTLFLAG_RD, &chipset_memory, 0,
|
|
"PCI chipset memory address");
|
|
SYSCTL_LONG(_hw_chipset, OID_AUTO, dense, CTLFLAG_RD, &chipset_dense, 0,
|
|
"PCI chipset dense memory address");
|
|
SYSCTL_LONG(_hw_chipset, OID_AUTO, hae_mask, CTLFLAG_RD, &chipset_hae_mask, 0,
|
|
"PCI chipset mask for HAE register");
|
|
|
|
int
|
|
alpha_pci_route_interrupt(device_t bus, device_t dev, int pin)
|
|
{
|
|
/*
|
|
* Validate requested pin number.
|
|
*/
|
|
if ((pin < 1) || (pin > 4))
|
|
return(255);
|
|
|
|
if (platform.pci_intr_route)
|
|
return(platform.pci_intr_route(bus, dev, pin));
|
|
return(255);
|
|
}
|
|
|
|
#ifdef DEV_ISA
|
|
struct resource *
|
|
alpha_platform_alloc_ide_intr(int chan)
|
|
{
|
|
int irqs[2] = { 14, 15 };
|
|
return isa_alloc_intr(0, 0, irqs[chan]);
|
|
}
|
|
|
|
int
|
|
alpha_platform_release_ide_intr(int chan, struct resource *res)
|
|
{
|
|
return isa_release_intr(0, 0, res);
|
|
}
|
|
|
|
int
|
|
alpha_platform_setup_ide_intr(device_t dev,
|
|
struct resource *res,
|
|
driver_intr_t *fn, void *arg,
|
|
void **cookiep)
|
|
{
|
|
return isa_setup_intr(0, dev, res, INTR_TYPE_BIO, fn, arg, cookiep);
|
|
}
|
|
|
|
int
|
|
alpha_platform_teardown_ide_intr(device_t dev,
|
|
struct resource *res, void *cookie)
|
|
{
|
|
return isa_teardown_intr(0, dev, res, cookie);
|
|
}
|
|
#else
|
|
struct resource *
|
|
alpha_platform_alloc_ide_intr(int chan)
|
|
{
|
|
return (NULL);
|
|
}
|
|
int
|
|
alpha_platform_release_ide_intr(int chan, struct resource *res)
|
|
{
|
|
return (ENXIO);
|
|
}
|
|
|
|
int
|
|
alpha_platform_setup_ide_intr(struct resource *res,
|
|
driver_intr_t *fn, void *arg, void **cookiep)
|
|
{
|
|
return (ENXIO);
|
|
}
|
|
|
|
int
|
|
alpha_platform_teardown_ide_intr(struct resource *res, void *cookie)
|
|
{
|
|
return (ENXIO);
|
|
}
|
|
#endif
|
|
|
|
static struct rman irq_rman, port_rman, mem_rman;
|
|
|
|
int
|
|
alpha_platform_pci_setup_intr(device_t dev, device_t child,
|
|
struct resource *irq, int flags,
|
|
driver_intr_t *intr, void *arg,
|
|
void **cookiep)
|
|
{
|
|
#ifdef DEV_ISA
|
|
/*
|
|
* XXX - If we aren't the resource manager for this IRQ, assume that
|
|
* it is actually handled by the ISA PIC.
|
|
*/
|
|
if(irq->r_rm != &irq_rman)
|
|
return isa_setup_intr(dev, child, irq, flags, intr, arg,
|
|
cookiep);
|
|
else
|
|
#endif
|
|
return bus_generic_setup_intr(dev, child, irq, flags, intr,
|
|
arg, cookiep);
|
|
}
|
|
|
|
int
|
|
alpha_platform_pci_teardown_intr(device_t dev, device_t child,
|
|
struct resource *irq, void *cookie)
|
|
{
|
|
#ifdef DEV_ISA
|
|
/*
|
|
* XXX - If we aren't the resource manager for this IRQ, assume that
|
|
* it is actually handled by the ISA PIC.
|
|
*/
|
|
if(irq->r_rm != &irq_rman)
|
|
return isa_teardown_intr(dev, child, irq, cookie);
|
|
else
|
|
#endif
|
|
return bus_generic_teardown_intr(dev, child, irq, cookie);
|
|
}
|
|
|
|
void
|
|
pci_init_resources(void)
|
|
{
|
|
irq_rman.rm_start = 0;
|
|
irq_rman.rm_end = 65536;
|
|
irq_rman.rm_type = RMAN_ARRAY;
|
|
irq_rman.rm_descr = "PCI Mapped Interrupts";
|
|
if (rman_init(&irq_rman)
|
|
|| rman_manage_region(&irq_rman, 0, 65536))
|
|
panic("pci_init_resources irq_rman");
|
|
|
|
port_rman.rm_start = 0;
|
|
port_rman.rm_end = ~0u;
|
|
port_rman.rm_type = RMAN_ARRAY;
|
|
port_rman.rm_descr = "I/O ports";
|
|
if (rman_init(&port_rman)
|
|
|| rman_manage_region(&port_rman, 0x0, (1L << 32)))
|
|
panic("pci_init_resources port_rman");
|
|
|
|
mem_rman.rm_start = 0;
|
|
mem_rman.rm_end = ~0u;
|
|
mem_rman.rm_type = RMAN_ARRAY;
|
|
mem_rman.rm_descr = "I/O memory";
|
|
if (rman_init(&mem_rman)
|
|
|| rman_manage_region(&mem_rman, 0x0, (1L << 32)))
|
|
panic("pci_init_resources mem_rman");
|
|
}
|
|
|
|
/*
|
|
* Allocate a resource on behalf of child. NB: child is usually going to be a
|
|
* child of one of our descendants, not a direct child of the pci chipset.
|
|
*/
|
|
struct resource *
|
|
alpha_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
|
|
u_long start, u_long end, u_long count, u_int flags)
|
|
{
|
|
struct rman *rm;
|
|
struct resource *rv;
|
|
void *va;
|
|
|
|
switch (type) {
|
|
case SYS_RES_IRQ:
|
|
#ifdef DEV_ISA
|
|
if((start >= ISA_IRQ_OFFSET) &&
|
|
(end < ISA_IRQ_OFFSET + ISA_IRQ_LEN)) {
|
|
return isa_alloc_intrs(bus, child,
|
|
start - ISA_IRQ_OFFSET,
|
|
end - ISA_IRQ_OFFSET);
|
|
}
|
|
else
|
|
#endif
|
|
rm = &irq_rman;
|
|
break;
|
|
|
|
case SYS_RES_IOPORT:
|
|
case SYS_RES_MEMORY:
|
|
rm = ALPHAPCI_GET_RMAN(bus, type);
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
rv = rman_reserve_resource(rm, start, end, count, flags, child);
|
|
if (rv == 0)
|
|
return 0;
|
|
|
|
rman_set_bustag(rv, ALPHAPCI_GET_BUSTAG(bus, type));
|
|
rman_set_bushandle(rv, rv->r_start);
|
|
switch (type) {
|
|
case SYS_RES_MEMORY:
|
|
va = 0;
|
|
if (flags & PCI_RF_DENSE)
|
|
va = ALPHAPCI_CVT_DENSE(bus, rv->r_start);
|
|
else if (flags & PCI_RF_BWX)
|
|
va = ALPHAPCI_CVT_BWX(bus, rv->r_start);
|
|
else
|
|
va = (void *)ALPHA_PHYS_TO_K0SEG(rv->r_start);
|
|
rman_set_virtual(rv, va);
|
|
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
pci_activate_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
return (rman_activate_resource(r));
|
|
}
|
|
|
|
int
|
|
pci_deactivate_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
return (rman_deactivate_resource(r));
|
|
}
|
|
|
|
int
|
|
pci_release_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
return (rman_release_resource(r));
|
|
}
|
|
|
|
struct alpha_busspace *
|
|
pci_get_bustag(device_t dev, int type)
|
|
{
|
|
switch (type) {
|
|
case SYS_RES_IOPORT:
|
|
return busspace_isa_io;
|
|
|
|
case SYS_RES_MEMORY:
|
|
return busspace_isa_mem;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct rman *
|
|
pci_get_rman(device_t dev, int type)
|
|
{
|
|
switch (type) {
|
|
case SYS_RES_IOPORT:
|
|
return &port_rman;
|
|
|
|
case SYS_RES_MEMORY:
|
|
return &mem_rman;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include "opt_ddb.h"
|
|
#ifdef DDB
|
|
#include <ddb/ddb.h>
|
|
|
|
DB_COMMAND(in, db_in)
|
|
{
|
|
int c;
|
|
int size;
|
|
|
|
if (!have_addr)
|
|
return;
|
|
|
|
size = -1;
|
|
while ((c = *modif++) != '\0') {
|
|
switch (c) {
|
|
case 'b':
|
|
size = 1;
|
|
break;
|
|
case 'w':
|
|
size = 2;
|
|
break;
|
|
case 'l':
|
|
size = 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (size < 0) {
|
|
db_printf("bad size\n");
|
|
return;
|
|
}
|
|
|
|
if (count <= 0) count = 1;
|
|
while (--count >= 0) {
|
|
db_printf("%08lx:\t", addr);
|
|
switch (size) {
|
|
case 1:
|
|
db_printf("%02x\n", inb(addr));
|
|
break;
|
|
case 2:
|
|
db_printf("%04x\n", inw(addr));
|
|
break;
|
|
case 4:
|
|
db_printf("%08x\n", inl(addr));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|