/* * Misc utility routines for accessing chip-specific features * of the BOOKER NCI (non coherent interconnect) based Broadcom chips. * * Broadcom Proprietary and Confidential. Copyright (C) 2020, * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; * the contents of this file may not be disclosed to third parties, * copied or duplicated in any form, in whole or in part, without * the prior written permission of Broadcom. * * * <> */ #include #include #include #include #include #include "siutils_priv.h" #include #include #include #define NCI_BAD_REG 0xbbadd000u /* Bad Register Address */ #define NCI_BAD_INDEX -1 /* Bad Index */ #define OOBR_BASE_MASK 0x00001FFFu /* Mask to get Base address of OOBR */ #define EROM1_BASE_MASK 0x00000FFFu /* Mask to get Base address of EROM1 */ /* Core Info */ #define COREINFO_COREID_MASK 0x00000FFFu /* Bit-11 to 0 */ #define COREINFO_REV_MASK 0x000FF000u /* Core Rev Mask */ #define COREINFO_REV_SHIFT 12u /* Bit-12 */ #define COREINFO_MFG_MASK 0x00F00000u /* Core Mfg Mask */ #define COREINFO_MFG_SHIFT 20u /* Bit-20 */ #define COREINFO_BPID_MASK 0x07000000u /* 26-24 Gives Backplane ID */ #define COREINFO_BPID_SHIFT 24u /* Bit:26-24 */ #define COREINFO_ISBP_MASK 0x08000000u /* Is Backplane or Bridge */ #define COREINFO_ISBP_SHIFT 27u /* Bit:27 */ /* Interface Config */ #define IC_IFACECNT_MASK 0x0000F000u /* No of Interface Descriptor Mask */ #define IC_IFACECNT_SHIFT 12u /* Bit-12 */ #define IC_IFACEOFFSET_MASK 0x00000FFFu /* OFFSET for 1st Interface Descriptor */ /* DMP Reg Offset */ #define DMP_DMPCTRL_REG_OFFSET 8u /* Interface Descriptor Masks */ #define ID_NODEPTR_MASK 0xFFFFFFF8u /* Master/Slave Network Interface Addr */ #define ID_NODETYPE_MASK 0x00000007u /* 0:Booker 1:IDM 1-0xf:Reserved */ #define ID_WORDOFFSET_MASK 0xF0000000u /* WordOffset to next Iface Desc in EROM2 */ #define ID_WORDOFFSET_SHIFT 28u /* WordOffset bits 31-28 */ #define ID_CORETYPE_MASK 0x08000000u /* CORE belongs to OOBR(0) or EROM(1) */ #define ID_CORETYPE_SHIFT 27u /* Bit-27 */ #define ID_MI_MASK 0x04000000u /* 0: Slave Interface, 1:Master Interface */ #define ID_MI_SHIFT 26u /* Bit-26 */ #define ID_NADDR_MASK 0x03000000u /* No of Slave Address Regions */ #define ID_NADDR_SHIFT 24u /* Bit:25-24 */ #define ID_BPID_MASK 0x00F00000u /* Give Backplane ID */ #define ID_BPID_SHIFT 20u /* Bit:20-23 */ #define ID_COREINFOPTR_MASK 0x00001FFFu /* OOBR or EROM Offset */ #define ID_ENDMARKER 0xFFFFFFFFu /* End of EROM Part 2 */ /* Slave Port Address Descriptor Masks */ #define SLAVEPORT_BASE_ADDR_MASK 0xFFFFFF00u /* Bits 31:8 is the base address */ #define SLAVEPORT_BOUND_ADDR_MASK 0x00000040u /* Addr is not 2^n and with bound addr */ #define SLAVEPORT_BOUND_ADDR_SHIFT 6u /* Bit-6 */ #define SLAVEPORT_64BIT_ADDR_MASK 0x00000020u /* 64-bit base and bound fields */ #define SLAVEPORT_64BIT_ADDR_SHIFT 5u /* Bit-5 */ #define SLAVEPORT_ADDR_SIZE_MASK 0x0000001Fu /* Address Size mask */ #define SLAVEPORT_ADDR_TYPE_BOUND 0x1u /* Bound Addr */ #define SLAVEPORT_ADDR_TYPE_64 0x2u /* 64-Bit Addr */ #define SLAVEPORT_ADDR_MIN_SHIFT 0x8u /* Address space Size of the slave port */ #define SLAVEPORT_ADDR_SIZE(adesc) (1u << ((adesc & SLAVEPORT_ADDR_SIZE_MASK) + \ SLAVEPORT_ADDR_MIN_SHIFT)) #define GET_NEXT_EROM_ADDR(addr) ((uint32*)((uintptr)(addr) + 4u)) #define NCI_DEFAULT_CORE_UNIT (0u) /* Error Codes */ enum { NCI_OK = 0, NCI_BACKPLANE_ID_MISMATCH = -1, NCI_INVALID_EROM2PTR = -2, NCI_WORDOFFSET_MISMATCH = -3, NCI_NOMEM = -4, NCI_MASTER_INVALID_ADDR = -5 }; #define GET_OOBR_BASE(erom2base) ((erom2base) & ~OOBR_BASE_MASK) #define GET_EROM1_BASE(erom2base) ((erom2base) & ~EROM1_BASE_MASK) #define CORE_ID(core_info) ((core_info) & COREINFO_COREID_MASK) #define GET_INFACECNT(iface_cfg) (((iface_cfg) & IC_IFACECNT_MASK) >> IC_IFACECNT_SHIFT) #define GET_NODEPTR(iface_desc_0) ((iface_desc_0) & ID_NODEPTR_MASK) #define GET_NODETYPE(iface_desc_0) ((iface_desc_0) & ID_NODETYPE_MASK) #define GET_WORDOFFSET(iface_desc_1) (((iface_desc_1) & ID_WORDOFFSET_MASK) \ >> ID_WORDOFFSET_SHIFT) #define IS_MASTER(iface_desc_1) (((iface_desc_1) & ID_MI_MASK) >> ID_MI_SHIFT) #define GET_CORETYPE(iface_desc_1) (((iface_desc_1) & ID_CORETYPE_MASK) >> ID_CORETYPE_SHIFT) #define GET_NUM_ADDR_REG(iface_desc_1) (((iface_desc_1) & ID_NADDR_MASK) >> ID_NADDR_SHIFT) #define GET_COREOFFSET(iface_desc_1) ((iface_desc_1) & ID_COREINFOPTR_MASK) #define ADDR_SIZE(sz) ((1u << ((sz) + 8u)) - 1u) #define CORE_REV(core_info) ((core_info) & COREINFO_REV_MASK) >> COREINFO_REV_SHIFT #define CORE_MFG(core_info) ((core_info) & COREINFO_MFG_MASK) >> COREINFO_MFG_SHIFT #define COREINFO_BPID(core_info) (((core_info) & COREINFO_BPID_MASK) >> COREINFO_BPID_SHIFT) #define IS_BACKPLANE(core_info) (((core_info) & COREINFO_ISBP_MASK) >> COREINFO_ISBP_SHIFT) #define ID_BPID(iface_desc_1) (((iface_desc_1) & ID_BPID_MASK) >> ID_BPID_SHIFT) #define IS_BACKPLANE_ID_SAME(core_info, iface_desc_1) \ (COREINFO_BPID((core_info)) == ID_BPID((iface_desc_1))) #define NCI_WORD_SIZE (4u) #define PCI_ACCESS_SIZE (4u) #define NCI_ADDR2NUM(addr) ((uintptr)(addr)) #define NCI_ADD_NUM(addr, size) (NCI_ADDR2NUM(addr) + (size)) #ifdef DONGLEBUILD #define NCI_ADD_ADDR(addr, size) ((uint32*)REG_MAP(NCI_ADD_NUM((addr), (size)), 0u)) #else /* !DONGLEBUILD */ #define NCI_ADD_ADDR(addr, size) ((uint32*)(NCI_ADD_NUM((addr), (size)))) #endif /* DONGLEBUILD */ #define NCI_INC_ADDR(addr, size) ((addr) = NCI_ADD_ADDR((addr), (size))) #define NODE_TYPE_BOOKER 0x0u #define NODE_TYPE_NIC400 0x1u #define BP_BOOKER 0x0u #define BP_NIC400 0x1u #define BP_APB1 0x2u #define BP_APB2 0x3u #define BP_CCI400 0x4u #define PCIE_WRITE_SIZE 4u static const char BACKPLANE_ID_NAME[][11] = { "BOOKER", "NIC400", "APB1", "APB2", "CCI400", "\0" }; #define APB_INF(ifd) ((ID_BPID((ifd).iface_desc_1) == BP_APB1) || \ (ID_BPID((ifd).iface_desc_1) == BP_APB2)) #define BOOKER_INF(ifd) (ID_BPID((ifd).iface_desc_1) == BP_BOOKER) #define NIC_INF(ifd) (ID_BPID((ifd).iface_desc_1) == BP_NIC400) /* BOOKER NCI LOG LEVEL */ #define NCI_LOG_LEVEL_ERROR 0x1u #define NCI_LOG_LEVEL_TRACE 0x2u #define NCI_LOG_LEVEL_INFO 0x4u #define NCI_LOG_LEVEL_PRINT 0x8u #ifndef NCI_DEFAULT_LOG_LEVEL #define NCI_DEFAULT_LOG_LEVEL (NCI_LOG_LEVEL_ERROR) #endif /* NCI_DEFAULT_LOG_LEVEL */ uint32 nci_log_level = NCI_DEFAULT_LOG_LEVEL; #ifdef DONGLEBUILD #define NCI_ERROR(args) do { if (nci_log_level & NCI_LOG_LEVEL_ERROR) { printf args; } } while (0u) #define NCI_TRACE(args) do { if (nci_log_level & NCI_LOG_LEVEL_TRACE) { printf args; } } while (0u) #define NCI_INFO(args) do { if (nci_log_level & NCI_LOG_LEVEL_INFO) { printf args; } } while (0u) #define NCI_PRINT(args) do { if (nci_log_level & NCI_LOG_LEVEL_PRINT) { printf args; } } while (0u) #else /* !DONGLEBUILD */ #define NCI_KERN_PRINT(...) printk(KERN_ERR __VA_ARGS__) #define NCI_ERROR(args) do { if (nci_log_level & NCI_LOG_LEVEL_ERROR) \ { NCI_KERN_PRINT args; } } while (0u) #define NCI_TRACE(args) do { if (nci_log_level & NCI_LOG_LEVEL_TRACE) \ { NCI_KERN_PRINT args; } } while (0u) #define NCI_INFO(args) do { if (nci_log_level & NCI_LOG_LEVEL_INFO) \ { NCI_KERN_PRINT args; } } while (0u) #define NCI_PRINT(args) do { if (nci_log_level & NCI_LOG_LEVEL_PRINT) \ { NCI_KERN_PRINT args; } } while (0u) #endif /* DONGLEBUILD */ #define NCI_EROM_WORD_SIZEOF 4u #define NCI_REGS_PER_CORE 2u #define NCI_EROM1_LEN(erom2base) (erom2base - GET_EROM1_BASE(erom2base)) #define NCI_NONOOBR_CORES(erom2base) NCI_EROM1_LEN(erom2base) \ /(NCI_REGS_PER_CORE * NCI_EROM_WORD_SIZEOF) /* AXI ID to CoreID + unit mappings */ typedef struct nci_axi_to_coreidx { uint coreid; uint coreunit; } nci_axi_to_coreidx_t; static const nci_axi_to_coreidx_t axi2coreidx_4397[] = { {CC_CORE_ID, 0}, /* 00 Chipcommon */ {PCIE2_CORE_ID, 0}, /* 01 PCIe */ {D11_CORE_ID, 0}, /* 02 D11 Main */ {ARMCR4_CORE_ID, 0}, /* 03 ARM */ {BT_CORE_ID, 0}, /* 04 BT AHB */ {D11_CORE_ID, 1}, /* 05 D11 Aux */ {D11_CORE_ID, 0}, /* 06 D11 Main l1 */ {D11_CORE_ID, 1}, /* 07 D11 Aux l1 */ {D11_CORE_ID, 0}, /* 08 D11 Main l2 */ {D11_CORE_ID, 1}, /* 09 D11 Aux l2 */ {NODEV_CORE_ID, 0}, /* 10 M2M DMA */ {NODEV_CORE_ID, 0}, /* 11 unused */ {NODEV_CORE_ID, 0}, /* 12 unused */ {NODEV_CORE_ID, 0}, /* 13 unused */ {NODEV_CORE_ID, 0}, /* 14 unused */ {NODEV_CORE_ID, 0} /* 15 unused */ }; typedef struct slave_port { uint32 adesc; /**< Address Descriptor 0 */ uint32 addrl; /**< Lower Base */ uint32 addrh; /**< Upper Base */ uint32 extaddrl; /**< Lower Bound */ uint32 extaddrh; /**< Ubber Bound */ } slave_port_t; typedef struct interface_desc { slave_port_t *sp; /**< Slave Port Addr 0-3 */ uint32 iface_desc_0; /**< Interface-0 Descriptor Word0 */ /* If Node Type 0-Booker xMNI/xSNI address. If Node Type 1-DMP wrapper Address */ uint32 node_ptr; /**< Core's Node pointer */ uint32 iface_desc_1; /**< Interface Descriptor Word1 */ uint8 num_addr_reg; /**< Number of Slave Port Addr (Valid only if master=0) */ uint8 coretype; /**< Core Belongs to 0:OOBR 1:Without OOBR */ uint8 master; /**< 1:Master 0:Slave */ uint8 node_type; /**< 0:Booker , 1:IDM Wrapper, 2-0xf: Reserved */ } interface_desc_t; typedef struct nci_cores { void *regs; /* 2:0-Node type (0-booker,1-IDM Wrapper) 31:3-Interconnect registyer space */ interface_desc_t *desc; /**< Interface & Address Descriptors */ /* * 11:0-CoreID, 19:12-RevID 23:20-MFG 26:24-Backplane ID if * bit 27 is 1 (Core is Backplane or Bridge ) */ uint32 coreinfo; /**< CoreInfo of each core */ /* * 11:0 - Offosewt of 1st Interface desc in EROM 15:12 - No. * of interfaces attachedto this core */ uint32 iface_cfg; /**< Interface config Reg */ uint32 dmp_regs_off; /**< DMP control & DMP status @ 0x48 from coreinfo */ uint32 coreid; /**< id of each core */ uint8 coreunit; /**< Unit differentiate same coreids */ uint8 iface_cnt; /**< no of Interface connected to each core */ uint8 PAD[2u]; } nci_cores_t; typedef struct nci_info { void *osh; /**< osl os handle */ nci_cores_t *cores; /**< Cores Parsed */ void *pci_bar_addr; /**< PCI BAR0 Window */ uint32 cc_erom2base; /**< Base of EROM2 from ChipCommon */ uint32 *erom1base; /**< Base of EROM1 */ uint32 *erom2base; /**< Base of EROM2 */ uint32 *oobr_base; /**< Base of OOBR */ uint16 bustype; /**< SI_BUS, PCI_BUS */ uint8 max_cores; /**< # Max cores indicated by Register */ uint8 num_cores; /**< # discovered cores */ uint8 refcnt; /**< Allocation reference count */ uint8 scan_done; /**< Set to TRUE when erom scan is done. */ uint8 PAD[2]; } nci_info_t; #define NI_IDM_RESET_ENTRY 0x1 #define NI_IDM_RESET_EXIT 0x0 /* AXI Slave Network Interface registers */ typedef volatile struct asni_regs { uint32 node_type; /* 0x000 */ uint32 node_info; /* 0x004 */ uint32 secr_acc; /* 0x008 */ uint32 pmusela; /* 0x00c */ uint32 pmuselb; /* 0x010 */ uint32 PAD[11]; uint32 node_feat; /* 0x040 */ uint32 bursplt; /* 0x044 */ uint32 addr_remap; /* 0x048 */ uint32 PAD[13]; uint32 sildbg; /* 0x080 */ uint32 qosctl; /* 0x084 */ uint32 wdatthrs; /* 0x088 */ uint32 arqosovr; /* 0x08c */ uint32 awqosovr; /* 0x090 */ uint32 atqosot; /* 0x094 */ uint32 arqosot; /* 0x098 */ uint32 awqosot; /* 0x09c */ uint32 axqosot; /* 0x0a0 */ uint32 qosrdpk; /* 0x0a4 */ uint32 qosrdbur; /* 0x0a8 */ uint32 qosrdavg; /* 0x0ac */ uint32 qoswrpk; /* 0x0b0 */ uint32 qoswrbur; /* 0x0b4 */ uint32 qoswravg; /* 0x0b8 */ uint32 qoscompk; /* 0x0bc */ uint32 qoscombur; /* 0x0c0 */ uint32 qoscomavg; /* 0x0c4 */ uint32 qosrbbqv; /* 0x0c8 */ uint32 qoswrbqv; /* 0x0cc */ uint32 qoscombqv; /* 0x0d0 */ uint32 PAD[11]; uint32 idm_device_id; /* 0x100 */ uint32 PAD[15]; uint32 idm_reset_ctrl; /* 0x140 */ } asni_regs_t; /* AXI Master Network Interface registers */ typedef volatile struct amni_regs { uint32 node_type; /* 0x000 */ uint32 node_info; /* 0x004 */ uint32 secr_acc; /* 0x008 */ uint32 pmusela; /* 0x00c */ uint32 pmuselb; /* 0x010 */ uint32 PAD[11]; uint32 node_feat; /* 0x040 */ uint32 PAD[15]; uint32 sildbg; /* 0x080 */ uint32 qosacc; /* 0x084 */ uint32 PAD[26]; uint32 interrupt_status; /* 0x0f0 */ uint32 interrupt_mask; /* 0x0f4 */ uint32 interrupt_status_ns; /* 0x0f8 */ uint32 interrupt_mask_ns; /* 0x0FC */ uint32 idm_device_id; /* 0x100 */ uint32 PAD[15]; uint32 idm_reset_ctrl; /* 0x140 */ } amni_regs_t; #define NCI_SPINWAIT_TIMEOUT (300u) /* DMP/io control and DMP/io status */ typedef struct dmp_regs { uint32 dmpctrl; uint32 dmpstatus; } dmp_regs_t; #ifdef _RTE_ static nci_info_t *knci_info = NULL; #endif /* _RTE_ */ static void nci_save_iface1_reg(interface_desc_t *desc, uint32 iface_desc_1); static uint32* nci_save_slaveport_addr(nci_info_t *nci, interface_desc_t *desc, uint32 *erom2ptr); static int nci_get_coreunit(nci_cores_t *cores, uint32 numcores, uint cid, uint32 iface_desc_1); static nci_cores_t* nci_initial_parse(nci_info_t *nci, uint32 *erom2ptr, uint32 *core_idx); static void _nci_setcoreidx_pcie_bus(si_t *sih, volatile void **regs, uint32 curmap, uint32 curwrap); static volatile void *_nci_setcoreidx(si_t *sih, uint coreidx); static uint32 _nci_get_curwrap(nci_info_t *nci, uint coreidx, uint wrapper_idx); static uint32 nci_get_curwrap(nci_info_t *nci, uint coreidx); static uint32 _nci_get_curmap(nci_info_t *nci, uint coreidx, uint slave_port_idx, uint base_idx); static uint32 nci_get_curmap(nci_info_t *nci, uint coreidx); static void _nci_core_reset(const si_t *sih, uint32 bits, uint32 resetbits); #if defined (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) static void nci_reset_APB(const si_info_t *sii, aidmp_t *ai, int *ret, uint32 errlog_status, uint32 errlog_id); static void nci_reset_axi_to(const si_info_t *sii, aidmp_t *ai); #endif /* (AXI_TIMEOUTS) || (AXI_TIMEOUTS_NIC) */ static uint32 nci_find_numcores(si_t *sih); #ifdef BOOKER_NIC400_INF static int32 nci_find_first_wrapper_idx(nci_info_t *nci, uint32 coreidx); #endif /* BOOKER_NIC400_INF */ /* * Description : This function will search for a CORE with matching 'core_id' and mismatching * 'wordoffset', if found then increments 'coreunit' by 1. */ /* TODO: Need to understand this. */ static int BCMATTACHFN(nci_get_coreunit)(nci_cores_t *cores, uint32 numcores, uint core_id, uint32 iface_desc_1) { uint32 core_idx; uint32 coreunit = NCI_DEFAULT_CORE_UNIT; for (core_idx = 0u; core_idx < numcores; core_idx++) { if ((cores[core_idx].coreid == core_id) && (GET_COREOFFSET(cores[core_idx].desc->iface_desc_1) != GET_COREOFFSET(iface_desc_1))) { coreunit = cores[core_idx].coreunit + 1; } } return coreunit; } /* * OOBR Region +-------------------------------+ + + + OOBR with EROM Data + + + +-------------------------------+ + + + EROM1 + + + +-------------------------------+ --> ChipCommon.EROMBASE + + + EROM2 + + + +-------------------------------+ */ /** * Function : nci_init * Description : Malloc's memory related to 'nci_info_t' and its internal elements. * * @paramter[in] * @regs : This is a ChipCommon Regster * @bustype : Bus Connect Type * * Return : On Succes 'nci_info_t' data structure is returned as void, * where all EROM parsed Cores are saved, * using this all EROM Cores are Freed. * On Failure 'NULL' is returned by printing ERROR messages */ void* BCMATTACHFN(nci_init)(si_t *sih, chipcregs_t *cc, uint bustype) { si_info_t *sii = SI_INFO(sih); nci_cores_t *cores; nci_info_t *nci = NULL; uint8 err_at = 0u; #ifdef _RTE_ if (knci_info) { knci_info->refcnt++; nci = knci_info; goto end; } #endif /* _RTE_ */ /* It is used only when NCI_ERROR is used */ BCM_REFERENCE(err_at); if ((nci = MALLOCZ(sii->osh, sizeof(*nci))) == NULL) { err_at = 1u; goto end; } sii->nci_info = nci; nci->osh = sii->osh; nci->refcnt++; nci->cc_erom2base = R_REG(nci->osh, &cc->eromptr); nci->bustype = bustype; switch (nci->bustype) { case SI_BUS: nci->erom2base = (uint32*)REG_MAP(nci->cc_erom2base, 0u); nci->oobr_base = (uint32*)REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), 0u); nci->erom1base = (uint32*)REG_MAP(GET_EROM1_BASE(nci->cc_erom2base), 0u); break; case PCI_BUS: /* Set wrappers address */ sii->curwrap = (void *)((uintptr)cc + SI_CORE_SIZE); /* Set access window to Erom Base(For NCI, EROM starts with OOBR) */ OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_EROM1_BASE(nci->cc_erom2base)); nci->erom1base = (uint32*)((uintptr)cc); nci->erom2base = (uint32*)((uintptr)cc + NCI_EROM1_LEN(nci->cc_erom2base)); break; default: err_at = 2u; ASSERT(0u); goto end; } nci->max_cores = nci_find_numcores(sih); if (!nci->max_cores) { err_at = 3u; goto end; } if ((cores = MALLOCZ(nci->osh, sizeof(*cores) * nci->max_cores)) == NULL) { err_at = 4u; goto end; } nci->cores = cores; #ifdef _RTE_ knci_info = nci; #endif /* _RTE_ */ end: if (err_at) { NCI_ERROR(("nci_init: Failed err_at=%#x\n", err_at)); nci_uninit(nci); nci = NULL; } return nci; } /** * Function : nci_uninit * Description : Free's memory related to 'nci_info_t' and its internal malloc'd elements. * * @paramter[in] * @nci : This is 'nci_info_t' data structure, where all EROM parsed Cores are saved, using this * all EROM Cores are Freed. * * Return : void */ void BCMATTACHFN(nci_uninit)(void *ctx) { nci_info_t *nci = (nci_info_t *)ctx; uint8 core_idx, desc_idx; interface_desc_t *desc; nci_cores_t *cores; slave_port_t *sp; if (nci == NULL) { return; } nci->refcnt--; #ifdef _RTE_ if (nci->refcnt != 0) { return; } #endif /* _RTE_ */ cores = nci->cores; if (cores == NULL) { goto end; } for (core_idx = 0u; core_idx < nci->num_cores; core_idx++) { desc = cores[core_idx].desc; if (desc == NULL) { break; } for (desc_idx = 0u; desc_idx < cores[core_idx].iface_cnt; desc_idx++) { sp = desc[desc_idx].sp; if (sp) { MFREE(nci->osh, sp, (sizeof(*sp) * desc[desc_idx].num_addr_reg)); } } MFREE(nci->osh, desc, (sizeof(*desc) * cores[core_idx].iface_cnt)); } MFREE(nci->osh, cores, sizeof(*cores) * nci->max_cores); end: #ifdef _RTE_ knci_info = NULL; #endif /* _RTE_ */ MFREE(nci->osh, nci, sizeof(*nci)); } /** * Function : nci_save_iface1_reg * Description : Interface1 Descriptor is obtained from the Reg and saved in * Internal data structures 'nci->cores'. * * @paramter[in] * @desc : Descriptor of Core which needs to be updated with obatained Interface1 Descritpor. * @iface_desc_1 : Obatained Interface1 Descritpor. * * Return : void */ static void BCMATTACHFN(nci_save_iface1_reg)(interface_desc_t *desc, uint32 iface_desc_1) { BCM_REFERENCE(BACKPLANE_ID_NAME); desc->coretype = GET_CORETYPE(iface_desc_1); desc->master = IS_MASTER(iface_desc_1); desc->iface_desc_1 = iface_desc_1; desc->num_addr_reg = GET_NUM_ADDR_REG(iface_desc_1); if (desc->master) { if (desc->num_addr_reg) { NCI_ERROR(("nci_save_iface1_reg: Master NODEPTR Addresses is not zero " "i.e. %d\n", GET_NUM_ADDR_REG(iface_desc_1))); ASSERT(0u); } } else { /* SLAVE 'NumAddressRegion' one less than actual slave ports, so increment by 1 */ desc->num_addr_reg++; } NCI_INFO(("\tnci_save_iface1_reg: %s InterfaceDesc:%#x WordOffset=%#x " "NoAddrReg=%#x %s_Offset=%#x BackplaneID=%s\n", desc->master?"Master":"Slave", desc->iface_desc_1, GET_WORDOFFSET(desc->iface_desc_1), desc->num_addr_reg, desc->coretype?"EROM1":"OOBR", GET_COREOFFSET(desc->iface_desc_1), BACKPLANE_ID_NAME[ID_BPID(desc->iface_desc_1)])); } /** * Function : nci_save_slaveport_addr * Description : All Slave Port Addr of Interface Descriptor are saved. * * @paramter[in] * @nci : This is 'nci_info_t' data structure, where all EROM parsed Cores are saved * @desc : Current Interface Descriptor. * @erom2ptr : Pointer to Address Descriptor0. * * Return : On Success, this function returns Erom2 Ptr to Next Interface Descriptor, * On Failure, NULL is returned. */ static uint32* BCMATTACHFN(nci_save_slaveport_addr)(nci_info_t *nci, interface_desc_t *desc, uint32 *erom2ptr) { slave_port_t *sp; uint32 adesc; uint32 sz; uint32 addr_idx; /* Allocate 'NumAddressRegion' of Slave Port */ if ((desc->sp = (slave_port_t *)MALLOCZ( nci->osh, (sizeof(*sp) * desc->num_addr_reg))) == NULL) { NCI_ERROR(("\tnci_save_slaveport_addr: Memory Allocation failed for Slave Port\n")); return NULL; } sp = desc->sp; /* Slave Port Addrs Desc */ for (addr_idx = 0u; addr_idx < desc->num_addr_reg; addr_idx++) { adesc = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); sp[addr_idx].adesc = adesc; sp[addr_idx].addrl = adesc & SLAVEPORT_BASE_ADDR_MASK; if (adesc & SLAVEPORT_64BIT_ADDR_MASK) { sp[addr_idx].addrh = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); sp[addr_idx].extaddrl = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); sp[addr_idx].extaddrh = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); NCI_INFO(("\tnci_save_slaveport_addr: SlavePortAddr[%#x]:0x%08x al=0x%08x " "ah=0x%08x extal=0x%08x extah=0x%08x\n", addr_idx, adesc, sp[addr_idx].addrl, sp[addr_idx].addrh, sp[addr_idx].extaddrl, sp[addr_idx].extaddrh)); } else if (adesc & SLAVEPORT_BOUND_ADDR_MASK) { sp[addr_idx].addrh = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); NCI_INFO(("\tnci_save_slaveport_addr: SlavePortAddr[%#x]:0x%08x al=0x%08x " "ah=0x%08x\n", addr_idx, adesc, sp[addr_idx].addrl, sp[addr_idx].addrh)); } else { sz = adesc & SLAVEPORT_ADDR_SIZE_MASK; sp[addr_idx].addrh = sp[addr_idx].addrl + ADDR_SIZE(sz); NCI_INFO(("\tnci_save_slaveport_addr: SlavePortAddr[%#x]:0x%08x al=0x%08x " "ah=0x%08x sz=0x%08x\n", addr_idx, adesc, sp[addr_idx].addrl, sp[addr_idx].addrh, sz)); } } return erom2ptr; } /** * Function : nci_initial_parse * Description : This function does * 1. Obtains OOBR/EROM1 pointer based on CoreType * 2. Analysis right CoreUnit for this 'core' * 3. Saves CoreInfo & Interface Config in Coresponding 'core' * * @paramter[in] * @nci : This is 'nci_info_t' data structure, where all EROM parsed Cores are saved. * @erom2ptr : Pointer to Interface Descriptor0. * @core_idx : New core index needs to be populated in this pointer. * * Return : On Success, this function returns 'core' where CoreInfo & Interface Config are saved. */ static nci_cores_t* BCMATTACHFN(nci_initial_parse)(nci_info_t *nci, uint32 *erom2ptr, uint32 *core_idx) { uint32 iface_desc_1; nci_cores_t *core; uint32 dmp_regs_off = 0u; uint32 iface_cfg = 0u; uint32 core_info; uint32 *ptr; uint coreid; iface_desc_1 = R_REG(nci->osh, erom2ptr); /* Get EROM1/OOBR Pointer based on CoreType */ if (!GET_CORETYPE(iface_desc_1)) { if (nci->bustype == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); nci->oobr_base = (uint32*)((uintptr)nci->erom1base); } ptr = NCI_ADD_ADDR(nci->oobr_base, GET_COREOFFSET(iface_desc_1)); } else { ptr = NCI_ADD_ADDR(nci->erom1base, GET_COREOFFSET(iface_desc_1)); } dmp_regs_off = GET_COREOFFSET(iface_desc_1) + DMP_DMPCTRL_REG_OFFSET; core_info = R_REG(nci->osh, ptr); NCI_INC_ADDR(ptr, NCI_WORD_SIZE); iface_cfg = R_REG(nci->osh, ptr); *core_idx = nci->num_cores; core = &nci->cores[*core_idx]; if (CORE_ID(core_info) < 0xFFu) { coreid = CORE_ID(core_info) | 0x800u; } else { coreid = CORE_ID(core_info); } /* Get coreunit from previous cores i.e. num_cores */ core->coreunit = nci_get_coreunit(nci->cores, nci->num_cores, coreid, iface_desc_1); core->coreid = coreid; /* Increment the num_cores once proper coreunit is known */ nci->num_cores++; NCI_INFO(("\n\nnci_initial_parse: core_idx:%d %s=%p \n", *core_idx, GET_CORETYPE(iface_desc_1)?"EROM1":"OOBR", ptr)); /* Core Info Register */ core->coreinfo = core_info; /* Save DMP register base address. */ core->dmp_regs_off = dmp_regs_off; NCI_INFO(("\tnci_initial_parse: COREINFO:%#x CId:%#x CUnit=%#x CRev=%#x CMfg=%#x\n", core->coreinfo, core->coreid, core->coreunit, CORE_REV(core->coreinfo), CORE_MFG(core->coreinfo))); /* Interface Config Register */ core->iface_cfg = iface_cfg; core->iface_cnt = GET_INFACECNT(iface_cfg); NCI_INFO(("\tnci_initial_parse: INTERFACE_CFG:%#x IfaceCnt=%#x IfaceOffset=%#x \n", iface_cfg, core->iface_cnt, iface_cfg & IC_IFACEOFFSET_MASK)); /* For PCI_BUS case set back BAR0 Window to EROM1 Base */ if (nci->bustype == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_EROM1_BASE(nci->cc_erom2base)); } return core; } static uint32 BCMATTACHFN(nci_find_numcores)(si_t *sih) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; volatile hndoobr_reg_t *oobr_reg = NULL; uint32 orig_bar0_win1 = 0u; uint32 num_oobr_cores = 0u; uint32 num_nonoobr_cores = 0u; /* No of Non-OOBR Cores */ num_nonoobr_cores = NCI_NONOOBR_CORES(nci->cc_erom2base); if (num_nonoobr_cores <= 0u) { NCI_ERROR(("nci_find_numcores: Invalid Number of non-OOBR cores %d\n", num_nonoobr_cores)); goto fail; } /* No of OOBR Cores */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: oobr_reg = (volatile hndoobr_reg_t*)REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* Save Original Bar0 Win1 */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); oobr_reg = (volatile hndoobr_reg_t*)sii->curmap; break; default: NCI_ERROR(("nci_find_numcores: Invalid bustype %d\n", BUSTYPE(sih->bustype))); ASSERT(0); goto fail; } num_oobr_cores = R_REG(nci->osh, &oobr_reg->capability) & OOBR_CAP_CORECNT_MASK; if (num_oobr_cores <= 0u) { NCI_ERROR(("nci_find_numcores: Invalid Number of OOBR cores %d\n", num_oobr_cores)); goto fail; } /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } NCI_PRINT(("nci_find_numcores: Total Cores found %d\n", (num_oobr_cores + num_nonoobr_cores))); /* Total No of Cores */ return (num_oobr_cores + num_nonoobr_cores); fail: return 0u; } /** * Function : nci_scan * Description : Function parses EROM in BOOKER NCI Architecture and saves all inforamtion about * Cores in 'nci_info_t' data structure. * * @paramter[in] * @nci : This is 'nci_info_t' data structure, where all EROM parsed Cores are saved. * * Return : On Success No of parsed Cores in EROM is returned, * On Failure '0' is returned by printing ERROR messages * in Console(If NCI_LOG_LEVEL is enabled). */ uint32 BCMATTACHFN(nci_scan)(si_t *sih) { si_info_t *sii = SI_INFO(sih); nci_info_t *nci = (nci_info_t *)sii->nci_info; axi_wrapper_t * axi_wrapper = sii->axi_wrapper; uint32 *cur_iface_desc_1_ptr; nci_cores_t *core; interface_desc_t *desc; uint32 wordoffset = 0u; uint32 iface_desc_0; uint32 iface_desc_1; uint32 *erom2ptr; uint8 iface_idx; uint32 core_idx; int err = 0; /* If scan was finished already */ if (nci->scan_done) { goto end; } erom2ptr = nci->erom2base; sii->axi_num_wrappers = 0; while (TRUE) { iface_desc_0 = R_REG(nci->osh, erom2ptr); if (iface_desc_0 == ID_ENDMARKER) { NCI_INFO(("\nnci_scan: Reached end of EROM2 with total cores=%d \n", nci->num_cores)); break; } /* Save current Iface1 Addr for comparision */ cur_iface_desc_1_ptr = GET_NEXT_EROM_ADDR(erom2ptr); /* Get CoreInfo, InterfaceCfg, CoreIdx */ core = nci_initial_parse(nci, cur_iface_desc_1_ptr, &core_idx); core->desc = (interface_desc_t *)MALLOCZ( nci->osh, (sizeof(*(core->desc)) * core->iface_cnt)); if (core->desc == NULL) { NCI_ERROR(("nci_scan: Mem Alloc failed for Iface and Addr " "Descriptor\n")); err = NCI_NOMEM; break; } for (iface_idx = 0u; iface_idx < core->iface_cnt; iface_idx++) { desc = &core->desc[iface_idx]; iface_desc_0 = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); iface_desc_1 = R_REG(nci->osh, erom2ptr); NCI_INC_ADDR(erom2ptr, NCI_WORD_SIZE); /* Interface Descriptor Register */ nci_save_iface1_reg(desc, iface_desc_1); if (desc->master && desc->num_addr_reg) { err = NCI_MASTER_INVALID_ADDR; goto end; } wordoffset = GET_WORDOFFSET(iface_desc_1); /* NodePointer Register */ desc->iface_desc_0 = iface_desc_0; desc->node_ptr = GET_NODEPTR(iface_desc_0); desc->node_type = GET_NODETYPE(iface_desc_0); if (axi_wrapper && (sii->axi_num_wrappers < SI_MAX_AXI_WRAPPERS)) { axi_wrapper[sii->axi_num_wrappers].mfg = CORE_MFG(core->coreinfo); axi_wrapper[sii->axi_num_wrappers].cid = CORE_ID(core->coreinfo); axi_wrapper[sii->axi_num_wrappers].rev = CORE_REV(core->coreinfo); axi_wrapper[sii->axi_num_wrappers].wrapper_type = desc->master; axi_wrapper[sii->axi_num_wrappers].wrapper_addr = desc->node_ptr; sii->axi_num_wrappers++; } NCI_INFO(("nci_scan: %s NodePointer:%#x Type=%s NODEPTR=%#x \n", desc->master?"Master":"Slave", desc->iface_desc_0, desc->node_type?"NIC-400":"BOOKER", desc->node_ptr)); /* Slave Port Addresses */ if (!desc->master) { erom2ptr = nci_save_slaveport_addr(nci, desc, erom2ptr); if (erom2ptr == NULL) { NCI_ERROR(("nci_scan: Invalid EROM2PTR\n")); err = NCI_INVALID_EROM2PTR; goto end; } } /* Current loop ends with next iface_desc_0 */ } if (wordoffset == 0u) { NCI_INFO(("\nnci_scan: EROM PARSING found END 'wordoffset=%#x' " "with total cores=%d \n", wordoffset, nci->num_cores)); break; } } nci->scan_done = TRUE; end: if (err) { NCI_ERROR(("nci_scan: Failed with Code %d\n", err)); nci->num_cores = 0; ASSERT(0u); } return nci->num_cores; } /** * Function : nci_dump_erom * Description : Function dumps EROM from inforamtion cores in 'nci_info_t' data structure. * * @paramter[in] * @nci : This is 'nci_info_t' data structure, where all EROM parsed Cores are saved. * * Return : void */ void BCMATTACHFN(nci_dump_erom)(void *ctx) { nci_info_t *nci = (nci_info_t *)ctx; nci_cores_t *core; interface_desc_t *desc; slave_port_t *sp; uint32 core_idx, addr_idx, iface_idx; uint32 core_info; BCM_REFERENCE(core_info); NCI_INFO(("\nnci_dump_erom: -- EROM Dump --\n")); for (core_idx = 0u; core_idx < nci->num_cores; core_idx++) { core = &nci->cores[core_idx]; /* Core Info Register */ core_info = core->coreinfo; NCI_INFO(("\nnci_dump_erom: core_idx=%d COREINFO:%#x CId:%#x CUnit:%#x CRev=%#x " "CMfg=%#x\n", core_idx, core_info, CORE_ID(core_info), core->coreunit, CORE_REV(core_info), CORE_MFG(core_info))); /* Interface Config Register */ NCI_INFO(("nci_dump_erom: IfaceCfg=%#x IfaceCnt=%#x \n", core->iface_cfg, core->iface_cnt)); for (iface_idx = 0u; iface_idx < core->iface_cnt; iface_idx++) { desc = &core->desc[iface_idx]; /* NodePointer Register */ NCI_INFO(("nci_dump_erom: %s iface_desc_0 Master=%#x MASTER_WRAP=%#x " "Type=%s \n", desc->master?"Master":"Slave", desc->iface_desc_0, desc->node_ptr, (desc->node_type)?"NIC-400":"BOOKER")); /* Interface Descriptor Register */ NCI_INFO(("nci_dump_erom: %s InterfaceDesc:%#x WOffset=%#x NoAddrReg=%#x " "%s_Offset=%#x\n", desc->master?"Master":"Slave", desc->iface_desc_1, GET_WORDOFFSET(desc->iface_desc_1), desc->num_addr_reg, desc->coretype?"EROM1":"OOBR", GET_COREOFFSET(desc->iface_desc_1))); /* Slave Port Addresses */ sp = desc->sp; if (!sp) { continue; } for (addr_idx = 0u; addr_idx < desc->num_addr_reg; addr_idx++) { if (sp[addr_idx].extaddrl) { NCI_INFO(("nci_dump_erom: SlavePortAddr[%#x]: AddrDesc=%#x" " al=%#x ah=%#x extal=%#x extah=%#x\n", addr_idx, sp[addr_idx].adesc, sp[addr_idx].addrl, sp[addr_idx].addrh, sp[addr_idx].extaddrl, sp[addr_idx].extaddrh)); } else { NCI_INFO(("nci_dump_erom: SlavePortAddr[%#x]: AddrDesc=%#x" " al=%#x ah=%#x\n", addr_idx, sp[addr_idx].adesc, sp[addr_idx].addrl, sp[addr_idx].addrh)); } } } } return; } /* * Switch to 'coreidx', issue a single arbitrary 32bit register mask & set operation, * switch back to the original core, and return the new value. */ uint BCMPOSTTRAPFN(nci_corereg)(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) { uint origidx = 0; volatile uint32 *r = NULL; uint w; bcm_int_bitmask_t intr_val; bool fast = FALSE; si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores_info = &nci->cores[coreidx]; NCI_TRACE(("nci_corereg coreidx %u regoff %u mask %u val %u\n", coreidx, regoff, mask, val)); ASSERT(GOODIDX(coreidx, nci->num_cores)); ASSERT(regoff < SI_CORE_SIZE); ASSERT((val & ~mask) == 0); if (coreidx >= SI_MAXCORES) { return 0; } if (BUSTYPE(sih->bustype) == SI_BUS) { /* If internal bus, we can always get at everything */ uint32 curmap = nci_get_curmap(nci, coreidx); BCM_REFERENCE(curmap); fast = TRUE; /* map if does not exist */ if (!cores_info->regs) { cores_info->regs = REG_MAP(curmap, SI_CORE_SIZE); ASSERT(GOODREGS(cores_info->regs)); } r = (volatile uint32 *)((volatile uchar *)cores_info->regs + regoff); } else if (BUSTYPE(sih->bustype) == PCI_BUS) { /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ if ((cores_info->coreid == CC_CORE_ID) && SI_FAST(sii)) { /* Chipc registers are mapped at 12KB */ fast = TRUE; r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); } else if (sii->pub.buscoreidx == coreidx) { /* pci registers are at either in the last 2KB of an 8KB window * or, in pcie and pci rev 13 at 8KB */ fast = TRUE; if (SI_FAST(sii)) { r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_PCIREGS_OFFSET + regoff); } else { r = (volatile uint32 *)((volatile char *)sii->curmap + ((regoff >= SBCONFIGOFF) ? PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + regoff); } } } if (!fast) { INTR_OFF(sii, &intr_val); /* save current core index */ origidx = si_coreidx(&sii->pub); /* switch core */ r = (volatile uint32*)((volatile uchar*)nci_setcoreidx(&sii->pub, coreidx) + regoff); } ASSERT(r != NULL); /* mask and set */ if (mask || val) { w = (R_REG(sii->osh, r) & ~mask) | val; W_REG(sii->osh, r, w); } /* readback */ w = R_REG(sii->osh, r); if (!fast) { /* restore core index */ if (origidx != coreidx) { nci_setcoreidx(&sii->pub, origidx); } INTR_RESTORE(sii, &intr_val); } return (w); } uint nci_corereg_writeonly(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) { uint origidx = 0; volatile uint32 *r = NULL; uint w = 0; bcm_int_bitmask_t intr_val; bool fast = FALSE; si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores_info = &nci->cores[coreidx]; NCI_TRACE(("nci_corereg_writeonly() coreidx %u regoff %u mask %u val %u\n", coreidx, regoff, mask, val)); ASSERT(GOODIDX(coreidx, nci->num_cores)); ASSERT(regoff < SI_CORE_SIZE); ASSERT((val & ~mask) == 0); if (coreidx >= SI_MAXCORES) { return 0; } if (BUSTYPE(sih->bustype) == SI_BUS) { /* If internal bus, we can always get at everything */ uint32 curmap = nci_get_curmap(nci, coreidx); BCM_REFERENCE(curmap); fast = TRUE; /* map if does not exist */ if (!cores_info->regs) { cores_info->regs = REG_MAP(curmap, SI_CORE_SIZE); ASSERT(GOODREGS(cores_info->regs)); } r = (volatile uint32 *)((volatile uchar *)cores_info->regs + regoff); } else if (BUSTYPE(sih->bustype) == PCI_BUS) { /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ if ((cores_info->coreid == CC_CORE_ID) && SI_FAST(sii)) { /* Chipc registers are mapped at 12KB */ fast = TRUE; r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); } else if (sii->pub.buscoreidx == coreidx) { /* pci registers are at either in the last 2KB of an 8KB window * or, in pcie and pci rev 13 at 8KB */ fast = TRUE; if (SI_FAST(sii)) { r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_PCIREGS_OFFSET + regoff); } else { r = (volatile uint32 *)((volatile char *)sii->curmap + ((regoff >= SBCONFIGOFF) ? PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + regoff); } } } if (!fast) { INTR_OFF(sii, &intr_val); /* save current core index */ origidx = si_coreidx(&sii->pub); /* switch core */ r = (volatile uint32*) ((volatile uchar*) nci_setcoreidx(&sii->pub, coreidx) + regoff); } ASSERT(r != NULL); /* mask and set */ if (mask || val) { w = (R_REG(sii->osh, r) & ~mask) | val; W_REG(sii->osh, r, w); } if (!fast) { /* restore core index */ if (origidx != coreidx) { nci_setcoreidx(&sii->pub, origidx); } INTR_RESTORE(sii, &intr_val); } return (w); } /* * If there is no need for fiddling with interrupts or core switches (typically silicon * back plane registers, pci registers and chipcommon registers), this function * returns the register offset on this core to a mapped address. This address can * be used for W_REG/R_REG directly. * * For accessing registers that would need a core switch, this function will return * NULL. */ volatile uint32 * nci_corereg_addr(si_t *sih, uint coreidx, uint regoff) { volatile uint32 *r = NULL; bool fast = FALSE; si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores_info = &nci->cores[coreidx]; NCI_TRACE(("nci_corereg_addr() coreidx %u regoff %u\n", coreidx, regoff)); ASSERT(GOODIDX(coreidx, nci->num_cores)); ASSERT(regoff < SI_CORE_SIZE); if (coreidx >= SI_MAXCORES) { return 0; } if (BUSTYPE(sih->bustype) == SI_BUS) { uint32 curmap = nci_get_curmap(nci, coreidx); BCM_REFERENCE(curmap); /* If internal bus, we can always get at everything */ fast = TRUE; /* map if does not exist */ if (!cores_info->regs) { cores_info->regs = REG_MAP(curmap, SI_CORE_SIZE); ASSERT(GOODREGS(cores_info->regs)); } r = (volatile uint32 *)((volatile uchar *)cores_info->regs + regoff); } else if (BUSTYPE(sih->bustype) == PCI_BUS) { /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ if ((cores_info->coreid == CC_CORE_ID) && SI_FAST(sii)) { /* Chipc registers are mapped at 12KB */ fast = TRUE; r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); } else if (sii->pub.buscoreidx == coreidx) { /* pci registers are at either in the last 2KB of an 8KB window * or, in pcie and pci rev 13 at 8KB */ fast = TRUE; if (SI_FAST(sii)) { r = (volatile uint32 *)((volatile char *)sii->curmap + PCI_16KB0_PCIREGS_OFFSET + regoff); } else { r = (volatile uint32 *)((volatile char *)sii->curmap + ((regoff >= SBCONFIGOFF) ? PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + regoff); } } } if (!fast) { ASSERT(sii->curidx == coreidx); r = (volatile uint32*) ((volatile uchar*)sii->curmap + regoff); } return (r); } uint BCMPOSTTRAPFN(nci_findcoreidx)(const si_t *sih, uint coreid, uint coreunit) { si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; uint core_idx; NCI_TRACE(("nci_findcoreidx() coreid %u coreunit %u\n", coreid, coreunit)); for (core_idx = 0; core_idx < nci->num_cores; core_idx++) { if ((nci->cores[core_idx].coreid == coreid) && (nci->cores[core_idx].coreunit == coreunit)) { return core_idx; } } return BADIDX; } static uint32 _nci_get_slave_addr_size(nci_info_t *nci, uint coreidx, uint32 slave_port_idx, uint base_idx) { uint32 size; uint32 add_desc; NCI_TRACE(("_nci_get_slave_addr_size() coreidx %u slave_port_idx %u base_idx %u\n", coreidx, slave_port_idx, base_idx)); add_desc = nci->cores[coreidx].desc[slave_port_idx].sp[base_idx].adesc; size = add_desc & SLAVEPORT_ADDR_SIZE_MASK; return ADDR_SIZE(size); } static uint32 BCMPOSTTRAPFN(_nci_get_curmap)(nci_info_t *nci, uint coreidx, uint slave_port_idx, uint base_idx) { /* TODO: Is handling of 64 bit addressing required */ NCI_TRACE(("_nci_get_curmap coreidx %u slave_port_idx %u base_idx %u\n", coreidx, slave_port_idx, base_idx)); return nci->cores[coreidx].desc[slave_port_idx].sp[base_idx].addrl; } /* Get the interface descriptor which is connected to APB and return its address */ static uint32 BCMPOSTTRAPFN(nci_get_curmap)(nci_info_t *nci, uint coreidx) { nci_cores_t *core_info = &nci->cores[coreidx]; uint32 iface_idx; NCI_TRACE(("nci_get_curmap coreidx %u\n", coreidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curmap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); /* If core is a Backplane or Bridge, then its slave port * will give the pointer to access registers. */ if (!IS_MASTER(core_info->desc[iface_idx].iface_desc_1) && (IS_BACKPLANE(core_info->coreinfo) || APB_INF(core_info->desc[iface_idx]))) { return _nci_get_curmap(nci, coreidx, iface_idx, 0); } } /* no valid slave port address is found */ return NCI_BAD_REG; } static uint32 BCMPOSTTRAPFN(_nci_get_curwrap)(nci_info_t *nci, uint coreidx, uint wrapper_idx) { return nci->cores[coreidx].desc[wrapper_idx].node_ptr; } static uint32 BCMPOSTTRAPFN(nci_get_curwrap)(nci_info_t *nci, uint coreidx) { nci_cores_t *core_info = &nci->cores[coreidx]; uint32 iface_idx; NCI_TRACE(("nci_get_curwrap coreidx %u\n", coreidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curwrap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); if ((ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_BOOKER) || (ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_NIC400)) { return _nci_get_curwrap(nci, coreidx, iface_idx); } } /* no valid master wrapper found */ return NCI_BAD_REG; } static void _nci_setcoreidx_pcie_bus(si_t *sih, volatile void **regs, uint32 curmap, uint32 curwrap) { si_info_t *sii = SI_INFO(sih); *regs = sii->curmap; switch (sii->slice) { case 0: /* main/first slice */ /* point bar0 window */ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, PCIE_WRITE_SIZE, curmap); // TODO: why curwrap is zero i.e no master wrapper if (curwrap != 0) { if (PCIE_GEN2(sii)) { OSL_PCI_WRITE_CONFIG(sii->osh, PCIE2_BAR0_WIN2, PCIE_WRITE_SIZE, curwrap); } else { OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN2, PCIE_WRITE_SIZE, curwrap); } } break; case 1: /* aux/second slice */ /* PCIE GEN2 only for other slices */ if (!PCIE_GEN2(sii)) { /* other slices not supported */ NCI_ERROR(("pci gen not supported for slice 1\n")); ASSERT(0); break; } /* 0x4000 - 0x4fff: enum space 0x5000 - 0x5fff: wrapper space */ *regs = (volatile uint8 *)*regs + PCI_SEC_BAR0_WIN_OFFSET; sii->curwrap = (void *)((uintptr)*regs + SI_CORE_SIZE); /* point bar0 window */ OSL_PCI_WRITE_CONFIG(sii->osh, PCIE2_BAR0_CORE2_WIN, PCIE_WRITE_SIZE, curmap); OSL_PCI_WRITE_CONFIG(sii->osh, PCIE2_BAR0_CORE2_WIN2, PCIE_WRITE_SIZE, curwrap); break; case 2: /* scan/third slice */ /* PCIE GEN2 only for other slices */ if (!PCIE_GEN2(sii)) { /* other slices not supported */ NCI_ERROR(("pci gen not supported for slice 1\n")); ASSERT(0); break; } /* 0x9000 - 0x9fff: enum space 0xa000 - 0xafff: wrapper space */ *regs = (volatile uint8 *)*regs + PCI_SEC_BAR0_WIN_OFFSET; sii->curwrap = (void *)((uintptr)*regs + SI_CORE_SIZE); /* point bar0 window */ nci_corereg(sih, sih->buscoreidx, PCIE_TER_BAR0_WIN, ~0, curmap); nci_corereg(sih, sih->buscoreidx, PCIE_TER_BAR0_WRAPPER, ~0, curwrap); break; default: ASSERT(0); break; } } static volatile void * BCMPOSTTRAPFN(_nci_setcoreidx)(si_t *sih, uint coreidx) { si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores_info = &nci->cores[coreidx]; uint32 curmap, curwrap; volatile void *regs = NULL; NCI_TRACE(("_nci_setcoreidx coreidx %u\n", coreidx)); if (!GOODIDX(coreidx, nci->num_cores)) { return (NULL); } /* * If the user has provided an interrupt mask enabled function, * then assert interrupts are disabled before switching the core. */ ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); curmap = nci_get_curmap(nci, coreidx); curwrap = nci_get_curwrap(nci, coreidx); switch (BUSTYPE(sih->bustype)) { case SI_BUS: /* map if does not exist */ if (!cores_info->regs) { cores_info->regs = REG_MAP(curmap, SI_CORE_SIZE); ASSERT(GOODREGS(cores_info->regs)); } sii->curmap = regs = cores_info->regs; sii->curwrap = REG_MAP(curwrap, SI_CORE_SIZE); break; case PCI_BUS: _nci_setcoreidx_pcie_bus(sih, ®s, curmap, curwrap); break; default: NCI_ERROR(("_nci_stcoreidx Invalid bustype %d\n", BUSTYPE(sih->bustype))); break; } sii->curidx = coreidx; return regs; } volatile void * BCMPOSTTRAPFN(nci_setcoreidx)(si_t *sih, uint coreidx) { return _nci_setcoreidx(sih, coreidx); } volatile void * BCMPOSTTRAPFN(nci_setcore)(si_t *sih, uint coreid, uint coreunit) { si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; uint core_idx; NCI_TRACE(("nci_setcore coreidx %u coreunit %u\n", coreid, coreunit)); core_idx = nci_findcoreidx(sih, coreid, coreunit); if (!GOODIDX(core_idx, nci->num_cores)) { return (NULL); } return nci_setcoreidx(sih, core_idx); } /* Get the value of the register at offset "offset" of currently configured core */ uint BCMPOSTTRAPFN(nci_get_wrap_reg)(const si_t *sih, uint32 offset, uint32 mask, uint32 val) { const si_info_t *sii = SI_INFO(sih); uint32 *addr = (uint32 *) ((uchar *)(sii->curwrap) + offset); NCI_TRACE(("nci_wrap_reg offset %u mask %u val %u\n", offset, mask, val)); if (mask || val) { uint32 w = R_REG(sii->osh, addr); w &= ~mask; w |= val; W_REG(sii->osh, addr, w); } return (R_REG(sii->osh, addr)); } uint nci_corevendor(const si_t *sih) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; NCI_TRACE(("nci_corevendor coreidx %u\n", sii->curidx)); return (nci->cores[sii->curidx].coreinfo & COREINFO_MFG_MASK) >> COREINFO_MFG_SHIFT; } uint BCMPOSTTRAPFN(nci_corerev)(const si_t *sih) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; uint coreidx = sii->curidx; NCI_TRACE(("nci_corerev coreidx %u\n", coreidx)); return (nci->cores[coreidx].coreinfo & COREINFO_REV_MASK) >> COREINFO_REV_SHIFT; } uint nci_corerev_minor(const si_t *sih) { return (nci_core_sflags(sih, 0, 0) >> SISF_MINORREV_D11_SHIFT) & SISF_MINORREV_D11_MASK; } uint BCMPOSTTRAPFN(nci_coreid)(const si_t *sih, uint coreidx) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; NCI_TRACE(("nci_coreid coreidx %u\n", coreidx)); return nci->cores[coreidx].coreid; } /** return total coreunit of coreid or zero if not found */ uint BCMPOSTTRAPFN(nci_numcoreunits)(const si_t *sih, uint coreid) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; uint found = 0; uint i; NCI_TRACE(("nci_numcoreunits coreidx %u\n", coreid)); for (i = 0; i < nci->num_cores; i++) { if (nci->cores[i].coreid == coreid) { found++; } } return found; } /* Return the address of the nth address space in the current core * Arguments: * sih : Pointer to struct si_t * spidx : slave port index * baidx : base address index */ uint32 nci_addr_space(const si_t *sih, uint spidx, uint baidx) { const si_info_t *sii = SI_INFO(sih); uint cidx; NCI_TRACE(("nci_addr_space spidx %u baidx %u\n", spidx, baidx)); cidx = sii->curidx; return _nci_get_curmap(sii->nci_info, cidx, spidx, baidx); } /* Return the size of the nth address space in the current core * Arguments: * sih : Pointer to struct si_t * spidx : slave port index * baidx : base address index */ uint32 nci_addr_space_size(const si_t *sih, uint spidx, uint baidx) { const si_info_t *sii = SI_INFO(sih); uint cidx; NCI_TRACE(("nci_addr_space_size spidx %u baidx %u\n", spidx, baidx)); cidx = sii->curidx; return _nci_get_slave_addr_size(sii->nci_info, cidx, spidx, baidx); } /* * Performs soft reset of attached device. * Writes have the following effect: * 0b1 Request attached device to enter reset. * Write is ignored if it occurs before soft reset exit has occurred. * * 0b0 Request attached device to exit reset. * Write is ignored if it occurs before soft reset entry has occurred. * * Software can poll this register to determine whether soft reset entry or exit has occurred, * using the following values: * 0b1 Indicates that the device is in reset. * 0b0 Indicates that the device is not in reset. * * * Note * The register value updates to reflect a request for reset entry or reset exit, * but the update can only occur after required internal conditions are met. * Until these conditions are met, a read to the register returns the old value. * For example, outstanding transactions currently being handled must complete before * the register value updates. * * To ensure reset propagation within the device, * it is the responsibility of software to allow enough cycles after * soft reset assertion is reflected in the reset control register * before exiting soft reset by triggering a write of 0b0. * If this responsibility is not met, the behavior is undefined or unpredictable. * * When the register value is 0b1, * the external soft reset pin that connects to the attached AXI master or slave * device is asserted, using the correct polarity of the reset pin. * When the register value is 0b0, the external softreset * pin that connects to the attached AXI master or slave device is deasserted, * using the correct polarity of the reset pin. */ static void BCMPOSTTRAPFN(_nci_core_reset)(const si_t *sih, uint32 bits, uint32 resetbits) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; amni_regs_t *amni = (amni_regs_t *)(uintptr)sii->curwrap; volatile dmp_regs_t *io; volatile uint32* erom_base = 0u; uint32 orig_bar0_win1 = 0u; volatile uint32 dummy; volatile uint32 reg_read; uint32 dmp_write_value; /* Point to OOBR base */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: erom_base = (volatile uint32*)REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* * Save Original Bar0 Win1. In nci, the io registers dmpctrl & dmpstatus * registers are implemented in the EROM section. REF - * https://docs.google.com/document/d/1HE7hAmvdoNFSnMI7MKQV1qVrFBZVsgLdNcILNOA2C8c * This requires addition BAR0 windows mapping to erom section in chipcommon. */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); erom_base = (volatile uint32*)sii->curmap; break; default: NCI_ERROR(("_nci_core_reset Invalid bustype %d\n", BUSTYPE(sih->bustype))); break; } /* Point to DMP Control */ io = (dmp_regs_t*)(NCI_ADD_ADDR(erom_base, nci->cores[sii->curidx].dmp_regs_off)); NCI_TRACE(("_nci_core_reset reg 0x%p io %p\n", amni, io)); /* Put core into reset */ W_REG(nci->osh, &amni->idm_reset_ctrl, NI_IDM_RESET_ENTRY); /* poll for the reset to happen */ while (TRUE) { /* Wait until reset is effective */ SPINWAIT(((reg_read = R_REG(nci->osh, &amni->idm_reset_ctrl)) != NI_IDM_RESET_ENTRY), NCI_SPINWAIT_TIMEOUT); if (reg_read == NI_IDM_RESET_ENTRY) { break; } } dmp_write_value = (bits | resetbits | SICF_FGC | SICF_CLOCK_EN); W_REG(nci->osh, &io->dmpctrl, dmp_write_value); /* poll for the dmp_reg write to happen */ while (TRUE) { /* Wait until reset is effective */ SPINWAIT(((reg_read = R_REG(nci->osh, &io->dmpctrl)) != dmp_write_value), NCI_SPINWAIT_TIMEOUT); if (reg_read == dmp_write_value) { break; } } /* take core out of reset */ W_REG(nci->osh, &amni->idm_reset_ctrl, 0u); /* poll for the core to come out of reset */ while (TRUE) { /* Wait until reset is effected */ SPINWAIT(((reg_read = R_REG(nci->osh, &amni->idm_reset_ctrl)) != NI_IDM_RESET_EXIT), NCI_SPINWAIT_TIMEOUT); if (reg_read == NI_IDM_RESET_EXIT) { break; } } dmp_write_value = (bits | SICF_CLOCK_EN); W_REG(nci->osh, &io->dmpctrl, (bits | SICF_CLOCK_EN)); /* poll for the core to come out of reset */ while (TRUE) { SPINWAIT(((reg_read = R_REG(nci->osh, &io->dmpctrl)) != dmp_write_value), NCI_SPINWAIT_TIMEOUT); if (reg_read == dmp_write_value) { break; } } dummy = R_REG(nci->osh, &io->dmpctrl); BCM_REFERENCE(dummy); /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } } /* reset and re-enable a core */ void BCMPOSTTRAPFN(nci_core_reset)(const si_t *sih, uint32 bits, uint32 resetbits) { const si_info_t *sii = SI_INFO(sih); int32 iface_idx = 0u; nci_info_t *nci = sii->nci_info; nci_cores_t *core = &nci->cores[sii->curidx]; /* If Wrapper is of NIC400, then call AI functionality */ for (iface_idx = core->iface_cnt-1; iface_idx >= 0; iface_idx--) { if (!(BOOKER_INF(core->desc[iface_idx]) || NIC_INF(core->desc[iface_idx]))) { continue; } #ifdef BOOKER_NIC400_INF if (core->desc[iface_idx].node_type == NODE_TYPE_NIC400) { ai_core_reset_ext(sih, bits, resetbits); } else #endif /* BOOKER_NIC400_INF */ { _nci_core_reset(sih, bits, resetbits); } } } #ifdef BOOKER_NIC400_INF static int32 BCMPOSTTRAPFN(nci_find_first_wrapper_idx)(nci_info_t *nci, uint32 coreidx) { nci_cores_t *core_info = &nci->cores[coreidx]; uint32 iface_idx; NCI_TRACE(("nci_find_first_wrapper_idx %u\n", coreidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_INFO(("nci_find_first_wrapper_idx: %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); if ((ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_BOOKER) || (ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_NIC400)) { return iface_idx; } } /* no valid master wrapper found */ return NCI_BAD_INDEX; } #endif /* BOOKER_NIC400_INF */ void nci_core_disable(const si_t *sih, uint32 bits) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; uint32 reg_read; volatile dmp_regs_t *io = NULL; uint32 orig_bar0_win1 = 0u; uint32 dmp_write_value; amni_regs_t *amni = (amni_regs_t *)(uintptr)sii->curwrap; nci_cores_t *core = &nci->cores[sii->curidx]; int32 iface_idx; NCI_TRACE(("nci_core_disable\n")); BCM_REFERENCE(core); BCM_REFERENCE(iface_idx); #ifdef BOOKER_NIC400_INF iface_idx = nci_find_first_wrapper_idx(nci, sii->curidx); if (iface_idx < 0) { NCI_ERROR(("nci_core_disable: First Wrapper is not found\n")); ASSERT(0u); return; } /* If Wrapper is of NIC400, then call AI functionality */ if (core->desc[iface_idx].master && (core->desc[iface_idx].node_type == NODE_TYPE_NIC400)) { return ai_core_disable(sih, bits); } #endif /* BOOKER_NIC400_INF */ ASSERT(GOODREGS(sii->curwrap)); reg_read = R_REG(nci->osh, &amni->idm_reset_ctrl); /* if core is already in reset, just return */ if (reg_read == NI_IDM_RESET_ENTRY) { return; } /* Put core into reset */ W_REG(nci->osh, &amni->idm_reset_ctrl, NI_IDM_RESET_ENTRY); while (TRUE) { /* Wait until reset is effected */ SPINWAIT(((reg_read = R_REG(nci->osh, &amni->idm_reset_ctrl)) != NI_IDM_RESET_ENTRY), NCI_SPINWAIT_TIMEOUT); if (reg_read == NI_IDM_RESET_ENTRY) { break; } } /* Point to OOBR base */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: io = (volatile dmp_regs_t*) REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* Save Original Bar0 Win1 */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); io = (volatile dmp_regs_t*)sii->curmap; break; default: NCI_ERROR(("nci_core_disable Invalid bustype %d\n", BUSTYPE(sih->bustype))); break; } /* Point to DMP Control */ io = (dmp_regs_t*)(NCI_ADD_ADDR(io, nci->cores[sii->curidx].dmp_regs_off)); dmp_write_value = (bits | SICF_FGC | SICF_CLOCK_EN); W_REG(nci->osh, &io->dmpctrl, dmp_write_value); /* poll for the dmp_reg write to happen */ while (TRUE) { /* Wait until reset is effected */ SPINWAIT(((reg_read = R_REG(nci->osh, &io->dmpctrl)) != dmp_write_value), NCI_SPINWAIT_TIMEOUT); if (reg_read == dmp_write_value) { break; } } /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } } bool BCMPOSTTRAPFN(nci_iscoreup)(const si_t *sih) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; amni_regs_t *ni = (amni_regs_t *)(uintptr)sii->curwrap; uint32 reset_ctrl; #ifdef BOOKER_NIC400_INF nci_cores_t *core = &nci->cores[sii->curidx]; int32 iface_idx = nci_find_first_wrapper_idx(nci, sii->curidx); if (iface_idx < 0) { NCI_ERROR(("nci_iscoreup: First Wrapper is not found\n")); ASSERT(0u); return FALSE; } /* If Wrapper is of NIC400, then call AI functionality */ if (core->desc[iface_idx].master && (core->desc[iface_idx].node_type == NODE_TYPE_NIC400)) { return ai_iscoreup(sih); } #endif /* BOOKER_NIC400_INF */ NCI_TRACE(("nci_iscoreup\n")); reset_ctrl = R_REG(nci->osh, &ni->idm_reset_ctrl); return (reset_ctrl == NI_IDM_RESET_ENTRY) ? FALSE : TRUE; } /* TODO: OOB Router core is not available. Can be removed. */ uint nci_intflag(si_t *sih) { return 0; } uint nci_flag(si_t *sih) { /* TODO: will be implemented if required for NCI */ return 0; } uint nci_flag_alt(const si_t *sih) { /* TODO: will be implemented if required for NCI */ return 0; } void BCMATTACHFN(nci_setint)(const si_t *sih, int siflag) { BCM_REFERENCE(sih); BCM_REFERENCE(siflag); /* TODO: Figure out how to set interrupt mask in nci */ } /* TODO: OOB Router core is not available. Can we remove or need an alternate implementation. */ uint32 nci_oobr_baseaddr(const si_t *sih, bool second) { return 0; } uint nci_coreunit(const si_t *sih) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores = nci->cores; uint idx; uint coreid; uint coreunit; uint i; coreunit = 0; idx = sii->curidx; ASSERT(GOODREGS(sii->curmap)); coreid = nci_coreid(sih, sii->curidx); /* count the cores of our type */ for (i = 0; i < idx; i++) { if (cores[i].coreid == coreid) { coreunit++; } } return (coreunit); } uint nci_corelist(const si_t *sih, uint coreid[]) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *cores = nci->cores; uint32 i; for (i = 0; i < sii->numcores; i++) { coreid[i] = cores[i].coreid; } return (sii->numcores); } /* Return the number of address spaces in current core */ int BCMATTACHFN(nci_numaddrspaces)(const si_t *sih) { /* TODO: Either save it or parse the EROM on demand, currently hardcode 2 */ BCM_REFERENCE(sih); return 2; } /* The value of wrap_pos should be greater than 0 */ /* wrapba, wrapba2 and wrapba3 */ uint32 nci_get_nth_wrapper(const si_t *sih, int32 wrap_pos) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = &nci->cores[sii->curidx]; uint32 iface_idx; uint32 addr = 0; ASSERT(wrap_pos >= 0); if (wrap_pos < 0) { return addr; } NCI_TRACE(("nci_get_curmap coreidx %u\n", sii->curidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curmap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); /* hack for core idx 8, coreidx without APB Backplane ID */ if (!IS_MASTER(core_info->desc[iface_idx].iface_desc_1)) { continue; } /* TODO: Should the interface be only BOOKER or NIC is also fine. */ if (GET_NODETYPE(core_info->desc[iface_idx].iface_desc_0) != NODE_TYPE_BOOKER) { continue; } /* Iterate till we do not get a wrapper at nth (wrap_pos) position */ if (wrap_pos == 0) { break; } wrap_pos--; } if (iface_idx < core_info->iface_cnt) { addr = GET_NODEPTR(core_info->desc[iface_idx].iface_desc_0); } return addr; } /* Get slave port address of the 0th slave (csp2ba) */ uint32 nci_get_axi_addr(const si_t *sih, uint32 *size) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = (const nci_cores_t *)&nci->cores[sii->curidx]; uint32 iface_idx; uint32 addr = 0; NCI_TRACE(("nci_get_curmap coreidx %u\n", sii->curidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curmap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); if (IS_MASTER(core_info->desc[iface_idx].iface_desc_1)) { continue; } if ((ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_BOOKER) || (ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_NIC400)) { break; } } if (iface_idx < core_info->iface_cnt) { /* * TODO: Is there any case where we need to return the slave port address * corresponding to index other than 0. */ if (&core_info->desc[iface_idx].sp[0] != NULL) { addr = core_info->desc[iface_idx].sp[0].addrl; if (size) { uint32 adesc = core_info->desc[iface_idx].sp[0].adesc; *size = SLAVEPORT_ADDR_SIZE(adesc); } } } return addr; } /* spidx shouldbe the index of the slave port which we are expecting. * The value will vary from 0 to num_addr_reg. */ /* coresba and coresba2 */ uint32 nci_get_core_baaddr(const si_t *sih, uint32 *size, int32 baidx) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = (const nci_cores_t *)&nci->cores[sii->curidx]; uint32 iface_idx; uint32 addr = 0; NCI_TRACE(("nci_get_curmap coreidx %u\n", sii->curidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curmap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); /* hack for core idx 8, coreidx without APB Backplane ID */ if (IS_MASTER(core_info->desc[iface_idx].iface_desc_1)) { continue; } if ((ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_APB1) || (ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_APB2)) { break; } } if (iface_idx < core_info->iface_cnt) { /* * TODO: Is there any case where we need to return the slave port address * corresponding to index other than 0. */ if ((core_info->desc[iface_idx].num_addr_reg > baidx) && (&core_info->desc[iface_idx].sp[baidx] != NULL)) { addr = core_info->desc[iface_idx].sp[baidx].addrl; if (size) { uint32 adesc = core_info->desc[iface_idx].sp[0].adesc; *size = SLAVEPORT_ADDR_SIZE(adesc); } } } return addr; } uint32 nci_addrspace(const si_t *sih, uint spidx, uint baidx) { if (spidx == CORE_SLAVE_PORT_0) { if (baidx == CORE_BASE_ADDR_0) { return nci_get_core_baaddr(sih, NULL, CORE_BASE_ADDR_0); } else if (baidx == CORE_BASE_ADDR_1) { return nci_get_core_baaddr(sih, NULL, CORE_BASE_ADDR_1); } } else if (spidx == CORE_SLAVE_PORT_1) { if (baidx == CORE_BASE_ADDR_0) { return nci_get_axi_addr(sih, NULL); } } SI_ERROR(("nci_addrspace: Need to parse the erom again to find %d base addr" " in %d slave port\n", baidx, spidx)); return 0; } uint32 BCMATTACHFN(nci_addrspacesize)(const si_t *sih, uint spidx, uint baidx) { uint32 size = 0; if (spidx == CORE_SLAVE_PORT_0) { if (baidx == CORE_BASE_ADDR_0) { nci_get_core_baaddr(sih, &size, CORE_BASE_ADDR_0); goto done; } else if (baidx == CORE_BASE_ADDR_1) { nci_get_core_baaddr(sih, &size, CORE_BASE_ADDR_1); goto done; } } else if (spidx == CORE_SLAVE_PORT_1) { if (baidx == CORE_BASE_ADDR_0) { nci_get_axi_addr(sih, &size); goto done; } } SI_ERROR(("nci_addrspacesize: Need to parse the erom again to find %d" " base addr in %d slave port\n", baidx, spidx)); done: return size; } uint32 BCMPOSTTRAPFN(nci_core_cflags)(const si_t *sih, uint32 mask, uint32 val) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *core = &nci->cores[sii->curidx]; uint32 orig_bar0_win1 = 0; int32 iface_idx; uint32 w; BCM_REFERENCE(iface_idx); if ((core[sii->curidx].coreid) == PMU_CORE_ID) { NCI_ERROR(("nci_core_cflags: Accessing PMU DMP register (ioctrl)\n")); return 0; } ASSERT(GOODREGS(sii->curwrap)); ASSERT((val & ~mask) == 0); #ifdef BOOKER_NIC400_INF iface_idx = nci_find_first_wrapper_idx(nci, sii->curidx); if (iface_idx < 0) { NCI_ERROR(("nci_core_cflags: First Wrapper is not found\n")); ASSERT(0u); return 0u; } /* If Wrapper is of NIC400, then call AI functionality */ if (core->desc[iface_idx].master && (core->desc[iface_idx].node_type == NODE_TYPE_NIC400)) { aidmp_t *ai = sii->curwrap; if (mask || val) { w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); W_REG(sii->osh, &ai->ioctrl, w); } return R_REG(sii->osh, &ai->ioctrl); } else #endif /* BOOKER_NIC400_INF */ { volatile dmp_regs_t *io = sii->curwrap; volatile uint32 reg_read; /* BOOKER */ /* Point to OOBR base */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: io = (volatile dmp_regs_t*) REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* Save Original Bar0 Win1 */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); io = (volatile dmp_regs_t*)sii->curmap; break; default: NCI_ERROR(("nci_core_cflags Invalid bustype %d\n", BUSTYPE(sih->bustype))); break; } /* Point to DMP Control */ io = (dmp_regs_t*)(NCI_ADD_ADDR(io, nci->cores[sii->curidx].dmp_regs_off)); if (mask || val) { w = ((R_REG(sii->osh, &io->dmpctrl) & ~mask) | val); W_REG(sii->osh, &io->dmpctrl, w); } reg_read = R_REG(sii->osh, &io->dmpctrl); /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } return reg_read; } } void BCMPOSTTRAPFN(nci_core_cflags_wo)(const si_t *sih, uint32 mask, uint32 val) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *core = &nci->cores[sii->curidx]; volatile dmp_regs_t *io = sii->curwrap; uint32 orig_bar0_win1 = 0; int32 iface_idx; uint32 w; BCM_REFERENCE(iface_idx); if ((core[sii->curidx].coreid) == PMU_CORE_ID) { NCI_ERROR(("nci_core_cflags: Accessing PMU DMP register (ioctrl)\n")); return; } ASSERT(GOODREGS(sii->curwrap)); ASSERT((val & ~mask) == 0); #ifdef BOOKER_NIC400_INF iface_idx = nci_find_first_wrapper_idx(nci, sii->curidx); if (iface_idx < 0) { NCI_ERROR(("nci_core_cflags_wo: First Wrapper is not found\n")); ASSERT(0u); return; } /* If Wrapper is of NIC400, then call AI functionality */ if (core->desc[iface_idx].master && (core->desc[iface_idx].node_type == NODE_TYPE_NIC400)) { aidmp_t *ai = sii->curwrap; if (mask || val) { w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); W_REG(sii->osh, &ai->ioctrl, w); } } else #endif /* BOOKER_NIC400_INF */ { /* BOOKER */ /* Point to OOBR base */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: io = (volatile dmp_regs_t*) REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* Save Original Bar0 Win1 */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); io = (volatile dmp_regs_t*)sii->curmap; break; default: NCI_ERROR(("nci_core_cflags_wo Invalid bustype %d\n", BUSTYPE(sih->bustype))); break; } /* Point to DMP Control */ io = (dmp_regs_t*)(NCI_ADD_ADDR(io, nci->cores[sii->curidx].dmp_regs_off)); if (mask || val) { w = ((R_REG(sii->osh, &io->dmpctrl) & ~mask) | val); W_REG(sii->osh, &io->dmpctrl, w); } /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } } } uint32 nci_core_sflags(const si_t *sih, uint32 mask, uint32 val) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; nci_cores_t *core = &nci->cores[sii->curidx]; uint32 orig_bar0_win1 = 0; int32 iface_idx; uint32 w; BCM_REFERENCE(iface_idx); if ((core[sii->curidx].coreid) == PMU_CORE_ID) { NCI_ERROR(("nci_core_sflags: Accessing PMU DMP register (ioctrl)\n")); return 0; } ASSERT(GOODREGS(sii->curwrap)); ASSERT((val & ~mask) == 0); ASSERT((mask & ~SISF_CORE_BITS) == 0); #ifdef BOOKER_NIC400_INF iface_idx = nci_find_first_wrapper_idx(nci, sii->curidx); if (iface_idx < 0) { NCI_ERROR(("nci_core_sflags: First Wrapper is not found\n")); ASSERT(0u); return 0u; } /* If Wrapper is of NIC400, then call AI functionality */ if (core->desc[iface_idx].master && (core->desc[iface_idx].node_type == NODE_TYPE_NIC400)) { aidmp_t *ai = sii->curwrap; if (mask || val) { w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val); W_REG(sii->osh, &ai->iostatus, w); } return R_REG(sii->osh, &ai->iostatus); } else #endif /* BOOKER_NIC400_INF */ { volatile dmp_regs_t *io = sii->curwrap; volatile uint32 reg_read; /* BOOKER */ /* Point to OOBR base */ switch (BUSTYPE(sih->bustype)) { case SI_BUS: io = (volatile dmp_regs_t*) REG_MAP(GET_OOBR_BASE(nci->cc_erom2base), SI_CORE_SIZE); break; case PCI_BUS: /* Save Original Bar0 Win1 */ orig_bar0_win1 = OSL_PCI_READ_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE); OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, GET_OOBR_BASE(nci->cc_erom2base)); io = (volatile dmp_regs_t*)sii->curmap; break; default: NCI_ERROR(("nci_core_sflags Invalid bustype %d\n", BUSTYPE(sih->bustype))); return 0u; } /* Point to DMP Control */ io = (dmp_regs_t*)(NCI_ADD_ADDR(io, nci->cores[sii->curidx].dmp_regs_off)); if (mask || val) { w = ((R_REG(sii->osh, &io->dmpstatus) & ~mask) | val); W_REG(sii->osh, &io->dmpstatus, w); } reg_read = R_REG(sii->osh, &io->dmpstatus); /* Point back to original base */ if (BUSTYPE(sih->bustype) == PCI_BUS) { OSL_PCI_WRITE_CONFIG(nci->osh, PCI_BAR0_WIN, PCI_ACCESS_SIZE, orig_bar0_win1); } return reg_read; } } /* TODO: Used only by host */ int nci_backplane_access(si_t *sih, uint addr, uint size, uint *val, bool read) { return 0; } int nci_backplane_access_64(si_t *sih, uint addr, uint size, uint64 *val, bool read) { return 0; } uint nci_num_slaveports(const si_t *sih, uint coreidx) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = (const nci_cores_t *)&nci->cores[coreidx]; uint32 iface_idx; uint32 numports = 0; NCI_TRACE(("nci_get_curmap coreidx %u\n", coreidx)); for (iface_idx = 0; iface_idx < core_info->iface_cnt; iface_idx++) { NCI_TRACE(("nci_get_curmap iface_idx %u BP_ID %u master %u\n", iface_idx, ID_BPID(core_info->desc[iface_idx].iface_desc_1), IS_MASTER(core_info->desc[iface_idx].iface_desc_1))); /* hack for core idx 8, coreidx without APB Backplane ID */ if (IS_MASTER(core_info->desc[iface_idx].iface_desc_1)) { continue; } if ((ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_APB1) || (ID_BPID(core_info->desc[iface_idx].iface_desc_1) == BP_APB2)) { break; } } if (iface_idx < core_info->iface_cnt) { numports = core_info->desc[iface_idx].num_addr_reg; } return numports; } #if defined(BCMDBG) || defined(BCMDBG_DUMP) || defined(BCMDBG_PHYDUMP) void nci_dumpregs(const si_t *sih, struct bcmstrbuf *b) { const si_info_t *sii = SI_INFO(sih); bcm_bprintf(b, "ChipNum:%x, ChipRev;%x, BusType:%x, BoardType:%x, BoardVendor:%x\n\n", sih->chip, sih->chiprev, sih->bustype, sih->boardtype, sih->boardvendor); BCM_REFERENCE(sii); /* TODO: Implement dump regs for nci. */ } #endif /* BCMDBG || BCMDBG_DUMP || BCMDBG_PHYDUMP */ #ifdef BCMDBG static void _nci_view(osl_t *osh, aidmp_t *ai, uint32 cid, uint32 addr, bool verbose) { /* TODO: This is WIP and will be developed once the * implementation is done based on the NCI. */ } void nci_view(si_t *sih, bool verbose) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = (const nci_cores_t *)nci->cores; osl_t *osh; /* TODO: We need to do the structure mapping correctly based on the BOOKER/NIC type */ aidmp_t *ai; uint32 cid, addr; ai = sii->curwrap; osh = sii->osh; if ((core_info[sii->curidx].coreid) == PMU_CORE_ID) { SI_ERROR(("Cannot access pmu DMP\n")); return; } cid = core_info[sii->curidx].coreid; addr = nci_get_nth_wrapper(sih, 0u); _nci_view(osh, ai, cid, addr, verbose); } void nci_viewall(si_t *sih, bool verbose) { const si_info_t *sii = SI_INFO(sih); nci_info_t *nci = sii->nci_info; const nci_cores_t *core_info = (const nci_cores_t *)nci->cores; osl_t *osh; aidmp_t *ai; uint32 cid, addr; uint i; osh = sii->osh; for (i = 0; i < sii->numcores; i++) { nci_setcoreidx(sih, i); if ((core_info[i].coreid) == PMU_CORE_ID) { SI_ERROR(("Skipping pmu DMP\n")); continue; } ai = sii->curwrap; cid = core_info[i].coreid; addr = nci_get_nth_wrapper(sih, 0u); _nci_view(osh, ai, cid, addr, verbose); } } #endif /* BCMDBG */ uint32 nci_clear_backplane_to(si_t *sih) { /* TODO: This is WIP and will be developed once the * implementation is done based on the NCI. */ return 0; } #if defined (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) static bool g_disable_backplane_logs = FALSE; static uint32 last_axi_error = AXI_WRAP_STS_NONE; static uint32 last_axi_error_log_status = 0; static uint32 last_axi_error_core = 0; static uint32 last_axi_error_wrap = 0; static uint32 last_axi_errlog_lo = 0; static uint32 last_axi_errlog_hi = 0; static uint32 last_axi_errlog_id = 0; /* slave error is ignored, so account for those cases */ static uint32 si_ignore_errlog_cnt = 0; static void nci_reset_APB(const si_info_t *sii, aidmp_t *ai, int *ret, uint32 errlog_status, uint32 errlog_id) { /* only reset APB Bridge on timeout (not slave error, or dec error) */ switch (errlog_status & AIELS_ERROR_MASK) { case AIELS_SLAVE_ERR: NCI_PRINT(("AXI slave error\n")); *ret |= AXI_WRAP_STS_SLAVE_ERR; break; case AIELS_TIMEOUT: nci_reset_axi_to(sii, ai); *ret |= AXI_WRAP_STS_TIMEOUT; break; case AIELS_DECODE: NCI_PRINT(("AXI decode error\n")); #ifdef USE_HOSTMEM /* Ignore known cases of CR4 prefetch abort bugs */ if ((errlog_id & (BCM_AXI_ID_MASK | BCM_AXI_ACCESS_TYPE_MASK)) != (BCM43xx_AXI_ACCESS_TYPE_PREFETCH | BCM43xx_CR4_AXI_ID)) #endif /* USE_HOSTMEM */ { *ret |= AXI_WRAP_STS_DECODE_ERR; } break; default: ASSERT(0); /* should be impossible */ } if (errlog_status & AIELS_MULTIPLE_ERRORS) { NCI_PRINT(("Multiple AXI Errors\n")); /* Set multiple errors bit only if actual error is not ignored */ if (*ret) { *ret |= AXI_WRAP_STS_MULTIPLE_ERRORS; } } return; } /* * API to clear the back plane timeout per core. * Caller may passs optional wrapper address. If present this will be used as * the wrapper base address. If wrapper base address is provided then caller * must provide the coreid also. * If both coreid and wrapper is zero, then err status of current bridge * will be verified. */ uint32 nci_clear_backplane_to_per_core(si_t *sih, uint coreid, uint coreunit, void *wrap) { int ret = AXI_WRAP_STS_NONE; aidmp_t *ai = NULL; uint32 errlog_status = 0; const si_info_t *sii = SI_INFO(sih); uint32 errlog_lo = 0, errlog_hi = 0, errlog_id = 0, errlog_flags = 0; uint32 current_coreidx = si_coreidx(sih); uint32 target_coreidx = nci_findcoreidx(sih, coreid, coreunit); #if defined(AXI_TIMEOUTS_NIC) si_axi_error_t * axi_error = sih->err_info ? &sih->err_info->axi_error[sih->err_info->count] : NULL; #endif /* AXI_TIMEOUTS_NIC */ bool restore_core = FALSE; if ((sii->axi_num_wrappers == 0) || #ifdef AXI_TIMEOUTS_NIC (!PCIE(sii)) || #endif /* AXI_TIMEOUTS_NIC */ FALSE) { SI_VMSG(("nci_clear_backplane_to_per_core, axi_num_wrappers:%d, Is_PCIE:%d," " BUS_TYPE:%d, ID:%x\n", sii->axi_num_wrappers, PCIE(sii), BUSTYPE(sii->pub.bustype), sii->pub.buscoretype)); return AXI_WRAP_STS_NONE; } if (wrap != NULL) { ai = (aidmp_t *)wrap; } else if (coreid && (target_coreidx != current_coreidx)) { if (nci_setcoreidx(sih, target_coreidx) == NULL) { /* Unable to set the core */ NCI_PRINT(("Set Code Failed: coreid:%x, unit:%d, target_coreidx:%d\n", coreid, coreunit, target_coreidx)); errlog_lo = target_coreidx; ret = AXI_WRAP_STS_SET_CORE_FAIL; goto end; } restore_core = TRUE; ai = (aidmp_t *)si_wrapperregs(sih); } else { /* Read error status of current wrapper */ ai = (aidmp_t *)si_wrapperregs(sih); /* Update CoreID to current Code ID */ coreid = nci_coreid(sih, sii->curidx); } /* read error log status */ errlog_status = R_REG(sii->osh, &ai->errlogstatus); if (errlog_status == ID32_INVALID) { /* Do not try to peek further */ NCI_PRINT(("nci_clear_backplane_to_per_core, errlogstatus:%x - " "Slave Wrapper:%x\n", errlog_status, coreid)); ret = AXI_WRAP_STS_WRAP_RD_ERR; errlog_lo = (uint32)(uintptr)&ai->errlogstatus; goto end; } if ((errlog_status & AIELS_ERROR_MASK) != 0) { uint32 tmp; uint32 count = 0; /* set ErrDone to clear the condition */ W_REG(sii->osh, &ai->errlogdone, AIELD_ERRDONE_MASK); /* SPINWAIT on errlogstatus timeout status bits */ while ((tmp = R_REG(sii->osh, &ai->errlogstatus)) & AIELS_ERROR_MASK) { if (tmp == ID32_INVALID) { NCI_PRINT(("nci_clear_backplane_to_per_core: prev errlogstatus:%x," " errlogstatus:%x\n", errlog_status, tmp)); ret = AXI_WRAP_STS_WRAP_RD_ERR; errlog_lo = (uint32)(uintptr)&ai->errlogstatus; goto end; } /* * Clear again, to avoid getting stuck in the loop, if a new error * is logged after we cleared the first timeout */ W_REG(sii->osh, &ai->errlogdone, AIELD_ERRDONE_MASK); count++; OSL_DELAY(10); if ((10 * count) > AI_REG_READ_TIMEOUT) { errlog_status = tmp; break; } } errlog_lo = R_REG(sii->osh, &ai->errlogaddrlo); errlog_hi = R_REG(sii->osh, &ai->errlogaddrhi); errlog_id = R_REG(sii->osh, &ai->errlogid); errlog_flags = R_REG(sii->osh, &ai->errlogflags); /* we are already in the error path, so OK to check for the slave error */ if (nci_ignore_errlog(sii, ai, errlog_lo, errlog_hi, errlog_id, errlog_status)) { si_ignore_errlog_cnt++; goto end; } nci_reset_APB(sii, ai, &ret, errlog_status, errlog_id); NCI_PRINT(("\tCoreID: %x\n", coreid)); NCI_PRINT(("\t errlog: lo 0x%08x, hi 0x%08x, id 0x%08x, flags 0x%08x" ", status 0x%08x\n", errlog_lo, errlog_hi, errlog_id, errlog_flags, errlog_status)); } end: if (ret != AXI_WRAP_STS_NONE) { last_axi_error = ret; last_axi_error_log_status = errlog_status; last_axi_error_core = coreid; last_axi_error_wrap = (uint32)ai; last_axi_errlog_lo = errlog_lo; last_axi_errlog_hi = errlog_hi; last_axi_errlog_id = errlog_id; } #if defined(AXI_TIMEOUTS_NIC) if (axi_error && (ret != AXI_WRAP_STS_NONE)) { axi_error->error = ret; axi_error->coreid = coreid; axi_error->errlog_lo = errlog_lo; axi_error->errlog_hi = errlog_hi; axi_error->errlog_id = errlog_id; axi_error->errlog_flags = errlog_flags; axi_error->errlog_status = errlog_status; sih->err_info->count++; if (sih->err_info->count == SI_MAX_ERRLOG_SIZE) { sih->err_info->count = SI_MAX_ERRLOG_SIZE - 1; NCI_PRINT(("AXI Error log overflow\n")); } } #endif /* AXI_TIMEOUTS_NIC */ if (restore_core) { if (nci_setcoreidx(sih, current_coreidx) == NULL) { /* Unable to set the core */ return ID32_INVALID; } } return ret; } /* TODO: It needs to be handled based on BOOKER/NCI DMP. */ /* reset AXI timeout */ static void nci_reset_axi_to(const si_info_t *sii, aidmp_t *ai) { /* reset APB Bridge */ OR_REG(sii->osh, &ai->resetctrl, AIRC_RESET); /* sync write */ (void)R_REG(sii->osh, &ai->resetctrl); /* clear Reset bit */ AND_REG(sii->osh, &ai->resetctrl, ~(AIRC_RESET)); /* sync write */ (void)R_REG(sii->osh, &ai->resetctrl); NCI_PRINT(("AXI timeout\n")); if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) { NCI_PRINT(("reset failed on wrapper %p\n", ai)); g_disable_backplane_logs = TRUE; } } void nci_wrapper_get_last_error(const si_t *sih, uint32 *error_status, uint32 *core, uint32 *lo, uint32 *hi, uint32 *id) { *error_status = last_axi_error_log_status; *core = last_axi_error_core; *lo = last_axi_errlog_lo; *hi = last_axi_errlog_hi; *id = last_axi_errlog_id; } uint32 nci_get_axi_timeout_reg(void) { return (GOODREGS(last_axi_errlog_lo) ? last_axi_errlog_lo : 0); } #endif /* AXI_TIMEOUTS || AXI_TIMEOUTS_NIC */ /* TODO: This function should be able to handle NIC as well as BOOKER */ bool nci_ignore_errlog(const si_info_t *sii, const aidmp_t *ai, uint32 lo_addr, uint32 hi_addr, uint32 err_axi_id, uint32 errsts) { uint32 ignore_errsts = AIELS_SLAVE_ERR; uint32 ignore_errsts_2 = 0; uint32 ignore_hi = BT_CC_SPROM_BADREG_HI; uint32 ignore_lo = BT_CC_SPROM_BADREG_LO; uint32 ignore_size = BT_CC_SPROM_BADREG_SIZE; bool address_check = TRUE; uint32 axi_id = 0; uint32 axi_id2 = 0; bool extd_axi_id_mask = FALSE; uint32 axi_id_mask; NCI_PRINT(("err check: core %p, error %d, axi id 0x%04x, addr(0x%08x:%08x)\n", ai, errsts, err_axi_id, hi_addr, lo_addr)); /* ignore the BT slave errors if the errlog is to chipcommon addr 0x190 */ switch (CHIPID(sii->pub.chip)) { case BCM4397_CHIP_GRPID: /* TODO: Are these IDs same for 4397 as well? */ #ifdef BTOVERPCIE axi_id = BCM4378_BT_AXI_ID; /* For BT over PCIE, ignore any slave error from BT. */ /* No need to check any address range */ address_check = FALSE; #endif /* BTOVERPCIE */ axi_id2 = BCM4378_ARM_PREFETCH_AXI_ID; extd_axi_id_mask = TRUE; ignore_errsts_2 = AIELS_DECODE; break; default: return FALSE; } axi_id_mask = extd_axi_id_mask ? AI_ERRLOGID_AXI_ID_MASK_EXTD : AI_ERRLOGID_AXI_ID_MASK; /* AXI ID check */ err_axi_id &= axi_id_mask; errsts &= AIELS_ERROR_MASK; /* check the ignore error cases. 2 checks */ if (!(((err_axi_id == axi_id) && (errsts == ignore_errsts)) || ((err_axi_id == axi_id2) && (errsts == ignore_errsts_2)))) { /* not the error ignore cases */ return FALSE; } /* check the specific address checks now, if specified */ if (address_check) { /* address range check */ if ((hi_addr != ignore_hi) || (lo_addr < ignore_lo) || (lo_addr >= (ignore_lo + ignore_size))) { return FALSE; } } NCI_PRINT(("err check: ignored\n")); return TRUE; } /* TODO: Check the CORE to AXI ID mapping for 4397 */ uint32 nci_findcoreidx_by_axiid(const si_t *sih, uint32 axiid) { uint coreid = 0; uint coreunit = 0; const nci_axi_to_coreidx_t *axi2coreidx = NULL; switch (CHIPID(sih->chip)) { case BCM4397_CHIP_GRPID: axi2coreidx = axi2coreidx_4397; break; default: NCI_PRINT(("Chipid mapping not found\n")); break; } if (!axi2coreidx) { return (BADIDX); } coreid = axi2coreidx[axiid].coreid; coreunit = axi2coreidx[axiid].coreunit; return nci_findcoreidx(sih, coreid, coreunit); } void nci_coreaddrspaceX(const si_t *sih, uint asidx, uint32 *addr, uint32 *size) { /* Adding just a wrapper. Will implement when required. */ } /* * this is not declared as static const, although that is the right thing to do * reason being if declared as static const, compile/link process would that in * read only section... * currently this code/array is used to identify the registers which are dumped * during trap processing * and usually for the trap buffer, .rodata buffer is reused, so for now just static */ /* TODO: Should we do another mapping for BOOKER and used correct one based on type of DMP. */ #ifdef DONGLEBUILD static uint32 BCMPOST_TRAP_RODATA(wrapper_offsets_to_dump)[] = { OFFSETOF(aidmp_t, ioctrl), OFFSETOF(aidmp_t, iostatus), OFFSETOF(aidmp_t, resetctrl), OFFSETOF(aidmp_t, resetstatus), OFFSETOF(aidmp_t, resetreadid), OFFSETOF(aidmp_t, resetwriteid), OFFSETOF(aidmp_t, errlogctrl), OFFSETOF(aidmp_t, errlogdone), OFFSETOF(aidmp_t, errlogstatus), OFFSETOF(aidmp_t, errlogaddrlo), OFFSETOF(aidmp_t, errlogaddrhi), OFFSETOF(aidmp_t, errlogid), OFFSETOF(aidmp_t, errloguser), OFFSETOF(aidmp_t, errlogflags), OFFSETOF(aidmp_t, itipoobaout), OFFSETOF(aidmp_t, itipoobbout), OFFSETOF(aidmp_t, itipoobcout), OFFSETOF(aidmp_t, itipoobdout) }; static uint32 BCMRAMFN(nci_get_sizeof_wrapper_offsets_to_dump)(void) { return (sizeof(wrapper_offsets_to_dump)); } static uint32 BCMRAMFN(nci_get_wrapper_base_addr)(uint32 **offset) { uint32 arr_size = ARRAYSIZE(wrapper_offsets_to_dump); *offset = &wrapper_offsets_to_dump[0]; return arr_size; } #ifdef UART_TRAP_DBG /* TODO: Is br_wrapba populated for 4397 NCI? */ void nci_dump_APB_Bridge_registers(const si_t *sih) { aidmp_t *ai; const si_info_t *sii = SI_INFO(sih); ai = (aidmp_t *)sii->br_wrapba[0]; printf("APB Bridge 0\n"); printf("lo 0x%08x, hi 0x%08x, id 0x%08x, flags 0x%08x", R_REG(sii->osh, &ai->errlogaddrlo), R_REG(sii->osh, &ai->errlogaddrhi), R_REG(sii->osh, &ai->errlogid), R_REG(sii->osh, &ai->errlogflags)); printf("\n status 0x%08x\n", R_REG(sii->osh, &ai->errlogstatus)); } #endif /* UART_TRAP_DBG */ uint32 BCMATTACHFN(nci_wrapper_dump_buf_size)(const si_t *sih) { uint32 buf_size = 0; uint32 wrapper_count = 0; const si_info_t *sii = SI_INFO(sih); wrapper_count = sii->axi_num_wrappers; if (wrapper_count == 0) { return 0; } /* cnt indicates how many registers, tag_id 0 will say these are address/value */ /* address/value pairs */ buf_size += 2 * (nci_get_sizeof_wrapper_offsets_to_dump() * wrapper_count); return buf_size; } uint32* nci_wrapper_dump_binary_one(const si_info_t *sii, uint32 *p32, uint32 wrap_ba) { uint i; uint32 *addr; uint32 arr_size; uint32 *offset_base; arr_size = nci_get_wrapper_base_addr(&offset_base); for (i = 0; i < arr_size; i++) { addr = (uint32 *)(wrap_ba + *(offset_base + i)); *p32++ = (uint32)addr; *p32++ = R_REG(sii->osh, addr); } return p32; } uint32 nci_wrapper_dump_binary(const si_t *sih, uchar *p) { uint32 *p32 = (uint32 *)p; uint32 i; const si_info_t *sii = SI_INFO(sih); for (i = 0; i < sii->axi_num_wrappers; i++) { p32 = nci_wrapper_dump_binary_one(sii, p32, sii->axi_wrapper[i].wrapper_addr); } return 0; } #if defined(ETD) uint32 nci_wrapper_dump_last_timeout(const si_t *sih, uint32 *error, uint32 *core, uint32 *ba, uchar *p) { #if defined (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) uint32 *p32; uint32 wrap_ba = last_axi_error_wrap; uint i; uint32 *addr; const si_info_t *sii = SI_INFO(sih); if (last_axi_error != AXI_WRAP_STS_NONE) { if (wrap_ba) { p32 = (uint32 *)p; uint32 arr_size; uint32 *offset_base; arr_size = nci_get_wrapper_base_addr(&offset_base); for (i = 0; i < arr_size; i++) { addr = (uint32 *)(wrap_ba + *(offset_base + i)); *p32++ = R_REG(sii->osh, addr); } } *error = last_axi_error; *core = last_axi_error_core; *ba = wrap_ba; } #else *error = 0; *core = 0; *ba = 0; #endif /* AXI_TIMEOUTS || AXI_TIMEOUTS_NIC */ return 0; } #endif /* ETD */ bool nci_check_enable_backplane_log(const si_t *sih) { #if defined (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) if (g_disable_backplane_logs) { return FALSE; } else { return TRUE; } #else /* (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) */ return FALSE; #endif /* (AXI_TIMEOUTS) || defined (AXI_TIMEOUTS_NIC) */ } #endif /* DONGLEBUILD */