patch-2.4.19 linux-2.4.19/arch/ia64/sn/io/pcibr.c
Next file: linux-2.4.19/arch/ia64/sn/io/pciio.c
Previous file: linux-2.4.19/arch/ia64/sn/io/pciba.c
Back to the patch index
Back to the overall index
- Lines: 9825
- Date:
Wed Dec 31 16:00:00 1969
- Orig file:
linux-2.4.18/arch/ia64/sn/io/pcibr.c
- Orig date:
Thu Apr 12 12:16:35 2001
diff -urN linux-2.4.18/arch/ia64/sn/io/pcibr.c linux-2.4.19/arch/ia64/sn/io/pcibr.c
@@ -1,9824 +0,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 Silicon Graphics, Inc.
- * Copyright (C) 2000 by Colin Ngam
- */
-
-#ifdef BRINGUP
-int NeedXbridgeSwap = 0;
-#endif
-
-#include <linux/types.h>
-#include <linux/config.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/sn/sgi.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/ioerror_handling.h>
-#include <asm/sn/xtalk/xbow.h>
-#include <asm/sn/ioc3.h>
-#include <asm/sn/eeprom.h>
-#include <asm/sn/sn1/bedrock.h>
-#include <asm/sn/sn_private.h>
-#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
-#include <asm/sn/sn1/hubio.h>
-#include <asm/sn/sn1/hubio_next.h>
-#endif
-
-#ifdef __ia64
-#define rmallocmap atemapalloc
-#define rmfreemap atemapfree
-#define rmfree atefree
-#define rmalloc atealloc
-#endif
-
-#undef PCIBR_ATE_DEBUG
-#if defined(BRINGUP)
-#if 0
-#define DEBUG 1 /* To avoid lots of bad printk() formats leave off */
-#endif
-#define PCI_DEBUG 1
-#define ATTACH_DEBUG 1
-#define PCIBR_SOFT_LIST 1
-#endif
-
-#ifndef LOCAL
-#define LOCAL static
-#endif
-
-/*
- * Macros related to the Lucent USS 302/312 usb timeout workaround. It
- * appears that if the lucent part can get into a retry loop if it sees a
- * DAC on the bus during a pio read retry. The loop is broken after about
- * 1ms, so we need to set up bridges holding this part to allow at least
- * 1ms for pio.
- */
-
-#define USS302_TIMEOUT_WAR
-
-#ifdef USS302_TIMEOUT_WAR
-#include <asm/sn/io.h>
-#define LUCENT_USBHC_VENDOR_ID_NUM 0x11c1
-#define LUCENT_USBHC302_DEVICE_ID_NUM 0x5801
-#define LUCENT_USBHC312_DEVICE_ID_NUM 0x5802
-#define USS302_BRIDGE_TIMEOUT_HLD 4
-#endif
-
-#define PCIBR_LLP_CONTROL_WAR
-#if defined (PCIBR_LLP_CONTROL_WAR)
-int pcibr_llp_control_war_cnt;
-#endif /* PCIBR_LLP_CONTROL_WAR */
-
-#define NEWAf(ptr,n,f) (ptr = kmem_zalloc((n)*sizeof (*(ptr)), (f&PCIIO_NOSLEEP)?KM_NOSLEEP:KM_SLEEP))
-#define NEWA(ptr,n) (ptr = kmem_zalloc((n)*sizeof (*(ptr)), KM_SLEEP))
-#define DELA(ptr,n) (kfree(ptr))
-
-#define NEWf(ptr,f) NEWAf(ptr,1,f)
-#define NEW(ptr) NEWA(ptr,1)
-#define DEL(ptr) DELA(ptr,1)
-
-int pcibr_devflag = D_MP;
-
-#ifdef LATER
-#define F(s,n) { 1l<<(s),-(s), n }
-
-struct reg_desc bridge_int_status_desc[] =
-{
- F(31, "MULTI_ERR"),
- F(30, "PMU_ESIZE_EFAULT"),
- F(29, "UNEXPECTED_RESP"),
- F(28, "BAD_XRESP_PACKET"),
- F(27, "BAD_XREQ_PACKET"),
- F(26, "RESP_XTALK_ERROR"),
- F(25, "REQ_XTALK_ERROR"),
- F(24, "INVALID_ADDRESS"),
- F(23, "UNSUPPORTED_XOP"),
- F(22, "XREQ_FIFO_OFLOW"),
- F(21, "LLP_REC_SNERROR"),
- F(20, "LLP_REC_CBERROR"),
- F(19, "LLP_RCTY"),
- F(18, "LLP_TX_RETRY"),
- F(17, "LLP_TCTY"),
- F(16, "SSRAM_PERR"),
- F(15, "PCI_ABORT"),
- F(14, "PCI_PARITY"),
- F(13, "PCI_SERR"),
- F(12, "PCI_PERR"),
- F(11, "PCI_MASTER_TOUT"),
- F(10, "PCI_RETRY_CNT"),
- F(9, "XREAD_REQ_TOUT"),
- F(8, "GIO_BENABLE_ERR"),
- F(7, "INT7"),
- F(6, "INT6"),
- F(5, "INT5"),
- F(4, "INT4"),
- F(3, "INT3"),
- F(2, "INT2"),
- F(1, "INT1"),
- F(0, "INT0"),
- {0}
-};
-
-struct reg_values space_v[] =
-{
- {PCIIO_SPACE_NONE, "none"},
- {PCIIO_SPACE_ROM, "ROM"},
- {PCIIO_SPACE_IO, "I/O"},
- {PCIIO_SPACE_MEM, "MEM"},
- {PCIIO_SPACE_MEM32, "MEM(32)"},
- {PCIIO_SPACE_MEM64, "MEM(64)"},
- {PCIIO_SPACE_CFG, "CFG"},
- {PCIIO_SPACE_WIN(0), "WIN(0)"},
- {PCIIO_SPACE_WIN(1), "WIN(1)"},
- {PCIIO_SPACE_WIN(2), "WIN(2)"},
- {PCIIO_SPACE_WIN(3), "WIN(3)"},
- {PCIIO_SPACE_WIN(4), "WIN(4)"},
- {PCIIO_SPACE_WIN(5), "WIN(5)"},
- {PCIIO_SPACE_BAD, "BAD"},
- {0}
-};
-
-struct reg_desc space_desc[] =
-{
- {0xFF, 0, "space", 0, space_v},
- {0}
-};
-
-#if DEBUG
-#define device_desc device_bits
-LOCAL struct reg_desc device_bits[] =
-{
- {BRIDGE_DEV_ERR_LOCK_EN, 0, "ERR_LOCK_EN"},
- {BRIDGE_DEV_PAGE_CHK_DIS, 0, "PAGE_CHK_DIS"},
- {BRIDGE_DEV_FORCE_PCI_PAR, 0, "FORCE_PCI_PAR"},
- {BRIDGE_DEV_VIRTUAL_EN, 0, "VIRTUAL_EN"},
- {BRIDGE_DEV_PMU_WRGA_EN, 0, "PMU_WRGA_EN"},
- {BRIDGE_DEV_DIR_WRGA_EN, 0, "DIR_WRGA_EN"},
- {BRIDGE_DEV_DEV_SIZE, 0, "DEV_SIZE"},
- {BRIDGE_DEV_RT, 0, "RT"},
- {BRIDGE_DEV_SWAP_PMU, 0, "SWAP_PMU"},
- {BRIDGE_DEV_SWAP_DIR, 0, "SWAP_DIR"},
- {BRIDGE_DEV_PREF, 0, "PREF"},
- {BRIDGE_DEV_PRECISE, 0, "PRECISE"},
- {BRIDGE_DEV_COH, 0, "COH"},
- {BRIDGE_DEV_BARRIER, 0, "BARRIER"},
- {BRIDGE_DEV_GBR, 0, "GBR"},
- {BRIDGE_DEV_DEV_SWAP, 0, "DEV_SWAP"},
- {BRIDGE_DEV_DEV_IO_MEM, 0, "DEV_IO_MEM"},
- {BRIDGE_DEV_OFF_MASK, BRIDGE_DEV_OFF_ADDR_SHFT, "DEV_OFF", "%x"},
- {0}
-};
-#endif /* DEBUG */
-
-#ifdef SUPPORT_PRINTING_R_FORMAT
-LOCAL struct reg_values xio_cmd_pactyp[] =
-{
- {0x0, "RdReq"},
- {0x1, "RdResp"},
- {0x2, "WrReqWithResp"},
- {0x3, "WrResp"},
- {0x4, "WrReqNoResp"},
- {0x5, "Reserved(5)"},
- {0x6, "FetchAndOp"},
- {0x7, "Reserved(7)"},
- {0x8, "StoreAndOp"},
- {0x9, "Reserved(9)"},
- {0xa, "Reserved(a)"},
- {0xb, "Reserved(b)"},
- {0xc, "Reserved(c)"},
- {0xd, "Reserved(d)"},
- {0xe, "SpecialReq"},
- {0xf, "SpecialResp"},
- {0}
-};
-
-LOCAL struct reg_desc xio_cmd_bits[] =
-{
- {WIDGET_DIDN, -28, "DIDN", "%x"},
- {WIDGET_SIDN, -24, "SIDN", "%x"},
- {WIDGET_PACTYP, -20, "PACTYP", 0, xio_cmd_pactyp},
- {WIDGET_TNUM, -15, "TNUM", "%x"},
- {WIDGET_COHERENT, 0, "COHERENT"},
- {WIDGET_DS, 0, "DS"},
- {WIDGET_GBR, 0, "GBR"},
- {WIDGET_VBPM, 0, "VBPM"},
- {WIDGET_ERROR, 0, "ERROR"},
- {WIDGET_BARRIER, 0, "BARRIER"},
- {0}
-};
-#endif /* SUPPORT_PRINTING_R_FORMAT */
-
-#if PCIBR_FREEZE_TIME || PCIBR_ATE_DEBUG
-LOCAL struct reg_desc ate_bits[] =
-{
- {0xFFFF000000000000ull, -48, "RMF", "%x"},
- {~(IOPGSIZE - 1) & /* may trim off some low bits */
- 0x0000FFFFFFFFF000ull, 0, "XIO", "%x"},
- {0x0000000000000F00ull, -8, "port", "%x"},
- {0x0000000000000010ull, 0, "Barrier"},
- {0x0000000000000008ull, 0, "Prefetch"},
- {0x0000000000000004ull, 0, "Precise"},
- {0x0000000000000002ull, 0, "Coherent"},
- {0x0000000000000001ull, 0, "Valid"},
- {0}
-};
-#endif
-
-#if PCIBR_ATE_DEBUG
-LOCAL struct reg_values ssram_sizes[] =
-{
- {BRIDGE_CTRL_SSRAM_512K, "512k"},
- {BRIDGE_CTRL_SSRAM_128K, "128k"},
- {BRIDGE_CTRL_SSRAM_64K, "64k"},
- {BRIDGE_CTRL_SSRAM_1K, "1k"},
- {0}
-};
-
-LOCAL struct reg_desc control_bits[] =
-{
- {BRIDGE_CTRL_FLASH_WR_EN, 0, "FLASH_WR_EN"},
- {BRIDGE_CTRL_EN_CLK50, 0, "EN_CLK50"},
- {BRIDGE_CTRL_EN_CLK40, 0, "EN_CLK40"},
- {BRIDGE_CTRL_EN_CLK33, 0, "EN_CLK33"},
- {BRIDGE_CTRL_RST_MASK, -24, "RST", "%x"},
- {BRIDGE_CTRL_IO_SWAP, 0, "IO_SWAP"},
- {BRIDGE_CTRL_MEM_SWAP, 0, "MEM_SWAP"},
- {BRIDGE_CTRL_PAGE_SIZE, 0, "PAGE_SIZE"},
- {BRIDGE_CTRL_SS_PAR_BAD, 0, "SS_PAR_BAD"},
- {BRIDGE_CTRL_SS_PAR_EN, 0, "SS_PAR_EN"},
- {BRIDGE_CTRL_SSRAM_SIZE_MASK, 0, "SSRAM_SIZE", 0, ssram_sizes},
- {BRIDGE_CTRL_F_BAD_PKT, 0, "F_BAD_PKT"},
- {BRIDGE_CTRL_LLP_XBAR_CRD_MASK, -12, "LLP_XBAR_CRD", "%d"},
- {BRIDGE_CTRL_CLR_RLLP_CNT, 0, "CLR_RLLP_CNT"},
- {BRIDGE_CTRL_CLR_TLLP_CNT, 0, "CLR_TLLP_CNT"},
- {BRIDGE_CTRL_SYS_END, 0, "SYS_END"},
- {BRIDGE_CTRL_MAX_TRANS_MASK, -4, "MAX_TRANS", "%d"},
- {BRIDGE_CTRL_WIDGET_ID_MASK, 0, "WIDGET_ID", "%x"},
- {0}
-};
-#endif
-#endif /* LATER */
-
-/* kbrick widgetnum-to-bus layout */
-int p_busnum[MAX_PORT_NUM] = { /* widget# */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
- 2, /* 0x8 */
- 1, /* 0x9 */
- 0, 0, /* 0xa - 0xb */
- 5, /* 0xc */
- 6, /* 0xd */
- 4, /* 0xe */
- 3, /* 0xf */
-};
-
-/*
- * Additional PIO spaces per slot are
- * recorded in this structure.
- */
-struct pciio_piospace_s {
- pciio_piospace_t next; /* another space for this device */
- char free; /* 1 if free, 0 if in use */
- pciio_space_t space; /* Which space is in use */
- iopaddr_t start; /* Starting address of the PIO space */
- size_t count; /* size of PIO space */
-};
-
-/* Use io spin locks. This ensures that all the PIO writes from a particular
- * CPU to a particular IO device are synched before the start of the next
- * set of PIO operations to the same device.
- */
-#define pcibr_lock(pcibr_soft) io_splock(&pcibr_soft->bs_lock)
-#define pcibr_unlock(pcibr_soft,s) io_spunlock(&pcibr_soft->bs_lock,s)
-
-#if PCIBR_SOFT_LIST
-typedef struct pcibr_list_s *pcibr_list_p;
-struct pcibr_list_s {
- pcibr_list_p bl_next;
- pcibr_soft_t bl_soft;
- devfs_handle_t bl_vhdl;
-};
-pcibr_list_p pcibr_list = 0;
-#endif
-
-typedef volatile unsigned *cfg_p;
-typedef volatile bridgereg_t *reg_p;
-
-#define INFO_LBL_PCIBR_ASIC_REV "_pcibr_asic_rev"
-
-#define PCIBR_D64_BASE_UNSET (0xFFFFFFFFFFFFFFFF)
-#define PCIBR_D32_BASE_UNSET (0xFFFFFFFF)
-
-#define PCIBR_VALID_SLOT(s) (s < 8)
-
-#ifdef SN_XXX
-extern int hub_device_flags_set(devfs_handle_t widget_dev,
- hub_widget_flags_t flags);
-#endif
-extern pciio_dmamap_t get_free_pciio_dmamap(devfs_handle_t);
-
-/*
- * This is the file operation table for the pcibr driver.
- * As each of the functions are implemented, put the
- * appropriate function name below.
- */
-struct file_operations pcibr_fops = {
- owner: THIS_MODULE,
- llseek: NULL,
- read: NULL,
- write: NULL,
- readdir: NULL,
- poll: NULL,
- ioctl: NULL,
- mmap: NULL,
- open: NULL,
- flush: NULL,
- release: NULL,
- fsync: NULL,
- fasync: NULL,
- lock: NULL,
- readv: NULL,
- writev: NULL
-};
-
-extern devfs_handle_t hwgraph_root;
-extern graph_error_t hwgraph_vertex_unref(devfs_handle_t vhdl);
-extern int cap_able(uint64_t x);
-extern uint64_t rmalloc(struct map *mp, size_t size);
-extern void rmfree(struct map *mp, size_t size, uint64_t a);
-extern int hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen);
-extern long atoi(register char *p);
-extern void *swap_ptr(void **loc, void *new);
-extern char *dev_to_name(devfs_handle_t dev, char *buf, uint buflen);
-extern cnodeid_t nodevertex_to_cnodeid(devfs_handle_t vhdl);
-extern graph_error_t hwgraph_edge_remove(devfs_handle_t from, char *name, devfs_handle_t *toptr);
-extern struct map *rmallocmap(uint64_t mapsiz);
-extern void rmfreemap(struct map *mp);
-extern int compare_and_swap_ptr(void **location, void *old_ptr, void *new_ptr);
-extern int io_path_map_widget(devfs_handle_t vertex);
-
-
-
-/* =====================================================================
- * Function Table of Contents
- *
- * The order of functions in this file has stopped
- * making much sense. We might want to take a look
- * at it some time and bring back some sanity, or
- * perhaps bust this file into smaller chunks.
- */
-
-LOCAL void do_pcibr_rrb_clear(bridge_t *, int);
-LOCAL void do_pcibr_rrb_flush(bridge_t *, int);
-LOCAL int do_pcibr_rrb_count_valid(bridge_t *, pciio_slot_t);
-LOCAL int do_pcibr_rrb_count_avail(bridge_t *, pciio_slot_t);
-LOCAL int do_pcibr_rrb_alloc(bridge_t *, pciio_slot_t, int);
-LOCAL int do_pcibr_rrb_free(bridge_t *, pciio_slot_t, int);
-
-LOCAL void do_pcibr_rrb_autoalloc(pcibr_soft_t, int, int);
-
-int pcibr_wrb_flush(devfs_handle_t);
-int pcibr_rrb_alloc(devfs_handle_t, int *, int *);
-int pcibr_rrb_check(devfs_handle_t, int *, int *, int *, int *);
-int pcibr_alloc_all_rrbs(devfs_handle_t, int, int, int, int, int, int, int, int, int);
-void pcibr_rrb_flush(devfs_handle_t);
-
-LOCAL int pcibr_try_set_device(pcibr_soft_t, pciio_slot_t, unsigned, bridgereg_t);
-void pcibr_release_device(pcibr_soft_t, pciio_slot_t, bridgereg_t);
-
-LOCAL void pcibr_clearwidint(bridge_t *);
-LOCAL void pcibr_setwidint(xtalk_intr_t);
-LOCAL int pcibr_probe_slot(bridge_t *, cfg_p, unsigned *);
-
-void pcibr_init(void);
-int pcibr_attach(devfs_handle_t);
-int pcibr_detach(devfs_handle_t);
-int pcibr_open(devfs_handle_t *, int, int, cred_t *);
-int pcibr_close(devfs_handle_t, int, int, cred_t *);
-int pcibr_map(devfs_handle_t, vhandl_t *, off_t, size_t, uint);
-int pcibr_unmap(devfs_handle_t, vhandl_t *);
-int pcibr_ioctl(devfs_handle_t, int, void *, int, struct cred *, int *);
-
-void pcibr_freeblock_sub(iopaddr_t *, iopaddr_t *, iopaddr_t, size_t);
-
-LOCAL int pcibr_init_ext_ate_ram(bridge_t *);
-LOCAL int pcibr_ate_alloc(pcibr_soft_t, int);
-LOCAL void pcibr_ate_free(pcibr_soft_t, int, int);
-
-LOCAL pcibr_info_t pcibr_info_get(devfs_handle_t);
-LOCAL pcibr_info_t pcibr_device_info_new(pcibr_soft_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
-LOCAL void pcibr_device_info_free(devfs_handle_t, pciio_slot_t);
-LOCAL iopaddr_t pcibr_addr_pci_to_xio(devfs_handle_t, pciio_slot_t, pciio_space_t, iopaddr_t, size_t, unsigned);
-
-pcibr_piomap_t pcibr_piomap_alloc(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, size_t, unsigned);
-void pcibr_piomap_free(pcibr_piomap_t);
-caddr_t pcibr_piomap_addr(pcibr_piomap_t, iopaddr_t, size_t);
-void pcibr_piomap_done(pcibr_piomap_t);
-caddr_t pcibr_piotrans_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, unsigned);
-iopaddr_t pcibr_piospace_alloc(devfs_handle_t, device_desc_t, pciio_space_t, size_t, size_t);
-void pcibr_piospace_free(devfs_handle_t, pciio_space_t, iopaddr_t, size_t);
-
-LOCAL iopaddr_t pcibr_flags_to_d64(unsigned, pcibr_soft_t);
-LOCAL bridge_ate_t pcibr_flags_to_ate(unsigned);
-
-pcibr_dmamap_t pcibr_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
-void pcibr_dmamap_free(pcibr_dmamap_t);
-LOCAL bridge_ate_p pcibr_ate_addr(pcibr_soft_t, int);
-LOCAL iopaddr_t pcibr_addr_xio_to_pci(pcibr_soft_t, iopaddr_t, size_t);
-iopaddr_t pcibr_dmamap_addr(pcibr_dmamap_t, paddr_t, size_t);
-alenlist_t pcibr_dmamap_list(pcibr_dmamap_t, alenlist_t, unsigned);
-void pcibr_dmamap_done(pcibr_dmamap_t);
-cnodeid_t pcibr_get_dmatrans_node(devfs_handle_t);
-iopaddr_t pcibr_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
-alenlist_t pcibr_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
-void pcibr_dmamap_drain(pcibr_dmamap_t);
-void pcibr_dmaaddr_drain(devfs_handle_t, paddr_t, size_t);
-void pcibr_dmalist_drain(devfs_handle_t, alenlist_t);
-iopaddr_t pcibr_dmamap_pciaddr_get(pcibr_dmamap_t);
-
-static unsigned pcibr_intr_bits(pciio_info_t info, pciio_intr_line_t lines);
-pcibr_intr_t pcibr_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);
-void pcibr_intr_free(pcibr_intr_t);
-LOCAL void pcibr_setpciint(xtalk_intr_t);
-int pcibr_intr_connect(pcibr_intr_t, intr_func_t, intr_arg_t, void *);
-void pcibr_intr_disconnect(pcibr_intr_t);
-
-devfs_handle_t pcibr_intr_cpu_get(pcibr_intr_t);
-void pcibr_xintr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);
-void pcibr_intr_func(intr_arg_t);
-
-LOCAL void print_bridge_errcmd(uint32_t, char *);
-
-void pcibr_error_dump(pcibr_soft_t);
-uint32_t pcibr_errintr_group(uint32_t);
-LOCAL void pcibr_pioerr_check(pcibr_soft_t);
-LOCAL void pcibr_error_intr_handler(intr_arg_t);
-
-LOCAL int pcibr_addr_toslot(pcibr_soft_t, iopaddr_t, pciio_space_t *, iopaddr_t *, pciio_function_t *);
-LOCAL void pcibr_error_cleanup(pcibr_soft_t, int);
-void pcibr_device_disable(pcibr_soft_t, int);
-LOCAL int pcibr_pioerror(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
-int pcibr_dmard_error(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
-int pcibr_dmawr_error(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
-LOCAL int pcibr_error_handler(error_handler_arg_t, int, ioerror_mode_t, ioerror_t *);
-int pcibr_error_devenable(devfs_handle_t, int);
-
-void pcibr_provider_startup(devfs_handle_t);
-void pcibr_provider_shutdown(devfs_handle_t);
-
-int pcibr_reset(devfs_handle_t);
-pciio_endian_t pcibr_endian_set(devfs_handle_t, pciio_endian_t, pciio_endian_t);
-int pcibr_priority_bits_set(pcibr_soft_t, pciio_slot_t, pciio_priority_t);
-pciio_priority_t pcibr_priority_set(devfs_handle_t, pciio_priority_t);
-int pcibr_device_flags_set(devfs_handle_t, pcibr_device_flags_t);
-
-LOCAL cfg_p pcibr_config_addr(devfs_handle_t, unsigned);
-uint64_t pcibr_config_get(devfs_handle_t, unsigned, unsigned);
-LOCAL uint64_t do_pcibr_config_get(cfg_p, unsigned, unsigned);
-void pcibr_config_set(devfs_handle_t, unsigned, unsigned, uint64_t);
-LOCAL void do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t);
-
-LOCAL pcibr_hints_t pcibr_hints_get(devfs_handle_t, int);
-void pcibr_hints_fix_rrbs(devfs_handle_t);
-void pcibr_hints_dualslot(devfs_handle_t, pciio_slot_t, pciio_slot_t);
-void pcibr_hints_intr_bits(devfs_handle_t, pcibr_intr_bits_f *);
-void pcibr_set_rrb_callback(devfs_handle_t, rrb_alloc_funct_t);
-void pcibr_hints_handsoff(devfs_handle_t);
-void pcibr_hints_subdevs(devfs_handle_t, pciio_slot_t, ulong);
-
-LOCAL int pcibr_slot_info_init(devfs_handle_t,pciio_slot_t);
-LOCAL int pcibr_slot_info_free(devfs_handle_t,pciio_slot_t);
-
-#ifdef LATER
-LOCAL int pcibr_slot_info_return(pcibr_soft_t, pciio_slot_t,
- pcibr_slot_info_resp_t);
-LOCAL void pcibr_slot_func_info_return(pcibr_info_h, int,
- pcibr_slot_func_info_resp_t);
-#endif /* LATER */
-
-LOCAL int pcibr_slot_addr_space_init(devfs_handle_t,pciio_slot_t);
-LOCAL int pcibr_slot_device_init(devfs_handle_t, pciio_slot_t);
-LOCAL int pcibr_slot_guest_info_init(devfs_handle_t,pciio_slot_t);
-LOCAL int pcibr_slot_initial_rrb_alloc(devfs_handle_t,pciio_slot_t);
-LOCAL int pcibr_slot_call_device_attach(devfs_handle_t,
- pciio_slot_t, int);
-LOCAL int pcibr_slot_call_device_detach(devfs_handle_t,
- pciio_slot_t, int);
-
-LOCAL int pcibr_slot_detach(devfs_handle_t, pciio_slot_t, int);
-LOCAL int pcibr_is_slot_sys_critical(devfs_handle_t, pciio_slot_t);
-#ifdef LATER
-LOCAL int pcibr_slot_query(devfs_handle_t, pcibr_slot_info_req_t);
-#endif
-
-/* =====================================================================
- * RRB management
- */
-
-#define LSBIT(word) ((word) &~ ((word)-1))
-
-#define PCIBR_RRB_SLOT_VIRTUAL 8
-
-LOCAL void
-do_pcibr_rrb_clear(bridge_t *bridge, int rrb)
-{
- bridgereg_t status;
-
- /* bridge_lock must be held;
- * this RRB must be disabled.
- */
-
- /* wait until RRB has no outstanduing XIO packets. */
- while ((status = bridge->b_resp_status) & BRIDGE_RRB_INUSE(rrb)) {
- ; /* XXX- beats on bridge. bad idea? */
- }
-
- /* if the RRB has data, drain it. */
- if (status & BRIDGE_RRB_VALID(rrb)) {
- bridge->b_resp_clear = BRIDGE_RRB_CLEAR(rrb);
-
- /* wait until RRB is no longer valid. */
- while ((status = bridge->b_resp_status) & BRIDGE_RRB_VALID(rrb)) {
- ; /* XXX- beats on bridge. bad idea? */
- }
- }
-}
-
-LOCAL void
-do_pcibr_rrb_flush(bridge_t *bridge, int rrbn)
-{
- reg_p rrbp = &bridge->b_rrb_map[rrbn & 1].reg;
- bridgereg_t rrbv;
- int shft = 4 * (rrbn >> 1);
- unsigned ebit = BRIDGE_RRB_EN << shft;
-
- rrbv = *rrbp;
- if (rrbv & ebit)
- *rrbp = rrbv & ~ebit;
-
- do_pcibr_rrb_clear(bridge, rrbn);
-
- if (rrbv & ebit)
- *rrbp = rrbv;
-}
-
-/*
- * pcibr_rrb_count_valid: count how many RRBs are
- * marked valid for the specified PCI slot on this
- * bridge.
- *
- * NOTE: The "slot" parameter for all pcibr_rrb
- * management routines must include the "virtual"
- * bit; when manageing both the normal and the
- * virtual channel, separate calls to these
- * routines must be made. To denote the virtual
- * channel, add PCIBR_RRB_SLOT_VIRTUAL to the slot
- * number.
- *
- * IMPL NOTE: The obvious algorithm is to iterate
- * through the RRB fields, incrementing a count if
- * the RRB is valid and matches the slot. However,
- * it is much simpler to use an algorithm derived
- * from the "partitioned add" idea. First, XOR in a
- * pattern such that the fields that match this
- * slot come up "all ones" and all other fields
- * have zeros in the mismatching bits. Then AND
- * together the bits in the field, so we end up
- * with one bit turned on for each field that
- * matched. Now we need to count these bits. This
- * can be done either with a series of shift/add
- * instructions or by using "tmp % 15"; I expect
- * that the cascaded shift/add will be faster.
- */
-
-LOCAL int
-do_pcibr_rrb_count_valid(bridge_t *bridge,
- pciio_slot_t slot)
-{
- bridgereg_t tmp;
-
- tmp = bridge->b_rrb_map[slot & 1].reg;
- tmp ^= 0x11111111 * (7 - slot / 2);
- tmp &= (0xCCCCCCCC & tmp) >> 2;
- tmp &= (0x22222222 & tmp) >> 1;
- tmp += tmp >> 4;
- tmp += tmp >> 8;
- tmp += tmp >> 16;
- return tmp & 15;
-}
-
-/*
- * do_pcibr_rrb_count_avail: count how many RRBs are
- * available to be allocated for the specified slot.
- *
- * IMPL NOTE: similar to the above, except we are
- * just counting how many fields have the valid bit
- * turned off.
- */
-LOCAL int
-do_pcibr_rrb_count_avail(bridge_t *bridge,
- pciio_slot_t slot)
-{
- bridgereg_t tmp;
-
- tmp = bridge->b_rrb_map[slot & 1].reg;
- tmp = (0x88888888 & ~tmp) >> 3;
- tmp += tmp >> 4;
- tmp += tmp >> 8;
- tmp += tmp >> 16;
- return tmp & 15;
-}
-
-/*
- * do_pcibr_rrb_alloc: allocate some additional RRBs
- * for the specified slot. Returns -1 if there were
- * insufficient free RRBs to satisfy the request,
- * or 0 if the request was fulfilled.
- *
- * Note that if a request can be partially filled,
- * it will be, even if we return failure.
- *
- * IMPL NOTE: again we avoid iterating across all
- * the RRBs; instead, we form up a word containing
- * one bit for each free RRB, then peel the bits
- * off from the low end.
- */
-LOCAL int
-do_pcibr_rrb_alloc(bridge_t *bridge,
- pciio_slot_t slot,
- int more)
-{
- int rv = 0;
- bridgereg_t reg, tmp, bit;
-
- reg = bridge->b_rrb_map[slot & 1].reg;
- tmp = (0x88888888 & ~reg) >> 3;
- while (more-- > 0) {
- bit = LSBIT(tmp);
- if (!bit) {
- rv = -1;
- break;
- }
- tmp &= ~bit;
- reg = ((reg & ~(bit * 15)) | (bit * (8 + slot / 2)));
- }
- bridge->b_rrb_map[slot & 1].reg = reg;
- return rv;
-}
-
-/*
- * do_pcibr_rrb_free: release some of the RRBs that
- * have been allocated for the specified
- * slot. Returns zero for success, or negative if
- * it was unable to free that many RRBs.
- *
- * IMPL NOTE: We form up a bit for each RRB
- * allocated to the slot, aligned with the VALID
- * bitfield this time; then we peel bits off one at
- * a time, releasing the corresponding RRB.
- */
-LOCAL int
-do_pcibr_rrb_free(bridge_t *bridge,
- pciio_slot_t slot,
- int less)
-{
- int rv = 0;
- bridgereg_t reg, tmp, clr, bit;
- int i;
-
- clr = 0;
- reg = bridge->b_rrb_map[slot & 1].reg;
-
- /* This needs to be done otherwise the rrb's on the virtual channel
- * for this slot won't be freed !!
- */
- tmp = reg & 0xbbbbbbbb;
-
- tmp ^= (0x11111111 * (7 - slot / 2));
- tmp &= (0x33333333 & tmp) << 2;
- tmp &= (0x44444444 & tmp) << 1;
- while (less-- > 0) {
- bit = LSBIT(tmp);
- if (!bit) {
- rv = -1;
- break;
- }
- tmp &= ~bit;
- reg &= ~bit;
- clr |= bit;
- }
- bridge->b_rrb_map[slot & 1].reg = reg;
-
- for (i = 0; i < 8; i++)
- if (clr & (8 << (4 * i)))
- do_pcibr_rrb_clear(bridge, (2 * i) + (slot & 1));
-
- return rv;
-}
-
-LOCAL void
-do_pcibr_rrb_autoalloc(pcibr_soft_t pcibr_soft,
- int slot,
- int more_rrbs)
-{
- bridge_t *bridge = pcibr_soft->bs_base;
- int got;
-
- for (got = 0; got < more_rrbs; ++got) {
- if (pcibr_soft->bs_rrb_res[slot & 7] > 0)
- pcibr_soft->bs_rrb_res[slot & 7]--;
- else if (pcibr_soft->bs_rrb_avail[slot & 1] > 0)
- pcibr_soft->bs_rrb_avail[slot & 1]--;
- else
- break;
- if (do_pcibr_rrb_alloc(bridge, slot, 1) < 0)
- break;
-#if PCIBR_RRB_DEBUG
- printk( "do_pcibr_rrb_autoalloc: add one to slot %d%s\n",
- slot & 7, slot & 8 ? "v" : "");
-#endif
- pcibr_soft->bs_rrb_valid[slot]++;
- }
-#if PCIBR_RRB_DEBUG
- printk("%s: %d+%d free RRBs. Allocation list:\n", pcibr_soft->bs_name,
- pcibr_soft->bs_rrb_avail[0],
- pcibr_soft->bs_rrb_avail[1]);
- for (slot = 0; slot < 8; ++slot)
- printk("\t%d+%d+%d",
- 0xFFF & pcibr_soft->bs_rrb_valid[slot],
- 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
- printk("\n");
-#endif
-}
-
-/*
- * Device driver interface to flush the write buffers for a specified
- * device hanging off the bridge.
- */
-int
-pcibr_wrb_flush(devfs_handle_t pconn_vhdl)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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;
- volatile bridgereg_t *wrb_flush;
-
- wrb_flush = &(bridge->b_wr_req_buf[pciio_slot].reg);
- while (*wrb_flush);
-
- return(0);
-}
-/*
- * Device driver interface to request RRBs for a specified device
- * hanging off a Bridge. The driver requests the total number of
- * RRBs it would like for the normal channel (vchan0) and for the
- * "virtual channel" (vchan1). The actual number allocated to each
- * channel is returned.
- *
- * If we cannot allocate at least one RRB to a channel that needs
- * at least one, return -1 (failure). Otherwise, satisfy the request
- * as best we can and return 0.
- */
-int
-pcibr_rrb_alloc(devfs_handle_t pconn_vhdl,
- int *count_vchan0,
- int *count_vchan1)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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;
- int desired_vchan0;
- int desired_vchan1;
- int orig_vchan0;
- int orig_vchan1;
- int delta_vchan0;
- int delta_vchan1;
- int final_vchan0;
- int final_vchan1;
- int avail_rrbs;
- unsigned long s;
- int error;
-
- /*
- * TBD: temper request with admin info about RRB allocation,
- * and according to demand from other devices on this Bridge.
- *
- * One way of doing this would be to allocate two RRBs
- * for each device on the bus, before any drivers start
- * asking for extras. This has the weakness that one
- * driver might not give back an "extra" RRB until after
- * another driver has already failed to get one that
- * it wanted.
- */
-
- s = pcibr_lock(pcibr_soft);
-
- /* How many RRBs do we own? */
- orig_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot];
- orig_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
-
- /* How many RRBs do we want? */
- desired_vchan0 = count_vchan0 ? *count_vchan0 : orig_vchan0;
- desired_vchan1 = count_vchan1 ? *count_vchan1 : orig_vchan1;
-
- /* How many RRBs are free? */
- avail_rrbs = pcibr_soft->bs_rrb_avail[pciio_slot & 1]
- + pcibr_soft->bs_rrb_res[pciio_slot];
-
- /* Figure desired deltas */
- delta_vchan0 = desired_vchan0 - orig_vchan0;
- delta_vchan1 = desired_vchan1 - orig_vchan1;
-
- /* Trim back deltas to something
- * that we can actually meet, by
- * decreasing the ending allocation
- * for whichever channel wants
- * more RRBs. If both want the same
- * number, cut the second channel.
- * NOTE: do not change the allocation for
- * a channel that was passed as NULL.
- */
- while ((delta_vchan0 + delta_vchan1) > avail_rrbs) {
- if (count_vchan0 &&
- (!count_vchan1 ||
- ((orig_vchan0 + delta_vchan0) >
- (orig_vchan1 + delta_vchan1))))
- delta_vchan0--;
- else
- delta_vchan1--;
- }
-
- /* Figure final RRB allocations
- */
- final_vchan0 = orig_vchan0 + delta_vchan0;
- final_vchan1 = orig_vchan1 + delta_vchan1;
-
- /* If either channel wants RRBs but our actions
- * would leave it with none, declare an error,
- * but DO NOT change any RRB allocations.
- */
- if ((desired_vchan0 && !final_vchan0) ||
- (desired_vchan1 && !final_vchan1)) {
-
- error = -1;
-
- } else {
-
- /* Commit the allocations: free, then alloc.
- */
- if (delta_vchan0 < 0)
- (void) do_pcibr_rrb_free(bridge, pciio_slot, -delta_vchan0);
- if (delta_vchan1 < 0)
- (void) do_pcibr_rrb_free(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, -delta_vchan1);
-
- if (delta_vchan0 > 0)
- (void) do_pcibr_rrb_alloc(bridge, pciio_slot, delta_vchan0);
- if (delta_vchan1 > 0)
- (void) do_pcibr_rrb_alloc(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, delta_vchan1);
-
- /* Return final values to caller.
- */
- if (count_vchan0)
- *count_vchan0 = final_vchan0;
- if (count_vchan1)
- *count_vchan1 = final_vchan1;
-
- /* prevent automatic changes to this slot's RRBs
- */
- pcibr_soft->bs_rrb_fixed |= 1 << pciio_slot;
-
- /* Track the actual allocations, release
- * any further reservations, and update the
- * number of available RRBs.
- */
-
- pcibr_soft->bs_rrb_valid[pciio_slot] = final_vchan0;
- pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL] = final_vchan1;
- pcibr_soft->bs_rrb_avail[pciio_slot & 1] =
- pcibr_soft->bs_rrb_avail[pciio_slot & 1]
- + pcibr_soft->bs_rrb_res[pciio_slot]
- - delta_vchan0
- - delta_vchan1;
- pcibr_soft->bs_rrb_res[pciio_slot] = 0;
-
-#if PCIBR_RRB_DEBUG
- printk("pcibr_rrb_alloc: slot %d set to %d+%d; %d+%d free\n",
- pciio_slot, final_vchan0, final_vchan1,
- pcibr_soft->bs_rrb_avail[0],
- pcibr_soft->bs_rrb_avail[1]);
- for (pciio_slot = 0; pciio_slot < 8; ++pciio_slot)
- printk("\t%d+%d+%d",
- 0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot],
- 0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[pciio_slot]);
- printk("\n");
-#endif
-
- error = 0;
- }
-
- pcibr_unlock(pcibr_soft, s);
- return error;
-}
-
-/*
- * Device driver interface to check the current state
- * of the RRB allocations.
- *
- * pconn_vhdl is your PCI connection point (specifies which
- * PCI bus and which slot).
- *
- * count_vchan0 points to where to return the number of RRBs
- * assigned to the primary DMA channel, used by all DMA
- * that does not explicitly ask for the alternate virtual
- * channel.
- *
- * count_vchan1 points to where to return the number of RRBs
- * assigned to the secondary DMA channel, used when
- * PCIBR_VCHAN1 and PCIIO_DMA_A64 are specified.
- *
- * count_reserved points to where to return the number of RRBs
- * that have been automatically reserved for your device at
- * startup, but which have not been assigned to a
- * channel. RRBs must be assigned to a channel to be used;
- * this can be done either with an explicit pcibr_rrb_alloc
- * call, or automatically by the infrastructure when a DMA
- * translation is constructed. Any call to pcibr_rrb_alloc
- * will release any unassigned reserved RRBs back to the
- * free pool.
- *
- * count_pool points to where to return the number of RRBs
- * that are currently unassigned and unreserved. This
- * number can (and will) change as other drivers make calls
- * to pcibr_rrb_alloc, or automatically allocate RRBs for
- * DMA beyond their initial reservation.
- *
- * NULL may be passed for any of the return value pointers
- * the caller is not interested in.
- *
- * The return value is "0" if all went well, or "-1" if
- * there is a problem. Additionally, if the wrong vertex
- * is passed in, one of the subsidiary support functions
- * could panic with a "bad pciio fingerprint."
- */
-
-int
-pcibr_rrb_check(devfs_handle_t pconn_vhdl,
- int *count_vchan0,
- int *count_vchan1,
- int *count_reserved,
- int *count_pool)
-{
- pciio_info_t pciio_info;
- pciio_slot_t pciio_slot;
- pcibr_soft_t pcibr_soft;
- unsigned long s;
- int error = -1;
-
- if ((pciio_info = pciio_info_get(pconn_vhdl)) &&
- (pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info)) &&
- ((pciio_slot = pciio_info_slot_get(pciio_info)) < 8)) {
-
- s = pcibr_lock(pcibr_soft);
-
- if (count_vchan0)
- *count_vchan0 =
- pcibr_soft->bs_rrb_valid[pciio_slot];
-
- if (count_vchan1)
- *count_vchan1 =
- pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
-
- if (count_reserved)
- *count_reserved =
- pcibr_soft->bs_rrb_res[pciio_slot];
-
- if (count_pool)
- *count_pool =
- pcibr_soft->bs_rrb_avail[pciio_slot & 1];
-
- error = 0;
-
- pcibr_unlock(pcibr_soft, s);
- }
- return error;
-}
-
-/* pcibr_alloc_all_rrbs allocates all the rrbs available in the quantities
- * requested for each of the devies. The evn_odd argument indicates whether
- * allcoation for the odd or even rrbs is requested and next group of four pairse
- * are the amount to assign to each device (they should sum to <= 8) and
- * whether to set the viritual bit for that device (1 indictaes yes, 0 indicates no)
- * the devices in order are either 0, 2, 4, 6 or 1, 3, 5, 7
- * if even_odd is even we alloc even rrbs else we allocate odd rrbs
- * returns 0 if no errors else returns -1
- */
-
-int
-pcibr_alloc_all_rrbs(devfs_handle_t vhdl, int even_odd,
- int dev_1_rrbs, int virt1, int dev_2_rrbs, int virt2,
- int dev_3_rrbs, int virt3, int dev_4_rrbs, int virt4)
-{
- devfs_handle_t pcibr_vhdl;
- pcibr_soft_t pcibr_soft = NULL;
- bridge_t *bridge = NULL;
-
- uint32_t rrb_setting = 0;
- int rrb_shift = 7;
- uint32_t cur_rrb;
- int dev_rrbs[4];
- int virt[4];
- int i, j;
- unsigned long s;
-
- if (GRAPH_SUCCESS ==
- hwgraph_traverse(vhdl, EDGE_LBL_PCI, &pcibr_vhdl)) {
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- if (pcibr_soft)
- bridge = pcibr_soft->bs_base;
- hwgraph_vertex_unref(pcibr_vhdl);
- }
- if (bridge == NULL)
- bridge = (bridge_t *) xtalk_piotrans_addr
- (vhdl, NULL, 0, sizeof(bridge_t), 0);
-
- even_odd &= 1;
-
- dev_rrbs[0] = dev_1_rrbs;
- dev_rrbs[1] = dev_2_rrbs;
- dev_rrbs[2] = dev_3_rrbs;
- dev_rrbs[3] = dev_4_rrbs;
-
- virt[0] = virt1;
- virt[1] = virt2;
- virt[2] = virt3;
- virt[3] = virt4;
-
- if ((dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs) > 8) {
- return -1;
- }
- if ((dev_1_rrbs < 0) || (dev_2_rrbs < 0) || (dev_3_rrbs < 0) || (dev_4_rrbs < 0)) {
- return -1;
- }
- /* walk through rrbs */
- for (i = 0; i < 4; i++) {
- if (virt[i]) {
- cur_rrb = i | 0xc;
- cur_rrb = cur_rrb << (rrb_shift * 4);
- rrb_shift--;
- rrb_setting = rrb_setting | cur_rrb;
- dev_rrbs[i] = dev_rrbs[i] - 1;
- }
- for (j = 0; j < dev_rrbs[i]; j++) {
- cur_rrb = i | 0x8;
- cur_rrb = cur_rrb << (rrb_shift * 4);
- rrb_shift--;
- rrb_setting = rrb_setting | cur_rrb;
- }
- }
-
- if (pcibr_soft)
- s = pcibr_lock(pcibr_soft);
-
- bridge->b_rrb_map[even_odd].reg = rrb_setting;
-
- if (pcibr_soft) {
-
- pcibr_soft->bs_rrb_fixed |= 0x55 << even_odd;
-
- /* since we've "FIXED" the allocations
- * for these slots, we probably can dispense
- * with tracking avail/res/valid data, but
- * keeping it up to date helps debugging.
- */
-
- pcibr_soft->bs_rrb_avail[even_odd] =
- 8 - (dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs);
-
- pcibr_soft->bs_rrb_res[even_odd + 0] = 0;
- pcibr_soft->bs_rrb_res[even_odd + 2] = 0;
- pcibr_soft->bs_rrb_res[even_odd + 4] = 0;
- pcibr_soft->bs_rrb_res[even_odd + 6] = 0;
-
- pcibr_soft->bs_rrb_valid[even_odd + 0] = dev_1_rrbs - virt1;
- pcibr_soft->bs_rrb_valid[even_odd + 2] = dev_2_rrbs - virt2;
- pcibr_soft->bs_rrb_valid[even_odd + 4] = dev_3_rrbs - virt3;
- pcibr_soft->bs_rrb_valid[even_odd + 6] = dev_4_rrbs - virt4;
-
- pcibr_soft->bs_rrb_valid[even_odd + 0 + PCIBR_RRB_SLOT_VIRTUAL] = virt1;
- pcibr_soft->bs_rrb_valid[even_odd + 2 + PCIBR_RRB_SLOT_VIRTUAL] = virt2;
- pcibr_soft->bs_rrb_valid[even_odd + 4 + PCIBR_RRB_SLOT_VIRTUAL] = virt3;
- pcibr_soft->bs_rrb_valid[even_odd + 6 + PCIBR_RRB_SLOT_VIRTUAL] = virt4;
-
- pcibr_unlock(pcibr_soft, s);
- }
- return 0;
-}
-
-/*
- * pcibr_rrb_flush: chase down all the RRBs assigned
- * to the specified connection point, and flush
- * them.
- */
-void
-pcibr_rrb_flush(devfs_handle_t pconn_vhdl)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned long s;
- reg_p rrbp;
- unsigned rrbm;
- int i;
- int rrbn;
- unsigned sval;
- unsigned mask;
-
- sval = BRIDGE_RRB_EN | (pciio_slot >> 1);
- mask = BRIDGE_RRB_EN | BRIDGE_RRB_PDEV;
- rrbn = pciio_slot & 1;
- rrbp = &bridge->b_rrb_map[rrbn].reg;
-
- s = pcibr_lock(pcibr_soft);
- rrbm = *rrbp;
- for (i = 0; i < 8; ++i) {
- if ((rrbm & mask) == sval)
- do_pcibr_rrb_flush(bridge, rrbn);
- rrbm >>= 4;
- rrbn += 2;
- }
- pcibr_unlock(pcibr_soft, s);
-}
-
-/* =====================================================================
- * Device(x) register management
- */
-
-/* pcibr_try_set_device: attempt to modify Device(x)
- * for the specified slot on the specified bridge
- * as requested in flags, limited to the specified
- * bits. Returns which BRIDGE bits were in conflict,
- * or ZERO if everything went OK.
- *
- * Caller MUST hold pcibr_lock when calling this function.
- */
-LOCAL int
-pcibr_try_set_device(pcibr_soft_t pcibr_soft,
- pciio_slot_t slot,
- unsigned flags,
- bridgereg_t mask)
-{
- bridge_t *bridge;
- pcibr_soft_slot_t slotp;
- bridgereg_t old;
- bridgereg_t new;
- bridgereg_t chg;
- bridgereg_t bad;
- bridgereg_t badpmu;
- bridgereg_t badd32;
- bridgereg_t badd64;
- bridgereg_t fix;
- unsigned long s;
- bridgereg_t xmask;
-
- xmask = mask;
- if (pcibr_soft->bs_xbridge) {
- if (mask == BRIDGE_DEV_PMU_BITS)
- xmask = XBRIDGE_DEV_PMU_BITS;
- if (mask == BRIDGE_DEV_D64_BITS)
- xmask = XBRIDGE_DEV_D64_BITS;
- }
-
- slotp = &pcibr_soft->bs_slot[slot];
-
- s = pcibr_lock(pcibr_soft);
-
- bridge = pcibr_soft->bs_base;
-
- old = slotp->bss_device;
-
- /* figure out what the desired
- * Device(x) bits are based on
- * the flags specified.
- */
-
- new = old;
-
- /* Currently, we inherit anything that
- * the new caller has not specified in
- * one way or another, unless we take
- * action here to not inherit.
- *
- * This is needed for the "swap" stuff,
- * since it could have been set via
- * pcibr_endian_set -- altho note that
- * any explicit PCIBR_BYTE_STREAM or
- * PCIBR_WORD_VALUES will freely override
- * the effect of that call (and vice
- * versa, no protection either way).
- *
- * I want to get rid of pcibr_endian_set
- * in favor of tracking DMA endianness
- * using the flags specified when DMA
- * channels are created.
- */
-
-#define BRIDGE_DEV_WRGA_BITS (BRIDGE_DEV_PMU_WRGA_EN | BRIDGE_DEV_DIR_WRGA_EN)
-#define BRIDGE_DEV_SWAP_BITS (BRIDGE_DEV_SWAP_PMU | BRIDGE_DEV_SWAP_DIR)
-
- /* Do not use Barrier, Write Gather,
- * or Prefetch unless asked.
- * Leave everything else as it
- * was from the last time.
- */
- new = new
- & ~BRIDGE_DEV_BARRIER
- & ~BRIDGE_DEV_WRGA_BITS
- & ~BRIDGE_DEV_PREF
- ;
-
- /* Generic macro flags
- */
- if (flags & PCIIO_DMA_DATA) {
- new = (new
- & ~BRIDGE_DEV_BARRIER) /* barrier off */
- | BRIDGE_DEV_PREF; /* prefetch on */
-
- }
- if (flags & PCIIO_DMA_CMD) {
- new = ((new
- & ~BRIDGE_DEV_PREF) /* prefetch off */
- & ~BRIDGE_DEV_WRGA_BITS) /* write gather off */
- | BRIDGE_DEV_BARRIER; /* barrier on */
- }
- /* Generic detail flags
- */
- if (flags & PCIIO_WRITE_GATHER)
- new |= BRIDGE_DEV_WRGA_BITS;
- if (flags & PCIIO_NOWRITE_GATHER)
- new &= ~BRIDGE_DEV_WRGA_BITS;
-
- if (flags & PCIIO_PREFETCH)
- new |= BRIDGE_DEV_PREF;
- if (flags & PCIIO_NOPREFETCH)
- new &= ~BRIDGE_DEV_PREF;
-
- if (flags & PCIBR_WRITE_GATHER)
- new |= BRIDGE_DEV_WRGA_BITS;
- if (flags & PCIBR_NOWRITE_GATHER)
- new &= ~BRIDGE_DEV_WRGA_BITS;
-
- if (flags & PCIIO_BYTE_STREAM)
- new |= (pcibr_soft->bs_xbridge) ?
- BRIDGE_DEV_SWAP_DIR : BRIDGE_DEV_SWAP_BITS;
- if (flags & PCIIO_WORD_VALUES)
- new &= (pcibr_soft->bs_xbridge) ?
- ~BRIDGE_DEV_SWAP_DIR : ~BRIDGE_DEV_SWAP_BITS;
-
- /* Provider-specific flags
- */
- if (flags & PCIBR_PREFETCH)
- new |= BRIDGE_DEV_PREF;
- if (flags & PCIBR_NOPREFETCH)
- new &= ~BRIDGE_DEV_PREF;
-
- if (flags & PCIBR_PRECISE)
- new |= BRIDGE_DEV_PRECISE;
- if (flags & PCIBR_NOPRECISE)
- new &= ~BRIDGE_DEV_PRECISE;
-
- if (flags & PCIBR_BARRIER)
- new |= BRIDGE_DEV_BARRIER;
- if (flags & PCIBR_NOBARRIER)
- new &= ~BRIDGE_DEV_BARRIER;
-
- if (flags & PCIBR_64BIT)
- new |= BRIDGE_DEV_DEV_SIZE;
- if (flags & PCIBR_NO64BIT)
- new &= ~BRIDGE_DEV_DEV_SIZE;
-
- chg = old ^ new; /* what are we changing, */
- chg &= xmask; /* of the interesting bits */
-
- if (chg) {
-
- badd32 = slotp->bss_d32_uctr ? (BRIDGE_DEV_D32_BITS & chg) : 0;
- if (pcibr_soft->bs_xbridge) {
- badpmu = slotp->bss_pmu_uctr ? (XBRIDGE_DEV_PMU_BITS & chg) : 0;
- badd64 = slotp->bss_d64_uctr ? (XBRIDGE_DEV_D64_BITS & chg) : 0;
- } else {
- badpmu = slotp->bss_pmu_uctr ? (BRIDGE_DEV_PMU_BITS & chg) : 0;
- badd64 = slotp->bss_d64_uctr ? (BRIDGE_DEV_D64_BITS & chg) : 0;
- }
- bad = badpmu | badd32 | badd64;
-
- if (bad) {
-
- /* some conflicts can be resolved by
- * forcing the bit on. this may cause
- * some performance degredation in
- * the stream(s) that want the bit off,
- * but the alternative is not allowing
- * the new stream at all.
- */
- if ( (fix = bad & (BRIDGE_DEV_PRECISE |
- BRIDGE_DEV_BARRIER)) ){
- bad &= ~fix;
- /* don't change these bits if
- * they are already set in "old"
- */
- chg &= ~(fix & old);
- }
- /* some conflicts can be resolved by
- * forcing the bit off. this may cause
- * some performance degredation in
- * the stream(s) that want the bit on,
- * but the alternative is not allowing
- * the new stream at all.
- */
- if ( (fix = bad & (BRIDGE_DEV_WRGA_BITS |
- BRIDGE_DEV_PREF)) ) {
- bad &= ~fix;
- /* don't change these bits if
- * we wanted to turn them on.
- */
- chg &= ~(fix & new);
- }
- /* conflicts in other bits mean
- * we can not establish this DMA
- * channel while the other(s) are
- * still present.
- */
- if (bad) {
- pcibr_unlock(pcibr_soft, s);
-#if (DEBUG && PCIBR_DEV_DEBUG)
- printk("pcibr_try_set_device: mod blocked by %R\n", bad, device_bits);
-#endif
- return bad;
- }
- }
- }
- if (mask == BRIDGE_DEV_PMU_BITS)
- slotp->bss_pmu_uctr++;
- if (mask == BRIDGE_DEV_D32_BITS)
- slotp->bss_d32_uctr++;
- if (mask == BRIDGE_DEV_D64_BITS)
- slotp->bss_d64_uctr++;
-
- /* the value we want to write is the
- * original value, with the bits for
- * our selected changes flipped, and
- * with any disabled features turned off.
- */
- new = old ^ chg; /* only change what we want to change */
-
- if (slotp->bss_device == new) {
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- bridge->b_device[slot].reg = new;
- slotp->bss_device = new;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
-#if DEBUG && PCIBR_DEV_DEBUG
- printk("pcibr Device(%d): 0x%p\n", slot, bridge->b_device[slot].reg);
-#endif
-
- return 0;
-}
-
-void
-pcibr_release_device(pcibr_soft_t pcibr_soft,
- pciio_slot_t slot,
- bridgereg_t mask)
-{
- pcibr_soft_slot_t slotp;
- unsigned long s;
-
- slotp = &pcibr_soft->bs_slot[slot];
-
- s = pcibr_lock(pcibr_soft);
-
- if (mask == BRIDGE_DEV_PMU_BITS)
- slotp->bss_pmu_uctr--;
- if (mask == BRIDGE_DEV_D32_BITS)
- slotp->bss_d32_uctr--;
- if (mask == BRIDGE_DEV_D64_BITS)
- slotp->bss_d64_uctr--;
-
- pcibr_unlock(pcibr_soft, s);
-}
-
-/*
- * flush write gather buffer for slot
- */
-LOCAL void
-pcibr_device_write_gather_flush(pcibr_soft_t pcibr_soft,
- pciio_slot_t slot)
-{
- bridge_t *bridge;
- unsigned long s;
- volatile uint32_t wrf;
- s = pcibr_lock(pcibr_soft);
- bridge = pcibr_soft->bs_base;
- wrf = bridge->b_wr_req_buf[slot].reg;
- pcibr_unlock(pcibr_soft, s);
-}
-
-/* =====================================================================
- * Bridge (pcibr) "Device Driver" entry points
- */
-
-/*
- * pcibr_probe_slot: read a config space word
- * while trapping any errors; reutrn zero if
- * all went OK, or nonzero if there was an error.
- * The value read, if any, is passed back
- * through the valp parameter.
- */
-LOCAL int
-pcibr_probe_slot(bridge_t *bridge,
- cfg_p cfg,
- unsigned *valp)
-{
- int rv;
- bridgereg_t old_enable, new_enable;
- int badaddr_val(volatile void *, int, volatile void *);
-
-
- old_enable = bridge->b_int_enable;
- new_enable = old_enable & ~BRIDGE_IMR_PCI_MST_TIMEOUT;
-
- bridge->b_int_enable = new_enable;
-
- /*
- * The xbridge doesn't clear b_err_int_view unless
- * multi-err is cleared...
- */
- if (is_xbridge(bridge))
- if (bridge->b_err_int_view & BRIDGE_ISR_PCI_MST_TIMEOUT) {
- bridge->b_int_rst_stat = BRIDGE_IRR_MULTI_CLR;
- }
-
- if (bridge->b_int_status & BRIDGE_IRR_PCI_GRP) {
- bridge->b_int_rst_stat = BRIDGE_IRR_PCI_GRP_CLR;
- (void) bridge->b_wid_tflush; /* flushbus */
- }
- rv = badaddr_val((void *) cfg, 4, valp);
-
- /*
- * The xbridge doesn't set master timeout in b_int_status
- * here. Fortunately it's in error_interrupt_view.
- */
- if (is_xbridge(bridge))
- if (bridge->b_err_int_view & BRIDGE_ISR_PCI_MST_TIMEOUT) {
- bridge->b_int_rst_stat = BRIDGE_IRR_MULTI_CLR;
- rv = 1; /* unoccupied slot */
- }
-
- bridge->b_int_enable = old_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
-
- return rv;
-}
-
-/*
- * pcibr_init: called once during system startup or
- * when a loadable driver is loaded.
- *
- * The driver_register function should normally
- * be in _reg, not _init. But the pcibr driver is
- * required by devinit before the _reg routines
- * are called, so this is an exception.
- */
-void
-pcibr_init(void)
-{
-#if DEBUG && ATTACH_DEBUG
- printk("pcibr_init\n");
-#endif
-
- xwidget_driver_register(XBRIDGE_WIDGET_PART_NUM,
- XBRIDGE_WIDGET_MFGR_NUM,
- "pcibr_",
- 0);
- xwidget_driver_register(BRIDGE_WIDGET_PART_NUM,
- BRIDGE_WIDGET_MFGR_NUM,
- "pcibr_",
- 0);
-}
-
-/*
- * open/close mmap/munmap interface would be used by processes
- * that plan to map the PCI bridge, and muck around with the
- * registers. This is dangerous to do, and will be allowed
- * to a select brand of programs. Typically these are
- * diagnostics programs, or some user level commands we may
- * write to do some weird things.
- * To start with expect them to have root priveleges.
- * We will ask for more later.
- */
-/* ARGSUSED */
-int
-pcibr_open(devfs_handle_t *devp, int oflag, int otyp, cred_t *credp)
-{
- return 0;
-}
-
-/*ARGSUSED */
-int
-pcibr_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
-{
- return 0;
-}
-
-/*ARGSUSED */
-int
-pcibr_map(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
-{
- int error;
- devfs_handle_t vhdl = dev_to_vhdl(dev);
- devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get(vhdl);
- pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- bridge_t *bridge = pcibr_soft->bs_base;
-
- hwgraph_vertex_unref(pcibr_vhdl);
-
- ASSERT(pcibr_soft);
- len = ctob(btoc(len)); /* Make len page aligned */
- error = v_mapphys(vt, (void *) ((__psunsigned_t) bridge + off), len);
-
- /*
- * If the offset being mapped corresponds to the flash prom
- * base, and if the mapping succeeds, and if the user
- * has requested the protections to be WRITE, enable the
- * flash prom to be written.
- *
- * XXX- deprecate this in favor of using the
- * real flash driver ...
- */
- if (!error &&
- ((off == BRIDGE_EXTERNAL_FLASH) ||
- (len > BRIDGE_EXTERNAL_FLASH))) {
- int s;
-
- /*
- * ensure that we write and read without any interruption.
- * The read following the write is required for the Bridge war
- */
- s = splhi();
- bridge->b_wid_control |= BRIDGE_CTRL_FLASH_WR_EN;
- bridge->b_wid_control; /* inval addr bug war */
- splx(s);
- }
- return error;
-}
-
-/*ARGSUSED */
-int
-pcibr_unmap(devfs_handle_t dev, vhandl_t *vt)
-{
- devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get((devfs_handle_t) dev);
- pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- bridge_t *bridge = pcibr_soft->bs_base;
-
- hwgraph_vertex_unref(pcibr_vhdl);
-
- /*
- * If flashprom write was enabled, disable it, as
- * this is the last unmap.
- */
- if (bridge->b_wid_control & BRIDGE_CTRL_FLASH_WR_EN) {
- int s;
-
- /*
- * ensure that we write and read without any interruption.
- * The read following the write is required for the Bridge war
- */
- s = splhi();
- bridge->b_wid_control &= ~BRIDGE_CTRL_FLASH_WR_EN;
- bridge->b_wid_control; /* inval addr bug war */
- splx(s);
- }
- return 0;
-}
-
-/* This is special case code used by grio. There are plans to make
- * this a bit more general in the future, but till then this should
- * be sufficient.
- */
-pciio_slot_t
-pcibr_device_slot_get(devfs_handle_t dev_vhdl)
-{
- char devname[MAXDEVNAME];
- devfs_handle_t tdev;
- pciio_info_t pciio_info;
- pciio_slot_t slot = PCIIO_SLOT_NONE;
-
- vertex_to_name(dev_vhdl, devname, MAXDEVNAME);
-
- /* run back along the canonical path
- * until we find a PCI connection point.
- */
- tdev = hwgraph_connectpt_get(dev_vhdl);
- while (tdev != GRAPH_VERTEX_NONE) {
- pciio_info = pciio_info_chk(tdev);
- if (pciio_info) {
- slot = pciio_info_slot_get(pciio_info);
- break;
- }
- hwgraph_vertex_unref(tdev);
- tdev = hwgraph_connectpt_get(tdev);
- }
- hwgraph_vertex_unref(tdev);
-
- return slot;
-}
-
-/*==========================================================================
- * BRIDGE PCI SLOT RELATED IOCTLs
- */
-char *pci_space_name[] = {"NONE",
- "ROM",
- "IO",
- "",
- "MEM",
- "MEM32",
- "MEM64",
- "CFG",
- "WIN0",
- "WIN1",
- "WIN2",
- "WIN3",
- "WIN4",
- "WIN5",
- "",
- "BAD"};
-
-
-#ifdef LATER
-
-void
-pcibr_slot_func_info_return(pcibr_info_h pcibr_infoh,
- int func,
- pcibr_slot_func_info_resp_t funcp)
-{
- pcibr_info_t pcibr_info = pcibr_infoh[func];
- int win;
-
- funcp->resp_f_status = 0;
-
- if (!pcibr_info) {
- return;
- }
-
- funcp->resp_f_status |= FUNC_IS_VALID;
-#ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(funcp->resp_f_slot_name, "%v", pcibr_info->f_vertex);
-#else
- sprintf(funcp->resp_f_slot_name, "%x", pcibr_info->f_vertex);
-#endif
-
- if(is_sys_critical_vertex(pcibr_info->f_vertex)) {
- funcp->resp_f_status |= FUNC_IS_SYS_CRITICAL;
- }
-
- funcp->resp_f_bus = pcibr_info->f_bus;
- funcp->resp_f_slot = pcibr_info->f_slot;
- funcp->resp_f_func = pcibr_info->f_func;
-#ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(funcp->resp_f_master_name, "%v", pcibr_info->f_master);
-#else
- sprintf(funcp->resp_f_master_name, "%x", pcibr_info->f_master);
-#endif
- funcp->resp_f_pops = pcibr_info->f_pops;
- funcp->resp_f_efunc = pcibr_info->f_efunc;
- funcp->resp_f_einfo = pcibr_info->f_einfo;
-
- funcp->resp_f_vendor = pcibr_info->f_vendor;
- funcp->resp_f_device = pcibr_info->f_device;
-
- for(win = 0 ; win < 6 ; win++) {
- funcp->resp_f_window[win].resp_w_base =
- pcibr_info->f_window[win].w_base;
- funcp->resp_f_window[win].resp_w_size =
- pcibr_info->f_window[win].w_size;
- sprintf(funcp->resp_f_window[win].resp_w_space,
- "%s",
- pci_space_name[pcibr_info->f_window[win].w_space]);
- }
-
- funcp->resp_f_rbase = pcibr_info->f_rbase;
- funcp->resp_f_rsize = pcibr_info->f_rsize;
-
- for (win = 0 ; win < 4; win++) {
- funcp->resp_f_ibit[win] = pcibr_info->f_ibit[win];
- }
-
- funcp->resp_f_att_det_error = pcibr_info->f_att_det_error;
-
-}
-
-int
-pcibr_slot_info_return(pcibr_soft_t pcibr_soft,
- pciio_slot_t slot,
- pcibr_slot_info_resp_t respp)
-{
- pcibr_soft_slot_t pss;
- int func;
- bridge_t *bridge = pcibr_soft->bs_base;
- reg_p b_respp;
- pcibr_slot_info_resp_t slotp;
- pcibr_slot_func_info_resp_t funcp;
-
- slotp = kmem_zalloc(sizeof(*slotp), KM_SLEEP);
- if (slotp == NULL) {
- return(ENOMEM);
- }
-
- pss = &pcibr_soft->bs_slot[slot];
-
- printk("\nPCI INFRASTRUCTURAL INFO FOR SLOT %d\n\n", slot);
-
- slotp->resp_has_host = pss->has_host;
- slotp->resp_host_slot = pss->host_slot;
-#ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(slotp->resp_slot_conn_name, "%v", pss->slot_conn);
-#else
- sprintf(slotp->resp_slot_conn_name, "%x", pss->slot_conn);
-#endif
- slotp->resp_slot_status = pss->slot_status;
- slotp->resp_l1_bus_num = io_path_map_widget(pcibr_soft->bs_vhdl);
-
- if (is_sys_critical_vertex(pss->slot_conn)) {
- slotp->resp_slot_status |= SLOT_IS_SYS_CRITICAL;
- }
-
- slotp->resp_bss_ninfo = pss->bss_ninfo;
-
- for (func = 0; func < pss->bss_ninfo; func++) {
- funcp = &(slotp->resp_func[func]);
- pcibr_slot_func_info_return(pss->bss_infos, func, funcp);
- }
-
- sprintf(slotp->resp_bss_devio_bssd_space, "%s",
- pci_space_name[pss->bss_devio.bssd_space]);
- slotp->resp_bss_devio_bssd_base = pss->bss_devio.bssd_base;
- slotp->resp_bss_device = pss->bss_device;
-
- slotp->resp_bss_pmu_uctr = pss->bss_pmu_uctr;
- slotp->resp_bss_d32_uctr = pss->bss_d32_uctr;
- slotp->resp_bss_d64_uctr = pss->bss_d64_uctr;
-
- slotp->resp_bss_d64_base = pss->bss_d64_base;
- slotp->resp_bss_d64_flags = pss->bss_d64_flags;
- slotp->resp_bss_d32_base = pss->bss_d32_base;
- slotp->resp_bss_d32_flags = pss->bss_d32_flags;
-
- slotp->resp_bss_ext_ates_active = atomic_read(&pss->bss_ext_ates_active);
-
- slotp->resp_bss_cmd_pointer = pss->bss_cmd_pointer;
- slotp->resp_bss_cmd_shadow = pss->bss_cmd_shadow;
-
- slotp->resp_bs_rrb_valid = pcibr_soft->bs_rrb_valid[slot];
- slotp->resp_bs_rrb_valid_v = pcibr_soft->bs_rrb_valid[slot +
- PCIBR_RRB_SLOT_VIRTUAL];
- slotp->resp_bs_rrb_res = pcibr_soft->bs_rrb_res[slot];
-
- if (slot & 1) {
- b_respp = &bridge->b_odd_resp;
- } else {
- b_respp = &bridge->b_even_resp;
- }
-
- slotp->resp_b_resp = *b_respp;
-
- slotp->resp_b_int_device = bridge->b_int_device;
- slotp->resp_b_int_enable = bridge->b_int_enable;
- slotp->resp_b_int_host = bridge->b_int_addr[slot].addr;
-
- if (COPYOUT(slotp, respp, sizeof(*respp))) {
- return(EFAULT);
- }
-
- kmem_free(slotp, sizeof(*slotp));
-
- return(0);
-}
-
-/*
- * pcibr_slot_query
- * Return information about the PCI slot maintained by the infrastructure.
- * Information is requested in the request structure.
- *
- * Information returned in the response structure:
- * Slot hwgraph name
- * Vendor/Device info
- * Base register info
- * Interrupt mapping from device pins to the bridge pins
- * Devio register
- * Software RRB info
- * RRB register info
- * Host/Gues info
- * PCI Bus #,slot #, function #
- * Slot provider hwgraph name
- * Provider Functions
- * Error handler
- * DMA mapping usage counters
- * DMA direct translation info
- * External SSRAM workaround info
- */
-int
-pcibr_slot_query(devfs_handle_t pcibr_vhdl, pcibr_slot_info_req_t reqp)
-{
- pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- pciio_slot_t slot = reqp->req_slot;
- pciio_slot_t tmp_slot;
- pcibr_slot_info_resp_t respp = (pcibr_slot_info_resp_t) reqp->req_respp;
- int size = reqp->req_size;
- int error;
-
- /* Make sure that we are dealing with a bridge device vertex */
- if (!pcibr_soft) {
- return(EINVAL);
- }
-
- /* Make sure that we have a valid PCI slot number or PCIIO_SLOT_NONE */
- if ((!PCIBR_VALID_SLOT(slot)) && (slot != PCIIO_SLOT_NONE)) {
- return(EINVAL);
- }
-
-#ifdef LATER
- /* Do not allow a query of a slot in a shoehorn */
- if(nic_vertex_info_match(pcibr_soft->bs_conn, XTALK_PCI_PART_NUM)) {
- return(EPERM);
- }
-#endif
-
- /* Return information for the requested PCI slot */
- if (slot != PCIIO_SLOT_NONE) {
- if (size < sizeof(*respp)) {
- return(EINVAL);
- }
-
- /* Acquire read access to the slot */
- mrlock(pcibr_soft->bs_slot[slot].slot_lock, MR_ACCESS, PZERO);
-
- error = pcibr_slot_info_return(pcibr_soft, slot, respp);
-
- /* Release the slot lock */
- mrunlock(pcibr_soft->bs_slot[slot].slot_lock);
-
- return(error);
- }
-
- /* Return information for all the slots */
- for (tmp_slot = 0; tmp_slot < 8; tmp_slot++) {
-
- if (size < sizeof(*respp)) {
- return(EINVAL);
- }
-
- /* Acquire read access to the slot */
- mrlock(pcibr_soft->bs_slot[tmp_slot].slot_lock, MR_ACCESS, PZERO);
-
- error = pcibr_slot_info_return(pcibr_soft, tmp_slot, respp);
-
- /* Release the slot lock */
- mrunlock(pcibr_soft->bs_slot[tmp_slot].slot_lock);
-
- if (error) {
- return(error);
- }
-
- ++respp;
- size -= sizeof(*respp);
- }
-
- return(error);
-}
-#endif /* LATER */
-
-
-/*ARGSUSED */
-int
-pcibr_ioctl(devfs_handle_t dev,
- int cmd,
- void *arg,
- int flag,
- struct cred *cr,
- int *rvalp)
-{
- devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get((devfs_handle_t)dev);
-#ifdef LATER
- pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-#endif
- int error = 0;
-
- hwgraph_vertex_unref(pcibr_vhdl);
-
- switch (cmd) {
-#ifdef LATER
- case GIOCSETBW:
- {
- grio_ioctl_info_t info;
- pciio_slot_t slot = 0;
-
- if (!cap_able((uint64_t)CAP_DEVICE_MGT)) {
- error = EPERM;
- break;
- }
- if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
- error = EFAULT;
- break;
- }
-#ifdef GRIO_DEBUG
- printk("pcibr:: prev_vhdl: %d reqbw: %lld\n",
- info.prev_vhdl, info.reqbw);
-#endif /* GRIO_DEBUG */
-
- if ((slot = pcibr_device_slot_get(info.prev_vhdl)) ==
- PCIIO_SLOT_NONE) {
- error = EIO;
- break;
- }
- if (info.reqbw)
- pcibr_priority_bits_set(pcibr_soft, slot, PCI_PRIO_HIGH);
- break;
- }
-
- case GIOCRELEASEBW:
- {
- grio_ioctl_info_t info;
- pciio_slot_t slot = 0;
-
- if (!cap_able(CAP_DEVICE_MGT)) {
- error = EPERM;
- break;
- }
- if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
- error = EFAULT;
- break;
- }
-#ifdef GRIO_DEBUG
- printk("pcibr:: prev_vhdl: %d reqbw: %lld\n",
- info.prev_vhdl, info.reqbw);
-#endif /* GRIO_DEBUG */
-
- if ((slot = pcibr_device_slot_get(info.prev_vhdl)) ==
- PCIIO_SLOT_NONE) {
- error = EIO;
- break;
- }
- if (info.reqbw)
- pcibr_priority_bits_set(pcibr_soft, slot, PCI_PRIO_LOW);
- break;
- }
-
- case PCIBR_SLOT_POWERUP:
- {
- pciio_slot_t slot;
-
- if (!cap_able(CAP_DEVICE_MGT)) {
- error = EPERM;
- break;
- }
-
- slot = (pciio_slot_t)(uint64_t)arg;
- error = pcibr_slot_powerup(pcibr_vhdl,slot);
- break;
- }
- case PCIBR_SLOT_SHUTDOWN:
- if (!cap_able(CAP_DEVICE_MGT)) {
- error = EPERM;
- break;
- }
-
- slot = (pciio_slot_t)(uint64_t)arg;
- error = pcibr_slot_powerup(pcibr_vhdl,slot);
- break;
- }
- case PCIBR_SLOT_QUERY:
- {
- struct pcibr_slot_info_req_s req;
-
- if (!cap_able(CAP_DEVICE_MGT)) {
- error = EPERM;
- break;
- }
-
- if (COPYIN(arg, &req, sizeof(req))) {
- error = EFAULT;
- break;
- }
-
- error = pcibr_slot_query(pcibr_vhdl, &req);
- break;
- }
-#endif /* LATER */
- default:
- break;
-
- }
-
- return error;
-}
-
-void
-pcibr_freeblock_sub(iopaddr_t *free_basep,
- iopaddr_t *free_lastp,
- iopaddr_t base,
- size_t size)
-{
- iopaddr_t free_base = *free_basep;
- iopaddr_t free_last = *free_lastp;
- iopaddr_t last = base + size - 1;
-
- if ((last < free_base) || (base > free_last)); /* free block outside arena */
-
- else if ((base <= free_base) && (last >= free_last))
- /* free block contains entire arena */
- *free_basep = *free_lastp = 0;
-
- else if (base <= free_base)
- /* free block is head of arena */
- *free_basep = last + 1;
-
- else if (last >= free_last)
- /* free block is tail of arena */
- *free_lastp = base - 1;
-
- /*
- * We are left with two regions: the free area
- * in the arena "below" the block, and the free
- * area in the arena "above" the block. Keep
- * the one that is bigger.
- */
-
- else if ((base - free_base) > (free_last - last))
- *free_lastp = base - 1; /* keep lower chunk */
- else
- *free_basep = last + 1; /* keep upper chunk */
-}
-
-/* Convert from ssram_bits in control register to number of SSRAM entries */
-#define ATE_NUM_ENTRIES(n) _ate_info[n]
-
-/* Possible choices for number of ATE entries in Bridge's SSRAM */
-LOCAL int _ate_info[] =
-{
- 0, /* 0 entries */
- 8 * 1024, /* 8K entries */
- 16 * 1024, /* 16K entries */
- 64 * 1024 /* 64K entries */
-};
-
-#define ATE_NUM_SIZES (sizeof(_ate_info) / sizeof(int))
-#define ATE_PROBE_VALUE 0x0123456789abcdefULL
-
-/*
- * Determine the size of this bridge's external mapping SSRAM, and set
- * the control register appropriately to reflect this size, and initialize
- * the external SSRAM.
- */
-LOCAL int
-pcibr_init_ext_ate_ram(bridge_t *bridge)
-{
- int largest_working_size = 0;
- int num_entries, entry;
- int i, j;
- bridgereg_t old_enable, new_enable;
- int s;
-
- /* Probe SSRAM to determine its size. */
- old_enable = bridge->b_int_enable;
- new_enable = old_enable & ~BRIDGE_IMR_PCI_MST_TIMEOUT;
- bridge->b_int_enable = new_enable;
-
- for (i = 1; i < ATE_NUM_SIZES; i++) {
- /* Try writing a value */
- bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] = ATE_PROBE_VALUE;
-
- /* Guard against wrap */
- for (j = 1; j < i; j++)
- bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(j) - 1] = 0;
-
- /* See if value was written */
- if (bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] == ATE_PROBE_VALUE)
- largest_working_size = i;
- }
- bridge->b_int_enable = old_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
-
- /*
- * ensure that we write and read without any interruption.
- * The read following the write is required for the Bridge war
- */
-
- s = splhi();
- bridge->b_wid_control = (bridge->b_wid_control
- & ~BRIDGE_CTRL_SSRAM_SIZE_MASK)
- | BRIDGE_CTRL_SSRAM_SIZE(largest_working_size);
- bridge->b_wid_control; /* inval addr bug war */
- splx(s);
-
- num_entries = ATE_NUM_ENTRIES(largest_working_size);
-
-#if PCIBR_ATE_DEBUG
- if (num_entries)
- printk("bridge at 0x%x: clearing %d external ATEs\n", bridge, num_entries);
- else
- printk("bridge at 0x%x: no externa9422l ATE RAM found\n", bridge);
-#endif
-
- /* Initialize external mapping entries */
- for (entry = 0; entry < num_entries; entry++)
- bridge->b_ext_ate_ram[entry] = 0;
-
- return (num_entries);
-}
-
-/*
- * Allocate "count" contiguous Bridge Address Translation Entries
- * on the specified bridge to be used for PCI to XTALK mappings.
- * Indices in rm map range from 1..num_entries. Indicies returned
- * to caller range from 0..num_entries-1.
- *
- * Return the start index on success, -1 on failure.
- */
-LOCAL int
-pcibr_ate_alloc(pcibr_soft_t pcibr_soft, int count)
-{
- int index = 0;
-
- index = (int) rmalloc(pcibr_soft->bs_int_ate_map, (size_t) count);
-/* printk("Colin: pcibr_ate_alloc - index %d count %d \n", index, count); */
-
- if (!index && pcibr_soft->bs_ext_ate_map)
- index = (int) rmalloc(pcibr_soft->bs_ext_ate_map, (size_t) count);
-
- /* rmalloc manages resources in the 1..n
- * range, with 0 being failure.
- * pcibr_ate_alloc manages resources
- * in the 0..n-1 range, with -1 being failure.
- */
- return index - 1;
-}
-
-LOCAL void
-pcibr_ate_free(pcibr_soft_t pcibr_soft, int index, int count)
-/* Who says there's no such thing as a free meal? :-) */
-{
- /* note the "+1" since rmalloc handles 1..n but
- * we start counting ATEs at zero.
- */
-/* printk("Colin: pcibr_ate_free - index %d count %d\n", index, count); */
-
- rmfree((index < pcibr_soft->bs_int_ate_size)
- ? pcibr_soft->bs_int_ate_map
- : pcibr_soft->bs_ext_ate_map,
- count, index + 1);
-}
-
-LOCAL pcibr_info_t
-pcibr_info_get(devfs_handle_t vhdl)
-{
- return (pcibr_info_t) pciio_info_get(vhdl);
-}
-
-pcibr_info_t
-pcibr_device_info_new(
- pcibr_soft_t pcibr_soft,
- pciio_slot_t slot,
- pciio_function_t rfunc,
- pciio_vendor_id_t vendor,
- pciio_device_id_t device)
-{
- pcibr_info_t pcibr_info;
- pciio_function_t func;
- int ibit;
-
- func = (rfunc == PCIIO_FUNC_NONE) ? 0 : rfunc;
-
- NEW(pcibr_info);
- pciio_device_info_new(&pcibr_info->f_c,
- pcibr_soft->bs_vhdl,
- slot, rfunc,
- vendor, device);
-
- if (slot != PCIIO_SLOT_NONE) {
-
- /*
- * Currently favored mapping from PCI
- * slot number and INTA/B/C/D to Bridge
- * PCI Interrupt Bit Number:
- *
- * SLOT A B C D
- * 0 0 4 0 4
- * 1 1 5 1 5
- * 2 2 6 2 6
- * 3 3 7 3 7
- * 4 4 0 4 0
- * 5 5 1 5 1
- * 6 6 2 6 2
- * 7 7 3 7 3
- *
- * XXX- allow pcibr_hints to override default
- * XXX- allow ADMIN to override pcibr_hints
- */
- for (ibit = 0; ibit < 4; ++ibit)
- pcibr_info->f_ibit[ibit] =
- (slot + 4 * ibit) & 7;
-
- /*
- * Record the info in the sparse func info space.
- */
- if (func < pcibr_soft->bs_slot[slot].bss_ninfo)
- pcibr_soft->bs_slot[slot].bss_infos[func] = pcibr_info;
- }
- return pcibr_info;
-}
-
-void
-pcibr_device_info_free(devfs_handle_t pcibr_vhdl, pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- pcibr_info_t pcibr_info;
- pciio_function_t func;
- pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[slot];
- int nfunc = slotp->bss_ninfo;
-
-
- for (func = 0; func < nfunc; func++) {
- pcibr_info = slotp->bss_infos[func];
-
- if (!pcibr_info)
- continue;
-
- slotp->bss_infos[func] = 0;
- pciio_device_info_unregister(pcibr_vhdl, &pcibr_info->f_c);
- pciio_device_info_free(&pcibr_info->f_c);
- DEL(pcibr_info);
- }
-
- /* Clear the DEVIO(x) for this slot */
- slotp->bss_devio.bssd_space = PCIIO_SPACE_NONE;
- slotp->bss_devio.bssd_base = PCIBR_D32_BASE_UNSET;
- slotp->bss_device = 0;
-
-
- /* Reset the mapping usage counters */
- slotp->bss_pmu_uctr = 0;
- slotp->bss_d32_uctr = 0;
- slotp->bss_d64_uctr = 0;
-
- /* Clear the Direct translation info */
- slotp->bss_d64_base = PCIBR_D64_BASE_UNSET;
- slotp->bss_d64_flags = 0;
- slotp->bss_d32_base = PCIBR_D32_BASE_UNSET;
- slotp->bss_d32_flags = 0;
-
- /* Clear out shadow info necessary for the external SSRAM workaround */
- slotp->bss_ext_ates_active = ATOMIC_INIT(0);
- slotp->bss_cmd_pointer = 0;
- slotp->bss_cmd_shadow = 0;
-
-}
-
-/*
- * PCI_ADDR_SPACE_LIMITS_LOAD
- * Gets the current values of
- * pci io base,
- * pci io last,
- * pci low memory base,
- * pci low memory last,
- * pci high memory base,
- * pci high memory last
- */
-#define PCI_ADDR_SPACE_LIMITS_LOAD() \
- pci_io_fb = pcibr_soft->bs_spinfo.pci_io_base; \
- pci_io_fl = pcibr_soft->bs_spinfo.pci_io_last; \
- pci_lo_fb = pcibr_soft->bs_spinfo.pci_swin_base; \
- pci_lo_fl = pcibr_soft->bs_spinfo.pci_swin_last; \
- pci_hi_fb = pcibr_soft->bs_spinfo.pci_mem_base; \
- pci_hi_fl = pcibr_soft->bs_spinfo.pci_mem_last;
-/*
- * PCI_ADDR_SPACE_LIMITS_STORE
- * Sets the current values of
- * pci io base,
- * pci io last,
- * pci low memory base,
- * pci low memory last,
- * pci high memory base,
- * pci high memory last
- */
-#define PCI_ADDR_SPACE_LIMITS_STORE() \
- pcibr_soft->bs_spinfo.pci_io_base = pci_io_fb; \
- pcibr_soft->bs_spinfo.pci_io_last = pci_io_fl; \
- pcibr_soft->bs_spinfo.pci_swin_base = pci_lo_fb; \
- pcibr_soft->bs_spinfo.pci_swin_last = pci_lo_fl; \
- pcibr_soft->bs_spinfo.pci_mem_base = pci_hi_fb; \
- pcibr_soft->bs_spinfo.pci_mem_last = pci_hi_fl;
-
-#define PCI_ADDR_SPACE_LIMITS_PRINT() \
- printf("+++++++++++++++++++++++\n" \
- "IO base 0x%x last 0x%x\n" \
- "SWIN base 0x%x last 0x%x\n" \
- "MEM base 0x%x last 0x%x\n" \
- "+++++++++++++++++++++++\n", \
- pcibr_soft->bs_spinfo.pci_io_base, \
- pcibr_soft->bs_spinfo.pci_io_last, \
- pcibr_soft->bs_spinfo.pci_swin_base, \
- pcibr_soft->bs_spinfo.pci_swin_last, \
- pcibr_soft->bs_spinfo.pci_mem_base, \
- pcibr_soft->bs_spinfo.pci_mem_last);
-
-/*
- * pcibr_slot_info_init
- * Probe for this slot and see if it is populated.
- * If it is populated initialize the generic PCI infrastructural
- * information associated with this particular PCI device.
- */
-int
-pcibr_slot_info_init(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- bridge_t *bridge;
- cfg_p cfgw;
- unsigned idword;
- unsigned pfail;
- unsigned idwords[8];
- pciio_vendor_id_t vendor;
- pciio_device_id_t device;
- unsigned htype;
-#if !defined(CONFIG_IA64_SGI_SN1)
- int nbars;
-#endif
- cfg_p wptr;
- int win;
- pciio_space_t space;
- iopaddr_t pci_io_fb, pci_io_fl;
- iopaddr_t pci_lo_fb, pci_lo_fl;
- iopaddr_t pci_hi_fb, pci_hi_fl;
- int nfunc;
- pciio_function_t rfunc;
- int func;
- devfs_handle_t conn_vhdl;
- pcibr_soft_slot_t slotp;
-
- /* Get the basic software information required to proceed */
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- if (!pcibr_soft)
- return(EINVAL);
-
- bridge = pcibr_soft->bs_base;
- if (!PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- /* If we have a host slot (eg:- IOC3 has 2 PCI slots and the initialization
- * is done by the host slot then we are done.
- */
- if (pcibr_soft->bs_slot[slot].has_host) {
- return(0);
- }
-
- /* Check for a slot with any system critical functions */
- if (pcibr_is_slot_sys_critical(pcibr_vhdl, slot))
- return(EPERM);
-
- /* Load the current values of allocated PCI address spaces */
- PCI_ADDR_SPACE_LIMITS_LOAD();
-
- /* Try to read the device-id/vendor-id from the config space */
- cfgw = bridge->b_type0_cfg_dev[slot].l;
-
- if (pcibr_probe_slot(bridge, cfgw, &idword))
- return(ENODEV);
-
- slotp = &pcibr_soft->bs_slot[slot];
- slotp->slot_status |= SLOT_POWER_UP;
-
- vendor = 0xFFFF & idword;
- /* If the vendor id is not valid then the slot is not populated
- * and we are done.
- */
- if (vendor == 0xFFFF)
- return(ENODEV);
-
- device = 0xFFFF & (idword >> 16);
- htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1);
-
- nfunc = 1;
- rfunc = PCIIO_FUNC_NONE;
- pfail = 0;
-
- /* NOTE: if a card claims to be multifunction
- * but only responds to config space 0, treat
- * it as a unifunction card.
- */
-
- if (htype & 0x80) { /* MULTIFUNCTION */
- for (func = 1; func < 8; ++func) {
- cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
- if (pcibr_probe_slot(bridge, cfgw, &idwords[func])) {
- pfail |= 1 << func;
- continue;
- }
- vendor = 0xFFFF & idwords[func];
- if (vendor == 0xFFFF) {
- pfail |= 1 << func;
- continue;
- }
- nfunc = func + 1;
- rfunc = 0;
- }
- cfgw = bridge->b_type0_cfg_dev[slot].l;
- }
- NEWA(pcibr_infoh, nfunc);
-
- pcibr_soft->bs_slot[slot].bss_ninfo = nfunc;
- pcibr_soft->bs_slot[slot].bss_infos = pcibr_infoh;
-
- for (func = 0; func < nfunc; ++func) {
- unsigned cmd_reg;
-
- if (func) {
- if (pfail & (1 << func))
- continue;
-
- idword = idwords[func];
- cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
-
- device = 0xFFFF & (idword >> 16);
- htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1);
- rfunc = func;
- }
- htype &= 0x7f;
- if (htype != 0x00) {
- PRINT_WARNING("%s pcibr: pci slot %d func %d has strange header type 0x%x\n",
- pcibr_soft->bs_name, slot, func, htype);
-#if defined(CONFIG_IA64_SGI_SN1)
- continue;
-#else
- nbars = 2;
- } else {
- nbars = PCI_CFG_BASE_ADDRS;
-#endif
- }
-#if DEBUG && ATTACH_DEBUG
- PRINT_NOTICE(
- "%s pcibr: pci slot %d func %d: vendor 0x%x device 0x%x",
- pcibr_soft->bs_name, slot, func, vendor, device);
-#endif
-
- pcibr_info = pcibr_device_info_new
- (pcibr_soft, slot, rfunc, vendor, device);
- conn_vhdl = pciio_device_info_register(pcibr_vhdl, &pcibr_info->f_c);
- if (func == 0)
- slotp->slot_conn = conn_vhdl;
-
- cmd_reg = cfgw[PCI_CFG_COMMAND / 4];
-
- wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
-
-#if defined(CONFIG_IA64_SGI_SN1)
- for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win)
-#else
- for (win = 0; win < nbars; ++win)
-#endif
- {
- iopaddr_t base, mask, code;
- size_t size;
-
- /*
- * GET THE BASE & SIZE OF THIS WINDOW:
- *
- * The low two or four bits of the BASE register
- * determines which address space we are in; the
- * rest is a base address. BASE registers
- * determine windows that are power-of-two sized
- * and naturally aligned, so we can get the size
- * of a window by writing all-ones to the
- * register, reading it back, and seeing which
- * bits are used for decode; the least
- * significant nonzero bit is also the size of
- * the window.
- *
- * WARNING: someone may already have allocated
- * some PCI space to this window, and in fact
- * PIO may be in process at this very moment
- * from another processor (or even from this
- * one, if we get interrupted)! So, if the BASE
- * already has a nonzero address, be generous
- * and use the LSBit of that address as the
- * size; this could overstate the window size.
- * Usually, when one card is set up, all are set
- * up; so, since we don't bitch about
- * overlapping windows, we are ok.
- *
- * UNFORTUNATELY, some cards do not clear their
- * BASE registers on reset. I have two heuristics
- * that can detect such cards: first, if the
- * decode enable is turned off for the space
- * that the window uses, we can disregard the
- * initial value. second, if the address is
- * outside the range that we use, we can disregard
- * it as well.
- *
- * This is looking very PCI generic. Except for
- * knowing how many slots and where their config
- * spaces are, this window loop and the next one
- * could probably be shared with other PCI host
- * adapters. It would be interesting to see if
- * this could be pushed up into pciio, when we
- * start supporting more PCI providers.
- */
-#ifdef LITTLE_ENDIAN
- base = wptr[((win*4)^4)/4];
-#else
- base = wptr[win];
-#endif
-
- if (base & PCI_BA_IO_SPACE) {
- /* BASE is in I/O space. */
- space = PCIIO_SPACE_IO;
- mask = -4;
- code = base & 3;
- base = base & mask;
- if (base == 0) {
- ; /* not assigned */
- } else if (!(cmd_reg & PCI_CMD_IO_SPACE)) {
- base = 0; /* decode not enabled */
- }
- } else {
- /* BASE is in MEM space. */
- space = PCIIO_SPACE_MEM;
- mask = -16;
- code = base & PCI_BA_MEM_LOCATION; /* extract BAR type */
- base = base & mask;
- if (base == 0) {
- ; /* not assigned */
- } else if (!(cmd_reg & PCI_CMD_MEM_SPACE)) {
- base = 0; /* decode not enabled */
- } else if (base & 0xC0000000) {
- base = 0; /* outside permissable range */
- } else if ((code == PCI_BA_MEM_64BIT) &&
-#ifdef LITTLE_ENDIAN
- (wptr[(((win + 1)*4)^4)/4] != 0)) {
-#else
- (wptr[win + 1] != 0)) {
-#endif /* LITTLE_ENDIAN */
- base = 0; /* outside permissable range */
- }
- }
-
- if (base != 0) { /* estimate size */
- size = base & -base;
- } else { /* calculate size */
-#ifdef LITTLE_ENDIAN
- wptr[((win*4)^4)/4] = ~0; /* turn on all bits */
- size = wptr[((win*4)^4)/4]; /* get stored bits */
-#else
- wptr[win] = ~0; /* turn on all bits */
- size = wptr[win]; /* get stored bits */
-#endif /* LITTLE_ENDIAN */
- size &= mask; /* keep addr */
- size &= -size; /* keep lsbit */
- if (size == 0)
- continue;
- }
-
- pcibr_info->f_window[win].w_space = space;
- pcibr_info->f_window[win].w_base = base;
- pcibr_info->f_window[win].w_size = size;
-
- /*
- * If this window already has PCI space
- * allocated for it, "subtract" that space from
- * our running freeblocks. Don't worry about
- * overlaps in existing allocated windows; we
- * may be overstating their sizes anyway.
- */
-
- if (base && size) {
- if (space == PCIIO_SPACE_IO) {
- pcibr_freeblock_sub(&pci_io_fb,
- &pci_io_fl,
- base, size);
- } else {
- pcibr_freeblock_sub(&pci_lo_fb,
- &pci_lo_fl,
- base, size);
- pcibr_freeblock_sub(&pci_hi_fb,
- &pci_hi_fl,
- base, size);
- }
- }
-#if defined(IOC3_VENDOR_ID_NUM) && defined(IOC3_DEVICE_ID_NUM)
- /*
- * IOC3 BASE_ADDR* BUG WORKAROUND
- *
-
- * If we write to BASE1 on the IOC3, the
- * data in BASE0 is replaced. The
- * original workaround was to remember
- * the value of BASE0 and restore it
- * when we ran off the end of the BASE
- * registers; however, a later
- * workaround was added (I think it was
- * rev 1.44) to avoid setting up
- * anything but BASE0, with the comment
- * that writing all ones to BASE1 set
- * the enable-parity-error test feature
- * in IOC3's SCR bit 14.
- *
- * So, unless we defer doing any PCI
- * space allocation until drivers
- * attach, and set up a way for drivers
- * (the IOC3 in paricular) to tell us
- * generically to keep our hands off
- * BASE registers, we gotta "know" about
- * the IOC3 here.
- *
- * Too bad the PCI folks didn't reserve the
- * all-zero value for 'no BASE here' (it is a
- * valid code for an uninitialized BASE in
- * 32-bit PCI memory space).
- */
-
- if ((vendor == IOC3_VENDOR_ID_NUM) &&
- (device == IOC3_DEVICE_ID_NUM))
- break;
-#endif
- if (code == PCI_BA_MEM_64BIT) {
- win++; /* skip upper half */
-#ifdef LITTLE_ENDIAN
- wptr[((win*4)^4)/4] = 0; /* which must be zero */
-#else
- wptr[win] = 0; /* which must be zero */
-#endif /* LITTLE_ENDIAN */
- }
- } /* next win */
- } /* next func */
-
- /* Store back the values for allocated PCI address spaces */
- PCI_ADDR_SPACE_LIMITS_STORE();
- return(0);
-}
-
-/*
- * pcibr_slot_info_free
- * Remove all the PCI infrastructural information associated
- * with a particular PCI device.
- */
-int
-pcibr_slot_info_free(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- int nfunc;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
-#if !defined(CONFIG_IA64_SGI_SN1)
- /* Clean out all the base registers */
- bridge = pcibr_soft->bs_base;
- cfgw = bridge->b_type0_cfg_dev[slot].l;
- wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
-
- for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win)
-#ifdef LITTLE_ENDIAN
- wptr[((win*4)^4)/4] = 0;
-#else
- wptr[win] = 0;
-#endif /* LITTLE_ENDIAN */
-#endif /* !CONFIG_IA64_SGI_SN1 */
-
- nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
-
- pcibr_device_info_free(pcibr_vhdl, slot);
-
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
- DELA(pcibr_infoh,nfunc);
- pcibr_soft->bs_slot[slot].bss_ninfo = 0;
-
- return(0);
-}
-
-int as_debug = 0;
-/*
- * pcibr_slot_addr_space_init
- * Reserve chunks of PCI address space as required by
- * the base registers in the card.
- */
-int
-pcibr_slot_addr_space_init(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- bridge_t *bridge;
- iopaddr_t pci_io_fb, pci_io_fl;
- iopaddr_t pci_lo_fb, pci_lo_fl;
- iopaddr_t pci_hi_fb, pci_hi_fl;
- size_t align;
- iopaddr_t mask;
- int nbars;
- int nfunc;
- int func;
- int win;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- bridge = pcibr_soft->bs_base;
-
- /* Get the current values for the allocated PCI address spaces */
- PCI_ADDR_SPACE_LIMITS_LOAD();
-
- if (as_debug)
-#ifdef LATER
- PCI_ADDR_SPACE_LIMITS_PRINT();
-#endif
- /* allocate address space,
- * for windows that have not been
- * previously assigned.
- */
- if (pcibr_soft->bs_slot[slot].has_host) {
- return(0);
- }
-
- nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
- if (nfunc < 1)
- return(EINVAL);
-
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
- if (!pcibr_infoh)
- return(EINVAL);
-
- /*
- * Try to make the DevIO windows not
- * overlap by pushing the "io" and "hi"
- * allocation areas up to the next one
- * or two megabyte bound. This also
- * keeps them from being zero.
- *
- * DO NOT do this with "pci_lo" since
- * the entire "lo" area is only a
- * megabyte, total ...
- */
- align = (slot < 2) ? 0x200000 : 0x100000;
- mask = -align;
- pci_io_fb = (pci_io_fb + align - 1) & mask;
- pci_hi_fb = (pci_hi_fb + align - 1) & mask;
-
- for (func = 0; func < nfunc; ++func) {
- cfg_p cfgw;
- cfg_p wptr;
- pciio_space_t space;
- iopaddr_t base;
- size_t size;
- cfg_p pci_cfg_cmd_reg_p;
- unsigned pci_cfg_cmd_reg;
- unsigned pci_cfg_cmd_reg_add = 0;
-
- pcibr_info = pcibr_infoh[func];
-
- if (!pcibr_info)
- continue;
-
- if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
- continue;
-
- cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
- wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
-
-#if defined(CONFIG_IA64_SGI_SN1)
- nbars = PCI_CFG_BASE_ADDRS;
-#else
- if ((do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1) & 0x7f) != 0)
- nbars = 2;
- else
- nbars = PCI_CFG_BASE_ADDRS;
-#endif
-
- for (win = 0; win < nbars; ++win) {
-
- space = pcibr_info->f_window[win].w_space;
- base = pcibr_info->f_window[win].w_base;
- size = pcibr_info->f_window[win].w_size;
-
- if (size < 1)
- continue;
-
- if (base >= size) {
-#if DEBUG && PCI_DEBUG
- printk("pcibr: slot %d func %d window %d is in %d[0x%x..0x%x], alloc by prom\n",
- slot, func, win, space, base, base + size - 1);
-#endif
- continue; /* already allocated */
- }
- align = size; /* ie. 0x00001000 */
- if (align < _PAGESZ)
- align = _PAGESZ; /* ie. 0x00004000 */
- mask = -align; /* ie. 0xFFFFC000 */
-
- switch (space) {
- case PCIIO_SPACE_IO:
- base = (pci_io_fb + align - 1) & mask;
- if ((base + size) > pci_io_fl) {
- base = 0;
- break;
- }
- pci_io_fb = base + size;
- break;
-
- case PCIIO_SPACE_MEM:
-#ifdef LITTLE_ENDIAN
- if ((wptr[((win*4)^4)/4] & PCI_BA_MEM_LOCATION) ==
-#else
- if ((wptr[win] & PCI_BA_MEM_LOCATION) ==
-#endif /* LITTLE_ENDIAN */
- PCI_BA_MEM_1MEG) {
- /* allocate from 20-bit PCI space */
- base = (pci_lo_fb + align - 1) & mask;
- if ((base + size) > pci_lo_fl) {
- base = 0;
- break;
- }
- pci_lo_fb = base + size;
- } else {
- /* allocate from 32-bit or 64-bit PCI space */
- base = (pci_hi_fb + align - 1) & mask;
- if ((base + size) > pci_hi_fl) {
- base = 0;
- break;
- }
- pci_hi_fb = base + size;
- }
- break;
-
- default:
- base = 0;
-#if DEBUG && PCI_DEBUG
- printk("pcibr: slot %d window %d had bad space code %d\n",
- slot, win, space);
-#endif
- }
- pcibr_info->f_window[win].w_base = base;
-#ifdef LITTLE_ENDIAN
- wptr[((win*4)^4)/4] = base;
-#if DEBUG && PCI_DEBUG
- printk("Setting base address 0x%p base 0x%x\n", &(wptr[((win*4)^4)/4]), base);
-#endif
-#else
- wptr[win] = base;
-#endif /* LITTLE_ENDIAN */
-
-#if DEBUG && PCI_DEBUG
- if (base >= size)
- printk("pcibr: slot %d func %d window %d is in %d [0x%x..0x%x], alloc by pcibr\n",
- slot, func, win, space, base, base + size - 1);
- else
- printk("pcibr: slot %d func %d window %d, unable to alloc 0x%x in 0x%p\n",
- slot, func, win, size, space);
-#endif
- } /* next base */
-
- /*
- * Allocate space for the EXPANSION ROM
- * NOTE: DO NOT DO THIS ON AN IOC3,
- * as it blows the system away.
- */
- base = size = 0;
- if ((pcibr_soft->bs_slot[slot].bss_vendor_id != IOC3_VENDOR_ID_NUM) ||
- (pcibr_soft->bs_slot[slot].bss_device_id != IOC3_DEVICE_ID_NUM)) {
-
- wptr = cfgw + PCI_EXPANSION_ROM / 4;
-#ifdef LITTLE_ENDIAN
- wptr[1] = 0xFFFFF000;
- mask = wptr[1];
-#else
- *wptr = 0xFFFFF000;
- mask = *wptr;
-#endif /* LITTLE_ENDIAN */
- if (mask & 0xFFFFF000) {
- size = mask & -mask;
- align = size;
- if (align < _PAGESZ)
- align = _PAGESZ;
- mask = -align;
- base = (pci_hi_fb + align - 1) & mask;
- if ((base + size) > pci_hi_fl)
- base = size = 0;
- else {
- pci_hi_fb = base + size;
-#ifdef LITTLE_ENDIAN
- wptr[1] = base;
-#else
- *wptr = base;
-#endif /* LITTLE_ENDIAN */
-#if DEBUG && PCI_DEBUG
- printk("%s/%d ROM in 0x%lx..0x%lx (alloc by pcibr)\n",
- pcibr_soft->bs_name, slot,
- base, base + size - 1);
-#endif
- }
- }
- }
- pcibr_info->f_rbase = base;
- pcibr_info->f_rsize = size;
-
- /*
- * if necessary, update the board's
- * command register to enable decoding
- * in the windows we added.
- *
- * There are some bits we always want to
- * be sure are set.
- */
- pci_cfg_cmd_reg_add |= PCI_CMD_IO_SPACE;
-
- /*
- * The Adaptec 1160 FC Controller WAR #767995:
- * The part incorrectly ignores the upper 32 bits of a 64 bit
- * address when decoding references to it's registers so to
- * keep it from responding to a bus cycle that it shouldn't
- * we only use I/O space to get at it's registers. Don't
- * enable memory space accesses on that PCI device.
- */
- #define FCADP_VENDID 0x9004 /* Adaptec Vendor ID from fcadp.h */
- #define FCADP_DEVID 0x1160 /* Adaptec 1160 Device ID from fcadp.h */
-
- if ((pcibr_info->f_vendor != FCADP_VENDID) ||
- (pcibr_info->f_device != FCADP_DEVID))
- pci_cfg_cmd_reg_add |= PCI_CMD_MEM_SPACE;
-
- pci_cfg_cmd_reg_add |= PCI_CMD_BUS_MASTER;
-
- pci_cfg_cmd_reg_p = cfgw + PCI_CFG_COMMAND / 4;
- pci_cfg_cmd_reg = *pci_cfg_cmd_reg_p;
-#if PCI_FBBE /* XXX- check here to see if dev can do fast-back-to-back */
- if (!((pci_cfg_cmd_reg >> 16) & PCI_STAT_F_BK_BK_CAP))
- fast_back_to_back_enable = 0;
-#endif
- pci_cfg_cmd_reg &= 0xFFFF;
- if (pci_cfg_cmd_reg_add & ~pci_cfg_cmd_reg)
- *pci_cfg_cmd_reg_p = pci_cfg_cmd_reg | pci_cfg_cmd_reg_add;
-
- } /* next func */
-
- /* Now that we have allocated new chunks of PCI address spaces to this
- * card we need to update the bookkeeping values which indicate
- * the current PCI address space allocations.
- */
- PCI_ADDR_SPACE_LIMITS_STORE();
- return(0);
-}
-
-/*
- * pcibr_slot_device_init
- * Setup the device register in the bridge for this PCI slot.
- */
-int
-pcibr_slot_device_init(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- bridgereg_t devreg;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- bridge = pcibr_soft->bs_base;
-
- /*
- * Adjustments to Device(x)
- * and init of bss_device shadow
- */
- devreg = bridge->b_device[slot].reg;
- devreg &= ~BRIDGE_DEV_PAGE_CHK_DIS;
- devreg |= BRIDGE_DEV_COH | BRIDGE_DEV_VIRTUAL_EN;
-#ifdef LITTLE_ENDIAN
- devreg |= BRIDGE_DEV_DEV_SWAP;
-#endif
- pcibr_soft->bs_slot[slot].bss_device = devreg;
- bridge->b_device[slot].reg = devreg;
-
-#if DEBUG && PCI_DEBUG
- printk("pcibr Device(%d): 0x%lx\n", slot, bridge->b_device[slot].reg);
-#endif
-
-#if DEBUG && PCI_DEBUG
- printk("pcibr: PCI space allocation done.\n");
-#endif
-
- return(0);
-}
-
-/*
- * pcibr_slot_guest_info_init
- * Setup the host/guest relations for a PCI slot.
- */
-int
-pcibr_slot_guest_info_init(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- pcibr_soft_slot_t slotp;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- slotp = &pcibr_soft->bs_slot[slot];
-
- /* create info and verticies for guest slots;
- * for compatibilitiy macros, create info
- * for even unpopulated slots (but do not
- * build verticies for them).
- */
- if (pcibr_soft->bs_slot[slot].bss_ninfo < 1) {
- NEWA(pcibr_infoh, 1);
- pcibr_soft->bs_slot[slot].bss_ninfo = 1;
- pcibr_soft->bs_slot[slot].bss_infos = pcibr_infoh;
-
- pcibr_info = pcibr_device_info_new
- (pcibr_soft, slot, PCIIO_FUNC_NONE,
- PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE);
-
- if (pcibr_soft->bs_slot[slot].has_host) {
- slotp->slot_conn = pciio_device_info_register
- (pcibr_vhdl, &pcibr_info->f_c);
- }
- }
-
- /* generate host/guest relations
- */
- if (pcibr_soft->bs_slot[slot].has_host) {
- int host = pcibr_soft->bs_slot[slot].host_slot;
- pcibr_soft_slot_t host_slotp = &pcibr_soft->bs_slot[host];
-
- hwgraph_edge_add(slotp->slot_conn,
- host_slotp->slot_conn,
- EDGE_LBL_HOST);
-
- /* XXX- only gives us one guest edge per
- * host. If/when we have a host with more than
- * one guest, we will need to figure out how
- * the host finds all its guests, and sorts
- * out which one is which.
- */
- hwgraph_edge_add(host_slotp->slot_conn,
- slotp->slot_conn,
- EDGE_LBL_GUEST);
- }
-
- return(0);
-}
-
-/*
- * pcibr_slot_initial_rrb_alloc
- * Allocate a default number of rrbs for this slot on
- * the two channels. This is dictated by the rrb allocation
- * strategy routine defined per platform.
- */
-
-int
-pcibr_slot_initial_rrb_alloc(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- bridge_t *bridge;
- int c0, c1;
- int r;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- bridge = pcibr_soft->bs_base;
-
- /* How may RRBs are on this slot?
- */
- c0 = do_pcibr_rrb_count_valid(bridge, slot);
- c1 = do_pcibr_rrb_count_valid(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL);
-
-#if PCIBR_RRB_DEBUG
- printk("pcibr_attach: slot %d started with %d+%d\n", slot, c0, c1);
-#endif
-
- /* Do we really need any?
- */
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
- pcibr_info = pcibr_infoh[0];
- if ((pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) &&
- !pcibr_soft->bs_slot[slot].has_host) {
- if (c0 > 0)
- do_pcibr_rrb_free(bridge, slot, c0);
- if (c1 > 0)
- do_pcibr_rrb_free(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL, c1);
- pcibr_soft->bs_rrb_valid[slot] = 0x1000;
- pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = 0x1000;
- return(ENODEV);
- }
-
- pcibr_soft->bs_rrb_avail[slot & 1] -= c0 + c1;
- pcibr_soft->bs_rrb_valid[slot] = c0;
- pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = c1;
-
- pcibr_soft->bs_rrb_avail[0] = do_pcibr_rrb_count_avail(bridge, 0);
- pcibr_soft->bs_rrb_avail[1] = do_pcibr_rrb_count_avail(bridge, 1);
-
- r = 3 - (c0 + c1);
-
- if (r > 0) {
- pcibr_soft->bs_rrb_res[slot] = r;
- pcibr_soft->bs_rrb_avail[slot & 1] -= r;
- }
-
-#if PCIBR_RRB_DEBUG
- printk("\t%d+%d+%d",
- 0xFFF & pcibr_soft->bs_rrb_valid[slot],
- 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
- printk("\n");
-#endif
-
- return(0);
-}
-
-/*
- * pcibr_slot_call_device_attach
- * This calls the associated driver attach routine for the PCI
- * card in this slot.
- */
-int
-pcibr_slot_call_device_attach(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot,
- int drv_flags)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- async_attach_t aa = NULL;
- int func;
- devfs_handle_t xconn_vhdl,conn_vhdl;
- int nfunc;
- int error_func;
- int error_slot = 0;
- int error = ENODEV;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
-
- if (pcibr_soft->bs_slot[slot].has_host) {
- return(EPERM);
- }
-
- xconn_vhdl = pcibr_soft->bs_conn;
- aa = async_attach_get_info(xconn_vhdl);
-
- nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
-
- for (func = 0; func < nfunc; ++func) {
-
- pcibr_info = pcibr_infoh[func];
-
- if (!pcibr_info)
- continue;
-
- if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
- continue;
-
- conn_vhdl = pcibr_info->f_vertex;
-
- /* If the PCI device has been disabled in the prom,
- * do not set it up for driver attach. NOTE: usrpci
- * and pciba will not "see" this connection point!
- */
- if (device_admin_info_get(conn_vhdl, ADMIN_LBL_DISABLED)) {
-#ifdef SUPPORT_PRINTING_V_FORMAT
- PRINT_WARNING("pcibr_slot_call_device_attach: %v disabled\n",
- conn_vhdl);
-#endif
- continue;
- }
-#ifdef LATER
- /*
- * Activate if and when we support cdl.
- */
- if (aa)
- async_attach_add_info(conn_vhdl, aa);
-#endif /* LATER */
-
- error_func = pciio_device_attach(conn_vhdl, drv_flags);
-
- pcibr_info->f_att_det_error = error_func;
-
- if (error_func)
- error_slot = error_func;
-
- error = error_slot;
-
- } /* next func */
-
- if (error) {
- if ((error != ENODEV) && (error != EUNATCH))
- pcibr_soft->bs_slot[slot].slot_status |= SLOT_STARTUP_INCMPLT;
- } else {
- pcibr_soft->bs_slot[slot].slot_status |= SLOT_STARTUP_CMPLT;
- }
-
- return(error);
-}
-
-/*
- * pcibr_slot_call_device_detach
- * This calls the associated driver detach routine for the PCI
- * card in this slot.
- */
-int
-pcibr_slot_call_device_detach(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot,
- int drv_flags)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- int func;
- devfs_handle_t conn_vhdl = GRAPH_VERTEX_NONE;
- int nfunc;
- int error_func;
- int error_slot = 0;
- int error = ENODEV;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
-
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(EINVAL);
-
- if (pcibr_soft->bs_slot[slot].has_host)
- return(EPERM);
-
- /* Make sure that we do not detach a system critical function vertex */
- if(pcibr_is_slot_sys_critical(pcibr_vhdl, slot))
- return(EPERM);
-
- nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
-
- for (func = 0; func < nfunc; ++func) {
-
- pcibr_info = pcibr_infoh[func];
-
- if (!pcibr_info)
- continue;
-
- if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
- continue;
-
- conn_vhdl = pcibr_info->f_vertex;
-
- error_func = pciio_device_detach(conn_vhdl, drv_flags);
-
- pcibr_info->f_att_det_error = error_func;
-
- if (error_func)
- error_slot = error_func;
-
- error = error_slot;
-
- } /* next func */
-
- pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK;
-
- if (error) {
- if ((error != ENODEV) && (error != EUNATCH))
- pcibr_soft->bs_slot[slot].slot_status |= SLOT_SHUTDOWN_INCMPLT;
- } else {
- if (conn_vhdl != GRAPH_VERTEX_NONE)
- pcibr_device_unregister(conn_vhdl);
- pcibr_soft->bs_slot[slot].slot_status |= SLOT_SHUTDOWN_CMPLT;
- }
-
- return(error);
-}
-
-/*
- * pcibr_slot_detach
- * This is a place holder routine to keep track of all the
- * slot-specific freeing that needs to be done.
- */
-int
-pcibr_slot_detach(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot,
- int drv_flags)
-{
- int error;
-
- /* Call the device detach function */
- error = (pcibr_slot_call_device_detach(pcibr_vhdl, slot, drv_flags));
- return (error);
-
-}
-
-/*
- * pcibr_is_slot_sys_critical
- * Check slot for any functions that are system critical.
- * Return 1 if any are system critical or 0 otherwise.
- *
- * This function will always return 0 when called by
- * pcibr_attach() because the system critical vertices
- * have not yet been set in the hwgraph.
- */
-int
-pcibr_is_slot_sys_critical(devfs_handle_t pcibr_vhdl,
- pciio_slot_t slot)
-{
- pcibr_soft_t pcibr_soft;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- devfs_handle_t conn_vhdl = GRAPH_VERTEX_NONE;
- int nfunc;
- int func;
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
- return(0);
-
- nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
- pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
-
- for (func = 0; func < nfunc; ++func) {
-
- pcibr_info = pcibr_infoh[func];
- if (!pcibr_info)
- continue;
-
- if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
- continue;
-
- conn_vhdl = pcibr_info->f_vertex;
- if (is_sys_critical_vertex(conn_vhdl)) {
-#if defined(SUPPORT_PRINTING_V_FORMAT)
- PRINT_WARNING("%v is a system critical device vertex\n", conn_vhdl);
-#else
- PRINT_WARNING("%p is a system critical device vertex\n", conn_vhdl);
-#endif
- return(1);
- }
-
- }
-
- return(0);
-}
-
-/*
- * pcibr_device_unregister
- * This frees up any hardware resources reserved for this PCI device
- * and removes any PCI infrastructural information setup for it.
- * This is usually used at the time of shutting down of the PCI card.
- */
-int
-pcibr_device_unregister(devfs_handle_t pconn_vhdl)
-{
- pciio_info_t pciio_info;
- devfs_handle_t pcibr_vhdl;
- pciio_slot_t slot;
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- int error_call;
- int error = 0;
-
- pciio_info = pciio_info_get(pconn_vhdl);
-
- pcibr_vhdl = pciio_info_master_get(pciio_info);
- slot = pciio_info_slot_get(pciio_info);
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- bridge = pcibr_soft->bs_base;
-
- /* Clear all the hardware xtalk resources for this device */
- xtalk_widgetdev_shutdown(pcibr_soft->bs_conn, slot);
-
- /* Flush all the rrbs */
- pcibr_rrb_flush(pconn_vhdl);
-
- /* Free the rrbs allocated to this slot */
- error_call = do_pcibr_rrb_free(bridge, slot,
- pcibr_soft->bs_rrb_valid[slot] +
- pcibr_soft->bs_rrb_valid[slot +
- PCIBR_RRB_SLOT_VIRTUAL]);
-
- if (error_call)
- error = ERANGE;
-
- pcibr_soft->bs_rrb_valid[slot] = 0;
- pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = 0;
- pcibr_soft->bs_rrb_res[slot] = 0;
-
- /* Flush the write buffers !! */
- error_call = pcibr_wrb_flush(pconn_vhdl);
-
- if (error_call)
- error = error_call;
-
- /* Clear the information specific to the slot */
- error_call = pcibr_slot_info_free(pcibr_vhdl, slot);
-
- if (error_call)
- error = error_call;
-
- return(error);
-
-}
-
-/*
- * build a convenience link path in the
- * form of ".../<iobrick>/bus/<busnum>"
- *
- * returns 1 on success, 0 otherwise
- *
- * depends on hwgraph separator == '/'
- */
-int
-pcibr_bus_cnvlink(devfs_handle_t f_c, int slot)
-{
- char dst[MAXDEVNAME];
- char *dp = dst;
- char *cp, *xp;
- int widgetnum;
- char pcibus[8];
- devfs_handle_t nvtx, svtx;
- int rv;
-
-#if DEBUG
- printk("pcibr_bus_cnvlink: slot= %d f_c= %p\n",
- slot, f_c);
- {
- int pos;
- char dname[256];
- pos = devfs_generate_path(f_c, dname, 256);
- printk("%s : path= %s\n", __FUNCTION__, &dname[pos]);
- }
-#endif
-
- if (GRAPH_SUCCESS != hwgraph_vertex_name_get(f_c, dst, MAXDEVNAME))
- return 0;
-
- /* dst example == /hw/module/001c02/Pbrick/xtalk/8/pci/direct */
-
- /* find the widget number */
- xp = strstr(dst, "/"EDGE_LBL_XTALK"/");
- if (xp == NULL)
- return 0;
- widgetnum = atoi(xp+7);
- if (widgetnum < XBOW_PORT_8 || widgetnum > XBOW_PORT_F)
- return 0;
-
- /* remove "/pci/direct" from path */
- cp = strstr(dst, "/" EDGE_LBL_PCI "/" "direct");
- if (cp == NULL)
- return 0;
- *cp = (char)NULL;
-
- /* get the vertex for the widget */
- if (GRAPH_SUCCESS != hwgraph_traverse(NULL, dp, &svtx))
- return 0;
-
- *xp = (char)NULL; /* remove "/xtalk/..." from path */
-
- /* dst example now == /hw/module/001c02/Pbrick */
-
- /* get the bus number */
- strcat(dst, "/bus");
- sprintf(pcibus, "%d", p_busnum[widgetnum]);
-
- /* link to bus to widget */
- rv = hwgraph_path_add(NULL, dp, &nvtx);
- if (GRAPH_SUCCESS == rv)
- rv = hwgraph_edge_add(nvtx, svtx, pcibus);
-
- return (rv == GRAPH_SUCCESS);
-}
-
-
-/*
- * pcibr_attach: called every time the crosstalk
- * infrastructure is asked to initialize a widget
- * that matches the part number we handed to the
- * registration routine above.
- */
-/*ARGSUSED */
-int
-pcibr_attach(devfs_handle_t xconn_vhdl)
-{
- /* REFERENCED */
- graph_error_t rc;
- devfs_handle_t pcibr_vhdl;
- devfs_handle_t ctlr_vhdl;
- bridge_t *bridge = NULL;
- bridgereg_t id;
- int rev;
- pcibr_soft_t pcibr_soft;
- pcibr_info_t pcibr_info;
- xwidget_info_t info;
- xtalk_intr_t xtalk_intr;
- device_desc_t dev_desc;
- int slot;
- int ibit;
- devfs_handle_t noslot_conn;
- char devnm[MAXDEVNAME], *s;
- pcibr_hints_t pcibr_hints;
- bridgereg_t b_int_enable;
- unsigned rrb_fixed = 0;
-
- iopaddr_t pci_io_fb, pci_io_fl;
- iopaddr_t pci_lo_fb, pci_lo_fl;
- iopaddr_t pci_hi_fb, pci_hi_fl;
-
- int spl_level;
-#ifdef LATER
- char *nicinfo = (char *)0;
-#endif
-
-#if PCI_FBBE
- int fast_back_to_back_enable;
-#endif
- l1sc_t *scp;
- nasid_t nasid;
-
- async_attach_t aa = NULL;
-
- aa = async_attach_get_info(xconn_vhdl);
-
-#if DEBUG && ATTACH_DEBUG
- printk("pcibr_attach: xconn_vhdl= %p\n", xconn_vhdl);
- {
- int pos;
- char dname[256];
- pos = devfs_generate_path(xconn_vhdl, dname, 256);
- printk("%s : path= %s \n", __FUNCTION__, &dname[pos]);
- }
-#endif
-
- /* Setup the PRB for the bridge in CONVEYOR BELT
- * mode. PRBs are setup in default FIRE-AND-FORGET
- * mode during the initialization.
- */
- hub_device_flags_set(xconn_vhdl, HUB_PIO_CONVEYOR);
-
- bridge = (bridge_t *)
- xtalk_piotrans_addr(xconn_vhdl, NULL,
- 0, sizeof(bridge_t), 0);
-
-#ifndef MEDUSA_HACK
- if ((bridge->b_wid_stat & BRIDGE_STAT_PCI_GIO_N) == 0)
- return -1; /* someone else handles GIO bridges. */
-#endif
-
-#ifdef BRINGUP
- if (XWIDGET_PART_REV_NUM(bridge->b_wid_id) == XBRIDGE_PART_REV_A)
- NeedXbridgeSwap = 1;
-#endif
-
- /*
- * Create the vertex for the PCI bus, which we
- * 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.
- */
- rc = hwgraph_path_add(xconn_vhdl, EDGE_LBL_PCI, &pcibr_vhdl);
- ASSERT(rc == GRAPH_SUCCESS);
-
- ctlr_vhdl = NULL;
- ctlr_vhdl = hwgraph_register(pcibr_vhdl, EDGE_LBL_CONTROLLER,
- 0, DEVFS_FL_AUTO_DEVNUM,
- 0, 0,
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
- &pcibr_fops, NULL);
-
- ASSERT(ctlr_vhdl != NULL);
-
- /*
- * decode the nic, and hang its stuff off our
- * connection point where other drivers can get
- * at it.
- */
-#ifdef LATER
- nicinfo = BRIDGE_VERTEX_MFG_INFO(xconn_vhdl, (nic_data_t) & bridge->b_nic);
-#endif
-
- /*
- * Get the hint structure; if some NIC callback
- * marked this vertex as "hands-off" then we
- * just return here, before doing anything else.
- */
- pcibr_hints = pcibr_hints_get(xconn_vhdl, 0);
-
- if (pcibr_hints && pcibr_hints->ph_hands_off)
- return -1; /* generic operations disabled */
-
- id = bridge->b_wid_id;
- rev = XWIDGET_PART_REV_NUM(id);
-
- hwgraph_info_add_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, (arbitrary_info_t) rev);
-
- /*
- * allocate soft state structure, fill in some
- * fields, and hook it up to our vertex.
- */
- NEW(pcibr_soft);
- BZERO(pcibr_soft, sizeof *pcibr_soft);
- pcibr_soft_set(pcibr_vhdl, pcibr_soft);
-
- pcibr_soft->bs_conn = xconn_vhdl;
- pcibr_soft->bs_vhdl = pcibr_vhdl;
- pcibr_soft->bs_base = bridge;
- pcibr_soft->bs_rev_num = rev;
- pcibr_soft->bs_intr_bits = pcibr_intr_bits;
- if (is_xbridge(bridge)) {
- pcibr_soft->bs_int_ate_size = XBRIDGE_INTERNAL_ATES;
- pcibr_soft->bs_xbridge = 1;
- } else {
- pcibr_soft->bs_int_ate_size = BRIDGE_INTERNAL_ATES;
- pcibr_soft->bs_xbridge = 0;
- }
-
- nasid = NASID_GET(bridge);
- scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
- pcibr_soft->bs_l1sc = scp;
- pcibr_soft->bs_moduleid = iobrick_module_get(scp);
- pcibr_soft->bsi_err_intr = 0;
-
- /* Bridges up through REV C
- * are unable to set the direct
- * byteswappers to BYTE_STREAM.
- */
- if (pcibr_soft->bs_rev_num <= BRIDGE_PART_REV_C) {
- pcibr_soft->bs_pio_end_io = PCIIO_WORD_VALUES;
- pcibr_soft->bs_pio_end_mem = PCIIO_WORD_VALUES;
- }
-#if PCIBR_SOFT_LIST
- {
- pcibr_list_p self;
-
- NEW(self);
- self->bl_soft = pcibr_soft;
- self->bl_vhdl = pcibr_vhdl;
- self->bl_next = pcibr_list;
- self->bl_next = swap_ptr((void **) &pcibr_list, (void *)self);
- }
-#endif
-
- /*
- * get the name of this bridge vertex and keep the info. Use this
- * only where it is really needed now: like error interrupts.
- */
- s = dev_to_name(pcibr_vhdl, devnm, MAXDEVNAME);
- pcibr_soft->bs_name = kmalloc(strlen(s) + 1, GFP_KERNEL);
- strcpy(pcibr_soft->bs_name, s);
-
-#if SHOW_REVS || DEBUG
-#if !DEBUG
- if (kdebug)
-#endif
- printk("%sBridge ASIC: rev %s (code=0x%x) at %s\n",
- is_xbridge(bridge) ? "X" : "",
- (rev == BRIDGE_PART_REV_A) ? "A" :
- (rev == BRIDGE_PART_REV_B) ? "B" :
- (rev == BRIDGE_PART_REV_C) ? "C" :
- (rev == BRIDGE_PART_REV_D) ? "D" :
- (rev == XBRIDGE_PART_REV_A) ? "A" :
- (rev == XBRIDGE_PART_REV_B) ? "B" :
- "unknown",
- rev, pcibr_soft->bs_name);
-#endif
-
- info = xwidget_info_get(xconn_vhdl);
- pcibr_soft->bs_xid = xwidget_info_id_get(info);
- pcibr_soft->bs_master = xwidget_info_master_get(info);
- pcibr_soft->bs_mxid = xwidget_info_masterid_get(info);
-
- /*
- * Init bridge lock.
- */
- spin_lock_init(&pcibr_soft->bs_lock);
-
- /*
- * If we have one, process the hints structure.
- */
- if (pcibr_hints) {
- rrb_fixed = pcibr_hints->ph_rrb_fixed;
-
- pcibr_soft->bs_rrb_fixed = rrb_fixed;
-
- if (pcibr_hints->ph_intr_bits)
- pcibr_soft->bs_intr_bits = pcibr_hints->ph_intr_bits;
-
- for (slot = 0; slot < 8; ++slot) {
- int hslot = pcibr_hints->ph_host_slot[slot] - 1;
-
- if (hslot < 0) {
- pcibr_soft->bs_slot[slot].host_slot = slot;
- } else {
- pcibr_soft->bs_slot[slot].has_host = 1;
- pcibr_soft->bs_slot[slot].host_slot = hslot;
- }
- }
- }
- /*
- * set up initial values for state fields
- */
- for (slot = 0; slot < 8; ++slot) {
- pcibr_soft->bs_slot[slot].bss_devio.bssd_space = PCIIO_SPACE_NONE;
- pcibr_soft->bs_slot[slot].bss_d64_base = PCIBR_D64_BASE_UNSET;
- pcibr_soft->bs_slot[slot].bss_d32_base = PCIBR_D32_BASE_UNSET;
- pcibr_soft->bs_slot[slot].bss_ext_ates_active = ATOMIC_INIT(0);
- }
-
- for (ibit = 0; ibit < 8; ++ibit) {
- pcibr_soft->bs_intr[ibit].bsi_xtalk_intr = 0;
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_soft = pcibr_soft;
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_list = NULL;
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_stat =
- &(bridge->b_int_status);
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_hdlrcnt = 0;
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_shared = 0;
- pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_wrap.iw_connected = 0;
- }
-
- /*
- * connect up our error handler
- */
- xwidget_error_register(xconn_vhdl, pcibr_error_handler, pcibr_soft);
-
- /*
- * Initialize various Bridge registers.
- */
-
- /*
- * On pre-Rev.D bridges, set the PCI_RETRY_CNT
- * to zero to avoid dropping stores. (#475347)
- */
- if (rev < BRIDGE_PART_REV_D)
- bridge->b_bus_timeout &= ~BRIDGE_BUS_PCI_RETRY_MASK;
-
- /*
- * Clear all pending interrupts.
- */
- bridge->b_int_rst_stat = (BRIDGE_IRR_ALL_CLR);
-
- /*
- * Until otherwise set up,
- * assume all interrupts are
- * from slot 7.
- */
- bridge->b_int_device = (uint32_t) 0xffffffff;
-
- {
- bridgereg_t dirmap;
- paddr_t paddr;
- iopaddr_t xbase;
- xwidgetnum_t xport;
- iopaddr_t offset;
- int num_entries = 0;
- int entry;
- cnodeid_t cnodeid;
- nasid_t nasid;
- char *node_val;
- devfs_handle_t node_vhdl;
- char vname[MAXDEVNAME];
-
- /* Set the Bridge's 32-bit PCI to XTalk
- * Direct Map register to the most useful
- * value we can determine. Note that we
- * must use a single xid for all of:
- * direct-mapped 32-bit DMA accesses
- * direct-mapped 64-bit DMA accesses
- * DMA accesses through the PMU
- * interrupts
- * This is the only way to guarantee that
- * completion interrupts will reach a CPU
- * after all DMA data has reached memory.
- * (Of course, there may be a few special
- * drivers/controllers that explicitly manage
- * this ordering problem.)
- */
-
- cnodeid = 0; /* default node id */
- /*
- * Determine the base address node id to be used for all 32-bit
- * Direct Mapping I/O. The default is node 0, but this can be changed
- * via a DEVICE_ADMIN directive and the PCIBUS_DMATRANS_NODE
- * attribute in the irix.sm config file. A device driver can obtain
- * this node value via a call to pcibr_get_dmatrans_node().
- */
- node_val = device_admin_info_get(pcibr_vhdl, ADMIN_LBL_DMATRANS_NODE);
- if (node_val != NULL) {
- node_vhdl = hwgraph_path_to_vertex(node_val);
- if (node_vhdl != GRAPH_VERTEX_NONE) {
- cnodeid = nodevertex_to_cnodeid(node_vhdl);
- }
- if ((node_vhdl == GRAPH_VERTEX_NONE) || (cnodeid == CNODEID_NONE)) {
- cnodeid = 0;
- vertex_to_name(pcibr_vhdl, vname, sizeof(vname));
- PRINT_WARNING( "Invalid hwgraph node path specified:\n DEVICE_ADMIN: %s %s=%s\n",
- vname, ADMIN_LBL_DMATRANS_NODE, node_val);
- }
- }
- nasid = COMPACT_TO_NASID_NODEID(cnodeid);
- paddr = NODE_OFFSET(nasid) + 0;
-
- /* currently, we just assume that if we ask
- * for a DMA mapping to "zero" the XIO
- * host will transmute this into a request
- * for the lowest hunk of memory.
- */
- xbase = xtalk_dmatrans_addr(xconn_vhdl, 0,
- paddr, _PAGESZ, 0);
-
- if (xbase != XIO_NOWHERE) {
- if (XIO_PACKED(xbase)) {
- xport = XIO_PORT(xbase);
- xbase = XIO_ADDR(xbase);
- } else
- xport = pcibr_soft->bs_mxid;
-
- offset = xbase & ((1ull << BRIDGE_DIRMAP_OFF_ADDRSHFT) - 1ull);
- xbase >>= BRIDGE_DIRMAP_OFF_ADDRSHFT;
-
- dirmap = xport << BRIDGE_DIRMAP_W_ID_SHFT;
-
- if (xbase)
- dirmap |= BRIDGE_DIRMAP_OFF & xbase;
- else if (offset >= (512 << 20))
- dirmap |= BRIDGE_DIRMAP_ADD512;
-
- bridge->b_dir_map = dirmap;
- }
- /*
- * Set bridge's idea of page size according to the system's
- * idea of "IO page size". TBD: The idea of IO page size
- * should really go away.
- */
- /*
- * ensure that we write and read without any interruption.
- * The read following the write is required for the Bridge war
- */
- spl_level = splhi();
-#if IOPGSIZE == 4096
- bridge->b_wid_control &= ~BRIDGE_CTRL_PAGE_SIZE;
-#elif IOPGSIZE == 16384
- bridge->b_wid_control |= BRIDGE_CTRL_PAGE_SIZE;
-#else
- <<<Unable to deal with IOPGSIZE >>>;
-#endif
- bridge->b_wid_control; /* inval addr bug war */
- splx(spl_level);
-
- /* Initialize internal mapping entries */
- for (entry = 0; entry < pcibr_soft->bs_int_ate_size; entry++)
- bridge->b_int_ate_ram[entry].wr = 0;
-
- /*
- * Determine if there's external mapping SSRAM on this
- * bridge. Set up Bridge control register appropriately,
- * inititlize SSRAM, and set software up to manage RAM
- * entries as an allocatable resource.
- *
- * Currently, we just use the rm* routines to manage ATE
- * allocation. We should probably replace this with a
- * Best Fit allocator.
- *
- * For now, if we have external SSRAM, avoid using
- * the internal ssram: we can't turn PREFETCH on
- * when we use the internal SSRAM; and besides,
- * this also guarantees that no allocation will
- * straddle the internal/external line, so we
- * can increment ATE write addresses rather than
- * recomparing against BRIDGE_INTERNAL_ATES every
- * time.
- */
- if (is_xbridge(bridge))
- num_entries = 0;
- else
- num_entries = pcibr_init_ext_ate_ram(bridge);
-
- /* we always have 128 ATEs (512 for Xbridge) inside the chip
- * even if disabled for debugging.
- */
- pcibr_soft->bs_int_ate_map = rmallocmap(pcibr_soft->bs_int_ate_size);
- pcibr_ate_free(pcibr_soft, 0, pcibr_soft->bs_int_ate_size);
-#if PCIBR_ATE_DEBUG
- printk("pcibr_attach: %d INTERNAL ATEs\n", pcibr_soft->bs_int_ate_size);
-#endif
-
- if (num_entries > pcibr_soft->bs_int_ate_size) {
-#if PCIBR_ATE_NOTBOTH /* for debug -- forces us to use external ates */
- printk("pcibr_attach: disabling internal ATEs.\n");
- pcibr_ate_alloc(pcibr_soft, pcibr_soft->bs_int_ate_size);
-#endif
- pcibr_soft->bs_ext_ate_map = rmallocmap(num_entries);
- pcibr_ate_free(pcibr_soft, pcibr_soft->bs_int_ate_size,
- num_entries - pcibr_soft->bs_int_ate_size);
-#if PCIBR_ATE_DEBUG
- printk("pcibr_attach: %d EXTERNAL ATEs\n",
- num_entries - pcibr_soft->bs_int_ate_size);
-#endif
- }
- }
-
- {
- bridgereg_t dirmap;
- iopaddr_t xbase;
-
- /*
- * now figure the *real* xtalk base address
- * that dirmap sends us to.
- */
- dirmap = bridge->b_dir_map;
- if (dirmap & BRIDGE_DIRMAP_OFF)
- xbase = (iopaddr_t)(dirmap & BRIDGE_DIRMAP_OFF)
- << BRIDGE_DIRMAP_OFF_ADDRSHFT;
- else if (dirmap & BRIDGE_DIRMAP_ADD512)
- xbase = 512 << 20;
- else
- xbase = 0;
-
- pcibr_soft->bs_dir_xbase = xbase;
-
- /* it is entirely possible that we may, at this
- * point, have our dirmap pointing somewhere
- * other than our "master" port.
- */
- pcibr_soft->bs_dir_xport =
- (dirmap & BRIDGE_DIRMAP_W_ID) >> BRIDGE_DIRMAP_W_ID_SHFT;
- }
-
- /* pcibr sources an error interrupt;
- * figure out where to send it.
- *
- * If any interrupts are enabled in bridge,
- * then the prom set us up and our interrupt
- * has already been reconnected in mlreset
- * above.
- *
- * Need to set the D_INTR_ISERR flag
- * in the dev_desc used for allocating the
- * error interrupt, so our interrupt will
- * be properly routed and prioritized.
- *
- * If our crosstalk provider wants to
- * fix widget error interrupts to specific
- * destinations, D_INTR_ISERR is how it
- * knows to do this.
- */
-
- dev_desc = device_desc_dup(pcibr_vhdl);
- device_desc_flags_set(dev_desc,
- device_desc_flags_get(dev_desc) | D_INTR_ISERR);
- device_desc_intr_name_set(dev_desc, "Bridge error");
-
- xtalk_intr = xtalk_intr_alloc(xconn_vhdl, dev_desc, pcibr_vhdl);
- ASSERT(xtalk_intr != NULL);
-
- device_desc_free(dev_desc);
-
- pcibr_soft->bsi_err_intr = xtalk_intr;
-
- /*
- * On IP35 with XBridge, we do some extra checks in pcibr_setwidint
- * in order to work around some addressing limitations. In order
- * for that fire wall to work properly, we need to make sure we
- * start from a known clean state.
- */
- pcibr_clearwidint(bridge);
-
- xtalk_intr_connect(xtalk_intr,
- (intr_func_t) pcibr_error_intr_handler,
- (intr_arg_t) pcibr_soft,
- (xtalk_intr_setfunc_t) pcibr_setwidint,
- (void *) bridge,
- (void *) 0);
-
- /*
- * now we can start handling error interrupts;
- * enable all of them.
- * NOTE: some PCI ints may already be enabled.
- */
- b_int_enable = bridge->b_int_enable | BRIDGE_ISR_ERRORS;
-
-
- bridge->b_int_enable = b_int_enable;
- bridge->b_int_mode = 0; /* do not send "clear interrupt" packets */
-
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
-
- /*
- * Depending on the rev of bridge, disable certain features.
- * Easiest way seems to be to force the PCIBR_NOwhatever
- * flag to be on for all DMA calls, which overrides any
- * PCIBR_whatever flag or even the setting of whatever
- * from the PCIIO_DMA_class flags (or even from the other
- * PCIBR flags, since NO overrides YES).
- */
- pcibr_soft->bs_dma_flags = 0;
-
- /* PREFETCH:
- * Always completely disabled for REV.A;
- * at "pcibr_prefetch_enable_rev", anyone
- * asking for PCIIO_PREFETCH gets it.
- * Between these two points, you have to ask
- * for PCIBR_PREFETCH, which promises that
- * your driver knows about known Bridge WARs.
- */
- if (pcibr_soft->bs_rev_num < BRIDGE_PART_REV_B)
- pcibr_soft->bs_dma_flags |= PCIBR_NOPREFETCH;
- else if (pcibr_soft->bs_rev_num <
- (BRIDGE_WIDGET_PART_NUM << 4 | pcibr_prefetch_enable_rev))
- pcibr_soft->bs_dma_flags |= PCIIO_NOPREFETCH;
-
- /* WRITE_GATHER:
- * Disabled up to but not including the
- * rev number in pcibr_wg_enable_rev. There
- * is no "WAR range" as with prefetch.
- */
- if (pcibr_soft->bs_rev_num <
- (BRIDGE_WIDGET_PART_NUM << 4 | pcibr_wg_enable_rev))
- pcibr_soft->bs_dma_flags |= PCIBR_NOWRITE_GATHER;
-
- pciio_provider_register(pcibr_vhdl, &pcibr_provider);
- pciio_provider_startup(pcibr_vhdl);
-
- pci_io_fb = 0x00000004; /* I/O FreeBlock Base */
- pci_io_fl = 0xFFFFFFFF; /* I/O FreeBlock Last */
-
- pci_lo_fb = 0x00000010; /* Low Memory FreeBlock Base */
- pci_lo_fl = 0x001FFFFF; /* Low Memory FreeBlock Last */
-
- pci_hi_fb = 0x00200000; /* High Memory FreeBlock Base */
- pci_hi_fl = 0x3FFFFFFF; /* High Memory FreeBlock Last */
-
-
- PCI_ADDR_SPACE_LIMITS_STORE();
-
- /* build "no-slot" connection point
- */
- pcibr_info = pcibr_device_info_new
- (pcibr_soft, PCIIO_SLOT_NONE, PCIIO_FUNC_NONE,
- PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE);
- noslot_conn = pciio_device_info_register
- (pcibr_vhdl, &pcibr_info->f_c);
-
- /* Remember the no slot connection point info for tearing it
- * down during detach.
- */
- pcibr_soft->bs_noslot_conn = noslot_conn;
- pcibr_soft->bs_noslot_info = pcibr_info;
-#if PCI_FBBE
- fast_back_to_back_enable = 1;
-#endif
-
-#if PCI_FBBE
- if (fast_back_to_back_enable) {
- /*
- * All devices on the bus are capable of fast back to back, so
- * we need to set the fast back to back bit in all devices on
- * the bus that are capable of doing such accesses.
- */
- }
-#endif
-
-#ifdef LATER
- /* If the bridge has been reset then there is no need to reset
- * the individual PCI slots.
- */
- for (slot = 0; slot < 8; ++slot)
- /* Reset all the slots */
- (void)pcibr_slot_reset(pcibr_vhdl, slot);
-#endif
-
- for (slot = 0; slot < 8; ++slot)
- /* Find out what is out there */
- (void)pcibr_slot_info_init(pcibr_vhdl,slot);
-
- for (slot = 0; slot < 8; ++slot)
- /* Set up the address space for this slot in the pci land */
- (void)pcibr_slot_addr_space_init(pcibr_vhdl,slot);
-
- for (slot = 0; slot < 8; ++slot)
- /* Setup the device register */
- (void)pcibr_slot_device_init(pcibr_vhdl, slot);
-
-#ifndef __ia64
-#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
- for (slot = 0; slot < 8; ++slot)
- /* Set up convenience links */
- if (is_xbridge(bridge))
- if (pcibr_soft->bs_slot[slot].bss_ninfo > 0) /* if occupied */
- pcibr_bus_cnvlink(pcibr_info->f_vertex, slot);
-#endif
-#endif
-
- for (slot = 0; slot < 8; ++slot)
- /* Setup host/guest relations */
- (void)pcibr_slot_guest_info_init(pcibr_vhdl,slot);
-
- for (slot = 0; slot < 8; ++slot)
- /* Initial RRB management */
- (void)pcibr_slot_initial_rrb_alloc(pcibr_vhdl,slot);
-
- /* driver attach routines should be called out from generic linux code */
- for (slot = 0; slot < 8; ++slot)
- /* Call the device attach */
- (void)pcibr_slot_call_device_attach(pcibr_vhdl, slot, 0);
-
- /*
- * Each Pbrick PCI bus only has slots 1 and 2. Similarly for
- * widget 0xe on Ibricks. Allocate RRB's accordingly.
- */
- if (pcibr_soft->bs_moduleid > 0) {
- switch (MODULE_GET_BTCHAR(pcibr_soft->bs_moduleid)) {
- case 'p': /* Pbrick */
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
- do_pcibr_rrb_autoalloc(pcibr_soft, 2, 8);
- break;
- case 'i': /* Ibrick */
- /* port 0xe on the Ibrick only has slots 1 and 2 */
- if (pcibr_soft->bs_xid == 0xe) {
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
- do_pcibr_rrb_autoalloc(pcibr_soft, 2, 8);
- }
- else {
- /* allocate one RRB for the serial port */
- do_pcibr_rrb_autoalloc(pcibr_soft, 0, 1);
- }
- break;
- } /* switch */
- }
-
-#ifdef LATER
- if (strstr(nicinfo, XTALK_PCI_PART_NUM)) {
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
-#if PCIBR_RRB_DEBUG
- printf("\n\nFound XTALK_PCI (030-1275) at %v\n", xconn_vhdl);
-
- printf("pcibr_attach: %v Shoebox RRB MANAGEMENT: %d+%d free\n",
- pcibr_vhdl,
- pcibr_soft->bs_rrb_avail[0],
- pcibr_soft->bs_rrb_avail[1]);
-
- for (slot = 0; slot < 8; ++slot)
- printf("\t%d+%d+%d",
- 0xFFF & pcibr_soft->bs_rrb_valid[slot],
- 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
-
- printf("\n");
-#endif
- }
-#else
- FIXME("pcibr_attach: Call do_pcibr_rrb_autoalloc nicinfo\n");
-#endif
-
- if (aa)
- async_attach_add_info(noslot_conn, aa);
-
- pciio_device_attach(noslot_conn, 0);
-
-
- /*
- * Tear down pointer to async attach info -- async threads for
- * bridge's descendants may be running but the bridge's work is done.
- */
- if (aa)
- async_attach_del_info(xconn_vhdl);
-
- return 0;
-}
-/*
- * pcibr_detach:
- * Detach the bridge device from the hwgraph after cleaning out all the
- * underlying vertices.
- */
-int
-pcibr_detach(devfs_handle_t xconn)
-{
- pciio_slot_t slot;
- devfs_handle_t pcibr_vhdl;
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
-
- /* Get the bridge vertex from its xtalk connection point */
- if (hwgraph_traverse(xconn, EDGE_LBL_PCI, &pcibr_vhdl) != GRAPH_SUCCESS)
- return(1);
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- bridge = pcibr_soft->bs_base;
-
- /* Disable the interrupts from the bridge */
- bridge->b_int_enable = 0;
-
- /* Detach all the PCI devices talking to this bridge */
- for(slot = 0; slot < 8; slot++) {
-#ifdef DEBUG
- printk("pcibr_device_detach called for %p/%d\n",
- pcibr_vhdl,slot);
-#endif
- pcibr_slot_detach(pcibr_vhdl, slot, 0);
- }
-
- /* Unregister the no-slot connection point */
- pciio_device_info_unregister(pcibr_vhdl,
- &(pcibr_soft->bs_noslot_info->f_c));
-
- spin_lock_destroy(&pcibr_soft->bs_lock);
- kfree(pcibr_soft->bs_name);
-
- /* Error handler gets unregistered when the widget info is
- * cleaned
- */
- /* Free the soft ATE maps */
- if (pcibr_soft->bs_int_ate_map)
- rmfreemap(pcibr_soft->bs_int_ate_map);
- if (pcibr_soft->bs_ext_ate_map)
- rmfreemap(pcibr_soft->bs_ext_ate_map);
-
- /* Disconnect the error interrupt and free the xtalk resources
- * associated with it.
- */
- xtalk_intr_disconnect(pcibr_soft->bsi_err_intr);
- xtalk_intr_free(pcibr_soft->bsi_err_intr);
-
- /* Clear the software state maintained by the bridge driver for this
- * bridge.
- */
- DEL(pcibr_soft);
- /* Remove the Bridge revision labelled info */
- (void)hwgraph_info_remove_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, NULL);
- /* Remove the character device associated with this bridge */
- (void)hwgraph_edge_remove(pcibr_vhdl, EDGE_LBL_CONTROLLER, NULL);
- /* Remove the PCI bridge vertex */
- (void)hwgraph_edge_remove(xconn, EDGE_LBL_PCI, NULL);
-
- return(0);
-}
-
-int
-pcibr_asic_rev(devfs_handle_t pconn_vhdl)
-{
- devfs_handle_t pcibr_vhdl;
- arbitrary_info_t ainfo;
-
- if (GRAPH_SUCCESS !=
- hwgraph_traverse(pconn_vhdl, EDGE_LBL_MASTER, &pcibr_vhdl))
- return -1;
-
- if (GRAPH_SUCCESS !=
- hwgraph_info_get_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, &ainfo))
- return -1;
-
- return (int) ainfo;
-}
-
-int
-pcibr_write_gather_flush(devfs_handle_t pconn_vhdl)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- pciio_slot_t slot;
- slot = pciio_info_slot_get(pciio_info);
- pcibr_device_write_gather_flush(pcibr_soft, slot);
- return 0;
-}
-
-/* =====================================================================
- * PIO MANAGEMENT
- */
-
-LOCAL iopaddr_t
-pcibr_addr_pci_to_xio(devfs_handle_t pconn_vhdl,
- pciio_slot_t slot,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- unsigned flags)
-{
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- bridge_t *bridge = pcibr_soft->bs_base;
-
- unsigned bar; /* which BASE reg on device is decoding */
- iopaddr_t xio_addr = XIO_NOWHERE;
-
- pciio_space_t wspace; /* which space device is decoding */
- iopaddr_t wbase; /* base of device decode on PCI */
- size_t wsize; /* size of device decode on PCI */
-
- int try; /* DevIO(x) window scanning order control */
- int win; /* which DevIO(x) window is being used */
- pciio_space_t mspace; /* target space for devio(x) register */
- iopaddr_t mbase; /* base of devio(x) mapped area on PCI */
- size_t msize; /* size of devio(x) mapped area on PCI */
- size_t mmask; /* addr bits stored in Device(x) */
-
- unsigned long s;
-
- s = pcibr_lock(pcibr_soft);
-
- if (pcibr_soft->bs_slot[slot].has_host) {
- slot = pcibr_soft->bs_slot[slot].host_slot;
- pcibr_info = pcibr_soft->bs_slot[slot].bss_infos[0];
- }
- if (space == PCIIO_SPACE_NONE)
- goto done;
-
- if (space == PCIIO_SPACE_CFG) {
- /*
- * Usually, the first mapping
- * established to a PCI device
- * is to its config space.
- *
- * In any case, we definitely
- * do NOT need to worry about
- * PCI BASE registers, and
- * MUST NOT attempt to point
- * the DevIO(x) window at
- * this access ...
- */
- if (((flags & PCIIO_BYTE_STREAM) == 0) &&
- ((pci_addr + req_size) <= BRIDGE_TYPE0_CFG_FUNC_OFF))
- xio_addr = pci_addr + BRIDGE_TYPE0_CFG_DEV(slot);
-
- goto done;
- }
- if (space == PCIIO_SPACE_ROM) {
- /* PIO to the Expansion Rom.
- * Driver is responsible for
- * enabling and disabling
- * decodes properly.
- */
- wbase = pcibr_info->f_rbase;
- wsize = pcibr_info->f_rsize;
-
- /*
- * While the driver should know better
- * than to attempt to map more space
- * than the device is decoding, he might
- * do it; better to bail out here.
- */
- if ((pci_addr + req_size) > wsize)
- goto done;
-
- pci_addr += wbase;
- space = PCIIO_SPACE_MEM;
- }
- /*
- * reduce window mappings to raw
- * space mappings (maybe allocating
- * windows), and try for DevIO(x)
- * usage (setting it if it is available).
- */
- bar = space - PCIIO_SPACE_WIN0;
- if (bar < 6) {
- wspace = pcibr_info->f_window[bar].w_space;
- if (wspace == PCIIO_SPACE_NONE)
- goto done;
-
- /* get PCI base and size */
- wbase = pcibr_info->f_window[bar].w_base;
- wsize = pcibr_info->f_window[bar].w_size;
-
- /*
- * While the driver should know better
- * than to attempt to map more space
- * than the device is decoding, he might
- * do it; better to bail out here.
- */
- if ((pci_addr + req_size) > wsize)
- goto done;
-
- /* shift from window relative to
- * decoded space relative.
- */
- pci_addr += wbase;
- space = wspace;
- } else
- bar = -1;
-
- /* Scan all the DevIO(x) windows twice looking for one
- * that can satisfy our request. The first time through,
- * only look at assigned windows; the second time, also
- * look at PCIIO_SPACE_NONE windows. Arrange the order
- * so we always look at our own window first.
- *
- * We will not attempt to satisfy a single request
- * by concatinating multiple windows.
- */
- for (try = 0; try < 16; ++try) {
- bridgereg_t devreg;
- unsigned offset;
-
- win = (try + slot) % 8;
-
- /* If this DevIO(x) mapping area can provide
- * a mapping to this address, use it.
- */
- msize = (win < 2) ? 0x200000 : 0x100000;
- mmask = -msize;
- if (space != PCIIO_SPACE_IO)
- mmask &= 0x3FFFFFFF;
-
- offset = pci_addr & (msize - 1);
-
- /* If this window can't possibly handle that request,
- * go on to the next window.
- */
- if (((pci_addr & (msize - 1)) + req_size) > msize)
- continue;
-
- devreg = pcibr_soft->bs_slot[win].bss_device;
-
- /* Is this window "nailed down"?
- * If not, maybe we can use it.
- * (only check this the second time through)
- */
- mspace = pcibr_soft->bs_slot[win].bss_devio.bssd_space;
- if ((try > 7) && (mspace == PCIIO_SPACE_NONE)) {
-
- /* If this is the primary DevIO(x) window
- * for some other device, skip it.
- */
- if ((win != slot) &&
- (PCIIO_VENDOR_ID_NONE !=
- pcibr_soft->bs_slot[win].bss_vendor_id))
- continue;
-
- /* It's a free window, and we fit in it.
- * Set up Device(win) to our taste.
- */
- mbase = pci_addr & mmask;
-
- /* check that we would really get from
- * here to there.
- */
- if ((mbase | offset) != pci_addr)
- continue;
-
- devreg &= ~BRIDGE_DEV_OFF_MASK;
- if (space != PCIIO_SPACE_IO)
- devreg |= BRIDGE_DEV_DEV_IO_MEM;
- else
- devreg &= ~BRIDGE_DEV_DEV_IO_MEM;
- devreg |= (mbase >> 20) & BRIDGE_DEV_OFF_MASK;
-
- /* default is WORD_VALUES.
- * if you specify both,
- * operation is undefined.
- */
- if (flags & PCIIO_BYTE_STREAM)
- devreg |= BRIDGE_DEV_DEV_SWAP;
- else
- devreg &= ~BRIDGE_DEV_DEV_SWAP;
-
- if (pcibr_soft->bs_slot[win].bss_device != devreg) {
- bridge->b_device[win].reg = devreg;
- pcibr_soft->bs_slot[win].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
-
-#if DEBUG && PCI_DEBUG
- printk("pcibr Device(%d): 0x%lx\n", win, bridge->b_device[win].reg);
-#endif
- }
- pcibr_soft->bs_slot[win].bss_devio.bssd_space = space;
- pcibr_soft->bs_slot[win].bss_devio.bssd_base = mbase;
- xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
-
-#if DEBUG && PCI_DEBUG
- printk("%s LINE %d map to space %d space desc 0x%x[%lx..%lx] for slot %d allocates DevIO(%d) devreg 0x%x\n",
- __FUNCTION__, __LINE__, space, space_desc,
- pci_addr, pci_addr + req_size - 1,
- slot, win, devreg);
-#endif
-
- goto done;
- } /* endif DevIO(x) not pointed */
- mbase = pcibr_soft->bs_slot[win].bss_devio.bssd_base;
-
- /* Now check for request incompat with DevIO(x)
- */
- if ((mspace != space) ||
- (pci_addr < mbase) ||
- ((pci_addr + req_size) > (mbase + msize)) ||
- ((flags & PCIIO_BYTE_STREAM) && !(devreg & BRIDGE_DEV_DEV_SWAP)) ||
- (!(flags & PCIIO_BYTE_STREAM) && (devreg & BRIDGE_DEV_DEV_SWAP)))
- continue;
-
- /* DevIO(x) window is pointed at PCI space
- * that includes our target. Calculate the
- * final XIO address, release the lock and
- * return.
- */
- xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
-
-#if DEBUG && PCI_DEBUG
- printk("%s LINE %d map to space %d [0x%p..0x%p] for slot %d uses DevIO(%d)\n",
- __FUNCTION__, __LINE__, space, pci_addr, pci_addr + req_size - 1, slot, win);
-#endif
- goto done;
- }
-
- switch (space) {
- /*
- * Accesses to device decode
- * areas that do a not fit
- * within the DevIO(x) space are
- * modified to be accesses via
- * the direct mapping areas.
- *
- * If necessary, drivers can
- * explicitly ask for mappings
- * into these address spaces,
- * but this should never be needed.
- */
- case PCIIO_SPACE_MEM: /* "mem space" */
- case PCIIO_SPACE_MEM32: /* "mem, use 32-bit-wide bus" */
- if ((pci_addr + BRIDGE_PCI_MEM32_BASE + req_size - 1) <=
- BRIDGE_PCI_MEM32_LIMIT)
- xio_addr = pci_addr + BRIDGE_PCI_MEM32_BASE;
- break;
-
- case PCIIO_SPACE_MEM64: /* "mem, use 64-bit-wide bus" */
- if ((pci_addr + BRIDGE_PCI_MEM64_BASE + req_size - 1) <=
- BRIDGE_PCI_MEM64_LIMIT)
- xio_addr = pci_addr + BRIDGE_PCI_MEM64_BASE;
- break;
-
- case PCIIO_SPACE_IO: /* "i/o space" */
- /* Bridge Hardware Bug WAR #482741:
- * The 4G area that maps directly from
- * XIO space to PCI I/O space is busted
- * until Bridge Rev D.
- */
- if ((pcibr_soft->bs_rev_num > BRIDGE_PART_REV_C) &&
- ((pci_addr + BRIDGE_PCI_IO_BASE + req_size - 1) <=
- BRIDGE_PCI_IO_LIMIT))
- xio_addr = pci_addr + BRIDGE_PCI_IO_BASE;
- break;
- }
-
- /* Check that "Direct PIO" byteswapping matches,
- * try to change it if it does not.
- */
- if (xio_addr != XIO_NOWHERE) {
- unsigned bst; /* nonzero to set bytestream */
- unsigned *bfp; /* addr of record of how swapper is set */
- unsigned swb; /* which control bit to mung */
- unsigned bfo; /* current swapper setting */
- unsigned bfn; /* desired swapper setting */
-
- bfp = ((space == PCIIO_SPACE_IO)
- ? (&pcibr_soft->bs_pio_end_io)
- : (&pcibr_soft->bs_pio_end_mem));
-
- bfo = *bfp;
-
- bst = flags & PCIIO_BYTE_STREAM;
-
- bfn = bst ? PCIIO_BYTE_STREAM : PCIIO_WORD_VALUES;
-
- if (bfn == bfo) { /* we already match. */
- ;
- } else if (bfo != 0) { /* we have a conflict. */
-#if DEBUG && PCI_DEBUG
- printk("pcibr_addr_pci_to_xio: swap conflict in space %d , was%s%s, want%s%s\n",
- space,
- bfo & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfo & PCIIO_WORD_VALUES ? " WORD_VALUES" : "",
- bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
-#endif
- xio_addr = XIO_NOWHERE;
- } else { /* OK to make the change. */
- bridgereg_t octl, nctl;
-
- swb = (space == PCIIO_SPACE_IO) ? BRIDGE_CTRL_IO_SWAP : BRIDGE_CTRL_MEM_SWAP;
- octl = bridge->b_wid_control;
- nctl = bst ? octl | swb : octl & ~swb;
-
- if (octl != nctl) /* make the change if any */
- bridge->b_wid_control = nctl;
-
- *bfp = bfn; /* record the assignment */
-
-#if DEBUG && PCI_DEBUG
- printk("pcibr_addr_pci_to_xio: swap for space %d set to%s%s\n",
- space,
- bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
-#endif
- }
- }
- done:
- pcibr_unlock(pcibr_soft, s);
- return xio_addr;
-}
-
-/*ARGSUSED6 */
-pcibr_piomap_t
-pcibr_piomap_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- size_t req_size_max,
- unsigned flags)
-{
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- 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);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
-
- pcibr_piomap_t *mapptr;
- pcibr_piomap_t maplist;
- pcibr_piomap_t pcibr_piomap;
- iopaddr_t xio_addr;
- xtalk_piomap_t xtalk_piomap;
- unsigned long s;
-
- /* Make sure that the req sizes are non-zero */
- if ((req_size < 1) || (req_size_max < 1))
- return NULL;
-
- /*
- * Code to translate slot/space/addr
- * into xio_addr is common between
- * this routine and pcibr_piotrans_addr.
- */
- xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
-
- if (xio_addr == XIO_NOWHERE)
- return NULL;
-
- /* Check the piomap list to see if there is already an allocated
- * piomap entry but not in use. If so use that one. Otherwise
- * allocate a new piomap entry and add it to the piomap list
- */
- mapptr = &(pcibr_info->f_piomap);
-
- s = pcibr_lock(pcibr_soft);
- for (pcibr_piomap = *mapptr;
- pcibr_piomap != NULL;
- pcibr_piomap = pcibr_piomap->bp_next) {
- if (pcibr_piomap->bp_mapsz == 0)
- break;
- }
-
- if (pcibr_piomap)
- mapptr = NULL;
- else {
- pcibr_unlock(pcibr_soft, s);
- NEW(pcibr_piomap);
- }
-
- pcibr_piomap->bp_dev = pconn_vhdl;
- pcibr_piomap->bp_slot = pciio_slot;
- pcibr_piomap->bp_flags = flags;
- pcibr_piomap->bp_space = space;
- pcibr_piomap->bp_pciaddr = pci_addr;
- pcibr_piomap->bp_mapsz = req_size;
- pcibr_piomap->bp_soft = pcibr_soft;
- pcibr_piomap->bp_toc[0] = ATOMIC_INIT(0);
-
- if (mapptr) {
- s = pcibr_lock(pcibr_soft);
- maplist = *mapptr;
- pcibr_piomap->bp_next = maplist;
- *mapptr = pcibr_piomap;
- }
- pcibr_unlock(pcibr_soft, s);
-
-
- if (pcibr_piomap) {
- xtalk_piomap =
- xtalk_piomap_alloc(xconn_vhdl, 0,
- xio_addr,
- req_size, req_size_max,
- flags & PIOMAP_FLAGS);
- if (xtalk_piomap) {
- pcibr_piomap->bp_xtalk_addr = xio_addr;
- pcibr_piomap->bp_xtalk_pio = xtalk_piomap;
- } else {
- pcibr_piomap->bp_mapsz = 0;
- pcibr_piomap = 0;
- }
- }
- return pcibr_piomap;
-}
-
-/*ARGSUSED */
-void
-pcibr_piomap_free(pcibr_piomap_t pcibr_piomap)
-{
- xtalk_piomap_free(pcibr_piomap->bp_xtalk_pio);
- pcibr_piomap->bp_xtalk_pio = 0;
- pcibr_piomap->bp_mapsz = 0;
-}
-
-/*ARGSUSED */
-caddr_t
-pcibr_piomap_addr(pcibr_piomap_t pcibr_piomap,
- iopaddr_t pci_addr,
- size_t req_size)
-{
- return xtalk_piomap_addr(pcibr_piomap->bp_xtalk_pio,
- pcibr_piomap->bp_xtalk_addr +
- pci_addr - pcibr_piomap->bp_pciaddr,
- req_size);
-}
-
-/*ARGSUSED */
-void
-pcibr_piomap_done(pcibr_piomap_t pcibr_piomap)
-{
- xtalk_piomap_done(pcibr_piomap->bp_xtalk_pio);
-}
-
-/*ARGSUSED */
-caddr_t
-pcibr_piotrans_addr(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- unsigned flags)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
-
- iopaddr_t xio_addr;
-
- xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
-
- if (xio_addr == XIO_NOWHERE)
- return NULL;
-
- return xtalk_piotrans_addr(xconn_vhdl, 0, xio_addr, req_size, flags & PIOMAP_FLAGS);
-}
-
-/*
- * PIO Space allocation and management.
- * Allocate and Manage the PCI PIO space (mem and io space)
- * This routine is pretty simplistic at this time, and
- * does pretty trivial management of allocation and freeing..
- * The current scheme is prone for fragmentation..
- * Change the scheme to use bitmaps.
- */
-
-/*ARGSUSED */
-iopaddr_t
-pcibr_piospace_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- size_t req_size,
- size_t alignment)
-{
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
-
- pciio_piospace_t piosp;
- unsigned long s;
-
- iopaddr_t *pciaddr, *pcilast;
- iopaddr_t start_addr;
- size_t align_mask;
-
- /*
- * Check for proper alignment
- */
- ASSERT(alignment >= NBPP);
- ASSERT((alignment & (alignment - 1)) == 0);
-
- align_mask = alignment - 1;
- s = pcibr_lock(pcibr_soft);
-
- /*
- * First look if a previously allocated chunk exists.
- */
- if ((piosp = pcibr_info->f_piospace)) {
- /*
- * Look through the list for a right sized free chunk.
- */
- do {
- if (piosp->free &&
- (piosp->space == space) &&
- (piosp->count >= req_size) &&
- !(piosp->start & align_mask)) {
- piosp->free = 0;
- pcibr_unlock(pcibr_soft, s);
- return piosp->start;
- }
- piosp = piosp->next;
- } while (piosp);
- }
- ASSERT(!piosp);
-
- switch (space) {
- case PCIIO_SPACE_IO:
- pciaddr = &pcibr_soft->bs_spinfo.pci_io_base;
- pcilast = &pcibr_soft->bs_spinfo.pci_io_last;
- break;
- case PCIIO_SPACE_MEM:
- case PCIIO_SPACE_MEM32:
- pciaddr = &pcibr_soft->bs_spinfo.pci_mem_base;
- pcilast = &pcibr_soft->bs_spinfo.pci_mem_last;
- break;
- default:
- ASSERT(0);
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
-
- start_addr = *pciaddr;
-
- /*
- * Align start_addr.
- */
- if (start_addr & align_mask)
- start_addr = (start_addr + align_mask) & ~align_mask;
-
- if ((start_addr + req_size) > *pcilast) {
- /*
- * If too big a request, reject it.
- */
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- *pciaddr = (start_addr + req_size);
-
- NEW(piosp);
- piosp->free = 0;
- piosp->space = space;
- piosp->start = start_addr;
- piosp->count = req_size;
- piosp->next = pcibr_info->f_piospace;
- pcibr_info->f_piospace = piosp;
-
- pcibr_unlock(pcibr_soft, s);
- return start_addr;
-}
-
-/*ARGSUSED */
-void
-pcibr_piospace_free(devfs_handle_t pconn_vhdl,
- pciio_space_t space,
- iopaddr_t pciaddr,
- size_t req_size)
-{
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
-
- pciio_piospace_t piosp;
- unsigned long s;
- char name[1024];
-
- /*
- * Look through the bridge data structures for the pciio_piospace_t
- * structure corresponding to 'pciaddr'
- */
- s = pcibr_lock(pcibr_soft);
- piosp = pcibr_info->f_piospace;
- while (piosp) {
- /*
- * Piospace free can only be for the complete
- * chunk and not parts of it..
- */
- if (piosp->start == pciaddr) {
- if (piosp->count == req_size)
- break;
- /*
- * Improper size passed for freeing..
- * Print a message and break;
- */
- hwgraph_vertex_name_get(pconn_vhdl, name, 1024);
- PRINT_WARNING("pcibr_piospace_free: error");
- PRINT_WARNING("Device %s freeing size (0x%lx) different than allocated (0x%lx)",
- name, req_size, piosp->count);
- PRINT_WARNING("Freeing 0x%lx instead", piosp->count);
- break;
- }
- piosp = piosp->next;
- }
-
- if (!piosp) {
- PRINT_WARNING(
- "pcibr_piospace_free: Address 0x%lx size 0x%lx - No match\n",
- pciaddr, req_size);
- pcibr_unlock(pcibr_soft, s);
- return;
- }
- piosp->free = 1;
- pcibr_unlock(pcibr_soft, s);
- return;
-}
-
-/* =====================================================================
- * DMA MANAGEMENT
- *
- * The Bridge ASIC provides three methods of doing
- * DMA: via a "direct map" register available in
- * 32-bit PCI space (which selects a contiguous 2G
- * address space on some other widget), via
- * "direct" addressing via 64-bit PCI space (all
- * destination information comes from the PCI
- * address, including transfer attributes), and via
- * a "mapped" region that allows a bunch of
- * different small mappings to be established with
- * the PMU.
- *
- * For efficiency, we most prefer to use the 32-bit
- * direct mapping facility, since it requires no
- * resource allocations. The advantage of using the
- * PMU over the 64-bit direct is that single-cycle
- * PCI addressing can be used; the advantage of
- * using 64-bit direct over PMU addressing is that
- * we do not have to allocate entries in the PMU.
- */
-
-/*
- * Convert PCI-generic software flags and Bridge-specific software flags
- * into Bridge-specific Direct Map attribute bits.
- */
-LOCAL iopaddr_t
-pcibr_flags_to_d64(unsigned flags, pcibr_soft_t pcibr_soft)
-{
- iopaddr_t attributes = 0;
-
- /* Sanity check: Bridge only allows use of VCHAN1 via 64-bit addrs */
-#ifdef LATER
- ASSERT_ALWAYS(!(flags & PCIBR_VCHAN1) || (flags & PCIIO_DMA_A64));
-#endif
-
- /* Generic macro flags
- */
- if (flags & PCIIO_DMA_DATA) { /* standard data channel */
- attributes &= ~PCI64_ATTR_BAR; /* no barrier bit */
- attributes |= PCI64_ATTR_PREF; /* prefetch on */
- }
- if (flags & PCIIO_DMA_CMD) { /* standard command channel */
- attributes |= PCI64_ATTR_BAR; /* barrier bit on */
- attributes &= ~PCI64_ATTR_PREF; /* disable prefetch */
- }
- /* Generic detail flags
- */
- if (flags & PCIIO_PREFETCH)
- attributes |= PCI64_ATTR_PREF;
- if (flags & PCIIO_NOPREFETCH)
- attributes &= ~PCI64_ATTR_PREF;
-
- /* the swap bit is in the address attributes for xbridge */
- if (pcibr_soft->bs_xbridge) {
- if (flags & PCIIO_BYTE_STREAM)
- attributes |= PCI64_ATTR_SWAP;
- if (flags & PCIIO_WORD_VALUES)
- attributes &= ~PCI64_ATTR_SWAP;
- }
-
- /* Provider-specific flags
- */
- if (flags & PCIBR_BARRIER)
- attributes |= PCI64_ATTR_BAR;
- if (flags & PCIBR_NOBARRIER)
- attributes &= ~PCI64_ATTR_BAR;
-
- if (flags & PCIBR_PREFETCH)
- attributes |= PCI64_ATTR_PREF;
- if (flags & PCIBR_NOPREFETCH)
- attributes &= ~PCI64_ATTR_PREF;
-
- if (flags & PCIBR_PRECISE)
- attributes |= PCI64_ATTR_PREC;
- if (flags & PCIBR_NOPRECISE)
- attributes &= ~PCI64_ATTR_PREC;
-
- if (flags & PCIBR_VCHAN1)
- attributes |= PCI64_ATTR_VIRTUAL;
- if (flags & PCIBR_VCHAN0)
- attributes &= ~PCI64_ATTR_VIRTUAL;
-
- return (attributes);
-}
-
-/*
- * Convert PCI-generic software flags and Bridge-specific software flags
- * into Bridge-specific Address Translation Entry attribute bits.
- */
-LOCAL bridge_ate_t
-pcibr_flags_to_ate(unsigned flags)
-{
- bridge_ate_t attributes;
-
- /* default if nothing specified:
- * NOBARRIER
- * NOPREFETCH
- * NOPRECISE
- * COHERENT
- * Plus the valid bit
- */
- attributes = ATE_CO | ATE_V;
-
- /* Generic macro flags
- */
- if (flags & PCIIO_DMA_DATA) { /* standard data channel */
- attributes &= ~ATE_BAR; /* no barrier */
- attributes |= ATE_PREF; /* prefetch on */
- }
- if (flags & PCIIO_DMA_CMD) { /* standard command channel */
- attributes |= ATE_BAR; /* barrier bit on */
- attributes &= ~ATE_PREF; /* disable prefetch */
- }
- /* Generic detail flags
- */
- if (flags & PCIIO_PREFETCH)
- attributes |= ATE_PREF;
- if (flags & PCIIO_NOPREFETCH)
- attributes &= ~ATE_PREF;
-
- /* Provider-specific flags
- */
- if (flags & PCIBR_BARRIER)
- attributes |= ATE_BAR;
- if (flags & PCIBR_NOBARRIER)
- attributes &= ~ATE_BAR;
-
- if (flags & PCIBR_PREFETCH)
- attributes |= ATE_PREF;
- if (flags & PCIBR_NOPREFETCH)
- attributes &= ~ATE_PREF;
-
- if (flags & PCIBR_PRECISE)
- attributes |= ATE_PREC;
- if (flags & PCIBR_NOPRECISE)
- attributes &= ~ATE_PREC;
-
- return (attributes);
-}
-
-/*ARGSUSED */
-pcibr_dmamap_t
-pcibr_dmamap_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- size_t req_size_max,
- unsigned flags)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t slot;
- xwidgetnum_t xio_port;
-
- xtalk_dmamap_t xtalk_dmamap;
- pcibr_dmamap_t pcibr_dmamap;
- int ate_count;
- int ate_index;
-
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
-
-#ifdef IRIX
- NEWf(pcibr_dmamap, flags);
-#else
- /*
- * On SNIA64, these maps are pre-allocated because pcibr_dmamap_alloc()
- * can be called within an interrupt thread.
- */
- pcibr_dmamap = (pcibr_dmamap_t)get_free_pciio_dmamap(pcibr_soft->bs_vhdl);
-#endif
-
- if (!pcibr_dmamap)
- return 0;
-
- xtalk_dmamap = xtalk_dmamap_alloc(xconn_vhdl, dev_desc, req_size_max,
- flags & DMAMAP_FLAGS);
- if (!xtalk_dmamap) {
-#if PCIBR_ATE_DEBUG
- printk("pcibr_attach: xtalk_dmamap_alloc failed\n");
-#endif
- DEL(pcibr_dmamap);
- return 0;
- }
- xio_port = pcibr_soft->bs_mxid;
- slot = pciio_info_slot_get(pciio_info);
-
- pcibr_dmamap->bd_dev = pconn_vhdl;
- pcibr_dmamap->bd_slot = slot;
- pcibr_dmamap->bd_soft = pcibr_soft;
- pcibr_dmamap->bd_xtalk = xtalk_dmamap;
- pcibr_dmamap->bd_max_size = req_size_max;
- pcibr_dmamap->bd_xio_port = xio_port;
-
- if (flags & PCIIO_DMA_A64) {
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D64_BITS)) {
- iopaddr_t pci_addr;
- int have_rrbs;
- int min_rrbs;
-
- /* Device is capable of A64 operations,
- * and the attributes of the DMA are
- * consistant with any previous DMA
- * mappings using shared resources.
- */
-
- pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
-
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_xio_addr = 0;
- pcibr_dmamap->bd_pci_addr = pci_addr;
-
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
- if (flags & PCIBR_VCHAN1)
- slot += PCIBR_RRB_SLOT_VIRTUAL;
- have_rrbs = pcibr_soft->bs_rrb_valid[slot];
- if (have_rrbs < 2) {
- if (pci_addr & PCI64_ATTR_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
- }
- }
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using direct64\n");
-#endif
- return pcibr_dmamap;
- }
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use direct64\n");
-#endif
- flags &= ~PCIIO_DMA_A64;
- }
- if (flags & PCIIO_FIXED) {
- /* warning: mappings may fail later,
- * if direct32 can't get to the address.
- */
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D32_BITS)) {
- /* User desires DIRECT A32 operations,
- * and the attributes of the DMA are
- * consistant with any previous DMA
- * mappings using shared resources.
- * Mapping calls may fail if target
- * is outside the direct32 range.
- */
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using direct32\n");
-#endif
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_xio_addr = pcibr_soft->bs_dir_xbase;
- pcibr_dmamap->bd_pci_addr = PCI32_DIRECT_BASE;
- return pcibr_dmamap;
- }
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use direct32\n");
-#endif
- /* If the user demands FIXED and we can't
- * give it to him, fail.
- */
- xtalk_dmamap_free(xtalk_dmamap);
- DEL(pcibr_dmamap);
- return 0;
- }
- /*
- * Allocate Address Translation Entries from the mapping RAM.
- * Unless the PCIBR_NO_ATE_ROUNDUP flag is specified,
- * the maximum number of ATEs is based on the worst-case
- * scenario, where the requested target is in the
- * last byte of an ATE; thus, mapping IOPGSIZE+2
- * does end up requiring three ATEs.
- */
- if (!(flags & PCIBR_NO_ATE_ROUNDUP)) {
- ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */
- +req_size_max /* max mapping bytes */
- - 1) + 1; /* round UP */
- } else { /* assume requested target is page aligned */
- ate_count = IOPG(req_size_max /* max mapping bytes */
- - 1) + 1; /* round UP */
- }
-
- ate_index = pcibr_ate_alloc(pcibr_soft, ate_count);
-
- if (ate_index != -1) {
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_PMU_BITS)) {
- bridge_ate_t ate_proto;
- int have_rrbs;
- int min_rrbs;
-
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using PMU\n");
-#endif
-
- ate_proto = pcibr_flags_to_ate(flags);
-
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_pci_addr =
- PCI32_MAPPED_BASE + IOPGSIZE * ate_index;
- /*
- * for xbridge the byte-swap bit == bit 29 of PCI address
- */
- if (pcibr_soft->bs_xbridge) {
- if (flags & PCIIO_BYTE_STREAM)
- ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
- /*
- * If swap was set in bss_device in pcibr_endian_set()
- * we need to change the address bit.
- */
- if (pcibr_soft->bs_slot[slot].bss_device &
- BRIDGE_DEV_SWAP_PMU)
- ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
- if (flags & PCIIO_WORD_VALUES)
- ATE_SWAP_OFF(pcibr_dmamap->bd_pci_addr);
- }
- pcibr_dmamap->bd_xio_addr = 0;
- pcibr_dmamap->bd_ate_ptr = pcibr_ate_addr(pcibr_soft, ate_index);
- pcibr_dmamap->bd_ate_index = ate_index;
- pcibr_dmamap->bd_ate_count = ate_count;
- pcibr_dmamap->bd_ate_proto = ate_proto;
-
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
- have_rrbs = pcibr_soft->bs_rrb_valid[slot];
- if (have_rrbs < 2) {
- if (ate_proto & ATE_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
- }
- }
- if (ate_index >= pcibr_soft->bs_int_ate_size &&
- !pcibr_soft->bs_xbridge) {
- bridge_t *bridge = pcibr_soft->bs_base;
- volatile unsigned *cmd_regp;
- unsigned cmd_reg;
- unsigned long s;
-
- pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_SSRAM;
-
- s = pcibr_lock(pcibr_soft);
- cmd_regp = &(bridge->
- b_type0_cfg_dev[slot].
- l[PCI_CFG_COMMAND / 4]);
- cmd_reg = *cmd_regp;
- pcibr_soft->bs_slot[slot].bss_cmd_pointer = cmd_regp;
- pcibr_soft->bs_slot[slot].bss_cmd_shadow = cmd_reg;
- pcibr_unlock(pcibr_soft, s);
- }
- return pcibr_dmamap;
- }
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use PMU\n");
-#endif
- pcibr_ate_free(pcibr_soft, ate_index, ate_count);
- }
- /* total failure: sorry, you just can't
- * get from here to there that way.
- */
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: complete failure.\n");
-#endif
- xtalk_dmamap_free(xtalk_dmamap);
- DEL(pcibr_dmamap);
- return 0;
-}
-
-/*ARGSUSED */
-void
-pcibr_dmamap_free(pcibr_dmamap_t pcibr_dmamap)
-{
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
- pciio_slot_t slot = pcibr_dmamap->bd_slot;
-
- unsigned flags = pcibr_dmamap->bd_flags;
-
- /* Make sure that bss_ext_ates_active
- * is properly kept up to date.
- */
-
- if (PCIBR_DMAMAP_BUSY & flags)
- if (PCIBR_DMAMAP_SSRAM & flags)
- atomic_dec(&(pcibr_soft->bs_slot[slot]. bss_ext_ates_active));
-
- xtalk_dmamap_free(pcibr_dmamap->bd_xtalk);
-
- if (pcibr_dmamap->bd_flags & PCIIO_DMA_A64) {
- pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_D64_BITS);
- }
- if (pcibr_dmamap->bd_ate_count) {
- pcibr_ate_free(pcibr_dmamap->bd_soft,
- pcibr_dmamap->bd_ate_index,
- pcibr_dmamap->bd_ate_count);
- pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_PMU_BITS);
- }
-#ifdef IRIX
- DEL(pcibr_dmamap);
-#endif
-}
-
-/*
- * Setup an Address Translation Entry as specified. Use either the Bridge
- * internal maps or the external map RAM, as appropriate.
- */
-LOCAL bridge_ate_p
-pcibr_ate_addr(pcibr_soft_t pcibr_soft,
- int ate_index)
-{
- bridge_t *bridge = pcibr_soft->bs_base;
-
- return (ate_index < pcibr_soft->bs_int_ate_size)
- ? &(bridge->b_int_ate_ram[ate_index].wr)
- : &(bridge->b_ext_ate_ram[ate_index]);
-}
-
-/*
- * pcibr_addr_xio_to_pci: given a PIO range, hand
- * back the corresponding base PCI MEM address;
- * this is used to short-circuit DMA requests that
- * loop back onto this PCI bus.
- */
-LOCAL iopaddr_t
-pcibr_addr_xio_to_pci(pcibr_soft_t soft,
- iopaddr_t xio_addr,
- size_t req_size)
-{
- iopaddr_t xio_lim = xio_addr + req_size - 1;
- iopaddr_t pci_addr;
- pciio_slot_t slot;
-
- if ((xio_addr >= BRIDGE_PCI_MEM32_BASE) &&
- (xio_lim <= BRIDGE_PCI_MEM32_LIMIT)) {
- pci_addr = xio_addr - BRIDGE_PCI_MEM32_BASE;
- return pci_addr;
- }
- if ((xio_addr >= BRIDGE_PCI_MEM64_BASE) &&
- (xio_lim <= BRIDGE_PCI_MEM64_LIMIT)) {
- pci_addr = xio_addr - BRIDGE_PCI_MEM64_BASE;
- return pci_addr;
- }
- for (slot = 0; slot < 8; ++slot)
- if ((xio_addr >= BRIDGE_DEVIO(slot)) &&
- (xio_lim < BRIDGE_DEVIO(slot + 1))) {
- bridgereg_t dev;
-
- dev = soft->bs_slot[slot].bss_device;
- pci_addr = dev & BRIDGE_DEV_OFF_MASK;
- pci_addr <<= BRIDGE_DEV_OFF_ADDR_SHFT;
- pci_addr += xio_addr - BRIDGE_DEVIO(slot);
- return (dev & BRIDGE_DEV_DEV_IO_MEM) ? pci_addr : PCI_NOWHERE;
- }
- return 0;
-}
-
-/* We are starting to get more complexity
- * surrounding writing ATEs, so pull
- * the writing code into this new function.
- */
-
-#if PCIBR_FREEZE_TIME
-#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, &freeze_time, cmd_regs)
-#else
-#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, cmd_regs)
-#endif
-
-LOCAL unsigned
-ate_freeze(pcibr_dmamap_t pcibr_dmamap,
-#if PCIBR_FREEZE_TIME
- unsigned *freeze_time_ptr,
-#endif
- unsigned *cmd_regs)
-{
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
-#ifdef LATER
- int dma_slot = pcibr_dmamap->bd_slot;
-#endif
- int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
- int slot;
-
- unsigned long s;
- unsigned cmd_reg;
- volatile unsigned *cmd_lwa;
- unsigned cmd_lwd;
-
- if (!ext_ates)
- return 0;
-
- /* Bridge Hardware Bug WAR #484930:
- * Bridge can't handle updating External ATEs
- * while DMA is occuring that uses External ATEs,
- * even if the particular ATEs involved are disjoint.
- */
-
- /* need to prevent anyone else from
- * unfreezing the grant while we
- * are working; also need to prevent
- * this thread from being interrupted
- * to keep PCI grant freeze time
- * at an absolute minimum.
- */
- s = pcibr_lock(pcibr_soft);
-
-#ifdef LATER
- /* just in case pcibr_dmamap_done was not called */
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
- pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
- atomic_dec(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
- xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
- }
-#endif /* LATER */
-#if PCIBR_FREEZE_TIME
- *freeze_time_ptr = get_timestamp();
-#endif
-
- cmd_lwa = 0;
- for (slot = 0; slot < 8; ++slot)
- if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
- cmd_reg = pcibr_soft->
- bs_slot[slot].
- bss_cmd_shadow;
- if (cmd_reg & PCI_CMD_BUS_MASTER) {
- cmd_lwa = pcibr_soft->
- bs_slot[slot].
- bss_cmd_pointer;
- cmd_lwd = cmd_reg ^ PCI_CMD_BUS_MASTER;
- cmd_lwa[0] = cmd_lwd;
- }
- cmd_regs[slot] = cmd_reg;
- } else
- cmd_regs[slot] = 0;
-
- if (cmd_lwa) {
- bridge_t *bridge = pcibr_soft->bs_base;
-
- /* Read the last master bit that has been cleared. This PIO read
- * on the PCI bus is to ensure the completion of any DMAs that
- * are due to bus requests issued by PCI devices before the
- * clearing of master bits.
- */
- cmd_lwa[0];
-
- /* Flush all the write buffers in the bridge */
- for (slot = 0; slot < 8; ++slot)
- if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
- /* Flush the write buffer associated with this
- * PCI device which might be using dma map RAM.
- */
- bridge->b_wr_req_buf[slot].reg;
- }
- }
- return s;
-}
-
-#define ATE_WRITE() ate_write(ate_ptr, ate_count, ate)
-
-LOCAL void
-ate_write(bridge_ate_p ate_ptr,
- int ate_count,
- bridge_ate_t ate)
-{
- while (ate_count-- > 0) {
- *ate_ptr++ = ate;
- ate += IOPGSIZE;
- }
-}
-
-
-#if PCIBR_FREEZE_TIME
-#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, ate, ate_total, freeze_time, cmd_regs, s)
-#else
-#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, cmd_regs, s)
-#endif
-
-LOCAL void
-ate_thaw(pcibr_dmamap_t pcibr_dmamap,
- int ate_index,
-#if PCIBR_FREEZE_TIME
- bridge_ate_t ate,
- int ate_total,
- unsigned freeze_time_start,
-#endif
- unsigned *cmd_regs,
- unsigned s)
-{
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
- int dma_slot = pcibr_dmamap->bd_slot;
- int slot;
- bridge_t *bridge = pcibr_soft->bs_base;
- int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
-
- unsigned cmd_reg;
-
-#if PCIBR_FREEZE_TIME
- unsigned freeze_time;
- static unsigned max_freeze_time = 0;
- static unsigned max_ate_total;
-#endif
-
- if (!ext_ates)
- return;
-
- /* restore cmd regs */
- for (slot = 0; slot < 8; ++slot)
- if ((cmd_reg = cmd_regs[slot]) & PCI_CMD_BUS_MASTER)
- bridge->b_type0_cfg_dev[slot].l[PCI_CFG_COMMAND / 4] = cmd_reg;
-
- pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_BUSY;
- atomic_inc(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
-
-#if PCIBR_FREEZE_TIME
- freeze_time = get_timestamp() - freeze_time_start;
-
- if ((max_freeze_time < freeze_time) ||
- (max_ate_total < ate_total)) {
- if (max_freeze_time < freeze_time)
- max_freeze_time = freeze_time;
- if (max_ate_total < ate_total)
- max_ate_total = ate_total;
- pcibr_unlock(pcibr_soft, s);
- printk("%s: pci freeze time %d usec for %d ATEs\n"
- "\tfirst ate: %R\n",
- pcibr_soft->bs_name,
- freeze_time * 1000 / 1250,
- ate_total,
- ate, ate_bits);
- } else
-#endif
- pcibr_unlock(pcibr_soft, s);
-}
-
-/*ARGSUSED */
-iopaddr_t
-pcibr_dmamap_addr(pcibr_dmamap_t pcibr_dmamap,
- paddr_t paddr,
- size_t req_size)
-{
- pcibr_soft_t pcibr_soft;
- iopaddr_t xio_addr;
- xwidgetnum_t xio_port;
- iopaddr_t pci_addr;
- unsigned flags;
-
- ASSERT(pcibr_dmamap != NULL);
- ASSERT(req_size > 0);
- ASSERT(req_size <= pcibr_dmamap->bd_max_size);
-
- pcibr_soft = pcibr_dmamap->bd_soft;
-
- flags = pcibr_dmamap->bd_flags;
-
- xio_addr = xtalk_dmamap_addr(pcibr_dmamap->bd_xtalk, paddr, req_size);
- if (XIO_PACKED(xio_addr)) {
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_dmamap->bd_xio_port;
-
- /* If this DMA is to an address that
- * refers back to this Bridge chip,
- * reduce it back to the correct
- * PCI MEM address.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
- } else if (flags & PCIIO_DMA_A64) {
- /* A64 DMA:
- * always use 64-bit direct mapping,
- * which always works.
- * Device(x) was set up during
- * dmamap allocation.
- */
-
- /* attributes are already bundled up into bd_pci_addr.
- */
- pci_addr = pcibr_dmamap->bd_pci_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT)
- | xio_addr;
-
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- pci_addr &= ~PCI64_ATTR_PREF;
-
-#if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmamap_addr (direct64):\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\tXIO port 0x%x offset 0x%x\n"
- "\treturning PCI 0x%x\n",
- paddr, paddr + req_size - 1,
- xio_port, xio_addr, pci_addr);
-#endif
- } else if (flags & PCIIO_FIXED) {
- /* A32 direct DMA:
- * always use 32-bit direct mapping,
- * which may fail.
- * Device(x) was set up during
- * dmamap allocation.
- */
-
- if (xio_port != pcibr_soft->bs_dir_xport)
- pci_addr = 0; /* wrong DIDN */
- else if (xio_addr < pcibr_dmamap->bd_xio_addr)
- pci_addr = 0; /* out of range */
- else if ((xio_addr + req_size) >
- (pcibr_dmamap->bd_xio_addr + BRIDGE_DMA_DIRECT_SIZE))
- pci_addr = 0; /* out of range */
- else
- pci_addr = pcibr_dmamap->bd_pci_addr +
- xio_addr - pcibr_dmamap->bd_xio_addr;
-
-#if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmamap_addr (direct32):\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\tXIO port 0x%x offset 0x%x\n"
- "\treturning PCI 0x%x\n",
- paddr, paddr + req_size - 1,
- xio_port, xio_addr, pci_addr);
-#endif
- } else {
- bridge_t *bridge = pcibr_soft->bs_base;
- iopaddr_t offset = IOPGOFF(xio_addr);
- bridge_ate_t ate_proto = pcibr_dmamap->bd_ate_proto;
- int ate_count = IOPG(offset + req_size - 1) + 1;
-
- int ate_index = pcibr_dmamap->bd_ate_index;
- unsigned cmd_regs[8];
- unsigned s;
-
-#if PCIBR_FREEZE_TIME
- int ate_total = ate_count;
- unsigned freeze_time;
-#endif
-
-#if PCIBR_ATE_DEBUG
- bridge_ate_t ate_cmp;
- bridge_ate_p ate_cptr;
- unsigned ate_lo, ate_hi;
- int ate_bad = 0;
- int ate_rbc = 0;
-#endif
- bridge_ate_p ate_ptr = pcibr_dmamap->bd_ate_ptr;
- bridge_ate_t ate;
-
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- ate_proto &= ~ATE_PREF;
-
- ate = ate_proto
- | (xio_port << ATE_TIDSHIFT)
- | (xio_addr - offset);
-
- pci_addr = pcibr_dmamap->bd_pci_addr + offset;
-
- /* Fill in our mapping registers
- * with the appropriate xtalk data,
- * and hand back the PCI address.
- */
-
- ASSERT(ate_count > 0);
- if (ate_count <= pcibr_dmamap->bd_ate_count) {
- ATE_FREEZE();
- ATE_WRITE();
- ATE_THAW();
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- } else {
- /* The number of ATE's required is greater than the number
- * allocated for this map. One way this can happen is if
- * pcibr_dmamap_alloc() was called with the PCIBR_NO_ATE_ROUNDUP
- * flag, and then when that map is used (right now), the
- * target address tells us we really did need to roundup.
- * The other possibility is that the map is just plain too
- * small to handle the requested target area.
- */
-#if PCIBR_ATE_DEBUG
- PRINT_WARNING( "pcibr_dmamap_addr :\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\tate_count 0x%x bd_ate_count 0x%x\n"
- "\tATE's required > number allocated\n",
- paddr, paddr + req_size - 1,
- ate_count, pcibr_dmamap->bd_ate_count);
-#endif
- pci_addr = 0;
- }
-
- }
- return pci_addr;
-}
-
-/*ARGSUSED */
-alenlist_t
-pcibr_dmamap_list(pcibr_dmamap_t pcibr_dmamap,
- alenlist_t palenlist,
- unsigned flags)
-{
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge=NULL;
-
- unsigned al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
- int inplace = flags & PCIIO_INPLACE;
-
- alenlist_t pciio_alenlist = 0;
- alenlist_t xtalk_alenlist;
- size_t length;
- iopaddr_t offset;
- unsigned direct64;
- int ate_index = 0;
- int ate_count = 0;
- int ate_total = 0;
- bridge_ate_p ate_ptr = (bridge_ate_p)0;
- bridge_ate_t ate_proto = (bridge_ate_t)0;
- bridge_ate_t ate_prev;
- bridge_ate_t ate;
- alenaddr_t xio_addr;
- xwidgetnum_t xio_port;
- iopaddr_t pci_addr;
- alenaddr_t new_addr;
-
- unsigned cmd_regs[8];
- unsigned s = 0;
-
-#if PCIBR_FREEZE_TIME
- unsigned freeze_time;
-#endif
- int ate_freeze_done = 0; /* To pair ATE_THAW
- * with an ATE_FREEZE
- */
-
- pcibr_soft = pcibr_dmamap->bd_soft;
-
- xtalk_alenlist = xtalk_dmamap_list(pcibr_dmamap->bd_xtalk, palenlist,
- flags & DMAMAP_FLAGS);
- if (!xtalk_alenlist)
- goto fail;
-
- alenlist_cursor_init(xtalk_alenlist, 0, NULL);
-
- if (inplace) {
- pciio_alenlist = xtalk_alenlist;
- } else {
- pciio_alenlist = alenlist_create(al_flags);
- if (!pciio_alenlist)
- goto fail;
- }
-
- direct64 = pcibr_dmamap->bd_flags & PCIIO_DMA_A64;
- if (!direct64) {
- bridge = pcibr_soft->bs_base;
- ate_ptr = pcibr_dmamap->bd_ate_ptr;
- ate_index = pcibr_dmamap->bd_ate_index;
- ate_proto = pcibr_dmamap->bd_ate_proto;
- ATE_FREEZE();
- ate_freeze_done = 1; /* Remember that we need to do an ATE_THAW */
- }
- pci_addr = pcibr_dmamap->bd_pci_addr;
-
- ate_prev = 0; /* matches no valid ATEs */
- while (ALENLIST_SUCCESS ==
- alenlist_get(xtalk_alenlist, NULL, 0,
- &xio_addr, &length, al_flags)) {
- if (XIO_PACKED(xio_addr)) {
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_dmamap->bd_xio_port;
-
- if (xio_port == pcibr_soft->bs_xid) {
- new_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, length);
- if (new_addr == PCI_NOWHERE)
- goto fail;
- } else if (direct64) {
- new_addr = pci_addr | xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
-
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- new_addr &= ~PCI64_ATTR_PREF;
-
- } else {
- /* calculate the ate value for
- * the first address. If it
- * matches the previous
- * ATE written (ie. we had
- * multiple blocks in the
- * same IOPG), then back up
- * and reuse that ATE.
- *
- * We are NOT going to
- * aggressively try to
- * reuse any other ATEs.
- */
- offset = IOPGOFF(xio_addr);
- ate = ate_proto
- | (xio_port << ATE_TIDSHIFT)
- | (xio_addr - offset);
- if (ate == ate_prev) {
-#if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_list: ATE share\n");
-#endif
- ate_ptr--;
- ate_index--;
- pci_addr -= IOPGSIZE;
- }
- new_addr = pci_addr + offset;
-
- /* Fill in the hardware ATEs
- * that contain this block.
- */
- ate_count = IOPG(offset + length - 1) + 1;
- ate_total += ate_count;
-
- /* Ensure that this map contains enough ATE's */
- if (ate_total > pcibr_dmamap->bd_ate_count) {
-#if PCIBR_ATE_DEBUG
- PRINT_WARNING( "pcibr_dmamap_list :\n"
- "\twanted xio_addr [0x%x..0x%x]\n"
- "\tate_total 0x%x bd_ate_count 0x%x\n"
- "\tATE's required > number allocated\n",
- xio_addr, xio_addr + length - 1,
- ate_total, pcibr_dmamap->bd_ate_count);
-#endif
- goto fail;
- }
-
- ATE_WRITE();
-
- ate_index += ate_count;
- ate_ptr += ate_count;
-
- ate_count <<= IOPFNSHIFT;
- ate += ate_count;
- pci_addr += ate_count;
- }
-
- /* write the PCI DMA address
- * out to the scatter-gather list.
- */
- if (inplace) {
- if (ALENLIST_SUCCESS !=
- alenlist_replace(pciio_alenlist, NULL,
- &new_addr, &length, al_flags))
- goto fail;
- } else {
- if (ALENLIST_SUCCESS !=
- alenlist_append(pciio_alenlist,
- new_addr, length, al_flags))
- goto fail;
- }
- }
- if (!inplace)
- alenlist_done(xtalk_alenlist);
-
- /* Reset the internal cursor of the alenlist to be returned back
- * to the caller.
- */
- alenlist_cursor_init(pciio_alenlist, 0, NULL);
-
-
- /* In case an ATE_FREEZE was done do the ATE_THAW to unroll all the
- * changes that ATE_FREEZE has done to implement the external SSRAM
- * bug workaround.
- */
- if (ate_freeze_done) {
- ATE_THAW();
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- return pciio_alenlist;
-
- fail:
- /* There are various points of failure after doing an ATE_FREEZE
- * We need to do an ATE_THAW. Otherwise the ATEs are locked forever.
- * The decision to do an ATE_THAW needs to be based on whether a
- * an ATE_FREEZE was done before.
- */
- if (ate_freeze_done) {
- ATE_THAW();
- bridge->b_wid_tflush;
- }
- if (pciio_alenlist && !inplace)
- alenlist_destroy(pciio_alenlist);
- return 0;
-}
-
-/*ARGSUSED */
-void
-pcibr_dmamap_done(pcibr_dmamap_t pcibr_dmamap)
-{
- /*
- * We could go through and invalidate ATEs here;
- * for performance reasons, we don't.
- * We also don't enforce the strict alternation
- * between _addr/_list and _done, but Hub does.
- */
-
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
- pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
-
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
- atomic_dec(&(pcibr_dmamap->bd_soft->bs_slot[pcibr_dmamap->bd_slot]. bss_ext_ates_active));
- }
-
- xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
-}
-
-
-/*
- * For each bridge, the DIR_OFF value in the Direct Mapping Register
- * determines the PCI to Crosstalk memory mapping to be used for all
- * 32-bit Direct Mapping memory accesses. This mapping can be to any
- * node in the system. This function will return that compact node id.
- */
-
-/*ARGSUSED */
-cnodeid_t
-pcibr_get_dmatrans_node(devfs_handle_t pconn_vhdl)
-{
-
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
-
- return(NASID_TO_COMPACT_NODEID(NASID_GET(pcibr_soft->bs_dir_xbase)));
-}
-
-/*ARGSUSED */
-iopaddr_t
-pcibr_dmatrans_addr(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- paddr_t paddr,
- size_t req_size,
- unsigned flags)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
-
- xwidgetnum_t xio_port;
- iopaddr_t xio_addr;
- iopaddr_t pci_addr;
-
- int have_rrbs;
- int min_rrbs;
-
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
-
- xio_addr = xtalk_dmatrans_addr(xconn_vhdl, 0, paddr, req_size,
- flags & DMAMAP_FLAGS);
-
- if (!xio_addr) {
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- return 0;
- }
- /*
- * find which XIO port this goes to.
- */
- if (XIO_PACKED(xio_addr)) {
- if (xio_addr == XIO_NOWHERE) {
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- return 0;
- }
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
-
- } else
- xio_port = pcibr_soft->bs_mxid;
-
- /*
- * If this DMA comes back to us,
- * return the PCI MEM address on
- * which it would land, or NULL
- * if the target is something
- * on bridge other than PCI MEM.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
- return pci_addr;
- }
- /* If the caller can use A64, try to
- * satisfy the request with the 64-bit
- * direct map. This can fail if the
- * configuration bits in Device(x)
- * conflict with our flags.
- */
-
- if (flags & PCIIO_DMA_A64) {
- pci_addr = slotp->bss_d64_base;
- if (!(flags & PCIBR_VCHAN1))
- flags |= PCIBR_VCHAN0;
- if ((pci_addr != PCIBR_D64_BASE_UNSET) &&
- (flags == slotp->bss_d64_flags)) {
-
- pci_addr |= xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
-
-#if DEBUG && PCIBR_DMA_DEBUG
-#if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
-#endif
- printk("pcibr_dmatrans_addr: [reuse]\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tdirect 64bit address is 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, pci_addr);
-#endif
- return (pci_addr);
- }
- if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS)) {
- pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
- slotp->bss_d64_flags = flags;
- slotp->bss_d64_base = pci_addr;
- pci_addr |= xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
-
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
- if (flags & PCIBR_VCHAN1)
- pciio_slot += PCIBR_RRB_SLOT_VIRTUAL;
- have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
- if (have_rrbs < 2) {
- if (pci_addr & PCI64_ATTR_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
- }
- }
-#if PCIBR_DMA_DEBUG
-#if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
-#endif
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tdirect 64bit address is 0x%x\n"
- "\tnew flags: 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, pci_addr, (uint64_t) flags);
-#endif
- return (pci_addr);
- }
- /* our flags conflict with Device(x).
- */
- flags = flags
- & ~PCIIO_DMA_A64
- & ~PCIBR_VCHAN0
- ;
-
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tUnable to set Device(x) bits for Direct-64\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- }
- /* Try to satisfy the request with the 32-bit direct
- * map. This can fail if the configuration bits in
- * Device(x) conflict with our flags, or if the
- * target address is outside where DIR_OFF points.
- */
- {
- size_t map_size = 1ULL << 31;
- iopaddr_t xio_base = pcibr_soft->bs_dir_xbase;
- iopaddr_t offset = xio_addr - xio_base;
- iopaddr_t endoff = req_size + offset;
-
- if ((req_size > map_size) ||
- (xio_addr < xio_base) ||
- (xio_port != pcibr_soft->bs_dir_xport) ||
- (endoff > map_size)) {
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\txio region outside direct32 target\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- } else {
- pci_addr = slotp->bss_d32_base;
- if ((pci_addr != PCIBR_D32_BASE_UNSET) &&
- (flags == slotp->bss_d32_flags)) {
-
- pci_addr |= offset;
-
-#if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr: [reuse]\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tmapped via direct32 offset 0x%x\n"
- "\twill DMA via pci addr 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, offset, pci_addr);
-#endif
- return (pci_addr);
- }
- if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS)) {
-
- pci_addr = PCI32_DIRECT_BASE;
- slotp->bss_d32_flags = flags;
- slotp->bss_d32_base = pci_addr;
- pci_addr |= offset;
-
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
- have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
- if (have_rrbs < 2) {
- if (slotp->bss_device & BRIDGE_DEV_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
- }
- }
-#if PCIBR_DMA_DEBUG
-#if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
-#endif
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tmapped via direct32 offset 0x%x\n"
- "\twill DMA via pci addr 0x%x\n"
- "\tnew flags: 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, offset, pci_addr, (uint64_t) flags);
-#endif
- return (pci_addr);
- }
- /* our flags conflict with Device(x).
- */
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tUnable to set Device(x) bits for Direct-32\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- }
- }
-
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n"
- "\tno acceptable PCI address found or constructable\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
-
- return 0;
-}
-
-/*ARGSUSED */
-alenlist_t
-pcibr_dmatrans_list(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- alenlist_t palenlist,
- unsigned flags)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
- xwidgetnum_t xio_port;
-
- alenlist_t pciio_alenlist = 0;
- alenlist_t xtalk_alenlist = 0;
-
- int inplace;
- unsigned direct64;
- unsigned al_flags;
-
- iopaddr_t xio_base;
- alenaddr_t xio_addr;
- size_t xio_size;
-
- size_t map_size;
- iopaddr_t pci_base;
- alenaddr_t pci_addr;
-
- unsigned relbits = 0;
-
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
-
- inplace = flags & PCIIO_INPLACE;
- direct64 = flags & PCIIO_DMA_A64;
- al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
-
- if (direct64) {
- map_size = 1ull << 48;
- xio_base = 0;
- pci_base = slotp->bss_d64_base;
- if ((pci_base != PCIBR_D64_BASE_UNSET) &&
- (flags == slotp->bss_d64_flags)) {
- /* reuse previous base info */
- } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS) < 0) {
- /* DMA configuration conflict */
- goto fail;
- } else {
- relbits = BRIDGE_DEV_D64_BITS;
- pci_base =
- pcibr_flags_to_d64(flags, pcibr_soft);
- }
- } else {
- xio_base = pcibr_soft->bs_dir_xbase;
- map_size = 1ull << 31;
- pci_base = slotp->bss_d32_base;
- if ((pci_base != PCIBR_D32_BASE_UNSET) &&
- (flags == slotp->bss_d32_flags)) {
- /* reuse previous base info */
- } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS) < 0) {
- /* DMA configuration conflict */
- goto fail;
- } else {
- relbits = BRIDGE_DEV_D32_BITS;
- pci_base = PCI32_DIRECT_BASE;
- }
- }
-
- xtalk_alenlist = xtalk_dmatrans_list(xconn_vhdl, 0, palenlist,
- flags & DMAMAP_FLAGS);
- if (!xtalk_alenlist)
- goto fail;
-
- alenlist_cursor_init(xtalk_alenlist, 0, NULL);
-
- if (inplace) {
- pciio_alenlist = xtalk_alenlist;
- } else {
- pciio_alenlist = alenlist_create(al_flags);
- if (!pciio_alenlist)
- goto fail;
- }
-
- while (ALENLIST_SUCCESS ==
- alenlist_get(xtalk_alenlist, NULL, 0,
- &xio_addr, &xio_size, al_flags)) {
-
- /*
- * find which XIO port this goes to.
- */
- if (XIO_PACKED(xio_addr)) {
- if (xio_addr == XIO_NOWHERE) {
-#if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:\n"
- "\tpciio connection point %v\n"
- "\txtalk connection point %v\n"
- "\twanted paddr [0x%x..0x%x]\n"
- "\txtalk_dmatrans_addr returned 0x%x\n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
-#endif
- return 0;
- }
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_soft->bs_mxid;
-
- /*
- * If this DMA comes back to us,
- * return the PCI MEM address on
- * which it would land, or NULL
- * if the target is something
- * on bridge other than PCI MEM.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, xio_size);
- if ( (pci_addr == (alenaddr_t)NULL) )
- goto fail;
- } else if (direct64) {
- ASSERT(xio_port != 0);
- pci_addr = pci_base | xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
- } else {
- iopaddr_t offset = xio_addr - xio_base;
- iopaddr_t endoff = xio_size + offset;
-
- if ((xio_size > map_size) ||
- (xio_addr < xio_base) ||
- (xio_port != pcibr_soft->bs_dir_xport) ||
- (endoff > map_size))
- goto fail;
-
- pci_addr = pci_base + (xio_addr - xio_base);
- }
-
- /* write the PCI DMA address
- * out to the scatter-gather list.
- */
- if (inplace) {
- if (ALENLIST_SUCCESS !=
- alenlist_replace(pciio_alenlist, NULL,
- &pci_addr, &xio_size, al_flags))
- goto fail;
- } else {
- if (ALENLIST_SUCCESS !=
- alenlist_append(pciio_alenlist,
- pci_addr, xio_size, al_flags))
- goto fail;
- }
- }
-
- if (relbits) {
- if (direct64) {
- slotp->bss_d64_flags = flags;
- slotp->bss_d64_base = pci_base;
- } else {
- slotp->bss_d32_flags = flags;
- slotp->bss_d32_base = pci_base;
- }
- }
- if (!inplace)
- alenlist_done(xtalk_alenlist);
-
- /* Reset the internal cursor of the alenlist to be returned back
- * to the caller.
- */
- alenlist_cursor_init(pciio_alenlist, 0, NULL);
- return pciio_alenlist;
-
- fail:
- if (relbits)
- pcibr_release_device(pcibr_soft, pciio_slot, relbits);
- if (pciio_alenlist && !inplace)
- alenlist_destroy(pciio_alenlist);
- return 0;
-}
-
-void
-pcibr_dmamap_drain(pcibr_dmamap_t map)
-{
- xtalk_dmamap_drain(map->bd_xtalk);
-}
-
-void
-pcibr_dmaaddr_drain(devfs_handle_t pconn_vhdl,
- paddr_t paddr,
- size_t bytes)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
-
- xtalk_dmaaddr_drain(xconn_vhdl, paddr, bytes);
-}
-
-void
-pcibr_dmalist_drain(devfs_handle_t pconn_vhdl,
- alenlist_t list)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
-
- xtalk_dmalist_drain(xconn_vhdl, list);
-}
-
-/*
- * Get the starting PCIbus address out of the given DMA map.
- * This function is supposed to be used by a close friend of PCI bridge
- * since it relies on the fact that the starting address of the map is fixed at
- * the allocation time in the current implementation of PCI bridge.
- */
-iopaddr_t
-pcibr_dmamap_pciaddr_get(pcibr_dmamap_t pcibr_dmamap)
-{
- return (pcibr_dmamap->bd_pci_addr);
-}
-
-/* =====================================================================
- * INTERRUPT MANAGEMENT
- */
-
-static unsigned
-pcibr_intr_bits(pciio_info_t info,
- pciio_intr_line_t lines)
-{
- pciio_slot_t slot = pciio_info_slot_get(info);
- unsigned bbits = 0;
-
- /*
- * Currently favored mapping from PCI
- * slot number and INTA/B/C/D to Bridge
- * PCI Interrupt Bit Number:
- *
- * SLOT A B C D
- * 0 0 4 0 4
- * 1 1 5 1 5
- * 2 2 6 2 6
- * 3 3 7 3 7
- * 4 4 0 4 0
- * 5 5 1 5 1
- * 6 6 2 6 2
- * 7 7 3 7 3
- */
-
- if (slot < 8) {
- if (lines & (PCIIO_INTR_LINE_A| PCIIO_INTR_LINE_C))
- bbits |= 1 << slot;
- if (lines & (PCIIO_INTR_LINE_B| PCIIO_INTR_LINE_D))
- bbits |= 1 << (slot ^ 4);
- }
- return bbits;
-}
-
-
-/*
- * Get the next wrapper pointer queued in the interrupt circular buffer.
- */
-#ifdef KERNEL_THREADS
-pcibr_intr_wrap_t
-pcibr_wrap_get(pcibr_intr_cbuf_t cbuf)
-{
- pcibr_intr_wrap_t wrap;
-
- if (cbuf->ib_in == cbuf->ib_out)
- PRINT_PANIC("pcibr intr circular buffer empty, cbuf=0x%x, ib_in=ib_out=%d\n",
- cbuf, cbuf->ib_out);
-
- wrap = cbuf->ib_cbuf[cbuf->ib_out++];
- cbuf->ib_out = cbuf->ib_out % IBUFSIZE;
- return(wrap);
-}
-
-/*
- * Queue a wrapper pointer in the interrupt circular buffer.
- */
-void
-pcibr_wrap_put(pcibr_intr_wrap_t wrap, pcibr_intr_cbuf_t cbuf)
-{
- int in;
- unsigned long s;
-
- /*
- * Multiple CPUs could be executing this code simultaneously
- * if a handler has registered multiple interrupt lines and
- * the interrupts are directed to different CPUs.
- */
- s = mutex_spinlock(&cbuf->ib_lock);
- in = (cbuf->ib_in + 1) % IBUFSIZE;
- if (in == cbuf->ib_out)
- PRINT_PANIC("pcibr intr circular buffer full, cbuf=0x%x, ib_in=%d\n",
- cbuf, cbuf->ib_in);
-
- cbuf->ib_cbuf[cbuf->ib_in] = wrap;
- cbuf->ib_in = in;
- mutex_spinunlock(&cbuf->ib_lock, s);
- return;
-}
-#endif /* KERNEL_THREADS */
-
-/*
- * There are end cases where a deadlock can occur if interrupt
- * processing completes and the Bridge b_int_status bit is still set.
- *
- * One scenerio is if a second PCI interrupt occurs within 60ns of
- * the previous interrupt being cleared. In this case the Bridge
- * does not detect the transition, the Bridge b_int_status bit
- * remains set, and because no transition was detected no interrupt
- * packet is sent to the Hub/Heart.
- *
- * A second scenerio is possible when a b_int_status bit is being
- * shared by multiple devices:
- * Device #1 generates interrupt
- * Bridge b_int_status bit set
- * Device #2 generates interrupt
- * interrupt processing begins
- * ISR for device #1 runs and
- * clears interrupt
- * Device #1 generates interrupt
- * ISR for device #2 runs and
- * clears interrupt
- * (b_int_status bit still set)
- * interrupt processing completes
- *
- * Interrupt processing is now complete, but an interrupt is still
- * outstanding for Device #1. But because there was no transition of
- * the b_int_status bit, no interrupt packet will be generated and
- * a deadlock will occur.
- *
- * To avoid these deadlock situations, this function is used
- * to check if a specific Bridge b_int_status bit is set, and if so,
- * cause the setting of the corresponding interrupt bit.
- *
- * On a XBridge (IP35), we do this by writing the appropriate Bridge Force
- * Interrupt register.
- */
-void
-pcibr_force_interrupt(pcibr_intr_wrap_t wrap)
-{
- unsigned bit;
- pcibr_soft_t pcibr_soft = wrap->iw_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- cpuid_t cpuvertex_to_cpuid(devfs_handle_t vhdl);
-
- bit = wrap->iw_intr;
-
- if (pcibr_soft->bs_xbridge) {
- bridge->b_force_pin[bit].intr = 1;
- } else if ((1 << bit) & *wrap->iw_stat) {
- cpuid_t cpu;
- unsigned intr_bit;
- xtalk_intr_t xtalk_intr =
- pcibr_soft->bs_intr[bit].bsi_xtalk_intr;
-
- intr_bit = (short) xtalk_intr_vector_get(xtalk_intr);
- cpu = cpuvertex_to_cpuid(xtalk_intr_cpu_get(xtalk_intr));
- REMOTE_CPU_SEND_INTR(cpu, intr_bit);
- }
-}
-
-/* Wrapper for pcibr interrupt threads. */
-#ifdef KERNEL_THREADS
-static void
-pcibr_intrd(pcibr_intr_t intr)
-{
- pcibr_intr_wrap_t wrap;
-
- /* Called on each restart */
- ASSERT(cpuid() == intr->bi_mustruncpu);
-
-#ifdef ITHREAD_LATENCY
- xthread_update_latstats(intr->bi_tinfo.thd_latstats);
-#endif /* ITHREAD_LATENCY */
-
- ASSERT(intr->bi_func != NULL);
- intr->bi_func(intr->bi_arg); /* Invoke the interrupt handler */
-
- /*
- * The pcibr_intrd thread needs access to the wrapper struct
- * specific to the current interrupt it is processing. Because
- * multiple calls/wakeups to the thread could be queued, each
- * potentially from a different interrupt line (PCIIO_INTR_LINE_A,
- * etc), multiple wrapper struct pointers need to be queued. This
- * is done via a circular buffer of wrapper struct pointers.
- */
- wrap = pcibr_wrap_get(&intr->bi_ibuf);
-
- /*
- * The interrupt handler has completed. Now decrement the running
- * count tracking the number of handlers still running for this line.
- * If this was the last handler to complete (i.e., iw_hdlrcnt == 0),
- * avoid a potential deadlock condition and ensure that another
- * interrupt will occur if the Bridge b_int_status bit is still
- * set.
- */
- atomicAddInt(&(wrap->iw_hdlrcnt), -1);
- if (wrap->iw_hdlrcnt == 0)
- pcibr_force_interrupt(wrap);
-
- ipsema(&intr->bi_tinfo.thd_isync); /* Sleep 'till next interrupt */
- /* NOTREACHED */
-}
-
-static void
-pcibr_intrd_start(pcibr_intr_t intr)
-{
- ASSERT(intr->bi_mustruncpu >= 0);
- setmustrun(intr->bi_mustruncpu);
-
- xthread_set_func(KT_TO_XT(curthreadp), (xt_func_t *)pcibr_intrd, (void *)intr);
- atomicSetInt(&intr->bi_tinfo.thd_flags, THD_INIT);
- ipsema(&intr->bi_tinfo.thd_isync); /* Comes out in pcibr_intrd */
- /* NOTREACHED */
-}
-
-
-static void
-pcibr_thread_setup(pcibr_intr_t intr, int bridge_levels, ilvl_t intr_swlevel)
-{
- char thread_name[32];
-
- sprintf(thread_name, "pcibr_intrd[0x%x]", bridge_levels);
- thread_name[IT_NAMELEN-1] = '\0';
-
- /* XXX need to adjust priority whenever an interrupt is connected */
- intr->bi_tinfo.thd_pri = intr_swlevel;
- atomicSetInt(&intr->bi_tinfo.thd_flags, THD_ISTHREAD | THD_REG);
- xthread_setup(thread_name, intr_swlevel, &intr->bi_tinfo,
- (xt_func_t *)pcibr_intrd_start,
- (void *)intr);
-}
-#endif /* KERNEL_THREADS */
-
-
-
-/*ARGSUSED */
-pcibr_intr_t
-pcibr_intr_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_intr_line_t lines,
- devfs_handle_t owner_dev)
-{
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pcibr_info->f_slot;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- bridge_t *bridge = pcibr_soft->bs_base;
- int is_threaded = 0;
-#ifdef KERNEL_THREADS
- cpuid_t mustruncpu = CPU_NONE;
- cpuid_t old_intrcpu = CPU_NONE;
-#endif
- int thread_swlevel;
-
- xtalk_intr_t *xtalk_intr_p;
- pcibr_intr_t *pcibr_intr_p;
- pcibr_intr_list_t *intr_list_p;
-
- unsigned pcibr_int_bits;
- unsigned pcibr_int_bit;
- xtalk_intr_t xtalk_intr = (xtalk_intr_t)0;
- hub_intr_t hub_intr;
- pcibr_intr_t pcibr_intr;
- pcibr_intr_list_t intr_entry;
- pcibr_intr_list_t intr_list;
- bridgereg_t int_dev;
-
-#if DEBUG && INTR_DEBUG
- printk("%v: pcibr_intr_alloc\n"
- "%v:%s%s%s%s%s\n",
- owner_dev, pconn_vhdl,
- !(lines & 15) ? " No INTs?" : "",
- lines & 1 ? " INTA" : "",
- lines & 2 ? " INTB" : "",
- lines & 4 ? " INTC" : "",
- lines & 8 ? " INTD" : "");
-#endif
-
- NEW(pcibr_intr);
- if (!pcibr_intr)
- return NULL;
-
- if (dev_desc) {
- cpuid_t intr_target_from_desc(device_desc_t, int);
-
-#ifdef KERNEL_THREADS
- is_threaded = !(device_desc_flags_get(dev_desc) & D_INTR_NOTHREAD);
- if (is_threaded) {
- /*
- * If the device descriptor contains interrupt target info,
- * save the CPU requested. This is the CPU the pcibr_intrd
- * thread will be set to run on.
- *
- * We need to get the interrupt target info at this time, because
- * the original intr_target value can be overwritten, as part of
- * the xtalk_intr_alloc_nothd() call, with the actual interrupt CPU.
- * This can be different than the requested CPU if the lower layers
- * could not direct the hardware interrupt to the requested CPU.
- * Regardless of which CPU processes the hardware interrupt, the
- * ISR thread will still be setup to run on the CPU originally
- * requested.
- */
- mustruncpu = intr_target_from_desc(dev_desc, SUBNODE_ANY);
- thread_swlevel = device_desc_intr_swlevel_get(dev_desc);
- }
-#endif /* KERNEL_THREADS */
- } else {
- extern int default_intr_pri;
-
- is_threaded = 1; /* PCI interrupts are threaded, by default */
- thread_swlevel = default_intr_pri;
- }
-
- pcibr_intr->bi_dev = pconn_vhdl;
- pcibr_intr->bi_lines = lines;
- pcibr_intr->bi_soft = pcibr_soft;
- pcibr_intr->bi_ibits = 0; /* bits will be added below */
- pcibr_intr->bi_func = 0; /* unset until connect */
- pcibr_intr->bi_arg = 0; /* unset until connect */
- pcibr_intr->bi_flags = is_threaded ? 0 : PCIIO_INTR_NOTHREAD;
- pcibr_intr->bi_mustruncpu = CPU_NONE;
-#ifdef KERNEL_THREADS
- pcibr_intr->bi_ibuf.ib_in = 0;
- pcibr_intr->bi_ibuf.ib_out = 0;
-#endif
- mutex_spinlock_init(&pcibr_intr->bi_ibuf.ib_lock);
-
- pcibr_int_bits = pcibr_soft->bs_intr_bits((pciio_info_t)pcibr_info, lines);
-
-
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and make sure there are xtalk resources
- * allocated for it.
- */
-#if DEBUG && INTR_DEBUG
- printk("pcibr_int_bits: 0x%X\n", pcibr_int_bits);
-#endif
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit ++) {
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- xtalk_intr_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
-
- xtalk_intr = *xtalk_intr_p;
-
- if (xtalk_intr == NULL) {
- /*
- * This xtalk_intr_alloc is constrained for two reasons:
- * 1) Normal interrupts and error interrupts need to be delivered
- * through a single xtalk target widget so that there aren't any
- * ordering problems with DMA, completion interrupts, and error
- * interrupts. (Use of xconn_vhdl forces this.)
- *
- * 2) On IP35, addressing constraints on IP35 and Bridge force
- * us to use a single PI number for all interrupts from a
- * single Bridge. (IP35-specific code forces this, and we
- * verify in pcibr_setwidint.)
- */
-
- /*
- * All code dealing with threaded PCI interrupt handlers
- * is located at the pcibr level. Because of this,
- * we always want the lower layers (hub/heart_intr_alloc,
- * intr_level_connect) to treat us as non-threaded so we
- * don't set up a duplicate threaded environment. We make
- * this happen by calling a special xtalk interface.
- */
- xtalk_intr = xtalk_intr_alloc_nothd(xconn_vhdl, dev_desc,
- owner_dev);
-#if DEBUG && INTR_DEBUG
- printk("%v: xtalk_intr=0x%X\n", xconn_vhdl, xtalk_intr);
-#endif
-
- /* both an assert and a runtime check on this:
- * we need to check in non-DEBUG kernels, and
- * the ASSERT gets us more information when
- * we use DEBUG kernels.
- */
- ASSERT(xtalk_intr != NULL);
- if (xtalk_intr == NULL) {
- /* it is quite possible that our
- * xtalk_intr_alloc failed because
- * someone else got there first,
- * and we can find their results
- * in xtalk_intr_p.
- */
- if (!*xtalk_intr_p) {
-#ifdef SUPPORT_PRINTING_V_FORMAT
- PRINT_ALERT(
- "pcibr_intr_alloc %v: unable to get xtalk interrupt resources",
- xconn_vhdl);
-#endif
- /* yes, we leak resources here. */
- return 0;
- }
- } else if (compare_and_swap_ptr((void **) xtalk_intr_p, NULL, xtalk_intr)) {
- /*
- * now tell the bridge which slot is
- * using this interrupt line.
- */
- int_dev = bridge->b_int_device;
- int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
- int_dev |= pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit);
- bridge->b_int_device = int_dev; /* XXXMP */
-
-#if DEBUG && INTR_DEBUG
- printk("%v: bridge intr bit %d clears my wrb\n",
- pconn_vhdl, pcibr_int_bit);
-#endif
- } else {
- /* someone else got one allocated first;
- * free the one we just created, and
- * retrieve the one they allocated.
- */
- xtalk_intr_free(xtalk_intr);
- xtalk_intr = *xtalk_intr_p;
-#if PARANOID
- /* once xtalk_intr is set, we never clear it,
- * so if the CAS fails above, this condition
- * can "never happen" ...
- */
- if (!xtalk_intr) {
- PRINT_ALERT(
- "pcibr_intr_alloc %v: unable to set xtalk interrupt resources",
- xconn_vhdl);
- /* yes, we leak resources here. */
- return 0;
- }
-#endif
- }
- }
-
-#ifdef KERNEL_THREADS
- if (is_threaded) {
- cpuid_t intrcpu = cpuvertex_to_cpuid(xtalk_intr_cpu_get(xtalk_intr));
-
- /*
- * It is possible that 2 (or more) interrupts originating on a
- * single Bridge and used by a single device were assigned to
- * different CPUs. If this occurs issue a warning message for
- * this sub-optimal configuration. There are two ways this
- * could happen:
- *
- * - There were insufficient xtalk interrupt resources to
- * allow all interrupts to be assigned to the same CPU.
- * This is an unlikely case, but could happen if someone
- * tries to target a lot of interrupts to a single CPU.
- *
- * - If there is no device descriptor associated with this
- * device, the xtalk/hub/heart layers will not know to
- * assign the same CPU to any additional interrupts this
- * driver has specified, and will perform the normal load
- * leveling of interrupts across CPUs.
- * (The lower layers store the CPU assigned to the first
- * interrupt in the device desc, if present, and then when
- * called again for additional interrupts for the same device,
- * use this information to assign the same CPU to these
- * interrupts.)
- */
- if ((old_intrcpu != CPU_NONE) && (old_intrcpu != intrcpu)) {
-#if defined(SUPPORT_PRINTING_V_FORMAT)
- PRINT_WARNING("Conflict on where to schedule interrupts for %v\n", pconn_vhdl);
-#else
- PRINT_WARNING("Conflict on where to schedule interrupts for 0x%x\n", pconn_vhdl);
-#endif
- PRINT_WARNING("(on cpu %d or on cpu %d), cpu %d used\n", old_intrcpu, intrcpu, intrcpu);
- }
- if (old_intrcpu == CPU_NONE)
- old_intrcpu = intrcpu;
- /*
- * For threaded drivers, set the interrupt thread to run wherever
- * the interrupt is targeted, or where requested in the dev_desc.
- */
- if (mustruncpu != CPU_NONE) {
- pcibr_intr->bi_mustruncpu = mustruncpu;
- if (mustruncpu != intrcpu) {
- PRINT_WARNING("Request to target PCI interrupts to CPU %d could not\n"
- " be satisfied, CPU %d used. However, interrupt thread\n"
- " pcibr_intrd will run on CPU %d as requested.\n"
- " %v (0x%x)\n",
- mustruncpu, intrcpu, mustruncpu, owner_dev,
- owner_dev);
- }
- } else {
- pcibr_intr->bi_mustruncpu = intrcpu;
- }
- ASSERT(pcibr_intr->bi_mustruncpu >= 0);
-
- }
-#endif /* KERNEL_THREADS */
-
- pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;
-
- NEW(intr_entry);
- intr_entry->il_next = NULL;
- intr_entry->il_intr = pcibr_intr;
- intr_entry->il_wrbf = &(bridge->b_wr_req_buf[pciio_slot].reg);
- intr_list_p =
- &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
-#if DEBUG && INTR_DEBUG
-#if defined(SUPPORT_PRINTING_V_FORMAT)
- printk("0x%x: Bridge bit %d wrap=0x%x\n",
- pconn_vhdl, pcibr_int_bit,
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);
-#else
- printk("%v: Bridge bit %d wrap=0x%x\n",
- pconn_vhdl, pcibr_int_bit,
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);
-#endif
-#endif
-
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* we are the first interrupt on this bridge bit.
- */
-#if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) allocated [FIRST]\n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
-#endif
- continue;
- }
- intr_list = *intr_list_p;
- pcibr_intr_p = &intr_list->il_intr;
- if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
- /* first entry on list was erased,
- * and we replaced it, so we
- * don't need our intr_entry.
- */
- DEL(intr_entry);
-#if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) replaces erased first\n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
-#endif
- continue;
- }
- intr_list_p = &intr_list->il_next;
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* we are the new second interrupt on this bit.
- */
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared = 1;
-#if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) is new SECOND\n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
-#endif
- continue;
- }
- while (1) {
- pcibr_intr_p = &intr_list->il_intr;
- if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
- /* an entry on list was erased,
- * and we replaced it, so we
- * don't need our intr_entry.
- */
- DEL(intr_entry);
-#if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) replaces erased Nth\n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
-#endif
- break;
- }
- intr_list_p = &intr_list->il_next;
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* entry appended to share list
- */
-#if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) is new Nth\n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
-#endif
- break;
- }
- /* step to next record in chain
- */
- intr_list = *intr_list_p;
- }
- }
- }
-
-#ifdef KERNEL_THREADS
- if (is_threaded) {
- /* Set pcibr_intr->bi_tinfo */
- pcibr_thread_setup(pcibr_intr, pcibr_int_bits, thread_swlevel);
- ASSERT(!(pcibr_intr->bi_flags & PCIIO_INTR_CONNECTED));
- }
-#endif /* KERNEL_THREADS */
-
-#if DEBUG && INTR_DEBUG
- printk("%v pcibr_intr_alloc complete\n", pconn_vhdl);
-#endif
- hub_intr = (hub_intr_t)xtalk_intr;
- pcibr_intr->bi_irq = hub_intr->i_bit;
- pcibr_intr->bi_cpu = hub_intr->i_cpuid;
- return pcibr_intr;
-}
-
-/*ARGSUSED */
-void
-pcibr_intr_free(pcibr_intr_t pcibr_intr)
-{
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- unsigned pcibr_int_bit;
- pcibr_intr_list_t intr_list;
- int intr_shared;
- xtalk_intr_t *xtalk_intrp;
-
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++) {
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- for (intr_list =
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
- intr_list != NULL;
- intr_list = intr_list->il_next)
- if (compare_and_swap_ptr((void **) &intr_list->il_intr,
- pcibr_intr,
- NULL)) {
-#if DEBUG && INTR_DEBUG
- printk("%s: cleared a handler from bit %d\n",
- pcibr_soft->bs_name, pcibr_int_bit);
-#endif
- }
- /* If this interrupt line is not being shared between multiple
- * devices release the xtalk interrupt resources.
- */
- intr_shared =
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared;
- xtalk_intrp = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
-
- if ((!intr_shared) && (*xtalk_intrp)) {
-
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t int_dev;
-
- xtalk_intr_free(*xtalk_intrp);
- *xtalk_intrp = 0;
-
- /* Clear the PCI device interrupt to bridge interrupt pin
- * mapping.
- */
- int_dev = bridge->b_int_device;
- int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
- bridge->b_int_device = int_dev;
-
- }
- }
- }
- DEL(pcibr_intr);
-}
-
-LOCAL void
-pcibr_setpciint(xtalk_intr_t xtalk_intr)
-{
- iopaddr_t addr = xtalk_intr_addr_get(xtalk_intr);
- xtalk_intr_vector_t vect = xtalk_intr_vector_get(xtalk_intr);
- bridgereg_t *int_addr = (bridgereg_t *)
- xtalk_intr_sfarg_get(xtalk_intr);
-
- *int_addr = ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
- (BRIDGE_INT_ADDR_FLD & vect));
-}
-
-/*ARGSUSED */
-int
-pcibr_intr_connect(pcibr_intr_t pcibr_intr,
- intr_func_t intr_func,
- intr_arg_t intr_arg,
- void *thread)
-{
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
- bridgereg_t b_int_enable;
- unsigned long s;
-
- if (pcibr_intr == NULL)
- return -1;
-
-#if DEBUG && INTR_DEBUG
- printk("%v: pcibr_intr_connect 0x%X(0x%X)\n",
- pcibr_intr->bi_dev, intr_func, intr_arg);
-#endif
-
- pcibr_intr->bi_func = intr_func;
- pcibr_intr->bi_arg = intr_arg;
- *((volatile unsigned *)&pcibr_intr->bi_flags) |= PCIIO_INTR_CONNECTED;
-
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and make sure there are xtalk resources
- * allocated for it.
- */
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- pcibr_intr_wrap_t intr_wrap;
- xtalk_intr_t xtalk_intr;
-
- xtalk_intr = pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
-
- intr_wrap = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
- /*
- * If this interrupt line is being shared and the connect has
- * already been done, no need to do it again.
- */
- if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected)
- continue;
-
-
- /*
- * Use the pcibr wrapper function to handle all Bridge interrupts
- * regardless of whether the interrupt line is shared or not.
- */
- xtalk_intr_connect(xtalk_intr,
- pcibr_intr_func,
- (intr_arg_t) intr_wrap,
- (xtalk_intr_setfunc_t) pcibr_setpciint,
- (void *) &(bridge->b_int_addr[pcibr_int_bit].addr),
- 0);
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 1;
-
-#if DEBUG && INTR_DEBUG
- printk("%v bridge bit %d wrapper connected\n",
- pcibr_intr->bi_dev, pcibr_int_bit);
-#endif
- }
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable |= pcibr_int_bits;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
-
- return 0;
-}
-
-/*ARGSUSED */
-void
-pcibr_intr_disconnect(pcibr_intr_t pcibr_intr)
-{
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
- pcibr_intr_wrap_t intr_wrap;
- bridgereg_t b_int_enable;
- unsigned long s;
-
- /* Stop calling the function. Now.
- */
- *((volatile unsigned *)&pcibr_intr->bi_flags) &= ~PCIIO_INTR_CONNECTED;
- pcibr_intr->bi_func = 0;
- pcibr_intr->bi_arg = 0;
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and disconnect the interrupt.
- */
-
- /* don't disable interrupts for lines that
- * are shared between devices.
- */
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if ((pcibr_int_bits & (1 << pcibr_int_bit)) &&
- (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared))
- pcibr_int_bits &= ~(1 << pcibr_int_bit);
- if (!pcibr_int_bits)
- return;
-
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable &= ~pcibr_int_bits;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
-
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- /* if the interrupt line is now shared,
- * do not disconnect it.
- */
- if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
- continue;
-
- xtalk_intr_disconnect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 0;
-
-#if DEBUG && INTR_DEBUG
- printk("%s: xtalk disconnect done for Bridge bit %d\n",
- pcibr_soft->bs_name, pcibr_int_bit);
-#endif
-
- /* if we are sharing the interrupt line,
- * connect us up; this closes the hole
- * where the another pcibr_intr_alloc()
- * was in progress as we disconnected.
- */
- intr_wrap = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
- if (!pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
- continue;
-
-
- xtalk_intr_connect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr,
- pcibr_intr_func,
- (intr_arg_t) intr_wrap,
- (xtalk_intr_setfunc_t) pcibr_setpciint,
- (void *) &(bridge->b_int_addr[pcibr_int_bit].addr),
- 0);
- }
-}
-
-/*ARGSUSED */
-devfs_handle_t
-pcibr_intr_cpu_get(pcibr_intr_t pcibr_intr)
-{
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
-
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit))
- return xtalk_intr_cpu_get(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
- return 0;
-}
-
-/* =====================================================================
- * INTERRUPT HANDLING
- */
-LOCAL void
-pcibr_clearwidint(bridge_t *bridge)
-{
- bridge->b_wid_int_upper = 0;
- bridge->b_wid_int_lower = 0;
-}
-
-
-LOCAL void
-pcibr_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);
- widgetreg_t NEW_b_wid_int_upper, NEW_b_wid_int_lower;
- widgetreg_t OLD_b_wid_int_upper, OLD_b_wid_int_lower;
-
- bridge_t *bridge = (bridge_t *)xtalk_intr_sfarg_get(intr);
-
- NEW_b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
- XTALK_ADDR_TO_UPPER(addr));
- NEW_b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
-
- OLD_b_wid_int_upper = bridge->b_wid_int_upper;
- OLD_b_wid_int_lower = bridge->b_wid_int_lower;
-
-#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
- /* Verify that all interrupts from this Bridge are using a single PI */
- if ((OLD_b_wid_int_upper != 0) && (OLD_b_wid_int_lower != 0)) {
- /*
- * Once set, these registers shouldn't change; they should
- * be set multiple times with the same values.
- *
- * If we're attempting to change these registers, it means
- * that our heuristics for allocating interrupts in a way
- * appropriate for IP35 have failed, and the admin needs to
- * explicitly direct some interrupts (or we need to make the
- * heuristics more clever).
- *
- * In practice, we hope this doesn't happen very often, if
- * at all.
- */
- if ((OLD_b_wid_int_upper != NEW_b_wid_int_upper) ||
- (OLD_b_wid_int_lower != NEW_b_wid_int_lower)) {
- PRINT_WARNING("Interrupt allocation is too complex.\n");
- PRINT_WARNING("Use explicit administrative interrupt targetting.\n");
- PRINT_WARNING("bridge=0x%lx targ=0x%x\n", (unsigned long)bridge, targ);
- PRINT_WARNING("NEW=0x%x/0x%x OLD=0x%x/0x%x\n",
- NEW_b_wid_int_upper, NEW_b_wid_int_lower,
- OLD_b_wid_int_upper, OLD_b_wid_int_lower);
- PRINT_PANIC("PCI Bridge interrupt targetting error\n");
- }
- }
-#endif /* CONFIG_SGI_IP35 */
-
- bridge->b_wid_int_upper = NEW_b_wid_int_upper;
- bridge->b_wid_int_lower = NEW_b_wid_int_lower;
- bridge->b_int_host_err = vect;
-}
-
-/*
- * pcibr_intr_preset: called during mlreset time
- * if the platform specific code needs to route
- * one of the Bridge's xtalk interrupts before the
- * xtalk infrastructure is available.
- */
-void
-pcibr_xintr_preset(void *which_widget,
- int which_widget_intr,
- xwidgetnum_t targ,
- iopaddr_t addr,
- xtalk_intr_vector_t vect)
-{
- bridge_t *bridge = (bridge_t *) which_widget;
-
- if (which_widget_intr == -1) {
- /* bridge widget error interrupt */
- bridge->b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
- XTALK_ADDR_TO_UPPER(addr));
- bridge->b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
- bridge->b_int_host_err = vect;
-
- /* turn on all interrupts except
- * the PCI interrupt requests,
- * at least at heart.
- */
- bridge->b_int_enable |= ~BRIDGE_IMR_INT_MSK;
-
- } else {
- /* routing a PCI device interrupt.
- * targ and low 38 bits of addr must
- * be the same as the already set
- * value for the widget error interrupt.
- */
- bridge->b_int_addr[which_widget_intr].addr =
- ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
- (BRIDGE_INT_ADDR_FLD & vect));
- /*
- * now bridge can let it through;
- * NB: still should be blocked at
- * xtalk provider end, until the service
- * function is set.
- */
- bridge->b_int_enable |= 1 << vect;
- }
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
-}
-
-
-/*
- * pcibr_intr_func()
- *
- * This is the pcibr interrupt "wrapper" function that is called,
- * in interrupt context, to initiate the interrupt handler(s) registered
- * (via pcibr_intr_alloc/connect) for the occuring interrupt. Non-threaded
- * handlers will be called directly, and threaded handlers will have their
- * thread woken up.
- */
-void
-pcibr_intr_func(intr_arg_t arg)
-{
- pcibr_intr_wrap_t wrap = (pcibr_intr_wrap_t) arg;
- reg_p wrbf;
- intr_func_t func;
- pcibr_intr_t intr;
- pcibr_intr_list_t list;
- int clearit;
-#ifdef KERNEL_THREADS
- int do_nonthreaded = 0;
- int do_threaded = 1;
- int is_threaded = 0;
-#else
- int do_nonthreaded = 1;
- int do_threaded = 0;
- int is_threaded = 0;
-#endif
- int nonthreaded_count = 0;
- int x = 0;
-
- /*
- * If any handler is still running from a previous interrupt
- * just return. If there's a need to call the handler(s) again,
- * another interrupt will be generated either by the device or by
- * pcibr_force_interrupt().
- */
-
- if (wrap->iw_hdlrcnt) {
- return;
- }
-
- /*
- * Call all interrupt handlers registered.
- * First, the pcibr_intrd threads for any threaded handlers will be
- * awoken, then any non-threaded handlers will be called sequentially.
- */
-
- clearit = 1;
- while (do_threaded || do_nonthreaded) {
- for (list = wrap->iw_list; list != NULL; list = list->il_next) {
- if ((intr = list->il_intr) &&
- (intr->bi_flags & PCIIO_INTR_CONNECTED)) {
-
- ASSERT(intr->bi_func);
-
- /*
- * This device may have initiated write
- * requests since the bridge last saw
- * an edge on this interrupt input; flushing
- * the buffer prior to invoking the handler
- * should help but may not be sufficient if we
- * get more requests after the flush, followed
- * by the card deciding it wants service, before
- * the interrupt handler checks to see if things need
- * to be done.
- *
- * There is a similar race condition if
- * an interrupt handler loops around and
- * notices further service is requred.
- * Perhaps we need to have an explicit
- * call that interrupt handlers need to
- * do between noticing that DMA to memory
- * has completed, but before observing the
- * contents of memory?
- */
-
-#ifdef KERNEL_THREADS
- is_threaded = !(intr->bi_flags & PCIIO_INTR_NOTHREAD);
- if (!is_threaded) {
- nonthreaded_count++;
- }
-
- if ((do_threaded) && (is_threaded)) {
- /* Only need to flush write buffers if sharing */
-
- if ((wrap->iw_shared) && (wrbf = list->il_wrbf)) {
- if (x = *wrbf) /* write request buffer flush */
-#ifdef SUPPORT_PRINTING_V_FORMAT
- PRINT_ALERT("pcibr_intr_func %v: \n"
- "write buffer flush failed, wrbf=0x%x\n",
- list->il_intr->bi_dev, wrbf);
-#else
- PRINT_ALERT("pcibr_intr_func 0x%x: \n"
- "write buffer flush failed, wrbf=0x%x\n",
- list->il_intr->bi_dev, wrbf);
-#endif
- }
-
- /*
- * Keep a running count of the number of interrupt
- * handlers that have yet to complete.
- */
- atomicAddInt(&(wrap->iw_hdlrcnt), 1);
-
- /*
- * Prior to waking up pcibr_intrd, a pointer to the
- * wrapper struct corresponding to the interrupt taken
- * needs to be queued in the interrupt circular buffer.
- * The pcibr_intrd thread needs the wrapper pointer in
- * order to decrement the handler count (iw_hdlrcnt).
- */
- pcibr_wrap_put(wrap, &intr->bi_ibuf);
-#ifdef ITHREAD_LATENCY
- xthread_set_istamp(intr->bi_tinfo.thd_latstats);
-#endif /* ITHREAD_LATENCY */
- up(&intr->bi_tinfo.thd_isync);
- } else
-#endif /* KERNEL_THREADS */
- if ((do_nonthreaded) && (!is_threaded)) {
- /* Non-threaded.
- * Call the interrupt handler at interrupt level
- */
-
- /* Only need to flush write buffers if sharing */
-
- if ((wrap->iw_shared) && (wrbf = list->il_wrbf)) {
- if ((x = *wrbf)) /* write request buffer flush */
-#ifdef SUPPORT_PRINTING_V_FORMAT
- PRINT_ALERT("pcibr_intr_func %v: \n"
- "write buffer flush failed, wrbf=0x%x\n",
- list->il_intr->bi_dev, wrbf);
-#else
- PRINT_ALERT("pcibr_intr_func %p: \n"
- "write buffer flush failed, wrbf=0x%x\n",
- list->il_intr->bi_dev, wrbf);
-#endif
- }
-
- func = intr->bi_func;
- func(intr->bi_arg);
- }
-
- clearit = 0;
- }
- }
-
- if (do_threaded) {
- /*
- * All threaded handlers have been called;
- * next do non-threaded, if any.
- */
- do_threaded = 0;
-
- if (nonthreaded_count)
- do_nonthreaded = 1;
- } else {
- do_nonthreaded = 0;
- /*
- * If the non-threaded handler was the last to complete,
- * (i.e., no threaded handlers still running) force an
- * interrupt to avoid a potential deadlock situation.
- */
- if (wrap->iw_hdlrcnt == 0) {
- pcibr_force_interrupt(wrap);
- }
- }
- }
-
- /* If there were no handlers,
- * disable the interrupt and return.
- * It will get enabled again after
- * a handler is connected.
- * If we don't do this, we would
- * sit here and spin through the
- * list forever.
- */
- if (clearit) {
- pcibr_soft_t pcibr_soft = wrap->iw_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t b_int_enable;
- bridgereg_t mask = 1 << wrap->iw_intr;
- unsigned long s;
-
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable &= ~mask;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
- return;
- }
-}
-
-/* =====================================================================
- * ERROR HANDLING
- */
-
-#ifdef DEBUG
-#ifdef ERROR_DEBUG
-#define BRIDGE_PIOERR_TIMEOUT 100 /* Timeout with ERROR_DEBUG defined */
-#else
-#define BRIDGE_PIOERR_TIMEOUT 40 /* Timeout in debug mode */
-#endif
-#else
-#define BRIDGE_PIOERR_TIMEOUT 1 /* Timeout in non-debug mode */
-#endif
-
-LOCAL void
-print_bridge_errcmd(uint32_t cmdword, char *errtype)
-{
-#ifdef SUPPORT_PRINTING_R_FORMAT
- PRINT_WARNING(
- " Bridge %s error command word register %R",
- errtype, cmdword, xio_cmd_bits);
-#else
- PRINT_WARNING(
- " Bridge %s error command word register 0x%x",
- errtype, cmdword);
-#endif
-}
-
-LOCAL char *pcibr_isr_errs[] =
-{
- "", "", "", "", "", "", "", "",
- "08: GIO non-contiguous byte enable in crosstalk packet",
- "09: PCI to Crosstalk read request timeout",
- "10: PCI retry operation count exhausted.",
- "11: PCI bus device select timeout",
- "12: PCI device reported parity error",
- "13: PCI Address/Cmd parity error ",
- "14: PCI Bridge detected parity error",
- "15: PCI abort condition",
- "16: SSRAM parity error",
- "17: LLP Transmitter Retry count wrapped",
- "18: LLP Transmitter side required Retry",
- "19: LLP Receiver retry count wrapped",
- "20: LLP Receiver check bit error",
- "21: LLP Receiver sequence number error",
- "22: Request packet overflow",
- "23: Request operation not supported by bridge",
- "24: Request packet has invalid address for bridge widget",
- "25: Incoming request xtalk command word error bit set or invalid sideband",
- "26: Incoming response xtalk command word error bit set or invalid sideband",
- "27: Framing error, request cmd data size does not match actual",
- "28: Framing error, response cmd data size does not match actual",
- "29: Unexpected response arrived",
- "30: Access to SSRAM beyond device limits",
- "31: Multiple errors occurred",
-};
-
-/*
- * PCI Bridge Error interrupt handling.
- * This routine gets invoked from system interrupt dispatcher
- * and is responsible for invoking appropriate error handler,
- * depending on the type of error.
- * This IS a duplicate of bridge_errintr defined specfic to IP30.
- * There are some minor differences in terms of the return value and
- * parameters passed. One of these two should be removed at some point
- * of time.
- */
-/*ARGSUSED */
-void
-pcibr_error_dump(pcibr_soft_t pcibr_soft)
-{
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t int_status;
- int i;
-
- int_status = (bridge->b_int_status & ~BRIDGE_ISR_INT_MSK);
-
- PRINT_ALERT( "%s PCI BRIDGE ERROR: int_status is 0x%X",
- pcibr_soft->bs_name, int_status);
-
- for (i = PCIBR_ISR_ERR_START; i < PCIBR_ISR_MAX_ERRS; i++) {
- if (int_status & (1 << i)) {
- PRINT_WARNING( "%s", pcibr_isr_errs[i]);
- }
- }
-
- if (int_status & BRIDGE_ISR_XTALK_ERROR) {
- print_bridge_errcmd(bridge->b_wid_err_cmdword, "");
-
- PRINT_WARNING(" Bridge error address 0x%lx",
- (((uint64_t) bridge->b_wid_err_upper << 32) |
- bridge->b_wid_err_lower));
-
- print_bridge_errcmd(bridge->b_wid_aux_err, "Aux");
-
- if (int_status & (BRIDGE_ISR_BAD_XRESP_PKT | BRIDGE_ISR_RESP_XTLK_ERR)) {
- PRINT_WARNING(" Bridge response buffer: dev-num %d buff-num %d addr 0x%lx\n",
- ((bridge->b_wid_resp_upper >> 20) & 0x3),
- ((bridge->b_wid_resp_upper >> 16) & 0xF),
- (((uint64_t) (bridge->b_wid_resp_upper & 0xFFFF) << 32) |
- bridge->b_wid_resp_lower));
- }
- }
- if (int_status & BRIDGE_ISR_SSRAM_PERR)
- PRINT_WARNING(" Bridge SSRAM parity error register 0x%x",
- bridge->b_ram_perr);
-
- if (int_status & BRIDGE_ISR_PCIBUS_ERROR) {
- PRINT_WARNING(" PCI/GIO error upper address register 0x%x",
- bridge->b_pci_err_upper);
-
- PRINT_WARNING(" PCI/GIO error lower address register 0x%x",
- bridge->b_pci_err_lower);
- }
- if (int_status & BRIDGE_ISR_ERROR_FATAL) {
- PRINT_PANIC("PCI Bridge Error interrupt killed the system");
- /*NOTREACHED */
- } else {
- PRINT_ALERT( "Non-fatal Error in Bridge..");
- }
-}
-
-#define PCIBR_ERRINTR_GROUP(error) \
- (( error & (BRIDGE_IRR_PCI_GRP|BRIDGE_IRR_GIO_GRP)
-
-uint32_t
-pcibr_errintr_group(uint32_t error)
-{
- uint32_t group = BRIDGE_IRR_MULTI_CLR;
-
- if (error & BRIDGE_IRR_PCI_GRP)
- group |= BRIDGE_IRR_PCI_GRP_CLR;
- if (error & BRIDGE_IRR_SSRAM_GRP)
- group |= BRIDGE_IRR_SSRAM_GRP_CLR;
- if (error & BRIDGE_IRR_LLP_GRP)
- group |= BRIDGE_IRR_LLP_GRP_CLR;
- if (error & BRIDGE_IRR_REQ_DSP_GRP)
- group |= BRIDGE_IRR_REQ_DSP_GRP_CLR;
- if (error & BRIDGE_IRR_RESP_BUF_GRP)
- group |= BRIDGE_IRR_RESP_BUF_GRP_CLR;
- if (error & BRIDGE_IRR_CRP_GRP)
- group |= BRIDGE_IRR_CRP_GRP_CLR;
-
- return group;
-
-}
-
-
-/* pcibr_pioerr_check():
- * Check to see if this pcibr has a PCI PIO
- * TIMEOUT error; if so, clear it and bump
- * the timeout-count on any piomaps that
- * could cover the address.
- */
-static void
-pcibr_pioerr_check(pcibr_soft_t soft)
-{
- bridge_t *bridge;
- bridgereg_t b_int_status;
- bridgereg_t b_pci_err_lower;
- bridgereg_t b_pci_err_upper;
- iopaddr_t pci_addr;
- pciio_slot_t slot;
- pcibr_piomap_t map;
- iopaddr_t base;
- size_t size;
- unsigned win;
- int func;
-
- bridge = soft->bs_base;
- b_int_status = bridge->b_int_status;
- if (b_int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
- b_pci_err_lower = bridge->b_pci_err_lower;
- b_pci_err_upper = bridge->b_pci_err_upper;
- b_int_status = bridge->b_int_status;
- if (b_int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
- bridge->b_int_rst_stat = (BRIDGE_IRR_PCI_GRP_CLR|
- BRIDGE_IRR_MULTI_CLR);
-
- pci_addr = b_pci_err_upper & BRIDGE_ERRUPPR_ADDRMASK;
- pci_addr = (pci_addr << 32) | b_pci_err_lower;
-
- slot = 8;
- while (slot-- > 0) {
- int nfunc = soft->bs_slot[slot].bss_ninfo;
- pcibr_info_h pcibr_infoh = soft->bs_slot[slot].bss_infos;
-
- for (func = 0; func < nfunc; func++) {
- pcibr_info_t pcibr_info = pcibr_infoh[func];
-
- if (!pcibr_info)
- continue;
-
- for (map = pcibr_info->f_piomap;
- map != NULL; map = map->bp_next) {
- base = map->bp_pciaddr;
- size = map->bp_mapsz;
- win = map->bp_space - PCIIO_SPACE_WIN(0);
- if (win < 6)
- base +=
- soft->bs_slot[slot].bss_window[win].bssw_base;
- else if (map->bp_space == PCIIO_SPACE_ROM)
- base += pcibr_info->f_rbase;
- if ((pci_addr >= base) && (pci_addr < (base + size)))
- atomic_inc(map->bp_toc);
- }
- }
- }
- }
- }
-}
-
-/*
- * PCI Bridge Error interrupt handler.
- * This gets invoked, whenever a PCI bridge sends an error interrupt.
- * Primarily this servers two purposes.
- * - If an error can be handled (typically a PIO read/write
- * error, we try to do it silently.
- * - If an error cannot be handled, we die violently.
- * Interrupt due to PIO errors:
- * - Bridge sends an interrupt, whenever a PCI operation
- * done by the bridge as the master fails. Operations could
- * be either a PIO read or a PIO write.
- * PIO Read operation also triggers a bus error, and it's
- * We primarily ignore this interrupt in that context..
- * For PIO write errors, this is the only indication.
- * and we have to handle with the info from here.
- *
- * So, there is no way to distinguish if an interrupt is
- * due to read or write error!.
- */
-
-
-LOCAL void
-pcibr_error_intr_handler(intr_arg_t arg)
-{
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- bridgereg_t int_status;
- bridgereg_t err_status;
- int i;
-
- /* REFERENCED */
- bridgereg_t disable_errintr_mask = 0;
-
-#if PCIBR_SOFT_LIST
- /* IP27 seems to be handing us junk.
- */
- {
- pcibr_list_p entry;
-
- entry = pcibr_list;
- while (1) {
- if (entry == NULL) {
- printk("pcibr_error_intr_handler:\n"
- "\tparameter (0x%p) is not a pcibr_soft!",
- arg);
- PRINT_PANIC("Invalid parameter to pcibr_error_intr_handler");
- }
- if ((intr_arg_t) entry->bl_soft == arg)
- break;
- entry = entry->bl_next;
- }
- }
-#endif
- pcibr_soft = (pcibr_soft_t) arg;
- bridge = pcibr_soft->bs_base;
-
- /*
- * pcibr_error_intr_handler gets invoked whenever bridge encounters
- * an error situation, and the interrupt for that error is enabled.
- * This routine decides if the error is fatal or not, and takes
- * action accordingly.
- *
- * In one case there is a need for special action.
- * In case of PIO read/write timeouts due to user level, we do
- * get an error interrupt. In this case, way to handle would
- * be to start a timeout. If the error was due to "read", bus
- * error handling code takes care of it. If error is due to write,
- * it's handled at timeout
- */
-
- /* int_status is which bits we have to clear;
- * err_status is the bits we haven't handled yet.
- */
-
- int_status = bridge->b_int_status & ~BRIDGE_ISR_INT_MSK;
- err_status = int_status & ~BRIDGE_ISR_MULTI_ERR;
-
- if (!(int_status & ~BRIDGE_ISR_INT_MSK)) {
- /*
- * No error bit set!!.
- */
- return;
- }
- /* If we have a PCIBUS_PIOERR,
- * hand it to the logger but otherwise
- * ignore the event.
- */
- if (int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
- pcibr_pioerr_check(pcibr_soft);
- err_status &= ~BRIDGE_ISR_PCIBUS_PIOERR;
- int_status &= ~BRIDGE_ISR_PCIBUS_PIOERR;
- }
-
-
- if (err_status) {
- struct bs_errintr_stat_s *bs_estat = pcibr_soft->bs_errintr_stat;
-
- for (i = PCIBR_ISR_ERR_START; i < PCIBR_ISR_MAX_ERRS; i++, bs_estat++) {
- if (err_status & (1 << i)) {
- uint32_t errrate = 0;
- uint32_t errcount = 0;
- uint32_t errinterval = 0, current_tick = 0;
- int panic_on_llp_tx_retry = 0;
- int is_llp_tx_retry_intr = 0;
-
- bs_estat->bs_errcount_total++;
-
-#ifdef LATER
- current_tick = lbolt;
-#else
- current_tick = 0;
-#endif
- errinterval = (current_tick - bs_estat->bs_lasterr_timestamp);
- errcount = (bs_estat->bs_errcount_total -
- bs_estat->bs_lasterr_snapshot);
-
- is_llp_tx_retry_intr = (BRIDGE_ISR_LLP_TX_RETRY == (1 << i));
-
- /* On a non-zero error rate (which is equivalent to
- * to 100 errors /sec at least) for the LLP transmitter
- * retry interrupt we need to panic the system
- * to prevent potential data corruption .
- * NOTE : errcount is being compared to PCIBR_ERRTIME_THRESHOLD
- * to make sure that we are not seing cases like x error
- * interrupts per y ticks for very low x ,y (x > y ) which
- * makes error rate be > 100 /sec.
- */
-
- /* Check for the divide by zero condition while
- * calculating the error rates.
- */
-
- if (errinterval) {
- errrate = errcount / errinterval;
- /* If able to calculate error rate
- * on a LLP transmitter retry interrupt check
- * if the error rate is nonzero and we have seen
- * a certain minimum number of errors.
- */
- if (is_llp_tx_retry_intr &&
- errrate &&
- (errcount >= PCIBR_ERRTIME_THRESHOLD)) {
- panic_on_llp_tx_retry = 1;
- }
- } else {
- errrate = 0;
- /* Since we are not able to calculate the
- * error rate check if we exceeded a certain
- * minimum number of errors for LLP transmitter
- * retries. Note that this can only happen
- * within the first tick after the last snapshot.
- */
- if (is_llp_tx_retry_intr &&
- (errcount >= PCIBR_ERRINTR_DISABLE_LEVEL)) {
- panic_on_llp_tx_retry = 1;
- }
- }
- if (panic_on_llp_tx_retry) {
- static uint32_t last_printed_rate;
-
- if (errrate > last_printed_rate) {
- last_printed_rate = errrate;
- /* Print the warning only if the error rate
- * for the transmitter retry interrupt
- * exceeded the previously printed rate.
- */
- PRINT_WARNING(
- "%s: %s, Excessive error interrupts : %d/tick\n",
- pcibr_soft->bs_name,
- pcibr_isr_errs[i],
- errrate);
-
- }
- /*
- * Update snapshot, and time
- */
- bs_estat->bs_lasterr_timestamp = current_tick;
- bs_estat->bs_lasterr_snapshot =
- bs_estat->bs_errcount_total;
-
- }
- /*
- * If the error rate is high enough, print the error rate.
- */
- if (errinterval > PCIBR_ERRTIME_THRESHOLD) {
-
- if (errrate > PCIBR_ERRRATE_THRESHOLD) {
- PRINT_NOTICE( "%s: %s, Error rate %d/tick",
- pcibr_soft->bs_name,
- pcibr_isr_errs[i],
- errrate);
- /*
- * Update snapshot, and time
- */
- bs_estat->bs_lasterr_timestamp = current_tick;
- bs_estat->bs_lasterr_snapshot =
- bs_estat->bs_errcount_total;
- }
- }
- if (bs_estat->bs_errcount_total > PCIBR_ERRINTR_DISABLE_LEVEL) {
- /*
- * We have seen a fairly large number of errors of
- * this type. Let's disable the interrupt. But flash
- * a message about the interrupt being disabled.
- */
- PRINT_NOTICE(
- "%s Disabling error interrupt type %s. Error count %d",
- pcibr_soft->bs_name,
- pcibr_isr_errs[i],
- bs_estat->bs_errcount_total);
- disable_errintr_mask |= (1 << i);
- }
- }
- }
- }
-
- if (disable_errintr_mask) {
- /*
- * Disable some high frequency errors as they
- * could eat up too much cpu time.
- */
- bridge->b_int_enable &= ~disable_errintr_mask;
- }
- /*
- * If we leave the PROM cacheable, T5 might
- * try to do a cache line sized writeback to it,
- * which will cause a BRIDGE_ISR_INVLD_ADDR.
- */
- if ((err_status & BRIDGE_ISR_INVLD_ADDR) &&
- (0x00000000 == bridge->b_wid_err_upper) &&
- (0x00C00000 == (0xFFC00000 & bridge->b_wid_err_lower)) &&
- (0x00402000 == (0x00F07F00 & bridge->b_wid_err_cmdword))) {
- err_status &= ~BRIDGE_ISR_INVLD_ADDR;
- }
-#if defined (PCIBR_LLP_CONTROL_WAR)
- /*
- * The bridge bug, where the llp_config or control registers
- * need to be read back after being written, affects an MP
- * system since there could be small windows between writing
- * the register and reading it back on one cpu while another
- * cpu is fielding an interrupt. If we run into this scenario,
- * workaround the problem by ignoring the error. (bug 454474)
- * pcibr_llp_control_war_cnt keeps an approximate number of
- * times we saw this problem on a system.
- */
-
- if ((err_status & BRIDGE_ISR_INVLD_ADDR) &&
- ((((uint64_t) bridge->b_wid_err_upper << 32) | (bridge->b_wid_err_lower))
- == (BRIDGE_INT_RST_STAT & 0xff0))) {
-#ifdef LATER
- if (kdebug)
- PRINT_NOTICE( "%s bridge: ignoring llp/control address interrupt",
- pcibr_soft->bs_name);
-#endif
- pcibr_llp_control_war_cnt++;
- err_status &= ~BRIDGE_ISR_INVLD_ADDR;
- }
-#endif /* PCIBR_LLP_CONTROL_WAR */
-
-#ifdef DEBUG
- if (err_status & BRIDGE_ISR_ERROR_DUMP)
- pcibr_error_dump(pcibr_soft);
-#else
- if (err_status & BRIDGE_ISR_ERROR_FATAL) {
- printk("BRIDGE ERR STATUS 0x%x\n", err_status);
- pcibr_error_dump(pcibr_soft);
- }
-#endif
-
- /*
- * We can't return without re-enabling the interrupt, since
- * it would cause problems for devices like IOC3 (Lost
- * interrupts ?.). So, just cleanup the interrupt, and
- * use saved values later..
- */
- bridge->b_int_rst_stat = pcibr_errintr_group(int_status);
-}
-
-/*
- * pcibr_addr_toslot
- * Given the 'pciaddr' find out which slot this address is
- * allocated to, and return the slot number.
- * While we have the info handy, construct the
- * function number, space code and offset as well.
- *
- * NOTE: if this routine is called, we don't know whether
- * the address is in CFG, MEM, or I/O space. We have to guess.
- * This will be the case on PIO stores, where the only way
- * we have of getting the address is to check the Bridge, which
- * stores the PCI address but not the space and not the xtalk
- * address (from which we could get it).
- */
-LOCAL int
-pcibr_addr_toslot(pcibr_soft_t pcibr_soft,
- iopaddr_t pciaddr,
- pciio_space_t *spacep,
- iopaddr_t *offsetp,
- pciio_function_t *funcp)
-{
- int s, f=0, w;
- iopaddr_t base;
- size_t size;
- pciio_piospace_t piosp;
-
- /*
- * Check if the address is in config space
- */
-
- if ((pciaddr >= BRIDGE_CONFIG_BASE) && (pciaddr < BRIDGE_CONFIG_END)) {
-
- if (pciaddr >= BRIDGE_CONFIG1_BASE)
- pciaddr -= BRIDGE_CONFIG1_BASE;
- else
- pciaddr -= BRIDGE_CONFIG_BASE;
-
- s = pciaddr / BRIDGE_CONFIG_SLOT_SIZE;
- pciaddr %= BRIDGE_CONFIG_SLOT_SIZE;
-
- if (funcp) {
- f = pciaddr / 0x100;
- pciaddr %= 0x100;
- }
- if (spacep)
- *spacep = PCIIO_SPACE_CFG;
- if (offsetp)
- *offsetp = pciaddr;
- if (funcp)
- *funcp = f;
-
- return s;
- }
- for (s = 0; s < 8; s++) {
- int nf = pcibr_soft->bs_slot[s].bss_ninfo;
- pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[s].bss_infos;
-
- for (f = 0; f < nf; f++) {
- pcibr_info_t pcibr_info = pcibr_infoh[f];
-
- if (!pcibr_info)
- continue;
- for (w = 0; w < 6; w++) {
- if (pcibr_info->f_window[w].w_space
- == PCIIO_SPACE_NONE) {
- continue;
- }
- base = pcibr_info->f_window[w].w_base;
- size = pcibr_info->f_window[w].w_size;
-
- if ((pciaddr >= base) && (pciaddr < (base + size))) {
- if (spacep)
- *spacep = PCIIO_SPACE_WIN(w);
- if (offsetp)
- *offsetp = pciaddr - base;
- if (funcp)
- *funcp = f;
- return s;
- } /* endif match */
- } /* next window */
- } /* next func */
- } /* next slot */
-
- /*
- * Check if the address was allocated as part of the
- * pcibr_piospace_alloc calls.
- */
- for (s = 0; s < 8; s++) {
- int nf = pcibr_soft->bs_slot[s].bss_ninfo;
- pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[s].bss_infos;
-
- for (f = 0; f < nf; f++) {
- pcibr_info_t pcibr_info = pcibr_infoh[f];
-
- if (!pcibr_info)
- continue;
- piosp = pcibr_info->f_piospace;
- while (piosp) {
- if ((piosp->start <= pciaddr) &&
- ((piosp->count + piosp->start) > pciaddr)) {
- if (spacep)
- *spacep = piosp->space;
- if (offsetp)
- *offsetp = pciaddr - piosp->start;
- return s;
- } /* endif match */
- piosp = piosp->next;
- } /* next piosp */
- } /* next func */
- } /* next slot */
-
- /*
- * Some other random address on the PCI bus ...
- * we have no way of knowing whether this was
- * a MEM or I/O access; so, for now, we just
- * assume that the low 1G is MEM, the next
- * 3G is I/O, and anything above the 4G limit
- * is obviously MEM.
- */
-
- if (spacep)
- *spacep = ((pciaddr < (1ul << 30)) ? PCIIO_SPACE_MEM :
- (pciaddr < (4ul << 30)) ? PCIIO_SPACE_IO :
- PCIIO_SPACE_MEM);
- if (offsetp)
- *offsetp = pciaddr;
-
- return PCIIO_SLOT_NONE;
-
-}
-
-LOCAL void
-pcibr_error_cleanup(pcibr_soft_t pcibr_soft, int error_code)
-{
- bridge_t *bridge = pcibr_soft->bs_base;
-
- ASSERT(error_code & IOECODE_PIO);
- error_code = error_code;
-
- bridge->b_int_rst_stat =
- (BRIDGE_IRR_PCI_GRP_CLR | BRIDGE_IRR_MULTI_CLR);
- (void) bridge->b_wid_tflush; /* flushbus */
-}
-
-/*
- * pcibr_error_extract
- * Given the 'pcibr vertex handle' find out which slot
- * the bridge status error address (from pcibr_soft info
- * hanging off the vertex)
- * allocated to, and return the slot number.
- * While we have the info handy, construct the
- * space code and offset as well.
- *
- * NOTE: if this routine is called, we don't know whether
- * the address is in CFG, MEM, or I/O space. We have to guess.
- * This will be the case on PIO stores, where the only way
- * we have of getting the address is to check the Bridge, which
- * stores the PCI address but not the space and not the xtalk
- * address (from which we could get it).
- *
- * XXX- this interface has no way to return the function
- * number on a multifunction card, even though that data
- * is available.
- */
-
-pciio_slot_t
-pcibr_error_extract(devfs_handle_t pcibr_vhdl,
- pciio_space_t *spacep,
- iopaddr_t *offsetp)
-{
- pcibr_soft_t pcibr_soft = 0;
- iopaddr_t bserr_addr;
- bridge_t *bridge;
- pciio_slot_t slot = PCIIO_SLOT_NONE;
- arbitrary_info_t rev;
-
- /* Do a sanity check as to whether we really got a
- * bridge vertex handle.
- */
- if (hwgraph_info_get_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, &rev) !=
- GRAPH_SUCCESS)
- return(slot);
-
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- if (pcibr_soft) {
- bridge = pcibr_soft->bs_base;
- bserr_addr =
- bridge->b_pci_err_lower |
- ((uint64_t) (bridge->b_pci_err_upper &
- BRIDGE_ERRUPPR_ADDRMASK) << 32);
-
- slot = pcibr_addr_toslot(pcibr_soft, bserr_addr,
- spacep, offsetp, NULL);
- }
- return slot;
-}
-
-/*ARGSUSED */
-void
-pcibr_device_disable(pcibr_soft_t pcibr_soft, int devnum)
-{
- /*
- * XXX
- * Device failed to handle error. Take steps to
- * disable this device ? HOW TO DO IT ?
- *
- * If there are any Read response buffers associated
- * with this device, it's time to get them back!!
- *
- * We can disassociate any interrupt level associated
- * with this device, and disable that interrupt level
- *
- * For now it's just a place holder
- */
-}
-
-/*
- * pcibr_pioerror
- * Handle PIO error that happened at the bridge pointed by pcibr_soft.
- *
- * Queries the Bus interface attached to see if the device driver
- * mapping the device-number that caused error can handle the
- * situation. If so, it will clean up any error, and return
- * indicating the error was handled. If the device driver is unable
- * to handle the error, it expects the bus-interface to disable that
- * device, and takes any steps needed here to take away any resources
- * associated with this device.
- */
-
-#define BEM_ADD_STR(s) printk("%s", (s))
-#ifdef SUPPORT_SGI_CMN_ERR_STUFF
-#define BEM_ADD_VAR(v) printk("\t%20s: 0x%x\n", #v, (v))
-#define BEM_ADD_REG(r) printk("\t%20s: %R\n", #r, (r), r ## _desc)
-
-#define BEM_ADD_NSPC(n,s) printk("\t%20s: %R\n", n, s, space_desc)
-#else
-#define BEM_ADD_VAR(v)
-#define BEM_ADD_REG(r)
-#define BEM_ADD_NSPC(n,s)
-#endif
-#define BEM_ADD_SPC(s) BEM_ADD_NSPC(#s, s)
-
-/* BEM_ADD_IOE doesn't dump the whole ioerror, it just
- * decodes the PCI specific portions -- we count on our
- * callers to dump the raw IOE data.
- */
-#ifdef LATER
-#define BEM_ADD_IOE(ioe) \
- do { \
- if (IOERROR_FIELDVALID(ioe, busspace)) { \
- unsigned spc; \
- unsigned win; \
- \
- spc = IOERROR_GETVALUE(ioe, busspace); \
- win = spc - PCIIO_SPACE_WIN(0); \
- \
- switch (spc) { \
- case PCIIO_SPACE_CFG: \
- printk("\tPCI Slot %d Func %d CFG space Offset 0x%x\n", \
- pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe, widgetdev)), \
- pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe, widgetdev)), \
- IOERROR_GETVALUE(ioe, busaddr)); \
- break; \
- case PCIIO_SPACE_IO: \
- printk("\tPCI I/O space Offset 0x%x\n", \
- IOERROR_GETVALUE(ioe, busaddr)); \
- break; \
- case PCIIO_SPACE_MEM: \
- case PCIIO_SPACE_MEM32: \
- case PCIIO_SPACE_MEM64: \
- printk("\tPCI MEM space Offset 0x%x\n", \
- IOERROR_GETVALUE(ioe, busaddr)); \
- break; \
- default: \
- if (win < 6) { \
- printk("\tPCI Slot %d Func %d Window %d Offset 0x%x\n",\
- pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe, widgetdev)), \
- pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe, widgetdev)), \
- win, \
- IOERROR_GETVALUE(ioe, busaddr)); \
- } \
- break; \
- } \
- } \
- } while (0)
-#else
-#define BEM_ADD_IOE(ioe)
-#endif
-
-/*ARGSUSED */
-LOCAL int
-pcibr_pioerror(
- pcibr_soft_t pcibr_soft,
- int error_code,
- ioerror_mode_t mode,
- ioerror_t *ioe)
-{
- int retval = IOERROR_HANDLED;
-
- devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
- bridge_t *bridge = pcibr_soft->bs_base;
-
- bridgereg_t bridge_int_status;
- bridgereg_t bridge_pci_err_lower;
- bridgereg_t bridge_pci_err_upper;
- bridgereg_t bridge_pci_err_addr;
-
- iopaddr_t bad_xaddr;
-
- pciio_space_t raw_space; /* raw PCI space */
- iopaddr_t raw_paddr; /* raw PCI address */
-
- pciio_space_t space; /* final PCI space */
- pciio_slot_t slot; /* final PCI slot, if appropriate */
- pciio_function_t func; /* final PCI func, if appropriate */
- iopaddr_t offset; /* final PCI offset */
-
- int cs, cw, cf;
- pciio_space_t wx;
- iopaddr_t wb;
- size_t ws;
- iopaddr_t wl;
-
-
- /*
- * We expect to have an "xtalkaddr" coming in,
- * and need to construct the slot/space/offset.
- */
-
-#ifdef LATER
- bad_xaddr = IOERROR_GETVALUE(ioe, xtalkaddr);
-#else
- bad_xaddr = -1;
-#endif
-
- slot = PCIIO_SLOT_NONE;
- func = PCIIO_FUNC_NONE;
- raw_space = PCIIO_SPACE_NONE;
- raw_paddr = 0;
-
- if ((bad_xaddr >= BRIDGE_TYPE0_CFG_DEV0) &&
- (bad_xaddr < BRIDGE_TYPE1_CFG)) {
- raw_paddr = bad_xaddr - BRIDGE_TYPE0_CFG_DEV0;
- slot = raw_paddr / BRIDGE_TYPE0_CFG_SLOT_OFF;
- raw_paddr = raw_paddr % BRIDGE_TYPE0_CFG_SLOT_OFF;
- raw_space = PCIIO_SPACE_CFG;
- }
- if ((bad_xaddr >= BRIDGE_TYPE1_CFG) &&
- (bad_xaddr < (BRIDGE_TYPE1_CFG + 0x1000))) {
- /* Type 1 config space:
- * slot and function numbers not known.
- * Perhaps we can read them back?
- */
- raw_paddr = bad_xaddr - BRIDGE_TYPE1_CFG;
- raw_space = PCIIO_SPACE_CFG;
- }
- if ((bad_xaddr >= BRIDGE_DEVIO0) &&
- (bad_xaddr < BRIDGE_DEVIO(BRIDGE_DEV_CNT))) {
- int x;
-
- raw_paddr = bad_xaddr - BRIDGE_DEVIO0;
- x = raw_paddr / BRIDGE_DEVIO_OFF;
- raw_paddr %= BRIDGE_DEVIO_OFF;
- /* first two devio windows are double-sized */
- if ((x == 1) || (x == 3))
- raw_paddr += BRIDGE_DEVIO_OFF;
- if (x > 0)
- x--;
- if (x > 1)
- x--;
- /* x is which devio reg; no guarantee
- * PCI slot x will be responding.
- * still need to figure out who decodes
- * space/offset on the bus.
- */
- raw_space = pcibr_soft->bs_slot[x].bss_devio.bssd_space;
- if (raw_space == PCIIO_SPACE_NONE) {
- /* Someone got an error because they
- * accessed the PCI bus via a DevIO(x)
- * window that pcibr has not yet assigned
- * to any specific PCI address. It is
- * quite possible that the Device(x)
- * register has been changed since they
- * made their access, but we will give it
- * our best decode shot.
- */
- raw_space = pcibr_soft->bs_slot[x].bss_device
- & BRIDGE_DEV_DEV_IO_MEM
- ? PCIIO_SPACE_MEM
- : PCIIO_SPACE_IO;
- raw_paddr +=
- (pcibr_soft->bs_slot[x].bss_device &
- BRIDGE_DEV_OFF_MASK) <<
- BRIDGE_DEV_OFF_ADDR_SHFT;
- } else
- raw_paddr += pcibr_soft->bs_slot[x].bss_devio.bssd_base;
- }
- if ((bad_xaddr >= BRIDGE_PCI_MEM32_BASE) &&
- (bad_xaddr <= BRIDGE_PCI_MEM32_LIMIT)) {
- raw_space = PCIIO_SPACE_MEM32;
- raw_paddr = bad_xaddr - BRIDGE_PCI_MEM32_BASE;
- }
- if ((bad_xaddr >= BRIDGE_PCI_MEM64_BASE) &&
- (bad_xaddr <= BRIDGE_PCI_MEM64_LIMIT)) {
- raw_space = PCIIO_SPACE_MEM64;
- raw_paddr = bad_xaddr - BRIDGE_PCI_MEM64_BASE;
- }
- if ((bad_xaddr >= BRIDGE_PCI_IO_BASE) &&
- (bad_xaddr <= BRIDGE_PCI_IO_LIMIT)) {
- raw_space = PCIIO_SPACE_IO;
- raw_paddr = bad_xaddr - BRIDGE_PCI_IO_BASE;
- }
- space = raw_space;
- offset = raw_paddr;
-
- if ((slot == PCIIO_SLOT_NONE) && (space != PCIIO_SPACE_NONE)) {
- /* we've got a space/offset but not which
- * PCI slot decodes it. Check through our
- * notions of which devices decode where.
- *
- * Yes, this "duplicates" some logic in
- * pcibr_addr_toslot; the difference is,
- * this code knows which space we are in,
- * and can really really tell what is
- * going on (no guessing).
- */
-
- for (cs = 0; (cs < 8) && (slot == PCIIO_SLOT_NONE); cs++) {
- int nf = pcibr_soft->bs_slot[cs].bss_ninfo;
- pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos;
-
- for (cf = 0; (cf < nf) && (slot == PCIIO_SLOT_NONE); cf++) {
- pcibr_info_t pcibr_info = pcibr_infoh[cf];
-
- if (!pcibr_info)
- continue;
- for (cw = 0; (cw < 6) && (slot == PCIIO_SLOT_NONE); ++cw) {
- if (((wx = pcibr_info->f_window[cw].w_space) != PCIIO_SPACE_NONE) &&
- ((wb = pcibr_info->f_window[cw].w_base) != 0) &&
- ((ws = pcibr_info->f_window[cw].w_size) != 0) &&
- ((wl = wb + ws) > wb) &&
- ((wb <= offset) && (wl > offset))) {
- /* MEM, MEM32 and MEM64 need to
- * compare as equal ...
- */
- if ((wx == space) ||
- (((wx == PCIIO_SPACE_MEM) ||
- (wx == PCIIO_SPACE_MEM32) ||
- (wx == PCIIO_SPACE_MEM64)) &&
- ((space == PCIIO_SPACE_MEM) ||
- (space == PCIIO_SPACE_MEM32) ||
- (space == PCIIO_SPACE_MEM64)))) {
- slot = cs;
- func = cf;
- space = PCIIO_SPACE_WIN(cw);
- offset -= wb;
- } /* endif window space match */
- } /* endif window valid and addr match */
- } /* next window unless slot set */
- } /* next func unless slot set */
- } /* next slot unless slot set */
- /* XXX- if slot is still -1, no PCI devices are
- * decoding here using their standard PCI BASE
- * registers. This would be a really good place
- * to cross-coordinate with the pciio PCI
- * address space allocation routines, to find
- * out if this address is "allocated" by any of
- * our subsidiary devices.
- */
- }
- /* Scan all piomap records on this PCI bus to update
- * the TimeOut Counters on all matching maps. If we
- * don't already know the slot number, take it from
- * the first matching piomap. Note that we have to
- * compare maps against raw_space and raw_paddr
- * since space and offset could already be
- * window-relative.
- *
- * There is a chance that one CPU could update
- * through this path, and another CPU could also
- * update due to an interrupt. Closing this hole
- * would only result in the possibility of some
- * errors never getting logged at all, and since the
- * use for bp_toc is as a logical test rather than a
- * strict count, the excess counts are not a
- * problem.
- */
- for (cs = 0; cs < 8; ++cs) {
- int nf = pcibr_soft->bs_slot[cs].bss_ninfo;
- pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos;
-
- for (cf = 0; cf < nf; cf++) {
- pcibr_info_t pcibr_info = pcibr_infoh[cf];
- pcibr_piomap_t map;
-
- if (!pcibr_info)
- continue;
-
- for (map = pcibr_info->f_piomap;
- map != NULL; map = map->bp_next) {
- wx = map->bp_space;
- wb = map->bp_pciaddr;
- ws = map->bp_mapsz;
- cw = wx - PCIIO_SPACE_WIN(0);
- if (cw < 6) {
- wb += pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base;
- wx = pcibr_soft->bs_slot[cs].bss_window[cw].bssw_space;
- }
- if (wx == PCIIO_SPACE_ROM) {
- wb += pcibr_info->f_rbase;
- wx = PCIIO_SPACE_MEM;
- }
- if ((wx == PCIIO_SPACE_MEM32) ||
- (wx == PCIIO_SPACE_MEM64))
- wx = PCIIO_SPACE_MEM;
- wl = wb + ws;
- if ((wx == raw_space) && (raw_paddr >= wb) && (raw_paddr < wl)) {
- atomic_inc(map->bp_toc);
- if (slot == PCIIO_SLOT_NONE) {
- slot = cs;
- space = map->bp_space;
- if (cw < 6)
- offset -= pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base;
- }
- }
- }
- }
- }
-
- if (space != PCIIO_SPACE_NONE) {
- if (slot != PCIIO_SLOT_NONE) {
-#ifdef LATER
- if (func != PCIIO_FUNC_NONE)
- IOERROR_SETVALUE(ioe, widgetdev,
- pciio_widgetdev_create(slot,func));
- else
- IOERROR_SETVALUE(ioe, widgetdev,
- pciio_widgetdev_create(slot,0));
-#else
- if (func != PCIIO_FUNC_NONE) {
- IOERROR_SETVALUE(ioe, widgetdev,
- pciio_widgetdev_create(slot,func));
- } else {
- IOERROR_SETVALUE(ioe, widgetdev,
- pciio_widgetdev_create(slot,0));
- }
-#endif
- }
-
- IOERROR_SETVALUE(ioe, busspace, space);
- IOERROR_SETVALUE(ioe, busaddr, offset);
- }
- if (mode == MODE_DEVPROBE) {
- /*
- * During probing, we don't really care what the
- * error is. Clean up the error in Bridge, notify
- * subsidiary devices, and return success.
- */
- pcibr_error_cleanup(pcibr_soft, error_code);
-
- /* if appropriate, give the error handler for this slot
- * a shot at this probe access as well.
- */
- return (slot == PCIIO_SLOT_NONE) ? IOERROR_HANDLED :
- pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
- }
- /*
- * If we don't know what "PCI SPACE" the access
- * was targeting, we may have problems at the
- * Bridge itself. Don't touch any bridge registers,
- * and do complain loudly.
- */
-
- if (space == PCIIO_SPACE_NONE) {
- printk("XIO Bus Error at %s\n"
- "\taccess to XIO bus offset 0x%lx\n"
- "\tdoes not correspond to any PCI address\n",
- pcibr_soft->bs_name, bad_xaddr);
-
- /* caller will dump contents of ioe struct */
- return IOERROR_XTALKLEVEL;
- }
- /*
- * Read the PCI Bridge error log registers.
- */
- bridge_int_status = bridge->b_int_status;
- bridge_pci_err_upper = bridge->b_pci_err_upper;
- bridge_pci_err_lower = bridge->b_pci_err_lower;
-
- bridge_pci_err_addr =
- bridge_pci_err_lower
- | (((iopaddr_t) bridge_pci_err_upper
- & BRIDGE_ERRUPPR_ADDRMASK) << 32);
-
- /*
- * Actual PCI Error handling situation.
- * Typically happens when a user level process accesses
- * PCI space, and it causes some error.
- *
- * Due to PCI Bridge implementation, we get two indication
- * for a read error: an interrupt and a Bus error.
- * We like to handle read error in the bus error context.
- * But the interrupt comes and goes before bus error
- * could make much progress. (NOTE: interrupd does
- * come in _after_ bus error processing starts. But it's
- * completed by the time bus error code reaches PCI PIO
- * error handling.
- * Similarly write error results in just an interrupt,
- * and error handling has to be done at interrupt level.
- * There is no way to distinguish at interrupt time, if an
- * error interrupt is due to read/write error..
- */
-
- /* We know the xtalk addr, the raw PCI bus space,
- * the raw PCI bus address, the decoded PCI bus
- * space, the offset within that space, and the
- * decoded PCI slot (which may be "PCIIO_SLOT_NONE" if no slot
- * is known to be involved).
- */
-
- /*
- * Hand the error off to the handler registered
- * for the slot that should have decoded the error,
- * or to generic PCI handling (if pciio decides that
- * such is appropriate).
- */
- retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
-
- if (retval != IOERROR_HANDLED) {
-
- /* Generate a generic message for IOERROR_UNHANDLED
- * since the subsidiary handlers were silent, and
- * did no recovery.
- */
- if (retval == IOERROR_UNHANDLED) {
- retval = IOERROR_PANIC;
-
- /* we may or may not want to print some of this,
- * depending on debug level and which error code.
- */
-
- PRINT_ALERT(
- "PIO Error on PCI Bus %s",
- pcibr_soft->bs_name);
- /* this decodes part of the ioe; our caller
- * will dump the raw details in DEBUG and
- * kdebug kernels.
- */
- BEM_ADD_IOE(ioe);
- }
-#if defined(FORCE_ERRORS)
- if (0) {
-#elif !DEBUG
- if (kdebug) {
-#endif
- /*
- * dump raw data from bridge
- */
-
- BEM_ADD_STR("DEBUG DATA -- raw info from Bridge ASIC:\n");
- BEM_ADD_REG(bridge_int_status);
- BEM_ADD_VAR(bridge_pci_err_upper);
- BEM_ADD_VAR(bridge_pci_err_lower);
- BEM_ADD_VAR(bridge_pci_err_addr);
- BEM_ADD_SPC(raw_space);
- BEM_ADD_VAR(raw_paddr);
- if (IOERROR_FIELDVALID(ioe, widgetdev)) {
-
-#ifdef LATER
- slot = pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe,
- widgetdev));
- func = pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe,
- widgetdev));
-#else
- slot = -1;
- func = -1;
-#endif
- if (slot < 8) {
-#ifdef SUPPORT_SGI_CMN_ERR_STUFF
- bridgereg_t device = bridge->b_device[slot].reg;
-#endif
-
- BEM_ADD_VAR(slot);
- BEM_ADD_VAR(func);
- BEM_ADD_REG(device);
- }
- }
-#if !DEBUG || defined(FORCE_ERRORS)
- }
-#endif
-
- /*
- * Since error could not be handled at lower level,
- * error data logged has not been cleared.
- * Clean up errors, and
- * re-enable bridge to interrupt on error conditions.
- * NOTE: Wheather we get the interrupt on PCI_ABORT or not is
- * dependent on INT_ENABLE register. This write just makes sure
- * that if the interrupt was enabled, we do get the interrupt.
- *
- * CAUTION: Resetting bit BRIDGE_IRR_PCI_GRP_CLR, acknowledges
- * a group of interrupts. If while handling this error,
- * some other error has occurred, that would be
- * implicitly cleared by this write.
- * Need a way to ensure we don't inadvertently clear some
- * other errors.
- */
-#ifdef LATER
- if (IOERROR_FIELDVALID(ioe, widgetdev))
- pcibr_device_disable(pcibr_soft,
- pciio_widgetdev_slot_get(
- IOERROR_GETVALUE(ioe, widgetdev)));
-#endif
-
- if (mode == MODE_DEVUSERERROR)
- pcibr_error_cleanup(pcibr_soft, error_code);
- }
- return retval;
-}
-
-/*
- * bridge_dmaerror
- * Some error was identified in a DMA transaction.
- * This routine will identify the <device, address> that caused the error,
- * and try to invoke the appropriate bus service to handle this.
- */
-
-#define BRIDGE_DMA_READ_ERROR (BRIDGE_ISR_RESP_XTLK_ERR|BRIDGE_ISR_XREAD_REQ_TIMEOUT)
-
-int
-pcibr_dmard_error(
- pcibr_soft_t pcibr_soft,
- int error_code,
- ioerror_mode_t mode,
- ioerror_t *ioe)
-{
- devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t bus_lowaddr, bus_uppraddr;
- int retval = 0;
- int bufnum;
-
- /*
- * In case of DMA errors, bridge should have logged the
- * address that caused the error.
- * Look up the address, in the bridge error registers, and
- * take appropriate action
- */
-#ifdef LATER
- ASSERT(IOERROR_GETVALUE(ioe, widgetnum) == pcibr_soft->bs_xid);
- ASSERT(bridge);
-#endif
-
- /*
- * read error log registers
- */
- bus_lowaddr = bridge->b_wid_resp_lower;
- bus_uppraddr = bridge->b_wid_resp_upper;
-
- bufnum = BRIDGE_RESP_ERRUPPR_BUFNUM(bus_uppraddr);
- IOERROR_SETVALUE(ioe, widgetdev,
- pciio_widgetdev_create(
- BRIDGE_RESP_ERRUPPR_DEVICE(bus_uppraddr),
- 0));
- IOERROR_SETVALUE(ioe, busaddr,
- (bus_lowaddr |
- ((iopaddr_t)
- (bus_uppraddr &
- BRIDGE_ERRUPPR_ADDRMASK) << 32)));
-
- /*
- * need to ensure that the xtalk address in ioe
- * maps to PCI error address read from bridge.
- * How to convert PCI address back to Xtalk address ?
- * (better idea: convert XTalk address to PCI address
- * and then do the compare!)
- */
-
- retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
- if (retval != IOERROR_HANDLED)
-#ifdef LATER
- pcibr_device_disable(pcibr_soft,
- pciio_widgetdev_slot_get(
- IOERROR_GETVALUE(ioe,widgetdev)));
-#else
- pcibr_device_disable(pcibr_soft,
- pciio_widgetdev_slot_get(-1));
-#endif
-
- /*
- * Re-enable bridge to interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR
- * NOTE: Wheather we get the interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR or
- * not is dependent on INT_ENABLE register. This write just makes sure
- * that if the interrupt was enabled, we do get the interrupt.
- */
- bridge->b_int_rst_stat = BRIDGE_IRR_RESP_BUF_GRP_CLR;
-
- /*
- * Also, release the "bufnum" back to buffer pool that could be re-used.
- * This is done by "disabling" the buffer for a moment, then restoring
- * the original assignment.
- */
-
- {
- reg_p regp;
- bridgereg_t regv;
- bridgereg_t mask;
-
- regp = (bufnum & 1)
- ? &bridge->b_odd_resp
- : &bridge->b_even_resp;
-
- mask = 0xF << ((bufnum >> 1) * 4);
-
- regv = *regp;
- *regp = regv & ~mask;
- *regp = regv;
- }
-
- return retval;
-}
-
-/*
- * pcibr_dmawr_error:
- * Handle a dma write error caused by a device attached to this bridge.
- *
- * ioe has the widgetnum, widgetdev, and memaddr fields updated
- * But we don't know the PCI address that corresponds to "memaddr"
- * nor do we know which device driver is generating this address.
- *
- * There is no easy way to find out the PCI address(es) that map
- * to a specific system memory address. Bus handling code is also
- * of not much help, since they don't keep track of the DMA mapping
- * that have been handed out.
- * So it's a dead-end at this time.
- *
- * If translation is available, we could invoke the error handling
- * interface of the device driver.
- */
-/*ARGSUSED */
-int
-pcibr_dmawr_error(
- pcibr_soft_t pcibr_soft,
- int error_code,
- ioerror_mode_t mode,
- ioerror_t *ioe)
-{
- devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
- int retval;
-
- retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
-
-#ifdef LATER
- if (retval != IOERROR_HANDLED) {
- pcibr_device_disable(pcibr_soft,
- pciio_widgetdev_slot_get(
- IOERROR_GETVALUE(ioe, widgetdev)));
-
- }
-#endif
- return retval;
-}
-
-/*
- * Bridge error handler.
- * Interface to handle all errors that involve bridge in some way.
- *
- * This normally gets called from xtalk error handler.
- * ioe has different set of fields set depending on the error that
- * was encountered. So, we have a bit field indicating which of the
- * fields are valid.
- *
- * NOTE: This routine could be operating in interrupt context. So,
- * don't try to sleep here (till interrupt threads work!!)
- */
-LOCAL int
-pcibr_error_handler(
- error_handler_arg_t einfo,
- int error_code,
- ioerror_mode_t mode,
- ioerror_t *ioe)
-{
- pcibr_soft_t pcibr_soft;
- int retval = IOERROR_BADERRORCODE;
-
- pcibr_soft = (pcibr_soft_t) einfo;
-
- /* If we are in the action handling phase clean out the error state
- * on the xswitch.
- */
-#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
- if (e_state == ERROR_STATE_ACTION)
- (void)error_state_set(xconn_vhdl, ERROR_STATE_NONE);
-#endif
-
-#if DEBUG && ERROR_DEBUG
- printk("%s: pcibr_error_handler\n", pcibr_soft->bs_name);
-#endif
-
- ASSERT(pcibr_soft != NULL);
-
- if (error_code & IOECODE_PIO)
- retval = pcibr_pioerror(pcibr_soft, error_code, mode, ioe);
-
- if (error_code & IOECODE_DMA) {
- if (error_code & IOECODE_READ) {
- /*
- * DMA read error occurs when a device attached to the bridge
- * tries to read some data from system memory, and this
- * either results in a timeout or access error.
- * First case is indicated by the bit "XREAD_REQ_TOUT"
- * and second case by "RESP_XTALK_ERROR" bit in bridge error
- * interrupt status register.
- *
- * pcibr_error_intr_handler would get invoked first, and it has
- * the responsibility of calling pcibr_error_handler with
- * suitable parameters.
- */
-
- retval = pcibr_dmard_error(pcibr_soft, error_code, MODE_DEVERROR, ioe);
- }
- if (error_code & IOECODE_WRITE) {
- /*
- * A device attached to this bridge has been generating
- * bad DMA writes. Find out the device attached, and
- * slap on it's wrist.
- */
-
- retval = pcibr_dmawr_error(pcibr_soft, error_code, MODE_DEVERROR, ioe);
- }
- }
- return retval;
-
-}
-
-/*
- * Reenable a device after handling the error.
- * This is called by the lower layers when they wish to be reenabled
- * after an error.
- * Note that each layer would be calling the previous layer to reenable
- * first, before going ahead with their own re-enabling.
- */
-
-int
-pcibr_error_devenable(devfs_handle_t pconn_vhdl, int error_code)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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);
-
- ASSERT(error_code & IOECODE_PIO);
-
- /* If the error is not known to be a write,
- * we have to call devenable.
- * write errors are isolated to the bridge.
- */
- if (!(error_code & IOECODE_WRITE)) {
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- int rc;
-
- rc = xtalk_error_devenable(xconn_vhdl, pciio_slot, error_code);
- if (rc != IOERROR_HANDLED)
- return rc;
- }
- pcibr_error_cleanup(pcibr_soft, error_code);
- return IOERROR_HANDLED;
-}
-
-/* =====================================================================
- * CONFIGURATION MANAGEMENT
- */
-/*ARGSUSED */
-void
-pcibr_provider_startup(devfs_handle_t pcibr)
-{
-}
-
-/*ARGSUSED */
-void
-pcibr_provider_shutdown(devfs_handle_t pcibr)
-{
-}
-
-int
-pcibr_reset(devfs_handle_t conn)
-{
- pciio_info_t pciio_info = pciio_info_get(conn);
- 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;
- bridgereg_t ctlreg;
- unsigned cfgctl[8];
- unsigned long s;
- int f, nf;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- int win;
-
- if (pcibr_soft->bs_slot[pciio_slot].has_host) {
- pciio_slot = pcibr_soft->bs_slot[pciio_slot].host_slot;
- pcibr_info = pcibr_soft->bs_slot[pciio_slot].bss_infos[0];
- }
- if (pciio_slot < 4) {
- s = pcibr_lock(pcibr_soft);
- nf = pcibr_soft->bs_slot[pciio_slot].bss_ninfo;
- pcibr_infoh = pcibr_soft->bs_slot[pciio_slot].bss_infos;
- for (f = 0; f < nf; ++f)
- if (pcibr_infoh[f])
- cfgctl[f] = bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4];
-
- ctlreg = bridge->b_wid_control;
- bridge->b_wid_control = ctlreg | BRIDGE_CTRL_RST(pciio_slot);
- /* XXX delay? */
- bridge->b_wid_control = ctlreg;
- /* XXX delay? */
-
- for (f = 0; f < nf; ++f)
- if ((pcibr_info = pcibr_infoh[f]))
- for (win = 0; win < 6; ++win)
- if (pcibr_info->f_window[win].w_base != 0)
- bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_BASE_ADDR(win) / 4] =
- pcibr_info->f_window[win].w_base;
- for (f = 0; f < nf; ++f)
- if (pcibr_infoh[f])
- bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4] = cfgctl[f];
- pcibr_unlock(pcibr_soft, s);
-
- return 0;
- }
-#ifdef SUPPORT_PRINTING_V_FORMAT
- PRINT_WARNING( "%v: pcibr_reset unimplemented for slot %d\n",
- conn, pciio_slot);
-#endif
- return -1;
-}
-
-pciio_endian_t
-pcibr_endian_set(devfs_handle_t pconn_vhdl,
- pciio_endian_t device_end,
- pciio_endian_t desired_end)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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);
- bridgereg_t devreg;
- unsigned long s;
-
- /*
- * Bridge supports hardware swapping; so we can always
- * arrange for the caller's desired endianness.
- */
-
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- if (device_end != desired_end)
- devreg |= BRIDGE_DEV_SWAP_BITS;
- else
- devreg &= ~BRIDGE_DEV_SWAP_BITS;
-
- /* NOTE- if we ever put SWAP bits
- * onto the disabled list, we will
- * have to change the logic here.
- */
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
-
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
-
-#if DEBUG && PCIBR_DEV_DEBUG
- printk("pcibr Device(%d): 0x%p\n", pciio_slot, bridge->b_device[pciio_slot].reg);
-#endif
-
- return desired_end;
-}
-
-/* This (re)sets the GBR and REALTIME bits and also keeps track of how
- * many sets are outstanding. Reset succeeds only if the number of outstanding
- * sets == 1.
- */
-int
-pcibr_priority_bits_set(pcibr_soft_t pcibr_soft,
- pciio_slot_t pciio_slot,
- pciio_priority_t device_prio)
-{
- unsigned long s;
- int *counter;
- bridgereg_t rtbits = 0;
- bridgereg_t devreg;
- int rc = PRIO_SUCCESS;
-
- /* in dual-slot configurations, the host and the
- * guest have separate DMA resources, so they
- * have separate requirements for priority bits.
- */
-
- counter = &(pcibr_soft->bs_slot[pciio_slot].bss_pri_uctr);
-
- /*
- * Bridge supports PCI notions of LOW and HIGH priority
- * arbitration rings via a "REAL_TIME" bit in the per-device
- * Bridge register. The "GBR" bit controls access to the GBR
- * ring on the xbow. These two bits are (re)set together.
- *
- * XXX- Bug in Rev B Bridge Si:
- * Symptom: Prefetcher starts operating incorrectly. This happens
- * due to corruption of the address storage ram in the prefetcher
- * when a non-real time PCI request is pulled and a real-time one is
- * put in it's place. Workaround: Use only a single arbitration ring
- * on PCI bus. GBR and RR can still be uniquely used per
- * device. NETLIST MERGE DONE, WILL BE FIXED IN REV C.
- */
-
- if (pcibr_soft->bs_rev_num != BRIDGE_PART_REV_B)
- rtbits |= BRIDGE_DEV_RT;
-
- /* NOTE- if we ever put DEV_RT or DEV_GBR on
- * the disabled list, we will have to take
- * it into account here.
- */
-
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- if (device_prio == PCI_PRIO_HIGH) {
- if ((++*counter == 1)) {
- if (rtbits)
- devreg |= rtbits;
- else
- rc = PRIO_FAIL;
- }
- } else if (device_prio == PCI_PRIO_LOW) {
- if (*counter <= 0)
- rc = PRIO_FAIL;
- else if (--*counter == 0)
- if (rtbits)
- devreg &= ~rtbits;
- }
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
-
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
-
- return rc;
-}
-
-pciio_priority_t
-pcibr_priority_set(devfs_handle_t pconn_vhdl,
- pciio_priority_t device_prio)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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);
-
- (void) pcibr_priority_bits_set(pcibr_soft, pciio_slot, device_prio);
-
- return device_prio;
-}
-
-/*
- * Interfaces to allow special (e.g. SGI) drivers to set/clear
- * Bridge-specific device flags. Many flags are modified through
- * PCI-generic interfaces; we don't allow them to be directly
- * manipulated here. Only flags that at this point seem pretty
- * Bridge-specific can be set through these special interfaces.
- * We may add more flags as the need arises, or remove flags and
- * create PCI-generic interfaces as the need arises.
- *
- * Returns 0 on failure, 1 on success
- */
-int
-pcibr_device_flags_set(devfs_handle_t pconn_vhdl,
- pcibr_device_flags_t flags)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_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);
- bridgereg_t set = 0;
- bridgereg_t clr = 0;
-
- ASSERT((flags & PCIBR_DEVICE_FLAGS) == flags);
-
- if (flags & PCIBR_WRITE_GATHER)
- set |= BRIDGE_DEV_PMU_WRGA_EN;
- if (flags & PCIBR_NOWRITE_GATHER)
- clr |= BRIDGE_DEV_PMU_WRGA_EN;
-
- if (flags & PCIBR_WRITE_GATHER)
- set |= BRIDGE_DEV_DIR_WRGA_EN;
- if (flags & PCIBR_NOWRITE_GATHER)
- clr |= BRIDGE_DEV_DIR_WRGA_EN;
-
- if (flags & PCIBR_PREFETCH)
- set |= BRIDGE_DEV_PREF;
- if (flags & PCIBR_NOPREFETCH)
- clr |= BRIDGE_DEV_PREF;
-
- if (flags & PCIBR_PRECISE)
- set |= BRIDGE_DEV_PRECISE;
- if (flags & PCIBR_NOPRECISE)
- clr |= BRIDGE_DEV_PRECISE;
-
- if (flags & PCIBR_BARRIER)
- set |= BRIDGE_DEV_BARRIER;
- if (flags & PCIBR_NOBARRIER)
- clr |= BRIDGE_DEV_BARRIER;
-
- if (flags & PCIBR_64BIT)
- set |= BRIDGE_DEV_DEV_SIZE;
- if (flags & PCIBR_NO64BIT)
- clr |= BRIDGE_DEV_DEV_SIZE;
-
- if (set || clr) {
- bridgereg_t devreg;
- unsigned long s;
-
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- devreg = (devreg & ~clr) | set;
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
-
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
-#if DEBUG && PCIBR_DEV_DEBUG
- printk("pcibr Device(%d): %R\n", pciio_slot, bridge->b_device[pciio_slot].regbridge->b_device[pciio_slot].reg, device_bits);
-#endif
- }
- return (1);
-}
-
-#ifdef LITTLE_ENDIAN
-/*
- * on sn-ia we need to twiddle the addresses going out
- * the pci bus because we use the unswizzled synergy space
- * (the alternative is to use the swizzled synergy space
- * and byte swap the data)
- */
-#define CB(b,r) (((volatile uint8_t *) b)[((r)^4)])
-#define CS(b,r) (((volatile uint16_t *) b)[((r^4)/2)])
-#define CW(b,r) (((volatile uint32_t *) b)[((r^4)/4)])
-#else
-#define CB(b,r) (((volatile uint8_t *) cfgbase)[(r)^3])
-#define CS(b,r) (((volatile uint16_t *) cfgbase)[((r)/2)^1])
-#define CW(b,r) (((volatile uint32_t *) cfgbase)[(r)/4])
-#endif /* LITTLE_ENDIAN */
-
-
-LOCAL cfg_p
-pcibr_config_addr(devfs_handle_t conn,
- unsigned reg)
-{
- pcibr_info_t pcibr_info;
- pciio_slot_t pciio_slot;
- pciio_function_t pciio_func;
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- cfg_p cfgbase = (cfg_p)0;
-
- pcibr_info = pcibr_info_get(conn);
-
- pciio_slot = pcibr_info->f_slot;
- if (pciio_slot == PCIIO_SLOT_NONE)
- pciio_slot = PCI_TYPE1_SLOT(reg);
-
- pciio_func = pcibr_info->f_func;
- if (pciio_func == PCIIO_FUNC_NONE)
- pciio_func = PCI_TYPE1_FUNC(reg);
-
- pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
-
- bridge = pcibr_soft->bs_base;
-
- cfgbase = bridge->b_type0_cfg_dev[pciio_slot].f[pciio_func].l;
-
- return cfgbase;
-}
-
-uint64_t
-pcibr_config_get(devfs_handle_t conn,
- unsigned reg,
- unsigned size)
-{
- return do_pcibr_config_get(pcibr_config_addr(conn, reg),
- PCI_TYPE1_REG(reg), size);
-}
-
-LOCAL uint64_t
-do_pcibr_config_get(
- cfg_p cfgbase,
- unsigned reg,
- unsigned size)
-{
- unsigned value;
-
-
- value = CW(cfgbase, reg);
-
- if (reg & 3)
- value >>= 8 * (reg & 3);
- if (size < 4)
- value &= (1 << (8 * size)) - 1;
-
- return value;
-}
-
-void
-pcibr_config_set(devfs_handle_t conn,
- unsigned reg,
- unsigned size,
- uint64_t value)
-{
- do_pcibr_config_set(pcibr_config_addr(conn, reg),
- PCI_TYPE1_REG(reg), size, value);
-}
-
-LOCAL void
-do_pcibr_config_set(cfg_p cfgbase,
- unsigned reg,
- unsigned size,
- uint64_t value)
-{
- switch (size) {
- case 1:
- CB(cfgbase, reg) = value;
- break;
- case 2:
- if (reg & 1) {
- CB(cfgbase, reg) = value;
- CB(cfgbase, reg + 1) = value >> 8;
- } else
- CS(cfgbase, reg) = value;
- break;
- case 3:
- if (reg & 1) {
- CB(cfgbase, reg) = value;
- CS(cfgbase, (reg + 1)) = value >> 8;
- } else {
- CS(cfgbase, reg) = value;
- CB(cfgbase, reg + 2) = value >> 16;
- }
- break;
-
- case 4:
- CW(cfgbase, reg) = value;
- break;
- }
-}
-
-pciio_provider_t pcibr_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 *) pcibr_error_devenable,
- (pciio_error_extract_f *) pcibr_error_extract,
-
-#ifdef LATER
- (pciio_driver_reg_callback_f *) pcibr_driver_reg_callback,
- (pciio_driver_unreg_callback_f *) pcibr_driver_unreg_callback,
-#else
- (pciio_driver_reg_callback_f *) 0,
- (pciio_driver_unreg_callback_f *) 0,
-#endif
- (pciio_device_unregister_f *) pcibr_device_unregister,
- (pciio_dma_enabled_f *) pcibr_dma_enabled,
-};
-
-LOCAL pcibr_hints_t
-pcibr_hints_get(devfs_handle_t xconn_vhdl, int alloc)
-{
- arbitrary_info_t ainfo = 0;
- graph_error_t rv;
- pcibr_hints_t hint;
-
- rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
-
- if (alloc && (rv != GRAPH_SUCCESS)) {
-
- NEW(hint);
- hint->rrb_alloc_funct = NULL;
- hint->ph_intr_bits = NULL;
- rv = hwgraph_info_add_LBL(xconn_vhdl,
- INFO_LBL_PCIBR_HINTS,
- (arbitrary_info_t) hint);
- if (rv != GRAPH_SUCCESS)
- goto abnormal_exit;
-
- rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
-
- if (rv != GRAPH_SUCCESS)
- goto abnormal_exit;
-
- if (ainfo != (arbitrary_info_t) hint)
- goto abnormal_exit;
- }
- return (pcibr_hints_t) ainfo;
-
-abnormal_exit:
-#ifdef LATER
- printf("SHOULD NOT BE HERE\n");
-#endif
- DEL(hint);
- return(NULL);
-
-}
-
-void
-pcibr_hints_fix_some_rrbs(devfs_handle_t xconn_vhdl, unsigned mask)
-{
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
-
- if (hint)
- hint->ph_rrb_fixed = mask;
-#if DEBUG
- else
- printk("pcibr_hints_fix_rrbs: pcibr_hints_get failed at\n"
- "\t%p\n", xconn_vhdl);
-#endif
-}
-
-void
-pcibr_hints_fix_rrbs(devfs_handle_t xconn_vhdl)
-{
- pcibr_hints_fix_some_rrbs(xconn_vhdl, 0xFF);
-}
-
-void
-pcibr_hints_dualslot(devfs_handle_t xconn_vhdl,
- pciio_slot_t host,
- pciio_slot_t guest)
-{
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
-
- if (hint)
- hint->ph_host_slot[guest] = host + 1;
-#if DEBUG
- else
- printk("pcibr_hints_dualslot: pcibr_hints_get failed at\n"
- "\t%p\n", xconn_vhdl);
-#endif
-}
-
-void
-pcibr_hints_intr_bits(devfs_handle_t xconn_vhdl,
- pcibr_intr_bits_f *xxx_intr_bits)
-{
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
-
- if (hint)
- hint->ph_intr_bits = xxx_intr_bits;
-#if DEBUG
- else
- printk("pcibr_hints_intr_bits: pcibr_hints_get failed at\n"
- "\t%p\n", xconn_vhdl);
-#endif
-}
-
-void
-pcibr_set_rrb_callback(devfs_handle_t xconn_vhdl, rrb_alloc_funct_t rrb_alloc_funct)
-{
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
-
- if (hint)
- hint->rrb_alloc_funct = rrb_alloc_funct;
-}
-
-void
-pcibr_hints_handsoff(devfs_handle_t xconn_vhdl)
-{
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
-
- if (hint)
- hint->ph_hands_off = 1;
-#if DEBUG
- else
- printk("pcibr_hints_handsoff: pcibr_hints_get failed at\n"
- "\t%p\n", xconn_vhdl);
-#endif
-}
-
-void
-pcibr_hints_subdevs(devfs_handle_t xconn_vhdl,
- pciio_slot_t slot,
- uint64_t subdevs)
-{
- arbitrary_info_t ainfo = 0;
- char sdname[16];
- devfs_handle_t pconn_vhdl = GRAPH_VERTEX_NONE;
-
- sprintf(sdname, "pci/%d", slot);
- (void) hwgraph_path_add(xconn_vhdl, sdname, &pconn_vhdl);
- if (pconn_vhdl == GRAPH_VERTEX_NONE) {
-#if DEBUG
- printk("pcibr_hints_subdevs: hwgraph_path_create failed at\n"
- "\t%p (seeking %s)\n", xconn_vhdl, sdname);
-#endif
- return;
- }
- hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
- if (ainfo == 0) {
- uint64_t *subdevp;
-
- NEW(subdevp);
- if (!subdevp) {
-#if DEBUG
- printk("pcibr_hints_subdevs: subdev ptr alloc failed at\n"
- "\t%p\n", pconn_vhdl);
-#endif
- return;
- }
- *subdevp = subdevs;
- hwgraph_info_add_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, (arbitrary_info_t) subdevp);
- hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
- if (ainfo == (arbitrary_info_t) subdevp)
- return;
- DEL(subdevp);
- if (ainfo == (arbitrary_info_t) NULL) {
-#if DEBUG
- printk("pcibr_hints_subdevs: null subdevs ptr at\n"
- "\t%p\n", pconn_vhdl);
-#endif
- return;
- }
-#if DEBUG
- printk("pcibr_subdevs_get: dup subdev add_LBL at\n"
- "\t%p\n", pconn_vhdl);
-#endif
- }
- *(uint64_t *) ainfo = subdevs;
-}
-
-
-#ifdef LATER
-
-#include <sys/idbg.h>
-#include <sys/idbgentry.h>
-
-char *pci_space[] = {"NONE",
- "ROM",
- "IO",
- "",
- "MEM",
- "MEM32",
- "MEM64",
- "CFG",
- "WIN0",
- "WIN1",
- "WIN2",
- "WIN3",
- "WIN4",
- "WIN5",
- "",
- "BAD"};
-
-void
-idbg_pss_func(pcibr_info_h pcibr_infoh, int func)
-{
- pcibr_info_t pcibr_info = pcibr_infoh[func];
- char name[MAXDEVNAME];
- int win;
-
- if (!pcibr_info)
- return;
- qprintf("Per-slot Function Info\n");
-#ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(name, "%v", pcibr_info->f_vertex);
-#endif
- qprintf("\tSlot Name : %s\n",name);
- qprintf("\tPCI Bus : %d ",pcibr_info->f_bus);
- qprintf("Slot : %d ", pcibr_info->f_slot);
- qprintf("Function : %d ", pcibr_info->f_func);
- qprintf("VendorId : 0x%x " , pcibr_info->f_vendor);
- qprintf("DeviceId : 0x%x\n", pcibr_info->f_device);
-#ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(name, "%v", pcibr_info->f_master);
-#endif
- qprintf("\tBus provider : %s\n",name);
- qprintf("\tProvider Fns : 0x%x ", pcibr_info->f_pops);
- qprintf("Error Handler : 0x%x Arg 0x%x\n",
- pcibr_info->f_efunc,pcibr_info->f_einfo);
- for(win = 0 ; win < 6 ; win++)
- qprintf("\tBase Reg #%d space %s base 0x%x size 0x%x\n",
- win,pci_space[pcibr_info->f_window[win].w_space],
- pcibr_info->f_window[win].w_base,
- pcibr_info->f_window[win].w_size);
-
- qprintf("\tRom base 0x%x size 0x%x\n",
- pcibr_info->f_rbase,pcibr_info->f_rsize);
-
- qprintf("\tInterrupt Bit Map\n");
- qprintf("\t\tPCI Int#\tBridge Pin#\n");
- for (win = 0 ; win < 4; win++)
- qprintf("\t\tINT%c\t\t%d\n",win+'A',pcibr_info->f_ibit[win]);
- qprintf("\n");
-}
-
-
-void
-idbg_pss_info(pcibr_soft_t pcibr_soft, pciio_slot_t slot)
-{
- pcibr_soft_slot_t pss;
- char slot_conn_name[MAXDEVNAME];
- int func;
-
- pss = &pcibr_soft->bs_slot[slot];
- qprintf("PCI INFRASTRUCTURAL INFO FOR SLOT %d\n", slot);
- qprintf("\tHost Present ? %s ", pss->has_host ? "yes" : "no");
- qprintf("\tHost Slot : %d\n",pss->host_slot);
- sprintf(slot_conn_name, "%v", pss->slot_conn);
- qprintf("\tSlot Conn : %s\n",slot_conn_name);
- qprintf("\t#Functions : %d\n",pss->bss_ninfo);
- for (func = 0; func < pss->bss_ninfo; func++)
- idbg_pss_func(pss->bss_infos,func);
- qprintf("\tSpace : %s ",pci_space[pss->bss_devio.bssd_space]);
- qprintf("\tBase : 0x%x ", pss->bss_devio.bssd_base);
- qprintf("\tShadow Devreg : 0x%x\n", pss->bss_device);
- qprintf("\tUsage counts : pmu %d d32 %d d64 %d\n",
- pss->bss_pmu_uctr,pss->bss_d32_uctr,pss->bss_d64_uctr);
-
- qprintf("\tDirect Trans Info : d64_base 0x%x d64_flags 0x%x"
- "d32_base 0x%x d32_flags 0x%x\n",
- pss->bss_d64_base, pss->bss_d64_flags,
- pss->bss_d32_base, pss->bss_d32_flags);
-
- qprintf("\tExt ATEs active ? %s",
- atomic_read(&pss->bss_ext_ates_active) ? "yes" : "no");
- qprintf(" Command register : 0x%x ", pss->bss_cmd_pointer);
- qprintf(" Shadow command val : 0x%x\n", pss->bss_cmd_shadow);
-
- qprintf("\tRRB Info : Valid %d+%d Reserved %d\n",
- pcibr_soft->bs_rrb_valid[slot],
- pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
-
-}
-
-int ips = 0;
-
-void
-idbg_pss(pcibr_soft_t pcibr_soft)
-{
- pciio_slot_t slot;
-
-
- if (ips >= 0 && ips < 8)
- idbg_pss_info(pcibr_soft,ips);
- else if (ips < 0)
- for (slot = 0; slot < 8; slot++)
- idbg_pss_info(pcibr_soft,slot);
- else
- qprintf("Invalid ips %d\n",ips);
-}
-
-#endif /* LATER */
-
-int
-pcibr_dma_enabled(devfs_handle_t pconn_vhdl)
-{
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
-
-
- return xtalk_dma_enabled(pcibr_soft->bs_conn);
-}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)