Commit 364545de authored by Jesse Barnes's avatar Jesse Barnes Committed by David Mosberger

[PATCH] ia64: SN update

And here's the SN specific part of the update.  This should get an SN2
compile all the way to the link stage, where I still have some devfs
stuff to cleanup.
parent 2853991a
......@@ -52,11 +52,12 @@ core-$(CONFIG_IA64_GENERIC) += arch/ia64/dig/ arch/ia64/hp/common/ arch/ia64/hp
core-$(CONFIG_IA64_HP_ZX1) += arch/ia64/dig/
core-$(CONFIG_IA64_SGI_SN) += arch/ia64/sn/kernel/ \
arch/ia64/sn/io/ \
arch/ia64/sn/io/sn2/ \
arch/ia64/sn/io/sn2/pcibr/ \
arch/ia64/sn/kernel/sn2/
drivers-$(CONFIG_PCI) += arch/ia64/pci/
drivers-$(CONFIG_IA64_HP_SIM) += arch/ia64/hp/sim/
drivers-$(CONFIG_IA64_HP_ZX1) += arch/ia64/hp/common/ arch/ia64/hp/zx1/
drivers-$(CONFIG_IA64_SGI_SN) += arch/ia64/sn/fakeprom/
boot := arch/ia64/boot
tools := arch/ia64/tools
......
......@@ -17,6 +17,6 @@ obj-$(CONFIG_IA64_SGI_SN) += stubs.o sgi_if.o xswitch.o klgraph_hack.o \
hcl.o labelcl.o invent.o sgi_io_sim.o \
klgraph_hack.o hcl_util.o cdl.o hubdev.o hubspc.o \
alenlist.o pci.o pci_dma.o ate_utils.o \
ifconfig_net.o io.o ifconfig_bus.o
ifconfig_net.o io.o ioconfig_bus.o
obj-$(CONFIG_PCIBA) += pciba.o
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* ioconfig_bus - SGI's Persistent PCI Bus Numbering.
*
* Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/sn/sgi.h>
#include <linux/devfs_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm//sn/sn_sal.h>
#include <asm/sn/addrs.h>
#include <asm/sn/ioconfig_bus.h>
#define SGI_IOCONFIG_BUS "SGI-PERSISTENT PCI BUS NUMBERING"
#define SGI_IOCONFIG_BUS_VERSION "1.0"
/*
* Some Global definitions.
*/
devfs_handle_t ioconfig_bus_handle = NULL;
unsigned long ioconfig_bus_debug = 0;
#ifdef IOCONFIG_BUS_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
u64 ioconfig_file = 0;
u64 ioconfig_file_size = 0;
u64 ioconfig_activated = 0;
char ioconfig_kernopts[128];
/*
* For debugging purpose .. hardcode a table ..
*/
struct ascii_moduleid *ioconfig_bus_table;
u64 ioconfig_bus_table_size = 0;
int free_entry = 0;
int new_entry = 0;
int next_basebus_number = 0;
void
ioconfig_get_busnum(char *io_moduleid, int *bus_num)
{
struct ascii_moduleid *temp;
int index;
DBG("ioconfig_get_busnum io_moduleid %s\n", io_moduleid);
*bus_num = -1;
temp = ioconfig_bus_table;
for (index = 0; index < free_entry; temp++, index++) {
if ( (io_moduleid[0] == temp->io_moduleid[0]) &&
(io_moduleid[1] == temp->io_moduleid[1]) &&
(io_moduleid[2] == temp->io_moduleid[2]) &&
(io_moduleid[4] == temp->io_moduleid[4]) &&
(io_moduleid[5] == temp->io_moduleid[5]) ) {
*bus_num = index * 0x10;
return;
}
}
/*
* New IO Brick encountered.
*/
if (((int)io_moduleid[0]) == 0) {
DBG("ioconfig_get_busnum: Invalid Module Id given %s\n", io_moduleid);
return;
}
io_moduleid[3] = '#';
strcpy((char *)&(ioconfig_bus_table[free_entry].io_moduleid), io_moduleid);
*bus_num = free_entry * 0x10;
free_entry++;
}
void
dump_ioconfig_table()
{
int index = 0;
struct ascii_moduleid *temp;
temp = ioconfig_bus_table;
while (index < free_entry) {
DBG("ASSCI Module ID %s\n", temp->io_moduleid);
temp++;
index++;
}
}
/*
* nextline
* This routine returns the nextline in the buffer.
*/
int nextline(char *buffer, char **next, char *line)
{
char *temp;
if (buffer[0] == 0x0) {
return(0);
}
temp = buffer;
while (*temp != 0) {
*line = *temp;
if (*temp != '\n'){
*line = *temp;
temp++; line++;
} else
break;
}
if (*temp == 0)
*next = temp;
else
*next = ++temp;
return(1);
}
/*
* build_pcibus_name
* This routine parses the ioconfig contents read into
* memory by ioconfig command in EFI and builds the
* persistent pci bus naming table.
*/
void
build_moduleid_table(char *file_contents, struct ascii_moduleid *table)
{
/*
* Read the whole file into memory.
*/
int rc;
char *name;
char *temp;
char *next;
char *current;
char *line;
struct ascii_moduleid *moduleid;
line = kmalloc(256, GFP_KERNEL);
memset(line, 0,256);
name = kmalloc(125, GFP_KERNEL);
memset(name, 0, 125);
moduleid = table;
current = file_contents;
while (nextline(current, &next, line)){
DBG("current 0x%lx next 0x%lx\n", current, next);
temp = line;
/*
* Skip all leading Blank lines ..
*/
while (isspace(*temp))
if (*temp != '\n')
temp++;
else
break;
if (*temp == '\n') {
current = next;
memset(line, 0, 256);
continue;
}
/*
* Skip comment lines
*/
if (*temp == '#') {
current = next;
memset(line, 0, 256);
continue;
}
/*
* Get the next free entry in the table.
*/
rc = sscanf(temp, "%s", name);
strcpy(&moduleid->io_moduleid[0], name);
DBG("Found %s\n", name);
moduleid++;
free_entry++;
current = next;
memset(line, 0, 256);
}
new_entry = free_entry;
kfree(line);
kfree(name);
return;
}
void
ioconfig_bus_init(void)
{
struct ia64_sal_retval ret_stuff;
u64 *temp;
int cnode;
DBG("ioconfig_bus_init called.\n");
for (cnode = 0; cnode < numnodes; cnode++) {
nasid_t nasid;
/*
* Make SAL call to get the address of the bus configuration table.
*/
ret_stuff.status = (uint64_t)0;
ret_stuff.v0 = (uint64_t)0;
ret_stuff.v1 = (uint64_t)0;
ret_stuff.v2 = (uint64_t)0;
nasid = COMPACT_TO_NASID_NODEID(cnode);
SAL_CALL(ret_stuff, SN_SAL_BUS_CONFIG, 0, nasid, 0, 0, 0, 0, 0);
temp = (u64 *)TO_NODE_CAC(nasid, ret_stuff.v0);
ioconfig_file = *temp;
DBG("ioconfig_bus_init: Nasid %d ret_stuff.v0 0x%lx\n", nasid,
ret_stuff.v0);
if (ioconfig_file) {
ioconfig_file_size = ret_stuff.v1;
ioconfig_file = (ioconfig_file | CACHEABLE_MEM_SPACE);
ioconfig_activated = 1;
break;
}
}
DBG("ioconfig_bus_init: ret_stuff.v0 %p ioconfig_file %p %d\n",
ret_stuff.v0, (void *)ioconfig_file, (int)ioconfig_file_size);
ioconfig_bus_table = kmalloc( 512, GFP_KERNEL );
memset(ioconfig_bus_table, 0, 512);
/*
* If ioconfig options are given on the bootline .. take it.
*/
if (*ioconfig_kernopts != '\0') {
/*
* ioconfig="..." kernel options given.
*/
DBG("ioconfig_bus_init: Kernel Options given.\n");
(void) build_moduleid_table((char *)ioconfig_kernopts, ioconfig_bus_table);
(void) dump_ioconfig_table(ioconfig_bus_table);
return;
}
if (ioconfig_activated) {
DBG("ioconfig_bus_init: ioconfig file given.\n");
(void) build_moduleid_table((char *)ioconfig_file, ioconfig_bus_table);
(void) dump_ioconfig_table(ioconfig_bus_table);
} else {
DBG("ioconfig_bus_init: ioconfig command not executed in prom\n");
}
}
void
ioconfig_bus_new_entries(void)
{
int index = 0;
struct ascii_moduleid *temp;
if ((ioconfig_activated) && (free_entry > new_entry)) {
printk("### Please add the following new IO Bricks Module ID \n");
printk("### to your Persistent Bus Numbering Config File\n");
} else
return;
index = new_entry;
temp = &ioconfig_bus_table[index];
while (index < free_entry) {
printk("%s\n", temp);
temp++;
index++;
}
printk("### End\n");
}
static int ioconfig_bus_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct ioconfig_parm parm;
/*
* Copy in the parameters.
*/
copy_from_user(&parm, (char *)arg, sizeof(struct ioconfig_parm));
parm.number = free_entry - new_entry;
parm.ioconfig_activated = ioconfig_activated;
copy_to_user((char *)arg, &parm, sizeof(struct ioconfig_parm));
copy_to_user((char *)parm.buffer, &ioconfig_bus_table[new_entry], sizeof(struct ascii_moduleid) * (free_entry - new_entry));
return 0;
}
/*
* ioconfig_bus_open - Opens the special device node "/dev/hw/.ioconfig_bus".
*/
static int ioconfig_bus_open(struct inode * inode, struct file * filp)
{
if (ioconfig_bus_debug) {
DBG("ioconfig_bus_open called.\n");
}
return(0);
}
/*
* ioconfig_bus_close - Closes the special device node "/dev/hw/.ioconfig_bus".
*/
static int ioconfig_bus_close(struct inode * inode, struct file * filp)
{
if (ioconfig_bus_debug) {
DBG("ioconfig_bus_close called.\n");
}
return(0);
}
struct file_operations ioconfig_bus_fops = {
ioctl:ioconfig_bus_ioctl,
open:ioconfig_bus_open, /* open */
release:ioconfig_bus_close /* release */
};
/*
* init_ifconfig_bus() - Boot time initialization. Ensure that it is called
* after devfs has been initialized.
*
*/
int init_ioconfig_bus(void)
{
ioconfig_bus_handle = NULL;
ioconfig_bus_handle = hwgraph_register(hwgraph_root, ".ioconfig_bus",
0, DEVFS_FL_AUTO_DEVNUM,
0, 0,
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
&ioconfig_bus_fops, NULL);
if (ioconfig_bus_handle == NULL) {
panic("Unable to create SGI PERSISTENT BUS NUMBERING Driver.\n");
}
return(0);
}
static int __init ioconfig_bus_setup (char *str)
{
char *temp;
DBG("ioconfig_bus_setup: Kernel Options %s\n", str);
temp = (char *)ioconfig_kernopts;
memset(temp, 0, 128);
while ( (*str != '\0') && !isspace (*str) ) {
if (*str == ',') {
*temp = '\n';
temp++;
str++;
continue;
}
*temp = *str;
temp++;
str++;
}
return(0);
}
__setup("ioconfig=", ioconfig_bus_setup);
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
#
# Makefile for the sn2 specific io routines.
EXTRA_CFLAGS := -DLITTLE_ENDIAN
obj-y += bte_error.o geo_op.o klconflib.o klgraph.o l1.o \
l1_command.o ml_iograph.o ml_SN_init.o ml_SN_intr.o module.o \
pci_bus_cvlink.o pciio.o pic.o sgi_io_init.o shub.o shuberror.o \
shub_intr.o shubio.o xbow.o xtalk.o
obj-$(CONFIG_KDB) += kdba_io.o
obj-$(CONFIG_SHUB_1_0_SPECIFIC) += efi-rtc.o
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
/*
* @doc file m:hwcfg
* DESCRIPTION:
*
* This file contains routines for manipulating and generating
* Geographic IDs. They are in a file by themself since they have
* no dependencies on other modules.
*
* ORIGIN:
*
* New for SN2
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/hw_irq.h>
#include <asm/sn/types.h>
#include <asm/sn/sgi.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn2/shub_mmr_t.h>
#include <asm/sn/sn2/shubio.h>
#include <asm/sal.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/module.h>
#include <asm/sn/geo.h>
/********** Global functions and data (visible outside the module) ***********/
/*
* @doc gf:geo_module
*
* moduleid_t geo_module(geoid_t g)
*
* DESCRIPTION:
*
* Return the moduleid component of a geoid.
*
* INTERNALS:
*
* Return INVALID_MODULE for an invalid geoid. Otherwise extract the
* moduleid from the structure, and return it.
*
* ORIGIN:
*
* New for SN2
*/
moduleid_t
geo_module(geoid_t g)
{
if (g.any.type == GEO_TYPE_INVALID)
return INVALID_MODULE;
else
return g.any.module;
}
/*
* @doc gf:geo_slab
*
* slabid_t geo_slab(geoid_t g)
*
* DESCRIPTION:
*
* Return the slabid component of a geoid.
*
* INTERNALS:
*
* Return INVALID_SLAB for an invalid geoid. Otherwise extract the
* slabid from the structure, and return it.
*
* ORIGIN:
*
* New for SN2
*/
slabid_t
geo_slab(geoid_t g)
{
if (g.any.type == GEO_TYPE_INVALID)
return INVALID_SLAB;
else
return g.any.slab;
}
/*
* @doc gf:geo_type
*
* geo_type_t geo_type(geoid_t g)
*
* DESCRIPTION:
*
* Return the type component of a geoid.
*
* INTERNALS:
*
* Extract the type from the structure, and return it.
*
* ORIGIN:
*
* New for SN2
*/
geo_type_t
geo_type(geoid_t g)
{
return g.any.type;
}
/*
* @doc gf:geo_valid
*
* int geo_valid(geoid_t g)
*
* DESCRIPTION:
*
* Return nonzero if g has a valid geoid type.
*
* INTERNALS:
*
* Test the type against GEO_TYPE_INVALID, and return the result.
*
* ORIGIN:
*
* New for SN2
*/
int
geo_valid(geoid_t g)
{
return g.any.type != GEO_TYPE_INVALID;
}
/*
* @doc gf:geo_cmp
*
* int geo_cmp(geoid_t g0, geoid_t g1)
*
* DESCRIPTION:
*
* Compare two geoid_t values, from the coarsest field to the finest.
* The comparison should be consistent with the physical locations of
* of the hardware named by the geoids.
*
* INTERNALS:
*
* First compare the module, then the slab, type, and type-specific fields.
*
* ORIGIN:
*
* New for SN2
*/
int
geo_cmp(geoid_t g0, geoid_t g1)
{
int rv;
/* Compare the common fields */
rv = MODULE_CMP(geo_module(g0), geo_module(g1));
if (rv != 0)
return rv;
rv = geo_slab(g0) - geo_slab(g1);
if (rv != 0)
return rv;
/* Within a slab, sort by type */
rv = geo_type(g0) - geo_type(g1);
if (rv != 0)
return rv;
switch(geo_type(g0)) {
case GEO_TYPE_CPU:
rv = g0.cpu.slice - g1.cpu.slice;
break;
case GEO_TYPE_IOCARD:
rv = g0.pcicard.bus - g1.pcicard.bus;
if (rv) break;
rv = SLOTNUM_GETSLOT(g0.pcicard.slot) -
SLOTNUM_GETSLOT(g1.pcicard.slot);
break;
case GEO_TYPE_MEM:
rv = g0.mem.membus - g1.mem.membus;
if (rv) break;
rv = g0.mem.memslot - g1.mem.memslot;
break;
default:
rv = 0;
}
return rv;
}
/*
* @doc gf:geo_new
*
* geoid_t geo_new(geo_type_t type, ...)
*
* DESCRIPTION:
*
* Generate a new geoid_t value of the given type from its components.
* Expected calling sequences:
* \@itemize \@bullet
* \@item
* \@code\{geo_new(GEO_TYPE_INVALID)\}
* \@item
* \@code\{geo_new(GEO_TYPE_MODULE, moduleid_t m)\}
* \@item
* \@code\{geo_new(GEO_TYPE_NODE, moduleid_t m, slabid_t s)\}
* \@item
* \@code\{geo_new(GEO_TYPE_RTR, moduleid_t m, slabid_t s)\}
* \@item
* \@code\{geo_new(GEO_TYPE_IOCNTL, moduleid_t m, slabid_t s)\}
* \@item
* \@code\{geo_new(GEO_TYPE_IOCARD, moduleid_t m, slabid_t s, char bus, slotid_t slot)\}
* \@item
* \@code\{geo_new(GEO_TYPE_CPU, moduleid_t m, slabid_t s, char slice)\}
* \@item
* \@code\{geo_new(GEO_TYPE_MEM, moduleid_t m, slabid_t s, char membus, char slot)\}
* \@end itemize
*
* Invalid types return a GEO_TYPE_INVALID geoid_t.
*
* INTERNALS:
*
* Use the type to determine which fields to expect. Write the fields into
* a new geoid_t and return it. Note: scalars smaller than an "int" are
* promoted to "int" by the "..." operator, so we need extra casts on "char",
* "slotid_t", and "slabid_t".
*
* ORIGIN:
*
* New for SN2
*/
geoid_t
geo_new(geo_type_t type, ...)
{
va_list al;
geoid_t g;
memset(&g, 0, sizeof(g));
va_start(al, type);
/* Make sure the type is sane */
if (type >= GEO_TYPE_MAX)
type = GEO_TYPE_INVALID;
g.any.type = type;
if (type == GEO_TYPE_INVALID)
goto done; /* invalid geoids have no components at all */
g.any.module = va_arg(al, moduleid_t);
if (type == GEO_TYPE_MODULE)
goto done;
g.any.slab = (slabid_t)va_arg(al, int);
/* Some types have additional components */
switch(type) {
case GEO_TYPE_CPU:
g.cpu.slice = (char)va_arg(al, int);
break;
case GEO_TYPE_IOCARD:
g.pcicard.bus = (char)va_arg(al, int);
g.pcicard.slot = (slotid_t)va_arg(al, int);
break;
case GEO_TYPE_MEM:
g.mem.membus = (char)va_arg(al, int);
g.mem.memslot = (char)va_arg(al, int);
break;
default:
break;
}
done:
va_end(al);
return g;
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/ctype.h>
#include <asm/sn/sgi.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/module.h>
#include <asm/sn/router.h>
#include <asm/sn/xtalk/xbow.h>
#define printf printk
int hasmetarouter;
#define LDEBUG 0
#define NIC_UNKNOWN ((nic_t) -1)
#undef DEBUG_KLGRAPH
#ifdef DEBUG_KLGRAPH
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif /* DEBUG_KLGRAPH */
static void sort_nic_names(lboard_t *) ;
u64 klgraph_addr[MAX_COMPACT_NODES];
int module_number = 0;
lboard_t *
find_lboard(lboard_t *start, unsigned char brd_type)
{
/* Search all boards stored on this node. */
while (start) {
if (start->brd_type == brd_type)
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
lboard_t *
find_lboard_class(lboard_t *start, unsigned char brd_type)
{
/* Search all boards stored on this node. */
while (start) {
if (KLCLASS(start->brd_type) == KLCLASS(brd_type))
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
klinfo_t *
find_component(lboard_t *brd, klinfo_t *kli, unsigned char struct_type)
{
int index, j;
if (kli == (klinfo_t *)NULL) {
index = 0;
} else {
for (j = 0; j < KLCF_NUM_COMPS(brd); j++) {
if (kli == KLCF_COMP(brd, j))
break;
}
index = j;
if (index == KLCF_NUM_COMPS(brd)) {
DBG("find_component: Bad pointer: 0x%p\n", kli);
return (klinfo_t *)NULL;
}
index++; /* next component */
}
for (; index < KLCF_NUM_COMPS(brd); index++) {
kli = KLCF_COMP(brd, index);
DBG("find_component: brd %p kli %p request type = 0x%x kli type 0x%x\n", brd, kli, kli->struct_type, KLCF_COMP_TYPE(kli));
if (KLCF_COMP_TYPE(kli) == struct_type)
return kli;
}
/* Didn't find it. */
return (klinfo_t *)NULL;
}
klinfo_t *
find_first_component(lboard_t *brd, unsigned char struct_type)
{
return find_component(brd, (klinfo_t *)NULL, struct_type);
}
lboard_t *
find_lboard_modslot(lboard_t *start, geoid_t geoid)
{
/* Search all boards stored on this node. */
while (start) {
if (geo_cmp(start->brd_geoid, geoid))
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
lboard_t *
find_lboard_module(lboard_t *start, geoid_t geoid)
{
/* Search all boards stored on this node. */
while (start) {
if (geo_cmp(start->brd_geoid, geoid))
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
lboard_t *
find_lboard_module_class(lboard_t *start, geoid_t geoid,
unsigned char brd_type)
{
while (start) {
DBG("find_lboard_module_class: lboard 0x%p, start->brd_geoid 0x%x, mod 0x%x, start->brd_type 0x%x, brd_type 0x%x\n", start, start->brd_geoid, geoid, start->brd_type, brd_type);
if (geo_cmp(start->brd_geoid, geoid) &&
(KLCLASS(start->brd_type) == KLCLASS(brd_type)))
return start;
start = KLCF_NEXT(start);
}
/* Didn't find it. */
return (lboard_t *)NULL;
}
/*
* Convert a NIC name to a name for use in the hardware graph.
*/
void
nic_name_convert(char *old_name, char *new_name)
{
int i;
char c;
char *compare_ptr;
if ((old_name[0] == '\0') || (old_name[1] == '\0')) {
strcpy(new_name, EDGE_LBL_XWIDGET);
} else {
for (i = 0; i < strlen(old_name); i++) {
c = old_name[i];
if (isalpha(c))
new_name[i] = tolower(c);
else if (isdigit(c))
new_name[i] = c;
else
new_name[i] = '_';
}
new_name[i] = '\0';
}
/* XXX -
* Since a bunch of boards made it out with weird names like
* IO6-fibbbed and IO6P2, we need to look for IO6 in a name and
* replace it with "baseio" to avoid confusion in the field.
* We also have to make sure we don't report media_io instead of
* baseio.
*/
/* Skip underscores at the beginning of the name */
for (compare_ptr = new_name; (*compare_ptr) == '_'; compare_ptr++)
;
/*
* Check for some names we need to replace. Early boards
* had junk following the name so check only the first
* characters.
*/
if (!strncmp(new_name, "io6", 3) ||
!strncmp(new_name, "mio", 3) ||
!strncmp(new_name, "media_io", 8))
strcpy(new_name, "baseio");
else if (!strncmp(new_name, "divo", 4))
strcpy(new_name, "divo") ;
}
/*
* Find the lboard structure and get the board name.
* If we can't find the structure or it's too low a revision,
* use default name.
*/
lboard_t *
get_board_name(nasid_t nasid, geoid_t geoid, slotid_t slot, char *name)
{
lboard_t *brd;
brd = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(nasid),
geoid);
#ifndef _STANDALONE
{
cnodeid_t cnode = NASID_TO_COMPACT_NODEID(nasid);
if (!brd && (NODEPDA(cnode)->xbow_peer != INVALID_NASID))
brd = find_lboard_modslot((lboard_t *)
KL_CONFIG_INFO(NODEPDA(cnode)->xbow_peer),
geoid);
}
#endif
if (!brd || (brd->brd_sversion < 2)) {
strcpy(name, EDGE_LBL_XWIDGET);
} else {
nic_name_convert(brd->brd_name, name);
}
/*
* PV # 540860
* If the name is not 'baseio'
* get the lowest of all the names in the nic string.
* This is needed for boards like divo, which can have
* a bunch of daughter cards, but would like to be called
* divo. We could do this for baseio
* but it has some special case names that we would not
* like to disturb at this point.
*/
/* gfx boards don't need any of this name scrambling */
if (brd && (KLCLASS(brd->brd_type) == KLCLASS_GFX)) {
return(brd);
}
if (!(!strcmp(name, "baseio") )) {
if (brd) {
sort_nic_names(brd) ;
/* Convert to small case, '-' to '_' etc */
nic_name_convert(brd->brd_name, name) ;
}
}
return(brd);
}
/*
* get_actual_nasid
*
* Completely disabled brds have their klconfig on
* some other nasid as they have no memory. But their
* actual nasid is hidden in the klconfig. Use this
* routine to get it. Works for normal boards too.
*/
nasid_t
get_actual_nasid(lboard_t *brd)
{
klhub_t *hub ;
if (!brd)
return INVALID_NASID ;
/* find out if we are a completely disabled brd. */
hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
if (!hub)
return INVALID_NASID ;
if (!(hub->hub_info.flags & KLINFO_ENABLE)) /* disabled node brd */
return hub->hub_info.physid ;
else
return brd->brd_nasid ;
}
int
xbow_port_io_enabled(nasid_t nasid, int link)
{
lboard_t *brd;
klxbow_t *xbow_p;
/*
* look for boards that might contain an xbow or xbridge
*/
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IOBRICK_XBOW);
if (brd == NULL) return 0;
if ((xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW))
== NULL)
return 0;
if (!XBOW_PORT_TYPE_IO(xbow_p, link) || !XBOW_PORT_IS_ENABLED(xbow_p, link))
return 0;
return 1;
}
void
board_to_path(lboard_t *brd, char *path)
{
moduleid_t modnum;
char *board_name;
char buffer[16];
ASSERT(brd);
switch (KLCLASS(brd->brd_type)) {
case KLCLASS_NODE:
board_name = EDGE_LBL_NODE;
break;
case KLCLASS_ROUTER:
if (brd->brd_type == KLTYPE_META_ROUTER) {
board_name = EDGE_LBL_META_ROUTER;
hasmetarouter++;
} else if (brd->brd_type == KLTYPE_REPEATER_ROUTER) {
board_name = EDGE_LBL_REPEATER_ROUTER;
hasmetarouter++;
} else
board_name = EDGE_LBL_ROUTER;
break;
case KLCLASS_MIDPLANE:
board_name = EDGE_LBL_MIDPLANE;
break;
case KLCLASS_IO:
board_name = EDGE_LBL_IO;
break;
case KLCLASS_IOBRICK:
if (brd->brd_type == KLTYPE_PBRICK)
board_name = EDGE_LBL_PBRICK;
else if (brd->brd_type == KLTYPE_IBRICK)
board_name = EDGE_LBL_IBRICK;
else if (brd->brd_type == KLTYPE_XBRICK)
board_name = EDGE_LBL_XBRICK;
else
board_name = EDGE_LBL_IOBRICK;
break;
default:
board_name = EDGE_LBL_UNKNOWN;
}
modnum = geo_module(brd->brd_geoid);
memset(buffer, 0, 16);
format_module_id(buffer, modnum, MODULE_FORMAT_BRIEF);
sprintf(path, EDGE_LBL_MODULE "/%s/" EDGE_LBL_SLAB "/%d/%s", buffer, geo_slab(brd->brd_geoid), board_name);
}
/*
* Get the module number for a NASID.
*/
moduleid_t
get_module_id(nasid_t nasid)
{
lboard_t *brd;
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
if (!brd)
return INVALID_MODULE;
else
return geo_module(brd->brd_geoid);
}
#define MHZ 1000000
/* Get the canonical hardware graph name for the given pci component
* on the given io board.
*/
void
device_component_canonical_name_get(lboard_t *brd,
klinfo_t *component,
char *name)
{
slotid_t slot;
char board_name[20];
ASSERT(brd);
/* Convert the [ CLASS | TYPE ] kind of slotid
* into a string
*/
slot = brd->brd_slot;
/* Get the io board name */
if (!brd || (brd->brd_sversion < 2)) {
strcpy(name, EDGE_LBL_XWIDGET);
} else {
nic_name_convert(brd->brd_name, board_name);
}
/* Give out the canonical name of the pci device*/
sprintf(name,
"/dev/hw/"EDGE_LBL_MODULE "/%x/"EDGE_LBL_SLAB"/%d/"
EDGE_LBL_SLOT"/%s/"EDGE_LBL_PCI"/%d",
geo_module(brd->brd_geoid), geo_slab(brd->brd_geoid),
board_name, KLCF_BRIDGE_W_ID(component));
}
/*
* Get the serial number of the main component of a board
* Returns 0 if a valid serial number is found
* 1 otherwise.
* Assumptions: Nic manufacturing string has the following format
* *Serial:<serial_number>;*
*/
static int
component_serial_number_get(lboard_t *board,
klconf_off_t mfg_nic_offset,
char *serial_number,
char *key_pattern)
{
char *mfg_nic_string;
char *serial_string,*str;
int i;
char *serial_pattern = "Serial:";
/* We have an error on a null mfg nic offset */
if (!mfg_nic_offset)
return(1);
/* Get the hub's manufacturing nic information
* which is in the form of a pre-formatted string
*/
mfg_nic_string =
(char *)NODE_OFFSET_TO_K0(NASID_GET(board),
mfg_nic_offset);
/* There is no manufacturing nic info */
if (!mfg_nic_string)
return(1);
str = mfg_nic_string;
/* Look for the key pattern first (if it is specified)
* and then print the serial number corresponding to that.
*/
if (strcmp(key_pattern,"") &&
!(str = strstr(mfg_nic_string,key_pattern)))
return(1);
/* There is no serial number info in the manufacturing
* nic info
*/
if (!(serial_string = strstr(str,serial_pattern)))
return(1);
serial_string = serial_string + strlen(serial_pattern);
/* Copy the serial number information from the klconfig */
i = 0;
while (serial_string[i] != ';') {
serial_number[i] = serial_string[i];
i++;
}
serial_number[i] = 0;
return(0);
}
/*
* Get the serial number of a board
* Returns 0 if a valid serial number is found
* 1 otherwise.
*/
int
board_serial_number_get(lboard_t *board,char *serial_number)
{
ASSERT(board && serial_number);
if (!board || !serial_number)
return(1);
strcpy(serial_number,"");
switch(KLCLASS(board->brd_type)) {
case KLCLASS_CPU: { /* Node board */
klhub_t *hub;
/* Get the hub component information */
hub = (klhub_t *)find_first_component(board,
KLSTRUCT_HUB);
/* If we don't have a hub component on an IP27
* then we have a weird klconfig.
*/
if (!hub)
return(1);
/* Get the serial number information from
* the hub's manufacturing nic info
*/
if (component_serial_number_get(board,
hub->hub_mfg_nic,
serial_number,
"IP37"))
return(1);
break;
}
case KLCLASS_IO: { /* IO board */
if (KLTYPE(board->brd_type) == KLTYPE_TPU) {
/* Special case for TPU boards */
kltpu_t *tpu;
/* Get the tpu component information */
tpu = (kltpu_t *)find_first_component(board,
KLSTRUCT_TPU);
/* If we don't have a tpu component on a tpu board
* then we have a weird klconfig.
*/
if (!tpu)
return(1);
/* Get the serial number information from
* the tpu's manufacturing nic info
*/
if (component_serial_number_get(board,
tpu->tpu_mfg_nic,
serial_number,
""))
return(1);
break;
} else if ((KLTYPE(board->brd_type) == KLTYPE_GSN_A) ||
(KLTYPE(board->brd_type) == KLTYPE_GSN_B)) {
/* Special case for GSN boards */
klgsn_t *gsn;
/* Get the gsn component information */
gsn = (klgsn_t *)find_first_component(board,
((KLTYPE(board->brd_type) == KLTYPE_GSN_A) ?
KLSTRUCT_GSN_A : KLSTRUCT_GSN_B));
/* If we don't have a gsn component on a gsn board
* then we have a weird klconfig.
*/
if (!gsn)
return(1);
/* Get the serial number information from
* the gsn's manufacturing nic info
*/
if (component_serial_number_get(board,
gsn->gsn_mfg_nic,
serial_number,
""))
return(1);
break;
} else {
klbri_t *bridge;
/* Get the bridge component information */
bridge = (klbri_t *)find_first_component(board,
KLSTRUCT_BRI);
/* If we don't have a bridge component on an IO board
* then we have a weird klconfig.
*/
if (!bridge)
return(1);
/* Get the serial number information from
* the bridge's manufacturing nic info
*/
if (component_serial_number_get(board,
bridge->bri_mfg_nic,
serial_number,
""))
return(1);
break;
}
}
case KLCLASS_ROUTER: { /* Router board */
klrou_t *router;
/* Get the router component information */
router = (klrou_t *)find_first_component(board,
KLSTRUCT_ROU);
/* If we don't have a router component on a router board
* then we have a weird klconfig.
*/
if (!router)
return(1);
/* Get the serial number information from
* the router's manufacturing nic info
*/
if (component_serial_number_get(board,
router->rou_mfg_nic,
serial_number,
""))
return(1);
break;
}
case KLCLASS_GFX: { /* Gfx board */
klgfx_t *graphics;
/* Get the graphics component information */
graphics = (klgfx_t *)find_first_component(board, KLSTRUCT_GFX);
/* If we don't have a gfx component on a gfx board
* then we have a weird klconfig.
*/
if (!graphics)
return(1);
/* Get the serial number information from
* the graphics's manufacturing nic info
*/
if (component_serial_number_get(board,
graphics->gfx_mfg_nic,
serial_number,
""))
return(1);
break;
}
default:
strcpy(serial_number,"");
break;
}
return(0);
}
#include "asm/sn/sn_private.h"
xwidgetnum_t
nodevertex_widgetnum_get(devfs_handle_t node_vtx)
{
hubinfo_t hubinfo_p;
hwgraph_info_get_LBL(node_vtx, INFO_LBL_NODE_INFO,
(arbitrary_info_t *) &hubinfo_p);
return(hubinfo_p->h_widgetid);
}
devfs_handle_t
nodevertex_xbow_peer_get(devfs_handle_t node_vtx)
{
hubinfo_t hubinfo_p;
nasid_t xbow_peer_nasid;
cnodeid_t xbow_peer;
hwgraph_info_get_LBL(node_vtx, INFO_LBL_NODE_INFO,
(arbitrary_info_t *) &hubinfo_p);
xbow_peer_nasid = hubinfo_p->h_nodepda->xbow_peer;
if(xbow_peer_nasid == INVALID_NASID)
return ( (devfs_handle_t)-1);
xbow_peer = NASID_TO_COMPACT_NODEID(xbow_peer_nasid);
return(NODEPDA(xbow_peer)->node_vertex);
}
/* NIC Sorting Support */
#define MAX_NICS_PER_STRING 32
#define MAX_NIC_NAME_LEN 32
static char *
get_nic_string(lboard_t *lb)
{
int i;
klinfo_t *k = NULL ;
klconf_off_t mfg_off = 0 ;
char *mfg_nic = NULL ;
for (i = 0; i < KLCF_NUM_COMPS(lb); i++) {
k = KLCF_COMP(lb, i) ;
switch(k->struct_type) {
case KLSTRUCT_BRI:
mfg_off = ((klbri_t *)k)->bri_mfg_nic ;
break ;
case KLSTRUCT_HUB:
mfg_off = ((klhub_t *)k)->hub_mfg_nic ;
break ;
case KLSTRUCT_ROU:
mfg_off = ((klrou_t *)k)->rou_mfg_nic ;
break ;
case KLSTRUCT_GFX:
mfg_off = ((klgfx_t *)k)->gfx_mfg_nic ;
break ;
case KLSTRUCT_TPU:
mfg_off = ((kltpu_t *)k)->tpu_mfg_nic ;
break ;
case KLSTRUCT_GSN_A:
case KLSTRUCT_GSN_B:
mfg_off = ((klgsn_t *)k)->gsn_mfg_nic ;
break ;
case KLSTRUCT_XTHD:
mfg_off = ((klxthd_t *)k)->xthd_mfg_nic ;
break;
default:
mfg_off = 0 ;
break ;
}
if (mfg_off)
break ;
}
if ((mfg_off) && (k))
mfg_nic = (char *)NODE_OFFSET_TO_K0(k->nasid, mfg_off) ;
return mfg_nic ;
}
char *
get_first_string(char **ptrs, int n)
{
int i ;
char *tmpptr ;
if ((ptrs == NULL) || (n == 0))
return NULL ;
tmpptr = ptrs[0] ;
if (n == 1)
return tmpptr ;
for (i = 0 ; i < n ; i++) {
if (strcmp(tmpptr, ptrs[i]) > 0)
tmpptr = ptrs[i] ;
}
return tmpptr ;
}
int
get_ptrs(char *idata, char **ptrs, int n, char *label)
{
int i = 0 ;
char *tmp = idata ;
if ((ptrs == NULL) || (idata == NULL) || (label == NULL) || (n == 0))
return 0 ;
while ( (tmp = strstr(tmp, label)) ){
tmp += strlen(label) ;
/* check for empty name field, and last NULL ptr */
if ((i < (n-1)) && (*tmp != ';')) {
ptrs[i++] = tmp ;
}
}
ptrs[i] = NULL ;
return i ;
}
/*
* sort_nic_names
*
* Does not really do sorting. Find the alphabetically lowest
* name among all the nic names found in a nic string.
*
* Return:
* Nothing
*
* Side Effects:
*
* lb->brd_name gets the new name found
*/
static void
sort_nic_names(lboard_t *lb)
{
char *nic_str ;
char *ptrs[MAX_NICS_PER_STRING] ;
char name[MAX_NIC_NAME_LEN] ;
char *tmp, *tmp1 ;
*name = 0 ;
/* Get the nic pointer from the lb */
if ((nic_str = get_nic_string(lb)) == NULL)
return ;
tmp = get_first_string(ptrs,
get_ptrs(nic_str, ptrs, MAX_NICS_PER_STRING, "Name:")) ;
if (tmp == NULL)
return ;
if ( (tmp1 = strchr(tmp, ';')) ){
strncpy(name, tmp, tmp1-tmp) ;
name[tmp1-tmp] = 0 ;
} else {
strncpy(name, tmp, (sizeof(name) -1)) ;
name[sizeof(name)-1] = 0 ;
}
strcpy(lb->brd_name, name) ;
}
char brick_types[MAX_BRICK_TYPES + 1] = "crikxdpn%#012345";
/*
* Format a module id for printing.
*/
void
format_module_id(char *buffer, moduleid_t m, int fmt)
{
int rack, position;
char brickchar;
rack = MODULE_GET_RACK(m);
ASSERT(MODULE_GET_BTYPE(m) < MAX_BRICK_TYPES);
brickchar = MODULE_GET_BTCHAR(m);
position = MODULE_GET_BPOS(m);
if (fmt == MODULE_FORMAT_BRIEF) {
/* Brief module number format, eg. 002c15 */
/* Decompress the rack number */
*buffer++ = '0' + RACK_GET_CLASS(rack);
*buffer++ = '0' + RACK_GET_GROUP(rack);
*buffer++ = '0' + RACK_GET_NUM(rack);
/* Add the brick type */
*buffer++ = brickchar;
}
else if (fmt == MODULE_FORMAT_LONG) {
/* Fuller hwgraph format, eg. rack/002/bay/15 */
strcpy(buffer, EDGE_LBL_RACK "/"); buffer += strlen(buffer);
*buffer++ = '0' + RACK_GET_CLASS(rack);
*buffer++ = '0' + RACK_GET_GROUP(rack);
*buffer++ = '0' + RACK_GET_NUM(rack);
strcpy(buffer, "/" EDGE_LBL_RPOS "/"); buffer += strlen(buffer);
}
/* Add the bay position, using at least two digits */
if (position < 10)
*buffer++ = '0';
sprintf(buffer, "%d", position);
}
/*
* Parse a module id, in either brief or long form.
* Returns < 0 on error.
* The long form does not include a brick type, so it defaults to 0 (CBrick)
*/
int
parse_module_id(char *buffer)
{
unsigned int v, rack, bay, type, form;
moduleid_t m;
char c;
if (strstr(buffer, EDGE_LBL_RACK "/") == buffer) {
form = MODULE_FORMAT_LONG;
buffer += strlen(EDGE_LBL_RACK "/");
/* A long module ID must be exactly 5 non-template chars. */
if (strlen(buffer) != strlen("/" EDGE_LBL_RPOS "/") + 5)
return -1;
}
else {
form = MODULE_FORMAT_BRIEF;
/* A brief module id must be exactly 6 characters */
if (strlen(buffer) != 6)
return -2;
}
/* The rack number must be exactly 3 digits */
if (!(isdigit(buffer[0]) && isdigit(buffer[1]) && isdigit(buffer[2])))
return -3;
rack = 0;
v = *buffer++ - '0';
if (v > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
return -4;
RACK_ADD_CLASS(rack, v);
v = *buffer++ - '0';
if (v > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
return -5;
RACK_ADD_GROUP(rack, v);
v = *buffer++ - '0';
/* rack numbers are 1-based */
if (v-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
return -6;
RACK_ADD_NUM(rack, v);
if (form == MODULE_FORMAT_BRIEF) {
/* Next should be a module type character. Accept ucase or lcase. */
c = *buffer++;
if (!isalpha(c))
return -7;
/* strchr() returns a pointer into brick_types[], or NULL */
type = (unsigned int)(strchr(brick_types, tolower(c)) - brick_types);
if (type > MODULE_BTYPE_MASK >> MODULE_BTYPE_SHFT)
return -8;
}
else {
/* Hardcode the module type, and skip over the boilerplate */
type = MODULE_CBRICK;
if (strstr(buffer, "/" EDGE_LBL_RPOS "/") != buffer)
return -9;
buffer += strlen("/" EDGE_LBL_RPOS "/");
}
/* The bay number is last. Make sure it's exactly two digits */
if (!(isdigit(buffer[0]) && isdigit(buffer[1]) && !buffer[2]))
return -10;
bay = 10 * (buffer[0] - '0') + (buffer[1] - '0');
if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
return -11;
m = RBT_TO_MODULE(rack, bay, type);
/* avoid sign extending the moduleid_t */
return (int)(unsigned short)m;
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
/*
* klgraph.c-
* This file specifies the interface between the kernel and the PROM's
* configuration data structures.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/kldir.h>
#include <asm/sn/gda.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/router.h>
#include <asm/sn/xtalk/xbow.h>
#include <asm/sn/hcl_util.h>
// #define KLGRAPH_DEBUG 1
#ifdef KLGRAPH_DEBUG
#define GRPRINTF(x) printk x
#define CE_GRPANIC CE_PANIC
#else
#define GRPRINTF(x)
#define CE_GRPANIC CE_PANIC
#endif
#include <asm/sn/sn_private.h>
extern char arg_maxnodes[];
extern u64 klgraph_addr[];
void mark_cpuvertex_as_cpu(devfs_handle_t vhdl, cpuid_t cpuid);
/*
* Support for verbose inventory via hardware graph.
* klhwg_invent_alloc allocates the necessary size of inventory information
* and fills in the generic information.
*/
invent_generic_t *
klhwg_invent_alloc(cnodeid_t cnode, int class, int size)
{
invent_generic_t *invent;
invent = kern_malloc(size);
if (!invent) return NULL;
invent->ig_module = NODE_MODULEID(cnode);
invent->ig_slot = SLOTNUM_GETSLOT(NODE_SLOTID(cnode));
invent->ig_invclass = class;
return invent;
}
/*
* Add detailed disabled cpu inventory info to the hardware graph.
*/
void
klhwg_disabled_cpu_invent_info(devfs_handle_t cpuv,
cnodeid_t cnode,
klcpu_t *cpu, slotid_t slot)
{
invent_cpuinfo_t *cpu_invent;
diag_inv_t *diag_invent;
cpu_invent = (invent_cpuinfo_t *)
klhwg_invent_alloc(cnode, INV_PROCESSOR, sizeof(invent_cpuinfo_t));
if (!cpu_invent)
return;
/* Diag information on this processor */
diag_invent = (diag_inv_t *)
klhwg_invent_alloc(cnode, INV_CPUDIAGVAL, sizeof(diag_inv_t));
if (!diag_invent)
return;
/* Disabled CPU */
cpu_invent->ic_gen.ig_flag = 0x0;
cpu_invent->ic_gen.ig_slot = slot;
cpu_invent->ic_cpu_info.cpuflavor = cpu->cpu_prid;
cpu_invent->ic_cpu_info.cpufq = cpu->cpu_speed;
cpu_invent->ic_cpu_info.sdfreq = cpu->cpu_scachespeed;
cpu_invent->ic_cpu_info.sdsize = cpu->cpu_scachesz;
cpu_invent->ic_cpuid = cpu->cpu_info.virtid;
cpu_invent->ic_slice = cpu->cpu_info.physid;
/* Disabled CPU label */
hwgraph_info_add_LBL(cpuv, INFO_LBL_DETAIL_INVENT,
(arbitrary_info_t) cpu_invent);
hwgraph_info_export_LBL(cpuv, INFO_LBL_DETAIL_INVENT,
sizeof(invent_cpuinfo_t));
/* Diagval label - stores reason for disable +{virt,phys}id +diagval*/
hwgraph_info_add_LBL(cpuv, INFO_LBL_DIAGVAL,
(arbitrary_info_t) diag_invent);
hwgraph_info_export_LBL(cpuv, INFO_LBL_DIAGVAL,
sizeof(diag_inv_t));
}
/*
* Add detailed cpu inventory info to the hardware graph.
*/
void
klhwg_cpu_invent_info(devfs_handle_t cpuv,
cnodeid_t cnode,
klcpu_t *cpu)
{
invent_cpuinfo_t *cpu_invent;
cpu_invent = (invent_cpuinfo_t *)
klhwg_invent_alloc(cnode, INV_PROCESSOR, sizeof(invent_cpuinfo_t));
if (!cpu_invent)
return;
if (KLCONFIG_INFO_ENABLED((klinfo_t *)cpu))
cpu_invent->ic_gen.ig_flag = INVENT_ENABLED;
else
cpu_invent->ic_gen.ig_flag = 0x0;
cpu_invent->ic_cpu_info.cpuflavor = cpu->cpu_prid;
cpu_invent->ic_cpu_info.cpufq = cpu->cpu_speed;
cpu_invent->ic_cpu_info.sdfreq = cpu->cpu_scachespeed;
cpu_invent->ic_cpu_info.sdsize = cpu->cpu_scachesz;
cpu_invent->ic_cpuid = cpu->cpu_info.virtid;
cpu_invent->ic_slice = cpu_physical_id_to_slice(cpu->cpu_info.virtid);
hwgraph_info_add_LBL(cpuv, INFO_LBL_DETAIL_INVENT,
(arbitrary_info_t) cpu_invent);
hwgraph_info_export_LBL(cpuv, INFO_LBL_DETAIL_INVENT,
sizeof(invent_cpuinfo_t));
}
/*
* Add information about the baseio prom version number
* as a part of detailed inventory info in the hwgraph.
*/
void
klhwg_baseio_inventory_add(devfs_handle_t baseio_vhdl,cnodeid_t cnode)
{
invent_miscinfo_t *baseio_inventory;
unsigned char version = 0,revision = 0;
/* Allocate memory for the "detailed inventory" info
* for the baseio
*/
baseio_inventory = (invent_miscinfo_t *)
klhwg_invent_alloc(cnode, INV_PROM, sizeof(invent_miscinfo_t));
baseio_inventory->im_type = INV_IO6PROM;
/* Store the revision info in the inventory */
baseio_inventory->im_version = version;
baseio_inventory->im_rev = revision;
/* Put the inventory info in the hardware graph */
hwgraph_info_add_LBL(baseio_vhdl, INFO_LBL_DETAIL_INVENT,
(arbitrary_info_t) baseio_inventory);
/* Make the information available to the user programs
* thru hwgfs.
*/
hwgraph_info_export_LBL(baseio_vhdl, INFO_LBL_DETAIL_INVENT,
sizeof(invent_miscinfo_t));
}
char *hub_rev[] = {
"0.0",
"1.0",
"2.0",
"2.1",
"2.2",
"2.3"
};
/*
* Add detailed cpu inventory info to the hardware graph.
*/
void
klhwg_hub_invent_info(devfs_handle_t hubv,
cnodeid_t cnode,
klhub_t *hub)
{
invent_miscinfo_t *hub_invent;
hub_invent = (invent_miscinfo_t *)
klhwg_invent_alloc(cnode, INV_MISC, sizeof(invent_miscinfo_t));
if (!hub_invent)
return;
if (KLCONFIG_INFO_ENABLED((klinfo_t *)hub))
hub_invent->im_gen.ig_flag = INVENT_ENABLED;
hub_invent->im_type = INV_HUB;
hub_invent->im_rev = hub->hub_info.revision;
hub_invent->im_speed = hub->hub_speed;
hwgraph_info_add_LBL(hubv, INFO_LBL_DETAIL_INVENT,
(arbitrary_info_t) hub_invent);
hwgraph_info_export_LBL(hubv, INFO_LBL_DETAIL_INVENT,
sizeof(invent_miscinfo_t));
}
/* ARGSUSED */
void
klhwg_add_hub(devfs_handle_t node_vertex, klhub_t *hub, cnodeid_t cnode)
{
devfs_handle_t myhubv;
devfs_handle_t hub_mon;
int rc;
extern struct file_operations shub_mon_fops;
GRPRINTF(("klhwg_add_hub: adding %s\n", EDGE_LBL_HUB));
(void) hwgraph_path_add(node_vertex, EDGE_LBL_HUB, &myhubv);
rc = device_master_set(myhubv, node_vertex);
hub_mon = hwgraph_register(myhubv, EDGE_LBL_PERFMON,
0, DEVFS_FL_AUTO_DEVNUM,
0, 0,
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
&shub_mon_fops, (void *)(long)cnode);
}
/* ARGSUSED */
void
klhwg_add_disabled_cpu(devfs_handle_t node_vertex, cnodeid_t cnode, klcpu_t *cpu, slotid_t slot)
{
devfs_handle_t my_cpu;
char name[120];
cpuid_t cpu_id;
nasid_t nasid;
nasid = COMPACT_TO_NASID_NODEID(cnode);
cpu_id = nasid_slice_to_cpuid(nasid, cpu->cpu_info.physid);
if(cpu_id != -1){
sprintf(name, "%s/%s/%c", EDGE_LBL_DISABLED, EDGE_LBL_CPU, 'a' + cpu->cpu_info.physid);
(void) hwgraph_path_add(node_vertex, name, &my_cpu);
mark_cpuvertex_as_cpu(my_cpu, cpu_id);
device_master_set(my_cpu, node_vertex);
klhwg_disabled_cpu_invent_info(my_cpu, cnode, cpu, slot);
return;
}
}
/* ARGSUSED */
void
klhwg_add_cpu(devfs_handle_t node_vertex, cnodeid_t cnode, klcpu_t *cpu)
{
devfs_handle_t my_cpu, cpu_dir;
char name[120];
cpuid_t cpu_id;
nasid_t nasid;
nasid = COMPACT_TO_NASID_NODEID(cnode);
cpu_id = nasid_slice_to_cpuid(nasid, cpu->cpu_info.physid);
sprintf(name, "%s/%d/%c",
EDGE_LBL_CPUBUS,
0,
'a' + cpu->cpu_info.physid);
GRPRINTF(("klhwg_add_cpu: adding %s to vertex 0x%p\n", name, node_vertex));
(void) hwgraph_path_add(node_vertex, name, &my_cpu);
mark_cpuvertex_as_cpu(my_cpu, cpu_id);
device_master_set(my_cpu, node_vertex);
/* Add an alias under the node's CPU directory */
if (hwgraph_edge_get(node_vertex, EDGE_LBL_CPU, &cpu_dir) == GRAPH_SUCCESS) {
sprintf(name, "%c", 'a' + cpu->cpu_info.physid);
(void) hwgraph_edge_add(cpu_dir, my_cpu, name);
}
klhwg_cpu_invent_info(my_cpu, cnode, cpu);
}
void
klhwg_add_xbow(cnodeid_t cnode, nasid_t nasid)
{
lboard_t *brd;
klxbow_t *xbow_p;
nasid_t hub_nasid;
cnodeid_t hub_cnode;
int widgetnum;
devfs_handle_t xbow_v, hubv;
/*REFERENCED*/
graph_error_t err;
if ((brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IOBRICK_XBOW)) == NULL)
return;
if (KL_CONFIG_DUPLICATE_BOARD(brd))
return;
GRPRINTF(("klhwg_add_xbow: adding cnode %d nasid %d xbow edges\n",
cnode, nasid));
if ((xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW))
== NULL)
return;
for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
if (!XBOW_PORT_TYPE_HUB(xbow_p, widgetnum))
continue;
hub_nasid = XBOW_PORT_NASID(xbow_p, widgetnum);
if (hub_nasid == INVALID_NASID) {
printk(KERN_WARNING "hub widget %d, skipping xbow graph\n", widgetnum);
continue;
}
hub_cnode = NASID_TO_COMPACT_NODEID(hub_nasid);
if (is_specified(arg_maxnodes) && hub_cnode == INVALID_CNODEID) {
continue;
}
hubv = cnodeid_to_vertex(hub_cnode);
err = hwgraph_path_add(hubv, EDGE_LBL_XTALK, &xbow_v);
if (err != GRAPH_SUCCESS) {
if (err == GRAPH_DUP)
printk(KERN_WARNING "klhwg_add_xbow: Check for "
"working routers and router links!");
PRINT_PANIC("klhwg_add_xbow: Failed to add "
"edge: vertex 0x%p to vertex 0x%p,"
"error %d\n",
(void *)hubv, (void *)xbow_v, err);
}
xswitch_vertex_init(xbow_v);
NODEPDA(hub_cnode)->xbow_vhdl = xbow_v;
/*
* XXX - This won't work is we ever hook up two hubs
* by crosstown through a crossbow.
*/
if (hub_nasid != nasid) {
NODEPDA(hub_cnode)->xbow_peer = nasid;
NODEPDA(NASID_TO_COMPACT_NODEID(nasid))->xbow_peer =
hub_nasid;
}
GRPRINTF(("klhwg_add_xbow: adding port nasid %d %s to vertex 0x%p\n",
hub_nasid, EDGE_LBL_XTALK, hubv));
}
}
/* ARGSUSED */
void
klhwg_add_node(devfs_handle_t hwgraph_root, cnodeid_t cnode, gda_t *gdap)
{
nasid_t nasid;
lboard_t *brd;
klhub_t *hub;
devfs_handle_t node_vertex = NULL;
char path_buffer[100];
int rv;
char *s;
int board_disabled = 0;
klcpu_t *cpu;
nasid = COMPACT_TO_NASID_NODEID(cnode);
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
GRPRINTF(("klhwg_add_node: Adding cnode %d, nasid %d, brd 0x%p\n",
cnode, nasid, brd));
ASSERT(brd);
do {
devfs_handle_t cpu_dir;
/* Generate a hardware graph path for this board. */
board_to_path(brd, path_buffer);
GRPRINTF(("klhwg_add_node: adding %s to vertex 0x%p\n",
path_buffer, hwgraph_root));
rv = hwgraph_path_add(hwgraph_root, path_buffer, &node_vertex);
if (rv != GRAPH_SUCCESS)
PRINT_PANIC("Node vertex creation failed. "
"Path == %s",
path_buffer);
hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
ASSERT(hub);
if(hub->hub_info.flags & KLINFO_ENABLE)
board_disabled = 0;
else
board_disabled = 1;
if(!board_disabled) {
mark_nodevertex_as_node(node_vertex,
cnode + board_disabled * numnodes);
s = dev_to_name(node_vertex, path_buffer, sizeof(path_buffer));
NODEPDA(cnode)->hwg_node_name =
kmalloc(strlen(s) + 1,
GFP_KERNEL);
ASSERT_ALWAYS(NODEPDA(cnode)->hwg_node_name != NULL);
strcpy(NODEPDA(cnode)->hwg_node_name, s);
hubinfo_set(node_vertex, NODEPDA(cnode)->pdinfo);
/* Set up node board's slot */
NODEPDA(cnode)->slotdesc = brd->brd_slot;
/* Set up the module we're in */
NODEPDA(cnode)->geoid = brd->brd_geoid;
NODEPDA(cnode)->module = module_lookup(geo_module(brd->brd_geoid));
}
/* Get the first CPU structure */
cpu = (klcpu_t *)find_first_component(brd, KLSTRUCT_CPU);
/*
* If there's at least 1 CPU, add a "cpu" directory to represent
* the collection of all CPUs attached to this node.
*/
if (cpu) {
graph_error_t rv;
rv = hwgraph_path_add(node_vertex, EDGE_LBL_CPU, &cpu_dir);
if (rv != GRAPH_SUCCESS)
panic("klhwg_add_node: Cannot create CPU directory\n");
}
/* Add each CPU */
while (cpu) {
cpuid_t cpu_id;
cpu_id = nasid_slice_to_cpuid(nasid,cpu->cpu_info.physid);
if (cpu_enabled(cpu_id))
klhwg_add_cpu(node_vertex, cnode, cpu);
else
klhwg_add_disabled_cpu(node_vertex, cnode, cpu, brd->brd_slot);
cpu = (klcpu_t *)
find_component(brd, (klinfo_t *)cpu, KLSTRUCT_CPU);
} /* while */
if(!board_disabled)
klhwg_add_hub(node_vertex, hub, cnode);
brd = KLCF_NEXT(brd);
if (brd)
brd = find_lboard(brd, KLTYPE_SNIA);
else
break;
} while(brd);
}
/* ARGSUSED */
void
klhwg_add_all_routers(devfs_handle_t hwgraph_root)
{
nasid_t nasid;
cnodeid_t cnode;
lboard_t *brd;
devfs_handle_t node_vertex;
char path_buffer[100];
int rv;
for (cnode = 0; cnode < numnodes; cnode++) {
nasid = COMPACT_TO_NASID_NODEID(cnode);
GRPRINTF(("klhwg_add_all_routers: adding router on cnode %d\n",
cnode));
brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
KLTYPE_ROUTER);
if (!brd)
/* No routers stored in this node's memory */
continue;
do {
ASSERT(brd);
GRPRINTF(("Router board struct is %p\n", brd));
/* Don't add duplicate boards. */
if (brd->brd_flags & DUPLICATE_BOARD)
continue;
GRPRINTF(("Router 0x%p module number is %d\n", brd, brd->brd_geoid));
/* Generate a hardware graph path for this board. */
board_to_path(brd, path_buffer);
GRPRINTF(("Router path is %s\n", path_buffer));
/* Add the router */
GRPRINTF(("klhwg_add_all_routers: adding %s to vertex 0x%p\n",
path_buffer, hwgraph_root));
rv = hwgraph_path_add(hwgraph_root, path_buffer, &node_vertex);
if (rv != GRAPH_SUCCESS)
PRINT_PANIC("Router vertex creation "
"failed. Path == %s",
path_buffer);
GRPRINTF(("klhwg_add_all_routers: get next board from 0x%p\n",
brd));
/* Find the rest of the routers stored on this node. */
} while ( (brd = find_lboard_class(KLCF_NEXT(brd),
KLTYPE_ROUTER)) );
GRPRINTF(("klhwg_add_all_routers: Done.\n"));
}
}
/* ARGSUSED */
void
klhwg_connect_one_router(devfs_handle_t hwgraph_root, lboard_t *brd,
cnodeid_t cnode, nasid_t nasid)
{
klrou_t *router;
char path_buffer[50];
char dest_path[50];
devfs_handle_t router_hndl;
devfs_handle_t dest_hndl;
int rc;
int port;
lboard_t *dest_brd;
GRPRINTF(("klhwg_connect_one_router: Connecting router on cnode %d\n",
cnode));
/* Don't add duplicate boards. */
if (brd->brd_flags & DUPLICATE_BOARD) {
GRPRINTF(("klhwg_connect_one_router: Duplicate router 0x%p on cnode %d\n",
brd, cnode));
return;
}
/* Generate a hardware graph path for this board. */
board_to_path(brd, path_buffer);
rc = hwgraph_traverse(hwgraph_root, path_buffer, &router_hndl);
if (rc != GRAPH_SUCCESS && is_specified(arg_maxnodes))
return;
if (rc != GRAPH_SUCCESS)
printk(KERN_WARNING "Can't find router: %s", path_buffer);
/* We don't know what to do with multiple router components */
if (brd->brd_numcompts != 1) {
PRINT_PANIC("klhwg_connect_one_router: %d cmpts on router\n",
brd->brd_numcompts);
return;
}
/* Convert component 0 to klrou_t ptr */
router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd),
brd->brd_compts[0]);
for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
/* See if the port's active */
if (router->rou_port[port].port_nasid == INVALID_NASID) {
GRPRINTF(("klhwg_connect_one_router: port %d inactive.\n",
port));
continue;
}
if (is_specified(arg_maxnodes) && NASID_TO_COMPACT_NODEID(router->rou_port[port].port_nasid)
== INVALID_CNODEID) {
continue;
}
dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
router->rou_port[port].port_nasid,
router->rou_port[port].port_offset);
/* Generate a hardware graph path for this board. */
board_to_path(dest_brd, dest_path);
rc = hwgraph_traverse(hwgraph_root, dest_path, &dest_hndl);
if (rc != GRAPH_SUCCESS) {
if (is_specified(arg_maxnodes) && KL_CONFIG_DUPLICATE_BOARD(dest_brd))
continue;
PRINT_PANIC("Can't find router: %s", dest_path);
}
GRPRINTF(("klhwg_connect_one_router: Link from %s/%d to %s\n",
path_buffer, port, dest_path));
sprintf(dest_path, "%d", port);
rc = hwgraph_edge_add(router_hndl, dest_hndl, dest_path);
if (rc == GRAPH_DUP) {
GRPRINTF(("Skipping port %d. nasid %d %s/%s\n",
port, router->rou_port[port].port_nasid,
path_buffer, dest_path));
continue;
}
if (rc != GRAPH_SUCCESS && !is_specified(arg_maxnodes))
PRINT_PANIC("Can't create edge: %s/%s to vertex 0x%p error 0x%x\n",
path_buffer, dest_path, (void *)dest_hndl, rc);
}
}
void
klhwg_connect_routers(devfs_handle_t hwgraph_root)
{
nasid_t nasid;
cnodeid_t cnode;
lboard_t *brd;
for (cnode = 0; cnode < numnodes; cnode++) {
nasid = COMPACT_TO_NASID_NODEID(cnode);
GRPRINTF(("klhwg_connect_routers: Connecting routers on cnode %d\n",
cnode));
brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
KLTYPE_ROUTER);
if (!brd)
continue;
do {
nasid = COMPACT_TO_NASID_NODEID(cnode);
klhwg_connect_one_router(hwgraph_root, brd,
cnode, nasid);
/* Find the rest of the routers stored on this node. */
} while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) );
}
}
void
klhwg_connect_hubs(devfs_handle_t hwgraph_root)
{
nasid_t nasid;
cnodeid_t cnode;
lboard_t *brd;
klhub_t *hub;
lboard_t *dest_brd;
devfs_handle_t hub_hndl;
devfs_handle_t dest_hndl;
char path_buffer[50];
char dest_path[50];
graph_error_t rc;
int port;
for (cnode = 0; cnode < numnodes; cnode++) {
nasid = COMPACT_TO_NASID_NODEID(cnode);
GRPRINTF(("klhwg_connect_hubs: Connecting hubs on cnode %d\n",
cnode));
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
ASSERT(brd);
hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
ASSERT(hub);
for (port = 1; port <= MAX_NI_PORTS; port++) {
/* See if the port's active */
if (hub->hub_port[port].port_nasid == INVALID_NASID) {
GRPRINTF(("klhwg_connect_hubs: port inactive.\n"));
continue;
}
if (is_specified(arg_maxnodes) && NASID_TO_COMPACT_NODEID(hub->hub_port[port].port_nasid) == INVALID_CNODEID)
continue;
/* Generate a hardware graph path for this board. */
board_to_path(brd, path_buffer);
GRPRINTF(("klhwg_connect_hubs: Hub path is %s.\n", path_buffer));
rc = hwgraph_traverse(hwgraph_root, path_buffer, &hub_hndl);
if (rc != GRAPH_SUCCESS)
printk(KERN_WARNING "Can't find hub: %s", path_buffer);
dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
hub->hub_port[port].port_nasid,
hub->hub_port[port].port_offset);
/* Generate a hardware graph path for this board. */
board_to_path(dest_brd, dest_path);
rc = hwgraph_traverse(hwgraph_root, dest_path, &dest_hndl);
if (rc != GRAPH_SUCCESS) {
if (is_specified(arg_maxnodes) && KL_CONFIG_DUPLICATE_BOARD(dest_brd))
continue;
PRINT_PANIC("Can't find board: %s", dest_path);
} else {
char buf[1024];
GRPRINTF(("klhwg_connect_hubs: Link from %s to %s.\n",
path_buffer, dest_path));
rc = hwgraph_path_add(hub_hndl, EDGE_LBL_INTERCONNECT, &hub_hndl);
sprintf(buf,"%s/%s",path_buffer,EDGE_LBL_INTERCONNECT);
rc = hwgraph_traverse(hwgraph_root, buf, &hub_hndl);
sprintf(buf,"%d",port);
rc = hwgraph_edge_add(hub_hndl, dest_hndl, buf);
if (rc != GRAPH_SUCCESS)
PRINT_PANIC("Can't create edge: %s/%s to vertex 0x%p, error 0x%x\n",
path_buffer, dest_path, (void *)dest_hndl, rc);
}
}
}
}
/* Store the pci/vme disabled board information as extended administrative
* hints which can later be used by the drivers using the device/driver
* admin interface.
*/
void
klhwg_device_disable_hints_add(void)
{
cnodeid_t cnode; /* node we are looking at */
nasid_t nasid; /* nasid of the node */
lboard_t *board; /* board we are looking at */
int comp_index; /* component index */
klinfo_t *component; /* component in the board we are
* looking at
*/
char device_name[MAXDEVNAME];
for(cnode = 0; cnode < numnodes; cnode++) {
nasid = COMPACT_TO_NASID_NODEID(cnode);
board = (lboard_t *)KL_CONFIG_INFO(nasid);
/* Check out all the board info stored on a node */
while(board) {
/* No need to look at duplicate boards or non-io
* boards
*/
if (KL_CONFIG_DUPLICATE_BOARD(board) ||
KLCLASS(board->brd_type) != KLCLASS_IO) {
board = KLCF_NEXT(board);
continue;
}
/* Check out all the components of a board */
for (comp_index = 0;
comp_index < KLCF_NUM_COMPS(board);
comp_index++) {
component = KLCF_COMP(board,comp_index);
/* If the component is enabled move on to
* the next component
*/
if (KLCONFIG_INFO_ENABLED(component))
continue;
/* NOTE : Since the prom only supports
* the disabling of pci devices the following
* piece of code makes sense.
* Make sure that this assumption is valid
*/
/* This component is disabled. Store this
* hint in the extended device admin table
*/
/* Get the canonical name of the pci device */
device_component_canonical_name_get(board,
component,
device_name);
#ifdef DEBUG
printf("%s DISABLED\n",device_name);
#endif
}
/* go to the next board info stored on this
* node
*/
board = KLCF_NEXT(board);
}
}
}
void
klhwg_add_all_modules(devfs_handle_t hwgraph_root)
{
cmoduleid_t cm;
char name[128];
devfs_handle_t vhdl;
devfs_handle_t module_vhdl;
int rc;
char buffer[16];
/* Add devices under each module */
for (cm = 0; cm < nummodules; cm++) {
/* Use module as module vertex fastinfo */
memset(buffer, 0, 16);
format_module_id(buffer, modules[cm]->id, MODULE_FORMAT_BRIEF);
sprintf(name, EDGE_LBL_MODULE "/%s", buffer);
rc = hwgraph_path_add(hwgraph_root, name, &module_vhdl);
ASSERT(rc == GRAPH_SUCCESS);
rc = rc;
hwgraph_fastinfo_set(module_vhdl, (arbitrary_info_t) modules[cm]);
/* Add system controller */
sprintf(name,
EDGE_LBL_MODULE "/%s/" EDGE_LBL_L1,
buffer);
rc = hwgraph_path_add(hwgraph_root, name, &vhdl);
ASSERT_ALWAYS(rc == GRAPH_SUCCESS);
rc = rc;
hwgraph_info_add_LBL(vhdl,
INFO_LBL_ELSC,
(arbitrary_info_t) (__psint_t) 1);
}
}
void
klhwg_add_all_nodes(devfs_handle_t hwgraph_root)
{
cnodeid_t cnode;
for (cnode = 0; cnode < numnodes; cnode++) {
klhwg_add_node(hwgraph_root, cnode, NULL);
}
for (cnode = 0; cnode < numnodes; cnode++) {
klhwg_add_xbow(cnode, cnodeid_to_nasid(cnode));
}
/*
* As for router hardware inventory information, we set this
* up in router.c.
*/
klhwg_add_all_routers(hwgraph_root);
klhwg_connect_routers(hwgraph_root);
klhwg_connect_hubs(hwgraph_root);
/* Go through the entire system's klconfig
* to figure out which pci components have been disabled
*/
klhwg_device_disable_hints_add();
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
/* In general, this file is organized in a hierarchy from lower-level
* to higher-level layers, as follows:
*
* UART routines
* Bedrock/L1 "PPP-like" protocol implementation
* System controller "message" interface (allows multiplexing
* of various kinds of requests and responses with
* console I/O)
* Console interface:
* "l1_cons", the glue that allows the L1 to act
* as the system console for the stdio libraries
*
* Routines making use of the system controller "message"-style interface
* can be found in l1_command.c.
*/
#include <linux/types.h>
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/eeprom.h>
#include <asm/sn/router.h>
#include <asm/sn/module.h>
#include <asm/sn/ksys/l1.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/clksupport.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/uart16550.h>
#include <asm/sn/simulator.h>
#define UART_BAUD_RATE 57600
int
get_L1_baud(void)
{
return UART_BAUD_RATE;
}
/* Return the current interrupt level */
int
l1_get_intr_value( void )
{
return(0);
}
/* Disconnect the callup functions - throw away interrupts */
void
l1_unconnect_intr(void)
{
}
/* Set up uart interrupt handling for this node's uart */
void
l1_connect_intr(void *rx_notify, void *tx_notify)
{
#if 0
// Will need code here for sn2 - something like this
console_nodepda = NODEPDA(NASID_TO_COMPACT_NODEID(get_master_nasid());
intr_connect_level(console_nodepda->node_first_cpu,
SGI_UART_VECTOR, INTPEND0_MAXMASK,
dummy_intr_func);
request_irq(SGI_UART_VECTOR | (console_nodepda->node_first_cpu << 8),
intr_func, SA_INTERRUPT | SA_SHIRQ,
"l1_protocol_driver", (void *)sc);
#endif
}
/* These are functions to use from serial_in/out when in protocol
* mode to send and receive uart control regs. These are external
* interfaces into the protocol driver.
*/
void
l1_control_out(int offset, int value)
{
/* quietly ignore unless simulator */
if ( IS_RUNNING_ON_SIMULATOR() ) {
extern u64 master_node_bedrock_address;
if ( master_node_bedrock_address != (u64)0 ) {
writeb(value, (unsigned long)master_node_bedrock_address +
(offset<< 3));
}
return;
}
}
/* Console input exported interface. Return a register value. */
int
l1_control_in_polled(int offset)
{
static int l1_control_in_local(int);
return(l1_control_in_local(offset));
}
int
l1_control_in(int offset)
{
static int l1_control_in_local(int);
return(l1_control_in_local(offset));
}
static int
l1_control_in_local(int offset)
{
int sal_call_status = 0, input;
int ret = 0;
if ( IS_RUNNING_ON_SIMULATOR() ) {
extern u64 master_node_bedrock_address;
ret = readb((unsigned long)master_node_bedrock_address +
(offset<< 3));
return(ret);
}
if ( offset == REG_LSR ) {
ret = (LSR_XHRE | LSR_XSRE); /* can send anytime */
sal_call_status = ia64_sn_console_check(&input);
if ( !sal_call_status && input ) {
/* input pending */
ret |= LSR_RCA;
}
}
return(ret);
}
/*
* Console input exported interface. Return a character (if one is available)
*/
int
l1_serial_in_polled(void)
{
static int l1_serial_in_local(void);
return(l1_serial_in_local());
}
int
l1_serial_in(void)
{
static int l1_serial_in_local(void);
if ( IS_RUNNING_ON_SIMULATOR() ) {
extern u64 master_node_bedrock_address;
return(readb((unsigned long)master_node_bedrock_address + (REG_DAT<< 3)));
}
return(l1_serial_in_local());
}
static int
l1_serial_in_local(void)
{
int ch;
if ( IS_RUNNING_ON_SIMULATOR() ) {
extern u64 master_node_bedrock_address;
return(readb((unsigned long)master_node_bedrock_address + (REG_DAT<< 3)));
}
if ( !(ia64_sn_console_getc(&ch)) )
return(ch);
else
return(0);
}
/* Console output exported interface. Write message to the console. */
int
l1_serial_out( char *str, int len )
{
int counter = len;
/* Ignore empty messages */
if ( len == 0 )
return(len);
#if defined(CONFIG_IA64_EARLY_PRINTK)
/* Need to setup SAL calls so the PROM calls will work */
{
static int inited;
void early_sn_setup(void);
if(!inited) {
inited=1;
early_sn_setup();
}
}
#endif
if ( IS_RUNNING_ON_SIMULATOR() ) {
extern u64 master_node_bedrock_address;
void early_sn_setup(void);
if (!master_node_bedrock_address)
early_sn_setup();
if ( master_node_bedrock_address != (u64)0 ) {
#ifdef FLAG_DIRECT_CONSOLE_WRITES
/* This is an easy way to pre-pend the output to know whether the output
* was done via sal or directly */
writeb('[', (unsigned long)master_node_bedrock_address + (REG_DAT<< 3));
writeb('+', (unsigned long)master_node_bedrock_address + (REG_DAT<< 3));
writeb(']', (unsigned long)master_node_bedrock_address + (REG_DAT<< 3));
writeb(' ', (unsigned long)master_node_bedrock_address + (REG_DAT<< 3));
#endif /* FLAG_DIRECT_CONSOLE_WRITES */
while ( counter > 0 ) {
writeb(*str, (unsigned long)master_node_bedrock_address + (REG_DAT<< 3));
counter--;
str++;
}
}
return(len);
}
/* Attempt to write things out thru the sal */
if ( ia64_sn_console_putb(str, len) )
return(0);
return((counter <= 0) ? 0 : (len - counter));
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/eeprom.h>
#include <asm/sn/router.h>
#include <asm/sn/module.h>
#include <asm/sn/ksys/l1.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/clksupport.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/sn_sal.h>
#include <linux/ctype.h>
#define ELSC_TIMEOUT 1000000 /* ELSC response timeout (usec) */
#define LOCK_TIMEOUT 5000000 /* Hub lock timeout (usec) */
#define hub_cpu_get() 0
#define LBYTE(caddr) (*(char *) caddr)
extern char *bcopy(const char * src, char * dest, int count);
#define LDEBUG 0
/*
* ELSC data is in NVRAM page 7 at the following offsets.
*/
#define NVRAM_MAGIC_AD 0x700 /* magic number used for init */
#define NVRAM_PASS_WD 0x701 /* password (4 bytes in length) */
#define NVRAM_DBG1 0x705 /* virtual XOR debug switches */
#define NVRAM_DBG2 0x706 /* physical XOR debug switches */
#define NVRAM_CFG 0x707 /* ELSC Configuration info */
#define NVRAM_MODULE 0x708 /* system module number */
#define NVRAM_BIST_FLG 0x709 /* BIST flags (2 bits per nodeboard) */
#define NVRAM_PARTITION 0x70a /* module's partition id */
#define NVRAM_DOMAIN 0x70b /* module's domain id */
#define NVRAM_CLUSTER 0x70c /* module's cluster id */
#define NVRAM_CELL 0x70d /* module's cellid */
#define NVRAM_MAGIC_NO 0x37 /* value of magic number */
#define NVRAM_SIZE 16 /* 16 bytes in nvram */
/* elsc_display_line writes up to 12 characters to either the top or bottom
* line of the L1 display. line points to a buffer containing the message
* to be displayed. The zero-based line number is specified by lnum (so
* lnum == 0 specifies the top line and lnum == 1 specifies the bottom).
* Lines longer than 12 characters, or line numbers not less than
* L1_DISPLAY_LINES, cause elsc_display_line to return an error.
*/
int elsc_display_line(nasid_t nasid, char *line, int lnum)
{
return 0;
}
/*
* iobrick routines
*/
/* iobrick_rack_bay_type_get fills in the three int * arguments with the
* rack number, bay number and brick type of the L1 being addressed. Note
* that if the L1 operation fails and this function returns an error value,
* garbage may be written to brick_type.
*/
int iobrick_rack_bay_type_get( nasid_t nasid, uint *rack,
uint *bay, uint *brick_type )
{
int result = 0;
if ( ia64_sn_sysctl_iobrick_module_get(nasid, &result) )
return( ELSC_ERROR_CMD_SEND );
*rack = (result & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
*bay = (result & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
*brick_type = (result & L1_ADDR_TYPE_MASK) >> L1_ADDR_TYPE_SHFT;
*brick_type = toupper(*brick_type);
return 0;
}
int iomoduleid_get(nasid_t nasid)
{
int result = 0;
if ( ia64_sn_sysctl_iobrick_module_get(nasid, &result) )
return( ELSC_ERROR_CMD_SEND );
return result;
}
int iobrick_module_get(nasid_t nasid)
{
uint rnum, rack, bay, brick_type, t;
int ret;
/* construct module ID from rack and slot info */
if ((ret = iobrick_rack_bay_type_get(nasid, &rnum, &bay, &brick_type)) < 0)
return ret;
if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
return ELSC_ERROR_MODULE;
/* Build a moduleid_t-compatible rack number */
rack = 0;
t = rnum / 100; /* rack class (CPU/IO) */
if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
return ELSC_ERROR_MODULE;
RACK_ADD_CLASS(rack, t);
rnum %= 100;
t = rnum / 10; /* rack group */
if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
return ELSC_ERROR_MODULE;
RACK_ADD_GROUP(rack, t);
t = rnum % 10; /* rack number (one-based) */
if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
return ELSC_ERROR_MODULE;
RACK_ADD_NUM(rack, t);
switch( brick_type ) {
case 'I':
brick_type = MODULE_IBRICK; break;
case 'P':
brick_type = MODULE_PBRICK; break;
case 'X':
brick_type = MODULE_XBRICK; break;
}
ret = RBT_TO_MODULE(rack, bay, brick_type);
return ret;
}
/*
* iobrick_module_get_nasid() returns a module_id which has the brick
* type encoded in bits 15-12, but this is not the true brick type...
* The module_id returned by iobrick_module_get_nasid() is modified
* to make a PEBRICKs & PXBRICKs look like a PBRICK. So this routine
* iobrick_type_get_nasid() returns the true unmodified brick type.
*/
int
iobrick_type_get_nasid(nasid_t nasid)
{
uint rack, bay, type;
int t, ret;
extern char brick_types[];
if ((ret = iobrick_rack_bay_type_get(nasid, &rack, &bay, &type)) < 0) {
return ret;
}
/* convert brick_type to lower case */
if ((type >= 'A') && (type <= 'Z'))
type = type - 'A' + 'a';
/* convert to a module.h brick type */
for( t = 0; t < MAX_BRICK_TYPES; t++ ) {
if( brick_types[t] == type )
return t;
}
return -1; /* unknown brick */
}
int iobrick_module_get_nasid(nasid_t nasid)
{
int io_moduleid;
#ifdef PIC_LATER
uint rack, bay;
if (PEBRICK_NODE(nasid)) {
if (peer_iobrick_rack_bay_get(nasid, &rack, &bay)) {
printf("Could not read rack and bay location "
"of PEBrick at nasid %d\n", nasid);
}
io_moduleid = peer_iobrick_module_get(sc, rack, bay);
}
#endif /* PIC_LATER */
io_moduleid = iobrick_module_get(nasid);
return io_moduleid;
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/snconfig.h>
extern int numcpus;
extern char arg_maxnodes[];
extern cpuid_t master_procid;
extern int hasmetarouter;
int maxcpus;
cpumask_t boot_cpumask;
hubreg_t region_mask = 0;
extern xwidgetnum_t hub_widget_id(nasid_t);
extern int valid_icache_reasons; /* Reasons to flush the icache */
extern int valid_dcache_reasons; /* Reasons to flush the dcache */
extern u_char miniroot;
extern volatile int need_utlbmiss_patch;
extern void iograph_early_init(void);
nasid_t master_nasid = INVALID_NASID; /* This is the partition master nasid */
nasid_t master_baseio_nasid = INVALID_NASID; /* This is the master base I/O nasid */
/*
* mlreset(void)
* very early machine reset - at this point NO interrupts have been
* enabled; nor is memory, tlb, p0, etc setup.
*
* slave is zero when mlreset is called for the master processor and
* is nonzero thereafter.
*/
void
mlreset(int slave)
{
/*
* We are the master cpu and node.
*/
master_nasid = get_nasid();
set_master_bridge_base();
/* We're the master processor */
master_procid = smp_processor_id();
master_nasid = cpuid_to_nasid(master_procid);
/*
* master_nasid we get back better be same as one from
* get_nasid()
*/
ASSERT_ALWAYS(master_nasid == get_nasid());
/* early initialization of iograph */
iograph_early_init();
/* Initialize Hub Pseudodriver Management */
hubdev_init();
}
/* XXX - Move the meat of this to intr.c ? */
/*
* Set up the platform-dependent fields in the nodepda.
*/
void init_platform_nodepda(nodepda_t *npda, cnodeid_t node)
{
hubinfo_t hubinfo;
extern void router_map_init(nodepda_t *);
extern void router_queue_init(nodepda_t *,cnodeid_t);
extern void intr_init_vecblk(nodepda_t *, cnodeid_t, int);
/* Allocate per-node platform-dependent data */
hubinfo = (hubinfo_t)alloc_bootmem_node(NODE_DATA(node), sizeof(struct hubinfo_s));
npda->pdinfo = (void *)hubinfo;
hubinfo->h_nodepda = npda;
hubinfo->h_cnodeid = node;
hubinfo->h_nasid = COMPACT_TO_NASID_NODEID(node);
spin_lock_init(&hubinfo->h_crblock);
hubinfo->h_widgetid = hub_widget_id(hubinfo->h_nasid);
npda->xbow_peer = INVALID_NASID;
/*
* Initialize the linked list of
* router info pointers to the dependent routers
*/
npda->npda_rip_first = NULL;
/*
* npda_rip_last always points to the place
* where the next element is to be inserted
* into the list
*/
npda->npda_rip_last = &npda->npda_rip_first;
npda->geoid.any.type = GEO_TYPE_INVALID;
mutex_init_locked(&npda->xbow_sema); /* init it locked? */
}
/* XXX - Move the interrupt stuff to intr.c ? */
/*
* Set up the platform-dependent fields in the processor pda.
* Must be done _after_ init_platform_nodepda().
* If we need a lock here, something else is wrong!
*/
void init_platform_pda(cpuid_t cpu)
{
}
void
update_node_information(cnodeid_t cnodeid)
{
nodepda_t *npda = NODEPDA(cnodeid);
nodepda_router_info_t *npda_rip;
/* Go through the list of router info
* structures and copy some frequently
* accessed info from the info hanging
* off the corresponding router vertices
*/
npda_rip = npda->npda_rip_first;
while(npda_rip) {
if (npda_rip->router_infop) {
npda_rip->router_portmask =
npda_rip->router_infop->ri_portmask;
npda_rip->router_slot =
npda_rip->router_infop->ri_slotnum;
} else {
/* No router, no ports. */
npda_rip->router_portmask = 0;
}
npda_rip = npda_rip->router_next;
}
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <asm/sn/sgi.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/xtalk/xbow.h>
#include <asm/sn/pci/bridge.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/eeprom.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/xtalk/xswitch.h>
#include <asm/sn/xtalk/xwidget.h>
#include <asm/sn/xtalk/xtalk_private.h>
#include <asm/sn/xtalk/xtalkaddrs.h>
/* #define IOGRAPH_DEBUG */
#ifdef IOGRAPH_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif /* IOGRAPH_DEBUG */
/* #define PROBE_TEST */
/* At most 2 hubs can be connected to an xswitch */
#define NUM_XSWITCH_VOLUNTEER 2
extern unsigned char Is_pic_on_this_nasid[512];
/*
* Track which hubs have volunteered to manage devices hanging off of
* a Crosstalk Switch (e.g. xbow). This structure is allocated,
* initialized, and hung off the xswitch vertex early on when the
* xswitch vertex is created.
*/
typedef struct xswitch_vol_s {
mutex_t xswitch_volunteer_mutex;
int xswitch_volunteer_count;
devfs_handle_t xswitch_volunteer[NUM_XSWITCH_VOLUNTEER];
} *xswitch_vol_t;
void
xswitch_vertex_init(devfs_handle_t xswitch)
{
xswitch_vol_t xvolinfo;
int rc;
extern void * snia_kmem_zalloc(size_t size, int flag);
xvolinfo = snia_kmem_zalloc(sizeof(struct xswitch_vol_s), GFP_KERNEL);
mutex_init(&xvolinfo->xswitch_volunteer_mutex);
rc = hwgraph_info_add_LBL(xswitch,
INFO_LBL_XSWITCH_VOL,
(arbitrary_info_t)xvolinfo);
ASSERT(rc == GRAPH_SUCCESS); rc = rc;
}
/*
* When assignment of hubs to widgets is complete, we no longer need the
* xswitch volunteer structure hanging around. Destroy it.
*/
static void
xswitch_volunteer_delete(devfs_handle_t xswitch)
{
xswitch_vol_t xvolinfo;
int rc;
extern void snia_kmem_free(void *ptr, size_t size);
rc = hwgraph_info_remove_LBL(xswitch,
INFO_LBL_XSWITCH_VOL,
(arbitrary_info_t *)&xvolinfo);
snia_kmem_free(xvolinfo, sizeof(struct xswitch_vol_s));
}
/*
* A Crosstalk master volunteers to manage xwidgets on the specified xswitch.
*/
/* ARGSUSED */
static void
volunteer_for_widgets(devfs_handle_t xswitch, devfs_handle_t master)
{
xswitch_vol_t xvolinfo = NULL;
devfs_handle_t hubv;
hubinfo_t hubinfo;
(void)hwgraph_info_get_LBL(xswitch,
INFO_LBL_XSWITCH_VOL,
(arbitrary_info_t *)&xvolinfo);
if (xvolinfo == NULL) {
if (!is_headless_node_vertex(master))
printk(KERN_WARNING
"volunteer for widgets: vertex 0x%p has no info label",
(void *)xswitch);
return;
}
mutex_lock(&xvolinfo->xswitch_volunteer_mutex);
ASSERT(xvolinfo->xswitch_volunteer_count < NUM_XSWITCH_VOLUNTEER);
xvolinfo->xswitch_volunteer[xvolinfo->xswitch_volunteer_count] = master;
xvolinfo->xswitch_volunteer_count++;
/*
* if dual ported, make the lowest widgetid always be
* xswitch_volunteer[0].
*/
if (xvolinfo->xswitch_volunteer_count == NUM_XSWITCH_VOLUNTEER) {
hubv = xvolinfo->xswitch_volunteer[0];
hubinfo_get(hubv, &hubinfo);
if (hubinfo->h_widgetid != XBOW_HUBLINK_LOW) {
xvolinfo->xswitch_volunteer[0] =
xvolinfo->xswitch_volunteer[1];
xvolinfo->xswitch_volunteer[1] = hubv;
}
}
mutex_unlock(&xvolinfo->xswitch_volunteer_mutex);
}
extern int xbow_port_io_enabled(nasid_t nasid, int widgetnum);
/*
* Assign all the xwidgets hanging off the specified xswitch to the
* Crosstalk masters that have volunteered for xswitch duty.
*/
/* ARGSUSED */
static void
assign_widgets_to_volunteers(devfs_handle_t xswitch, devfs_handle_t hubv)
{
xswitch_info_t xswitch_info;
xswitch_vol_t xvolinfo = NULL;
xwidgetnum_t widgetnum;
int num_volunteer;
nasid_t nasid;
hubinfo_t hubinfo;
extern int iobrick_type_get_nasid(nasid_t);
hubinfo_get(hubv, &hubinfo);
nasid = hubinfo->h_nasid;
xswitch_info = xswitch_info_get(xswitch);
ASSERT(xswitch_info != NULL);
(void)hwgraph_info_get_LBL(xswitch,
INFO_LBL_XSWITCH_VOL,
(arbitrary_info_t *)&xvolinfo);
if (xvolinfo == NULL) {
if (!is_headless_node_vertex(hubv))
printk(KERN_WARNING
"assign_widgets_to_volunteers:vertex 0x%p has "
" no info label",
(void *)xswitch);
return;
}
num_volunteer = xvolinfo->xswitch_volunteer_count;
ASSERT(num_volunteer > 0);
/* Assign master hub for xswitch itself. */
if (HUB_WIDGET_ID_MIN > 0) {
hubv = xvolinfo->xswitch_volunteer[0];
xswitch_info_master_assignment_set(xswitch_info, (xwidgetnum_t)0, hubv);
}
/*
* TBD: Use administrative information to alter assignment of
* widgets to hubs.
*/
for (widgetnum=HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
int i;
/*
* Ignore disabled/empty ports.
*/
if (!xbow_port_io_enabled(nasid, widgetnum))
continue;
/*
* If this is the master IO board, assign it to the same
* hub that owned it in the prom.
*/
if (is_master_baseio_nasid_widget(nasid, widgetnum)) {
extern nasid_t get_master_baseio_nasid(void);
for (i=0; i<num_volunteer; i++) {
hubv = xvolinfo->xswitch_volunteer[i];
hubinfo_get(hubv, &hubinfo);
nasid = hubinfo->h_nasid;
if (nasid == get_master_baseio_nasid())
goto do_assignment;
}
PRINT_PANIC("Nasid == %d, console nasid == %d",
nasid, get_master_baseio_nasid());
}
/*
* Assuming that we're dual-hosted and that PCI cards
* are naturally placed left-to-right, alternate PCI
* buses across both Cbricks. For Pbricks, and Ibricks,
* io_brick_map_widget() returns the PCI bus number
* associated with the given brick type and widget number.
* For Xbricks, it returns the XIO slot number.
*/
i = 0;
if (num_volunteer > 1) {
int bt;
bt = iobrick_type_get_nasid(nasid);
if (bt >= 0) {
/*
* PXBRICK has two busses per widget so this
* algorithm wouldn't work (all busses would
* be assigned to one volunteer). Change the
* bricktype to PBRICK whose mapping is setup
* suchthat 2 of the PICs will be assigned to
* one volunteer and the other one will be
* assigned to the other volunteer.
*/
if (bt == MODULE_PXBRICK)
bt = MODULE_PBRICK;
i = io_brick_map_widget(bt, widgetnum) & 1;
}
}
hubv = xvolinfo->xswitch_volunteer[i];
do_assignment:
/*
* At this point, we want to make hubv the master of widgetnum.
*/
xswitch_info_master_assignment_set(xswitch_info, widgetnum, hubv);
}
xswitch_volunteer_delete(xswitch);
}
/*
* Early iograph initialization. Called by master CPU in mlreset().
* Useful for including iograph.o in kernel.o.
*/
void
iograph_early_init(void)
{
/*
* Need new way to get this information ..
*/
cnodeid_t cnode;
nasid_t nasid;
lboard_t *board;
/*
* Init. the board-to-hwgraph link early, so FRU analyzer
* doesn't trip on leftover values if we panic early on.
*/
for(cnode = 0; cnode < numnodes; cnode++) {
nasid = COMPACT_TO_NASID_NODEID(cnode);
board = (lboard_t *)KL_CONFIG_INFO(nasid);
DBG("iograph_early_init: Found board 0x%p\n", board);
/* Check out all the board info stored on a node */
while(board) {
board->brd_graph_link = GRAPH_VERTEX_NONE;
board = KLCF_NEXT(board);
DBG("iograph_early_init: Found board 0x%p\n", board);
}
}
hubio_init();
}
/*
* Let boot processor know that we're done initializing our node's IO
* and then exit.
*/
/* ARGSUSED */
static void
io_init_done(cnodeid_t cnodeid,cpu_cookie_t c)
{
/* Let boot processor know that we're done. */
}
/*
* Probe to see if this hub's xtalk link is active. If so,
* return the Crosstalk Identification of the widget that we talk to.
* This is called before any of the Crosstalk infrastructure for
* this hub is set up. It's usually called on the node that we're
* probing, but not always.
*
* TBD: Prom code should actually do this work, and pass through
* hwid for our use.
*/
static void
early_probe_for_widget(devfs_handle_t hubv, xwidget_hwid_t hwid)
{
hubreg_t llp_csr_reg;
nasid_t nasid;
hubinfo_t hubinfo;
hubinfo_get(hubv, &hubinfo);
nasid = hubinfo->h_nasid;
llp_csr_reg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
/*
* If link is up, read the widget's part number.
* A direct connect widget must respond to widgetnum=0.
*/
if (llp_csr_reg & IIO_LLP_CSR_IS_UP) {
/* TBD: Put hub into "indirect" mode */
/*
* We're able to read from a widget because our hub's
* WIDGET_ID was set up earlier.
*/
widgetreg_t widget_id = *(volatile widgetreg_t *)
(RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
DBG("early_probe_for_widget: Hub Vertex 0x%p is UP widget_id = 0x%x Register 0x%p\n", hubv, widget_id,
(volatile widgetreg_t *)(RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID) );
hwid->part_num = XWIDGET_PART_NUM(widget_id);
hwid->rev_num = XWIDGET_REV_NUM(widget_id);
hwid->mfg_num = XWIDGET_MFG_NUM(widget_id);
/* TBD: link reset */
} else {
hwid->part_num = XWIDGET_PART_NUM_NONE;
hwid->rev_num = XWIDGET_REV_NUM_NONE;
hwid->mfg_num = XWIDGET_MFG_NUM_NONE;
}
}
/* Add inventory information to the widget vertex
* Right now (module,slot,revision) is being
* added as inventory information.
*/
static void
xwidget_inventory_add(devfs_handle_t widgetv,
lboard_t *board,
struct xwidget_hwid_s hwid)
{
if (!board)
return;
/* Donot add inventory information for the baseio
* on a speedo with an xbox. It has already been
* taken care of in SN00_vmc.
* Speedo with xbox's baseio comes in at slot io1 (widget 9)
*/
device_inventory_add(widgetv,INV_IOBD,board->brd_type,
geo_module(board->brd_geoid),
SLOTNUM_GETSLOT(board->brd_slot),
hwid.rev_num);
}
/*
* io_xswitch_widget_init
*
*/
void
io_xswitch_widget_init(devfs_handle_t xswitchv,
devfs_handle_t hubv,
xwidgetnum_t widgetnum,
async_attach_t aa)
{
xswitch_info_t xswitch_info;
xwidgetnum_t hub_widgetid;
devfs_handle_t widgetv;
cnodeid_t cnode;
widgetreg_t widget_id;
nasid_t nasid, peer_nasid;
struct xwidget_hwid_s hwid;
hubinfo_t hubinfo;
/*REFERENCED*/
int rc;
char pathname[128];
lboard_t *board = NULL;
char buffer[16];
char bt;
moduleid_t io_module;
slotid_t get_widget_slotnum(int xbow, int widget);
DBG("\nio_xswitch_widget_init: hubv 0x%p, xswitchv 0x%p, widgetnum 0x%x\n", hubv, xswitchv, widgetnum);
/*
* Verify that xswitchv is indeed an attached xswitch.
*/
xswitch_info = xswitch_info_get(xswitchv);
ASSERT(xswitch_info != NULL);
hubinfo_get(hubv, &hubinfo);
nasid = hubinfo->h_nasid;
cnode = NASID_TO_COMPACT_NODEID(nasid);
hub_widgetid = hubinfo->h_widgetid;
/*
* Check that the widget is an io widget and is enabled
* on this nasid or the `peer' nasid. The peer nasid
* is the other hub/bedrock connected to the xbow.
*/
peer_nasid = NODEPDA(cnode)->xbow_peer;
if (peer_nasid == INVALID_NASID)
/* If I don't have a peer, use myself. */
peer_nasid = nasid;
if (!xbow_port_io_enabled(nasid, widgetnum) &&
!xbow_port_io_enabled(peer_nasid, widgetnum)) {
return;
}
if (xswitch_info_link_ok(xswitch_info, widgetnum)) {
char name[4];
lboard_t dummy;
/*
* If the current hub is not supposed to be the master
* for this widgetnum, then skip this widget.
*/
if (xswitch_info_master_assignment_get(xswitch_info,
widgetnum) != hubv) {
return;
}
board = find_lboard_class(
(lboard_t *)KL_CONFIG_INFO(nasid),
KLCLASS_IOBRICK);
if (!board && NODEPDA(cnode)->xbow_peer != INVALID_NASID) {
board = find_lboard_class(
(lboard_t *)KL_CONFIG_INFO( NODEPDA(cnode)->xbow_peer),
KLCLASS_IOBRICK);
}
if (board) {
DBG("io_xswitch_widget_init: Found KLTYPE_IOBRICK Board 0x%p brd_type 0x%x\n", board, board->brd_type);
} else {
DBG("io_xswitch_widget_init: FIXME did not find IOBOARD\n");
board = &dummy;
}
/* Copy over the nodes' geoid info */
{
lboard_t *brd;
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
if ( brd != (lboard_t *)0 ) {
board->brd_geoid = brd->brd_geoid;
}
}
/*
* Make sure we really want to say xbrick, pbrick,
* etc. rather than XIO, graphics, etc.
*/
memset(buffer, 0, 16);
format_module_id(buffer, geo_module(board->brd_geoid), MODULE_FORMAT_BRIEF);
sprintf(pathname, EDGE_LBL_MODULE "/%s/" EDGE_LBL_SLAB "/%d" "/%cbrick" "/%s/%d",
buffer,
geo_slab(board->brd_geoid),
(board->brd_type == KLTYPE_IBRICK) ? 'I' :
(board->brd_type == KLTYPE_PBRICK) ? 'P' :
(board->brd_type == KLTYPE_XBRICK) ? 'X' : '?',
EDGE_LBL_XTALK, widgetnum);
DBG("io_xswitch_widget_init: path= %s\n", pathname);
rc = hwgraph_path_add(hwgraph_root, pathname, &widgetv);
ASSERT(rc == GRAPH_SUCCESS);
/* This is needed to let the user programs to map the
* module,slot numbers to the corresponding widget numbers
* on the crossbow.
*/
device_master_set(hwgraph_connectpt_get(widgetv), hubv);
sprintf(name, "%d", widgetnum);
DBG("io_xswitch_widget_init: FIXME hwgraph_edge_add %s xswitchv 0x%p, widgetv 0x%p\n", name, xswitchv, widgetv);
rc = hwgraph_edge_add(xswitchv, widgetv, name);
/*
* crosstalk switch code tracks which
* widget is attached to each link.
*/
xswitch_info_vhdl_set(xswitch_info, widgetnum, widgetv);
/*
* Peek at the widget to get its crosstalk part and
* mfgr numbers, then present it to the generic xtalk
* bus provider to have its driver attach routine
* called (or not).
*/
widget_id = XWIDGET_ID_READ(nasid, widgetnum);
hwid.part_num = XWIDGET_PART_NUM(widget_id);
hwid.rev_num = XWIDGET_REV_NUM(widget_id);
hwid.mfg_num = XWIDGET_MFG_NUM(widget_id);
/* Store some inventory information about
* the xwidget in the hardware graph.
*/
xwidget_inventory_add(widgetv,board,hwid);
(void)xwidget_register(&hwid, widgetv, widgetnum,
hubv, hub_widgetid,
aa);
ia64_sn_sysctl_iobrick_module_get(nasid, &io_module);
if (io_module >= 0) {
char buffer[16];
devfs_handle_t to, from;
memset(buffer, 0, 16);
format_module_id(buffer, geo_module(board->brd_geoid), MODULE_FORMAT_BRIEF);
bt = toupper(MODULE_GET_BTCHAR(io_module));
/* Add a helper vertex so xbow monitoring
* can identify the brick type. It's simply
* an edge from the widget 0 vertex to the
* brick vertex.
*/
sprintf(pathname, "/dev/hw/" EDGE_LBL_MODULE "/%s/"
EDGE_LBL_SLAB "/%d/"
EDGE_LBL_NODE "/" EDGE_LBL_XTALK "/"
"0",
buffer, geo_slab(board->brd_geoid));
from = hwgraph_path_to_vertex(pathname);
ASSERT_ALWAYS(from);
sprintf(pathname, "/dev/hw/" EDGE_LBL_MODULE "/%s/"
EDGE_LBL_SLAB "/%d/"
"%cbrick",
buffer, geo_slab(board->brd_geoid), bt);
to = hwgraph_path_to_vertex(pathname);
ASSERT_ALWAYS(to);
rc = hwgraph_edge_add(from, to,
EDGE_LBL_INTERCONNECT);
if (rc == -EEXIST)
goto link_done;
if (rc != GRAPH_SUCCESS) {
printk("%s: Unable to establish link"
" for xbmon.", pathname);
}
link_done:
}
#ifdef SN0_USE_BTE
bte_bpush_war(cnode, (void *)board);
#endif
}
}
static void
io_init_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnode)
{
xwidgetnum_t widgetnum;
async_attach_t aa;
aa = async_attach_new();
DBG("io_init_xswitch_widgets: xswitchv 0x%p for cnode %d\n", xswitchv, cnode);
for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX;
widgetnum++) {
io_xswitch_widget_init(xswitchv,
cnodeid_to_vertex(cnode),
widgetnum, aa);
}
/*
* Wait for parallel attach threads, if any, to complete.
*/
async_attach_waitall(aa);
async_attach_free(aa);
}
/*
* For each PCI bridge connected to the xswitch, add a link from the
* board's klconfig info to the bridge's hwgraph vertex. This lets
* the FRU analyzer find the bridge without traversing the hardware
* graph and risking hangs.
*/
static void
io_link_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnodeid)
{
xwidgetnum_t widgetnum;
char pathname[128];
devfs_handle_t vhdl;
nasid_t nasid, peer_nasid;
lboard_t *board;
/* And its connected hub's nasids */
nasid = COMPACT_TO_NASID_NODEID(cnodeid);
peer_nasid = NODEPDA(cnodeid)->xbow_peer;
/*
* Look for paths matching "<widgetnum>/pci" under xswitchv.
* For every widget, init. its lboard's hwgraph link. If the
* board has a PCI bridge, point the link to it.
*/
for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX;
widgetnum++) {
sprintf(pathname, "%d", widgetnum);
if (hwgraph_traverse(xswitchv, pathname, &vhdl) !=
GRAPH_SUCCESS)
continue;
board = find_lboard_module((lboard_t *)KL_CONFIG_INFO(nasid),
NODEPDA(cnodeid)->geoid);
if (board == NULL && peer_nasid != INVALID_NASID) {
/*
* Try to find the board on our peer
*/
board = find_lboard_module(
(lboard_t *)KL_CONFIG_INFO(peer_nasid),
NODEPDA(cnodeid)->geoid);
}
if (board == NULL) {
printk(KERN_WARNING "Could not find PROM info for vertex 0x%p, "
"FRU analyzer may fail",
(void *)vhdl);
return;
}
if ( Is_pic_on_this_nasid[nasid] ) {
/* Check both buses */
sprintf(pathname, "%d/"EDGE_LBL_PCIX_0, widgetnum);
if (hwgraph_traverse(xswitchv, pathname, &vhdl) == GRAPH_SUCCESS)
board->brd_graph_link = vhdl;
else {
sprintf(pathname, "%d/"EDGE_LBL_PCIX_1, widgetnum);
if (hwgraph_traverse(xswitchv, pathname, &vhdl) == GRAPH_SUCCESS)
board->brd_graph_link = vhdl;
else
board->brd_graph_link = GRAPH_VERTEX_NONE;
}
}
else {
sprintf(pathname, "%d/"EDGE_LBL_PCI, widgetnum);
if (hwgraph_traverse(xswitchv, pathname, &vhdl) == GRAPH_SUCCESS)
board->brd_graph_link = vhdl;
else
board->brd_graph_link = GRAPH_VERTEX_NONE;
}
}
}
/*
* Initialize all I/O on the specified node.
*/
static void
io_init_node(cnodeid_t cnodeid)
{
/*REFERENCED*/
devfs_handle_t hubv, switchv, widgetv;
struct xwidget_hwid_s hwid;
hubinfo_t hubinfo;
int is_xswitch;
nodepda_t *npdap;
struct semaphore *peer_sema = 0;
uint32_t widget_partnum;
nodepda_router_info_t *npda_rip;
cpu_cookie_t c = 0;
extern int hubdev_docallouts(devfs_handle_t);
npdap = NODEPDA(cnodeid);
/*
* Get the "top" vertex for this node's hardware
* graph; it will carry the per-hub hub-specific
* data, and act as the crosstalk provider master.
* It's canonical path is probably something of the
* form /hw/module/%M/slot/%d/node
*/
hubv = cnodeid_to_vertex(cnodeid);
DBG("io_init_node: Initialize IO for cnode %d hubv(node) 0x%p npdap 0x%p\n", cnodeid, hubv, npdap);
ASSERT(hubv != GRAPH_VERTEX_NONE);
hubdev_docallouts(hubv);
/*
* Set up the dependent routers if we have any.
*/
npda_rip = npdap->npda_rip_first;
while(npda_rip) {
/* If the router info has not been initialized
* then we need to do the router initialization
*/
if (!npda_rip->router_infop) {
router_init(cnodeid,0,npda_rip);
}
npda_rip = npda_rip->router_next;
}
/*
* Read mfg info on this hub
*/
/*
* If nothing connected to this hub's xtalk port, we're done.
*/
early_probe_for_widget(hubv, &hwid);
if (hwid.part_num == XWIDGET_PART_NUM_NONE) {
#ifdef PROBE_TEST
if ((cnodeid == 1) || (cnodeid == 2)) {
int index;
for (index = 0; index < 600; index++)
DBG("Interfering with device probing!!!\n");
}
#endif
/* io_init_done takes cpu cookie as 2nd argument
* to do a restorenoderun for the setnoderun done
* at the start of this thread
*/
DBG("**** io_init_node: Node's 0x%p hub widget has XWIDGET_PART_NUM_NONE ****\n", hubv);
return;
/* NOTREACHED */
}
/*
* attach our hub_provider information to hubv,
* so we can use it as a crosstalk provider "master"
* vertex.
*/
xtalk_provider_register(hubv, &hub_provider);
xtalk_provider_startup(hubv);
/*
* Create a vertex to represent the crosstalk bus
* attached to this hub, and a vertex to be used
* as the connect point for whatever is out there
* on the other side of our crosstalk connection.
*
* Crosstalk Switch drivers "climb up" from their
* connection point to try and take over the switch
* point.
*
* Of course, the edges and verticies may already
* exist, in which case our net effect is just to
* associate the "xtalk_" driver with the connection
* point for the device.
*/
(void)hwgraph_path_add(hubv, EDGE_LBL_XTALK, &switchv);
DBG("io_init_node: Created 'xtalk' entry to '../node/' xtalk vertex 0x%p\n", switchv);
ASSERT(switchv != GRAPH_VERTEX_NONE);
(void)hwgraph_edge_add(hubv, switchv, EDGE_LBL_IO);
DBG("io_init_node: Created symlink 'io' from ../node/io to ../node/xtalk \n");
/*
* We need to find the widget id and update the basew_id field
* accordingly. In particular, SN00 has direct connected bridge,
* and hence widget id is Not 0.
*/
widget_partnum = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + WIDGET_ID))) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
if (widget_partnum == BRIDGE_WIDGET_PART_NUM ||
widget_partnum == XBRIDGE_WIDGET_PART_NUM){
npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID);
DBG("io_init_node: Found XBRIDGE widget_partnum= 0x%x\n", widget_partnum);
} else if ((widget_partnum == XBOW_WIDGET_PART_NUM) ||
(widget_partnum == XXBOW_WIDGET_PART_NUM) ||
(widget_partnum == PXBOW_WIDGET_PART_NUM) ) {
/*
* Xbow control register does not have the widget ID field.
* So, hard code the widget ID to be zero.
*/
DBG("io_init_node: Found XBOW widget_partnum= 0x%x\n", widget_partnum);
npdap->basew_id = 0;
} else {
npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID);
panic(" ****io_init_node: Unknown Widget Part Number 0x%x Widget ID 0x%x attached to Hubv 0x%p ****\n", widget_partnum, npdap->basew_id, (void *)hubv);
/*NOTREACHED*/
}
{
char widname[10];
sprintf(widname, "%x", npdap->basew_id);
(void)hwgraph_path_add(switchv, widname, &widgetv);
DBG("io_init_node: Created '%s' to '..node/xtalk/' vertex 0x%p\n", widname, widgetv);
ASSERT(widgetv != GRAPH_VERTEX_NONE);
}
nodepda->basew_xc = widgetv;
is_xswitch = xwidget_hwid_is_xswitch(&hwid);
/*
* Try to become the master of the widget. If this is an xswitch
* with multiple hubs connected, only one will succeed. Mastership
* of an xswitch is used only when touching registers on that xswitch.
* The slave xwidgets connected to the xswitch can be owned by various
* masters.
*/
if (device_master_set(widgetv, hubv) == 0) {
/* Only one hub (thread) per Crosstalk device or switch makes
* it to here.
*/
/*
* Initialize whatever xwidget is hanging off our hub.
* Whatever it is, it's accessible through widgetnum 0.
*/
hubinfo_get(hubv, &hubinfo);
(void)xwidget_register(&hwid, widgetv, npdap->basew_id, hubv, hubinfo->h_widgetid, NULL);
if (!is_xswitch) {
/* io_init_done takes cpu cookie as 2nd argument
* to do a restorenoderun for the setnoderun done
* at the start of this thread
*/
io_init_done(cnodeid,c);
/* NOTREACHED */
}
/*
* Special handling for Crosstalk Switches (e.g. xbow).
* We need to do things in roughly the following order:
* 1) Initialize xswitch hardware (done above)
* 2) Determine which hubs are available to be widget masters
* 3) Discover which links are active from the xswitch
* 4) Assign xwidgets hanging off the xswitch to hubs
* 5) Initialize all xwidgets on the xswitch
*/
volunteer_for_widgets(switchv, hubv);
/* If there's someone else on this crossbow, recognize him */
if (npdap->xbow_peer != INVALID_NASID) {
nodepda_t *peer_npdap = NODEPDA(NASID_TO_COMPACT_NODEID(npdap->xbow_peer));
peer_sema = &peer_npdap->xbow_sema;
volunteer_for_widgets(switchv, peer_npdap->node_vertex);
}
assign_widgets_to_volunteers(switchv, hubv);
/* Signal that we're done */
if (peer_sema) {
mutex_unlock(peer_sema);
}
}
else {
/* Wait 'til master is done assigning widgets. */
mutex_lock(&npdap->xbow_sema);
}
#ifdef PROBE_TEST
if ((cnodeid == 1) || (cnodeid == 2)) {
int index;
for (index = 0; index < 500; index++)
DBG("Interfering with device probing!!!\n");
}
#endif
/* Now both nodes can safely inititialize widgets */
io_init_xswitch_widgets(switchv, cnodeid);
io_link_xswitch_widgets(switchv, cnodeid);
/* io_init_done takes cpu cookie as 2nd argument
* to do a restorenoderun for the setnoderun done
* at the start of this thread
*/
io_init_done(cnodeid,c);
DBG("\nio_init_node: DONE INITIALIZED ALL I/O FOR CNODEID %d\n\n", cnodeid);
}
#define IOINIT_STKSZ (16 * 1024)
#define __DEVSTR1 "/../.master/"
#define __DEVSTR2 "/target/"
#define __DEVSTR3 "/lun/0/disk/partition/"
#define __DEVSTR4 "/../ef"
/*
* ioconfig starts numbering SCSI's at NUM_BASE_IO_SCSI_CTLR.
*/
#define NUM_BASE_IO_SCSI_CTLR 6
/*
* This tells ioconfig where it can start numbering scsi controllers.
* Below this base number, platform-specific handles the numbering.
* XXX Irix legacy..controller numbering should be part of devfsd's job
*/
int num_base_io_scsi_ctlr = 2; /* used by syssgi */
devfs_handle_t base_io_scsi_ctlr_vhdl[NUM_BASE_IO_SCSI_CTLR];
static devfs_handle_t baseio_enet_vhdl,baseio_console_vhdl;
/*
* Put the logical controller number information in the
* scsi controller vertices for each scsi controller that
* is in a "fixed position".
*/
static void
scsi_ctlr_nums_add(devfs_handle_t pci_vhdl)
{
{
int i;
num_base_io_scsi_ctlr = NUM_BASE_IO_SCSI_CTLR;
/* Initialize base_io_scsi_ctlr_vhdl array */
for (i=0; i<num_base_io_scsi_ctlr; i++)
base_io_scsi_ctlr_vhdl[i] = GRAPH_VERTEX_NONE;
}
{
/*
* May want to consider changing the SN0 code, above, to work more like
* the way this works.
*/
devfs_handle_t base_ibrick_xbridge_vhdl;
devfs_handle_t base_ibrick_xtalk_widget_vhdl;
devfs_handle_t scsi_ctlr_vhdl;
int i;
graph_error_t rv;
/*
* This is a table of "well-known" SCSI controllers and their well-known
* controller numbers. The names in the table start from the base IBrick's
* Xbridge vertex, so the first component is the xtalk widget number.
*/
static struct {
char *base_ibrick_scsi_path;
int controller_number;
} hardwired_scsi_controllers[] = {
{"15/" EDGE_LBL_PCI "/1/" EDGE_LBL_SCSI_CTLR "/0", 0},
{"15/" EDGE_LBL_PCI "/2/" EDGE_LBL_SCSI_CTLR "/0", 1},
{"15/" EDGE_LBL_PCI "/3/" EDGE_LBL_SCSI_CTLR "/0", 2},
{"14/" EDGE_LBL_PCI "/1/" EDGE_LBL_SCSI_CTLR "/0", 3},
{"14/" EDGE_LBL_PCI "/2/" EDGE_LBL_SCSI_CTLR "/0", 4},
{"15/" EDGE_LBL_PCI "/6/ohci/0/" EDGE_LBL_SCSI_CTLR "/0", 5},
{NULL, -1} /* must be last */
};
base_ibrick_xtalk_widget_vhdl = hwgraph_connectpt_get(pci_vhdl);
ASSERT_ALWAYS(base_ibrick_xtalk_widget_vhdl != GRAPH_VERTEX_NONE);
base_ibrick_xbridge_vhdl = hwgraph_connectpt_get(base_ibrick_xtalk_widget_vhdl);
ASSERT_ALWAYS(base_ibrick_xbridge_vhdl != GRAPH_VERTEX_NONE);
hwgraph_vertex_unref(base_ibrick_xtalk_widget_vhdl);
/*
* Iterate through the list of well-known SCSI controllers.
* For each controller found, set it's controller number according
* to the table.
*/
for (i=0; hardwired_scsi_controllers[i].base_ibrick_scsi_path != NULL; i++) {
rv = hwgraph_path_lookup(base_ibrick_xbridge_vhdl,
hardwired_scsi_controllers[i].base_ibrick_scsi_path, &scsi_ctlr_vhdl, NULL);
if (rv != GRAPH_SUCCESS) /* No SCSI at this path */
continue;
ASSERT(hardwired_scsi_controllers[i].controller_number < NUM_BASE_IO_SCSI_CTLR);
base_io_scsi_ctlr_vhdl[hardwired_scsi_controllers[i].controller_number] = scsi_ctlr_vhdl;
device_controller_num_set(scsi_ctlr_vhdl, hardwired_scsi_controllers[i].controller_number);
hwgraph_vertex_unref(scsi_ctlr_vhdl); /* (even though we're actually keeping a reference) */
}
hwgraph_vertex_unref(base_ibrick_xbridge_vhdl);
}
}
#include <asm/sn/ioerror_handling.h>
devfs_handle_t sys_critical_graph_root = GRAPH_VERTEX_NONE;
/* Define the system critical vertices and connect them through
* a canonical parent-child relationships for easy traversal
* during io error handling.
*/
static void
sys_critical_graph_init(void)
{
devfs_handle_t bridge_vhdl,master_node_vhdl;
devfs_handle_t xbow_vhdl = GRAPH_VERTEX_NONE;
extern devfs_handle_t hwgraph_root;
devfs_handle_t pci_slot_conn;
int slot;
devfs_handle_t baseio_console_conn;
DBG("sys_critical_graph_init: FIXME.\n");
baseio_console_conn = hwgraph_connectpt_get(baseio_console_vhdl);
if (baseio_console_conn == NULL) {
return;
}
/* Get the vertex handle for the baseio bridge */
bridge_vhdl = device_master_get(baseio_console_conn);
/* Get the master node of the baseio card */
master_node_vhdl = cnodeid_to_vertex(
master_node_get(baseio_console_vhdl));
/* Add the "root->node" part of the system critical graph */
sys_critical_graph_vertex_add(hwgraph_root,master_node_vhdl);
/* Check if we have a crossbow */
if (hwgraph_traverse(master_node_vhdl,
EDGE_LBL_XTALK"/0",
&xbow_vhdl) == GRAPH_SUCCESS) {
/* We have a crossbow.Add "node->xbow" part of the system
* critical graph.
*/
sys_critical_graph_vertex_add(master_node_vhdl,xbow_vhdl);
/* Add "xbow->baseio bridge" of the system critical graph */
sys_critical_graph_vertex_add(xbow_vhdl,bridge_vhdl);
hwgraph_vertex_unref(xbow_vhdl);
} else
/* We donot have a crossbow. Add "node->baseio_bridge"
* part of the system critical graph.
*/
sys_critical_graph_vertex_add(master_node_vhdl,bridge_vhdl);
/* Add all the populated PCI slot vertices to the system critical
* graph with the bridge vertex as the parent.
*/
for (slot = 0 ; slot < 8; slot++) {
char slot_edge[10];
sprintf(slot_edge,"%d",slot);
if (hwgraph_traverse(bridge_vhdl,slot_edge, &pci_slot_conn)
!= GRAPH_SUCCESS)
continue;
sys_critical_graph_vertex_add(bridge_vhdl,pci_slot_conn);
hwgraph_vertex_unref(pci_slot_conn);
}
hwgraph_vertex_unref(bridge_vhdl);
/* Add the "ioc3 pci connection point -> console ioc3" part
* of the system critical graph
*/
if (hwgraph_traverse(baseio_console_vhdl,"..",&pci_slot_conn) ==
GRAPH_SUCCESS) {
sys_critical_graph_vertex_add(pci_slot_conn,
baseio_console_vhdl);
hwgraph_vertex_unref(pci_slot_conn);
}
/* Add the "ethernet pci connection point -> base ethernet" part of
* the system critical graph
*/
if (hwgraph_traverse(baseio_enet_vhdl,"..",&pci_slot_conn) ==
GRAPH_SUCCESS) {
sys_critical_graph_vertex_add(pci_slot_conn,
baseio_enet_vhdl);
hwgraph_vertex_unref(pci_slot_conn);
}
/* Add the "scsi controller pci connection point -> base scsi
* controller" part of the system critical graph
*/
if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[0],
"../..",&pci_slot_conn) == GRAPH_SUCCESS) {
sys_critical_graph_vertex_add(pci_slot_conn,
base_io_scsi_ctlr_vhdl[0]);
hwgraph_vertex_unref(pci_slot_conn);
}
if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[1],
"../..",&pci_slot_conn) == GRAPH_SUCCESS) {
sys_critical_graph_vertex_add(pci_slot_conn,
base_io_scsi_ctlr_vhdl[1]);
hwgraph_vertex_unref(pci_slot_conn);
}
hwgraph_vertex_unref(baseio_console_conn);
}
static void
baseio_ctlr_num_set(void)
{
char name[MAXDEVNAME];
devfs_handle_t console_vhdl, pci_vhdl, enet_vhdl;
devfs_handle_t ioc3_console_vhdl_get(void);
DBG("baseio_ctlr_num_set; FIXME\n");
console_vhdl = ioc3_console_vhdl_get();
if (console_vhdl == GRAPH_VERTEX_NONE)
return;
/* Useful for setting up the system critical graph */
baseio_console_vhdl = console_vhdl;
vertex_to_name(console_vhdl,name,MAXDEVNAME);
strcat(name,__DEVSTR1);
pci_vhdl = hwgraph_path_to_vertex(name);
scsi_ctlr_nums_add(pci_vhdl);
/* Unref the pci_vhdl due to the reference by hwgraph_path_to_vertex
*/
hwgraph_vertex_unref(pci_vhdl);
vertex_to_name(console_vhdl, name, MAXDEVNAME);
strcat(name, __DEVSTR4);
enet_vhdl = hwgraph_path_to_vertex(name);
/* Useful for setting up the system critical graph */
baseio_enet_vhdl = enet_vhdl;
device_controller_num_set(enet_vhdl, 0);
/* Unref the enet_vhdl due to the reference by hwgraph_path_to_vertex
*/
hwgraph_vertex_unref(enet_vhdl);
}
/* #endif */
/*
* Initialize all I/O devices. Starting closest to nodes, probe and
* initialize outward.
*/
void
init_all_devices(void)
{
/* Governor on init threads..bump up when safe
* (beware many devfs races)
*/
cnodeid_t cnodeid, active;
active = 0;
for (cnodeid = 0; cnodeid < numnodes; cnodeid++) {
DBG("init_all_devices: Calling io_init_node() for cnode %d\n", cnodeid);
io_init_node(cnodeid);
DBG("init_all_devices: Done io_init_node() for cnode %d\n", cnodeid);
}
for (cnodeid = 0; cnodeid < numnodes; cnodeid++)
/*
* Update information generated by IO init.
*/
update_node_information(cnodeid);
baseio_ctlr_num_set();
/* Setup the system critical graph (which is a subgraph of the
* main hwgraph). This information is useful during io error
* handling.
*/
sys_critical_graph_init();
#if HWG_PRINT
hwgraph_print();
#endif
}
#define toint(x) ((int)(x) - (int)('0'))
void
devnamefromarcs(char *devnm)
{
int val;
char tmpnm[MAXDEVNAME];
char *tmp1, *tmp2;
val = strncmp(devnm, "dks", 3);
if (val != 0)
return;
tmp1 = devnm + 3;
if (!isdigit(*tmp1))
return;
val = 0;
while (isdigit(*tmp1)) {
val = 10*val+toint(*tmp1);
tmp1++;
}
if(*tmp1 != 'd')
return;
else
tmp1++;
if ((val < 0) || (val >= num_base_io_scsi_ctlr)) {
int i;
int viable_found = 0;
DBG("Only controller numbers 0..%d are supported for\n", NUM_BASE_IO_SCSI_CTLR-1);
DBG("prom \"root\" variables of the form dksXdXsX.\n");
DBG("To use another disk you must use the full hardware graph path\n\n");
DBG("Possible controller numbers for use in 'dksXdXsX' on this system: ");
for (i=0; i<NUM_BASE_IO_SCSI_CTLR; i++) {
if (base_io_scsi_ctlr_vhdl[i] != GRAPH_VERTEX_NONE) {
DBG("%d ", i);
viable_found=1;
}
}
if (viable_found)
DBG("\n");
else
DBG("none found!\n");
DELAY(15000000);
//prom_reboot();
panic("FIXME: devnamefromarcs: should call prom_reboot here.\n");
/* NOTREACHED */
}
ASSERT(base_io_scsi_ctlr_vhdl[val] != GRAPH_VERTEX_NONE);
vertex_to_name(base_io_scsi_ctlr_vhdl[val],
tmpnm,
MAXDEVNAME);
tmp2 = tmpnm + strlen(tmpnm);
strcpy(tmp2, __DEVSTR2);
tmp2 += strlen(__DEVSTR2);
while (*tmp1 != 's') {
if((*tmp2++ = *tmp1++) == '\0')
return;
}
tmp1++;
strcpy(tmp2, __DEVSTR3);
tmp2 += strlen(__DEVSTR3);
while ( (*tmp2++ = *tmp1++) )
;
tmp2--;
*tmp2++ = '/';
strcpy(tmp2, EDGE_LBL_BLOCK);
strcpy(devnm,tmpnm);
}
static
struct io_brick_map_s io_brick_tab[] = {
/* Ibrick widget number to PCI bus number map */
{ MODULE_IBRICK, /* Ibrick type */
/* PCI Bus # Widget # */
{ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
0, /* 0x8 */
0, /* 0x9 */
0, 0, /* 0xa - 0xb */
0, /* 0xc */
0, /* 0xd */
2, /* 0xe */
1 /* 0xf */
}
},
/* Pbrick widget number to PCI bus number map */
{ MODULE_PBRICK, /* Pbrick type */
/* PCI Bus # Widget # */
{ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
2, /* 0x8 */
1, /* 0x9 */
0, 0, /* 0xa - 0xb */
4, /* 0xc */
6, /* 0xd */
3, /* 0xe */
5 /* 0xf */
}
},
/* PXbrick widget number to PCI bus number map */
{ MODULE_PXBRICK, /* PXbrick type */
/* PCI Bus # Widget # */
{ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
0, /* 0x8 */
0, /* 0x9 */
0, 0, /* 0xa - 0xb */
1, /* 0xc */
5, /* 0xd */
0, /* 0xe */
3 /* 0xf */
}
},
/* Xbrick widget to XIO slot map */
{ MODULE_XBRICK, /* Xbrick type */
/* XIO Slot # Widget # */
{ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
1, /* 0x8 */
3, /* 0x9 */
0, 0, /* 0xa - 0xb */
2, /* 0xc */
4, /* 0xd */
0, /* 0xe */
0 /* 0xf */
}
}
};
/*
* Use the brick's type to map a widget number to a meaningful int
*/
int
io_brick_map_widget(int brick_type, int widget_num)
{
int num_bricks, i;
/* Calculate number of bricks in table */
num_bricks = sizeof(io_brick_tab)/sizeof(io_brick_tab[0]);
/* Look for brick prefix in table */
for (i = 0; i < num_bricks; i++) {
if (brick_type == io_brick_tab[i].ibm_type)
return(io_brick_tab[i].ibm_map_wid[widget_num]);
}
return 0;
}
/*
* Use the device's vertex to map the device's widget to a meaningful int
*/
int
io_path_map_widget(devfs_handle_t vertex)
{
char hw_path_name[MAXDEVNAME];
char *wp, *bp, *sp = NULL;
int widget_num;
long atoi(char *);
int hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen);
/* Get the full path name of the vertex */
if (GRAPH_SUCCESS != hwgraph_vertex_name_get(vertex, hw_path_name,
MAXDEVNAME))
return 0;
/* Find the widget number in the path name */
wp = strstr(hw_path_name, "/"EDGE_LBL_XTALK"/");
if (wp == NULL)
return 0;
widget_num = atoi(wp+7);
if (widget_num < XBOW_PORT_8 || widget_num > XBOW_PORT_F)
return 0;
/* Find "brick" in the path name */
bp = strstr(hw_path_name, "brick");
if (bp == NULL)
return 0;
/* Find preceding slash */
sp = bp;
while (sp > hw_path_name) {
sp--;
if (*sp == '/')
break;
}
/* Invalid if no preceding slash */
if (!sp)
return 0;
/* Bump slash pointer to "brick" prefix */
sp++;
/*
* Verify "brick" prefix length; valid exaples:
* 'I' from "/Ibrick"
* 'P' from "/Pbrick"
* 'X' from "/Xbrick"
*/
if ((bp - sp) != 1)
return 0;
return (io_brick_map_widget((int)*sp, widget_num));
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/io.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/xtalk/xbow.h>
#include <asm/sn/pci/bridge.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn1/hubdev.h>
#include <asm/sn/module.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xswitch.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/sn_cpuid.h>
/* #define LDEBUG 1 */
#ifdef LDEBUG
#define DPRINTF printk
#define printf printk
#else
#define DPRINTF(x...)
#endif
module_t *modules[MODULE_MAX];
int nummodules;
#define SN00_SERIAL_FUDGE 0x3b1af409d513c2
#define SN0_SERIAL_FUDGE 0x6e
void
encode_int_serial(uint64_t src,uint64_t *dest)
{
uint64_t val;
int i;
val = src + SN00_SERIAL_FUDGE;
for (i = 0; i < sizeof(long long); i++) {
((char*)dest)[i] =
((char*)&val)[sizeof(long long)/2 +
((i%2) ? ((i/2 * -1) - 1) : (i/2))];
}
}
void
decode_int_serial(uint64_t src, uint64_t *dest)
{
uint64_t val;
int i;
for (i = 0; i < sizeof(long long); i++) {
((char*)&val)[sizeof(long long)/2 +
((i%2) ? ((i/2 * -1) - 1) : (i/2))] =
((char*)&src)[i];
}
*dest = val - SN00_SERIAL_FUDGE;
}
void
encode_str_serial(const char *src, char *dest)
{
int i;
for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++) {
dest[i] = src[MAX_SERIAL_NUM_SIZE/2 +
((i%2) ? ((i/2 * -1) - 1) : (i/2))] +
SN0_SERIAL_FUDGE;
}
}
void
decode_str_serial(const char *src, char *dest)
{
int i;
for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++) {
dest[MAX_SERIAL_NUM_SIZE/2 +
((i%2) ? ((i/2 * -1) - 1) : (i/2))] = src[i] -
SN0_SERIAL_FUDGE;
}
}
module_t *module_lookup(moduleid_t id)
{
int i;
for (i = 0; i < nummodules; i++)
if (modules[i]->id == id) {
DPRINTF("module_lookup: found m=0x%p\n", modules[i]);
return modules[i];
}
return NULL;
}
/*
* module_add_node
*
* The first time a new module number is seen, a module structure is
* inserted into the module list in order sorted by module number
* and the structure is initialized.
*
* The node number is added to the list of nodes in the module.
*/
module_t *module_add_node(geoid_t geoid, cnodeid_t cnodeid)
{
module_t *m;
int i;
char buffer[16];
moduleid_t moduleid;
memset(buffer, 0, 16);
moduleid = geo_module(geoid);
format_module_id(buffer, moduleid, MODULE_FORMAT_BRIEF);
DPRINTF("module_add_node: moduleid=%s node=%d\n", buffer, cnodeid);
if ((m = module_lookup(moduleid)) == 0) {
m = kmalloc(sizeof (module_t), GFP_KERNEL);
memset(m, 0 , sizeof(module_t));
ASSERT_ALWAYS(m);
m->id = moduleid;
spin_lock_init(&m->lock);
mutex_init_locked(&m->thdcnt);
/* Insert in sorted order by module number */
for (i = nummodules; i > 0 && modules[i - 1]->id > moduleid; i--)
modules[i] = modules[i - 1];
modules[i] = m;
nummodules++;
}
m->nodes[m->nodecnt] = cnodeid;
m->geoid[m->nodecnt] = geoid;
m->nodecnt++;
DPRINTF("module_add_node: module %s now has %d nodes\n", buffer, m->nodecnt);
return m;
}
int module_probe_snum(module_t *m, nasid_t nasid)
{
lboard_t *board;
klmod_serial_num_t *comp;
char * bcopy(const char * src, char * dest, int count);
char serial_number[16];
/*
* record brick serial number
*/
board = find_lboard((lboard_t *) KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
if (! board || KL_CONFIG_DUPLICATE_BOARD(board))
{
#if LDEBUG
printf ("module_probe_snum: no IP35 board found!\n");
#endif
return 0;
}
board_serial_number_get( board, serial_number );
if( serial_number[0] != '\0' ) {
encode_str_serial( serial_number, m->snum.snum_str );
m->snum_valid = 1;
}
#if LDEBUG
else {
printf("module_probe_snum: brick serial number is null!\n");
}
printf("module_probe_snum: brick serial number == %s\n", serial_number);
#endif /* DEBUG */
board = find_lboard((lboard_t *) KL_CONFIG_INFO(nasid),
KLTYPE_IOBRICK_XBOW);
if (! board || KL_CONFIG_DUPLICATE_BOARD(board))
return 0;
comp = GET_SNUM_COMP(board);
if (comp) {
#if LDEBUG
int i;
printf("********found module with id %x and string", m->id);
for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++)
printf(" %x ", comp->snum.snum_str[i]);
printf("\n"); /* Fudged string is not ASCII */
#endif
if (comp->snum.snum_str[0] != '\0') {
bcopy(comp->snum.snum_str,
m->sys_snum,
MAX_SERIAL_NUM_SIZE);
m->sys_snum_valid = 1;
}
}
if (m->sys_snum_valid)
return 1;
else {
DPRINTF("Invalid serial number for module %d, "
"possible missing or invalid NIC.", m->id);
return 0;
}
}
void
io_module_init(void)
{
cnodeid_t node;
lboard_t *board;
nasid_t nasid;
int nserial;
module_t *m;
DPRINTF("*******module_init\n");
nserial = 0;
for (node = 0; node < numnodes; node++) {
nasid = COMPACT_TO_NASID_NODEID(node);
board = find_lboard((lboard_t *) KL_CONFIG_INFO(nasid), KLTYPE_SNIA);
ASSERT(board);
m = module_add_node(board->brd_geoid, node);
if (! m->snum_valid && module_probe_snum(m, nasid))
nserial++;
}
DPRINTF("********found total of %d serial numbers in the system\n",
nserial);
if (nserial == 0)
DPRINTF(KERN_WARNING "io_module_init: No serial number found.\n");
}
int
get_kmod_info(cmoduleid_t cmod, module_info_t *mod_info)
{
if (cmod < 0 || cmod >= nummodules)
return -EINVAL;
mod_info->mod_num = modules[cmod]->id;
ia64_sn_sys_serial_get(mod_info->serial_str);
mod_info->serial_num = ia64_sn_partition_serial_get();
return 0;
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <asm/sn/types.h>
#include <asm/sn/hack.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/driver.h>
#include <asm/sn/iograph.h>
#include <asm/param.h>
#include <asm/sn/pio.h>
#include <asm/sn/xtalk/xwidget.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/addrs.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/intr.h>
#include <asm/sn/xtalk/xtalkaddrs.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/nodepda.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/pci/pci_bus_cvlink.h>
#include <asm/sn/simulator.h>
#include <asm/sn/sn_cpuid.h>
extern int bridge_rev_b_data_check_disable;
devfs_handle_t busnum_to_pcibr_vhdl[MAX_PCI_XWIDGET];
nasid_t busnum_to_nid[MAX_PCI_XWIDGET];
void * busnum_to_atedmamaps[MAX_PCI_XWIDGET];
unsigned char num_bridges;
static int done_probing = 0;
static int pci_bus_map_create(devfs_handle_t xtalk, char * io_moduleid);
devfs_handle_t devfn_to_vertex(unsigned char busnum, unsigned int devfn);
extern unsigned char Is_pic_on_this_nasid[512];
extern void sn_init_irq_desc(void);
extern void register_pcibr_intr(int irq, pcibr_intr_t intr);
/*
* For the given device, initialize whether it is a PIC device.
*/
static void
set_isPIC(struct sn_device_sysdata *device_sysdata)
{
pciio_info_t pciio_info = pciio_info_get(device_sysdata->vhdl);
pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
device_sysdata->isPIC = IS_PIC_SOFT(pcibr_soft);;
}
/*
* pci_bus_cvlink_init() - To be called once during initialization before
* SGI IO Infrastructure init is called.
*/
void
pci_bus_cvlink_init(void)
{
extern void ioconfig_bus_init(void);
memset(busnum_to_pcibr_vhdl, 0x0, sizeof(devfs_handle_t) * MAX_PCI_XWIDGET);
memset(busnum_to_nid, 0x0, sizeof(nasid_t) * MAX_PCI_XWIDGET);
memset(busnum_to_atedmamaps, 0x0, sizeof(void *) * MAX_PCI_XWIDGET);
num_bridges = 0;
ioconfig_bus_init();
}
/*
* pci_bus_to_vertex() - Given a logical Linux Bus Number returns the associated
* pci bus vertex from the SGI IO Infrastructure.
*/
devfs_handle_t
pci_bus_to_vertex(unsigned char busnum)
{
devfs_handle_t pci_bus = NULL;
/*
* First get the xwidget vertex.
*/
pci_bus = busnum_to_pcibr_vhdl[busnum];
return(pci_bus);
}
/*
* devfn_to_vertex() - returns the vertex of the device given the bus, slot,
* and function numbers.
*/
devfs_handle_t
devfn_to_vertex(unsigned char busnum, unsigned int devfn)
{
int slot = 0;
int func = 0;
char name[16];
devfs_handle_t pci_bus = NULL;
devfs_handle_t device_vertex = (devfs_handle_t)NULL;
/*
* Go get the pci bus vertex.
*/
pci_bus = pci_bus_to_vertex(busnum);
if (!pci_bus) {
/*
* During probing, the Linux pci code invents non existant
* bus numbers and pci_dev structures and tries to access
* them to determine existance. Don't crib during probing.
*/
if (done_probing)
printk("devfn_to_vertex: Invalid bus number %d given.\n", busnum);
return(NULL);
}
/*
* Go get the slot&function vertex.
* Should call pciio_slot_func_to_name() when ready.
*/
slot = PCI_SLOT(devfn);
func = PCI_FUNC(devfn);
/*
* For a NON Multi-function card the name of the device looks like:
* ../pci/1, ../pci/2 ..
*/
if (func == 0) {
sprintf(name, "%d", slot);
if (hwgraph_traverse(pci_bus, name, &device_vertex) ==
GRAPH_SUCCESS) {
if (device_vertex) {
return(device_vertex);
}
}
}
/*
* This maybe a multifunction card. It's names look like:
* ../pci/1a, ../pci/1b, etc.
*/
sprintf(name, "%d%c", slot, 'a'+func);
if (hwgraph_traverse(pci_bus, name, &device_vertex) != GRAPH_SUCCESS) {
if (!device_vertex) {
return(NULL);
}
}
return(device_vertex);
}
/*
* For the given device, initialize the addresses for both the Device(x) Flush
* Write Buffer register and the Xbow Flush Register for the port the PCI bus
* is connected.
*/
static void
set_flush_addresses(struct pci_dev *device_dev,
struct sn_device_sysdata *device_sysdata)
{
pciio_info_t pciio_info = pciio_info_get(device_sysdata->vhdl);
pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
bridge_t *bridge = pcibr_soft->bs_base;
nasid_t nasid;
/*
* Get the nasid from the bridge.
*/
nasid = NASID_GET(device_sysdata->dma_buf_sync);
if (IS_PIC_DEVICE(device_dev)) {
device_sysdata->dma_buf_sync = (volatile unsigned int *)
&bridge->b_wr_req_buf[pciio_slot].reg;
device_sysdata->xbow_buf_sync = (volatile unsigned int *)
XBOW_PRIO_LINKREGS_PTR(NODE_SWIN_BASE(nasid, 0),
pcibr_soft->bs_xid);
} else {
/*
* Accessing Xbridge and Xbow register when SHUB swapoper is on!.
*/
device_sysdata->dma_buf_sync = (volatile unsigned int *)
((uint64_t)&(bridge->b_wr_req_buf[pciio_slot].reg)^4);
device_sysdata->xbow_buf_sync = (volatile unsigned int *)
((uint64_t)(XBOW_PRIO_LINKREGS_PTR(
NODE_SWIN_BASE(nasid, 0), pcibr_soft->bs_xid)) ^ 4);
}
#ifdef DEBUG
printk("set_flush_addresses: dma_buf_sync %p xbow_buf_sync %p\n",
device_sysdata->dma_buf_sync, device_sysdata->xbow_buf_sync);
printk("set_flush_addresses: dma_buf_sync\n");
while((volatile unsigned int )*device_sysdata->dma_buf_sync);
printk("set_flush_addresses: xbow_buf_sync\n");
while((volatile unsigned int )*device_sysdata->xbow_buf_sync);
#endif
}
/*
* Most drivers currently do not properly tell the arch specific pci dma
* interfaces whether they can handle A64. Here is where we privately
* keep track of this.
*/
static void __init
set_sn_pci64(struct pci_dev *dev)
{
unsigned short vendor = dev->vendor;
unsigned short device = dev->device;
if (vendor == PCI_VENDOR_ID_QLOGIC) {
if ((device == PCI_DEVICE_ID_QLOGIC_ISP2100) ||
(device == PCI_DEVICE_ID_QLOGIC_ISP2200)) {
SET_PCIA64(dev);
return;
}
}
if (vendor == PCI_VENDOR_ID_SGI) {
if (device == PCI_DEVICE_ID_SGI_IOC3) {
SET_PCIA64(dev);
return;
}
}
}
/*
* sn_pci_fixup() - This routine is called when platform_pci_fixup() is
* invoked at the end of pcibios_init() to link the Linux pci
* infrastructure to SGI IO Infrasturcture - ia64/kernel/pci.c
*
* Other platform specific fixup can also be done here.
*/
void
sn_pci_fixup(int arg)
{
struct list_head *ln;
struct pci_bus *pci_bus = NULL;
struct pci_dev *device_dev = NULL;
struct sn_widget_sysdata *widget_sysdata;
struct sn_device_sysdata *device_sysdata;
pciio_intr_t intr_handle;
int cpuid, bit;
devfs_handle_t device_vertex;
pciio_intr_line_t lines;
extern void sn_pci_find_bios(void);
extern int numnodes;
int cnode;
extern void io_sh_swapper(int, int);
for (cnode = 0; cnode < numnodes; cnode++) {
if ( !Is_pic_on_this_nasid[cnodeid_to_nasid(cnode)] )
io_sh_swapper((cnodeid_to_nasid(cnode)), 0);
}
if (arg == 0) {
#ifdef CONFIG_PROC_FS
extern void register_sn_procfs(void);
#endif
sn_init_irq_desc();
sn_pci_find_bios();
for (cnode = 0; cnode < numnodes; cnode++) {
extern void intr_init_vecblk(nodepda_t *npda, cnodeid_t, int);
intr_init_vecblk(NODEPDA(cnode), cnode, 0);
}
/*
* When we return to generic Linux, Swapper is always on ..
*/
for (cnode = 0; cnode < numnodes; cnode++) {
if ( !Is_pic_on_this_nasid[cnodeid_to_nasid(cnode)] )
io_sh_swapper((cnodeid_to_nasid(cnode)), 1);
}
#ifdef CONFIG_PROC_FS
register_sn_procfs();
#endif
return;
}
done_probing = 1;
/*
* Initialize the pci bus vertex in the pci_bus struct.
*/
for( ln = pci_root_buses.next; ln != &pci_root_buses; ln = ln->next) {
pci_bus = pci_bus_b(ln);
widget_sysdata = kmalloc(sizeof(struct sn_widget_sysdata),
GFP_KERNEL);
widget_sysdata->vhdl = pci_bus_to_vertex(pci_bus->number);
pci_bus->sysdata = (void *)widget_sysdata;
}
/*
* set the root start and end so that drivers calling check_region()
* won't see a conflict
*/
ioport_resource.start = 0xc000000000000000;
ioport_resource.end = 0xcfffffffffffffff;
/*
* Set the root start and end for Mem Resource.
*/
iomem_resource.start = 0;
iomem_resource.end = 0xffffffffffffffff;
/*
* Initialize the device vertex in the pci_dev struct.
*/
pci_for_each_dev(device_dev) {
unsigned int irq;
int idx;
u16 cmd;
devfs_handle_t vhdl;
unsigned long size;
extern int bit_pos_to_irq(int);
if (device_dev->vendor == PCI_VENDOR_ID_SGI &&
device_dev->device == PCI_DEVICE_ID_SGI_IOC3) {
extern void pci_fixup_ioc3(struct pci_dev *d);
pci_fixup_ioc3(device_dev);
}
/* Set the device vertex */
device_sysdata = kmalloc(sizeof(struct sn_device_sysdata),
GFP_KERNEL);
device_sysdata->vhdl = devfn_to_vertex(device_dev->bus->number, device_dev->devfn);
device_sysdata->isa64 = 0;
/*
* Set the xbridge Device(X) Write Buffer Flush and Xbow Flush
* register addresses.
*/
(void) set_flush_addresses(device_dev, device_sysdata);
device_dev->sysdata = (void *) device_sysdata;
set_sn_pci64(device_dev);
set_isPIC(device_sysdata);
pci_read_config_word(device_dev, PCI_COMMAND, &cmd);
/*
* Set the resources address correctly. The assumption here
* is that the addresses in the resource structure has been
* read from the card and it was set in the card by our
* Infrastructure ..
*/
vhdl = device_sysdata->vhdl;
for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {
size = 0;
size = device_dev->resource[idx].end -
device_dev->resource[idx].start;
if (size) {
device_dev->resource[idx].start = (unsigned long)pciio_pio_addr(vhdl, 0, PCIIO_SPACE_WIN(idx), 0, size, 0, (IS_PIC_DEVICE(device_dev)) ? 0 : PCIIO_BYTE_STREAM);
device_dev->resource[idx].start |= __IA64_UNCACHED_OFFSET;
}
else
continue;
device_dev->resource[idx].end =
device_dev->resource[idx].start + size;
if (device_dev->resource[idx].flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (device_dev->resource[idx].flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
#if 0
/*
* Software WAR for a Software BUG.
* This is only temporary.
* See PV 872791
*/
/*
* Now handle the ROM resource ..
*/
size = device_dev->resource[PCI_ROM_RESOURCE].end -
device_dev->resource[PCI_ROM_RESOURCE].start;
if (size) {
device_dev->resource[PCI_ROM_RESOURCE].start =
(unsigned long) pciio_pio_addr(vhdl, 0, PCIIO_SPACE_ROM, 0,
size, 0, (IS_PIC_DEVICE(device_dev)) ? 0 : PCIIO_BYTE_STREAM);
device_dev->resource[PCI_ROM_RESOURCE].start |= __IA64_UNCACHED_OFFSET;
device_dev->resource[PCI_ROM_RESOURCE].end =
device_dev->resource[PCI_ROM_RESOURCE].start + size;
}
#endif
/*
* Update the Command Word on the Card.
*/
cmd |= PCI_COMMAND_MASTER; /* If the device doesn't support */
/* bit gets dropped .. no harm */
pci_write_config_word(device_dev, PCI_COMMAND, cmd);
pci_read_config_byte(device_dev, PCI_INTERRUPT_PIN, (unsigned char *)&lines);
if (device_dev->vendor == PCI_VENDOR_ID_SGI &&
device_dev->device == PCI_DEVICE_ID_SGI_IOC3 ) {
lines = 1;
}
device_sysdata = (struct sn_device_sysdata *)device_dev->sysdata;
device_vertex = device_sysdata->vhdl;
intr_handle = pciio_intr_alloc(device_vertex, NULL, lines, device_vertex);
bit = intr_handle->pi_irq;
cpuid = intr_handle->pi_cpu;
irq = bit;
irq = irq + (cpuid << 8);
pciio_intr_connect(intr_handle, (intr_func_t)0, (intr_arg_t)0);
device_dev->irq = irq;
register_pcibr_intr(irq, (pcibr_intr_t)intr_handle);
#ifdef ajmtestintr
{
int slot = PCI_SLOT(device_dev->devfn);
static int timer_set = 0;
pcibr_intr_t pcibr_intr = (pcibr_intr_t)intr_handle;
pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
extern void intr_test_handle_intr(int, void*, struct pt_regs *);
if (!timer_set) {
intr_test_set_timer();
timer_set = 1;
}
intr_test_register_irq(irq, pcibr_soft, slot);
request_irq(irq, intr_test_handle_intr,0,NULL, NULL);
}
#endif
}
for (cnode = 0; cnode < numnodes; cnode++) {
if ( !Is_pic_on_this_nasid[cnodeid_to_nasid(cnode)] )
io_sh_swapper((cnodeid_to_nasid(cnode)), 1);
}
}
/*
* linux_bus_cvlink() Creates a link between the Linux PCI Bus number
* to the actual hardware component that it represents:
* /dev/hw/linux/busnum/0 -> ../../../hw/module/001c01/slab/0/Ibrick/xtalk/15/pci
*
* The bus vertex, when called to devfs_generate_path() returns:
* hw/module/001c01/slab/0/Ibrick/xtalk/15/pci
* hw/module/001c01/slab/1/Pbrick/xtalk/12/pci-x/0
* hw/module/001c01/slab/1/Pbrick/xtalk/12/pci-x/1
*/
void
linux_bus_cvlink(void)
{
char name[8];
int index;
for (index=0; index < MAX_PCI_XWIDGET; index++) {
if (!busnum_to_pcibr_vhdl[index])
continue;
sprintf(name, "%x", index);
(void) hwgraph_edge_add(linux_busnum, busnum_to_pcibr_vhdl[index],
name);
}
}
/*
* pci_bus_map_create() - Called by pci_bus_to_hcl_cvlink() to finish the job.
*
* Linux PCI Bus numbers are assigned from lowest module_id numbers
* (rack/slot etc.) starting from HUB_WIDGET_ID_MAX down to
* HUB_WIDGET_ID_MIN:
* widgetnum 15 gets lower Bus Number than widgetnum 14 etc.
*
* Given 2 modules 001c01 and 001c02 we get the following mappings:
* 001c01, widgetnum 15 = Bus number 0
* 001c01, widgetnum 14 = Bus number 1
* 001c02, widgetnum 15 = Bus number 3
* 001c02, widgetnum 14 = Bus number 4
* etc.
*
* The rational for starting Bus Number 0 with Widget number 15 is because
* the system boot disks are always connected via Widget 15 Slot 0 of the
* I-brick. Linux creates /dev/sd* devices(naming) strating from Bus Number 0
* Therefore, /dev/sda1 will be the first disk, on Widget 15 of the lowest
* module id(Master Cnode) of the system.
*
*/
static int
pci_bus_map_create(devfs_handle_t xtalk, char * io_moduleid)
{
devfs_handle_t master_node_vertex = NULL;
devfs_handle_t xwidget = NULL;
devfs_handle_t pci_bus = NULL;
hubinfo_t hubinfo = NULL;
xwidgetnum_t widgetnum;
char pathname[128];
graph_error_t rv;
int bus;
int basebus_num;
int bus_number;
/*
* Loop throught this vertex and get the Xwidgets ..
*/
/* PCI devices */
for (widgetnum = HUB_WIDGET_ID_MAX; widgetnum >= HUB_WIDGET_ID_MIN; widgetnum--) {
sprintf(pathname, "%d", widgetnum);
xwidget = NULL;
/*
* Example - /hw/module/001c16/Pbrick/xtalk/8 is the xwidget
* /hw/module/001c16/Pbrick/xtalk/8/pci/1 is device
*/
rv = hwgraph_traverse(xtalk, pathname, &xwidget);
if ( (rv != GRAPH_SUCCESS) ) {
if (!xwidget) {
continue;
}
}
sprintf(pathname, "%d/"EDGE_LBL_PCI, widgetnum);
pci_bus = NULL;
if (hwgraph_traverse(xtalk, pathname, &pci_bus) != GRAPH_SUCCESS)
if (!pci_bus) {
continue;
}
/*
* Assign the correct bus number and also the nasid of this
* pci Xwidget.
*
* Should not be any race here ...
*/
num_bridges++;
busnum_to_pcibr_vhdl[num_bridges - 1] = pci_bus;
/*
* Get the master node and from there get the NASID.
*/
master_node_vertex = device_master_get(xwidget);
if (!master_node_vertex) {
printk("WARNING: pci_bus_map_create: Unable to get .master for vertex 0x%p\n", (void *)xwidget);
}
hubinfo_get(master_node_vertex, &hubinfo);
if (!hubinfo) {
printk("WARNING: pci_bus_map_create: Unable to get hubinfo for master node vertex 0x%p\n", (void *)master_node_vertex);
return(1);
} else {
busnum_to_nid[num_bridges - 1] = hubinfo->h_nasid;
}
/*
* Pre assign DMA maps needed for 32 Bits Page Map DMA.
*/
busnum_to_atedmamaps[num_bridges - 1] = (void *) kmalloc(
sizeof(struct sn_dma_maps_s) * MAX_ATE_MAPS, GFP_KERNEL);
if (!busnum_to_atedmamaps[num_bridges - 1])
printk("WARNING: pci_bus_map_create: Unable to precreate ATE DMA Maps for busnum %d vertex 0x%p\n", num_bridges - 1, (void *)xwidget);
memset(busnum_to_atedmamaps[num_bridges - 1], 0x0,
sizeof(struct sn_dma_maps_s) * MAX_ATE_MAPS);
}
/*
* PCIX devices
* We number busses differently for PCI-X devices.
* We start from Lowest Widget on up ..
*/
(void) ioconfig_get_busnum((char *)io_moduleid, &basebus_num);
for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
/* Do both buses */
for ( bus = 0; bus < 2; bus++ ) {
sprintf(pathname, "%d", widgetnum);
xwidget = NULL;
/*
* Example - /hw/module/001c16/Pbrick/xtalk/8 is the xwidget
* /hw/module/001c16/Pbrick/xtalk/8/pci-x/0 is the bus
* /hw/module/001c16/Pbrick/xtalk/8/pci-x/0/1 is device
*/
rv = hwgraph_traverse(xtalk, pathname, &xwidget);
if ( (rv != GRAPH_SUCCESS) ) {
if (!xwidget) {
continue;
}
}
if ( bus == 0 )
sprintf(pathname, "%d/"EDGE_LBL_PCIX_0, widgetnum);
else
sprintf(pathname, "%d/"EDGE_LBL_PCIX_1, widgetnum);
pci_bus = NULL;
if (hwgraph_traverse(xtalk, pathname, &pci_bus) != GRAPH_SUCCESS)
if (!pci_bus) {
continue;
}
/*
* Assign the correct bus number and also the nasid of this
* pci Xwidget.
*
* Should not be any race here ...
*/
bus_number = basebus_num + bus + io_brick_map_widget(MODULE_PXBRICK, widgetnum);
#ifdef DEBUG
printk("bus_number %d basebus_num %d bus %d io %d\n",
bus_number, basebus_num, bus,
io_brick_map_widget(MODULE_PXBRICK, widgetnum));
#endif
busnum_to_pcibr_vhdl[bus_number] = pci_bus;
/*
* Pre assign DMA maps needed for 32 Bits Page Map DMA.
*/
busnum_to_atedmamaps[bus_number] = (void *) kmalloc(
sizeof(struct sn_dma_maps_s) * MAX_ATE_MAPS, GFP_KERNEL);
if (!busnum_to_atedmamaps[bus_number])
printk("WARNING: pci_bus_map_create: Unable to precreate ATE DMA Maps for busnum %d vertex 0x%p\n", num_bridges - 1, (void *)xwidget);
memset(busnum_to_atedmamaps[bus_number], 0x0,
sizeof(struct sn_dma_maps_s) * MAX_ATE_MAPS);
}
}
return(0);
}
/*
* pci_bus_to_hcl_cvlink() - This routine is called after SGI IO Infrastructure
* initialization has completed to set up the mappings between Xbridge
* and logical pci bus numbers. We also set up the NASID for each of these
* xbridges.
*
* Must be called before pci_init() is invoked.
*/
int
pci_bus_to_hcl_cvlink(void)
{
devfs_handle_t devfs_hdl = NULL;
devfs_handle_t xtalk = NULL;
int rv = 0;
char name[256];
char tmp_name[256];
int i, ii;
/*
* Figure out which IO Brick is connected to the Compute Bricks.
*/
for (i = 0; i < nummodules; i++) {
extern int iomoduleid_get(nasid_t);
moduleid_t iobrick_id;
nasid_t nasid = -1;
int nodecnt;
int n = 0;
nodecnt = modules[i]->nodecnt;
for ( n = 0; n < nodecnt; n++ ) {
nasid = cnodeid_to_nasid(modules[i]->nodes[n]);
iobrick_id = iomoduleid_get(nasid);
if ((int)iobrick_id > 0) { /* Valid module id */
char name[12];
memset(name, 0, 12);
format_module_id((char *)&(modules[i]->io[n].moduleid), iobrick_id, MODULE_FORMAT_BRIEF);
}
}
}
devfs_hdl = hwgraph_path_to_vertex("/dev/hw/module");
for (i = 0; i < nummodules ; i++) {
for ( ii = 0; ii < 2 ; ii++ ) {
memset(name, 0, 256);
memset(tmp_name, 0, 256);
format_module_id(name, modules[i]->id, MODULE_FORMAT_BRIEF);
sprintf(tmp_name, "/slab/%d/Pbrick/xtalk", geo_slab(modules[i]->geoid[ii]));
strcat(name, tmp_name);
xtalk = NULL;
rv = hwgraph_edge_get(devfs_hdl, name, &xtalk);
pci_bus_map_create(xtalk, (char *)&(modules[i]->io[ii].moduleid));
}
}
/*
* Create the Linux PCI bus number vertex link.
*/
(void)linux_bus_cvlink();
(void)ioconfig_bus_new_entries();
return(0);
}
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
#
# Makefile for the sn2 specific pci bridge routines.
EXTRA_CFLAGS := -DLITTLE_ENDIAN
ifdef CONFIG_IA64_SGI_SN2
EXTRA_CFLAGS += -DSHUB_SWAP_WAR
endif
obj-$(CONFIG_IA64_SGI_SN2) += pcibr_dvr.o pcibr_ate.o pcibr_config.o \
pcibr_dvr.o pcibr_hints.o \
pcibr_intr.o pcibr_rrb.o pcibr_slot.o \
pcibr_error.o
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#define USRPCI 0
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/xtalk/xbow.h> /* Must be before iograph.h to get MAX_PORT_NUM */
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/pci/bridge.h>
#include <asm/sn/ioerror_handling.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pciio_private.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/io.h>
#include <asm/sn/pci/pci_bus_cvlink.h>
#include <asm/sn/ate_utils.h>
#include <asm/sn/simulator.h>
#ifdef __ia64
#define rmallocmap atemapalloc
#define rmfreemap atemapfree
#define rmfree atefree
#define rmalloc atealloc
#endif
#define DEBUG_PCIIO
#undef DEBUG_PCIIO /* turn this on for yet more console output */
#define GET_NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
#define DO_DEL(ptr) (kfree(ptr))
char pciio_info_fingerprint[] = "pciio_info";
cdl_p pciio_registry = NULL;
int
badaddr_val(volatile void *addr, int len, volatile void *ptr)
{
int ret = 0;
volatile void *new_addr;
switch (len) {
case 4:
new_addr = (void *) addr;
ret = ia64_sn_probe_io_slot((long)new_addr, len, (void *)ptr);
break;
default:
printk(KERN_WARNING "badaddr_val given len %x but supports len of 4 only\n", len);
}
if (ret < 0)
panic("badaddr_val: unexpected status (%d) in probing", ret);
return(ret);
}
nasid_t
get_console_nasid(void)
{
extern nasid_t console_nasid;
extern nasid_t master_baseio_nasid;
if (console_nasid < 0) {
console_nasid = ia64_sn_get_console_nasid();
if (console_nasid < 0) {
// ZZZ What do we do if we don't get a console nasid on the hardware????
if (IS_RUNNING_ON_SIMULATOR() )
console_nasid = master_baseio_nasid;
}
}
return console_nasid;
}
nasid_t
get_master_baseio_nasid(void)
{
extern nasid_t master_baseio_nasid;
extern char master_baseio_wid;
if (master_baseio_nasid < 0) {
nasid_t tmp;
master_baseio_nasid = ia64_sn_get_master_baseio_nasid();
if ( master_baseio_nasid >= 0 ) {
master_baseio_wid = WIDGETID_GET(KL_CONFIG_CH_CONS_INFO(master_baseio_nasid)->memory_base);
}
}
return master_baseio_nasid;
}
int
hub_dma_enabled(devfs_handle_t xconn_vhdl)
{
return(0);
}
int
hub_error_devenable(devfs_handle_t xconn_vhdl, int devnum, int error_code)
{
return(0);
}
void
ioerror_dump(char *name, int error_code, int error_mode, ioerror_t *ioerror)
{
}
/******
****** end hack defines ......
******/
/* =====================================================================
* PCI Generic Bus Provider
* Implement PCI provider operations. The pciio* layer provides a
* platform-independent interface for PCI devices. This layer
* switches among the possible implementations of a PCI adapter.
*/
/* =====================================================================
* Provider Function Location SHORTCUT
*
* On platforms with only one possible PCI provider, macros can be
* set up at the top that cause the table lookups and indirections to
* completely disappear.
*/
/* =====================================================================
* Function Table of Contents
*/
#if !defined(DEV_FUNC)
static pciio_provider_t *pciio_to_provider_fns(devfs_handle_t dev);
#endif
pciio_piomap_t pciio_piomap_alloc(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, size_t, unsigned);
void pciio_piomap_free(pciio_piomap_t);
caddr_t pciio_piomap_addr(pciio_piomap_t, iopaddr_t, size_t);
void pciio_piomap_done(pciio_piomap_t);
caddr_t pciio_piotrans_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, unsigned);
caddr_t pciio_pio_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, pciio_piomap_t *, unsigned);
iopaddr_t pciio_piospace_alloc(devfs_handle_t, device_desc_t, pciio_space_t, size_t, size_t);
void pciio_piospace_free(devfs_handle_t, pciio_space_t, iopaddr_t, size_t);
pciio_dmamap_t pciio_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
void pciio_dmamap_free(pciio_dmamap_t);
iopaddr_t pciio_dmamap_addr(pciio_dmamap_t, paddr_t, size_t);
alenlist_t pciio_dmamap_list(pciio_dmamap_t, alenlist_t, unsigned);
void pciio_dmamap_done(pciio_dmamap_t);
iopaddr_t pciio_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
alenlist_t pciio_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
void pciio_dmamap_drain(pciio_dmamap_t);
void pciio_dmaaddr_drain(devfs_handle_t, paddr_t, size_t);
void pciio_dmalist_drain(devfs_handle_t, alenlist_t);
iopaddr_t pciio_dma_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, pciio_dmamap_t *, unsigned);
pciio_intr_t pciio_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);
void pciio_intr_free(pciio_intr_t);
int pciio_intr_connect(pciio_intr_t, intr_func_t, intr_arg_t);
void pciio_intr_disconnect(pciio_intr_t);
devfs_handle_t pciio_intr_cpu_get(pciio_intr_t);
void pciio_slot_func_to_name(char *, pciio_slot_t, pciio_function_t);
void pciio_provider_startup(devfs_handle_t);
void pciio_provider_shutdown(devfs_handle_t);
pciio_endian_t pciio_endian_set(devfs_handle_t, pciio_endian_t, pciio_endian_t);
pciio_priority_t pciio_priority_set(devfs_handle_t, pciio_priority_t);
devfs_handle_t pciio_intr_dev_get(pciio_intr_t);
devfs_handle_t pciio_pio_dev_get(pciio_piomap_t);
pciio_slot_t pciio_pio_slot_get(pciio_piomap_t);
pciio_space_t pciio_pio_space_get(pciio_piomap_t);
iopaddr_t pciio_pio_pciaddr_get(pciio_piomap_t);
ulong pciio_pio_mapsz_get(pciio_piomap_t);
caddr_t pciio_pio_kvaddr_get(pciio_piomap_t);
devfs_handle_t pciio_dma_dev_get(pciio_dmamap_t);
pciio_slot_t pciio_dma_slot_get(pciio_dmamap_t);
pciio_info_t pciio_info_chk(devfs_handle_t);
pciio_info_t pciio_info_get(devfs_handle_t);
void pciio_info_set(devfs_handle_t, pciio_info_t);
devfs_handle_t pciio_info_dev_get(pciio_info_t);
pciio_slot_t pciio_info_slot_get(pciio_info_t);
pciio_function_t pciio_info_function_get(pciio_info_t);
pciio_vendor_id_t pciio_info_vendor_id_get(pciio_info_t);
pciio_device_id_t pciio_info_device_id_get(pciio_info_t);
devfs_handle_t pciio_info_master_get(pciio_info_t);
arbitrary_info_t pciio_info_mfast_get(pciio_info_t);
pciio_provider_t *pciio_info_pops_get(pciio_info_t);
error_handler_f *pciio_info_efunc_get(pciio_info_t);
error_handler_arg_t *pciio_info_einfo_get(pciio_info_t);
pciio_space_t pciio_info_bar_space_get(pciio_info_t, int);
iopaddr_t pciio_info_bar_base_get(pciio_info_t, int);
size_t pciio_info_bar_size_get(pciio_info_t, int);
iopaddr_t pciio_info_rom_base_get(pciio_info_t);
size_t pciio_info_rom_size_get(pciio_info_t);
void pciio_init(void);
int pciio_attach(devfs_handle_t);
void pciio_provider_register(devfs_handle_t, pciio_provider_t *pciio_fns);
void pciio_provider_unregister(devfs_handle_t);
pciio_provider_t *pciio_provider_fns_get(devfs_handle_t);
int pciio_driver_register(pciio_vendor_id_t, pciio_device_id_t, char *driver_prefix, unsigned);
void pciio_driver_unregister(char *driver_prefix);
devfs_handle_t pciio_device_register(devfs_handle_t, devfs_handle_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
void pciio_device_unregister(devfs_handle_t);
pciio_info_t pciio_device_info_new(pciio_info_t, devfs_handle_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
void pciio_device_info_free(pciio_info_t);
devfs_handle_t pciio_device_info_register(devfs_handle_t, pciio_info_t);
void pciio_device_info_unregister(devfs_handle_t, pciio_info_t);
int pciio_device_attach(devfs_handle_t, int);
int pciio_device_detach(devfs_handle_t, int);
void pciio_error_register(devfs_handle_t, error_handler_f *, error_handler_arg_t);
int pciio_reset(devfs_handle_t);
int pciio_write_gather_flush(devfs_handle_t);
int pciio_slot_inuse(devfs_handle_t);
/* =====================================================================
* Provider Function Location
*
* If there is more than one possible provider for
* this platform, we need to examine the master
* vertex of the current vertex for a provider
* function structure, and indirect through the
* appropriately named member.
*/
#if !defined(DEV_FUNC)
static pciio_provider_t *
pciio_to_provider_fns(devfs_handle_t dev)
{
pciio_info_t card_info;
pciio_provider_t *provider_fns;
/*
* We're called with two types of vertices, one is
* the bridge vertex (ends with "pci") and the other is the
* pci slot vertex (ends with "pci/[0-8]"). For the first type
* we need to get the provider from the PFUNCS label. For
* the second we get it from fastinfo/c_pops.
*/
provider_fns = pciio_provider_fns_get(dev);
if (provider_fns == NULL) {
card_info = pciio_info_get(dev);
if (card_info != NULL) {
provider_fns = pciio_info_pops_get(card_info);
}
}
if (provider_fns == NULL)
#if defined(SUPPORT_PRINTING_V_FORMAT)
PRINT_PANIC("%v: provider_fns == NULL", dev);
#else
PRINT_PANIC("0x%p: provider_fns == NULL", (void *)dev);
#endif
return provider_fns;
}
#define DEV_FUNC(dev,func) pciio_to_provider_fns(dev)->func
#define CAST_PIOMAP(x) ((pciio_piomap_t)(x))
#define CAST_DMAMAP(x) ((pciio_dmamap_t)(x))
#define CAST_INTR(x) ((pciio_intr_t)(x))
#endif
/*
* Many functions are not passed their vertex
* information directly; rather, they must
* dive through a resource map. These macros
* are available to coordinate this detail.
*/
#define PIOMAP_FUNC(map,func) DEV_FUNC((map)->pp_dev,func)
#define DMAMAP_FUNC(map,func) DEV_FUNC((map)->pd_dev,func)
#define INTR_FUNC(intr_hdl,func) DEV_FUNC((intr_hdl)->pi_dev,func)
/* =====================================================================
* PIO MANAGEMENT
*
* For mapping system virtual address space to
* pciio space on a specified card
*/
pciio_piomap_t
pciio_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */
device_desc_t dev_desc, /* device descriptor */
pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
iopaddr_t addr, /* lowest address (or offset in window) */
size_t byte_count, /* size of region containing our mappings */
size_t byte_count_max, /* maximum size of a mapping */
unsigned flags)
{ /* defined in sys/pio.h */
return (pciio_piomap_t) DEV_FUNC(dev, piomap_alloc)
(dev, dev_desc, space, addr, byte_count, byte_count_max, flags);
}
void
pciio_piomap_free(pciio_piomap_t pciio_piomap)
{
PIOMAP_FUNC(pciio_piomap, piomap_free)
(CAST_PIOMAP(pciio_piomap));
}
caddr_t
pciio_piomap_addr(pciio_piomap_t pciio_piomap, /* mapping resources */
iopaddr_t pciio_addr, /* map for this pciio address */
size_t byte_count)
{ /* map this many bytes */
pciio_piomap->pp_kvaddr = PIOMAP_FUNC(pciio_piomap, piomap_addr)
(CAST_PIOMAP(pciio_piomap), pciio_addr, byte_count);
return pciio_piomap->pp_kvaddr;
}
void
pciio_piomap_done(pciio_piomap_t pciio_piomap)
{
PIOMAP_FUNC(pciio_piomap, piomap_done)
(CAST_PIOMAP(pciio_piomap));
}
caddr_t
pciio_piotrans_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
iopaddr_t addr, /* starting address (or offset in window) */
size_t byte_count, /* map this many bytes */
unsigned flags)
{ /* (currently unused) */
return DEV_FUNC(dev, piotrans_addr)
(dev, dev_desc, space, addr, byte_count, flags);
}
caddr_t
pciio_pio_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
iopaddr_t addr, /* starting address (or offset in window) */
size_t byte_count, /* map this many bytes */
pciio_piomap_t *mapp, /* where to return the map pointer */
unsigned flags)
{ /* PIO flags */
pciio_piomap_t map = 0;
int errfree = 0;
caddr_t res;
if (mapp) {
map = *mapp; /* possible pre-allocated map */
*mapp = 0; /* record "no map used" */
}
res = pciio_piotrans_addr
(dev, dev_desc, space, addr, byte_count, flags);
if (res)
return res; /* pciio_piotrans worked */
if (!map) {
map = pciio_piomap_alloc
(dev, dev_desc, space, addr, byte_count, byte_count, flags);
if (!map)
return res; /* pciio_piomap_alloc failed */
errfree = 1;
}
res = pciio_piomap_addr
(map, addr, byte_count);
if (!res) {
if (errfree)
pciio_piomap_free(map);
return res; /* pciio_piomap_addr failed */
}
if (mapp)
*mapp = map; /* pass back map used */
return res; /* pciio_piomap_addr succeeded */
}
iopaddr_t
pciio_piospace_alloc(devfs_handle_t dev, /* Device requiring space */
device_desc_t dev_desc, /* Device descriptor */
pciio_space_t space, /* MEM32/MEM64/IO */
size_t byte_count, /* Size of mapping */
size_t align)
{ /* Alignment needed */
if (align < NBPP)
align = NBPP;
return DEV_FUNC(dev, piospace_alloc)
(dev, dev_desc, space, byte_count, align);
}
void
pciio_piospace_free(devfs_handle_t dev, /* Device freeing space */
pciio_space_t space, /* Type of space */
iopaddr_t pciaddr, /* starting address */
size_t byte_count)
{ /* Range of address */
DEV_FUNC(dev, piospace_free)
(dev, space, pciaddr, byte_count);
}
/* =====================================================================
* DMA MANAGEMENT
*
* For mapping from pci space to system
* physical space.
*/
pciio_dmamap_t
pciio_dmamap_alloc(devfs_handle_t dev, /* set up mappings for this device */
device_desc_t dev_desc, /* device descriptor */
size_t byte_count_max, /* max size of a mapping */
unsigned flags)
{ /* defined in dma.h */
return (pciio_dmamap_t) DEV_FUNC(dev, dmamap_alloc)
(dev, dev_desc, byte_count_max, flags);
}
void
pciio_dmamap_free(pciio_dmamap_t pciio_dmamap)
{
DMAMAP_FUNC(pciio_dmamap, dmamap_free)
(CAST_DMAMAP(pciio_dmamap));
}
iopaddr_t
pciio_dmamap_addr(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */
paddr_t paddr, /* map for this address */
size_t byte_count)
{ /* map this many bytes */
return DMAMAP_FUNC(pciio_dmamap, dmamap_addr)
(CAST_DMAMAP(pciio_dmamap), paddr, byte_count);
}
alenlist_t
pciio_dmamap_list(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */
alenlist_t alenlist, /* map this Address/Length List */
unsigned flags)
{
return DMAMAP_FUNC(pciio_dmamap, dmamap_list)
(CAST_DMAMAP(pciio_dmamap), alenlist, flags);
}
void
pciio_dmamap_done(pciio_dmamap_t pciio_dmamap)
{
DMAMAP_FUNC(pciio_dmamap, dmamap_done)
(CAST_DMAMAP(pciio_dmamap));
}
iopaddr_t
pciio_dmatrans_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
paddr_t paddr, /* system physical address */
size_t byte_count, /* length */
unsigned flags)
{ /* defined in dma.h */
return DEV_FUNC(dev, dmatrans_addr)
(dev, dev_desc, paddr, byte_count, flags);
}
alenlist_t
pciio_dmatrans_list(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
alenlist_t palenlist, /* system address/length list */
unsigned flags)
{ /* defined in dma.h */
return DEV_FUNC(dev, dmatrans_list)
(dev, dev_desc, palenlist, flags);
}
iopaddr_t
pciio_dma_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
paddr_t paddr, /* system physical address */
size_t byte_count, /* length */
pciio_dmamap_t *mapp, /* map to use, then map we used */
unsigned flags)
{ /* PIO flags */
pciio_dmamap_t map = 0;
int errfree = 0;
iopaddr_t res;
if (mapp) {
map = *mapp; /* possible pre-allocated map */
*mapp = 0; /* record "no map used" */
}
res = pciio_dmatrans_addr
(dev, dev_desc, paddr, byte_count, flags);
if (res)
return res; /* pciio_dmatrans worked */
if (!map) {
map = pciio_dmamap_alloc
(dev, dev_desc, byte_count, flags);
if (!map)
return res; /* pciio_dmamap_alloc failed */
errfree = 1;
}
res = pciio_dmamap_addr
(map, paddr, byte_count);
if (!res) {
if (errfree)
pciio_dmamap_free(map);
return res; /* pciio_dmamap_addr failed */
}
if (mapp)
*mapp = map; /* pass back map used */
return res; /* pciio_dmamap_addr succeeded */
}
void
pciio_dmamap_drain(pciio_dmamap_t map)
{
DMAMAP_FUNC(map, dmamap_drain)
(CAST_DMAMAP(map));
}
void
pciio_dmaaddr_drain(devfs_handle_t dev, paddr_t addr, size_t size)
{
DEV_FUNC(dev, dmaaddr_drain)
(dev, addr, size);
}
void
pciio_dmalist_drain(devfs_handle_t dev, alenlist_t list)
{
DEV_FUNC(dev, dmalist_drain)
(dev, list);
}
/* =====================================================================
* INTERRUPT MANAGEMENT
*
* Allow crosstalk devices to establish interrupts
*/
/*
* Allocate resources required for an interrupt as specified in intr_desc.
* Return resource handle in intr_hdl.
*/
pciio_intr_t
pciio_intr_alloc(devfs_handle_t dev, /* which Crosstalk device */
device_desc_t dev_desc, /* device descriptor */
pciio_intr_line_t lines, /* INTR line(s) to attach */
devfs_handle_t owner_dev)
{ /* owner of this interrupt */
return (pciio_intr_t) DEV_FUNC(dev, intr_alloc)
(dev, dev_desc, lines, owner_dev);
}
/*
* Free resources consumed by intr_alloc.
*/
void
pciio_intr_free(pciio_intr_t intr_hdl)
{
INTR_FUNC(intr_hdl, intr_free)
(CAST_INTR(intr_hdl));
}
/*
* Associate resources allocated with a previous pciio_intr_alloc call with the
* described handler, arg, name, etc.
*
* Returns 0 on success, returns <0 on failure.
*/
int
pciio_intr_connect(pciio_intr_t intr_hdl,
intr_func_t intr_func, intr_arg_t intr_arg) /* pciio intr resource handle */
{
return INTR_FUNC(intr_hdl, intr_connect)
(CAST_INTR(intr_hdl), intr_func, intr_arg);
}
/*
* Disassociate handler with the specified interrupt.
*/
void
pciio_intr_disconnect(pciio_intr_t intr_hdl)
{
INTR_FUNC(intr_hdl, intr_disconnect)
(CAST_INTR(intr_hdl));
}
/*
* Return a hwgraph vertex that represents the CPU currently
* targeted by an interrupt.
*/
devfs_handle_t
pciio_intr_cpu_get(pciio_intr_t intr_hdl)
{
return INTR_FUNC(intr_hdl, intr_cpu_get)
(CAST_INTR(intr_hdl));
}
void
pciio_slot_func_to_name(char *name,
pciio_slot_t slot,
pciio_function_t func)
{
/*
* standard connection points:
*
* PCIIO_SLOT_NONE: .../pci/direct
* PCIIO_FUNC_NONE: .../pci/<SLOT> ie. .../pci/3
* multifunction: .../pci/<SLOT><FUNC> ie. .../pci/3c
*/
if (slot == PCIIO_SLOT_NONE)
sprintf(name, EDGE_LBL_DIRECT);
else if (func == PCIIO_FUNC_NONE)
sprintf(name, "%d", slot);
else
sprintf(name, "%d%c", slot, 'a'+func);
}
/*
* pciio_cardinfo_get
*
* Get the pciio info structure corresponding to the
* specified PCI "slot" (we like it when the same index
* number is used for the PCI IDSEL, the REQ/GNT pair,
* and the interrupt line being used for INTA. We like
* it so much we call it the slot number).
*/
static pciio_info_t
pciio_cardinfo_get(
devfs_handle_t pciio_vhdl,
pciio_slot_t pci_slot)
{
char namebuf[16];
pciio_info_t info = 0;
devfs_handle_t conn;
pciio_slot_func_to_name(namebuf, pci_slot, PCIIO_FUNC_NONE);
if (GRAPH_SUCCESS ==
hwgraph_traverse(pciio_vhdl, namebuf, &conn)) {
info = pciio_info_chk(conn);
hwgraph_vertex_unref(conn);
}
return info;
}
/*
* pciio_error_handler:
* dispatch an error to the appropriate
* pciio connection point, or process
* it as a generic pci error.
* Yes, the first parameter is the
* provider vertex at the middle of
* the bus; we get to the pciio connect
* point using the ioerror widgetdev field.
*
* This function is called by the
* specific PCI provider, after it has figured
* out where on the PCI bus (including which slot,
* if it can tell) the error came from.
*/
/*ARGSUSED */
int
pciio_error_handler(
devfs_handle_t pciio_vhdl,
int error_code,
ioerror_mode_t mode,
ioerror_t *ioerror)
{
pciio_info_t pciio_info;
devfs_handle_t pconn_vhdl;
#if USRPCI
devfs_handle_t usrpci_v;
#endif
pciio_slot_t slot;
int retval;
#ifdef EHE_ENABLE
error_state_t e_state;
#endif /* EHE_ENABLE */
#if DEBUG && ERROR_DEBUG
printk("%v: pciio_error_handler\n", pciio_vhdl);
#endif
IOERR_PRINTF(printk(KERN_NOTICE "%v: PCI Bus Error: Error code: %d Error mode: %d\n",
pciio_vhdl, error_code, mode));
/* If there is an error handler sitting on
* the "no-slot" connection point, give it
* first crack at the error. NOTE: it is
* quite possible that this function may
* do further refining of the ioerror.
*/
pciio_info = pciio_cardinfo_get(pciio_vhdl, PCIIO_SLOT_NONE);
if (pciio_info && pciio_info->c_efunc) {
pconn_vhdl = pciio_info_dev_get(pciio_info);
#ifdef EHE_ENABLE
e_state = error_state_get(pciio_vhdl);
if (e_state == ERROR_STATE_ACTION)
(void)error_state_set(pciio_vhdl, ERROR_STATE_NONE);
if (error_state_set(pconn_vhdl,e_state) == ERROR_RETURN_CODE_CANNOT_SET_STATE)
return(IOERROR_UNHANDLED);
#endif
retval = pciio_info->c_efunc
(pciio_info->c_einfo, error_code, mode, ioerror);
if (retval != IOERROR_UNHANDLED)
return retval;
}
/* Is the error associated with a particular slot?
*/
if (IOERROR_FIELDVALID(ioerror, widgetdev)) {
short widgetdev;
/*
* NOTE :
* widgetdev is a 4byte value encoded as slot in the higher order
* 2 bytes and function in the lower order 2 bytes.
*/
IOERROR_GETVALUE(widgetdev, ioerror, widgetdev);
slot = pciio_widgetdev_slot_get(widgetdev);
/* If this slot has an error handler,
* deliver the error to it.
*/
pciio_info = pciio_cardinfo_get(pciio_vhdl, slot);
if (pciio_info != NULL) {
if (pciio_info->c_efunc != NULL) {
pconn_vhdl = pciio_info_dev_get(pciio_info);
#ifdef EHE_ENABLE
e_state = error_state_get(pciio_vhdl);
if (e_state == ERROR_STATE_ACTION)
(void)error_state_set(pciio_vhdl, ERROR_STATE_NONE);
if (error_state_set(pconn_vhdl,e_state) ==
ERROR_RETURN_CODE_CANNOT_SET_STATE)
return(IOERROR_UNHANDLED);
#endif /* EHE_ENABLE */
retval = pciio_info->c_efunc
(pciio_info->c_einfo, error_code, mode, ioerror);
if (retval != IOERROR_UNHANDLED)
return retval;
}
#if USRPCI
/* If the USRPCI driver is available and
* knows about this connection point,
* deliver the error to it.
*
* OK to use pconn_vhdl here, even though we
* have already UNREF'd it, since we know that
* it is not going away.
*/
pconn_vhdl = pciio_info_dev_get(pciio_info);
if (GRAPH_SUCCESS == hwgraph_traverse(pconn_vhdl, EDGE_LBL_USRPCI, &usrpci_v)) {
iopaddr_t busaddr;
IOERROR_GETVALUE(busaddr, ioerror, busaddr);
retval = usrpci_error_handler (usrpci_v, error_code, busaddr);
hwgraph_vertex_unref(usrpci_v);
if (retval != IOERROR_UNHANDLED) {
/*
* This unref is not needed. If this code is called often enough,
* the system will crash, due to vertex reference count reaching 0,
* causing vertex to be unallocated. -jeremy
* hwgraph_vertex_unref(pconn_vhdl);
*/
return retval;
}
}
#endif
}
}
return (mode == MODE_DEVPROBE)
? IOERROR_HANDLED /* probes are OK */
: IOERROR_UNHANDLED; /* otherwise, foo! */
}
/* =====================================================================
* CONFIGURATION MANAGEMENT
*/
/*
* Startup a crosstalk provider
*/
void
pciio_provider_startup(devfs_handle_t pciio_provider)
{
DEV_FUNC(pciio_provider, provider_startup)
(pciio_provider);
}
/*
* Shutdown a crosstalk provider
*/
void
pciio_provider_shutdown(devfs_handle_t pciio_provider)
{
DEV_FUNC(pciio_provider, provider_shutdown)
(pciio_provider);
}
/*
* Specify endianness constraints. The driver tells us what the device
* does and how it would like to see things in memory. We reply with
* how things will actually appear in memory.
*/
pciio_endian_t
pciio_endian_set(devfs_handle_t dev,
pciio_endian_t device_end,
pciio_endian_t desired_end)
{
ASSERT((device_end == PCIDMA_ENDIAN_BIG) || (device_end == PCIDMA_ENDIAN_LITTLE));
ASSERT((desired_end == PCIDMA_ENDIAN_BIG) || (desired_end == PCIDMA_ENDIAN_LITTLE));
#if DEBUG
#if defined(SUPPORT_PRINTING_V_FORMAT)
printk(KERN_ALERT "%v: pciio_endian_set is going away.\n"
"\tplease use PCIIO_BYTE_STREAM or PCIIO_WORD_VALUES in your\n"
"\tpciio_dmamap_alloc and pciio_dmatrans calls instead.\n",
dev);
#else
printk(KERN_ALERT "0x%x: pciio_endian_set is going away.\n"
"\tplease use PCIIO_BYTE_STREAM or PCIIO_WORD_VALUES in your\n"
"\tpciio_dmamap_alloc and pciio_dmatrans calls instead.\n",
dev);
#endif
#endif
return DEV_FUNC(dev, endian_set)
(dev, device_end, desired_end);
}
/*
* Specify PCI arbitration priority.
*/
pciio_priority_t
pciio_priority_set(devfs_handle_t dev,
pciio_priority_t device_prio)
{
ASSERT((device_prio == PCI_PRIO_HIGH) || (device_prio == PCI_PRIO_LOW));
return DEV_FUNC(dev, priority_set)
(dev, device_prio);
}
/*
* Read value of configuration register
*/
uint64_t
pciio_config_get(devfs_handle_t dev,
unsigned reg,
unsigned size)
{
uint64_t value = 0;
unsigned shift = 0;
/* handle accesses that cross words here,
* since that's common code between all
* possible providers.
*/
while (size > 0) {
unsigned biw = 4 - (reg&3);
if (biw > size)
biw = size;
value |= DEV_FUNC(dev, config_get)
(dev, reg, biw) << shift;
shift += 8*biw;
reg += biw;
size -= biw;
}
return value;
}
/*
* Change value of configuration register
*/
void
pciio_config_set(devfs_handle_t dev,
unsigned reg,
unsigned size,
uint64_t value)
{
/* handle accesses that cross words here,
* since that's common code between all
* possible providers.
*/
while (size > 0) {
unsigned biw = 4 - (reg&3);
if (biw > size)
biw = size;
DEV_FUNC(dev, config_set)
(dev, reg, biw, value);
reg += biw;
size -= biw;
value >>= biw * 8;
}
}
/* =====================================================================
* GENERIC PCI SUPPORT FUNCTIONS
*/
/*
* Issue a hardware reset to a card.
*/
int
pciio_reset(devfs_handle_t dev)
{
return DEV_FUNC(dev, reset) (dev);
}
/*
* flush write gather buffers
*/
int
pciio_write_gather_flush(devfs_handle_t dev)
{
return DEV_FUNC(dev, write_gather_flush) (dev);
}
devfs_handle_t
pciio_intr_dev_get(pciio_intr_t pciio_intr)
{
return (pciio_intr->pi_dev);
}
/****** Generic crosstalk pio interfaces ******/
devfs_handle_t
pciio_pio_dev_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_dev);
}
pciio_slot_t
pciio_pio_slot_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_slot);
}
pciio_space_t
pciio_pio_space_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_space);
}
iopaddr_t
pciio_pio_pciaddr_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_pciaddr);
}
ulong
pciio_pio_mapsz_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_mapsz);
}
caddr_t
pciio_pio_kvaddr_get(pciio_piomap_t pciio_piomap)
{
return (pciio_piomap->pp_kvaddr);
}
/****** Generic crosstalk dma interfaces ******/
devfs_handle_t
pciio_dma_dev_get(pciio_dmamap_t pciio_dmamap)
{
return (pciio_dmamap->pd_dev);
}
pciio_slot_t
pciio_dma_slot_get(pciio_dmamap_t pciio_dmamap)
{
return (pciio_dmamap->pd_slot);
}
/****** Generic pci slot information interfaces ******/
pciio_info_t
pciio_info_chk(devfs_handle_t pciio)
{
arbitrary_info_t ainfo = 0;
hwgraph_info_get_LBL(pciio, INFO_LBL_PCIIO, &ainfo);
return (pciio_info_t) ainfo;
}
pciio_info_t
pciio_info_get(devfs_handle_t pciio)
{
pciio_info_t pciio_info;
pciio_info = (pciio_info_t) hwgraph_fastinfo_get(pciio);
#ifdef DEBUG_PCIIO
{
int pos;
char dname[256];
pos = devfs_generate_path(pciio, dname, 256);
printk("%s : path= %s\n", __FUNCTION__, &dname[pos]);
}
#endif /* DEBUG_PCIIO */
if ((pciio_info != NULL) &&
(pciio_info->c_fingerprint != pciio_info_fingerprint)
&& (pciio_info->c_fingerprint != NULL)) {
return((pciio_info_t)-1); /* Should panic .. */
}
return pciio_info;
}
void
pciio_info_set(devfs_handle_t pciio, pciio_info_t pciio_info)
{
if (pciio_info != NULL)
pciio_info->c_fingerprint = pciio_info_fingerprint;
hwgraph_fastinfo_set(pciio, (arbitrary_info_t) pciio_info);
/* Also, mark this vertex as a PCI slot
* and use the pciio_info, so pciio_info_chk
* can work (and be fairly efficient).
*/
hwgraph_info_add_LBL(pciio, INFO_LBL_PCIIO,
(arbitrary_info_t) pciio_info);
}
devfs_handle_t
pciio_info_dev_get(pciio_info_t pciio_info)
{
return (pciio_info->c_vertex);
}
pciio_slot_t
pciio_info_slot_get(pciio_info_t pciio_info)
{
return (pciio_info->c_slot);
}
pciio_function_t
pciio_info_function_get(pciio_info_t pciio_info)
{
return (pciio_info->c_func);
}
pciio_vendor_id_t
pciio_info_vendor_id_get(pciio_info_t pciio_info)
{
return (pciio_info->c_vendor);
}
pciio_device_id_t
pciio_info_device_id_get(pciio_info_t pciio_info)
{
return (pciio_info->c_device);
}
devfs_handle_t
pciio_info_master_get(pciio_info_t pciio_info)
{
return (pciio_info->c_master);
}
arbitrary_info_t
pciio_info_mfast_get(pciio_info_t pciio_info)
{
return (pciio_info->c_mfast);
}
pciio_provider_t *
pciio_info_pops_get(pciio_info_t pciio_info)
{
return (pciio_info->c_pops);
}
error_handler_f *
pciio_info_efunc_get(pciio_info_t pciio_info)
{
return (pciio_info->c_efunc);
}
error_handler_arg_t *
pciio_info_einfo_get(pciio_info_t pciio_info)
{
return (pciio_info->c_einfo);
}
pciio_space_t
pciio_info_bar_space_get(pciio_info_t info, int win)
{
return info->c_window[win].w_space;
}
iopaddr_t
pciio_info_bar_base_get(pciio_info_t info, int win)
{
return info->c_window[win].w_base;
}
size_t
pciio_info_bar_size_get(pciio_info_t info, int win)
{
return info->c_window[win].w_size;
}
iopaddr_t
pciio_info_rom_base_get(pciio_info_t info)
{
return info->c_rbase;
}
size_t
pciio_info_rom_size_get(pciio_info_t info)
{
return info->c_rsize;
}
/* =====================================================================
* GENERIC PCI INITIALIZATION FUNCTIONS
*/
/*
* pciioinit: called once during device driver
* initializtion if this driver is configured into
* the system.
*/
void
pciio_init(void)
{
cdl_p cp;
#if DEBUG && ATTACH_DEBUG
printf("pciio_init\n");
#endif
/* Allocate the registry.
* We might already have one.
* If we don't, go get one.
* MPness: someone might have
* set one up for us while we
* were not looking; use an atomic
* compare-and-swap to commit to
* using the new registry if and
* only if nobody else did first.
* If someone did get there first,
* toss the one we allocated back
* into the pool.
*/
if (pciio_registry == NULL) {
cp = cdl_new(EDGE_LBL_PCI, "vendor", "device");
if (!compare_and_swap_ptr((void **) &pciio_registry, NULL, (void *) cp)) {
cdl_del(cp);
}
}
ASSERT(pciio_registry != NULL);
}
/*
* pciioattach: called for each vertex in the graph
* that is a PCI provider.
*/
/*ARGSUSED */
int
pciio_attach(devfs_handle_t pciio)
{
#if DEBUG && ATTACH_DEBUG
#if defined(SUPPORT_PRINTING_V_FORMAT)
printk("%v: pciio_attach\n", pciio);
#else
printk("0x%x: pciio_attach\n", pciio);
#endif
#endif
return 0;
}
/*
* Associate a set of pciio_provider functions with a vertex.
*/
void
pciio_provider_register(devfs_handle_t provider, pciio_provider_t *pciio_fns)
{
hwgraph_info_add_LBL(provider, INFO_LBL_PFUNCS, (arbitrary_info_t) pciio_fns);
}
/*
* Disassociate a set of pciio_provider functions with a vertex.
*/
void
pciio_provider_unregister(devfs_handle_t provider)
{
arbitrary_info_t ainfo;
hwgraph_info_remove_LBL(provider, INFO_LBL_PFUNCS, (long *) &ainfo);
}
/*
* Obtain a pointer to the pciio_provider functions for a specified Crosstalk
* provider.
*/
pciio_provider_t *
pciio_provider_fns_get(devfs_handle_t provider)
{
arbitrary_info_t ainfo = 0;
(void) hwgraph_info_get_LBL(provider, INFO_LBL_PFUNCS, &ainfo);
return (pciio_provider_t *) ainfo;
}
/*ARGSUSED4 */
int
pciio_driver_register(
pciio_vendor_id_t vendor_id,
pciio_device_id_t device_id,
char *driver_prefix,
unsigned flags)
{
/* a driver's init routine might call
* pciio_driver_register before the
* system calls pciio_init; so we
* make the init call ourselves here.
*/
if (pciio_registry == NULL)
pciio_init();
return cdl_add_driver(pciio_registry,
vendor_id, device_id,
driver_prefix, flags, NULL);
}
/*
* Remove an initialization function.
*/
void
pciio_driver_unregister(
char *driver_prefix)
{
/* before a driver calls unregister,
* it must have called register; so
* we can assume we have a registry here.
*/
ASSERT(pciio_registry != NULL);
cdl_del_driver(pciio_registry, driver_prefix, NULL);
}
/*
* Set the slot status for a device supported by the
* driver being registered.
*/
void
pciio_driver_reg_callback(
devfs_handle_t pconn_vhdl,
int key1,
int key2,
int error)
{
}
/*
* Set the slot status for a device supported by the
* driver being unregistered.
*/
void
pciio_driver_unreg_callback(
devfs_handle_t pconn_vhdl,
int key1,
int key2,
int error)
{
}
/*
* Call some function with each vertex that
* might be one of this driver's attach points.
*/
void
pciio_iterate(char *driver_prefix,
pciio_iter_f * func)
{
/* a driver's init routine might call
* pciio_iterate before the
* system calls pciio_init; so we
* make the init call ourselves here.
*/
if (pciio_registry == NULL)
pciio_init();
ASSERT(pciio_registry != NULL);
cdl_iterate(pciio_registry, driver_prefix, (cdl_iter_f *) func);
}
devfs_handle_t
pciio_device_register(
devfs_handle_t connectpt, /* vertex for /hw/.../pciio/%d */
devfs_handle_t master, /* card's master ASIC (PCI provider) */
pciio_slot_t slot, /* card's slot */
pciio_function_t func, /* card's func */
pciio_vendor_id_t vendor_id,
pciio_device_id_t device_id)
{
return pciio_device_info_register
(connectpt, pciio_device_info_new (NULL, master, slot, func,
vendor_id, device_id));
}
void
pciio_device_unregister(devfs_handle_t pconn)
{
DEV_FUNC(pconn,device_unregister)(pconn);
}
pciio_info_t
pciio_device_info_new(
pciio_info_t pciio_info,
devfs_handle_t master,
pciio_slot_t slot,
pciio_function_t func,
pciio_vendor_id_t vendor_id,
pciio_device_id_t device_id)
{
if (!pciio_info)
GET_NEW(pciio_info);
ASSERT(pciio_info != NULL);
pciio_info->c_slot = slot;
pciio_info->c_func = func;
pciio_info->c_vendor = vendor_id;
pciio_info->c_device = device_id;
pciio_info->c_master = master;
pciio_info->c_mfast = hwgraph_fastinfo_get(master);
pciio_info->c_pops = pciio_provider_fns_get(master);
pciio_info->c_efunc = 0;
pciio_info->c_einfo = 0;
return pciio_info;
}
void
pciio_device_info_free(pciio_info_t pciio_info)
{
/* NOTE : pciio_info is a structure within the pcibr_info
* and not a pointer to memory allocated on the heap !!
*/
BZERO((char *)pciio_info,sizeof(pciio_info));
}
devfs_handle_t
pciio_device_info_register(
devfs_handle_t connectpt, /* vertex at center of bus */
pciio_info_t pciio_info) /* details about the connectpt */
{
char name[32];
devfs_handle_t pconn;
int device_master_set(devfs_handle_t, devfs_handle_t);
pciio_slot_func_to_name(name,
pciio_info->c_slot,
pciio_info->c_func);
if (GRAPH_SUCCESS !=
hwgraph_path_add(connectpt, name, &pconn))
return pconn;
pciio_info->c_vertex = pconn;
pciio_info_set(pconn, pciio_info);
#ifdef DEBUG_PCIIO
{
int pos;
char dname[256];
pos = devfs_generate_path(pconn, dname, 256);
printk("%s : pconn path= %s \n", __FUNCTION__, &dname[pos]);
}
#endif /* DEBUG_PCIIO */
/*
* create link to our pci provider
*/
device_master_set(pconn, pciio_info->c_master);
#if USRPCI
/*
* Call into usrpci provider to let it initialize for
* the given slot.
*/
if (pciio_info->c_slot != PCIIO_SLOT_NONE)
usrpci_device_register(pconn, pciio_info->c_master, pciio_info->c_slot);
#endif
return pconn;
}
void
pciio_device_info_unregister(devfs_handle_t connectpt,
pciio_info_t pciio_info)
{
char name[32];
devfs_handle_t pconn;
if (!pciio_info)
return;
pciio_slot_func_to_name(name,
pciio_info->c_slot,
pciio_info->c_func);
hwgraph_edge_remove(connectpt,name,&pconn);
pciio_info_set(pconn,0);
/* Remove the link to our pci provider */
hwgraph_edge_remove(pconn, EDGE_LBL_MASTER, NULL);
hwgraph_vertex_unref(pconn);
hwgraph_vertex_destroy(pconn);
}
/* Add the pci card inventory information to the hwgraph
*/
static void
pciio_device_inventory_add(devfs_handle_t pconn_vhdl)
{
pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
ASSERT(pciio_info);
ASSERT(pciio_info->c_vertex == pconn_vhdl);
/* Donot add inventory for non-existent devices */
if ((pciio_info->c_vendor == PCIIO_VENDOR_ID_NONE) ||
(pciio_info->c_device == PCIIO_DEVICE_ID_NONE))
return;
device_inventory_add(pconn_vhdl,INV_IOBD,INV_PCIADAP,
pciio_info->c_vendor,pciio_info->c_device,
pciio_info->c_slot);
}
/*ARGSUSED */
int
pciio_device_attach(devfs_handle_t pconn,
int drv_flags)
{
pciio_info_t pciio_info;
pciio_vendor_id_t vendor_id;
pciio_device_id_t device_id;
pciio_device_inventory_add(pconn);
pciio_info = pciio_info_get(pconn);
vendor_id = pciio_info->c_vendor;
device_id = pciio_info->c_device;
/* we don't start attaching things until
* all the driver init routines (including
* pciio_init) have been called; so we
* can assume here that we have a registry.
*/
ASSERT(pciio_registry != NULL);
return(cdl_add_connpt(pciio_registry, vendor_id, device_id, pconn, drv_flags));
}
int
pciio_device_detach(devfs_handle_t pconn,
int drv_flags)
{
pciio_info_t pciio_info;
pciio_vendor_id_t vendor_id;
pciio_device_id_t device_id;
pciio_info = pciio_info_get(pconn);
vendor_id = pciio_info->c_vendor;
device_id = pciio_info->c_device;
/* we don't start attaching things until
* all the driver init routines (including
* pciio_init) have been called; so we
* can assume here that we have a registry.
*/
ASSERT(pciio_registry != NULL);
return(cdl_del_connpt(pciio_registry, vendor_id, device_id,
pconn, drv_flags));
}
/* SN2 */
/*
* Allocate (if necessary) and initialize a PCI window mapping structure.
*/
pciio_win_map_t
pciio_device_win_map_new(pciio_win_map_t win_map,
size_t region_size,
size_t page_size)
{
ASSERT((page_size & (page_size - 1)) == 0);
ASSERT((region_size & (page_size - 1)) == 0);
if (win_map == NULL)
NEW(win_map);
/*
* The map array tracks the free ``pages'' in the region. The worst
* case scenario is when every other page in the region is free --
* e.i. maximum fragmentation. This leads to (max pages + 1) / 2 + 1
* map entries. The first "+1" handles the divide by 2 rounding; the
* second handles the need for an end marker sentinel.
*/
win_map->wm_map = rmallocmap((region_size / page_size + 1) / 2 + 1);
win_map->wm_page_size = page_size;
ASSERT(win_map->wm_map != NULL);
return win_map;
}
/*
* Free resources associated with a PCI window mapping structure.
*/
extern void
pciio_device_win_map_free(pciio_win_map_t win_map)
{
rmfreemap(win_map->wm_map);
bzero(win_map, sizeof *win_map);
}
/*
* Populate window map with specified free range.
*/
void
pciio_device_win_populate(pciio_win_map_t win_map,
iopaddr_t ioaddr,
size_t size)
{
ASSERT((size & (win_map->wm_page_size - 1)) == 0);
ASSERT((ioaddr & (win_map->wm_page_size - 1)) == 0);
rmfree(win_map->wm_map,
size / win_map->wm_page_size,
(unsigned long)ioaddr / win_map->wm_page_size);
}
/*
* Allocate space from the specified PCI window mapping resource. On
* success record information about the allocation in the supplied window
* allocation cookie (if non-NULL) and return the address of the allocated
* window. On failure return NULL.
*
* The "size" parameter is usually from a PCI device's Base Address Register
* (BAR) decoder. As such, the allocation must be aligned to be a multiple of
* that. The "align" parameter acts as a ``minimum alignment'' allocation
* constraint. The alignment contraint reflects system or device addressing
* restrictions such as the inability to share higher level ``windows''
* between devices, etc. The returned PCI address allocation will be a
* multiple of the alignment constraint both in alignment and size. Thus, the
* returned PCI address block is aligned to the maximum of the requested size
* and alignment.
*/
iopaddr_t
pciio_device_win_alloc(pciio_win_map_t win_map,
pciio_win_alloc_t win_alloc,
size_t start, size_t size, size_t align)
{
unsigned long base;
#ifdef PIC_LATER
ASSERT((size & (size - 1)) == 0);
ASSERT((align & (align - 1)) == 0);
/*
* Convert size and alignment to pages. If size is greated than the
* requested alignment, we bump the alignment up to size; otherwise
* convert the size into a multiple of the alignment request.
*/
size = (size + win_map->wm_page_size - 1) / win_map->wm_page_size;
align = align / win_map->wm_page_size;
if (size > align)
align = size;
else
size = (size + align - 1) & ~(align - 1);
/* XXXX */
base = rmalloc_align(win_map->wm_map, size, align, VM_NOSLEEP);
if (base == RMALLOC_FAIL)
return((iopaddr_t)NULL);
#else
int index_page, index_page_align;
int align_pages, size_pages;
int alloc_pages, free_pages;
int addr_align;
/* Convert PCI bus alignment from bytes to pages */
align_pages = align / win_map->wm_page_size;
/* Convert PCI request from bytes to pages */
size_pages = (size / win_map->wm_page_size) +
((size % win_map->wm_page_size) ? 1 : 0);
/* Align address with the larger of the size or the requested slot align */
if (size_pages > align_pages)
align_pages = size_pages;
/*
* Avoid wasting space by aligning - 1; this will prevent crossing
* another alignment boundary.
*/
alloc_pages = size_pages + (align_pages - 1);
/* Allocate PCI bus space in pages */
index_page = (int) rmalloc(win_map->wm_map,
(size_t) alloc_pages);
/* Error if no PCI bus address space available */
if (!index_page)
return 0;
/* PCI bus address index starts at 0 */
index_page--;
/* Align the page offset as requested */
index_page_align = (index_page + (align_pages - 1)) -
((index_page + (align_pages - 1)) % align_pages);
free_pages = (align_pages - 1) - (index_page_align - index_page);
/* Free unused PCI bus pages adjusting the index to start at 1 */
rmfree(win_map->wm_map,
free_pages,
(index_page_align + 1) + size_pages);
/* Return aligned PCI bus space in bytes */
addr_align = (index_page_align * win_map->wm_page_size);
base = index_page;
size = alloc_pages - free_pages;
#endif /* PIC_LATER */
/*
* If a window allocation cookie has been supplied, use it to keep
* track of all the allocated space assigned to this window.
*/
if (win_alloc) {
win_alloc->wa_map = win_map;
win_alloc->wa_base = base;
win_alloc->wa_pages = size;
}
return base * win_map->wm_page_size;
}
/*
* Free the specified window allocation back into the PCI window mapping
* resource. As noted above, we keep page addresses offset by 1 ...
*/
void
pciio_device_win_free(pciio_win_alloc_t win_alloc)
{
if (win_alloc->wa_pages)
rmfree(win_alloc->wa_map->wm_map,
win_alloc->wa_pages,
win_alloc->wa_base);
}
/*
* pciio_error_register:
* arrange for a function to be called with
* a specified first parameter plus other
* information when an error is encountered
* and traced to the pci slot corresponding
* to the connection point pconn.
*
* may also be called with a null function
* pointer to "unregister" the error handler.
*
* NOTE: subsequent calls silently overwrite
* previous data for this vertex. We assume that
* cooperating drivers, well, cooperate ...
*/
void
pciio_error_register(devfs_handle_t pconn,
error_handler_f *efunc,
error_handler_arg_t einfo)
{
pciio_info_t pciio_info;
pciio_info = pciio_info_get(pconn);
ASSERT(pciio_info != NULL);
pciio_info->c_efunc = efunc;
pciio_info->c_einfo = einfo;
}
/*
* Check if any device has been found in this slot, and return
* true or false
* vhdl is the vertex for the slot
*/
int
pciio_slot_inuse(devfs_handle_t pconn_vhdl)
{
pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
ASSERT(pciio_info);
ASSERT(pciio_info->c_vertex == pconn_vhdl);
if (pciio_info->c_vendor) {
/*
* Non-zero value for vendor indicate
* a board being found in this slot.
*/
return 1;
}
return 0;
}
int
pciio_dma_enabled(devfs_handle_t pconn_vhdl)
{
return DEV_FUNC(pconn_vhdl, dma_enabled)(pconn_vhdl);
}
int
pciio_info_type1_get(pciio_info_t pci_info)
{
return(0);
}
/*
* These are complementary Linux interfaces that takes in a pci_dev * as the
* first arguement instead of devfs_handle_t.
*/
iopaddr_t snia_pciio_dmatrans_addr(struct pci_dev *, device_desc_t, paddr_t, size_t, unsigned);
pciio_dmamap_t snia_pciio_dmamap_alloc(struct pci_dev *, device_desc_t, size_t, unsigned);
void snia_pciio_dmamap_free(pciio_dmamap_t);
iopaddr_t snia_pciio_dmamap_addr(pciio_dmamap_t, paddr_t, size_t);
void snia_pciio_dmamap_done(pciio_dmamap_t);
pciio_endian_t snia_pciio_endian_set(struct pci_dev *pci_dev, pciio_endian_t device_end,
pciio_endian_t desired_end);
#include <linux/module.h>
EXPORT_SYMBOL(snia_pciio_dmatrans_addr);
EXPORT_SYMBOL(snia_pciio_dmamap_alloc);
EXPORT_SYMBOL(snia_pciio_dmamap_free);
EXPORT_SYMBOL(snia_pciio_dmamap_addr);
EXPORT_SYMBOL(snia_pciio_dmamap_done);
EXPORT_SYMBOL(snia_pciio_endian_set);
int
snia_pcibr_rrb_alloc(struct pci_dev *pci_dev,
int *count_vchan0,
int *count_vchan1)
{
devfs_handle_t dev = PCIDEV_VERTEX(pci_dev);
return pcibr_rrb_alloc(dev, count_vchan0, count_vchan1);
}
EXPORT_SYMBOL(snia_pcibr_rrb_alloc);
pciio_endian_t
snia_pciio_endian_set(struct pci_dev *pci_dev,
pciio_endian_t device_end,
pciio_endian_t desired_end)
{
devfs_handle_t dev = PCIDEV_VERTEX(pci_dev);
return DEV_FUNC(dev, endian_set)
(dev, device_end, desired_end);
}
iopaddr_t
snia_pciio_dmatrans_addr(struct pci_dev *pci_dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
paddr_t paddr, /* system physical address */
size_t byte_count, /* length */
unsigned flags)
{ /* defined in dma.h */
devfs_handle_t dev = PCIDEV_VERTEX(pci_dev);
/*
* If the device is not a PIC, we always want the PCIIO_BYTE_STREAM to be
* set. Otherwise, it must not be set. This applies to SN1 and SN2.
*/
return DEV_FUNC(dev, dmatrans_addr)
(dev, dev_desc, paddr, byte_count, (IS_PIC_DEVICE(pci_dev)) ? (flags & ~PCIIO_BYTE_STREAM) : flags | PCIIO_BYTE_STREAM);
}
pciio_dmamap_t
snia_pciio_dmamap_alloc(struct pci_dev *pci_dev, /* set up mappings for this device */
device_desc_t dev_desc, /* device descriptor */
size_t byte_count_max, /* max size of a mapping */
unsigned flags)
{ /* defined in dma.h */
devfs_handle_t dev = PCIDEV_VERTEX(pci_dev);
/*
* If the device is not a PIC, we always want the PCIIO_BYTE_STREAM to be
* set. Otherwise, it must not be set. This applies to SN1 and SN2.
*/
return (pciio_dmamap_t) DEV_FUNC(dev, dmamap_alloc)
(dev, dev_desc, byte_count_max, (IS_PIC_DEVICE(pci_dev)) ? (flags & ~PCIIO_BYTE_STREAM) : flags | PCIIO_BYTE_STREAM);
}
void
snia_pciio_dmamap_free(pciio_dmamap_t pciio_dmamap)
{
DMAMAP_FUNC(pciio_dmamap, dmamap_free)
(CAST_DMAMAP(pciio_dmamap));
}
iopaddr_t
snia_pciio_dmamap_addr(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */
paddr_t paddr, /* map for this address */
size_t byte_count)
{ /* map this many bytes */
return DMAMAP_FUNC(pciio_dmamap, dmamap_addr)
(CAST_DMAMAP(pciio_dmamap), paddr, byte_count);
}
void
snia_pciio_dmamap_done(pciio_dmamap_t pciio_dmamap)
{
DMAMAP_FUNC(pciio_dmamap, dmamap_done)
(CAST_DMAMAP(pciio_dmamap));
}
/*
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/sn/sgi.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/addrs.h>
#include <asm/sn/arch.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/xtalk/xwidget.h>
#include <asm/sn/pci/bridge.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/pci/pci_defs.h>
#include <asm/sn/prio.h>
#include <asm/sn/xtalk/xbow.h>
#include <asm/sn/ioc3.h>
#include <asm/sn/eeprom.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_private.h>
extern char *bcopy(const char * src, char * dest, int count);
#define PCI_BUS_NO_1 1
int pic_devflag = D_MP;
extern int pcibr_attach2(devfs_handle_t, bridge_t *, devfs_handle_t, int, pcibr_soft_t *);
extern void pcibr_driver_reg_callback(devfs_handle_t, int, int, int);
extern void pcibr_driver_unreg_callback(devfs_handle_t, int, int, int);
void
pic_init(void)
{
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INIT, NULL, "pic_init()\n"));
xwidget_driver_register(PIC_WIDGET_PART_NUM_BUS0,
PIC_WIDGET_MFGR_NUM,
"pic_",
0);
}
/*
* copy inventory_t from conn_v to peer_conn_v
*/
int
pic_bus1_inventory_dup(devfs_handle_t conn_v, devfs_handle_t peer_conn_v)
{
inventory_t *pinv, *peer_pinv;
if (hwgraph_info_get_LBL(conn_v, INFO_LBL_INVENT,
(arbitrary_info_t *)&pinv) == GRAPH_SUCCESS)
{
NEW(peer_pinv);
bcopy(pinv, peer_pinv, sizeof(inventory_t));
if (hwgraph_info_add_LBL(peer_conn_v, INFO_LBL_INVENT,
(arbitrary_info_t)peer_pinv) != GRAPH_SUCCESS) {
DEL(peer_pinv);
return 0;
}
return 1;
}
printk("pic_bus1_inventory_dup: cannot get INFO_LBL_INVENT from 0x%lx\n ",
conn_v);
return 0;
}
/*
* copy xwidget_info_t from conn_v to peer_conn_v
*/
int
pic_bus1_widget_info_dup(devfs_handle_t conn_v, devfs_handle_t peer_conn_v,
cnodeid_t xbow_peer)
{
xwidget_info_t widget_info, peer_widget_info;
char peer_path[256];
char *p;
devfs_handle_t peer_hubv;
hubinfo_t peer_hub_info;
/* get the peer hub's widgetid */
peer_hubv = NODEPDA(xbow_peer)->node_vertex;
peer_hub_info = NULL;
hubinfo_get(peer_hubv, &peer_hub_info);
if (peer_hub_info == NULL)
return 0;
if (hwgraph_info_get_LBL(conn_v, INFO_LBL_XWIDGET,
(arbitrary_info_t *)&widget_info) == GRAPH_SUCCESS) {
NEW(peer_widget_info);
peer_widget_info->w_vertex = peer_conn_v;
peer_widget_info->w_id = widget_info->w_id;
peer_widget_info->w_master = peer_hubv;
peer_widget_info->w_masterid = peer_hub_info->h_widgetid;
/* structure copy */
peer_widget_info->w_hwid = widget_info->w_hwid;
peer_widget_info->w_efunc = 0;
peer_widget_info->w_einfo = 0;
peer_widget_info->w_name = kmalloc(strlen(peer_path) + 1, GFP_KERNEL);
strcpy(peer_widget_info->w_name, peer_path);
if (hwgraph_info_add_LBL(peer_conn_v, INFO_LBL_XWIDGET,
(arbitrary_info_t)peer_widget_info) != GRAPH_SUCCESS) {
DEL(peer_widget_info);
return 0;
}
xwidget_info_set(peer_conn_v, peer_widget_info);
return 1;
}
printk("pic_bus1_widget_info_dup: "
"cannot get INFO_LBL_XWIDGET from 0x%lx\n", conn_v);
return 0;
}
/*
* If this PIC is attached to two Cbricks ("dual-ported") then
* attach each bus to opposite Cbricks.
*
* If successful, return a new vertex suitable for attaching the PIC bus.
* If not successful, return zero and both buses will attach to the
* vertex passed into pic_attach().
*/
devfs_handle_t
pic_bus1_redist(nasid_t nasid, devfs_handle_t conn_v)
{
cnodeid_t cnode = NASID_TO_COMPACT_NODEID(nasid);
cnodeid_t xbow_peer = -1;
char pathname[256], peer_path[256], tmpbuf[256];
char *p;
int rc;
devfs_handle_t peer_conn_v;
int pos;
slabid_t slab;
if (NODEPDA(cnode)->xbow_peer >= 0) { /* if dual-ported */
/* create a path for this widget on the peer Cbrick */
/* pcibr widget hw/module/001c11/slab/0/Pbrick/xtalk/12 */
/* sprintf(pathname, "%v", conn_v); */
xbow_peer = NASID_TO_COMPACT_NODEID(NODEPDA(cnode)->xbow_peer);
pos = devfs_generate_path(conn_v, tmpbuf, 256);
strcpy(pathname, &tmpbuf[pos]);
p = pathname + strlen("hw/module/001c01/slab/0/");
memset(tmpbuf, 0, 16);
format_module_id(tmpbuf, geo_module((NODEPDA(xbow_peer))->geoid), MODULE_FORMAT_BRIEF);
slab = geo_slab((NODEPDA(xbow_peer))->geoid);
sprintf(peer_path, "module/%s/slab/%d/%s", tmpbuf, (int)slab, p);
/* Look for vertex for this widget on the peer Cbrick.
* Expect GRAPH_NOT_FOUND.
*/
rc = hwgraph_traverse(hwgraph_root, peer_path, &peer_conn_v);
if (GRAPH_SUCCESS == rc)
printk("pic_attach: found unexpected vertex: 0x%lx\n",
peer_conn_v);
else if (GRAPH_NOT_FOUND != rc) {
printk("pic_attach: hwgraph_traverse unexpectedly"
" returned 0x%x\n", rc);
} else {
/* try to add the widget vertex to the peer Cbrick */
rc = hwgraph_path_add(hwgraph_root, peer_path, &peer_conn_v);
if (GRAPH_SUCCESS != rc)
printk("pic_attach: hwgraph_path_add"
" failed with 0x%x\n", rc);
else {
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
"pic_bus1_redist: added vertex %v\n", peer_conn_v));
/* Now hang appropiate stuff off of the new
* vertex. We bail out if we cannot add something.
* In that case, we don't remove the newly added
* vertex but that should be safe and we don't
* really expect the additions to fail anyway.
*/
#if 0
if (!pic_bus1_inventory_dup(conn_v, peer_conn_v))
return 0;
pic_bus1_device_desc_dup(conn_v, peer_conn_v);
#endif
if (!pic_bus1_widget_info_dup(conn_v, peer_conn_v, xbow_peer))
return 0;
return peer_conn_v;
}
}
}
return 0;
}
int
pic_attach(devfs_handle_t conn_v)
{
int rc;
bridge_t *bridge0, *bridge1 = (bridge_t *)0;
devfs_handle_t pcibr_vhdl0, pcibr_vhdl1 = (devfs_handle_t)0;
pcibr_soft_t bus0_soft, bus1_soft = (pcibr_soft_t)0;
devfs_handle_t conn_v0, conn_v1, peer_conn_v;
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v, "pic_attach()\n"));
bridge0 = (bridge_t *) xtalk_piotrans_addr(conn_v, NULL,
0, sizeof(bridge_t), 0);
bridge1 = (bridge_t *)((char *)bridge0 + PIC_BUS1_OFFSET);
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
"pic_attach: bridge0=0x%x, bridge1=0x%x\n",
bridge0, bridge1));
conn_v0 = conn_v1 = conn_v;
/* If dual-ported then split the two PIC buses across both Cbricks */
if (peer_conn_v = pic_bus1_redist(NASID_GET(bridge0), conn_v))
conn_v1 = peer_conn_v;
/*
* Create the vertex for the PCI buses, which week
* will also use to hold the pcibr_soft and
* which will be the "master" vertex for all the
* pciio connection points we will hang off it.
* This needs to happen before we call nic_bridge_vertex_info
* as we are some of the *_vmc functions need access to the edges.
*
* Opening this vertex will provide access to
* the Bridge registers themselves.
*/
/* FIXME: what should the hwgraph path look like ? */
rc = hwgraph_path_add(conn_v0, EDGE_LBL_PCIX_0, &pcibr_vhdl0);
ASSERT(rc == GRAPH_SUCCESS);
rc = hwgraph_path_add(conn_v1, EDGE_LBL_PCIX_1, &pcibr_vhdl1);
ASSERT(rc == GRAPH_SUCCESS);
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
"pic_attach: pcibr_vhdl0=%v, pcibr_vhdl1=%v\n",
pcibr_vhdl0, pcibr_vhdl1));
/* register pci provider array */
pciio_provider_register(pcibr_vhdl0, &pci_pic_provider);
pciio_provider_register(pcibr_vhdl1, &pci_pic_provider);
pciio_provider_startup(pcibr_vhdl0);
pciio_provider_startup(pcibr_vhdl1);
pcibr_attach2(conn_v0, bridge0, pcibr_vhdl0, 0, &bus0_soft);
pcibr_attach2(conn_v1, bridge1, pcibr_vhdl1, 1, &bus1_soft);
/* save a pointer to the PIC's other bus's soft struct */
bus0_soft->bs_peers_soft = bus1_soft;
bus1_soft->bs_peers_soft = bus0_soft;
bus0_soft->bs_peers_soft = (pcibr_soft_t)0;
PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
"pic_attach: bus0_soft=0x%x, bus1_soft=0x%x\n",
bus0_soft, bus1_soft));
return 0;
}
/*
* pci provider functions
*
* mostly in pcibr.c but if any are needed here then
* this might be a way to get them here.
*/
pciio_provider_t pci_pic_provider =
{
(pciio_piomap_alloc_f *) pcibr_piomap_alloc,
(pciio_piomap_free_f *) pcibr_piomap_free,
(pciio_piomap_addr_f *) pcibr_piomap_addr,
(pciio_piomap_done_f *) pcibr_piomap_done,
(pciio_piotrans_addr_f *) pcibr_piotrans_addr,
(pciio_piospace_alloc_f *) pcibr_piospace_alloc,
(pciio_piospace_free_f *) pcibr_piospace_free,
(pciio_dmamap_alloc_f *) pcibr_dmamap_alloc,
(pciio_dmamap_free_f *) pcibr_dmamap_free,
(pciio_dmamap_addr_f *) pcibr_dmamap_addr,
(pciio_dmamap_list_f *) pcibr_dmamap_list,
(pciio_dmamap_done_f *) pcibr_dmamap_done,
(pciio_dmatrans_addr_f *) pcibr_dmatrans_addr,
(pciio_dmatrans_list_f *) pcibr_dmatrans_list,
(pciio_dmamap_drain_f *) pcibr_dmamap_drain,
(pciio_dmaaddr_drain_f *) pcibr_dmaaddr_drain,
(pciio_dmalist_drain_f *) pcibr_dmalist_drain,
(pciio_intr_alloc_f *) pcibr_intr_alloc,
(pciio_intr_free_f *) pcibr_intr_free,
(pciio_intr_connect_f *) pcibr_intr_connect,
(pciio_intr_disconnect_f *) pcibr_intr_disconnect,
(pciio_intr_cpu_get_f *) pcibr_intr_cpu_get,
(pciio_provider_startup_f *) pcibr_provider_startup,
(pciio_provider_shutdown_f *) pcibr_provider_shutdown,
(pciio_reset_f *) pcibr_reset,
(pciio_write_gather_flush_f *) pcibr_write_gather_flush,
(pciio_endian_set_f *) pcibr_endian_set,
(pciio_priority_set_f *) pcibr_priority_set,
(pciio_config_get_f *) pcibr_config_get,
(pciio_config_set_f *) pcibr_config_set,
(pciio_error_devenable_f *) 0,
(pciio_error_extract_f *) 0,
(pciio_driver_reg_callback_f *) pcibr_driver_reg_callback,
(pciio_driver_unreg_callback_f *) pcibr_driver_unreg_callback,
(pciio_device_unregister_f *) pcibr_device_unregister,
(pciio_dma_enabled_f *) pcibr_dma_enabled,
};
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/config.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/pci/pciba.h>
#include <linux/smp.h>
extern void mlreset(void);
extern int init_hcl(void);
extern void klgraph_hack_init(void);
extern void hubspc_init(void);
extern void pciio_init(void);
extern void pcibr_init(void);
extern void xtalk_init(void);
extern void xbow_init(void);
extern void xbmon_init(void);
extern void pciiox_init(void);
extern void pic_init(void);
extern void usrpci_init(void);
extern void ioc3_init(void);
extern void initialize_io(void);
extern void klhwg_add_all_modules(devfs_handle_t);
extern void klhwg_add_all_nodes(devfs_handle_t);
void sn_mp_setup(void);
extern devfs_handle_t hwgraph_root;
extern void io_module_init(void);
extern void pci_bus_cvlink_init(void);
extern void temp_hack(void);
extern int pci_bus_to_hcl_cvlink(void);
/* #define DEBUG_IO_INIT 1 */
#ifdef DEBUG_IO_INIT
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif /* DEBUG_IO_INIT */
/*
* per_hub_init
*
* This code is executed once for each Hub chip.
*/
static void
per_hub_init(cnodeid_t cnode)
{
nasid_t nasid;
nodepda_t *npdap;
ii_icmr_u_t ii_icmr;
ii_ibcr_u_t ii_ibcr;
nasid = COMPACT_TO_NASID_NODEID(cnode);
ASSERT(nasid != INVALID_NASID);
ASSERT(NASID_TO_COMPACT_NODEID(nasid) == cnode);
npdap = NODEPDA(cnode);
REMOTE_HUB_S(nasid, IIO_IWEIM, 0x8000);
/*
* Set the total number of CRBs that can be used.
*/
ii_icmr.ii_icmr_regval= 0x0;
ii_icmr.ii_icmr_fld_s.i_c_cnt = 0xf;
REMOTE_HUB_S(nasid, IIO_ICMR, ii_icmr.ii_icmr_regval);
/*
* Set the number of CRBs that both of the BTEs combined
* can use minus 1.
*/
ii_ibcr.ii_ibcr_regval= 0x0;
ii_ibcr.ii_ibcr_fld_s.i_count = 0x8;
REMOTE_HUB_S(nasid, IIO_IBCR, ii_ibcr.ii_ibcr_regval);
/*
* Set CRB timeout to be 10ms.
*/
#ifdef BRINGUP2
REMOTE_HUB_S(nasid, IIO_ICTP, 0xffffff );
REMOTE_HUB_S(nasid, IIO_ICTO, 0xff);
//REMOTE_HUB_S(nasid, IIO_IWI, 0x00FF00FF00FFFFFF);
#endif
/* Initialize error interrupts for this hub. */
hub_error_init(cnode);
}
/*
* This routine is responsible for the setup of all the IRIX hwgraph style
* stuff that's been pulled into linux. It's called by sn_pci_find_bios which
* is called just before the generic Linux PCI layer does its probing (by
* platform_pci_fixup aka sn_pci_fixup).
*
* It is very IMPORTANT that this call is only made by the Master CPU!
*
*/
void
sgi_master_io_infr_init(void)
{
int cnode;
extern void kdba_io_init();
/*
* Do any early init stuff .. einit_tbl[] etc.
*/
init_hcl(); /* Sets up the hwgraph compatibility layer with devfs */
/*
* initialize the Linux PCI to xwidget vertexes ..
*/
pci_bus_cvlink_init();
kdba_io_init();
#ifdef BRINGUP
/*
* Hack to provide statically initialzed klgraph entries.
*/
DBG("--> sgi_master_io_infr_init: calling klgraph_hack_init()\n");
klgraph_hack_init();
#endif /* BRINGUP */
/*
* This is the Master CPU. Emulate mlsetup and main.c in Irix.
*/
mlreset();
/*
* allowboot() is called by kern/os/main.c in main()
* Emulate allowboot() ...
* per_cpu_init() - only need per_hub_init()
* cpu_io_setup() - Nothing to do.
*
*/
sn_mp_setup();
for (cnode = 0; cnode < numnodes; cnode++) {
per_hub_init(cnode);
}
/* We can do headless hub cnodes here .. */
/*
* io_init[] stuff.
*
* Get SGI IO Infrastructure drivers to init and register with
* each other etc.
*/
hubspc_init();
pciio_init();
pcibr_init();
pic_init();
xtalk_init();
xbow_init();
xbmon_init();
pciiox_init();
usrpci_init();
ioc3_init();
/*
*
* Our IO Infrastructure drivers are in place ..
* Initialize the whole IO Infrastructure .. xwidget/device probes.
*
*/
initialize_io();
pci_bus_to_hcl_cvlink();
#ifdef CONFIG_PCIBA
DBG("--> sgi_master_io_infr_init: calling pciba_init()\n");
#ifndef BRINGUP2
pciba_init();
#endif
#endif
}
/*
* One-time setup for MP SN.
* Allocate per-node data, slurp prom klconfig information and
* convert it to hwgraph information.
*/
void
sn_mp_setup(void)
{
cpuid_t cpu;
for (cpu = 0; cpu < NR_CPUS; cpu++) {
/* Skip holes in CPU space */
if (cpu_enabled(cpu)) {
init_platform_pda(cpu);
}
}
/*
* Initialize platform-dependent vertices in the hwgraph:
* module
* node
* cpu
* memory
* slot
* hub
* router
* xbow
*/
io_module_init(); /* Use to be called module_init() .. */
klhwg_add_all_modules(hwgraph_root);
klhwg_add_all_nodes(hwgraph_root);
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
*/
#ident "$Revision: 1.167 $"
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/hw_irq.h>
#include <asm/sn/sgi.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn2/shub_mmr_t.h>
#include <asm/sal.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/sndrv.h>
/*
* Shub WAR for Xbridge Little Endian problem:
* Xbridge has to run in BIG ENDIAN even with Shub.
*/
/*
* io_sh_swapper: Turn on Shub byte swapping.
* All data destined to and from Shub to XIO are byte-swapped.
*/
void
io_sh_swapper(nasid_t nasid, int onoff)
{
ii_iwc_u_t ii_iwc;
ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
ii_iwc.ii_iwc_fld_s.i_dma_byte_swap = onoff;
REMOTE_HUB_S(nasid, IIO_IWC, ii_iwc.ii_iwc_regval);
ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
}
/*
* io_get_sh_swapper: Return current Swap mode.
* 1 = Swap on, 0 = Swap off.
*/
int
io_get_sh_swapper(nasid_t nasid)
{
ii_iwc_u_t ii_iwc;
ii_iwc.ii_iwc_regval = REMOTE_HUB_L(nasid, IIO_IWC);
return(ii_iwc.ii_iwc_fld_s.i_dma_byte_swap);
}
#define SHUB_NUM_ECF_REGISTERS 8
static uint32_t shub_perf_counts[SHUB_NUM_ECF_REGISTERS];
static shubreg_t shub_perf_counts_regs[SHUB_NUM_ECF_REGISTERS] = {
SH_PERFORMANCE_COUNTER0,
SH_PERFORMANCE_COUNTER1,
SH_PERFORMANCE_COUNTER2,
SH_PERFORMANCE_COUNTER3,
SH_PERFORMANCE_COUNTER4,
SH_PERFORMANCE_COUNTER5,
SH_PERFORMANCE_COUNTER6,
SH_PERFORMANCE_COUNTER7
};
static inline void
shub_mmr_write(cnodeid_t cnode, shubreg_t reg, uint64_t val)
{
int nasid = cnodeid_to_nasid(cnode);
volatile uint64_t *addr = (uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
*addr = val;
__ia64_mf_a();
}
static inline void
shub_mmr_write32(cnodeid_t cnode, shubreg_t reg, uint32_t val)
{
int nasid = cnodeid_to_nasid(cnode);
volatile uint32_t *addr = (uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
*addr = val;
__ia64_mf_a();
}
static inline uint64_t
shub_mmr_read(cnodeid_t cnode, shubreg_t reg)
{
int nasid = cnodeid_to_nasid(cnode);
volatile uint64_t val;
val = *(uint64_t *)(GLOBAL_MMR_ADDR(nasid, reg));
__ia64_mf_a();
return val;
}
static inline uint32_t
shub_mmr_read32(cnodeid_t cnode, shubreg_t reg)
{
int nasid = cnodeid_to_nasid(cnode);
volatile uint32_t val;
val = *(uint32_t *)(GLOBAL_MMR_ADDR(nasid, reg));
__ia64_mf_a();
return val;
}
static int
reset_shub_stats(cnodeid_t cnode)
{
int i;
for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
shub_perf_counts[i] = 0;
shub_mmr_write32(cnode, shub_perf_counts_regs[i], 0);
}
return 0;
}
static int
configure_shub_stats(cnodeid_t cnode, unsigned long arg)
{
uint64_t *p = (uint64_t *)arg;
uint64_t i;
uint64_t regcnt;
uint64_t regval[2];
if (copy_from_user((void *)&regcnt, p, sizeof(regcnt)))
return -EFAULT;
for (p++, i=0; i < regcnt; i++, p += 2) {
if (copy_from_user((void *)regval, (void *)p, sizeof(regval)))
return -EFAULT;
if (regval[0] & 0x7) {
printk("Error: configure_shub_stats: unaligned address 0x%016lx\n", regval[0]);
return -EINVAL;
}
shub_mmr_write(cnode, (shubreg_t)regval[0], regval[1]);
}
return 0;
}
static int
capture_shub_stats(cnodeid_t cnode, uint32_t *counts)
{
int i;
for (i=0; i < SHUB_NUM_ECF_REGISTERS; i++) {
counts[i] = shub_mmr_read32(cnode, shub_perf_counts_regs[i]);
}
return 0;
}
static int
shubstats_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
cnodeid_t cnode;
uint64_t longarg;
devfs_handle_t d;
int nasid;
if ((d = devfs_get_handle_from_inode(inode)) == NULL)
return -ENODEV;
cnode = (cnodeid_t)hwgraph_fastinfo_get(d);
switch (cmd) {
case SNDRV_SHUB_CONFIGURE:
return configure_shub_stats(cnode, arg);
break;
case SNDRV_SHUB_RESETSTATS:
reset_shub_stats(cnode);
break;
case SNDRV_SHUB_INFOSIZE:
longarg = sizeof(shub_perf_counts);
if (copy_to_user((void *)arg, &longarg, sizeof(longarg))) {
return -EFAULT;
}
break;
case SNDRV_SHUB_GETSTATS:
capture_shub_stats(cnode, shub_perf_counts);
if (copy_to_user((void *)arg, shub_perf_counts,
sizeof(shub_perf_counts))) {
return -EFAULT;
}
break;
case SNDRV_SHUB_GETNASID:
nasid = cnodeid_to_nasid(cnode);
if (copy_to_user((void *)arg, &nasid,
sizeof(nasid))) {
return -EFAULT;
}
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations shub_mon_fops = {
ioctl: shubstats_ioctl,
};
/* $Id: shubio.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000,2002-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/smp.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/intr.h>
#include <asm/sn/ioerror_handling.h>
#include <asm/sn/ioerror.h>
#include <asm/sn/sn2/shubio.h>
error_state_t error_state_get(devfs_handle_t v);
error_return_code_t error_state_set(devfs_handle_t v,error_state_t new_state);
/*
* Get the xtalk provider function pointer for the
* specified hub.
*/
/*ARGSUSED*/
int
hub_xp_error_handler(
devfs_handle_t hub_v,
nasid_t nasid,
int error_code,
ioerror_mode_t mode,
ioerror_t *ioerror)
{
/*REFERENCED*/
hubreg_t iio_imem;
devfs_handle_t xswitch;
error_state_t e_state;
cnodeid_t cnode;
/*
* Before walking down to the next level, check if
* the I/O link is up. If it's been disabled by the
* hub ii for some reason, we can't even touch the
* widget registers.
*/
iio_imem = REMOTE_HUB_L(nasid, IIO_IMEM);
if (!(iio_imem & (IIO_IMEM_B0ESD|IIO_IMEM_W0ESD))){
/*
* IIO_IMEM_B0ESD getting set, indicates II shutdown
* on HUB0 parts.. Hopefully that's not true for
* Hub1 parts..
*
*
* If either one of them is shut down, can't
* go any further.
*/
return IOERROR_XTALKLEVEL;
}
/* Get the error state of the hub */
e_state = error_state_get(hub_v);
cnode = NASID_TO_COMPACT_NODEID(nasid);
xswitch = NODEPDA(cnode)->basew_xc;
/* Set the error state of the crosstalk device to that of
* hub.
*/
if (error_state_set(xswitch , e_state) ==
ERROR_RETURN_CODE_CANNOT_SET_STATE)
return(IOERROR_UNHANDLED);
/* Clean the error state of the hub if we are in the action handling
* phase.
*/
if (e_state == ERROR_STATE_ACTION)
(void)error_state_set(hub_v, ERROR_STATE_NONE);
/* hand the error off to the switch or the directly
* connected crosstalk device.
*/
return xtalk_error_handler(xswitch,
error_code, mode, ioerror);
}
/*
* Check if the widget in error has been enabled for PIO accesses
*/
int
is_widget_pio_enabled(ioerror_t *ioerror)
{
cnodeid_t src_node;
nasid_t src_nasid;
hubreg_t ii_iowa;
xwidgetnum_t widget;
iopaddr_t p;
/* Get the node where the PIO error occurred */
IOERROR_GETVALUE(p,ioerror, srcnode);
src_node = p;
if (src_node == CNODEID_NONE)
return(0);
/* Get the nasid for the cnode */
src_nasid = COMPACT_TO_NASID_NODEID(src_node);
if (src_nasid == INVALID_NASID)
return(0);
/* Read the Outbound widget access register for this hub */
ii_iowa = REMOTE_HUB_L(src_nasid, IIO_IOWA);
IOERROR_GETVALUE(p,ioerror, widgetnum);
widget = p;
/* Check if the PIOs to the widget with PIO error have been
* enabled.
*/
if (ii_iowa & IIO_IOWA_WIDGET(widget))
return(1);
return(0);
}
/*
* Hub IO error handling.
*
* Gets invoked for different types of errors found at the hub.
* Typically this includes situations from bus error or due to
* an error interrupt (mostly generated at the hub).
*/
int
hub_ioerror_handler(
devfs_handle_t hub_v,
int error_code,
int mode,
struct io_error_s *ioerror)
{
hubinfo_t hinfo; /* Hub info pointer */
nasid_t nasid;
int retval = 0;
/*REFERENCED*/
iopaddr_t p;
IOERROR_DUMP("hub_ioerror_handler", error_code, mode, ioerror);
hubinfo_get(hub_v, &hinfo);
if (!hinfo){
/* Print an error message and return */
goto end;
}
nasid = hinfo->h_nasid;
switch(error_code) {
case PIO_READ_ERROR:
/*
* Cpu got a bus error while accessing IO space.
* hubaddr field in ioerror structure should have
* the IO address that caused access error.
*/
/*
* Identify if the physical address in hub_error_data
* corresponds to small/large window, and accordingly,
* get the xtalk address.
*/
/*
* Evaluate the widget number and the widget address that
* caused the error. Use 'vaddr' if it's there.
* This is typically true either during probing
* or a kernel driver getting into trouble.
* Otherwise, use paddr to figure out widget details
* This is typically true for user mode bus errors while
* accessing I/O space.
*/
IOERROR_GETVALUE(p,ioerror,vaddr);
if (p){
/*
* If neither in small window nor in large window range,
* outright reject it.
*/
IOERROR_GETVALUE(p,ioerror,vaddr);
if (NODE_SWIN_ADDR(nasid, (paddr_t)p)){
iopaddr_t hubaddr;
xwidgetnum_t widgetnum;
iopaddr_t xtalkaddr;
IOERROR_GETVALUE(p,ioerror,hubaddr);
hubaddr = p;
widgetnum = SWIN_WIDGETNUM(hubaddr);
xtalkaddr = SWIN_WIDGETADDR(hubaddr);
/*
* differentiate local register vs IO space access
*/
IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);
} else if (NODE_BWIN_ADDR(nasid, (paddr_t)p)){
/*
* Address corresponds to large window space.
* Convert it to xtalk address.
*/
int bigwin;
hub_piomap_t bw_piomap;
xtalk_piomap_t xt_pmap = NULL;
iopaddr_t hubaddr;
xwidgetnum_t widgetnum;
iopaddr_t xtalkaddr;
IOERROR_GETVALUE(p,ioerror,hubaddr);
hubaddr = p;
/*
* Have to loop to find the correct xtalk_piomap
* because the're not allocated on a one-to-one
* basis to the window number.
*/
for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) {
bw_piomap = hubinfo_bwin_piomap_get(hinfo,
bigwin);
if (bw_piomap->hpio_bigwin_num ==
(BWIN_WINDOWNUM(hubaddr) - 1)) {
xt_pmap = hub_piomap_xt_piomap(bw_piomap);
break;
}
}
ASSERT(xt_pmap);
widgetnum = xtalk_pio_target_get(xt_pmap);
xtalkaddr = xtalk_pio_xtalk_addr_get(xt_pmap) + BWIN_WIDGETADDR(hubaddr);
IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);
/*
* Make sure that widgetnum doesnot map to hub
* register widget number, as we never use
* big window to access hub registers.
*/
ASSERT(widgetnum != HUB_REGISTER_WIDGET);
}
} else if (IOERROR_FIELDVALID(ioerror,hubaddr)) {
iopaddr_t hubaddr;
xwidgetnum_t widgetnum;
iopaddr_t xtalkaddr;
IOERROR_GETVALUE(p,ioerror,hubaddr);
hubaddr = p;
if (BWIN_WINDOWNUM(hubaddr)){
int window = BWIN_WINDOWNUM(hubaddr) - 1;
hubreg_t itte;
itte = (hubreg_t)HUB_L(IIO_ITTE_GET(nasid, window));
widgetnum = (itte >> IIO_ITTE_WIDGET_SHIFT) &
IIO_ITTE_WIDGET_MASK;
xtalkaddr = (((itte >> IIO_ITTE_OFFSET_SHIFT) &
IIO_ITTE_OFFSET_MASK) <<
BWIN_SIZE_BITS) +
BWIN_WIDGETADDR(hubaddr);
} else {
widgetnum = SWIN_WIDGETNUM(hubaddr);
xtalkaddr = SWIN_WIDGETADDR(hubaddr);
}
IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);
} else {
IOERROR_DUMP("hub_ioerror_handler", error_code,
mode, ioerror);
IOERR_PRINTF(printk(
"hub_ioerror_handler: Invalid address passed"));
return IOERROR_INVALIDADDR;
}
IOERROR_GETVALUE(p,ioerror,widgetnum);
if ((p) == HUB_REGISTER_WIDGET) {
/*
* Error in accessing Hub local register
* This should happen mostly in SABLE mode..
*/
retval = 0;
} else {
/* Make sure that the outbound widget access for this
* widget is enabled.
*/
if (!is_widget_pio_enabled(ioerror)) {
if (error_state_get(hub_v) ==
ERROR_STATE_ACTION)
ioerror_dump("No outbound widget"
" access - ",
error_code, mode, ioerror);
return(IOERROR_HANDLED);
}
retval = hub_xp_error_handler(
hub_v, nasid, error_code, mode, ioerror);
}
IOERR_PRINTF(printk(
"hub_ioerror_handler:PIO_READ_ERROR return: %d",
retval));
break;
case PIO_WRITE_ERROR:
/*
* This hub received an interrupt indicating a widget
* attached to this hub got a timeout.
* widgetnum field should be filled to indicate the
* widget that caused error.
*
* NOTE: This hub may have nothing to do with this error.
* We are here since the widget attached to the xbow
* gets its PIOs through this hub.
*
* There is nothing that can be done at this level.
* Just invoke the xtalk error handling mechanism.
*/
IOERROR_GETVALUE(p,ioerror,widgetnum);
if ((p) == HUB_REGISTER_WIDGET) {
} else {
/* Make sure that the outbound widget access for this
* widget is enabled.
*/
if (!is_widget_pio_enabled(ioerror)) {
if (error_state_get(hub_v) ==
ERROR_STATE_ACTION)
ioerror_dump("No outbound widget"
" access - ",
error_code, mode, ioerror);
return(IOERROR_HANDLED);
}
retval = hub_xp_error_handler(
hub_v, nasid, error_code, mode, ioerror);
}
break;
case DMA_READ_ERROR:
/*
* DMA Read error always ends up generating an interrupt
* at the widget level, and never at the hub level. So,
* we don't expect to come here any time
*/
ASSERT(0);
retval = IOERROR_UNHANDLED;
break;
case DMA_WRITE_ERROR:
/*
* DMA Write error is generated when a write by an I/O
* device could not be completed. Problem is, device is
* totally unaware of this problem, and would continue
* writing to system memory. So, hub has a way to send
* an error interrupt on the first error, and bitbucket
* all further write transactions.
* Coming here indicates that hub detected one such error,
* and we need to handle it.
*
* Hub interrupt handler would have extracted physaddr,
* widgetnum, and widgetdevice from the CRB
*
* There is nothing special to do here, since gathering
* data from crb's is done elsewhere. Just pass the
* error to xtalk layer.
*/
retval = hub_xp_error_handler(hub_v, nasid, error_code, mode,
ioerror);
break;
default:
ASSERT(0);
return IOERROR_BADERRORCODE;
}
/*
* If error was not handled, we may need to take certain action
* based on the error code.
* For e.g. in case of PIO_READ_ERROR, we may need to release the
* PIO Read entry table (they are sticky after errors).
* Similarly other cases.
*
* Further Action TBD
*/
end:
if (retval == IOERROR_HWGRAPH_LOOKUP) {
/*
* If we get errors very early, we can't traverse
* the path using hardware graph.
* To handle this situation, we need a functions
* which don't depend on the hardware graph vertex to
* handle errors. This break the modularity of the
* existing code. Instead we print out the reason for
* not handling error, and return. On return, all the
* info collected would be dumped. This should provide
* sufficient info to analyse the error.
*/
printk("Unable to handle IO error: hardware graph not setup\n");
}
return retval;
}
#define L_BITSMINOR 18
#define L_MAXMAJ 0x1ff
#define emajor(x) (int )(((unsigned )(x)>>L_BITSMINOR) & L_MAXMAJ)
#define dev_is_vertex(dev) (emajor((dev_t)(dev)) == 0)
#define INFO_LBL_ERROR_STATE "error_state"
#define v_error_state_get(v,s) \
(hwgraph_info_get_LBL(v,INFO_LBL_ERROR_STATE, (arbitrary_info_t *)&s))
#define v_error_state_set(v,s,replace) \
(replace ? \
hwgraph_info_replace_LBL(v,INFO_LBL_ERROR_STATE,(arbitrary_info_t)s,0) :\
hwgraph_info_add_LBL(v,INFO_LBL_ERROR_STATE, (arbitrary_info_t)s))
#define v_error_state_clear(v) \
(hwgraph_info_remove_LBL(v,INFO_LBL_ERROR_STATE,0))
/*
* error_state_get
* Get the state of the vertex.
* Returns ERROR_STATE_INVALID on failure
* current state otherwise
*/
error_state_t
error_state_get(devfs_handle_t v)
{
error_state_t s;
/* Check if we have a valid hwgraph vertex */
if (!dev_is_vertex(v))
return(ERROR_STATE_NONE);
/* Get the labelled info hanging off the vertex which corresponds
* to the state.
*/
if (v_error_state_get(v, s) != GRAPH_SUCCESS) {
return(ERROR_STATE_NONE);
}
return(s);
}
/*
* error_state_set
* Set the state of the vertex
* Returns ERROR_RETURN_CODE_CANNOT_SET_STATE on failure
* ERROR_RETURN_CODE_SUCCESS otherwise
*/
error_return_code_t
error_state_set(devfs_handle_t v,error_state_t new_state)
{
error_state_t old_state;
boolean_t replace = B_TRUE;
/* Check if we have a valid hwgraph vertex */
if (!dev_is_vertex(v))
return(ERROR_RETURN_CODE_GENERAL_FAILURE);
/* This means that the error state needs to be cleaned */
if (new_state == ERROR_STATE_NONE) {
/* Make sure that we have an error state */
if (v_error_state_get(v,old_state) == GRAPH_SUCCESS)
v_error_state_clear(v);
return(ERROR_RETURN_CODE_SUCCESS);
}
/* Check if the state information has been set at least once
* for this vertex.
*/
if (v_error_state_get(v,old_state) != GRAPH_SUCCESS)
replace = B_FALSE;
if (v_error_state_set(v,new_state,replace) != GRAPH_SUCCESS) {
return(ERROR_RETURN_CODE_CANNOT_SET_STATE);
}
return(ERROR_RETURN_CODE_SUCCESS);
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/sn/sgi.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn2/sn_private.h>
#include <asm/sn/sn2/shubio.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/hack.h>
#include <asm/sn/pci/bridge.h>
#include <asm/sn/xtalk/xtalk_private.h>
#include <asm/sn/simulator.h>
/* #define DEBUG 1 */
/* #define XBOW_DEBUG 1 */
/* #define DEBUG_ERROR 1 */
/*
* Files needed to get the device driver entry points
*/
#include <asm/sn/xtalk/xbow.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/xtalk/xswitch.h>
#include <asm/sn/xtalk/xwidget.h>
#include <asm/sn/prio.h>
#include <asm/sn/hcl_util.h>
#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
#define DEL(ptr) (kfree(ptr))
int xbow_devflag = D_MP;
/*
* This file supports the Xbow chip. Main functions: initializtion,
* error handling, and GBR.
*/
/*
* each vertex corresponding to an xbow chip
* has a "fastinfo" pointer pointing at one
* of these things.
*/
typedef struct xbow_soft_s *xbow_soft_t;
struct xbow_soft_s {
devfs_handle_t conn; /* our connection point */
devfs_handle_t vhdl; /* xbow's private vertex */
devfs_handle_t busv; /* the xswitch vertex */
xbow_t *base; /* PIO pointer to crossbow chip */
char *name; /* hwgraph name */
xbow_perf_t xbow_perfcnt[XBOW_PERF_COUNTERS];
xbow_perf_link_t xbow_perflink[MAX_XBOW_PORTS];
xbow_link_status_t xbow_link_status[MAX_XBOW_PORTS];
spinlock_t xbow_perf_lock;
int link_monitor;
widget_cfg_t *wpio[MAX_XBOW_PORTS]; /* cached PIO pointer */
/* Bandwidth allocation state. Bandwidth values are for the
* destination port since contention happens there.
* Implicit mapping from xbow ports (8..f) -> (0..7) array indices.
*/
spinlock_t xbow_bw_alloc_lock; /* bw allocation lock */
unsigned long long bw_hiwm[MAX_XBOW_PORTS]; /* hiwater mark values */
unsigned long long bw_cur_used[MAX_XBOW_PORTS]; /* bw used currently */
};
#define xbow_soft_set(v,i) hwgraph_fastinfo_set((v), (arbitrary_info_t)(i))
#define xbow_soft_get(v) ((xbow_soft_t)hwgraph_fastinfo_get((v)))
/*
* Function Table of Contents
*/
void xbow_mlreset(xbow_t *);
void xbow_init(void);
int xbow_attach(devfs_handle_t);
int xbow_open(devfs_handle_t *, int, int, cred_t *);
int xbow_close(devfs_handle_t, int, int, cred_t *);
int xbow_map(devfs_handle_t, vhandl_t *, off_t, size_t, uint);
int xbow_unmap(devfs_handle_t, vhandl_t *);
int xbow_ioctl(devfs_handle_t, int, void *, int, struct cred *, int *);
int xbow_widget_present(xbow_t *, int);
static int xbow_link_alive(xbow_t *, int);
devfs_handle_t xbow_widget_lookup(devfs_handle_t, int);
void xbow_intr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);
void xbow_update_perf_counters(devfs_handle_t);
xbow_perf_link_t *xbow_get_perf_counters(devfs_handle_t);
int xbow_enable_perf_counter(devfs_handle_t, int, int, int);
xbow_link_status_t *xbow_get_llp_status(devfs_handle_t);
void xbow_update_llp_status(devfs_handle_t);
int xbow_disable_llp_monitor(devfs_handle_t);
int xbow_enable_llp_monitor(devfs_handle_t);
int xbow_prio_bw_alloc(devfs_handle_t, xwidgetnum_t, xwidgetnum_t,
unsigned long long, unsigned long long);
static void xbow_setwidint(xtalk_intr_t);
void idbg_xbowregs(int64_t);
xswitch_reset_link_f xbow_reset_link;
xswitch_provider_t xbow_provider =
{
xbow_reset_link,
};
/*
* This is the file operation table for the pcibr driver.
* As each of the functions are implemented, put the
* appropriate function name below.
*/
static int xbow_mmap(struct file * file, struct vm_area_struct * vma);
struct file_operations xbow_fops = {
owner: THIS_MODULE,
llseek: NULL,
read: NULL,
write: NULL,
readdir: NULL,
poll: NULL,
ioctl: NULL,
mmap: xbow_mmap,
open: xbow_open,
flush: NULL,
release: NULL,
fsync: NULL,
fasync: NULL,
lock: NULL,
readv: NULL,
writev: NULL,
sendpage: NULL,
get_unmapped_area: NULL
};
static int
xbow_mmap(struct file * file, struct vm_area_struct * vma)
{
unsigned long phys_addr;
int error = 0;
phys_addr = (unsigned long)file->private_data & ~0xc000000000000000; /* Mask out the Uncache bits */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_flags |= VM_RESERVED | VM_IO;
error = io_remap_page_range(vma, vma->vm_start, phys_addr,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
return(error);
}
/*
* xbow_mlreset: called at mlreset time if the
* platform specific code determines that there is
* a crossbow in a critical path that must be
* functional before the driver would normally get
* the device properly set up.
*
* what do we need to do, that the boot prom can
* not be counted on to have already done, that is
* generic across all platforms using crossbows?
*/
/*ARGSUSED */
void
xbow_mlreset(xbow_t * xbow)
{
}
/*
* xbow_init: called with the rest of the device
* driver XXX_init routines. This platform *might*
* have a Crossbow chip, or even several, but it
* might have none. Register with the crosstalk
* generic provider so when we encounter the chip
* the right magic happens.
*/
void
xbow_init(void)
{
#if DEBUG && ATTACH_DEBUG
printk("xbow_init\n");
#endif
xwidget_driver_register(PXBOW_WIDGET_PART_NUM,
0, /* XXBOW_WIDGET_MFGR_NUM, */
"xbow_",
CDL_PRI_HI); /* attach before friends */
xwidget_driver_register(XXBOW_WIDGET_PART_NUM,
0, /* XXBOW_WIDGET_MFGR_NUM, */
"xbow_",
CDL_PRI_HI); /* attach before friends */
xwidget_driver_register(XBOW_WIDGET_PART_NUM,
XBOW_WIDGET_MFGR_NUM,
"xbow_",
CDL_PRI_HI); /* attach before friends */
}
#ifdef XBRIDGE_REGS_SIM
/* xbow_set_simulated_regs: sets xbow regs as needed
* for powering through the boot
*/
void
xbow_set_simulated_regs(xbow_t *xbow, int port)
{
/*
* turn on link
*/
xbow->xb_link(port).link_status = (1<<31);
/*
* and give it a live widget too
*/
xbow->xb_link(port).link_aux_status = XB_AUX_STAT_PRESENT;
/*
* zero the link control reg
*/
xbow->xb_link(port).link_control = 0x0;
}
#endif /* XBRIDGE_REGS_SIM */
/*
* xbow_attach: the crosstalk provider has
* determined that there is a crossbow widget
* present, and has handed us the connection
* point for that vertex.
*
* We not only add our own vertex, but add
* some "xtalk switch" data to the switch
* vertex (at the connect point's parent) if
* it does not have any.
*/
/*ARGSUSED */
int
xbow_attach(devfs_handle_t conn)
{
/*REFERENCED */
devfs_handle_t vhdl;
devfs_handle_t busv;
xbow_t *xbow;
xbow_soft_t soft;
int port;
xswitch_info_t info;
xtalk_intr_t intr_hdl;
char devnm[MAXDEVNAME], *s;
xbowreg_t id;
int rev;
int i;
int xbow_num;
static void xbow_errintr_handler(int, void *, struct pt_regs *);
#if DEBUG && ATTACH_DEBUG
#if defined(SUPPORT_PRINTING_V_FORMAT)
printk("%v: xbow_attach\n", conn);
#else
printk("0x%x: xbow_attach\n", conn);
#endif
#endif
/*
* Get a PIO pointer to the base of the crossbow
* chip.
*/
#ifdef XBRIDGE_REGS_SIM
printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: allocating %ld bytes for xbow_s\n", sizeof(xbow_t));
xbow = (xbow_t *) kmalloc(sizeof(xbow_t), GFP_KERNEL);
/*
* turn on ports e and f like in a real live ibrick
*/
xbow_set_simulated_regs(xbow, 0xe);
xbow_set_simulated_regs(xbow, 0xf);
#else
xbow = (xbow_t *) xtalk_piotrans_addr(conn, 0, 0, sizeof(xbow_t), 0);
#endif /* XBRIDGE_REGS_SIM */
/*
* Locate the "switch" vertex: it is the parent
* of our connection point.
*/
busv = hwgraph_connectpt_get(conn);
#if DEBUG && ATTACH_DEBUG
printk("xbow_attach: Bus Vertex 0x%p, conn 0x%p, xbow register 0x%p wid= 0x%x\n", busv, conn, xbow, *(volatile u32 *)xbow);
#endif
ASSERT(busv != GRAPH_VERTEX_NONE);
/*
* Create our private vertex, and connect our
* driver information to it. This makes it possible
* for diagnostic drivers to open the crossbow
* vertex for access to registers.
*/
/*
* Register a xbow driver with devfs.
* file ops.
*/
vhdl = NULL;
vhdl = devfs_register(conn, EDGE_LBL_XBOW,
DEVFS_FL_AUTO_DEVNUM, 0, 0,
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
&xbow_fops, (void *)xbow);
if (!vhdl) {
printk(KERN_WARNING "xbow_attach: Unable to create char device for xbow conn %p\n",
(void *)conn);
}
/*
* Allocate the soft state structure and attach
* it to the xbow's vertex
*/
NEW(soft);
soft->conn = conn;
soft->vhdl = vhdl;
soft->busv = busv;
soft->base = xbow;
/* does the universe really need another macro? */
/* xbow_soft_set(vhdl, (arbitrary_info_t) soft); */
/* hwgraph_fastinfo_set(vhdl, (arbitrary_info_t) soft); */
#define XBOW_NUM_SUFFIX_FORMAT "[xbow# %d]"
/* Add xbow number as a suffix to the hwgraph name of the xbow.
* This is helpful while looking at the error/warning messages.
*/
xbow_num = 0;
/*
* get the name of this xbow vertex and keep the info.
* This is needed during errors and interupts, but as
* long as we have it, we can use it elsewhere.
*/
s = dev_to_name(vhdl, devnm, MAXDEVNAME);
soft->name = kmalloc(strlen(s) + strlen(XBOW_NUM_SUFFIX_FORMAT) + 1,
GFP_KERNEL);
sprintf(soft->name,"%s"XBOW_NUM_SUFFIX_FORMAT, s,xbow_num);
#ifdef XBRIDGE_REGS_SIM
/* my o200/ibrick has id=0x2d002049, but XXBOW_WIDGET_PART_NUM is defined
* as 0xd000, so I'm using that for the partnum bitfield.
*/
printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: need xb_wid_id value!!\n");
id = 0x2d000049;
#else
id = xbow->xb_wid_id;
#endif /* XBRIDGE_REGS_SIM */
rev = XWIDGET_PART_REV_NUM(id);
mutex_spinlock_init(&soft->xbow_perf_lock);
soft->xbow_perfcnt[0].xp_perf_reg = &xbow->xb_perf_ctr_a;
soft->xbow_perfcnt[1].xp_perf_reg = &xbow->xb_perf_ctr_b;
/* Initialization for GBR bw allocation */
mutex_spinlock_init(&soft->xbow_bw_alloc_lock);
#define XBOW_8_BIT_PORT_BW_MAX (400 * 1000 * 1000) /* 400 MB/s */
#define XBOW_16_BIT_PORT_BW_MAX (800 * 1000 * 1000) /* 800 MB/s */
/* Set bandwidth hiwatermark and current values */
for (i = 0; i < MAX_XBOW_PORTS; i++) {
soft->bw_hiwm[i] = XBOW_16_BIT_PORT_BW_MAX; /* for now */
soft->bw_cur_used[i] = 0;
}
/*
* attach the crossbow error interrupt.
*/
intr_hdl = xtalk_intr_alloc(conn, (device_desc_t)0, vhdl);
ASSERT(intr_hdl != NULL);
xtalk_intr_connect(intr_hdl,
(intr_func_t) xbow_errintr_handler,
(intr_arg_t) soft,
(xtalk_intr_setfunc_t) xbow_setwidint,
(void *) xbow);
request_irq(CPU_VECTOR_TO_IRQ(((hub_intr_t)intr_hdl)->i_cpuid,
((hub_intr_t)intr_hdl)->i_bit),
(intr_func_t)xbow_errintr_handler, 0, "XBOW error",
(intr_arg_t) soft);
#ifdef BUS_INT_WAR_NOT_YET
{
void sn_add_polled_interrupt(int, int);
sn_add_polled_interrupt(CPU_VECTOR_TO_IRQ(((hub_intr_t)intr_hdl)->i_cpuid,
((hub_intr_t)intr_hdl)->i_bit), 5000);
}
#endif
/*
* Enable xbow error interrupts
*/
xbow->xb_wid_control = (XB_WID_CTRL_REG_ACC_IE | XB_WID_CTRL_XTALK_IE);
/*
* take a census of the widgets present,
* leaving notes at the switch vertex.
*/
info = xswitch_info_new(busv);
for (port = MAX_PORT_NUM - MAX_XBOW_PORTS;
port < MAX_PORT_NUM; ++port) {
if (!xbow_link_alive(xbow, port)) {
#if DEBUG && XBOW_DEBUG
printk(KERN_INFO "0x%p link %d is not alive\n",
(void *)busv, port);
#endif
continue;
}
if (!xbow_widget_present(xbow, port)) {
#if DEBUG && XBOW_DEBUG
printk(KERN_INFO "0x%p link %d is alive but no widget is present\n", (void *)busv, port);
#endif
continue;
}
#if DEBUG && XBOW_DEBUG
printk(KERN_INFO "0x%p link %d has a widget\n",
(void *)busv, port);
#endif
xswitch_info_link_is_ok(info, port);
/*
* Turn some error interrupts on
* and turn others off. The PROM has
* some things turned on we don't
* want to see (bandwidth allocation
* errors for instance); so if it
* is not listed here, it is not on.
*/
xbow->xb_link(port).link_control =
( (xbow->xb_link(port).link_control
/*
* Turn off these bits; they are non-fatal,
* but we might want to save some statistics
* on the frequency of these errors.
* XXX FIXME XXX
*/
& ~XB_CTRL_RCV_CNT_OFLOW_IE
& ~XB_CTRL_XMT_CNT_OFLOW_IE
& ~XB_CTRL_BNDWDTH_ALLOC_IE
& ~XB_CTRL_RCV_IE)
/*
* These are the ones we want to turn on.
*/
| (XB_CTRL_ILLEGAL_DST_IE
| XB_CTRL_OALLOC_IBUF_IE
| XB_CTRL_XMT_MAX_RTRY_IE
| XB_CTRL_MAXREQ_TOUT_IE
| XB_CTRL_XMT_RTRY_IE
| XB_CTRL_SRC_TOUT_IE) );
}
xswitch_provider_register(busv, &xbow_provider);
return 0; /* attach successful */
}
/*ARGSUSED */
int
xbow_open(devfs_handle_t *devp, int oflag, int otyp, cred_t *credp)
{
return 0;
}
/*ARGSUSED */
int
xbow_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
{
return 0;
}
/*ARGSUSED */
int
xbow_map(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
{
devfs_handle_t vhdl = dev_to_vhdl(dev);
xbow_soft_t soft = xbow_soft_get(vhdl);
int error;
ASSERT(soft);
len = ctob(btoc(len));
/* XXX- this ignores the offset!!! */
error = v_mapphys(vt, (void *) soft->base, len);
return error;
}
/*ARGSUSED */
int
xbow_unmap(devfs_handle_t dev, vhandl_t *vt)
{
return 0;
}
/* This contains special-case code for grio. There are plans to make
* this general sometime in the future, but till then this should
* be good enough.
*/
xwidgetnum_t
xbow_widget_num_get(devfs_handle_t dev)
{
devfs_handle_t tdev;
char devname[MAXDEVNAME];
xwidget_info_t xwidget_info;
int i;
vertex_to_name(dev, devname, MAXDEVNAME);
/* If this is a pci controller vertex, traverse up using
* the ".." links to get to the widget.
*/
if (strstr(devname, EDGE_LBL_PCI) &&
strstr(devname, EDGE_LBL_CONTROLLER)) {
tdev = dev;
for (i=0; i< 2; i++) {
if (hwgraph_edge_get(tdev,
HWGRAPH_EDGELBL_DOTDOT, &tdev) !=
GRAPH_SUCCESS)
return XWIDGET_NONE;
}
if ((xwidget_info = xwidget_info_chk(tdev)) != NULL) {
return (xwidget_info_id_get(xwidget_info));
} else {
return XWIDGET_NONE;
}
}
return XWIDGET_NONE;
}
int
xbow_ioctl(devfs_handle_t dev,
int cmd,
void *arg,
int flag,
struct cred *cr,
int *rvalp)
{
devfs_handle_t vhdl;
int error = 0;
#if defined (DEBUG)
int rc;
devfs_handle_t conn;
struct xwidget_info_s *xwidget_info;
xbow_soft_t xbow_soft;
#endif
*rvalp = 0;
vhdl = dev_to_vhdl(dev);
#if defined (DEBUG)
xbow_soft = xbow_soft_get(vhdl);
conn = xbow_soft->conn;
xwidget_info = xwidget_info_get(conn);
ASSERT_ALWAYS(xwidget_info != NULL);
rc = xwidget_hwid_is_xswitch(&xwidget_info->w_hwid);
ASSERT_ALWAYS(rc != 0);
#endif
switch (cmd) {
case XBOWIOC_LLP_ERROR_ENABLE:
if ((error = xbow_enable_llp_monitor(vhdl)) != 0)
error = EINVAL;
break;
case XBOWIOC_LLP_ERROR_DISABLE:
if ((error = xbow_disable_llp_monitor(vhdl)) != 0)
error = EINVAL;
break;
default:
break;
}
return error;
}
/*
* xbow_widget_present: See if a device is present
* on the specified port of this crossbow.
*/
int
xbow_widget_present(xbow_t *xbow, int port)
{
if ( IS_RUNNING_ON_SIMULATOR() ) {
if ( (port == 14) || (port == 15) ) {
return 1;
}
else {
return 0;
}
}
else {
/* WAR: port 0xf on PIC is missing present bit */
if (XBOW_WAR_ENABLED(PV854827, xbow->xb_wid_id) &&
IS_PIC_XBOW(xbow->xb_wid_id) && port==0xf) {
return 1;
}
return xbow->xb_link(port).link_aux_status & XB_AUX_STAT_PRESENT;
}
}
static int
xbow_link_alive(xbow_t * xbow, int port)
{
xbwX_stat_t xbow_linkstat;
xbow_linkstat.linkstatus = xbow->xb_link(port).link_status;
return (xbow_linkstat.link_alive);
}
/*
* xbow_widget_lookup
* Lookup the edges connected to the xbow specified, and
* retrieve the handle corresponding to the widgetnum
* specified.
* If not found, return 0.
*/
devfs_handle_t
xbow_widget_lookup(devfs_handle_t vhdl,
int widgetnum)
{
xswitch_info_t xswitch_info;
devfs_handle_t conn;
xswitch_info = xswitch_info_get(vhdl);
conn = xswitch_info_vhdl_get(xswitch_info, widgetnum);
return conn;
}
/*
* xbow_setwidint: called when xtalk
* is establishing or migrating our
* interrupt service.
*/
static void
xbow_setwidint(xtalk_intr_t intr)
{
xwidgetnum_t targ = xtalk_intr_target_get(intr);
iopaddr_t addr = xtalk_intr_addr_get(intr);
xtalk_intr_vector_t vect = xtalk_intr_vector_get(intr);
xbow_t *xbow = (xbow_t *) xtalk_intr_sfarg_get(intr);
xbow_intr_preset((void *) xbow, 0, targ, addr, vect);
}
/*
* xbow_intr_preset: called during mlreset time
* if the platform specific code needs to route
* an xbow interrupt before the xtalk infrastructure
* is available for use.
*
* Also called from xbow_setwidint, so we don't
* replicate the guts of the routine.
*
* XXX- probably should be renamed xbow_wid_intr_set or
* something to reduce confusion.
*/
/*ARGSUSED3 */
void
xbow_intr_preset(void *which_widget,
int which_widget_intr,
xwidgetnum_t targ,
iopaddr_t addr,
xtalk_intr_vector_t vect)
{
xbow_t *xbow = (xbow_t *) which_widget;
xbow->xb_wid_int_upper = ((0xFF000000 & (vect << 24)) |
(0x000F0000 & (targ << 16)) |
XTALK_ADDR_TO_UPPER(addr));
xbow->xb_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
}
#define XEM_ADD_STR(s) printk("%s", (s))
#define XEM_ADD_NVAR(n,v) printk("\t%20s: 0x%llx\n", (n), ((unsigned long long)v))
#define XEM_ADD_VAR(v) XEM_ADD_NVAR(#v,(v))
#define XEM_ADD_IOEF(p,n) if (IOERROR_FIELDVALID(ioe,n)) { \
IOERROR_GETVALUE(p,ioe,n); \
XEM_ADD_NVAR("ioe." #n, p); \
}
#ifdef LATER
static void
xem_add_ioe(ioerror_t *ioe)
{
union tmp {
ushort stmp;
unsigned long long lltmp;
cpuid_t cputmp;
cnodeid_t cntmp;
iopaddr_t iotmp;
caddr_t catmp;
paddr_t patmp;
} tmp;
XEM_ADD_IOEF(tmp.stmp, errortype);
XEM_ADD_IOEF(tmp.stmp, widgetnum);
XEM_ADD_IOEF(tmp.stmp, widgetdev);
XEM_ADD_IOEF(tmp.cputmp, srccpu);
XEM_ADD_IOEF(tmp.cntmp, srcnode);
XEM_ADD_IOEF(tmp.cntmp, errnode);
XEM_ADD_IOEF(tmp.iotmp, sysioaddr);
XEM_ADD_IOEF(tmp.iotmp, xtalkaddr);
XEM_ADD_IOEF(tmp.iotmp, busspace);
XEM_ADD_IOEF(tmp.iotmp, busaddr);
XEM_ADD_IOEF(tmp.catmp, vaddr);
XEM_ADD_IOEF(tmp.patmp, memaddr);
XEM_ADD_IOEF(tmp.catmp, epc);
XEM_ADD_IOEF(tmp.catmp, ef);
XEM_ADD_IOEF(tmp.stmp, tnum);
}
#define XEM_ADD_IOE() (xem_add_ioe(ioe))
#endif /* LATER */
int xbow_xmit_retry_errors = 0;
int
xbow_xmit_retry_error(xbow_soft_t soft,
int port)
{
xswitch_info_t info;
devfs_handle_t vhdl;
widget_cfg_t *wid;
widgetreg_t id;
int part;
int mfgr;
wid = soft->wpio[port - BASE_XBOW_PORT];
if (wid == NULL) {
/* If we can't track down a PIO
* pointer to our widget yet,
* leave our caller knowing that
* we are interested in this
* interrupt if it occurs in
* the future.
*/
info = xswitch_info_get(soft->busv);
if (!info)
return 1;
vhdl = xswitch_info_vhdl_get(info, port);
if (vhdl == GRAPH_VERTEX_NONE)
return 1;
wid = (widget_cfg_t *) xtalk_piotrans_addr
(vhdl, 0, 0, sizeof *wid, 0);
if (!wid)
return 1;
soft->wpio[port - BASE_XBOW_PORT] = wid;
}
id = wid->w_id;
part = XWIDGET_PART_NUM(id);
mfgr = XWIDGET_MFG_NUM(id);
/* If this thing is not a Bridge,
* do not activate the WAR, and
* tell our caller we do not need
* to be called again.
*/
if ((part != BRIDGE_WIDGET_PART_NUM) ||
(mfgr != BRIDGE_WIDGET_MFGR_NUM)) {
/* FIXME: add Xbridge to the WAR.
* Shouldn't hurt anything. Later need to
* check if we can remove this.
*/
if ((part != XBRIDGE_WIDGET_PART_NUM) ||
(mfgr != XBRIDGE_WIDGET_MFGR_NUM))
return 0;
}
/* count how many times we
* have picked up after
* LLP Transmit problems.
*/
xbow_xmit_retry_errors++;
/* rewrite the control register
* to fix things up.
*/
wid->w_control = wid->w_control;
wid->w_control;
return 1;
}
/*
* xbow_errintr_handler will be called if the xbow
* sends an interrupt request to report an error.
*/
static void
xbow_errintr_handler(int irq, void *arg, struct pt_regs *ep)
{
ioerror_t ioe[1];
xbow_soft_t soft = (xbow_soft_t) arg;
xbow_t *xbow = soft->base;
xbowreg_t wid_control;
xbowreg_t wid_stat;
xbowreg_t wid_err_cmdword;
xbowreg_t wid_err_upper;
xbowreg_t wid_err_lower;
w_err_cmd_word_u wid_err;
unsigned long long wid_err_addr;
int fatal = 0;
int dump_ioe = 0;
static int xbow_error_handler(void *, int, ioerror_mode_t, ioerror_t *);
wid_control = xbow->xb_wid_control;
wid_stat = xbow->xb_wid_stat_clr;
wid_err_cmdword = xbow->xb_wid_err_cmdword;
wid_err_upper = xbow->xb_wid_err_upper;
wid_err_lower = xbow->xb_wid_err_lower;
xbow->xb_wid_err_cmdword = 0;
wid_err_addr = wid_err_lower | (((iopaddr_t) wid_err_upper & WIDGET_ERR_UPPER_ADDR_ONLY) << 32);
if (wid_stat & XB_WID_STAT_LINK_INTR_MASK) {
int port;
wid_err.r = wid_err_cmdword;
for (port = MAX_PORT_NUM - MAX_XBOW_PORTS;
port < MAX_PORT_NUM; port++) {
if (wid_stat & XB_WID_STAT_LINK_INTR(port)) {
xb_linkregs_t *link = &(xbow->xb_link(port));
xbowreg_t link_control = link->link_control;
xbowreg_t link_status = link->link_status_clr;
xbowreg_t link_aux_status = link->link_aux_status;
xbowreg_t link_pend;
link_pend = link_status & link_control &
(XB_STAT_ILLEGAL_DST_ERR
| XB_STAT_OALLOC_IBUF_ERR
| XB_STAT_RCV_CNT_OFLOW_ERR
| XB_STAT_XMT_CNT_OFLOW_ERR
| XB_STAT_XMT_MAX_RTRY_ERR
| XB_STAT_RCV_ERR
| XB_STAT_XMT_RTRY_ERR
| XB_STAT_MAXREQ_TOUT_ERR
| XB_STAT_SRC_TOUT_ERR
);
if (link_pend & XB_STAT_ILLEGAL_DST_ERR) {
if (wid_err.f.sidn == port) {
IOERROR_INIT(ioe);
IOERROR_SETVALUE(ioe, widgetnum, port);
IOERROR_SETVALUE(ioe, xtalkaddr, wid_err_addr);
if (IOERROR_HANDLED ==
xbow_error_handler(soft,
IOECODE_DMA,
MODE_DEVERROR,
ioe)) {
link_pend &= ~XB_STAT_ILLEGAL_DST_ERR;
} else {
dump_ioe++;
}
}
}
/* Xbow/Bridge WAR:
* if the bridge signals an LLP Transmitter Retry,
* rewrite its control register.
* If someone else triggers this interrupt,
* ignore (and disable) the interrupt.
*/
if (link_pend & XB_STAT_XMT_RTRY_ERR) {
if (!xbow_xmit_retry_error(soft, port)) {
link_control &= ~XB_CTRL_XMT_RTRY_IE;
link->link_control = link_control;
link->link_control; /* stall until written */
}
link_pend &= ~XB_STAT_XMT_RTRY_ERR;
}
if (link_pend) {
devfs_handle_t xwidget_vhdl;
char *xwidget_name;
/* Get the widget name corresponding to the current
* xbow link.
*/
xwidget_vhdl = xbow_widget_lookup(soft->busv,port);
xwidget_name = xwidget_name_get(xwidget_vhdl);
printk("%s port %X[%s] XIO Bus Error",
soft->name, port, xwidget_name);
if (link_status & XB_STAT_MULTI_ERR)
XEM_ADD_STR("\tMultiple Errors\n");
if (link_status & XB_STAT_ILLEGAL_DST_ERR)
XEM_ADD_STR("\tInvalid Packet Destination\n");
if (link_status & XB_STAT_OALLOC_IBUF_ERR)
XEM_ADD_STR("\tInput Overallocation Error\n");
if (link_status & XB_STAT_RCV_CNT_OFLOW_ERR)
XEM_ADD_STR("\tLLP receive error counter overflow\n");
if (link_status & XB_STAT_XMT_CNT_OFLOW_ERR)
XEM_ADD_STR("\tLLP transmit retry counter overflow\n");
if (link_status & XB_STAT_XMT_MAX_RTRY_ERR)
XEM_ADD_STR("\tLLP Max Transmitter Retry\n");
if (link_status & XB_STAT_RCV_ERR)
XEM_ADD_STR("\tLLP Receiver error\n");
if (link_status & XB_STAT_XMT_RTRY_ERR)
XEM_ADD_STR("\tLLP Transmitter Retry\n");
if (link_status & XB_STAT_MAXREQ_TOUT_ERR)
XEM_ADD_STR("\tMaximum Request Timeout\n");
if (link_status & XB_STAT_SRC_TOUT_ERR)
XEM_ADD_STR("\tSource Timeout Error\n");
{
int other_port;
for (other_port = 8; other_port < 16; ++other_port) {
if (link_aux_status & (1 << other_port)) {
/* XXX- need to go to "other_port"
* and clean up after the timeout?
*/
XEM_ADD_VAR(other_port);
}
}
}
#if !DEBUG
if (kdebug) {
#endif
XEM_ADD_VAR(link_control);
XEM_ADD_VAR(link_status);
XEM_ADD_VAR(link_aux_status);
#ifdef LATER
if (dump_ioe) {
XEM_ADD_IOE();
dump_ioe = 0;
}
#endif
#if !DEBUG
}
#endif
fatal++;
}
}
}
}
if (wid_stat & wid_control & XB_WID_STAT_WIDGET0_INTR) {
/* we have a "widget zero" problem */
if (wid_stat & (XB_WID_STAT_MULTI_ERR
| XB_WID_STAT_XTALK_ERR
| XB_WID_STAT_REG_ACC_ERR)) {
printk("%s Port 0 XIO Bus Error",
soft->name);
if (wid_stat & XB_WID_STAT_MULTI_ERR)
XEM_ADD_STR("\tMultiple Error\n");
if (wid_stat & XB_WID_STAT_XTALK_ERR)
XEM_ADD_STR("\tXIO Error\n");
if (wid_stat & XB_WID_STAT_REG_ACC_ERR)
XEM_ADD_STR("\tRegister Access Error\n");
fatal++;
}
}
if (fatal) {
XEM_ADD_VAR(wid_stat);
XEM_ADD_VAR(wid_control);
XEM_ADD_VAR(wid_err_cmdword);
XEM_ADD_VAR(wid_err_upper);
XEM_ADD_VAR(wid_err_lower);
XEM_ADD_VAR(wid_err_addr);
PRINT_PANIC("XIO Bus Error");
}
}
/*
* XBOW ERROR Handling routines.
* These get invoked as part of walking down the error handling path
* from hub/heart towards the I/O device that caused the error.
*/
/*
* xbow_error_handler
* XBow error handling dispatch routine.
* This is the primary interface used by external world to invoke
* in case of an error related to a xbow.
* Only functionality in this layer is to identify the widget handle
* given the widgetnum. Otherwise, xbow does not gathers any error
* data.
*/
static int
xbow_error_handler(
void *einfo,
int error_code,
ioerror_mode_t mode,
ioerror_t *ioerror)
{
int retval = IOERROR_WIDGETLEVEL;
xbow_soft_t soft = (xbow_soft_t) einfo;
int port;
devfs_handle_t conn;
devfs_handle_t busv;
xbow_t *xbow = soft->base;
xbowreg_t wid_stat;
xbowreg_t wid_err_cmdword;
xbowreg_t wid_err_upper;
xbowreg_t wid_err_lower;
unsigned long long wid_err_addr;
xb_linkregs_t *link;
xbowreg_t link_control;
xbowreg_t link_status;
xbowreg_t link_aux_status;
ASSERT(soft != 0);
busv = soft->busv;
#if DEBUG && ERROR_DEBUG
printk("%s: xbow_error_handler\n", soft->name, busv);
#endif
IOERROR_GETVALUE(port, ioerror, widgetnum);
if (port == 0) {
/* error during access to xbow:
* do NOT attempt to access xbow regs.
*/
if (mode == MODE_DEVPROBE)
return IOERROR_HANDLED;
if (error_code & IOECODE_DMA) {
printk(KERN_ALERT
"DMA error blamed on Crossbow at %s\n"
"\tbut Crosbow never initiates DMA!",
soft->name);
}
if (error_code & IOECODE_PIO) {
iopaddr_t tmp;
IOERROR_GETVALUE(tmp, ioerror, xtalkaddr);
printk(KERN_ALERT "PIO Error on XIO Bus %s\n"
"\tattempting to access XIO controller\n"
"\twith offset 0x%lx",
soft->name, tmp);
}
/* caller will dump contents of ioerror
* in DEBUG and kdebug kernels.
*/
return retval;
}
/*
* error not on port zero:
* safe to read xbow registers.
*/
wid_stat = xbow->xb_wid_stat;
wid_err_cmdword = xbow->xb_wid_err_cmdword;
wid_err_upper = xbow->xb_wid_err_upper;
wid_err_lower = xbow->xb_wid_err_lower;
wid_err_addr =
wid_err_lower
| (((iopaddr_t) wid_err_upper
& WIDGET_ERR_UPPER_ADDR_ONLY)
<< 32);
if ((port < BASE_XBOW_PORT) ||
(port >= MAX_PORT_NUM)) {
if (mode == MODE_DEVPROBE)
return IOERROR_HANDLED;
if (error_code & IOECODE_DMA) {
printk(KERN_ALERT
"DMA error blamed on XIO port at %s/%d\n"
"\tbut Crossbow does not support that port",
soft->name, port);
}
if (error_code & IOECODE_PIO) {
iopaddr_t tmp;
IOERROR_GETVALUE(tmp, ioerror, xtalkaddr);
printk(KERN_ALERT
"PIO Error on XIO Bus %s\n"
"\tattempting to access XIO port %d\n"
"\t(which Crossbow does not support)"
"\twith offset 0x%lx",
soft->name, port, tmp);
}
#if !DEBUG
if (kdebug) {
#endif
XEM_ADD_STR("Raw status values for Crossbow:\n");
XEM_ADD_VAR(wid_stat);
XEM_ADD_VAR(wid_err_cmdword);
XEM_ADD_VAR(wid_err_upper);
XEM_ADD_VAR(wid_err_lower);
XEM_ADD_VAR(wid_err_addr);
#if !DEBUG
}
#endif
/* caller will dump contents of ioerror
* in DEBUG and kdebug kernels.
*/
return retval;
}
/* access to valid port:
* ok to check port status.
*/
link = &(xbow->xb_link(port));
link_control = link->link_control;
link_status = link->link_status;
link_aux_status = link->link_aux_status;
/* Check that there is something present
* in that XIO port.
*/
/* WAR: PIC widget 0xf is missing prescense bit */
if (XBOW_WAR_ENABLED(PV854827, xbow->xb_wid_id) &&
IS_PIC_XBOW(xbow->xb_wid_id) && (port==0xf))
;
else
if (!(link_aux_status & XB_AUX_STAT_PRESENT)) {
/* nobody connected. */
if (mode == MODE_DEVPROBE)
return IOERROR_HANDLED;
if (error_code & IOECODE_DMA) {
printk(KERN_ALERT
"DMA error blamed on XIO port at %s/%d\n"
"\tbut there is no device connected there.",
soft->name, port);
}
if (error_code & IOECODE_PIO) {
iopaddr_t tmp;
IOERROR_GETVALUE(tmp, ioerror, xtalkaddr);
printk(KERN_ALERT
"PIO Error on XIO Bus %s\n"
"\tattempting to access XIO port %d\n"
"\t(which has no device connected)"
"\twith offset 0x%lx",
soft->name, port, tmp);
}
#if !DEBUG
if (kdebug) {
#endif
XEM_ADD_STR("Raw status values for Crossbow:\n");
XEM_ADD_VAR(wid_stat);
XEM_ADD_VAR(wid_err_cmdword);
XEM_ADD_VAR(wid_err_upper);
XEM_ADD_VAR(wid_err_lower);
XEM_ADD_VAR(wid_err_addr);
XEM_ADD_VAR(port);
XEM_ADD_VAR(link_control);
XEM_ADD_VAR(link_status);
XEM_ADD_VAR(link_aux_status);
#if !DEBUG
}
#endif
return retval;
}
/* Check that the link is alive.
*/
if (!(link_status & XB_STAT_LINKALIVE)) {
iopaddr_t tmp;
/* nobody connected. */
if (mode == MODE_DEVPROBE)
return IOERROR_HANDLED;
printk(KERN_ALERT
"%s%sError on XIO Bus %s port %d",
(error_code & IOECODE_DMA) ? "DMA " : "",
(error_code & IOECODE_PIO) ? "PIO " : "",
soft->name, port);
IOERROR_GETVALUE(tmp, ioerror, xtalkaddr);
if ((error_code & IOECODE_PIO) &&
(IOERROR_FIELDVALID(ioerror, xtalkaddr))) {
printk("\tAccess attempted to offset 0x%lx\n", tmp);
}
if (link_aux_status & XB_AUX_LINKFAIL_RST_BAD)
XEM_ADD_STR("\tLink never came out of reset\n");
else
XEM_ADD_STR("\tLink failed while transferring data\n");
}
/* get the connection point for the widget
* involved in this error; if it exists and
* is not our connectpoint, cycle back through
* xtalk_error_handler to deliver control to
* the proper handler (or to report a generic
* crosstalk error).
*
* If the downstream handler won't handle
* the problem, we let our upstream caller
* deal with it, after (in DEBUG and kdebug
* kernels) dumping the xbow state for this
* port.
*/
conn = xbow_widget_lookup(busv, port);
if ((conn != GRAPH_VERTEX_NONE) &&
(conn != soft->conn)) {
retval = xtalk_error_handler(conn, error_code, mode, ioerror);
if (retval == IOERROR_HANDLED)
return IOERROR_HANDLED;
}
if (mode == MODE_DEVPROBE)
return IOERROR_HANDLED;
if (retval == IOERROR_UNHANDLED) {
iopaddr_t tmp;
retval = IOERROR_PANIC;
printk(KERN_ALERT
"%s%sError on XIO Bus %s port %d",
(error_code & IOECODE_DMA) ? "DMA " : "",
(error_code & IOECODE_PIO) ? "PIO " : "",
soft->name, port);
IOERROR_GETVALUE(tmp, ioerror, xtalkaddr);
if ((error_code & IOECODE_PIO) &&
(IOERROR_FIELDVALID(ioerror, xtalkaddr))) {
printk("\tAccess attempted to offset 0x%lx\n", tmp);
}
}
#if !DEBUG
if (kdebug) {
#endif
XEM_ADD_STR("Raw status values for Crossbow:\n");
XEM_ADD_VAR(wid_stat);
XEM_ADD_VAR(wid_err_cmdword);
XEM_ADD_VAR(wid_err_upper);
XEM_ADD_VAR(wid_err_lower);
XEM_ADD_VAR(wid_err_addr);
XEM_ADD_VAR(port);
XEM_ADD_VAR(link_control);
XEM_ADD_VAR(link_status);
XEM_ADD_VAR(link_aux_status);
#if !DEBUG
}
#endif
/* caller will dump raw ioerror data
* in DEBUG and kdebug kernels.
*/
return retval;
}
void
xbow_update_perf_counters(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_perf_t *xbow_perf = xbow_soft->xbow_perfcnt;
xbow_perf_link_t *xbow_plink = xbow_soft->xbow_perflink;
xbow_perfcount_t perf_reg;
unsigned long s;
int link, i;
for (i = 0; i < XBOW_PERF_COUNTERS; i++, xbow_perf++) {
if (xbow_perf->xp_mode == XBOW_MONITOR_NONE)
continue;
s = mutex_spinlock(&xbow_soft->xbow_perf_lock);
perf_reg.xb_counter_val = *(xbowreg_t *) xbow_perf->xp_perf_reg;
link = perf_reg.xb_perf.link_select;
(xbow_plink + link)->xlp_cumulative[xbow_perf->xp_curmode] +=
((perf_reg.xb_perf.count - xbow_perf->xp_current) & XBOW_COUNTER_MASK);
xbow_perf->xp_current = perf_reg.xb_perf.count;
mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
}
}
xbow_perf_link_t *
xbow_get_perf_counters(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_perf_link_t *xbow_perf_link = xbow_soft->xbow_perflink;
return xbow_perf_link;
}
int
xbow_enable_perf_counter(devfs_handle_t vhdl, int link, int mode, int counter)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_perf_t *xbow_perf = xbow_soft->xbow_perfcnt;
xbow_linkctrl_t xbow_link_ctrl;
xbow_t *xbow = xbow_soft->base;
xbow_perfcount_t perf_reg;
unsigned long s;
int i;
link -= BASE_XBOW_PORT;
if ((link < 0) || (link >= MAX_XBOW_PORTS))
return -1;
if ((mode < XBOW_MONITOR_NONE) || (mode > XBOW_MONITOR_DEST_LINK))
return -1;
if ((counter < 0) || (counter >= XBOW_PERF_COUNTERS))
return -1;
s = mutex_spinlock(&xbow_soft->xbow_perf_lock);
if ((xbow_perf + counter)->xp_mode && mode) {
mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
return -1;
}
for (i = 0; i < XBOW_PERF_COUNTERS; i++) {
if (i == counter)
continue;
if (((xbow_perf + i)->xp_link == link) &&
((xbow_perf + i)->xp_mode)) {
mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
return -1;
}
}
xbow_perf += counter;
xbow_perf->xp_curlink = xbow_perf->xp_link = link;
xbow_perf->xp_curmode = xbow_perf->xp_mode = mode;
xbow_link_ctrl.xbl_ctrlword = xbow->xb_link_raw[link].link_control;
xbow_link_ctrl.xb_linkcontrol.perf_mode = mode;
xbow->xb_link_raw[link].link_control = xbow_link_ctrl.xbl_ctrlword;
perf_reg.xb_counter_val = *(xbowreg_t *) xbow_perf->xp_perf_reg;
perf_reg.xb_perf.link_select = link;
*(xbowreg_t *) xbow_perf->xp_perf_reg = perf_reg.xb_counter_val;
xbow_perf->xp_current = perf_reg.xb_perf.count;
mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
return 0;
}
xbow_link_status_t *
xbow_get_llp_status(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_link_status_t *xbow_llp_status = xbow_soft->xbow_link_status;
return xbow_llp_status;
}
void
xbow_update_llp_status(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_link_status_t *xbow_llp_status = xbow_soft->xbow_link_status;
xbow_t *xbow;
xbwX_stat_t lnk_sts;
xbow_aux_link_status_t aux_sts;
int link;
devfs_handle_t xwidget_vhdl;
char *xwidget_name;
xbow = (xbow_t *) xbow_soft->base;
for (link = 0; link < MAX_XBOW_PORTS; link++, xbow_llp_status++) {
/* Get the widget name corresponding the current link.
* Note : 0 <= link < MAX_XBOW_PORTS(8).
* BASE_XBOW_PORT(0x8) <= xwidget number < MAX_PORT_NUM (0x10)
*/
xwidget_vhdl = xbow_widget_lookup(xbow_soft->busv,link+BASE_XBOW_PORT);
xwidget_name = xwidget_name_get(xwidget_vhdl);
aux_sts.aux_linkstatus
= xbow->xb_link_raw[link].link_aux_status;
lnk_sts.linkstatus = xbow->xb_link_raw[link].link_status_clr;
if (lnk_sts.link_alive == 0)
continue;
xbow_llp_status->rx_err_count +=
aux_sts.xb_aux_linkstatus.rx_err_cnt;
xbow_llp_status->tx_retry_count +=
aux_sts.xb_aux_linkstatus.tx_retry_cnt;
if (lnk_sts.linkstatus & ~(XB_STAT_RCV_ERR | XB_STAT_XMT_RTRY_ERR | XB_STAT_LINKALIVE)) {
#ifdef LATER
printk(KERN_WARNING "link %d[%s]: bad status 0x%x\n",
link, xwidget_name, lnk_sts.linkstatus);
#endif
}
}
}
int
xbow_disable_llp_monitor(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
int port;
for (port = 0; port < MAX_XBOW_PORTS; port++) {
xbow_soft->xbow_link_status[port].rx_err_count = 0;
xbow_soft->xbow_link_status[port].tx_retry_count = 0;
}
xbow_soft->link_monitor = 0;
return 0;
}
int
xbow_enable_llp_monitor(devfs_handle_t vhdl)
{
xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
xbow_soft->link_monitor = 1;
return 0;
}
int
xbow_reset_link(devfs_handle_t xconn_vhdl)
{
xwidget_info_t widget_info;
xwidgetnum_t port;
xbow_t *xbow;
xbowreg_t ctrl;
xbwX_stat_t stat;
unsigned itick;
unsigned dtick;
static int ticks_per_ms = 0;
if (!ticks_per_ms) {
itick = get_timestamp();
us_delay(1000);
ticks_per_ms = get_timestamp() - itick;
}
widget_info = xwidget_info_get(xconn_vhdl);
port = xwidget_info_id_get(widget_info);
#ifdef XBOW_K1PTR /* defined if we only have one xbow ... */
xbow = XBOW_K1PTR;
#else
{
devfs_handle_t xbow_vhdl;
xbow_soft_t xbow_soft;
hwgraph_traverse(xconn_vhdl, ".master/xtalk/0/xbow", &xbow_vhdl);
xbow_soft = xbow_soft_get(xbow_vhdl);
xbow = xbow_soft->base;
}
#endif
/*
* This requires three PIOs (reset the link, check for the
* reset, restore the control register for the link) plus
* 10us to wait for the reset. We allow up to 1ms for the
* widget to come out of reset before giving up and
* returning a failure.
*/
ctrl = xbow->xb_link(port).link_control;
xbow->xb_link(port).link_reset = 0;
itick = get_timestamp();
while (1) {
stat.linkstatus = xbow->xb_link(port).link_status;
if (stat.link_alive)
break;
dtick = get_timestamp() - itick;
if (dtick > ticks_per_ms) {
return -1; /* never came out of reset */
}
DELAY(2); /* don't beat on link_status */
}
xbow->xb_link(port).link_control = ctrl;
return 0;
}
/*
* Dump xbow registers.
* input parameter is either a pointer to
* the xbow chip or the vertex handle for
* an xbow vertex.
*/
void
idbg_xbowregs(int64_t regs)
{
xbow_t *xbow;
int i;
xb_linkregs_t *link;
xbow = (xbow_t *) regs;
#ifdef LATER
qprintf("Printing xbow registers starting at 0x%x\n", xbow);
qprintf("wid %x status %x erruppr %x errlower %x control %x timeout %x\n",
xbow->xb_wid_id, xbow->xb_wid_stat, xbow->xb_wid_err_upper,
xbow->xb_wid_err_lower, xbow->xb_wid_control,
xbow->xb_wid_req_timeout);
qprintf("intr uppr %x lower %x errcmd %x llp ctrl %x arb_reload %x\n",
xbow->xb_wid_int_upper, xbow->xb_wid_int_lower,
xbow->xb_wid_err_cmdword, xbow->xb_wid_llp,
xbow->xb_wid_arb_reload);
#endif
for (i = 8; i <= 0xf; i++) {
link = &xbow->xb_link(i);
#ifdef LATER
qprintf("Link %d registers\n", i);
qprintf("\tctrl %x stat %x arbuppr %x arblowr %x auxstat %x\n",
link->link_control, link->link_status,
link->link_arb_upper, link->link_arb_lower,
link->link_aux_status);
#endif
}
}
#define XBOW_ARB_RELOAD_TICKS 25
/* granularity: 4 MB/s, max: 124 MB/s */
#define GRANULARITY ((100 * 1000000) / XBOW_ARB_RELOAD_TICKS)
#define XBOW_BYTES_TO_GBR(BYTES_per_s) (int) (BYTES_per_s / GRANULARITY)
#define XBOW_GBR_TO_BYTES(cnt) (bandwidth_t) ((cnt) * GRANULARITY)
#define CEILING_BYTES_TO_GBR(gbr, bytes_per_sec) \
((XBOW_GBR_TO_BYTES(gbr) < bytes_per_sec) ? gbr+1 : gbr)
#define XBOW_ARB_GBR_MAX 31
#define ABS(x) ((x > 0) ? (x) : (-1 * x))
/* absolute value */
int
xbow_bytes_to_gbr(bandwidth_t old_bytes_per_sec, bandwidth_t bytes_per_sec)
{
int gbr_granted;
int new_total_gbr;
int change_gbr;
bandwidth_t new_total_bw;
#ifdef GRIO_DEBUG
printk("xbow_bytes_to_gbr: old_bytes_per_sec %lld bytes_per_sec %lld\n",
old_bytes_per_sec, bytes_per_sec);
#endif /* GRIO_DEBUG */
gbr_granted = CEILING_BYTES_TO_GBR((XBOW_BYTES_TO_GBR(old_bytes_per_sec)),
old_bytes_per_sec);
new_total_bw = old_bytes_per_sec + bytes_per_sec;
new_total_gbr = CEILING_BYTES_TO_GBR((XBOW_BYTES_TO_GBR(new_total_bw)),
new_total_bw);
change_gbr = new_total_gbr - gbr_granted;
#ifdef GRIO_DEBUG
printk("xbow_bytes_to_gbr: gbr_granted %d new_total_gbr %d change_gbr %d\n",
gbr_granted, new_total_gbr, change_gbr);
#endif /* GRIO_DEBUG */
return (change_gbr);
}
/* Conversion from GBR to bytes */
bandwidth_t
xbow_gbr_to_bytes(int gbr)
{
return (XBOW_GBR_TO_BYTES(gbr));
}
/* Given the vhdl for the desired xbow, the src and dest. widget ids
* and the req_bw value, this xbow driver entry point accesses the
* xbow registers and allocates the desired bandwidth if available.
*
* If bandwidth allocation is successful, return success else return failure.
*/
int
xbow_prio_bw_alloc(devfs_handle_t vhdl,
xwidgetnum_t src_wid,
xwidgetnum_t dest_wid,
unsigned long long old_alloc_bw,
unsigned long long req_bw)
{
xbow_soft_t soft = xbow_soft_get(vhdl);
volatile xbowreg_t *xreg;
xbowreg_t mask;
unsigned long s;
int error = 0;
bandwidth_t old_bw_BYTES, req_bw_BYTES;
xbowreg_t old_xreg;
int old_bw_GBR, req_bw_GBR, new_bw_GBR;
#ifdef GRIO_DEBUG
printk("xbow_prio_bw_alloc: vhdl %d src_wid %d dest_wid %d req_bw %lld\n",
(int) vhdl, (int) src_wid, (int) dest_wid, req_bw);
#endif
ASSERT(XBOW_WIDGET_IS_VALID(src_wid));
ASSERT(XBOW_WIDGET_IS_VALID(dest_wid));
s = mutex_spinlock(&soft->xbow_bw_alloc_lock);
/* Get pointer to the correct register */
xreg = XBOW_PRIO_ARBREG_PTR(soft->base, dest_wid, src_wid);
/* Get mask for GBR count value */
mask = XB_ARB_GBR_MSK << XB_ARB_GBR_SHFT(src_wid);
req_bw_GBR = xbow_bytes_to_gbr(old_alloc_bw, req_bw);
req_bw_BYTES = (req_bw_GBR < 0) ? (-1 * xbow_gbr_to_bytes(ABS(req_bw_GBR)))
: xbow_gbr_to_bytes(req_bw_GBR);
#ifdef GRIO_DEBUG
printk("req_bw %lld req_bw_BYTES %lld req_bw_GBR %d\n",
req_bw, req_bw_BYTES, req_bw_GBR);
#endif /* GRIO_DEBUG */
old_bw_BYTES = soft->bw_cur_used[(int) dest_wid - MAX_XBOW_PORTS];
old_xreg = *xreg;
old_bw_GBR = (((*xreg) & mask) >> XB_ARB_GBR_SHFT(src_wid));
#ifdef GRIO_DEBUG
ASSERT(XBOW_BYTES_TO_GBR(old_bw_BYTES) == old_bw_GBR);
printk("old_bw_BYTES %lld old_bw_GBR %d\n", old_bw_BYTES, old_bw_GBR);
printk("req_bw_BYTES %lld old_bw_BYTES %lld soft->bw_hiwm %lld\n",
req_bw_BYTES, old_bw_BYTES,
soft->bw_hiwm[(int) dest_wid - MAX_XBOW_PORTS]);
#endif /* GRIO_DEBUG */
/* Accept the request only if we don't exceed the destination
* port HIWATER_MARK *AND* the max. link GBR arbitration count
*/
if (((old_bw_BYTES + req_bw_BYTES) <=
soft->bw_hiwm[(int) dest_wid - MAX_XBOW_PORTS]) &&
(req_bw_GBR + old_bw_GBR <= XBOW_ARB_GBR_MAX)) {
new_bw_GBR = (old_bw_GBR + req_bw_GBR);
/* Set this in the xbow link register */
*xreg = (old_xreg & ~mask) | \
(new_bw_GBR << XB_ARB_GBR_SHFT(src_wid) & mask);
soft->bw_cur_used[(int) dest_wid - MAX_XBOW_PORTS] =
xbow_gbr_to_bytes(new_bw_GBR);
} else {
error = 1;
}
mutex_spinunlock(&soft->xbow_bw_alloc_lock, s);
return (error);
}
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/sn/sgi.h>
#include <asm/sn/driver.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/hcl_util.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/xtalk/xswitch.h>
#include <asm/sn/xtalk/xwidget.h>
#include <asm/sn/xtalk/xtalk_private.h>
/*
* Implement crosstalk provider operations. The xtalk* layer provides a
* platform-independent interface for crosstalk devices. This layer
* switches among the possible implementations of a crosstalk adapter.
*
* On platforms with only one possible xtalk provider, macros can be
* set up at the top that cause the table lookups and indirections to
* completely disappear.
*/
#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
#define DEL(ptr) (kfree(ptr))
char widget_info_fingerprint[] = "widget_info";
cdl_p xtalk_registry = NULL;
#define DEV_FUNC(dev,func) hub_##func
#define CAST_PIOMAP(x) ((hub_piomap_t)(x))
#define CAST_DMAMAP(x) ((hub_dmamap_t)(x))
#define CAST_INTR(x) ((hub_intr_t)(x))
/* =====================================================================
* Function Table of Contents
*/
xtalk_piomap_t xtalk_piomap_alloc(devfs_handle_t, device_desc_t, iopaddr_t, size_t, size_t, unsigned);
void xtalk_piomap_free(xtalk_piomap_t);
caddr_t xtalk_piomap_addr(xtalk_piomap_t, iopaddr_t, size_t);
void xtalk_piomap_done(xtalk_piomap_t);
caddr_t xtalk_piotrans_addr(devfs_handle_t, device_desc_t, iopaddr_t, size_t, unsigned);
caddr_t xtalk_pio_addr(devfs_handle_t, device_desc_t, iopaddr_t, size_t, xtalk_piomap_t *, unsigned);
void xtalk_set_early_piotrans_addr(xtalk_early_piotrans_addr_f *);
caddr_t xtalk_early_piotrans_addr(xwidget_part_num_t, xwidget_mfg_num_t, int, iopaddr_t, size_t, unsigned);
static caddr_t null_xtalk_early_piotrans_addr(xwidget_part_num_t, xwidget_mfg_num_t, int, iopaddr_t, size_t, unsigned);
xtalk_dmamap_t xtalk_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
void xtalk_dmamap_free(xtalk_dmamap_t);
iopaddr_t xtalk_dmamap_addr(xtalk_dmamap_t, paddr_t, size_t);
alenlist_t xtalk_dmamap_list(xtalk_dmamap_t, alenlist_t, unsigned);
void xtalk_dmamap_done(xtalk_dmamap_t);
iopaddr_t xtalk_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
alenlist_t xtalk_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
void xtalk_dmamap_drain(xtalk_dmamap_t);
void xtalk_dmaaddr_drain(devfs_handle_t, iopaddr_t, size_t);
void xtalk_dmalist_drain(devfs_handle_t, alenlist_t);
xtalk_intr_t xtalk_intr_alloc(devfs_handle_t, device_desc_t, devfs_handle_t);
xtalk_intr_t xtalk_intr_alloc_nothd(devfs_handle_t, device_desc_t, devfs_handle_t);
void xtalk_intr_free(xtalk_intr_t);
int xtalk_intr_connect(xtalk_intr_t, intr_func_t, intr_arg_t, xtalk_intr_setfunc_t, void *);
void xtalk_intr_disconnect(xtalk_intr_t);
devfs_handle_t xtalk_intr_cpu_get(xtalk_intr_t);
int xtalk_error_handler(devfs_handle_t, int, ioerror_mode_t, ioerror_t *);
int xtalk_error_devenable(devfs_handle_t, int, int);
void xtalk_provider_startup(devfs_handle_t);
void xtalk_provider_shutdown(devfs_handle_t);
devfs_handle_t xtalk_intr_dev_get(xtalk_intr_t);
xwidgetnum_t xtalk_intr_target_get(xtalk_intr_t);
xtalk_intr_vector_t xtalk_intr_vector_get(xtalk_intr_t);
iopaddr_t xtalk_intr_addr_get(struct xtalk_intr_s *);
void *xtalk_intr_sfarg_get(xtalk_intr_t);
devfs_handle_t xtalk_pio_dev_get(xtalk_piomap_t);
xwidgetnum_t xtalk_pio_target_get(xtalk_piomap_t);
iopaddr_t xtalk_pio_xtalk_addr_get(xtalk_piomap_t);
ulong xtalk_pio_mapsz_get(xtalk_piomap_t);
caddr_t xtalk_pio_kvaddr_get(xtalk_piomap_t);
devfs_handle_t xtalk_dma_dev_get(xtalk_dmamap_t);
xwidgetnum_t xtalk_dma_target_get(xtalk_dmamap_t);
xwidget_info_t xwidget_info_chk(devfs_handle_t);
xwidget_info_t xwidget_info_get(devfs_handle_t);
void xwidget_info_set(devfs_handle_t, xwidget_info_t);
devfs_handle_t xwidget_info_dev_get(xwidget_info_t);
xwidgetnum_t xwidget_info_id_get(xwidget_info_t);
devfs_handle_t xwidget_info_master_get(xwidget_info_t);
xwidgetnum_t xwidget_info_masterid_get(xwidget_info_t);
xwidget_part_num_t xwidget_info_part_num_get(xwidget_info_t);
xwidget_mfg_num_t xwidget_info_mfg_num_get(xwidget_info_t);
char *xwidget_info_name_get(xwidget_info_t);
void xtalk_init(void);
void xtalk_provider_register(devfs_handle_t, xtalk_provider_t *);
void xtalk_provider_unregister(devfs_handle_t);
xtalk_provider_t *xtalk_provider_fns_get(devfs_handle_t);
int xwidget_driver_register(xwidget_part_num_t,
xwidget_mfg_num_t,
char *, unsigned);
void xwidget_driver_unregister(char *);
int xwidget_register(xwidget_hwid_t, devfs_handle_t,
xwidgetnum_t, devfs_handle_t,
xwidgetnum_t, async_attach_t);
int xwidget_unregister(devfs_handle_t);
void xwidget_reset(devfs_handle_t);
char *xwidget_name_get(devfs_handle_t);
#if !defined(DEV_FUNC)
/*
* There is more than one possible provider
* for this platform. We need to examine the
* master vertex of the current vertex for
* a provider function structure, and indirect
* through the appropriately named member.
*/
#define DEV_FUNC(dev,func) xwidget_to_provider_fns(dev)->func
#define CAST_PIOMAP(x) ((xtalk_piomap_t)(x))
#define CAST_DMAMAP(x) ((xtalk_dmamap_t)(x))
#define CAST_INTR(x) ((xtalk_intr_t)(x))
static xtalk_provider_t *
xwidget_to_provider_fns(devfs_handle_t xconn)
{
xwidget_info_t widget_info;
xtalk_provider_t *provider_fns;
widget_info = xwidget_info_get(xconn);
ASSERT(widget_info != NULL);
provider_fns = xwidget_info_pops_get(widget_info);
ASSERT(provider_fns != NULL);
return (provider_fns);
}
#endif
/*
* Many functions are not passed their vertex
* information directly; rather, they must
* dive through a resource map. These macros
* are available to coordinate this detail.
*/
#define PIOMAP_FUNC(map,func) DEV_FUNC(map->xp_dev,func)
#define DMAMAP_FUNC(map,func) DEV_FUNC(map->xd_dev,func)
#define INTR_FUNC(intr,func) DEV_FUNC(intr_hdl->xi_dev,func)
/* =====================================================================
* PIO MANAGEMENT
*
* For mapping system virtual address space to
* xtalk space on a specified widget
*/
xtalk_piomap_t
xtalk_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */
device_desc_t dev_desc, /* device descriptor */
iopaddr_t xtalk_addr, /* map for this xtalk_addr range */
size_t byte_count,
size_t byte_count_max, /* maximum size of a mapping */
unsigned flags)
{ /* defined in sys/pio.h */
return (xtalk_piomap_t) DEV_FUNC(dev, piomap_alloc)
(dev, dev_desc, xtalk_addr, byte_count, byte_count_max, flags);
}
void
xtalk_piomap_free(xtalk_piomap_t xtalk_piomap)
{
PIOMAP_FUNC(xtalk_piomap, piomap_free)
(CAST_PIOMAP(xtalk_piomap));
}
caddr_t
xtalk_piomap_addr(xtalk_piomap_t xtalk_piomap, /* mapping resources */
iopaddr_t xtalk_addr, /* map for this xtalk address */
size_t byte_count)
{ /* map this many bytes */
return PIOMAP_FUNC(xtalk_piomap, piomap_addr)
(CAST_PIOMAP(xtalk_piomap), xtalk_addr, byte_count);
}
void
xtalk_piomap_done(xtalk_piomap_t xtalk_piomap)
{
PIOMAP_FUNC(xtalk_piomap, piomap_done)
(CAST_PIOMAP(xtalk_piomap));
}
caddr_t
xtalk_piotrans_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
iopaddr_t xtalk_addr, /* Crosstalk address */
size_t byte_count, /* map this many bytes */
unsigned flags)
{ /* (currently unused) */
return DEV_FUNC(dev, piotrans_addr)
(dev, dev_desc, xtalk_addr, byte_count, flags);
}
caddr_t
xtalk_pio_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
iopaddr_t addr, /* starting address (or offset in window) */
size_t byte_count, /* map this many bytes */
xtalk_piomap_t *mapp, /* where to return the map pointer */
unsigned flags)
{ /* PIO flags */
xtalk_piomap_t map = 0;
caddr_t res;
if (mapp)
*mapp = 0; /* record "no map used" */
res = xtalk_piotrans_addr
(dev, dev_desc, addr, byte_count, flags);
if (res)
return res; /* xtalk_piotrans worked */
map = xtalk_piomap_alloc
(dev, dev_desc, addr, byte_count, byte_count, flags);
if (!map)
return res; /* xtalk_piomap_alloc failed */
res = xtalk_piomap_addr
(map, addr, byte_count);
if (!res) {
xtalk_piomap_free(map);
return res; /* xtalk_piomap_addr failed */
}
if (mapp)
*mapp = map; /* pass back map used */
return res; /* xtalk_piomap_addr succeeded */
}
/* =====================================================================
* EARLY PIOTRANS SUPPORT
*
* There are places where drivers (mgras, for instance)
* need to get PIO translations before the infrastructure
* is extended to them (setting up textports, for
* instance). These drivers should call
* xtalk_early_piotrans_addr with their xtalk ID
* information, a sequence number (so we can use the second
* mgras for instance), and the usual piotrans parameters.
*
* Machine specific code should provide an implementation
* of early_piotrans_addr, and present a pointer to this
* function to xtalk_set_early_piotrans_addr so it can be
* used by clients without the clients having to know what
* platform or what xtalk provider is in use.
*/
static xtalk_early_piotrans_addr_f null_xtalk_early_piotrans_addr;
xtalk_early_piotrans_addr_f *impl_early_piotrans_addr = null_xtalk_early_piotrans_addr;
/* xtalk_set_early_piotrans_addr:
* specify the early_piotrans_addr implementation function.
*/
void
xtalk_set_early_piotrans_addr(xtalk_early_piotrans_addr_f *impl)
{
impl_early_piotrans_addr = impl;
}
/* xtalk_early_piotrans_addr:
* figure out a PIO address for the "nth" crosstalk widget that
* matches the specified part and mfgr number. Returns NULL if
* there is no such widget, or if the requested mapping can not
* be constructed.
* Limitations on which crosstalk slots (and busses) are
* checked, and definitions of the ordering of the search across
* the crosstalk slots, are defined by the platform.
*/
caddr_t
xtalk_early_piotrans_addr(xwidget_part_num_t part_num,
xwidget_mfg_num_t mfg_num,
int which,
iopaddr_t xtalk_addr,
size_t byte_count,
unsigned flags)
{
return impl_early_piotrans_addr
(part_num, mfg_num, which, xtalk_addr, byte_count, flags);
}
/* null_xtalk_early_piotrans_addr:
* used as the early_piotrans_addr implementation until and
* unless a real implementation is provided. In DEBUG kernels,
* we want to know who is calling before the implementation is
* registered; in non-DEBUG kernels, return NULL representing
* lack of mapping support.
*/
/*ARGSUSED */
static caddr_t
null_xtalk_early_piotrans_addr(xwidget_part_num_t part_num,
xwidget_mfg_num_t mfg_num,
int which,
iopaddr_t xtalk_addr,
size_t byte_count,
unsigned flags)
{
#if DEBUG
PRINT_PANIC("null_xtalk_early_piotrans_addr");
#endif
return NULL;
}
/* =====================================================================
* DMA MANAGEMENT
*
* For mapping from crosstalk space to system
* physical space.
*/
xtalk_dmamap_t
xtalk_dmamap_alloc(devfs_handle_t dev, /* set up mappings for this device */
device_desc_t dev_desc, /* device descriptor */
size_t byte_count_max, /* max size of a mapping */
unsigned flags)
{ /* defined in dma.h */
return (xtalk_dmamap_t) DEV_FUNC(dev, dmamap_alloc)
(dev, dev_desc, byte_count_max, flags);
}
void
xtalk_dmamap_free(xtalk_dmamap_t xtalk_dmamap)
{
DMAMAP_FUNC(xtalk_dmamap, dmamap_free)
(CAST_DMAMAP(xtalk_dmamap));
}
iopaddr_t
xtalk_dmamap_addr(xtalk_dmamap_t xtalk_dmamap, /* use these mapping resources */
paddr_t paddr, /* map for this address */
size_t byte_count)
{ /* map this many bytes */
return DMAMAP_FUNC(xtalk_dmamap, dmamap_addr)
(CAST_DMAMAP(xtalk_dmamap), paddr, byte_count);
}
alenlist_t
xtalk_dmamap_list(xtalk_dmamap_t xtalk_dmamap, /* use these mapping resources */
alenlist_t alenlist, /* map this Address/Length List */
unsigned flags)
{
return DMAMAP_FUNC(xtalk_dmamap, dmamap_list)
(CAST_DMAMAP(xtalk_dmamap), alenlist, flags);
}
void
xtalk_dmamap_done(xtalk_dmamap_t xtalk_dmamap)
{
DMAMAP_FUNC(xtalk_dmamap, dmamap_done)
(CAST_DMAMAP(xtalk_dmamap));
}
iopaddr_t
xtalk_dmatrans_addr(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
paddr_t paddr, /* system physical address */
size_t byte_count, /* length */
unsigned flags)
{ /* defined in dma.h */
return DEV_FUNC(dev, dmatrans_addr)
(dev, dev_desc, paddr, byte_count, flags);
}
alenlist_t
xtalk_dmatrans_list(devfs_handle_t dev, /* translate for this device */
device_desc_t dev_desc, /* device descriptor */
alenlist_t palenlist, /* system address/length list */
unsigned flags)
{ /* defined in dma.h */
return DEV_FUNC(dev, dmatrans_list)
(dev, dev_desc, palenlist, flags);
}
void
xtalk_dmamap_drain(xtalk_dmamap_t map)
{
DMAMAP_FUNC(map, dmamap_drain)
(CAST_DMAMAP(map));
}
void
xtalk_dmaaddr_drain(devfs_handle_t dev, paddr_t addr, size_t size)
{
DEV_FUNC(dev, dmaaddr_drain)
(dev, addr, size);
}
void
xtalk_dmalist_drain(devfs_handle_t dev, alenlist_t list)
{
DEV_FUNC(dev, dmalist_drain)
(dev, list);
}
/* =====================================================================
* INTERRUPT MANAGEMENT
*
* Allow crosstalk devices to establish interrupts
*/
/*
* Allocate resources required for an interrupt as specified in intr_desc.
* Return resource handle in intr_hdl.
*/
xtalk_intr_t
xtalk_intr_alloc(devfs_handle_t dev, /* which Crosstalk device */
device_desc_t dev_desc, /* device descriptor */
devfs_handle_t owner_dev)
{ /* owner of this interrupt */
return (xtalk_intr_t) DEV_FUNC(dev, intr_alloc)
(dev, dev_desc, owner_dev);
}
/*
* Allocate resources required for an interrupt as specified in dev_desc.
* Unconditionally setup resources to be non-threaded.
* Return resource handle in intr_hdl.
*/
xtalk_intr_t
xtalk_intr_alloc_nothd(devfs_handle_t dev, /* which Crosstalk device */
device_desc_t dev_desc, /* device descriptor */
devfs_handle_t owner_dev) /* owner of this interrupt */
{
return (xtalk_intr_t) DEV_FUNC(dev, intr_alloc_nothd)
(dev, dev_desc, owner_dev);
}
/*
* Free resources consumed by intr_alloc.
*/
void
xtalk_intr_free(xtalk_intr_t intr_hdl)
{
INTR_FUNC(intr_hdl, intr_free)
(CAST_INTR(intr_hdl));
}
/*
* Associate resources allocated with a previous xtalk_intr_alloc call with the
* described handler, arg, name, etc.
*
* Returns 0 on success, returns <0 on failure.
*/
int
xtalk_intr_connect(xtalk_intr_t intr_hdl, /* xtalk intr resource handle */
intr_func_t intr_func, /* xtalk intr handler */
intr_arg_t intr_arg, /* arg to intr handler */
xtalk_intr_setfunc_t setfunc, /* func to set intr hw */
void *setfunc_arg) /* arg to setfunc */
{
return INTR_FUNC(intr_hdl, intr_connect)
(CAST_INTR(intr_hdl), intr_func, intr_arg, setfunc, setfunc_arg);
}
/*
* Disassociate handler with the specified interrupt.
*/
void
xtalk_intr_disconnect(xtalk_intr_t intr_hdl)
{
INTR_FUNC(intr_hdl, intr_disconnect)
(CAST_INTR(intr_hdl));
}
/*
* Return a hwgraph vertex that represents the CPU currently
* targeted by an interrupt.
*/
devfs_handle_t
xtalk_intr_cpu_get(xtalk_intr_t intr_hdl)
{
return INTR_FUNC(intr_hdl, intr_cpu_get)
(CAST_INTR(intr_hdl));
}
/*
* =====================================================================
* ERROR MANAGEMENT
*/
/*
* xtalk_error_handler:
* pass this error on to the handler registered
* at the specified xtalk connecdtion point,
* or complain about it here if there is no handler.
*
* This routine plays two roles during error delivery
* to most widgets: first, the external agent (heart,
* hub, or whatever) calls in with the error and the
* connect point representing the crosstalk switch,
* or whatever crosstalk device is directly connected
* to the agent.
*
* If there is a switch, it will generally look at the
* widget number stashed in the ioerror structure; and,
* if the error came from some widget other than the
* switch, it will call back into xtalk_error_handler
* with the connection point of the offending port.
*/
int
xtalk_error_handler(
devfs_handle_t xconn,
int error_code,
ioerror_mode_t mode,
ioerror_t *ioerror)
{
xwidget_info_t xwidget_info;
xwidget_info = xwidget_info_get(xconn);
/* Make sure that xwidget_info is a valid pointer before derefencing it.
* We could come in here during very early initialization.
*/
if (xwidget_info && xwidget_info->w_efunc)
return xwidget_info->w_efunc
(xwidget_info->w_einfo,
error_code, mode, ioerror);
/*
* no error handler registered for
* the offending port. it's not clear
* what needs to be done, but reporting
* it would be a good thing, unless it
* is a mode that requires nothing.
*/
if ((mode == MODE_DEVPROBE) || (mode == MODE_DEVUSERERROR) ||
(mode == MODE_DEVREENABLE))
return IOERROR_HANDLED;
#if defined(SUPPORT_PRINTING_V_FORMAT)
printk(KERN_WARNING "Xbow at %v encountered Fatal error", xconn);
#else
printk(KERN_WARNING "Xbow at 0x%p encountered Fatal error", xconn);
#endif
ioerror_dump("xtalk", error_code, mode, ioerror);
return IOERROR_UNHANDLED;
}
int
xtalk_error_devenable(devfs_handle_t xconn_vhdl, int devnum, int error_code)
{
return DEV_FUNC(xconn_vhdl, error_devenable) (xconn_vhdl, devnum, error_code);
}
/* =====================================================================
* CONFIGURATION MANAGEMENT
*/
/*
* Startup a crosstalk provider
*/
void
xtalk_provider_startup(devfs_handle_t xtalk_provider)
{
DEV_FUNC(xtalk_provider, provider_startup)
(xtalk_provider);
}
/*
* Shutdown a crosstalk provider
*/
void
xtalk_provider_shutdown(devfs_handle_t xtalk_provider)
{
DEV_FUNC(xtalk_provider, provider_shutdown)
(xtalk_provider);
}
/*
* Enable a device on a xtalk widget
*/
void
xtalk_widgetdev_enable(devfs_handle_t xconn_vhdl, int devnum)
{
DEV_FUNC(xconn_vhdl, widgetdev_enable) (xconn_vhdl, devnum);
}
/*
* Shutdown a device on a xtalk widget
*/
void
xtalk_widgetdev_shutdown(devfs_handle_t xconn_vhdl, int devnum)
{
DEV_FUNC(xconn_vhdl, widgetdev_shutdown) (xconn_vhdl, devnum);
}
int
xtalk_dma_enabled(devfs_handle_t xconn_vhdl)
{
return DEV_FUNC(xconn_vhdl, dma_enabled) (xconn_vhdl);
}
/*
* Generic crosstalk functions, for use with all crosstalk providers
* and all crosstalk devices.
*/
/****** Generic crosstalk interrupt interfaces ******/
devfs_handle_t
xtalk_intr_dev_get(xtalk_intr_t xtalk_intr)
{
return (xtalk_intr->xi_dev);
}
xwidgetnum_t
xtalk_intr_target_get(xtalk_intr_t xtalk_intr)
{
return (xtalk_intr->xi_target);
}
xtalk_intr_vector_t
xtalk_intr_vector_get(xtalk_intr_t xtalk_intr)
{
return (xtalk_intr->xi_vector);
}
iopaddr_t
xtalk_intr_addr_get(struct xtalk_intr_s *xtalk_intr)
{
return (xtalk_intr->xi_addr);
}
void *
xtalk_intr_sfarg_get(xtalk_intr_t xtalk_intr)
{
return (xtalk_intr->xi_sfarg);
}
/****** Generic crosstalk pio interfaces ******/
devfs_handle_t
xtalk_pio_dev_get(xtalk_piomap_t xtalk_piomap)
{
return (xtalk_piomap->xp_dev);
}
xwidgetnum_t
xtalk_pio_target_get(xtalk_piomap_t xtalk_piomap)
{
return (xtalk_piomap->xp_target);
}
iopaddr_t
xtalk_pio_xtalk_addr_get(xtalk_piomap_t xtalk_piomap)
{
return (xtalk_piomap->xp_xtalk_addr);
}
ulong
xtalk_pio_mapsz_get(xtalk_piomap_t xtalk_piomap)
{
return (xtalk_piomap->xp_mapsz);
}
caddr_t
xtalk_pio_kvaddr_get(xtalk_piomap_t xtalk_piomap)
{
return (xtalk_piomap->xp_kvaddr);
}
/****** Generic crosstalk dma interfaces ******/
devfs_handle_t
xtalk_dma_dev_get(xtalk_dmamap_t xtalk_dmamap)
{
return (xtalk_dmamap->xd_dev);
}
xwidgetnum_t
xtalk_dma_target_get(xtalk_dmamap_t xtalk_dmamap)
{
return (xtalk_dmamap->xd_target);
}
/****** Generic crosstalk widget information interfaces ******/
/* xwidget_info_chk:
* check to see if this vertex is a widget;
* if so, return its widget_info (if any).
* if not, return NULL.
*/
xwidget_info_t
xwidget_info_chk(devfs_handle_t xwidget)
{
arbitrary_info_t ainfo = 0;
hwgraph_info_get_LBL(xwidget, INFO_LBL_XWIDGET, &ainfo);
return (xwidget_info_t) ainfo;
}
xwidget_info_t
xwidget_info_get(devfs_handle_t xwidget)
{
xwidget_info_t widget_info;
widget_info = (xwidget_info_t)
hwgraph_fastinfo_get(xwidget);
#ifdef LATER
if ((widget_info != NULL) &&
(widget_info->w_fingerprint != widget_info_fingerprint))
#ifdef SUPPORT_PRINTING_V_FORMAT
PRINT_PANIC("%v bad xwidget_info", xwidget);
#else
PRINT_PANIC("%x bad xwidget_info", xwidget);
#endif
#endif /* LATER */
return (widget_info);
}
void
xwidget_info_set(devfs_handle_t xwidget, xwidget_info_t widget_info)
{
if (widget_info != NULL)
widget_info->w_fingerprint = widget_info_fingerprint;
hwgraph_fastinfo_set(xwidget, (arbitrary_info_t) widget_info);
/* Also, mark this vertex as an xwidget,
* and use the widget_info, so xwidget_info_chk
* can work (and be fairly efficient).
*/
hwgraph_info_add_LBL(xwidget, INFO_LBL_XWIDGET,
(arbitrary_info_t) widget_info);
}
devfs_handle_t
xwidget_info_dev_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_vertex);
}
xwidgetnum_t
xwidget_info_id_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_id);
}
devfs_handle_t
xwidget_info_master_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_master);
}
xwidgetnum_t
xwidget_info_masterid_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_masterid);
}
xwidget_part_num_t
xwidget_info_part_num_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_hwid.part_num);
}
xwidget_mfg_num_t
xwidget_info_mfg_num_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget_info");
return (xwidget_info->w_hwid.mfg_num);
}
/* Extract the widget name from the widget information
* for the xtalk widget.
*/
char *
xwidget_info_name_get(xwidget_info_t xwidget_info)
{
if (xwidget_info == NULL)
panic("null xwidget info");
return(xwidget_info->w_name);
}
/****** Generic crosstalk initialization interfaces ******/
/*
* One-time initialization needed for systems that support crosstalk.
*/
void
xtalk_init(void)
{
cdl_p cp;
#if DEBUG && ATTACH_DEBUG
printf("xtalk_init\n");
#endif
/* Allocate the registry.
* We might already have one.
* If we don't, go get one.
* MPness: someone might have
* set one up for us while we
* were not looking; use an atomic
* compare-and-swap to commit to
* using the new registry if and
* only if nobody else did first.
* If someone did get there first,
* toss the one we allocated back
* into the pool.
*/
if (xtalk_registry == NULL) {
cp = cdl_new(EDGE_LBL_XIO, "part", "mfgr");
if (!compare_and_swap_ptr((void **) &xtalk_registry, NULL, (void *) cp)) {
cdl_del(cp);
}
}
ASSERT(xtalk_registry != NULL);
}
/*
* Associate a set of xtalk_provider functions with a vertex.
*/
void
xtalk_provider_register(devfs_handle_t provider, xtalk_provider_t *xtalk_fns)
{
hwgraph_fastinfo_set(provider, (arbitrary_info_t) xtalk_fns);
}
/*
* Disassociate a set of xtalk_provider functions with a vertex.
*/
void
xtalk_provider_unregister(devfs_handle_t provider)
{
hwgraph_fastinfo_set(provider, (arbitrary_info_t)NULL);
}
/*
* Obtain a pointer to the xtalk_provider functions for a specified Crosstalk
* provider.
*/
xtalk_provider_t *
xtalk_provider_fns_get(devfs_handle_t provider)
{
return ((xtalk_provider_t *) hwgraph_fastinfo_get(provider));
}
/*
* Announce a driver for a particular crosstalk part.
* Returns 0 on success or -1 on failure. Failure occurs if the
* specified hardware already has a driver.
*/
/*ARGSUSED4 */
int
xwidget_driver_register(xwidget_part_num_t part_num,
xwidget_mfg_num_t mfg_num,
char *driver_prefix,
unsigned flags)
{
/* a driver's init routine could call
* xwidget_driver_register before the
* system calls xtalk_init; so, we
* make the call here.
*/
if (xtalk_registry == NULL)
xtalk_init();
return cdl_add_driver(xtalk_registry,
part_num, mfg_num,
driver_prefix, flags, NULL);
}
/*
* Inform xtalk infrastructure that a driver is no longer available for
* handling any widgets.
*/
void
xwidget_driver_unregister(char *driver_prefix)
{
/* before a driver calls unregister,
* it must have called registger; so we
* can assume we have a registry here.
*/
ASSERT(xtalk_registry != NULL);
cdl_del_driver(xtalk_registry, driver_prefix, NULL);
}
/*
* Call some function with each vertex that
* might be one of this driver's attach points.
*/
void
xtalk_iterate(char *driver_prefix,
xtalk_iter_f *func)
{
ASSERT(xtalk_registry != NULL);
cdl_iterate(xtalk_registry, driver_prefix, (cdl_iter_f *)func);
}
/*
* xwidget_register:
* Register a xtalk device (xwidget) by doing the following.
* -allocate and initialize xwidget_info data
* -allocate a hwgraph vertex with name based on widget number (id)
* -look up the widget's initialization function and call it,
* or remember the vertex for later initialization.
*
*/
int
xwidget_register(xwidget_hwid_t hwid, /* widget's hardware ID */
devfs_handle_t widget, /* widget to initialize */
xwidgetnum_t id, /* widget's target id (0..f) */
devfs_handle_t master, /* widget's master vertex */
xwidgetnum_t targetid, /* master's target id (9/a) */
async_attach_t aa)
{
xwidget_info_t widget_info;
char *s,devnm[MAXDEVNAME];
/* Allocate widget_info and associate it with widget vertex */
NEW(widget_info);
/* Initialize widget_info */
widget_info->w_vertex = widget;
widget_info->w_id = id;
widget_info->w_master = master;
widget_info->w_masterid = targetid;
widget_info->w_hwid = *hwid; /* structure copy */
widget_info->w_efunc = 0;
widget_info->w_einfo = 0;
/*
* get the name of this xwidget vertex and keep the info.
* This is needed during errors and interupts, but as
* long as we have it, we can use it elsewhere.
*/
s = dev_to_name(widget,devnm,MAXDEVNAME);
widget_info->w_name = kmalloc(strlen(s) + 1, GFP_KERNEL);
strcpy(widget_info->w_name,s);
xwidget_info_set(widget, widget_info);
device_master_set(widget, master);
/* All the driver init routines (including
* xtalk_init) are called before we get into
* attaching devices, so we can assume we
* have a registry here.
*/
ASSERT(xtalk_registry != NULL);
/*
* Add pointer to async attach info -- tear down will be done when
* the particular descendant is done with the info.
*/
if (aa)
async_attach_add_info(widget, aa);
return cdl_add_connpt(xtalk_registry, hwid->part_num, hwid->mfg_num,
widget, 0);
}
/*
* xwidget_unregister :
* Unregister the xtalk device and detach all its hwgraph namespace.
*/
int
xwidget_unregister(devfs_handle_t widget)
{
xwidget_info_t widget_info;
xwidget_hwid_t hwid;
/* Make sure that we have valid widget information initialized */
if (!(widget_info = xwidget_info_get(widget)))
return(1);
/* Remove the inventory information associated
* with the widget.
*/
hwgraph_inventory_remove(widget, -1, -1, -1, -1, -1);
hwid = &(widget_info->w_hwid);
cdl_del_connpt(xtalk_registry, hwid->part_num, hwid->mfg_num,
widget, 0);
/* Clean out the xwidget information */
(void)kfree(widget_info->w_name);
BZERO((void *)widget_info, sizeof(widget_info));
DEL(widget_info);
return(0);
}
void
xwidget_error_register(devfs_handle_t xwidget,
error_handler_f *efunc,
error_handler_arg_t einfo)
{
xwidget_info_t xwidget_info;
xwidget_info = xwidget_info_get(xwidget);
ASSERT(xwidget_info != NULL);
xwidget_info->w_efunc = efunc;
xwidget_info->w_einfo = einfo;
}
/*
* Issue a link reset to a widget.
*/
void
xwidget_reset(devfs_handle_t xwidget)
{
xswitch_reset_link(xwidget);
}
void
xwidget_gfx_reset(devfs_handle_t xwidget)
{
xwidget_info_t info;
xswitch_reset_link(xwidget);
info = xwidget_info_get(xwidget);
#ifdef LATER
ASSERT_ALWAYS(info != NULL);
#endif
/*
* Enable this for other architectures once we add widget_reset to the
* xtalk provider interface.
*/
DEV_FUNC(xtalk_provider, widget_reset)
(xwidget_info_master_get(info), xwidget_info_id_get(info));
}
#define ANON_XWIDGET_NAME "No Name" /* Default Widget Name */
/* Get the canonical hwgraph name of xtalk widget */
char *
xwidget_name_get(devfs_handle_t xwidget_vhdl)
{
xwidget_info_t info;
/* If we have a bogus widget handle then return
* a default anonymous widget name.
*/
if (xwidget_vhdl == GRAPH_VERTEX_NONE)
return(ANON_XWIDGET_NAME);
/* Read the widget name stored in the widget info
* for the widget setup during widget initialization.
*/
info = xwidget_info_get(xwidget_vhdl);
ASSERT(info != NULL);
return(xwidget_info_name_get(info));
}
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <asm/io.h>
#include <linux/module.h>
extern void * sn_io_addr(unsigned long port); /* defined in sn[12]/iomv.c */
/**
* sn_inb - read a byte from a port
* @port: port to read from
*
* Reads a byte from @port and returns it to the caller.
*/
unsigned int
sn_inb (unsigned long port)
{
volatile unsigned char *addr = sn_io_addr(port);
unsigned char ret;
ret = *addr;
__ia64_mf_a();
return ret;
}
/**
* sn_inw - read a word from a port
* @port: port to read from
*
* Reads a word from @port and returns it to the caller.
*/
unsigned int
sn_inw (unsigned long port)
{
volatile unsigned short *addr = sn_io_addr(port);
unsigned short ret;
ret = *addr;
__ia64_mf_a();
return ret;
}
/**
* sn_inl - read a word from a port
* @port: port to read from
*
* Reads a word from @port and returns it to the caller.
*/
unsigned int
sn_inl (unsigned long port)
{
volatile unsigned int *addr = sn_io_addr(port);
unsigned int ret;
ret = *addr;
__ia64_mf_a();
return ret;
}
/**
* sn_outb - write a byte to a port
* @port: port to write to
* @val: value to write
*
* Writes @val to @port.
*/
void
sn_outb (unsigned char val, unsigned long port)
{
volatile unsigned char *addr = sn_io_addr(port);
*addr = val;
}
/**
* sn_outw - write a word to a port
* @port: port to write to
* @val: value to write
*
* Writes @val to @port.
*/
void
sn_outw (unsigned short val, unsigned long port)
{
volatile unsigned short *addr = sn_io_addr(port);
*addr = val;
}
/**
* sn_outl - write a word to a port
* @port: port to write to
* @val: value to write
*
* Writes @val to @port.
*/
void
sn_outl (unsigned int val, unsigned long port)
{
volatile unsigned int *addr = sn_io_addr(port);
*addr = val;
}
EXPORT_SYMBOL(sn_inb);
EXPORT_SYMBOL(sn_inw);
EXPORT_SYMBOL(sn_inl);
EXPORT_SYMBOL(sn_outb);
EXPORT_SYMBOL(sn_outw);
EXPORT_SYMBOL(sn_outl);
......@@ -40,7 +40,10 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/smp_lock.h>
#include <linux/acpi.h>
#ifdef CONFIG_KDB
#include <linux/kdb.h>
#endif
#include <asm/machvec.h>
#include <asm/page.h>
......@@ -53,7 +56,6 @@
#include <asm/irq.h>
#include <asm/hw_irq.h>
#include <asm/acpi-ext.h>
#include <asm/smp.h>
#include <asm/sn/sn_cpuid.h>
......
......@@ -267,7 +267,7 @@ sn_setup(char **cmdline_p)
/* PROM has wrong value on SN1 */
sn_rtc_cycles_per_second = 990177;
#endif
sn_rtc_usec_per_cyc = ((1000000UL<<IA64_USEC_PER_CYC_SHIFT)
sn_rtc_usec_per_cyc = ((1000000000UL<<IA64_NSEC_PER_CYC_SHIFT)
+ sn_rtc_cycles_per_second/2) / sn_rtc_cycles_per_second;
for (i=0;i<NR_CPUS;i++)
......
......@@ -32,5 +32,7 @@
# http://oss.sgi.com/projects/GenInfo/NoticeExplan
#
EXTRA_CFLAGS := -DLITTLE_ENDIAN
obj-y += cache.o iomv.o ptc_deadlock.o sn2_smp.o \
sn_proc_fs.o
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#include <asm/sn/sn2/shub_mmr.h>
#define ZEROVAL 0x3f // "zero" value for outstanding PIO requests
#define DEADLOCKBIT SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT
#define WRITECOUNT SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT
#define ALIAS_OFFSET (SH_PIO_WRITE_STATUS_0_ALIAS-SH_PIO_WRITE_STATUS_0)
.global sn2_ptc_deadlock_recovery_core
.proc sn2_ptc_deadlock_recovery_core
sn2_ptc_deadlock_recovery_core:
.regstk 5,0,0,0
ptc0 = in0
data0 = in1
ptc1 = in2
data1 = in3
piowc = in4
piowcphy = r30
psrsave = r2
zeroval = r3
scr1 = r16
scr2 = r17
extr.u piowcphy=piowc,0,61;; // Convert piowc to uncached physical address
dep piowcphy=-1,piowcphy,63,1
mov zeroval=ZEROVAL // "zero" value for PIO write count
1:
add scr2=ALIAS_OFFSET,piowc // Address of WRITE_STATUS alias register
mov scr1=7;; // Clear DEADLOCK, WRITE_ERROR, MULTI_WRITE_ERROR
st8.rel [scr2]=scr1;;
5: ld8.acq scr1=[piowc];; // Wait for PIOs to complete.
extr.u scr2=scr1,WRITECOUNT,7;;// PIO count
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b
////////////// BEGIN PHYSICAL MODE ////////////////////
mov psrsave=psr // Disable IC (no PMIs)
rsm psr.i | psr.dt | psr.ic;;
srlz.i;;
st8.rel [ptc0]=data0 // Write PTC0 & wait for completion.
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
extr.u scr2=scr1,WRITECOUNT,7;;// PIO count
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b;;
tbit.nz p8,p7=scr1,DEADLOCKBIT;;// Test for DEADLOCK
(p7) st8.rel [ptc1]=data1;; // Now write PTC1.
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
extr.u scr2=scr1,WRITECOUNT,7;;// PIO count
cmp.ne p6,p0=zeroval,scr2
(p6) br.cond.sptk 5b
tbit.nz p8,p0=scr1,DEADLOCKBIT;;// Test for DEADLOCK
mov psr.l=psrsave;; // Reenable IC
srlz.i;;
////////////// END PHYSICAL MODE ////////////////////
(p8) br.cond.spnt 1b;; // Repeat if DEADLOCK occurred.
br.ret.sptk rp
.endp sn2_ptc_deadlock_recovery_core
/*
*
* Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan
*/
#include <linux/config.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#include <asm/sn/sn_sal.h>
static int partition_id_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data) {
return sprintf(page, "%d\n", sn_local_partid());
}
struct proc_dir_entry * sgi_proc_dir = NULL;
void
register_sn_partition_id(void) {
struct proc_dir_entry *entry;
if (!sgi_proc_dir) {
sgi_proc_dir = proc_mkdir("sgi_sn", 0);
}
entry = create_proc_entry("partition_id", 0444, sgi_proc_dir);
if (entry) {
entry->nlink = 1;
entry->data = 0;
entry->read_proc = partition_id_read_proc;
entry->write_proc = NULL;
}
}
static int
system_serial_number_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data) {
return sprintf(page, "%s\n", sn_system_serial_number());
}
static int
licenseID_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data) {
return sprintf(page, "0x%lx\n",sn_partition_serial_number_val());
}
void
register_sn_serial_numbers(void) {
struct proc_dir_entry *entry;
if (!sgi_proc_dir) {
sgi_proc_dir = proc_mkdir("sgi_sn", 0);
}
entry = create_proc_entry("system_serial_number", 0444, sgi_proc_dir);
if (entry) {
entry->nlink = 1;
entry->data = 0;
entry->read_proc = system_serial_number_read_proc;
entry->write_proc = NULL;
}
entry = create_proc_entry("licenseID", 0444, sgi_proc_dir);
if (entry) {
entry->nlink = 1;
entry->data = 0;
entry->read_proc = licenseID_read_proc;
entry->write_proc = NULL;
}
}
// Disable forced interrupts, but leave the code in, just in case.
int sn_force_interrupt_flag = 0;
static int
sn_force_interrupt_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data) {
if (sn_force_interrupt_flag) {
return sprintf(page, "Force interrupt is enabled\n");
}
return sprintf(page, "Force interrupt is disabled\n");
}
static int
sn_force_interrupt_write_proc(struct file *file, const char *buffer,
unsigned long count, void *data)
{
if (*buffer == '0') {
sn_force_interrupt_flag = 0;
} else {
sn_force_interrupt_flag = 1;
}
return 1;
}
void
register_sn_force_interrupt(void) {
struct proc_dir_entry *entry;
if (!sgi_proc_dir) {
sgi_proc_dir = proc_mkdir("sgi_sn", 0);
}
entry = create_proc_entry("sn_force_interrupt",0444, sgi_proc_dir);
if (entry) {
entry->nlink = 1;
entry->data = 0;
entry->read_proc = sn_force_interrupt_read_proc;
entry->write_proc = sn_force_interrupt_write_proc;
}
}
void
register_sn_procfs(void) {
register_sn_partition_id();
register_sn_serial_numbers();
register_sn_force_interrupt();
}
#endif /* CONFIG_PROC_FS */
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#ifndef __SYS_GEO_H__
#define __SYS_GEO_H__
/* Include a platform-specific geo.h. It must define at least:
* geoid_t: Geographic identifier data type
* geo_type_t: Data type for the kind of geoid this is
* GEO_TYPE_xxx: Values for geo_type_t vars, eg. GEO_TYPE_NODE
* GEO_MAX_LEN: The maximum length of a geoid, formatted for printing
*/
#include <linux/config.h>
#ifdef CONFIG_IA64_SGI_SN2
#include <asm/sn/sn2/geo.h>
#else
#error <<BOMB! need geo.h for this platform >>
#endif /* !SN2 && ... */
/* Declarations applicable to all platforms */
/* parameter for hwcfg_format_geoid() */
#define GEO_FORMAT_HWGRAPH 1
#define GEO_FORMAT_BRIEF 2
/* (the parameter for hwcfg_format_geoid_compt() is defined in the
* platform-specific geo.h file) */
/* Routines for manipulating geoid_t values */
extern moduleid_t geo_module(geoid_t g);
extern slabid_t geo_slab(geoid_t g);
extern geo_type_t geo_type(geoid_t g);
extern int geo_valid(geoid_t g);
extern int geo_cmp(geoid_t g0, geoid_t g1);
extern geoid_t geo_new(geo_type_t type, ...);
extern geoid_t hwcfg_parse_geoid(char *buffer);
extern void hwcfg_format_geoid(char *buffer, geoid_t m, int fmt);
extern void hwcfg_format_geoid_compt(char *buffer, geoid_t m, int compt);
extern geoid_t hwcfg_geo_get_self(geo_type_t type);
extern geoid_t hwcfg_geo_get_by_nasid(geo_type_t type, nasid_t nasid);
#endif /* __SYS_GEO_H__ */
/*
* Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan
*/
#define IOCONFIG_PCIBUS "/boot/efi/ioconfig_pcibus"
#define POUND_CHAR '#'
#define MAX_LINE_LEN 128
#define MAXPATHLEN 128
struct ioconfig_parm {
unsigned long ioconfig_activated;
unsigned long number;
void *buffer;
};
struct ascii_moduleid{
unsigned char io_moduleid[8]; /* pci path name */
};
......@@ -273,6 +273,9 @@ struct module_s {
cnodeid_t nodes[MODULE_MAX_NODES];
#ifdef CONFIG_IA64_SGI_SN2
geoid_t geoid[MODULE_MAX_NODES];
struct {
char moduleid[8];
} io[MODULE_MAX_NODES];
#endif
int nodecnt; /* Number of nodes in array */
......
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#ifndef _ASM_SN_PCI_PIC_H
#define _ASM_SN_PCI_PIC_H
/*
* The PIC ASIC is a follow-on to the Bridge and Xbridge ASICs.
* It shares many of the same registers as those chips and therefore
* the primary structure for the PIC will be bridge_s as defined
* in irix/kern/sys/PCI/bridge.h. This file is intended as a complement
* to bridge.h, which includes this file.
*/
/*
* PIC AS DEVICE ZERO
* ------------------
*
* PIC handles PCI/X busses. PCI/X requires that the 'bridge' (i.e. PIC)
* be designated as 'device 0'. That is a departure from earlier SGI
* PCI bridges. Because of that we use config space 1 to access the
* config space of the first actual PCI device on the bus.
* Here's what the PIC manual says:
*
* The current PCI-X bus specification now defines that the parent
* hosts bus bridge (PIC for example) must be device 0 on bus 0. PIC
* reduced the total number of devices from 8 to 4 and removed the
* device registers and windows, now only supporting devices 0,1,2, and
* 3. PIC did leave all 8 configuration space windows. The reason was
* there was nothing to gain by removing them. Here in lies the problem.
* The device numbering we do using 0 through 3 is unrelated to the device
* numbering which PCI-X requires in configuration space. In the past we
* correlated Configs pace and our device space 0 <-> 0, 1 <-> 1, etc.
* PCI-X requires we start a 1, not 0 and currently the PX brick
* does associate our:
*
* device 0 with configuration space window 1,
* device 1 with configuration space window 2,
* device 2 with configuration space window 3,
* device 3 with configuration space window 4.
*
* The net effect is that all config space access are off-by-one with
* relation to other per-slot accesses on the PIC.
* Here is a table that shows some of that:
*
* Internal Slot#
* |
* | 0 1 2 3
* ----------|---------------------------------------
* config | 0x21000 0x22000 0x23000 0x24000
* |
* even rrb | 0[0] n/a 1[0] n/a [] == implied even/odd
* |
* odd rrb | n/a 0[1] n/a 1[1]
* |
* int dev | 00 01 10 11
* |
* ext slot# | 1 2 3 4
* ----------|---------------------------------------
*/
#ifndef __ASSEMBLY__
#ifdef __cplusplus
extern "C" {
#endif
// #include <sys/types.h>
#include <asm/sn/pci/pciio.h>
/*********************************************************************
* bus provider function table
*
* Normally, this table is only handed off explicitly
* during provider initialization, and the PCI generic
* layer will stash a pointer to it in the vertex; however,
* exporting it explicitly enables a performance hack in
* the generic PCI provider where if we know at compile
* time that the only possible PCI provider is a
* pcibr, we can go directly to this ops table.
*/
extern pciio_provider_t pci_pic_provider;
/*********************************************************************
* misc defines
*
*/
#define PIC_WIDGET_PART_NUM_BUS0 0xd102
#define PIC_WIDGET_PART_NUM_BUS1 0xd112
#define PIC_WIDGET_MFGR_NUM 0x24
#define PIC_WIDGET_REV_A 0x1
#define IS_PIC_PART_REV_A(rev) \
((rev == (PIC_WIDGET_PART_NUM_BUS0 << 4 | PIC_WIDGET_REV_A)) || \
(rev == (PIC_WIDGET_PART_NUM_BUS1 << 4 | PIC_WIDGET_REV_A)))
/*********************************************************************
* register offset defines
*
*/
/* Identification Register -- read-only */
#define PIC_IDENTIFICATION 0x00000000
/* Status Register -- read-only */
#define PIC_STATUS 0x00000008
/* Upper Address Holding Register Bus Side Errors -- read-only */
#define PIC_UPPER_ADDR_REG_BUS_SIDE_ERRS 0x00000010
/* Lower Address Holding Register Bus Side Errors -- read-only */
#define PIC_LOWER_ADDR_REG_BUS_SIDE_ERRS 0x00000018
/* Control Register -- read/write */
#define PIC_CONTROL 0x00000020
/* PCI Request Time-out Value Register -- read/write */
#define PIC_PCI_REQ_TIME_OUT_VALUE 0x00000028
/* Interrupt Destination Upper Address Register -- read/write */
#define PIC_INTR_DEST_UPPER_ADDR 0x00000030
/* Interrupt Destination Lower Address Register -- read/write */
#define PIC_INTR_DEST_LOWER_ADDR 0x00000038
/* Command Word Holding Register Bus Side -- read-only */
#define PIC_CMD_WORD_REG_BUS_SIDE 0x00000040
/* LLP Configuration Register (Bus 0 Only) -- read/write */
#define PIC_LLP_CFG_REG_(BUS_0_ONLY) 0x00000048
/* PCI Target Flush Register -- read-only */
#define PIC_PCI_TARGET_FLUSH 0x00000050
/* Command Word Holding Register Link Side -- read-only */
#define PIC_CMD_WORD_REG_LINK_SIDE 0x00000058
/* Response Buffer Error Upper Address Holding -- read-only */
#define PIC_RESP_BUF_ERR_UPPER_ADDR_ 0x00000060
/* Response Buffer Error Lower Address Holding -- read-only */
#define PIC_RESP_BUF_ERR_LOWER_ADDR_ 0x00000068
/* Test Pin Control Register -- read/write */
#define PIC_TEST_PIN_CONTROL 0x00000070
/* Address Holding Register Link Side Errors -- read-only */
#define PIC_ADDR_REG_LINK_SIDE_ERRS 0x00000078
/* Direct Map Register -- read/write */
#define PIC_DIRECT_MAP 0x00000080
/* PCI Map Fault Address Register -- read-only */
#define PIC_PCI_MAP_FAULT_ADDR 0x00000090
/* Arbitration Priority Register -- read/write */
#define PIC_ARBITRATION_PRIORITY 0x000000A0
/* Internal Ram Parity Error Register -- read-only */
#define PIC_INTERNAL_RAM_PARITY_ERR 0x000000B0
/* PCI Time-out Register -- read/write */
#define PIC_PCI_TIME_OUT 0x000000C0
/* PCI Type 1 Configuration Register -- read/write */
#define PIC_PCI_TYPE_1_CFG 0x000000C8
/* PCI Bus Error Upper Address Holding Register -- read-only */
#define PIC_PCI_BUS_ERR_UPPER_ADDR_ 0x000000D0
/* PCI Bus Error Lower Address Holding Register -- read-only */
#define PIC_PCI_BUS_ERR_LOWER_ADDR_ 0x000000D8
/* PCIX Error Address Register -- read-only */
#define PIC_PCIX_ERR_ADDR 0x000000E0
/* PCIX Error Attribute Register -- read-only */
#define PIC_PCIX_ERR_ATTRIBUTE 0x000000E8
/* PCIX Error Data Register -- read-only */
#define PIC_PCIX_ERR_DATA 0x000000F0
/* PCIX Read Request Timeout Error Register -- read-only */
#define PIC_PCIX_READ_REQ_TIMEOUT_ERR 0x000000F8
/* Interrupt Status Register -- read-only */
#define PIC_INTR_STATUS 0x00000100
/* Interrupt Enable Register -- read/write */
#define PIC_INTR_ENABLE 0x00000108
/* Reset Interrupt Status Register -- write-only */
#define PIC_RESET_INTR_STATUS 0x00000110
/* Interrupt Mode Register -- read/write */
#define PIC_INTR_MODE 0x00000118
/* Interrupt Device Register -- read/write */
#define PIC_INTR_DEVICE 0x00000120
/* Host Error Field Register -- read/write */
#define PIC_HOST_ERR_FIELD 0x00000128
/* Interrupt Pin 0 Host Address Register -- read/write */
#define PIC_INTR_PIN_0_HOST_ADDR 0x00000130
/* Interrupt Pin 1 Host Address Register -- read/write */
#define PIC_INTR_PIN_1_HOST_ADDR 0x00000138
/* Interrupt Pin 2 Host Address Register -- read/write */
#define PIC_INTR_PIN_2_HOST_ADDR 0x00000140
/* Interrupt Pin 3 Host Address Register -- read/write */
#define PIC_INTR_PIN_3_HOST_ADDR 0x00000148
/* Interrupt Pin 4 Host Address Register -- read/write */
#define PIC_INTR_PIN_4_HOST_ADDR 0x00000150
/* Interrupt Pin 5 Host Address Register -- read/write */
#define PIC_INTR_PIN_5_HOST_ADDR 0x00000158
/* Interrupt Pin 6 Host Address Register -- read/write */
#define PIC_INTR_PIN_6_HOST_ADDR 0x00000160
/* Interrupt Pin 7 Host Address Register -- read/write */
#define PIC_INTR_PIN_7_HOST_ADDR 0x00000168
/* Error Interrupt View Register -- read-only */
#define PIC_ERR_INTR_VIEW 0x00000170
/* Multiple Interrupt Register -- read-only */
#define PIC_MULTIPLE_INTR 0x00000178
/* Force Always Interrupt 0 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_0 0x00000180
/* Force Always Interrupt 1 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_1 0x00000188
/* Force Always Interrupt 2 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_2 0x00000190
/* Force Always Interrupt 3 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_3 0x00000198
/* Force Always Interrupt 4 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_4 0x000001A0
/* Force Always Interrupt 5 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_5 0x000001A8
/* Force Always Interrupt 6 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_6 0x000001B0
/* Force Always Interrupt 7 Register -- write-only */
#define PIC_FORCE_ALWAYS_INTR_7 0x000001B8
/* Force w/Pin Interrupt 0 Register -- write-only */
#define PIC_FORCE_PIN_INTR_0 0x000001C0
/* Force w/Pin Interrupt 1 Register -- write-only */
#define PIC_FORCE_PIN_INTR_1 0x000001C8
/* Force w/Pin Interrupt 2 Register -- write-only */
#define PIC_FORCE_PIN_INTR_2 0x000001D0
/* Force w/Pin Interrupt 3 Register -- write-only */
#define PIC_FORCE_PIN_INTR_3 0x000001D8
/* Force w/Pin Interrupt 4 Register -- write-only */
#define PIC_FORCE_PIN_INTR_4 0x000001E0
/* Force w/Pin Interrupt 5 Register -- write-only */
#define PIC_FORCE_PIN_INTR_5 0x000001E8
/* Force w/Pin Interrupt 6 Register -- write-only */
#define PIC_FORCE_PIN_INTR_6 0x000001F0
/* Force w/Pin Interrupt 7 Register -- write-only */
#define PIC_FORCE_PIN_INTR_7 0x000001F8
/* Device 0 Register -- read/write */
#define PIC_DEVICE_0 0x00000200
/* Device 1 Register -- read/write */
#define PIC_DEVICE_1 0x00000208
/* Device 2 Register -- read/write */
#define PIC_DEVICE_2 0x00000210
/* Device 3 Register -- read/write */
#define PIC_DEVICE_3 0x00000218
/* Device 0 Write Request Buffer Register -- read-only */
#define PIC_DEVICE_0_WRITE_REQ_BUF 0x00000240
/* Device 1 Write Request Buffer Register -- read-only */
#define PIC_DEVICE_1_WRITE_REQ_BUF 0x00000248
/* Device 2 Write Request Buffer Register -- read-only */
#define PIC_DEVICE_2_WRITE_REQ_BUF 0x00000250
/* Device 3 Write Request Buffer Register -- read-only */
#define PIC_DEVICE_3_WRITE_REQ_BUF 0x00000258
/* Even Device Response Buffer Register -- read/write */
#define PIC_EVEN_DEVICE_RESP_BUF 0x00000280
/* Odd Device Response Buffer Register -- read/write */
#define PIC_ODD_DEVICE_RESP_BUF 0x00000288
/* Read Response Buffer Status Register -- read-only */
#define PIC_READ_RESP_BUF_STATUS 0x00000290
/* Read Response Buffer Clear Register -- write-only */
#define PIC_READ_RESP_BUF_CLEAR 0x00000298
/* PCI RR 0 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_0_UPPER_ADDR_MATCH 0x00000300
/* PCI RR 0 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_0_LOWER_ADDR_MATCH 0x00000308
/* PCI RR 1 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_1_UPPER_ADDR_MATCH 0x00000310
/* PCI RR 1 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_1_LOWER_ADDR_MATCH 0x00000318
/* PCI RR 2 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_2_UPPER_ADDR_MATCH 0x00000320
/* PCI RR 2 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_2_LOWER_ADDR_MATCH 0x00000328
/* PCI RR 3 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_3_UPPER_ADDR_MATCH 0x00000330
/* PCI RR 3 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_3_LOWER_ADDR_MATCH 0x00000338
/* PCI RR 4 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_4_UPPER_ADDR_MATCH 0x00000340
/* PCI RR 4 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_4_LOWER_ADDR_MATCH 0x00000348
/* PCI RR 5 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_5_UPPER_ADDR_MATCH 0x00000350
/* PCI RR 5 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_5_LOWER_ADDR_MATCH 0x00000358
/* PCI RR 6 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_6_UPPER_ADDR_MATCH 0x00000360
/* PCI RR 6 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_6_LOWER_ADDR_MATCH 0x00000368
/* PCI RR 7 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_7_UPPER_ADDR_MATCH 0x00000370
/* PCI RR 7 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_7_LOWER_ADDR_MATCH 0x00000378
/* PCI RR 8 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_8_UPPER_ADDR_MATCH 0x00000380
/* PCI RR 8 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_8_LOWER_ADDR_MATCH 0x00000388
/* PCI RR 9 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_9_UPPER_ADDR_MATCH 0x00000390
/* PCI RR 9 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_9_LOWER_ADDR_MATCH 0x00000398
/* PCI RR 10 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_10_UPPER_ADDR_MATCH 0x000003A0
/* PCI RR 10 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_10_LOWER_ADDR_MATCH 0x000003A8
/* PCI RR 11 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_11_UPPER_ADDR_MATCH 0x000003B0
/* PCI RR 11 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_11_LOWER_ADDR_MATCH 0x000003B8
/* PCI RR 12 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_12_UPPER_ADDR_MATCH 0x000003C0
/* PCI RR 12 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_12_LOWER_ADDR_MATCH 0x000003C8
/* PCI RR 13 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_13_UPPER_ADDR_MATCH 0x000003D0
/* PCI RR 13 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_13_LOWER_ADDR_MATCH 0x000003D8
/* PCI RR 14 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_14_UPPER_ADDR_MATCH 0x000003E0
/* PCI RR 14 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_14_LOWER_ADDR_MATCH 0x000003E8
/* PCI RR 15 Upper Address Match Register -- read-only */
#define PIC_PCI_RR_15_UPPER_ADDR_MATCH 0x000003F0
/* PCI RR 15 Lower Address Match Register -- read-only */
#define PIC_PCI_RR_15_LOWER_ADDR_MATCH 0x000003F8
/* Buffer 0 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_0_FLUSH_CNT_WITH_DATA_TOUCH 0x00000400
/* Buffer 0 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_0_FLUSH_CNT_W_O_DATA_TOUCH 0x00000408
/* Buffer 0 Request in Flight Count Register -- read/write */
#define PIC_BUF_0_REQ_IN_FLIGHT_CNT 0x00000410
/* Buffer 0 Prefetch Request Count Register -- read/write */
#define PIC_BUF_0_PREFETCH_REQ_CNT 0x00000418
/* Buffer 0 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_0_TOTAL_PCI_RETRY_CNT 0x00000420
/* Buffer 0 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_0_MAX_PCI_RETRY_CNT 0x00000428
/* Buffer 0 Max Latency Count Register -- read/write */
#define PIC_BUF_0_MAX_LATENCY_CNT 0x00000430
/* Buffer 0 Clear All Register -- read/write */
#define PIC_BUF_0_CLEAR_ALL 0x00000438
/* Buffer 2 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_2_FLUSH_CNT_WITH_DATA_TOUCH 0x00000440
/* Buffer 2 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_2_FLUSH_CNT_W_O_DATA_TOUCH 0x00000448
/* Buffer 2 Request in Flight Count Register -- read/write */
#define PIC_BUF_2_REQ_IN_FLIGHT_CNT 0x00000450
/* Buffer 2 Prefetch Request Count Register -- read/write */
#define PIC_BUF_2_PREFETCH_REQ_CNT 0x00000458
/* Buffer 2 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_2_TOTAL_PCI_RETRY_CNT 0x00000460
/* Buffer 2 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_2_MAX_PCI_RETRY_CNT 0x00000468
/* Buffer 2 Max Latency Count Register -- read/write */
#define PIC_BUF_2_MAX_LATENCY_CNT 0x00000470
/* Buffer 2 Clear All Register -- read/write */
#define PIC_BUF_2_CLEAR_ALL 0x00000478
/* Buffer 4 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_4_FLUSH_CNT_WITH_DATA_TOUCH 0x00000480
/* Buffer 4 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_4_FLUSH_CNT_W_O_DATA_TOUCH 0x00000488
/* Buffer 4 Request in Flight Count Register -- read/write */
#define PIC_BUF_4_REQ_IN_FLIGHT_CNT 0x00000490
/* Buffer 4 Prefetch Request Count Register -- read/write */
#define PIC_BUF_4_PREFETCH_REQ_CNT 0x00000498
/* Buffer 4 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_4_TOTAL_PCI_RETRY_CNT 0x000004A0
/* Buffer 4 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_4_MAX_PCI_RETRY_CNT 0x000004A8
/* Buffer 4 Max Latency Count Register -- read/write */
#define PIC_BUF_4_MAX_LATENCY_CNT 0x000004B0
/* Buffer 4 Clear All Register -- read/write */
#define PIC_BUF_4_CLEAR_ALL 0x000004B8
/* Buffer 6 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_6_FLUSH_CNT_WITH_DATA_TOUCH 0x000004C0
/* Buffer 6 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_6_FLUSH_CNT_W_O_DATA_TOUCH 0x000004C8
/* Buffer 6 Request in Flight Count Register -- read/write */
#define PIC_BUF_6_REQ_IN_FLIGHT_CNT 0x000004D0
/* Buffer 6 Prefetch Request Count Register -- read/write */
#define PIC_BUF_6_PREFETCH_REQ_CNT 0x000004D8
/* Buffer 6 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_6_TOTAL_PCI_RETRY_CNT 0x000004E0
/* Buffer 6 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_6_MAX_PCI_RETRY_CNT 0x000004E8
/* Buffer 6 Max Latency Count Register -- read/write */
#define PIC_BUF_6_MAX_LATENCY_CNT 0x000004F0
/* Buffer 6 Clear All Register -- read/write */
#define PIC_BUF_6_CLEAR_ALL 0x000004F8
/* Buffer 8 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_8_FLUSH_CNT_WITH_DATA_TOUCH 0x00000500
/* Buffer 8 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_8_FLUSH_CNT_W_O_DATA_TOUCH 0x00000508
/* Buffer 8 Request in Flight Count Register -- read/write */
#define PIC_BUF_8_REQ_IN_FLIGHT_CNT 0x00000510
/* Buffer 8 Prefetch Request Count Register -- read/write */
#define PIC_BUF_8_PREFETCH_REQ_CNT 0x00000518
/* Buffer 8 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_8_TOTAL_PCI_RETRY_CNT 0x00000520
/* Buffer 8 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_8_MAX_PCI_RETRY_CNT 0x00000528
/* Buffer 8 Max Latency Count Register -- read/write */
#define PIC_BUF_8_MAX_LATENCY_CNT 0x00000530
/* Buffer 8 Clear All Register -- read/write */
#define PIC_BUF_8_CLEAR_ALL 0x00000538
/* Buffer 10 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_10_FLUSH_CNT_WITH_DATA_TOUCH 0x00000540
/* Buffer 10 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_10_FLUSH_CNT_W_O_DATA_TOUCH 0x00000548
/* Buffer 10 Request in Flight Count Register -- read/write */
#define PIC_BUF_10_REQ_IN_FLIGHT_CNT 0x00000550
/* Buffer 10 Prefetch Request Count Register -- read/write */
#define PIC_BUF_10_PREFETCH_REQ_CNT 0x00000558
/* Buffer 10 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_10_TOTAL_PCI_RETRY_CNT 0x00000560
/* Buffer 10 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_10_MAX_PCI_RETRY_CNT 0x00000568
/* Buffer 10 Max Latency Count Register -- read/write */
#define PIC_BUF_10_MAX_LATENCY_CNT 0x00000570
/* Buffer 10 Clear All Register -- read/write */
#define PIC_BUF_10_CLEAR_ALL 0x00000578
/* Buffer 12 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_12_FLUSH_CNT_WITH_DATA_TOUCH 0x00000580
/* Buffer 12 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_12_FLUSH_CNT_W_O_DATA_TOUCH 0x00000588
/* Buffer 12 Request in Flight Count Register -- read/write */
#define PIC_BUF_12_REQ_IN_FLIGHT_CNT 0x00000590
/* Buffer 12 Prefetch Request Count Register -- read/write */
#define PIC_BUF_12_PREFETCH_REQ_CNT 0x00000598
/* Buffer 12 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_12_TOTAL_PCI_RETRY_CNT 0x000005A0
/* Buffer 12 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_12_MAX_PCI_RETRY_CNT 0x000005A8
/* Buffer 12 Max Latency Count Register -- read/write */
#define PIC_BUF_12_MAX_LATENCY_CNT 0x000005B0
/* Buffer 12 Clear All Register -- read/write */
#define PIC_BUF_12_CLEAR_ALL 0x000005B8
/* Buffer 14 Flush Count with Data Touch Register -- read/write */
#define PIC_BUF_14_FLUSH_CNT_WITH_DATA_TOUCH 0x000005C0
/* Buffer 14 Flush Count w/o Data Touch Register -- read/write */
#define PIC_BUF_14_FLUSH_CNT_W_O_DATA_TOUCH 0x000005C8
/* Buffer 14 Request in Flight Count Register -- read/write */
#define PIC_BUF_14_REQ_IN_FLIGHT_CNT 0x000005D0
/* Buffer 14 Prefetch Request Count Register -- read/write */
#define PIC_BUF_14_PREFETCH_REQ_CNT 0x000005D8
/* Buffer 14 Total PCI Retry Count Register -- read/write */
#define PIC_BUF_14_TOTAL_PCI_RETRY_CNT 0x000005E0
/* Buffer 14 Max PCI Retry Count Register -- read/write */
#define PIC_BUF_14_MAX_PCI_RETRY_CNT 0x000005E8
/* Buffer 14 Max Latency Count Register -- read/write */
#define PIC_BUF_14_MAX_LATENCY_CNT 0x000005F0
/* Buffer 14 Clear All Register -- read/write */
#define PIC_BUF_14_CLEAR_ALL 0x000005F8
/* PCIX Read Buffer 0 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_0_ADDR 0x00000A00
/* PCIX Read Buffer 0 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_0_ATTRIBUTE 0x00000A08
/* PCIX Read Buffer 1 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_1_ADDR 0x00000A10
/* PCIX Read Buffer 1 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_1_ATTRIBUTE 0x00000A18
/* PCIX Read Buffer 2 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_2_ADDR 0x00000A20
/* PCIX Read Buffer 2 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_2_ATTRIBUTE 0x00000A28
/* PCIX Read Buffer 3 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_3_ADDR 0x00000A30
/* PCIX Read Buffer 3 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_3_ATTRIBUTE 0x00000A38
/* PCIX Read Buffer 4 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_4_ADDR 0x00000A40
/* PCIX Read Buffer 4 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_4_ATTRIBUTE 0x00000A48
/* PCIX Read Buffer 5 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_5_ADDR 0x00000A50
/* PCIX Read Buffer 5 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_5_ATTRIBUTE 0x00000A58
/* PCIX Read Buffer 6 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_6_ADDR 0x00000A60
/* PCIX Read Buffer 6 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_6_ATTRIBUTE 0x00000A68
/* PCIX Read Buffer 7 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_7_ADDR 0x00000A70
/* PCIX Read Buffer 7 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_7_ATTRIBUTE 0x00000A78
/* PCIX Read Buffer 8 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_8_ADDR 0x00000A80
/* PCIX Read Buffer 8 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_8_ATTRIBUTE 0x00000A88
/* PCIX Read Buffer 9 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_9_ADDR 0x00000A90
/* PCIX Read Buffer 9 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_9_ATTRIBUTE 0x00000A98
/* PCIX Read Buffer 10 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_10_ADDR 0x00000AA0
/* PCIX Read Buffer 10 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_10_ATTRIBUTE 0x00000AA8
/* PCIX Read Buffer 11 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_11_ADDR 0x00000AB0
/* PCIX Read Buffer 11 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_11_ATTRIBUTE 0x00000AB8
/* PCIX Read Buffer 12 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_12_ADDR 0x00000AC0
/* PCIX Read Buffer 12 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_12_ATTRIBUTE 0x00000AC8
/* PCIX Read Buffer 13 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_13_ADDR 0x00000AD0
/* PCIX Read Buffer 13 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_13_ATTRIBUTE 0x00000AD8
/* PCIX Read Buffer 14 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_14_ADDR 0x00000AE0
/* PCIX Read Buffer 14 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_14_ATTRIBUTE 0x00000AE8
/* PCIX Read Buffer 15 Address Register -- read-only */
#define PIC_PCIX_READ_BUF_15_ADDR 0x00000AF0
/* PCIX Read Buffer 15 Attribute Register -- read-only */
#define PIC_PCIX_READ_BUF_15_ATTRIBUTE 0x00000AF8
/* PCIX Write Buffer 0 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_0_ADDR 0x00000B00
/* PCIX Write Buffer 0 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_0_ATTRIBUTE 0x00000B08
/* PCIX Write Buffer 0 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_0_VALID 0x00000B10
/* PCIX Write Buffer 1 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_1_ADDR 0x00000B20
/* PCIX Write Buffer 1 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_1_ATTRIBUTE 0x00000B28
/* PCIX Write Buffer 1 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_1_VALID 0x00000B30
/* PCIX Write Buffer 2 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_2_ADDR 0x00000B40
/* PCIX Write Buffer 2 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_2_ATTRIBUTE 0x00000B48
/* PCIX Write Buffer 2 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_2_VALID 0x00000B50
/* PCIX Write Buffer 3 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_3_ADDR 0x00000B60
/* PCIX Write Buffer 3 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_3_ATTRIBUTE 0x00000B68
/* PCIX Write Buffer 3 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_3_VALID 0x00000B70
/* PCIX Write Buffer 4 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_4_ADDR 0x00000B80
/* PCIX Write Buffer 4 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_4_ATTRIBUTE 0x00000B88
/* PCIX Write Buffer 4 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_4_VALID 0x00000B90
/* PCIX Write Buffer 5 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_5_ADDR 0x00000BA0
/* PCIX Write Buffer 5 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_5_ATTRIBUTE 0x00000BA8
/* PCIX Write Buffer 5 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_5_VALID 0x00000BB0
/* PCIX Write Buffer 6 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_6_ADDR 0x00000BC0
/* PCIX Write Buffer 6 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_6_ATTRIBUTE 0x00000BC8
/* PCIX Write Buffer 6 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_6_VALID 0x00000BD0
/* PCIX Write Buffer 7 Address Register -- read-only */
#define PIC_PCIX_WRITE_BUF_7_ADDR 0x00000BE0
/* PCIX Write Buffer 7 Attribute Register -- read-only */
#define PIC_PCIX_WRITE_BUF_7_ATTRIBUTE 0x00000BE8
/* PCIX Write Buffer 7 Valid Register -- read-only */
#define PIC_PCIX_WRITE_BUF_7_VALID 0x00000BF0
/*********************************************************************
* misc typedefs
*
*/
typedef uint64_t picreg_t;
/*********************************************************************
* PIC register structures
*
*/
/*
* Identification Register
*
* The Identification register is a read only register used by the host CPU
* during configuration to determine the type of the widget. The format is
* the same as defined in IEEE 1149.1 JTAG Device Identification Register.
*/
typedef union pic_id_reg_u {
picreg_t pic_id_reg_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t rev_num : 4; /* 31:28 */
picreg_t part_num : 16; /* 27:12 */
picreg_t mfg_num : 11; /* 11:1 */
picreg_t : 1; /* 0:0 */
} pic_id_reg_fld_s;
} pic_id_reg_u_t;
/*
* Status Register
*
* The status register is a read register which holds status information of the
* Bus Subsection.
*/
typedef union pic_stat_reg_u {
picreg_t pic_stat_reg_regval;
struct {
picreg_t : 28; /* 63:36 */
picreg_t pci_x_speed : 2; /* 35:34 */
picreg_t pci_x_active : 1; /* 33:33 */
picreg_t : 1; /* 32:32 */
picreg_t llp_rec_cnt : 8; /* 31:24 */
picreg_t llp_tx_cnt : 8; /* 23:16 */
picreg_t rx_credit_cnt : 4; /* 15:12 */
picreg_t tx_credit_cnt : 4; /* 11:8 */
picreg_t pci_misc_input : 8; /* 7:0 */
} pic_stat_reg_fld_s;
} pic_stat_reg_u_t;
/*
* Upper Address Holding Register Bus Side Errors
*
* The upper address holding register is a read only register which contains
* the upper 16-bits of the address when certain error occurs (see error cases
* chapter). Subsequent errors are not logged until the error is cleared. The
* last logged value is held until the group is cleared and enabled.
*/
typedef union pic_upper_bus_err_u {
picreg_t pic_upper_bus_err_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t upp_addr : 16; /* 15:0 */
} pic_upper_bus_err_fld_s;
} pic_upper_bus_err_u_t;
/*
* Lower Address Holding Register Bus Side Errors
*
* The lower address holding register is a read only register which contains
* the address which either can be accessed as a word or double word. Sub-
* sequent errors are not logged until the error is cleared. The last logged
* value is held until the group is cleared and enabled.
*/
typedef union pic_lower_bus_err_u {
picreg_t pic_lower_bus_err_regval;
struct {
picreg_t : 16; /* 63:48 */
picreg_t upp_addr : 16; /* 47:32 */
picreg_t low_addr : 32; /* 31:0 */
} pic_lower_bus_err_fld_s;
} pic_lower_bus_err_u_t;
/*
* Control Register
*
* The control register is a read/write register which holds control informa-
* tion for the bus subsection.
*/
typedef union pic_control_reg_u {
picreg_t pic_control_reg_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 4; /* 31:28 */
picreg_t rst_pin_n : 4; /* 27:24 */
picreg_t : 1; /* 23:23 */
picreg_t mem_swap : 1; /* 22:22 */
picreg_t page_size : 1; /* 21:21 */
picreg_t : 4; /* 20:17 */
picreg_t f_bad_pkt : 1; /* 16:16 */
picreg_t llp_xbar_crd : 4; /* 15:12 */
picreg_t clr_rllp_cnt : 1; /* 11:11 */
picreg_t clr_tllp_cnt : 1; /* 10:10 */
picreg_t sys_end : 1; /* 9:9 */
picreg_t : 3; /* 8:6 */
picreg_t pci_speed : 2; /* 5:4 */
picreg_t widget_id : 4; /* 3:0 */
} pic_control_reg_fld_s;
} pic_control_reg_u_t;
/*
* PCI/PCI-X Request Time-out Value Register
*
* This register contains the reload value for the response timer. The request
* timer counts every 960 nS (32 PCI clocks)
*/
typedef union pic_pci_req_to_u {
picreg_t pic_pci_req_to_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 12; /* 31:20 */
picreg_t time_out : 20; /* 19:0 */
} pic_pci_req_to_fld_s;
} pic_pci_req_to_u_t;
/*
* Interrupt Destination Upper Address Register
*
* The interrupt destination upper address register is a read/write register
* containing the upper 16-bits of address of the host to which the interrupt
* is targeted. In addition the target ID is also contained in this register for
* use in Crosstalk mode.
*/
typedef union pic_int_desc_upper_u {
picreg_t pic_int_desc_upper_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 12; /* 31:20 */
picreg_t target_id : 4; /* 19:16 */
picreg_t upp_addr : 16; /* 15:0 */
} pic_int_desc_upper_fld_s;
} pic_int_desc_upper_u_t;
/*
* Interrupt Destination Lower Address Register
*
* The interrupt destination lower address register is a read/write register
* which contains the entire address of the host to which the interrupt is tar-
* geted. In addition the target ID is also contained in this register for use in
* Crosstalk mode.
*/
typedef union pic_int_desc_lower_u {
picreg_t pic_int_desc_lower_regval;
struct {
picreg_t : 12; /* 63:52 */
picreg_t target_id : 4; /* 51:48 */
picreg_t upp_addr : 16; /* 47:32 */
picreg_t low_addr : 32; /* 31:0 */
} pic_int_desc_lower_fld_s;
} pic_int_desc_lower_u_t;
/*
* Command Word Holding Register Bus Side Errors
*
* The command word holding is a read register that holds the command
* word of a Crosstalk packet when errors occur on the link side (see error
* chapter). Errors are indicated with error bits in the interrupt status regis-
* ter. Subsequent errors are not logged until the interrupt is cleared..
*/
typedef union pic_cmd_word_bus_err_u {
picreg_t pic_cmd_word_bus_err_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t didn : 4; /* 31:28 */
picreg_t sidn : 4; /* 27:24 */
picreg_t pactyp : 4; /* 23:20 */
picreg_t tnum : 5; /* 19:15 */
picreg_t coherent : 1; /* 14:14 */
picreg_t ds : 2; /* 13:12 */
picreg_t gbr : 1; /* 11:11 */
picreg_t vbpm : 1; /* 10:10 */
picreg_t error : 1; /* 9:9 */
picreg_t barrier : 1; /* 8:8 */
picreg_t : 8; /* 7:0 */
} pic_cmd_word_bus_err_fld_s;
} pic_cmd_word_bus_err_u_t;
/*
* LLP Configuration Register
*
* This register contains the configuration information for the LLP modules
* and is only valid on bus 0 side.
*/
typedef union pic_llp_cfg_u {
picreg_t pic_llp_cfg_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 6; /* 31:26 */
picreg_t llp_maxretry : 10; /* 25:16 */
picreg_t llp_nulltimeout : 6; /* 15:10 */
picreg_t llp_maxburst : 10; /* 9:0 */
} pic_llp_cfg_fld_s;
} pic_llp_cfg_u_t;
/*
* PCI/PCI-X Target Flush Register
*
* When read, this register will return a 0x00 after all previous transfers to
* the PCI bus subsection have completed.
*/
/*
* Command Word Holding Register Link Side Errors
*
* The command word holding is a read-only register that holds the com-
* mand word of a Crosstalk packet when request fifo overflow or unexpect-
* ed response errors occur. Errors are indicated with error bits in the
* interrupt status register. Subsequent errors are not logged until this inter-
* rupt is cleared.
*/
typedef union pic_cmd_word_link_err_u {
picreg_t pic_cmd_word_link_err_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t didn : 4; /* 31:28 */
picreg_t sidn : 4; /* 27:24 */
picreg_t pactyp : 4; /* 23:20 */
picreg_t tnum : 5; /* 19:15 */
picreg_t coherent : 1; /* 14:14 */
picreg_t ds : 2; /* 13:12 */
picreg_t gbr : 1; /* 11:11 */
picreg_t vbpm : 1; /* 10:10 */
picreg_t error : 1; /* 9:9 */
picreg_t barrier : 1; /* 8:8 */
picreg_t : 8; /* 7:0 */
} pic_cmd_word_link_err_fld_s;
} pic_cmd_word_link_err_u_t;
/*
* PCI Response Buffer Error Upper Address Holding Reg
*
* The response buffer error upper address holding register is a read only
* register which contains the upper 16-bits of the address when error asso-
* ciated with response buffer entries occur. Subsequent errors are not
* logged until the interrupt is cleared.
*/
typedef union pic_pci_rbuf_err_upper_u {
picreg_t pic_pci_rbuf_err_upper_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 9; /* 31:23 */
picreg_t dev_num : 3; /* 22:20 */
picreg_t buff_num : 4; /* 19:16 */
picreg_t upp_addr : 16; /* 15:0 */
} pic_pci_rbuf_err_upper_fld_s;
} pic_pci_rbuf_err_upper_u_t;
/*
* PCI Response Buffer Error Lower Address Holding Reg
*
* The response buffer error lower address holding register is a read only
* register which contains the address of the error associated with response
* buffer entries. Subsequent errors are not logged until the interrupt is
* cleared.
*/
typedef union pic_pci_rbuf_err_lower_u {
picreg_t pic_pci_rbuf_err_lower_regval;
struct {
picreg_t : 9; /* 63:55 */
picreg_t dev_num : 3; /* 54:52 */
picreg_t buff_num : 4; /* 51:48 */
picreg_t upp_addr : 16; /* 47:32 */
picreg_t low_addr : 32; /* 31:0 */
} pic_pci_rbuf_err_lower_fld_s;
} pic_pci_rbuf_err_lower_u_t;
/*
* Test Pin Control Register
*
* This register selects the output function and value to the four test pins on
* the PIC .
*/
typedef union pic_test_pin_cntl_u {
picreg_t pic_test_pin_cntl_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 8; /* 31:24 */
picreg_t tdata_out : 8; /* 23:16 */
picreg_t sel_tpin_7 : 2; /* 15:14 */
picreg_t sel_tpin_6 : 2; /* 13:12 */
picreg_t sel_tpin_5 : 2; /* 11:10 */
picreg_t sel_tpin_4 : 2; /* 9:8 */
picreg_t sel_tpin_3 : 2; /* 7:6 */
picreg_t sel_tpin_2 : 2; /* 5:4 */
picreg_t sel_tpin_1 : 2; /* 3:2 */
picreg_t sel_tpin_0 : 2; /* 1:0 */
} pic_test_pin_cntl_fld_s;
} pic_test_pin_cntl_u_t;
/*
* Address Holding Register Link Side Errors
*
* The address holding register is a read only register which contains the ad-
* dress which either can be accessed as a word or double word. Subsequent
* errors are not logged until the error is cleared. The last logged value is
* held until the group is cleared and enabled.
*/
typedef union pic_p_addr_lkerr_u {
picreg_t pic_p_addr_lkerr_regval;
struct {
picreg_t : 16; /* 63:48 */
picreg_t upp_addr : 16; /* 47:32 */
picreg_t low_addr : 32; /* 31:0 */
} pic_p_addr_lkerr_fld_s;
} pic_p_addr_lkerr_u_t;
/*
* PCI Direct Mapping Register
*
* This register is used to relocate a 2 GByte region for PCI to Crosstalk
* transfers.
*/
typedef union pic_p_dir_map_u {
picreg_t pic_p_dir_map_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 8; /* 31:24 */
picreg_t dir_w_id : 4; /* 23:20 */
picreg_t : 2; /* 19:18 */
picreg_t dir_add512 : 1; /* 17:17 */
picreg_t dir_off : 17; /* 16:0 */
} pic_p_dir_map_fld_s;
} pic_p_dir_map_u_t;
/*
* PCI Page Map Fault Address Register
*
* This register contains the address and device number when a page map
* fault occurred.
*/
typedef union pic_p_map_fault_u {
picreg_t pic_p_map_fault_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 10; /* 31:22 */
picreg_t pci_addr : 18; /* 21:4 */
picreg_t : 1; /* 3:3 */
picreg_t pci_dev_num : 3; /* 2:0 */
} pic_p_map_fault_fld_s;
} pic_p_map_fault_u_t;
/*
* Arbitration Register
*
* This register defines the priority and bus time out timing in PCI bus arbi-
* tration.
*/
typedef union pic_p_arb_u {
picreg_t pic_p_arb_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 8; /* 31:24 */
picreg_t dev_broke : 4; /* 23:20 */
picreg_t : 2; /* 19:18 */
picreg_t req_wait_tick : 2; /* 17:16 */
picreg_t : 4; /* 15:12 */
picreg_t req_wait_en : 4; /* 11:8 */
picreg_t disarb : 1; /* 7:7 */
picreg_t freeze_gnt : 1; /* 6:6 */
picreg_t : 1; /* 5:5 */
picreg_t en_bridge_hi : 2; /* 4:3 */
picreg_t : 1; /* 2:2 */
picreg_t en_bridge_lo : 2; /* 1:0 */
} pic_p_arb_fld_s;
} pic_p_arb_u_t;
/*
* Internal Ram Parity Error Register
*
* This register logs information about parity errors on internal ram access.
*/
typedef union pic_p_ram_perr_u {
picreg_t pic_p_ram_perr_regval;
struct {
picreg_t : 6; /* 63:58 */
picreg_t ate_err_addr : 10; /* 57:48 */
picreg_t : 7; /* 47:41 */
picreg_t rd_resp_err_addr : 9; /* 40:32 */
picreg_t wrt_resp_err_addr : 8; /* 31:24 */
picreg_t : 2; /* 23:22 */
picreg_t ate_err : 1; /* 21:21 */
picreg_t rd_resp_err : 1; /* 20:20 */
picreg_t wrt_resp_err : 1; /* 19:19 */
picreg_t dbe_ate : 3; /* 18:16 */
picreg_t dbe_rd : 8; /* 15:8 */
picreg_t dbe_wrt : 8; /* 7:0 */
} pic_p_ram_perr_fld_s;
} pic_p_ram_perr_u_t;
/*
* Time-out Register
*
* This register determines retry hold off and max retries allowed for PIO
* accesses to PCI/PCI-X.
*/
typedef union pic_p_bus_timeout_u {
picreg_t pic_p_bus_timeout_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 11; /* 31:21 */
picreg_t pci_retry_hld : 5; /* 20:16 */
picreg_t : 6; /* 15:10 */
picreg_t pci_retry_cnt : 10; /* 9:0 */
} pic_p_bus_timeout_fld_s;
} pic_p_bus_timeout_u_t;
/*
* PCI/PCI-X Type 1 Configuration Register
*
* This register is use during accesses to the PCI/PCI-X type 1 configuration
* space. The bits in this register are used to supplement the address during
* the configuration cycle to select the correct secondary bus and device.
*/
typedef union pic_type1_cfg_u {
picreg_t pic_type1_cfg_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 8; /* 31:24 */
picreg_t bus_num : 8; /* 23:16 */
picreg_t dev_num : 5; /* 15:11 */
picreg_t : 11; /* 10:0 */
} pic_type1_cfg_fld_s;
} pic_type1_cfg_u_t;
/*
* PCI Bus Error Upper Address Holding Register
*
* This register holds the value of the upper address on the PCI Bus when an
* error occurs.
*/
typedef union pic_p_pci_err_upper_u {
picreg_t pic_p_pci_err_upper_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 4; /* 31:28 */
picreg_t pci_xtalk_did : 4; /* 27:24 */
picreg_t : 2; /* 23:22 */
picreg_t pci_dac : 1; /* 21:21 */
picreg_t pci_dev_master : 1; /* 20:20 */
picreg_t pci_vdev : 1; /* 19:19 */
picreg_t pci_dev_num : 3; /* 18:16 */
picreg_t pci_uaddr_err : 16; /* 15:0 */
} pic_p_pci_err_upper_fld_s;
} pic_p_pci_err_upper_u_t;
/*
* PCI Bus Error Lower Address Holding Register
*
* This register holds the value of the lower address on the PCI Bus when an
* error occurs.
*/
typedef union pic_p_pci_err_lower_u {
picreg_t pic_p_pci_err_lower_regval;
struct {
picreg_t : 4; /* 63:60 */
picreg_t pci_xtalk_did : 4; /* 59:56 */
picreg_t : 2; /* 55:54 */
picreg_t pci_dac : 1; /* 53:53 */
picreg_t pci_dev_master : 1; /* 52:52 */
picreg_t pci_vdev : 1; /* 51:51 */
picreg_t pci_dev_num : 3; /* 50:48 */
picreg_t pci_uaddr_err : 16; /* 47:32 */
picreg_t pci_laddr_err : 32; /* 31:0 */
} pic_p_pci_err_lower_fld_s;
} pic_p_pci_err_lower_u_t;
/*
* PCI-X Error Address Register
*
* This register contains the address on the PCI-X bus when an error oc-
* curred.
*/
typedef union pic_p_pcix_err_addr_u {
picreg_t pic_p_pcix_err_addr_regval;
struct {
picreg_t pcix_err_addr : 64; /* 63:0 */
} pic_p_pcix_err_addr_fld_s;
} pic_p_pcix_err_addr_u_t;
/*
* PCI-X Error Attribute Register
*
* This register contains the attribute data on the PCI-X bus when an error
* occurred.
*/
typedef union pic_p_pcix_err_attr_u {
picreg_t pic_p_pcix_err_attr_regval;
struct {
picreg_t : 16; /* 63:48 */
picreg_t bus_cmd : 4; /* 47:44 */
picreg_t byte_cnt : 12; /* 43:32 */
picreg_t : 1; /* 31:31 */
picreg_t ns : 1; /* 30:30 */
picreg_t ro : 1; /* 29:29 */
picreg_t tag : 5; /* 28:24 */
picreg_t bus_num : 8; /* 23:16 */
picreg_t dev_num : 5; /* 15:11 */
picreg_t fun_num : 3; /* 10:8 */
picreg_t l_byte_cnt : 8; /* 7:0 */
} pic_p_pcix_err_attr_fld_s;
} pic_p_pcix_err_attr_u_t;
/*
* PCI-X Error Data Register
*
* This register contains the Data on the PCI-X bus when an error occurred.
*/
typedef union pic_p_pcix_err_data_u {
picreg_t pic_p_pcix_err_data_regval;
struct {
picreg_t pcix_err_data : 64; /* 63:0 */
} pic_p_pcix_err_data_fld_s;
} pic_p_pcix_err_data_u_t;
/*
* PCI-X Read Request Timeout Error Register
*
* This register contains a pointer into the PCI-X read data structure.
*/
typedef union pic_p_pcix_read_req_to_u {
picreg_t pic_p_pcix_read_req_to_regval;
struct {
picreg_t : 55; /* 63:9 */
picreg_t rd_buff_loc : 5; /* 8:4 */
picreg_t rd_buff_struct : 4; /* 3:0 */
} pic_p_pcix_read_req_to_fld_s;
} pic_p_pcix_read_req_to_u_t;
/*
* INT_STATUS Register
*
* This is the current interrupt status register which maintains the current
* status of all the interrupting devices which generated a n interrupt. This
* register is read only and all the bits are active high. A high bit at
* INT_STATE means the corresponding INT_N pin has been asserted
* (low).
*/
typedef union pic_p_int_status_u {
picreg_t pic_p_int_status_regval;
struct {
picreg_t : 22; /* 63:42 */
picreg_t int_ram_perr : 1; /* 41:41 */
picreg_t bus_arb_broke : 1; /* 40:40 */
picreg_t pci_x_req_tout : 1; /* 39:39 */
picreg_t pci_x_tabort : 1; /* 38:38 */
picreg_t pci_x_perr : 1; /* 37:37 */
picreg_t pci_x_serr : 1; /* 36:36 */
picreg_t pci_x_mretry : 1; /* 35:35 */
picreg_t pci_x_mtout : 1; /* 34:34 */
picreg_t pci_x_da_parity : 1; /* 33:33 */
picreg_t pci_x_ad_parity : 1; /* 32:32 */
picreg_t : 1; /* 31:31 */
picreg_t pmu_page_fault : 1; /* 30:30 */
picreg_t unexpected_resp : 1; /* 29:29 */
picreg_t bad_xresp_packet : 1; /* 28:28 */
picreg_t bad_xreq_packet : 1; /* 27:27 */
picreg_t resp_xtalk_error : 1; /* 26:26 */
picreg_t req_xtalk_error : 1; /* 25:25 */
picreg_t invalid_access : 1; /* 24:24 */
picreg_t unsupported_xop : 1; /* 23:23 */
picreg_t xreq_fifo_oflow : 1; /* 22:22 */
picreg_t llp_rec_snerror : 1; /* 21:21 */
picreg_t llp_rec_cberror : 1; /* 20:20 */
picreg_t llp_rcty : 1; /* 19:19 */
picreg_t llp_tx_retry : 1; /* 18:18 */
picreg_t llp_tcty : 1; /* 17:17 */
picreg_t : 1; /* 16:16 */
picreg_t pci_abort : 1; /* 15:15 */
picreg_t pci_parity : 1; /* 14:14 */
picreg_t pci_serr : 1; /* 13:13 */
picreg_t pci_perr : 1; /* 12:12 */
picreg_t pci_master_tout : 1; /* 11:11 */
picreg_t pci_retry_cnt : 1; /* 10:10 */
picreg_t xread_req_tout : 1; /* 9:9 */
picreg_t : 1; /* 8:8 */
picreg_t int_state : 8; /* 7:0 */
} pic_p_int_status_fld_s;
} pic_p_int_status_u_t;
/*
* Interrupt Enable Register
*
* This register enables the reporting of interrupt to the host. Each bit in this
* register corresponds to the same bit in Interrupt Status register. All bits
* are zero after reset.
*/
typedef union pic_p_int_enable_u {
picreg_t pic_p_int_enable_regval;
struct {
picreg_t : 22; /* 63:42 */
picreg_t en_int_ram_perr : 1; /* 41:41 */
picreg_t en_bus_arb_broke : 1; /* 40:40 */
picreg_t en_pci_x_req_tout : 1; /* 39:39 */
picreg_t en_pci_x_tabort : 1; /* 38:38 */
picreg_t en_pci_x_perr : 1; /* 37:37 */
picreg_t en_pci_x_serr : 1; /* 36:36 */
picreg_t en_pci_x_mretry : 1; /* 35:35 */
picreg_t en_pci_x_mtout : 1; /* 34:34 */
picreg_t en_pci_x_da_parity : 1; /* 33:33 */
picreg_t en_pci_x_ad_parity : 1; /* 32:32 */
picreg_t : 1; /* 31:31 */
picreg_t en_pmu_page_fault : 1; /* 30:30 */
picreg_t en_unexpected_resp : 1; /* 29:29 */
picreg_t en_bad_xresp_packet : 1; /* 28:28 */
picreg_t en_bad_xreq_packet : 1; /* 27:27 */
picreg_t en_resp_xtalk_error : 1; /* 26:26 */
picreg_t en_req_xtalk_error : 1; /* 25:25 */
picreg_t en_invalid_access : 1; /* 24:24 */
picreg_t en_unsupported_xop : 1; /* 23:23 */
picreg_t en_xreq_fifo_oflow : 1; /* 22:22 */
picreg_t en_llp_rec_snerror : 1; /* 21:21 */
picreg_t en_llp_rec_cberror : 1; /* 20:20 */
picreg_t en_llp_rcty : 1; /* 19:19 */
picreg_t en_llp_tx_retry : 1; /* 18:18 */
picreg_t en_llp_tcty : 1; /* 17:17 */
picreg_t : 1; /* 16:16 */
picreg_t en_pci_abort : 1; /* 15:15 */
picreg_t en_pci_parity : 1; /* 14:14 */
picreg_t en_pci_serr : 1; /* 13:13 */
picreg_t en_pci_perr : 1; /* 12:12 */
picreg_t en_pci_master_tout : 1; /* 11:11 */
picreg_t en_pci_retry_cnt : 1; /* 10:10 */
picreg_t en_xread_req_tout : 1; /* 9:9 */
picreg_t : 1; /* 8:8 */
picreg_t en_int_state : 8; /* 7:0 */
} pic_p_int_enable_fld_s;
} pic_p_int_enable_u_t;
/*
* Reset Interrupt Register
*
* A write of a "1" clears the bit and rearms the error registers. Writes also
* clear the error view register.
*/
typedef union pic_p_int_rst_u {
picreg_t pic_p_int_rst_regval;
struct {
picreg_t : 22; /* 63:42 */
picreg_t logv_int_ram_perr : 1; /* 41:41 */
picreg_t logv_bus_arb_broke : 1; /* 40:40 */
picreg_t logv_pci_x_req_tout : 1; /* 39:39 */
picreg_t logv_pci_x_tabort : 1; /* 38:38 */
picreg_t logv_pci_x_perr : 1; /* 37:37 */
picreg_t logv_pci_x_serr : 1; /* 36:36 */
picreg_t logv_pci_x_mretry : 1; /* 35:35 */
picreg_t logv_pci_x_mtout : 1; /* 34:34 */
picreg_t logv_pci_x_da_parity : 1; /* 33:33 */
picreg_t logv_pci_x_ad_parity : 1; /* 32:32 */
picreg_t : 1; /* 31:31 */
picreg_t logv_pmu_page_fault : 1; /* 30:30 */
picreg_t logv_unexpected_resp : 1; /* 29:29 */
picreg_t logv_bad_xresp_packet : 1; /* 28:28 */
picreg_t logv_bad_xreq_packet : 1; /* 27:27 */
picreg_t logv_resp_xtalk_error : 1; /* 26:26 */
picreg_t logv_req_xtalk_error : 1; /* 25:25 */
picreg_t logv_invalid_access : 1; /* 24:24 */
picreg_t logv_unsupported_xop : 1; /* 23:23 */
picreg_t logv_xreq_fifo_oflow : 1; /* 22:22 */
picreg_t logv_llp_rec_snerror : 1; /* 21:21 */
picreg_t logv_llp_rec_cberror : 1; /* 20:20 */
picreg_t logv_llp_rcty : 1; /* 19:19 */
picreg_t logv_llp_tx_retry : 1; /* 18:18 */
picreg_t logv_llp_tcty : 1; /* 17:17 */
picreg_t : 1; /* 16:16 */
picreg_t logv_pci_abort : 1; /* 15:15 */
picreg_t logv_pci_parity : 1; /* 14:14 */
picreg_t logv_pci_serr : 1; /* 13:13 */
picreg_t logv_pci_perr : 1; /* 12:12 */
picreg_t logv_pci_master_tout : 1; /* 11:11 */
picreg_t logv_pci_retry_cnt : 1; /* 10:10 */
picreg_t logv_xread_req_tout : 1; /* 9:9 */
picreg_t : 2; /* 8:7 */
picreg_t multi_clr : 1; /* 6:6 */
picreg_t : 6; /* 5:0 */
} pic_p_int_rst_fld_s;
} pic_p_int_rst_u_t;
/*
* Interrupt Mode Register
*
* This register defines the interrupting mode of the INT_N pins.
*/
typedef union pic_p_int_mode_u {
picreg_t pic_p_int_mode_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 24; /* 31:8 */
picreg_t en_clr_pkt : 8; /* 7:0 */
} pic_p_int_mode_fld_s;
} pic_p_int_mode_u_t;
/*
* Interrupt Device Select Register
*
* This register associates interrupt pins with devices thus allowing buffer
* management (flushing) when a device interrupt occurs.
*/
typedef union pic_p_int_device_u {
picreg_t pic_p_int_device_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 8; /* 31:24 */
picreg_t int7_dev : 3; /* 23:21 */
picreg_t int6_dev : 3; /* 20:18 */
picreg_t int5_dev : 3; /* 17:15 */
picreg_t int4_dev : 3; /* 14:12 */
picreg_t int3_dev : 3; /* 11:9 */
picreg_t int2_dev : 3; /* 8:6 */
picreg_t int1_dev : 3; /* 5:3 */
picreg_t int0_dev : 3; /* 2:0 */
} pic_p_int_device_fld_s;
} pic_p_int_device_u_t;
/*
* Host Error Interrupt Field Register
*
* This register tells which bit location in the host's Interrupt Status register
* to set or reset when any error condition happens.
*/
typedef union pic_p_int_host_err_u {
picreg_t pic_p_int_host_err_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 24; /* 31:8 */
picreg_t bridge_err_fld : 8; /* 7:0 */
} pic_p_int_host_err_fld_s;
} pic_p_int_host_err_u_t;
/*
* Interrupt (x) Host Address Register
*
* This register allow different host address to be assigned to each interrupt
* pin and the bit in the host.
*/
typedef union pic_p_int_addr_u {
picreg_t pic_p_int_addr_regval;
struct {
picreg_t : 8; /* 63:56 */
picreg_t int_fld : 8; /* 55:48 */
picreg_t int_addr : 48; /* 47:0 */
} pic_p_int_addr_fld_s;
} pic_p_int_addr_u_t;
/*
* Error Interrupt View Register
*
* This register contains the view of which interrupt occur even if they are
* not currently enabled. The group clear is used to clear these bits just like
* the interrupt status register bits.
*/
typedef union pic_p_err_int_view_u {
picreg_t pic_p_err_int_view_regval;
struct {
picreg_t : 22; /* 63:42 */
picreg_t int_ram_perr : 1; /* 41:41 */
picreg_t bus_arb_broke : 1; /* 40:40 */
picreg_t pci_x_req_tout : 1; /* 39:39 */
picreg_t pci_x_tabort : 1; /* 38:38 */
picreg_t pci_x_perr : 1; /* 37:37 */
picreg_t pci_x_serr : 1; /* 36:36 */
picreg_t pci_x_mretry : 1; /* 35:35 */
picreg_t pci_x_mtout : 1; /* 34:34 */
picreg_t pci_x_da_parity : 1; /* 33:33 */
picreg_t pci_x_ad_parity : 1; /* 32:32 */
picreg_t : 1; /* 31:31 */
picreg_t pmu_page_fault : 1; /* 30:30 */
picreg_t unexpected_resp : 1; /* 29:29 */
picreg_t bad_xresp_packet : 1; /* 28:28 */
picreg_t bad_xreq_packet : 1; /* 27:27 */
picreg_t resp_xtalk_error : 1; /* 26:26 */
picreg_t req_xtalk_error : 1; /* 25:25 */
picreg_t invalid_access : 1; /* 24:24 */
picreg_t unsupported_xop : 1; /* 23:23 */
picreg_t xreq_fifo_oflow : 1; /* 22:22 */
picreg_t llp_rec_snerror : 1; /* 21:21 */
picreg_t llp_rec_cberror : 1; /* 20:20 */
picreg_t llp_rcty : 1; /* 19:19 */
picreg_t llp_tx_retry : 1; /* 18:18 */
picreg_t llp_tcty : 1; /* 17:17 */
picreg_t : 1; /* 16:16 */
picreg_t pci_abort : 1; /* 15:15 */
picreg_t pci_parity : 1; /* 14:14 */
picreg_t pci_serr : 1; /* 13:13 */
picreg_t pci_perr : 1; /* 12:12 */
picreg_t pci_master_tout : 1; /* 11:11 */
picreg_t pci_retry_cnt : 1; /* 10:10 */
picreg_t xread_req_tout : 1; /* 9:9 */
picreg_t : 9; /* 8:0 */
} pic_p_err_int_view_fld_s;
} pic_p_err_int_view_u_t;
/*
* Multiple Interrupt Register
*
* This register indicates if any interrupt occurs more than once without be-
* ing cleared.
*/
typedef union pic_p_mult_int_u {
picreg_t pic_p_mult_int_regval;
struct {
picreg_t : 22; /* 63:42 */
picreg_t int_ram_perr : 1; /* 41:41 */
picreg_t bus_arb_broke : 1; /* 40:40 */
picreg_t pci_x_req_tout : 1; /* 39:39 */
picreg_t pci_x_tabort : 1; /* 38:38 */
picreg_t pci_x_perr : 1; /* 37:37 */
picreg_t pci_x_serr : 1; /* 36:36 */
picreg_t pci_x_mretry : 1; /* 35:35 */
picreg_t pci_x_mtout : 1; /* 34:34 */
picreg_t pci_x_da_parity : 1; /* 33:33 */
picreg_t pci_x_ad_parity : 1; /* 32:32 */
picreg_t : 1; /* 31:31 */
picreg_t pmu_page_fault : 1; /* 30:30 */
picreg_t unexpected_resp : 1; /* 29:29 */
picreg_t bad_xresp_packet : 1; /* 28:28 */
picreg_t bad_xreq_packet : 1; /* 27:27 */
picreg_t resp_xtalk_error : 1; /* 26:26 */
picreg_t req_xtalk_error : 1; /* 25:25 */
picreg_t invalid_access : 1; /* 24:24 */
picreg_t unsupported_xop : 1; /* 23:23 */
picreg_t xreq_fifo_oflow : 1; /* 22:22 */
picreg_t llp_rec_snerror : 1; /* 21:21 */
picreg_t llp_rec_cberror : 1; /* 20:20 */
picreg_t llp_rcty : 1; /* 19:19 */
picreg_t llp_tx_retry : 1; /* 18:18 */
picreg_t llp_tcty : 1; /* 17:17 */
picreg_t : 1; /* 16:16 */
picreg_t pci_abort : 1; /* 15:15 */
picreg_t pci_parity : 1; /* 14:14 */
picreg_t pci_serr : 1; /* 13:13 */
picreg_t pci_perr : 1; /* 12:12 */
picreg_t pci_master_tout : 1; /* 11:11 */
picreg_t pci_retry_cnt : 1; /* 10:10 */
picreg_t xread_req_tout : 1; /* 9:9 */
picreg_t : 1; /* 8:8 */
picreg_t int_state : 8; /* 7:0 */
} pic_p_mult_int_fld_s;
} pic_p_mult_int_u_t;
/*
* Force Always Interrupt (x) Register
*
* A write to this data independent write only register will force a set inter-
* rupt to occur as if the interrupt line had transitioned. If the interrupt line
* is already active an addition set interrupt packet is set. All buffer flush op-
* erations also occur on this operation.
*/
/*
* Force Interrupt (x) Register
*
* A write to this data independent write only register in conjunction with
* the assertion of the corresponding interrupt line will force a set interrupt
* to occur as if the interrupt line had transitioned. The interrupt line must
* be active for this operation to generate a set packet, otherwise the write
* PIO is ignored. All buffer flush operations also occur when the set packet
* is sent on this operation.
*/
/*
* Device Registers
*
* The Device registers contain device specific and mapping information.
*/
typedef union pic_device_reg_u {
picreg_t pic_device_reg_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 2; /* 31:30 */
picreg_t en_virtual1 : 1; /* 29:29 */
picreg_t en_error_lock : 1; /* 28:28 */
picreg_t en_page_chk : 1; /* 27:27 */
picreg_t force_pci_par : 1; /* 26:26 */
picreg_t en_virtual0 : 1; /* 25:25 */
picreg_t : 1; /* 24:24 */
picreg_t dir_wrt_gen : 1; /* 23:23 */
picreg_t dev_size : 1; /* 22:22 */
picreg_t real_time : 1; /* 21:21 */
picreg_t : 1; /* 20:20 */
picreg_t swap_direct : 1; /* 19:19 */
picreg_t prefetch : 1; /* 18:18 */
picreg_t precise : 1; /* 17:17 */
picreg_t coherent : 1; /* 16:16 */
picreg_t barrier : 1; /* 15:15 */
picreg_t gbr : 1; /* 14:14 */
picreg_t dev_swap : 1; /* 13:13 */
picreg_t dev_io_mem : 1; /* 12:12 */
picreg_t dev_off : 12; /* 11:0 */
} pic_device_reg_fld_s;
} pic_device_reg_u_t;
/*
* Device (x) Write Request Buffer Flush
*
* When read, this register will return a 0x00 after the write buffer associat-
* ed with the device has been flushed. (PCI Only)
*/
/*
* Even Device Read Response Buffer Register (PCI Only)
*
* This register is use to allocate the read response buffers for the even num-
* bered devices. (0,2)
*/
typedef union pic_p_even_resp_u {
picreg_t pic_p_even_resp_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t buff_14_en : 1; /* 31:31 */
picreg_t buff_14_vdev : 2; /* 30:29 */
picreg_t buff_14_pdev : 1; /* 28:28 */
picreg_t buff_12_en : 1; /* 27:27 */
picreg_t buff_12_vdev : 2; /* 26:25 */
picreg_t buff_12_pdev : 1; /* 24:24 */
picreg_t buff_10_en : 1; /* 23:23 */
picreg_t buff_10_vdev : 2; /* 22:21 */
picreg_t buff_10_pdev : 1; /* 20:20 */
picreg_t buff_8_en : 1; /* 19:19 */
picreg_t buff_8_vdev : 2; /* 18:17 */
picreg_t buff_8_pdev : 1; /* 16:16 */
picreg_t buff_6_en : 1; /* 15:15 */
picreg_t buff_6_vdev : 2; /* 14:13 */
picreg_t buff_6_pdev : 1; /* 12:12 */
picreg_t buff_4_en : 1; /* 11:11 */
picreg_t buff_4_vdev : 2; /* 10:9 */
picreg_t buff_4_pdev : 1; /* 8:8 */
picreg_t buff_2_en : 1; /* 7:7 */
picreg_t buff_2_vdev : 2; /* 6:5 */
picreg_t buff_2_pdev : 1; /* 4:4 */
picreg_t buff_0_en : 1; /* 3:3 */
picreg_t buff_0_vdev : 2; /* 2:1 */
picreg_t buff_0_pdev : 1; /* 0:0 */
} pic_p_even_resp_fld_s;
} pic_p_even_resp_u_t;
/*
* Odd Device Read Response Buffer Register (PCI Only)
*
* This register is use to allocate the read response buffers for the odd num-
* bered devices. (1,3))
*/
typedef union pic_p_odd_resp_u {
picreg_t pic_p_odd_resp_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t buff_15_en : 1; /* 31:31 */
picreg_t buff_15_vdev : 2; /* 30:29 */
picreg_t buff_15_pdev : 1; /* 28:28 */
picreg_t buff_13_en : 1; /* 27:27 */
picreg_t buff_13_vdev : 2; /* 26:25 */
picreg_t buff_13_pdev : 1; /* 24:24 */
picreg_t buff_11_en : 1; /* 23:23 */
picreg_t buff_11_vdev : 2; /* 22:21 */
picreg_t buff_11_pdev : 1; /* 20:20 */
picreg_t buff_9_en : 1; /* 19:19 */
picreg_t buff_9_vdev : 2; /* 18:17 */
picreg_t buff_9_pdev : 1; /* 16:16 */
picreg_t buff_7_en : 1; /* 15:15 */
picreg_t buff_7_vdev : 2; /* 14:13 */
picreg_t buff_7_pdev : 1; /* 12:12 */
picreg_t buff_5_en : 1; /* 11:11 */
picreg_t buff_5_vdev : 2; /* 10:9 */
picreg_t buff_5_pdev : 1; /* 8:8 */
picreg_t buff_3_en : 1; /* 7:7 */
picreg_t buff_3_vdev : 2; /* 6:5 */
picreg_t buff_3_pdev : 1; /* 4:4 */
picreg_t buff_1_en : 1; /* 3:3 */
picreg_t buff_1_vdev : 2; /* 2:1 */
picreg_t buff_1_pdev : 1; /* 0:0 */
} pic_p_odd_resp_fld_s;
} pic_p_odd_resp_u_t;
/*
* Read Response Buffer Status Register (PCI Only)
*
* This read only register contains the current response buffer status.
*/
typedef union pic_p_resp_status_u {
picreg_t pic_p_resp_status_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t rrb_valid : 16; /* 31:16 */
picreg_t rrb_inuse : 16; /* 15:0 */
} pic_p_resp_status_fld_s;
} pic_p_resp_status_u_t;
/*
* Read Response Buffer Clear Register (PCI Only)
*
* A write to this register clears the current contents of the buffer.
*/
typedef union pic_p_resp_clear_u {
picreg_t pic_p_resp_clear_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t rrb_clear : 16; /* 15:0 */
} pic_p_resp_clear_fld_s;
} pic_p_resp_clear_u_t;
/*
* PCI Read Response Buffer (x) Upper Address Match
*
* The PCI Bridge read response buffer upper address register is a read only
* register which contains the upper 16-bits of the address and status used to
* select the buffer for a PCI transaction.
*/
typedef union pic_p_buf_upper_addr_match_u {
picreg_t pic_p_buf_upper_addr_match_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t filled : 1; /* 31:31 */
picreg_t armed : 1; /* 30:30 */
picreg_t flush : 1; /* 29:29 */
picreg_t xerr : 1; /* 28:28 */
picreg_t pkterr : 1; /* 27:27 */
picreg_t timeout : 1; /* 26:26 */
picreg_t prefetch : 1; /* 25:25 */
picreg_t precise : 1; /* 24:24 */
picreg_t dw_be : 8; /* 23:16 */
picreg_t upp_addr : 16; /* 15:0 */
} pic_p_buf_upper_addr_match_fld_s;
} pic_p_buf_upper_addr_match_u_t;
/*
* PCI Read Response Buffer (x) Lower Address Match
*
* The PCI Bridge read response buffer lower address Match register is a
* read only register which contains the address and status used to select the
* buffer for a PCI transaction.
*/
typedef union pic_p_buf_lower_addr_match_u {
picreg_t pic_p_buf_lower_addr_match_regval;
struct {
picreg_t filled : 1; /* 63:63 */
picreg_t armed : 1; /* 62:62 */
picreg_t flush : 1; /* 61:61 */
picreg_t xerr : 1; /* 60:60 */
picreg_t pkterr : 1; /* 59:59 */
picreg_t timeout : 1; /* 58:58 */
picreg_t prefetch : 1; /* 57:57 */
picreg_t precise : 1; /* 56:56 */
picreg_t dw_be : 8; /* 55:48 */
picreg_t upp_addr : 16; /* 47:32 */
picreg_t low_addr : 32; /* 31:0 */
} pic_p_buf_lower_addr_match_fld_s;
} pic_p_buf_lower_addr_match_u_t;
/*
* PCI Buffer (x) Flush Count with Data Touch Register
*
* This counter is incremented each time the corresponding response buffer
* is flushed after at least a single data element in the buffer is used. A word
* write to this address clears the count.
*/
typedef union pic_flush_w_touch_u {
picreg_t pic_flush_w_touch_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t touch_cnt : 16; /* 15:0 */
} pic_flush_w_touch_fld_s;
} pic_flush_w_touch_u_t;
/*
* PCI Buffer (x) Flush Count w/o Data Touch Register
*
* This counter is incremented each time the corresponding response buffer
* is flushed without any data element in the buffer being used. A word
* write to this address clears the count.
*/
typedef union pic_flush_wo_touch_u {
picreg_t pic_flush_wo_touch_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t notouch_cnt : 16; /* 15:0 */
} pic_flush_wo_touch_fld_s;
} pic_flush_wo_touch_u_t;
/*
* PCI Buffer (x) Request in Flight Count Register
*
* This counter is incremented on each bus clock while the request is in-
* flight. A word write to this address clears the count. ]
*/
typedef union pic_inflight_u {
picreg_t pic_inflight_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t inflight_cnt : 16; /* 15:0 */
} pic_inflight_fld_s;
} pic_inflight_u_t;
/*
* PCI Buffer (x) Prefetch Request Count Register
*
* This counter is incremented each time the request using this buffer was
* generated from the prefetcher. A word write to this address clears the
* count.
*/
typedef union pic_prefetch_u {
picreg_t pic_prefetch_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t prefetch_cnt : 16; /* 15:0 */
} pic_prefetch_fld_s;
} pic_prefetch_u_t;
/*
* PCI Buffer (x) Total PCI Retry Count Register
*
* This counter is incremented each time a PCI bus retry occurs and the ad-
* dress matches the tag for the selected buffer. The buffer must also has this
* request in-flight. A word write to this address clears the count.
*/
typedef union pic_total_pci_retry_u {
picreg_t pic_total_pci_retry_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t retry_cnt : 16; /* 15:0 */
} pic_total_pci_retry_fld_s;
} pic_total_pci_retry_u_t;
/*
* PCI Buffer (x) Max PCI Retry Count Register
*
* This counter is contains the maximum retry count for a single request
* which was in-flight for this buffer. A word write to this address clears the
* count.
*/
typedef union pic_max_pci_retry_u {
picreg_t pic_max_pci_retry_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t max_retry_cnt : 16; /* 15:0 */
} pic_max_pci_retry_fld_s;
} pic_max_pci_retry_u_t;
/*
* PCI Buffer (x) Max Latency Count Register
*
* This counter is contains the maximum count (in bus clocks) for a single
* request which was in-flight for this buffer. A word write to this address
* clears the count.
*/
typedef union pic_max_latency_u {
picreg_t pic_max_latency_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t : 16; /* 31:16 */
picreg_t max_latency_cnt : 16; /* 15:0 */
} pic_max_latency_fld_s;
} pic_max_latency_u_t;
/*
* PCI Buffer (x) Clear All Register
*
* Any access to this register clears all the count values for the (x) registers.
*/
/*
* PCI-X Registers
*
* This register contains the address in the read buffer structure. There are
* 16 read buffer structures.
*/
typedef union pic_rd_buf_addr_u {
picreg_t pic_rd_buf_addr_regval;
struct {
picreg_t pcix_err_addr : 64; /* 63:0 */
} pic_rd_buf_addr_fld_s;
} pic_rd_buf_addr_u_t;
/*
* PCI-X Read Buffer (x) Attribute Register
*
* This register contains the attribute data in the read buffer structure. There
* are 16 read buffer structures.
*/
typedef union pic_px_read_buf_attr_u {
picreg_t pic_px_read_buf_attr_regval;
struct {
picreg_t : 16; /* 63:48 */
picreg_t bus_cmd : 4; /* 47:44 */
picreg_t byte_cnt : 12; /* 43:32 */
picreg_t entry_valid : 1; /* 31:31 */
picreg_t ns : 1; /* 30:30 */
picreg_t ro : 1; /* 29:29 */
picreg_t tag : 5; /* 28:24 */
picreg_t bus_num : 8; /* 23:16 */
picreg_t dev_num : 5; /* 15:11 */
picreg_t fun_num : 3; /* 10:8 */
picreg_t : 2; /* 7:6 */
picreg_t f_buffer_index : 6; /* 5:0 */
} pic_px_read_buf_attr_fld_s;
} pic_px_read_buf_attr_u_t;
/*
* PCI-X Write Buffer (x) Address Register
*
* This register contains the address in the write buffer structure. There are
* 8 write buffer structures.
*/
typedef union pic_px_write_buf_addr_u {
picreg_t pic_px_write_buf_addr_regval;
struct {
picreg_t pcix_err_addr : 64; /* 63:0 */
} pic_px_write_buf_addr_fld_s;
} pic_px_write_buf_addr_u_t;
/*
* PCI-X Write Buffer (x) Attribute Register
*
* This register contains the attribute data in the write buffer structure.
* There are 8 write buffer structures.
*/
typedef union pic_px_write_buf_attr_u {
picreg_t pic_px_write_buf_attr_regval;
struct {
picreg_t : 16; /* 63:48 */
picreg_t bus_cmd : 4; /* 47:44 */
picreg_t byte_cnt : 12; /* 43:32 */
picreg_t entry_valid : 1; /* 31:31 */
picreg_t ns : 1; /* 30:30 */
picreg_t ro : 1; /* 29:29 */
picreg_t tag : 5; /* 28:24 */
picreg_t bus_num : 8; /* 23:16 */
picreg_t dev_num : 5; /* 15:11 */
picreg_t fun_num : 3; /* 10:8 */
picreg_t : 2; /* 7:6 */
picreg_t f_buffer_index : 6; /* 5:0 */
} pic_px_write_buf_attr_fld_s;
} pic_px_write_buf_attr_u_t;
/*
* PCI-X Write Buffer (x) Valid Register
*
* This register contains the valid or inuse cache lines for this buffer struc-
* ture.
*/
typedef union pic_px_write_buf_valid_u {
picreg_t pic_px_write_buf_valid_regval;
struct {
picreg_t : 32; /* 63:32 */
picreg_t wrt_valid_buff : 32; /* 31:0 */
} pic_px_write_buf_valid_fld_s;
} pic_px_write_buf_valid_u_t;
#endif /* __ASSEMBLY__ */
#endif /* _ASM_SN_PCI_PIC_H */
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
*/
#ifndef _ASM_IA64_SN_RW_MMR_H
#define _ASM_IA64_SN_RW_MMR_H
/*
* This file contains macros used to access MMR registers via
* uncached physical addresses.
* pio_phys_read_mmr - read an MMR
* pio_phys_write_mmr - write an MMR
* pio_atomic_phys_write_mmrs - atomically write 2 MMRs with psr.ic=0
* (interrupt collection)
*
* Addresses passed to these routines should be uncached physical addresses
* ie., 0x80000....
*/
extern inline long
pio_phys_read_mmr(volatile long *mmr)
{
long val;
asm volatile
("mov r2=psr;;"
"rsm psr.i | psr.dt;;"
"srlz.i;;"
"ld8.acq %0=[%1];;"
"mov psr.l=r2;;"
"srlz.i;;"
: "=r"(val)
: "r"(mmr)
: "r2");
return val;
}
extern inline void
pio_phys_write_mmr(volatile long *mmr, long val)
{
asm volatile
("mov r2=psr;;"
"rsm psr.i | psr.dt;;"
"srlz.i;;"
"st8.rel [%0]=%1;;"
"mov psr.l=r2;;"
"srlz.i;;"
:: "r"(mmr), "r"(val)
: "r2", "memory");
}
extern inline void
pio_atomic_phys_write_mmrs(volatile long *mmr1, long val1, volatile long *mmr2, long val2)
{
asm volatile
("mov r2=psr;;"
"rsm psr.i | psr.dt | psr.ic;;"
"srlz.i;;"
"st8.rel [%0]=%1;"
"st8.rel [%2]=%3;;"
"mov psr.l=r2;;"
"srlz.i;;"
:: "r"(mmr1), "r"(val1), "r"(mmr2), "r"(val2)
: "r2", "memory");
}
#endif /* _ASM_IA64_SN_RW_MMR_H */
/* $Id$
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
*/
#ifndef __SYS_SN_SN2_GEO_H__
#define __SYS_SN_SN2_GEO_H__
/* Headers required by declarations in this file */
#include <asm/sn/slotnum.h>
/* The geoid_t implementation below is based loosely on the pcfg_t
implementation in sys/SN/promcfg.h. */
/* Type declaractions */
/* Size of a geoid_t structure (must be before decl. of geoid_u) */
#define GEOID_SIZE 8 /* Would 16 be better? The size can
be different on different platforms. */
#define MAX_SLABS 0xe /* slabs per module */
typedef unsigned char geo_type_t;
/* Fields common to all substructures */
typedef struct geo_any_s {
moduleid_t module; /* The module (box) this h/w lives in */
geo_type_t type; /* What type of h/w is named by this geoid_t */
slabid_t slab; /* The logical assembly within the module */
} geo_any_t;
/* Additional fields for particular types of hardware */
typedef struct geo_node_s {
geo_any_t any; /* No additional fields needed */
} geo_node_t;
typedef struct geo_rtr_s {
geo_any_t any; /* No additional fields needed */
} geo_rtr_t;
typedef struct geo_iocntl_s {
geo_any_t any; /* No additional fields needed */
} geo_iocntl_t;
typedef struct geo_pcicard_s {
geo_iocntl_t any;
char bus; /* Bus/widget number */
slotid_t slot; /* PCI slot number */
} geo_pcicard_t;
/* Subcomponents of a node */
typedef struct geo_cpu_s {
geo_node_t node;
char slice; /* Which CPU on the node */
} geo_cpu_t;
typedef struct geo_mem_s {
geo_node_t node;
char membus; /* The memory bus on the node */
char memslot; /* The memory slot on the bus */
} geo_mem_t;
typedef union geoid_u {
geo_any_t any;
geo_node_t node;
geo_iocntl_t iocntl;
geo_pcicard_t pcicard;
geo_rtr_t rtr;
geo_cpu_t cpu;
geo_mem_t mem;
char padsize[GEOID_SIZE];
} geoid_t;
/* Preprocessor macros */
#define GEO_MAX_LEN 48 /* max. formatted length, plus some pad:
module/001c07/slab/5/node/memory/2/slot/4 */
/* Values for geo_type_t */
#define GEO_TYPE_INVALID 0
#define GEO_TYPE_MODULE 1
#define GEO_TYPE_NODE 2
#define GEO_TYPE_RTR 3
#define GEO_TYPE_IOCNTL 4
#define GEO_TYPE_IOCARD 5
#define GEO_TYPE_CPU 6
#define GEO_TYPE_MEM 7
#define GEO_TYPE_MAX (GEO_TYPE_MEM+1)
/* Parameter for hwcfg_format_geoid_compt() */
#define GEO_COMPT_MODULE 1
#define GEO_COMPT_SLAB 2
#define GEO_COMPT_IOBUS 3
#define GEO_COMPT_IOSLOT 4
#define GEO_COMPT_CPU 5
#define GEO_COMPT_MEMBUS 6
#define GEO_COMPT_MEMSLOT 7
#define GEO_INVALID_STR "<invalid>"
#endif /* __SYS_SN_SN2_GEO_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment