pcibr.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:227k
- /*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1992 - 1997, 2000-2002 Silicon Graphics, Inc. All rights reserved.
- */
- int NeedXbridgeSwap = 0;
- #include <linux/types.h>
- #include <linux/config.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <asm/sn/sgi.h>
- #include <asm/sn/sn_cpuid.h>
- #include <asm/sn/addrs.h>
- #include <asm/sn/arch.h>
- #include <asm/sn/iograph.h>
- #include <asm/sn/invent.h>
- #include <asm/sn/hcl.h>
- #include <asm/sn/labelcl.h>
- #include <asm/sn/xtalk/xwidget.h>
- #include <asm/sn/pci/bridge.h>
- #include <asm/sn/pci/pciio.h>
- #include <asm/sn/pci/pcibr.h>
- #include <asm/sn/pci/pcibr_private.h>
- #include <asm/sn/pci/pci_defs.h>
- #include <asm/sn/prio.h>
- #include <asm/sn/xtalk/xbow.h>
- #include <asm/sn/ioc3.h>
- #include <asm/sn/eeprom.h>
- #include <asm/sn/io.h>
- #include <asm/sn/sn_private.h>
- #ifdef __ia64
- #define rmallocmap atemapalloc
- #define rmfreemap atemapfree
- #define rmfree atefree
- #define rmalloc atealloc
- #endif
- extern boolean_t is_sys_critical_vertex(devfs_handle_t);
- #undef PCIBR_ATE_DEBUG
- #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
- #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
- #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 */
- 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 */
- };
- #if PCIBR_SOFT_LIST
- pcibr_list_p pcibr_list = 0;
- #endif
- #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);
- extern void free_pciio_dmamap(pcibr_dmamap_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);
- 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);
- 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%sn",
- 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 freen",
- 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 %Rn", 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%pn", 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_initn");
- #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 = snia_kmem_zalloc(sizeof(*slotp), KM_SLEEP);
- if (slotp == NULL) {
- return(ENOMEM);
- }
- pss = &pcibr_soft->bs_slot[slot];
-
- printk("nPCI INFRASTRUCTURAL INFO FOR SLOT %dnn", 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);
- }
- snia_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);
- }
- /* 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: %lldn",
- 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: %lldn",
- 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 ATEsn", bridge, num_entries);
- else
- printk("bridge at 0x%x: no externa9422l ATE RAM foundn", 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 %dn", 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%xn"
- "SWIN base 0x%x last 0x%xn"
- "MEM base 0x%x last 0x%xn"
- "+++++++++++++++++++++++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;
- 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) {
- printk(KERN_WARNING "%s pcibr: pci slot %d func %d has strange header type 0x%xn",
- pcibr_soft->bs_name, slot, func, htype);
- continue;
- }
- #if DEBUG && ATTACH_DEBUG
- printk(KERN_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;
- #ifdef LITTLE_ENDIAN
- cmd_reg = cfgw[(PCI_CFG_COMMAND ^ 4) / 4];
- #else
- cmd_reg = cfgw[PCI_CFG_COMMAND / 4];
- #endif
-
- wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
- for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win) {
- 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);
- 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;
- nbars = PCI_CFG_BASE_ADDRS;
- 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 promn",
- 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 %dn",
- 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%xn", &(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 pcibrn",
- slot, func, win, space, base, base + size - 1);
- else
- printk("pcibr: slot %d func %d window %d, unable to alloc 0x%x in 0x%pn",
- 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%lxn", 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+%dn", 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;
- #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)
- printk(KERN_WARNING "%v is a system critical device vertexn", conn_vhdl);
- #else
- printk(KERN_WARNING "%p is a system critical device vertexn", (void *)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= %pn",
- slot, f_c);
- {
- int pos;
- char dname[256];
- pos = devfs_generate_path(f_c, dname, 256);
- printk("%s : path= %sn", __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 = (device_desc_t)0;
- 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= %pn", 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
- if (XWIDGET_PART_REV_NUM(bridge->b_wid_id) == XBRIDGE_PART_REV_A)
- NeedXbridgeSwap = 1;
- /*
- * 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 %sn",
- 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;
- }
- /*
- * 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;
- /* 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/controlers 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().
- */
- 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 ATEsn", 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 ATEsn",
- 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.
- */
- xtalk_intr = xtalk_intr_alloc(xconn_vhdl, dev_desc, pcibr_vhdl);
- ASSERT(xtalk_intr != NULL);
- 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, (xtalk_intr_setfunc_t)pcibr_setwidint, (void *)bridge);
- /*
- * 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 */