PCI-CardBus bridge support for sparc64:

- handle devices which has no OBP node.
- move PCI latency-timer initialization from pci_intr_map to
  pci_enumerate_bus.
- make PCI bus free space extents for cardbus devices.
- fix PCI config space map size.
- some code integrations.
This commit is contained in:
nakayama 2003-03-22 06:33:09 +00:00
parent a8e1b2f5c5
commit aad45dd3ad
3 changed files with 252 additions and 63 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: pci_machdep.c,v 1.34 2002/12/10 13:44:51 pk Exp $ */
/* $NetBSD: pci_machdep.c,v 1.35 2003/03/22 06:33:09 nakayama Exp $ */
/*
* Copyright (c) 1999, 2000 Matthew R. Green
@ -114,8 +114,10 @@ pci_make_tag(pc, b, d, f)
int d;
int f;
{
struct psycho_pbm *pp = pc->cookie;
struct ofw_pci_register reg;
pcitag_t tag;
int (*valid) __P((void *));
int busrange[2];
int node, len;
#ifdef DEBUG
@ -123,6 +125,18 @@ pci_make_tag(pc, b, d, f)
bzero(name, sizeof(name));
#endif
/*
* Refer to the PCI/CardBus bus node first.
* It returns a tag if node is present and bus is valid.
*/
if (0 <= b && b < 256) {
node = (*pp->pp_busnode)[b].node;
valid = (*pp->pp_busnode)[b].valid;
if (node != 0 && d == 0 &&
(valid == NULL || (*valid)((*pp->pp_busnode)[b].arg)))
return ofpci_make_tag(pc, node, b, d, f);
}
/*
* Hunt for the node that corresponds to this device
*
@ -247,8 +261,9 @@ pci_enumerate_bus(struct pci_softc *sc,
struct ofw_pci_register reg;
pci_chipset_tag_t pc = sc->sc_pc;
pcitag_t tag;
pcireg_t class, csr;
pcireg_t class, csr, bhlc, ic;
int node, b, d, f, ret;
int len, bus_frequency, lt, cl;
char name[30];
extern int pci_config_dump;
@ -257,12 +272,36 @@ pci_enumerate_bus(struct pci_softc *sc,
else
node = pc->rootnode;
len = OF_getproplen(node, "clock-frequency");
if (len < sizeof(bus_frequency)) {
DPRINTF(SPDB_PROBE,
("pci_enumerate_bus: clock-frequency len %d too small\n",
len));
return 0;
}
if (OF_getprop(node, "clock-frequency", &bus_frequency,
sizeof(bus_frequency)) != len) {
DPRINTF(SPDB_PROBE,
("pci_enumerate_bus: could not read clock-frequency\n"));
return 0;
}
bus_frequency /= 1000000;
/* Turn on parity for the bus. */
tag = ofpci_make_tag(pc, node, sc->sc_bus, 0, 0);
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
csr |= PCI_COMMAND_PARITY_ENABLE;
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
/*
* Initialize the latency timer register.
* The value 0x40 is from Solaris.
*/
bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
bhlc |= 0x40 << PCI_LATTIMER_SHIFT;
pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc);
if (pci_config_dump) pci_conf_print(pc, tag, NULL);
for (node = OF_child(node); node != 0 && node != -1;
@ -297,6 +336,30 @@ pci_enumerate_bus(struct pci_softc *sc,
csr |= PCI_COMMAND_PARITY_ENABLE;
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
/*
* Initialize the latency timer register for busmaster
* devices to work properly.
* latency-timer = min-grant * bus-freq / 4 (from FreeBSD)
* Also initialize the cache line size register.
* Solaris anytime sets this register to the value 0x10.
*/
bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
ic = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
lt = min(PCI_MIN_GNT(ic) * bus_frequency / 4, 255);
if (lt == 0 || lt < PCI_LATTIMER(bhlc))
lt = PCI_LATTIMER(bhlc);
cl = PCI_CACHELINE(bhlc);
if (cl == 0)
cl = 0x10;
bhlc &= ~((PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT) |
(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT));
bhlc |= (lt << PCI_LATTIMER_SHIFT) |
(cl << PCI_CACHELINE_SHIFT);
pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc);
ret = pci_probe_device(sc, tag, match, pap);
if (match != NULL && ret != 0)
return (ret);
@ -373,26 +436,10 @@ pci_intr_map(pa, ihp)
pci_intr_handle_t *ihp;
{
pcitag_t tag = pa->pa_tag;
pcireg_t bhlc, ic;
int bus_frequency, val, bus_node;
int interrupts;
int len, node = PCITAG_NODE(tag);
char devtype[30];
bus_node = OF_parent(node);
len = OF_getproplen(bus_node, "clock-frequency");
if (len < sizeof(bus_frequency)) {
DPRINTF(SPDB_INTMAP,
("pci_intr_map: clock-frequency len %d too small\n", len));
return (ENODEV);
}
if (OF_getprop(bus_node, "clock-frequency", (void *)&bus_frequency,
sizeof(bus_frequency)) != len) {
DPRINTF(SPDB_INTMAP,
("pci_intr_map: could not read bus_frequency\n"));
return (ENODEV);
}
len = OF_getproplen(node, "interrupts");
if (len < sizeof(interrupts)) {
DPRINTF(SPDB_INTMAP,
@ -419,23 +466,6 @@ pci_intr_map(pa, ihp)
}
}
/*
* Initialize the latency timer register for busmaster devices
* to work properly.
* latency-timer = min-grant * bus-freq / 4 (from FreeBSD)
*/
ic = pci_conf_read(pa->pa_pc, tag, PCI_INTERRUPT_REG);
bus_frequency /= 1000000;
val = min(PCI_MIN_GNT(ic) * bus_frequency / 4, 255);
if (val > 0) {
bhlc = pci_conf_read(pa->pa_pc, tag, PCI_BHLC_REG);
if (PCI_LATTIMER(bhlc) < val) {
bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
bhlc |= (val << PCI_LATTIMER_SHIFT);
pci_conf_write(pa->pa_pc, tag, PCI_BHLC_REG, bhlc);
}
}
/* XXXX -- we use the ino. What if there is a valid IGN? */
*ihp = interrupts;
return (0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: psycho.c,v 1.57 2002/12/10 13:44:51 pk Exp $ */
/* $NetBSD: psycho.c,v 1.58 2003/03/22 06:33:09 nakayama Exp $ */
/*
* Copyright (c) 2001, 2002 Eduardo E. Horvath
@ -55,6 +55,8 @@ int psycho_debug = 0x0;
#include <sys/time.h>
#include <sys/reboot.h>
#include <uvm/uvm.h>
#define _SPARC_BUS_DMA_PRIVATE
#include <machine/bus.h>
#include <machine/autoconf.h>
@ -73,6 +75,8 @@ int psycho_debug = 0x0;
static pci_chipset_tag_t psycho_alloc_chipset __P((struct psycho_pbm *, int,
pci_chipset_tag_t));
static struct extent *psycho_alloc_extent __P((struct psycho_pbm *, int, int,
char *));
static void psycho_get_bus_range __P((int, int *));
static void psycho_get_ranges __P((int, struct psycho_ranges **, int *));
static void psycho_set_intr __P((struct psycho_softc *, int, void *,
@ -94,6 +98,9 @@ static void psycho_iommu_init __P((struct psycho_softc *, int));
* bus space and bus dma support for UltraSPARC `psycho'. note that most
* of the bus dma support is provided by the iommu dvma controller.
*/
static int get_childspace __P((int));
static struct psycho_ranges *get_psychorange __P((struct psycho_pbm *, int));
static paddr_t psycho_bus_mmap __P((bus_space_tag_t, bus_addr_t, off_t,
int, int));
static int _psycho_bus_map __P((bus_space_tag_t, bus_addr_t, bus_size_t, int,
@ -382,12 +389,11 @@ found:
/*
* Allocate our psycho_pbm
*/
pp = sc->sc_psycho_this = malloc(sizeof *pp, M_DEVBUF, M_NOWAIT);
pp = sc->sc_psycho_this = malloc(sizeof *pp, M_DEVBUF,
M_NOWAIT | M_ZERO);
if (pp == NULL)
panic("could not allocate psycho pbm");
memset(pp, 0, sizeof *pp);
pp->pp_sc = sc;
/* grab the psycho ranges */
@ -398,6 +404,7 @@ found:
pba.pba_bus = psycho_br[0];
pba.pba_bridgetag = NULL;
pp->pp_busmax = psycho_br[1];
printf("bus range %u to %u", psycho_br[0], psycho_br[1]);
printf("; PCI bus %d", psycho_br[0]);
@ -419,6 +426,10 @@ found:
printf("\n");
/* allocate extents for free bus space */
pp->pp_exmem = psycho_alloc_extent(pp, sc->sc_node, 0x02, "psycho mem");
pp->pp_exio = psycho_alloc_extent(pp, sc->sc_node, 0x01, "psycho io");
/*
* And finally, if we're a sabre or the first of a pair of psycho's to
* arrive here, start up the IOMMU and get a config space tag.
@ -471,6 +482,14 @@ found:
sc->sc_regs->intr_retry_timer = 0xff;
}
/*
* Allocate bus node, this contains a prom node per bus.
*/
pp->pp_busnode = malloc(sizeof(*pp->pp_busnode), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (pp->pp_busnode == NULL)
panic("psycho_attach: malloc pp->pp_busnode");
/*
* Setup IOMMU and PCI configuration if we're the first
* of a pair of psycho's to arrive here.
@ -520,11 +539,14 @@ found:
i = sc->sc_bustag->type;
sc->sc_bustag->type = PCI_CONFIG_BUS_SPACE;
if (bus_space_map(sc->sc_bustag, sc->sc_basepaddr + 0x01000000,
0x0100000, 0, &bh))
0x01000000, 0, &bh))
panic("could not map psycho PCI configuration space");
sc->sc_bustag->type = i;
sc->sc_configaddr = bh;
} else {
/* Share bus numbers with the pair of mine */
pp->pp_busnode = osc->sc_psycho_this->pp_busnode;
/* Just copy IOMMU state, config tag and address */
sc->sc_is = osc->sc_is;
sc->sc_configtag = osc->sc_configtag;
@ -624,6 +646,84 @@ psycho_alloc_chipset(pp, node, pc)
return (npc);
}
/*
* create extent for free bus space, then allocate assigned regions.
*/
static struct extent *
psycho_alloc_extent(pp, node, ss, name)
struct psycho_pbm *pp;
int node;
int ss;
char *name;
{
struct psycho_registers *pa = NULL;
struct psycho_ranges *pr;
struct extent *ex;
bus_addr_t baddr, addr;
bus_size_t bsize, size;
int i, num;
/* get bus space size */
pr = get_psychorange(pp, ss);
if (pr == NULL) {
printf("psycho_alloc_extent: get_psychorange failed\n");
return NULL;
}
baddr = 0x00000000;
bsize = BUS_ADDR(pr->size_hi, pr->size_lo);
/* get available lists */
if (PROM_getprop(node, "available", sizeof(*pa), &num, (void **)&pa)) {
printf("psycho_alloc_extent: PROM_getprop failed\n");
return NULL;
}
/* create extent */
ex = extent_create(name, baddr, bsize - baddr - 1, M_DEVBUF, 0, 0,
EX_NOWAIT);
if (ex == NULL) {
printf("psycho_alloc_extent: extent_create failed\n");
goto ret;
}
/* allocate assigned regions */
for (i = 0; i < num; i++)
if (((pa[i].phys_hi >> 24) & 0x03) == ss) {
/* allocate bus space */
addr = BUS_ADDR(pa[i].phys_mid, pa[i].phys_lo);
size = BUS_ADDR(pa[i].size_hi, pa[i].size_lo);
if (extent_alloc_region(ex, baddr, addr - baddr,
EX_NOWAIT)) {
printf("psycho_alloc_extent: "
"extent_alloc_region %" PRIx64 "-%"
PRIx64 " failed\n", baddr, addr);
extent_destroy(ex);
ex = NULL;
goto ret;
}
baddr = addr + size;
}
/* allocate left region if available */
if (baddr < bsize)
if (extent_alloc_region(ex, baddr, bsize - baddr, EX_NOWAIT)) {
printf("psycho_alloc_extent: extent_alloc_region %"
PRIx64 "-%" PRIx64 " failed\n", baddr, bsize);
extent_destroy(ex);
ex = NULL;
goto ret;
}
#ifdef DEBUG
/* print extent */
extent_print(ex);
#endif
ret:
/* return extent */
free(pa, M_DEVBUF);
return ex;
}
/*
* grovel the OBP for various psycho properties
*/
@ -886,8 +986,6 @@ psycho_alloc_dma_tag(pp)
* PCI physical addresses.
*/
static int get_childspace __P((int));
static int
get_childspace(type)
int type;
@ -917,6 +1015,21 @@ get_childspace(type)
return (ss);
}
static struct psycho_ranges *
get_psychorange(pp, ss)
struct psycho_pbm *pp;
int ss;
{
int i;
for (i = 0; i < pp->pp_nrange; i++) {
if (((pp->pp_range[i].cspace >> 24) & 0x03) == ss)
return (&pp->pp_range[i]);
}
/* not found */
return (NULL);
}
static int
_psycho_bus_map(t, offset, size, flags, unused, hp)
bus_space_tag_t t;
@ -928,7 +1041,9 @@ _psycho_bus_map(t, offset, size, flags, unused, hp)
{
struct psycho_pbm *pp = t->cookie;
struct psycho_softc *sc = pp->pp_sc;
int i, ss;
struct psycho_ranges *pr;
bus_addr_t paddr;
int ss;
DPRINTF(PDB_BUSMAP,
("_psycho_bus_map: type %d off %qx sz %qx flags %d",
@ -938,15 +1053,11 @@ _psycho_bus_map(t, offset, size, flags, unused, hp)
ss = get_childspace(t->type);
DPRINTF(PDB_BUSMAP, (" cspace %d", ss));
for (i = 0; i < pp->pp_nrange; i++) {
bus_addr_t paddr;
if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss)
continue;
paddr = pp->pp_range[i].phys_lo + offset;
paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi<<32);
DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_map: mapping paddr space %lx offset %lx paddr %qx\n",
pr = get_psychorange(pp, ss);
if (pr != NULL) {
paddr = BUS_ADDR(pr->phys_hi, pr->phys_lo + offset);
DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_map: mapping paddr "
"space %lx offset %lx paddr %qx\n",
(long)ss, (long)offset,
(unsigned long long)paddr));
return ((*sc->sc_bustag->sparc_bus_map)(t, paddr, size,
@ -967,23 +1078,19 @@ psycho_bus_mmap(t, paddr, off, prot, flags)
bus_addr_t offset = paddr;
struct psycho_pbm *pp = t->cookie;
struct psycho_softc *sc = pp->pp_sc;
int i, ss;
struct psycho_ranges *pr;
int ss;
ss = get_childspace(t->type);
DPRINTF(PDB_BUSMAP, ("_psycho_bus_mmap: prot %x flags %d pa %qx\n",
prot, flags, (unsigned long long)paddr));
for (i = 0; i < pp->pp_nrange; i++) {
bus_addr_t paddr;
if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss)
continue;
paddr = pp->pp_range[i].phys_lo + offset;
paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi<<32);
pr = get_psychorange(pp, ss);
if (pr != NULL) {
paddr = BUS_ADDR(pr->phys_hi, pr->phys_lo + offset);
DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_mmap: mapping paddr "
"space %lx offset %lx paddr %qx\n",
"space %lx offset %lx paddr %qx\n",
(long)ss, (long)offset,
(unsigned long long)paddr));
return (bus_space_mmap(sc->sc_bustag, paddr, off,
@ -993,6 +1100,45 @@ psycho_bus_mmap(t, paddr, off, prot, flags)
return (-1);
}
/*
* Get a PCI offset address from bus_space_handle_t.
*/
bus_addr_t
psycho_bus_offset(t, hp)
bus_space_tag_t t;
bus_space_handle_t *hp;
{
struct psycho_pbm *pp = t->cookie;
struct psycho_ranges *pr;
bus_addr_t addr, offset;
vaddr_t va;
int ss;
addr = hp->_ptr;
ss = get_childspace(t->type);
DPRINTF(PDB_BUSMAP, ("psycho_bus_offset: type %d addr %" PRIx64
" cspace %d", t->type, addr, ss));
pr = get_psychorange(pp, ss);
if (pr != NULL) {
if (!PHYS_ASI(hp->_asi)) {
va = trunc_page((vaddr_t)addr);
if (pmap_extract(pmap_kernel(), va, &addr) == FALSE) {
DPRINTF(PDB_BUSMAP,
("\n pmap_extract FAILED\n"));
return (-1);
}
addr += hp->_ptr & PGOFSET;
}
offset = BUS_ADDR_PADDR(addr) - pr->phys_lo;
DPRINTF(PDB_BUSMAP, ("\npsycho_bus_offset: paddr %" PRIx64
" offset %" PRIx64 "\n", addr, offset));
return (offset);
}
DPRINTF(PDB_BUSMAP, ("\n FAILED\n"));
return (-1);
}
/*
* install an interrupt handler for a PCI device

View File

@ -1,4 +1,4 @@
/* $NetBSD: psychovar.h,v 1.8 2002/06/20 18:26:24 eeh Exp $ */
/* $NetBSD: psychovar.h,v 1.9 2003/03/22 06:33:10 nakayama Exp $ */
/*
* Copyright (c) 1999, 2000 Matthew R. Green
@ -51,6 +51,10 @@ struct psycho_pbm {
int pp_nrange;
int pp_nintmap;
/* extents for free bus space */
struct extent *pp_exmem;
struct extent *pp_exio;
/* chipset tag for this instance */
pci_chipset_tag_t pp_pc;
@ -59,6 +63,12 @@ struct psycho_pbm {
bus_space_tag_t pp_iot;
bus_dma_tag_t pp_dmat;
int pp_bus;
int pp_busmax;
struct pp_busnode {
int node;
int (*valid) __P((void *));
void *arg;
} (*pp_busnode)[256];
int pp_flags;
/* and pointers into the psycho regs for our bits */
@ -113,6 +123,9 @@ struct psycho_softc {
struct iommu_state *sc_is;
};
/* get a PCI offset address from bus_space_handle_t */
bus_addr_t psycho_bus_offset __P((bus_space_tag_t, bus_space_handle_t *));
/* config space is per-psycho. mem/io/dma are per-pci bus */
bus_dma_tag_t psycho_alloc_dma_tag __P((struct psycho_pbm *));
bus_space_tag_t psycho_alloc_bus_tag __P((struct psycho_pbm *, int));