/******************************************************************************* NAME $RCSfile: plxDiag.c,v $ SUMMARY PLX PCI-E switch diagnostics for Pikes Peak VERSION $Revision: 1.19 $ UPDATE DATE $Date: 2010/05/27 05:09:36 $ PROGRAMMER $Author: jimmy $ Copyright 2009 LSI Corporation. All Rights Reserved. DESCRIPTION: REFERENCE: *******************************************************************************/ #include #include #include #include #include "commPciChip.h" #include #include #include #include #include "plxDiag.h" #define EEPROM_MMIO_MODE #ifdef PLX_DEBUG #define DPRINTK printk #else #define DPRINTK(fmt...) do { } while(0) #endif /*FUNCTION PROTOTYPE*/ static int plxScanDevice(PLX_PCIE_SWT *swt); static void plxIpcSend(PLX_MSG *msg); int plxIpcInit(void); int plxPeerIpcInit(void); void plxIpcExit(void); int plxPeerIpcExit(void); int plxClearErrorStat(void); static PLX_PCIE_SWT plxSWT; static int plxIpcInitDone = 0; static int plxPeerIpcInitDone = 0; static phys_addr_t plxRemotePhy = 0; static volatile uint8_t *plxLocalRam = NULL; static volatile uint8_t *plxRemoteRam = NULL; static void (*plxRequestDone)(void); static struct task_struct *plxTask = NULL; static PLX_MSG *localPlxMsg = NULL; static PLX_MSG *remotePlxMsg = NULL; static __iomem void *plxMMIO = NULL; static DECLARE_WAIT_QUEUE_HEAD(plxWaitQueue); static PLX_NT_DIAG_CB ntDiagCb; extern int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val); /******************************************************************************* * PROCEDURE * * NAME: plxRegReadTest * SUMMARY: Generic pci register test * * SCOPE: Public * * DESCRIPTION: * * RETURNS: * * NOTES: * */ int plxRegReadTest(void) { return (pciRegTest(PCIE_PLX_PEX8648, pciReadTest)); } int plxRegAddressLineTest(void) { return (pciRegTest(PCIE_PLX_PEX8648, pciAddressLineTest)); } int plxRegDataLineTest(void) { return (pciRegTest(PCIE_PLX_PEX8648, pciDataLineTest)); } static int plxCheckSwitch(PLX_PCIE_SWT *swt) { int i; int usedPorts = 0; /* Make sure all ports available */ for (i = 0; i < MAX_PLX_PORT; i++) { if (swt->port[i].flag == PLX_PORT_USED) usedPorts++; } if (usedPorts != PIKES_PEAK_PLX_PORT_NUM) { DPRINTK("[PLX_DIAG] Error, detect port numbers: %d\n", usedPorts); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_EPORTS, 0, 0, 0, DG_FORMAT_NONE); return (-EFAULT); } /* Check port configuation & status */ for (i = 0; i < MAX_PLX_PORT; i++) { PLX_PORT *port = &swt->port[i]; if (port->flag == PLX_PORT_USED) { uint16_t status; /* check pci status error */ pci_read_config_word(port->pdev, PCI_STATUS, &status); status &= 0xf900; if (status) { printk("[PLX_DIAG] Port(%d) pci status error %x\n", port->portNum, status); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESTATUS, 0, 0, 0, DG_FORMAT_NONE); } /* check pcie device status error */ pci_read_config_word(port->pdev, PLX_PCIEDSTS, &status); //FIXME: check fatal & not-fatal error only status &= 0x6; if (status) { printk("[PLX_DIAG] Port(%d) pcie device status error %x\n", port->portNum, status); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESTATUS, 0, 0, 0, DG_FORMAT_NONE); } /* check lane width * upstream port -> 4 (8648) 20 (8649) * downstream port -> 0, 1, 8, 9 (8648) 0, 1, 16, 17 (8649) */ switch (port->portNum) { case 4: /* 8648 */ case 20: /* 8649 */ if (port->laneWidth != PLX_UPSTREAM_LANE_WIDTH) { printk("[PLX_DIAG] Port(%d) wrong lane width (%d)\n", port->portNum, port->laneWidth); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_EWIDTH, 0, 0, 0, DG_FORMAT_NONE); } break; case 9: /* 8648 */ case 17: /* 8649 */ if (sysHostCardType == HOST_CARD_NONE) break; case 0: case 1: case 8: /* 8648 */ case 16: /* 8649 */ if (port->laneWidth != PLX_DOWNSTREAM_LANE_WIDTH) { printk("[PLX_DIAG] Port(%d) wrong lane width (%d)\n", port->portNum, port->laneWidth); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_EWIDTH, 0, 0, 0, DG_FORMAT_NONE); } break; default: printk("[PLX_DIAG] Port(%d) should not exist, configuration error???\n", port->portNum); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_EPORTS, 0, 0, 0, DG_FORMAT_NONE); break; } } } return (0); } /******************************************************************************* * PROCEDURE * * NAME: plxDiagSwitch * SUMMARY: Check switch hw configuration and status * * SCOPE: Public * * DESCRIPTION: * * RETURNS: * * NOTES: * */ int plxDiagSwitch(void) { int err = 0; PLX_PCIE_SWT *swt = &plxSWT; memset(swt, 0, sizeof(PLX_PCIE_SWT)); err = plxScanDevice(swt); if (err) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESCAN, 0, 0, 0, DG_FORMAT_NONE); return (err); } plxCheckSwitch(swt); return (0); } #define PLX_DATA_LINE_PAT_SIZE 4 uint8_t patternPlxDataLine[] = { 0xFF, 0xAA, 0x55, 0x00 }; static void plxNTReqDone(void) { memcpy(ntDiagCb.rxMsg, localPlxMsg, sizeof(PLX_MSG)); ntDiagCb.rxDone = 1; wake_up(&plxWaitQueue); } /******************************************************************************* * PROCEDURE * * NAME: plxNTBridgeDiag * SUMMARY: Diagnose NTB connection between two controllers * * SCOPE: Public * * DESCRIPTION: * Using the plx ipc task to diagnose NTB connection * The specific data patterns are used to verify address line and data line * * RETURNS: * * NOTES: * */ int plxNTBridgeDiag(void) { uint32_t i; int time_left; /* initialize IPC task & NTB settings */ if (plxIpcInit()) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EINIT, 0, 0, 0, DG_FORMAT_NONE); return (-EFAULT); } if (plxPeerIpcInit()) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EINIT, 0, 0, 0, DG_FORMAT_NONE); return (-EFAULT); } ntDiagCb.txMsg = kzalloc(sizeof(PLX_MSG), GFP_KERNEL); if (!ntDiagCb.txMsg) { printk("Out of memory\n"); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EINIT, 0, 0, 0, DG_FORMAT_NONE); return (-ENOMEM); } ntDiagCb.rxMsg = kzalloc(sizeof(PLX_MSG), GFP_KERNEL); if (!ntDiagCb.rxMsg) { printk("Out of memory\n"); commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EINIT, 0, 0, 0, DG_FORMAT_NONE); kfree(ntDiagCb.txMsg); return (-ENOMEM); } /* setup request done callback */ plxRequestDone = plxNTReqDone; /* Address Line Test */ for (i = 0; i <= PLX_MSG_SHIFT; i++) { uint8_t *ptr = (uint8_t*) ntDiagCb.txMsg; int idx; idx = 1 << i; ptr[idx] = 0xff; } ntDiagCb.txMsg->signature = PLX_MSG_REQ_SIGNATURE; ntDiagCb.rxDone = 0; plxIpcSend(ntDiagCb.txMsg); /* wait 1 second */ time_left = wait_event_timeout(plxWaitQueue, ntDiagCb.rxDone, msecs_to_jiffies(1000)); if (!time_left) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_ETIMEDOUT, 0, 0, 0, DG_FORMAT_NONE); goto out; } else { if (memcmp((void*) ntDiagCb.txMsg->body, (void*) ntDiagCb.rxMsg->body, PLX_MSG_BODY_SIZE)) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EADDRLINE, 0, 0, 0, DG_FORMAT_NONE); goto out; } } /* Data Line Test */ for (i = 0; i < PLX_DATA_LINE_PAT_SIZE; i++) { ntDiagCb.txMsg->signature = PLX_MSG_REQ_SIGNATURE; memset((void*) ntDiagCb.txMsg->body, patternPlxDataLine[i], PLX_MSG_BODY_SIZE); ntDiagCb.rxDone = 0; plxIpcSend(ntDiagCb.txMsg); /* wait 1 second */ time_left = wait_event_timeout(plxWaitQueue, ntDiagCb.rxDone, msecs_to_jiffies(1000)); if (!time_left) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_ETIMEDOUT, 0, 0, 0, DG_FORMAT_NONE); break; } else { if (memcmp((void*) ntDiagCb.txMsg->body, (void*) ntDiagCb.rxMsg->body, PLX_MSG_BODY_SIZE)) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_NTB, PCIE_SWITCH_CODE_NTB, PCIE_SWITCH_MSG_NTB_EDATALINE, 0, 0, 0, DG_FORMAT_NONE); printk("PLX NT data line test compare error !!!\n"); break; } } } out: /* clean up resource */ plxRequestDone = NULL; kfree(ntDiagCb.txMsg); kfree(ntDiagCb.rxMsg); plxIpcExit(); plxPeerIpcExit(); return (0); } /******************************************************************************* * Gerneral APIs for PLX on Pikes Peak *******************************************************************************/ #define PLX_EEPROM_CMD_READ 3 #define PLX_EEPROM_CMD_READ_STATUS 5 #define PLX_EEPROM_CMD_WRITE_ENABLE 6 #define PLX_EEPROM_CMD_WRITE_DISABLE 4 #define PLX_EEPROM_CMD_WRITE 2 #define PLX_EEPROM_CMD_WRITE_STATUS 1 /******************************************************************************* * PROCEDURE * * NAME: plxScanDevice * SUMMARY: Scan PLX device on the system * * SCOPE: Private * * DESCRIPTION: * Find out all plx bridges on the system * get bridge hw configurations for diagnostics * * RETURNS: * * NOTES: * */ static int plxScanDevice(PLX_PCIE_SWT *swt) { struct pci_dev *pdev = NULL; uint8_t portNum; uint32_t dwVal; uint16_t wVal; int devFound = 0; while ((pdev = pci_get_device(0x10b5, PCI_ANY_ID, pdev))) { pci_read_config_dword(pdev, PLX_PCIELCAP, &dwVal); portNum = dwVal >> 24; if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL && pdev->pcie_type == PCI_EXP_TYPE_ENDPOINT) { /* This maybe NT virtual or NT link devices. * If we had device put in port slot then * this is NT link device from peer controller. * Otherwise put in port slot. */ if (swt->port[portNum].flag & PLX_PORT_USED) continue; } pci_read_config_word(pdev, PLX_PCIELSTS, &wVal); printk( KERN_INFO "portNum = %d\n", portNum ); swt->port[portNum].pdev = pdev; swt->port[portNum].flag |= PLX_PORT_USED; swt->port[portNum].portNum = portNum; swt->port[portNum].laneWidth = (wVal & 0x3f0) >> 4; swt->port[portNum].linkSpeed = wVal & 0xf; printk( KERN_INFO "laneWidth = %X, linkSpeed = %X\n", swt->port[portNum].laneWidth, swt->port[portNum].linkSpeed ); if (portNum == 0) { pci_read_config_word(pdev, PCI_DEVICE_ID, &swt->deviceID); } DPRINTK("PLX Port[%d], Landwidth x%d, LinkSpeed %d\n", portNum, swt->port[portNum].laneWidth, swt->port[portNum].linkSpeed); devFound++; } if (devFound == 0) return (-ENODEV); return (0); } #ifdef PLX_DEBUG static void plxDumpMsg(PLX_MSG *msg) { int i; printk("======================\n"); printk("signature [%x]\n", msg->signature); printk("======== body ========"); for (i = 0; i < 256; i++) { if (!(i%32)) printk("\n"); printk("%02x ", msg->body[i]); } printk("\n"); } #endif static void plxIpcSend(PLX_MSG *msg) { /* To avoid a race condition, * copy data buffer then the signature */ memcpy((void*) remotePlxMsg->body, (void*) msg->body, PLX_MSG_BODY_SIZE); memcpy((void*) &remotePlxMsg->signature, (void*) &msg->signature, sizeof(msg->signature)); } /******************************************************************************* * PROCEDURE * * NAME: plxIpcTask * SUMMARY: Ipc Task through plx ntb * * SCOPE: Private * * DESCRIPTION: * This linux kernel thread monitors contents of a memory buffer * The monitored buffer is only written by peer controller through * plx non-transparent bridge. The task echos back the receiving data * to peer controller's memory buffer. * * RETURNS: * * NOTES: * */ static int plxIpcTask(void *unused) { volatile uint32_t signature; while (!kthread_should_stop()) { signature = localPlxMsg->signature; switch (signature) { case PLX_MSG_REQ_SIGNATURE: DPRINTK("[PLX_IPC] receive REQ\n"); #ifdef PLX_DEBUG plxDumpMsg(localPlxMsg); #endif /* set response signature and * echo back receiving data */ localPlxMsg->signature = PLX_MSG_RSP_SIGNATURE; plxIpcSend(localPlxMsg); memset(localPlxMsg, 0, sizeof(PLX_MSG)); break; case PLX_MSG_RSP_SIGNATURE: if (plxRequestDone) plxRequestDone(); else printk("[plxIpcTask] No Req Done callback???\n"); memset(localPlxMsg, 0, sizeof(PLX_MSG)); break; case PLX_MSG_NO_SIGNATURE: break; default: printk("[plxIpcTask] Broken message??? signature = %x\n", signature); memset(localPlxMsg, 0, sizeof(PLX_MSG)); break; } msleep(100); } return (0); } /******************************************************************************* * PROCEDURE * * NAME: plxNTBInit * SUMMARY: Initialize resources for NTB diagnostics * * SCOPE: Private * * DESCRIPTION: * * RETURNS: * * NOTES: * */ static int plxNTBInit(void) { struct pci_dev *pdev = NULL; int done = 0; int pdevclass=0; while ((pdev = pci_get_device(0x10b5, PCI_ANY_ID, pdev))) { if ((pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) && (((pdev->class >> 8) == PCI_CLASS_BRIDGE_OTHER) || ((pdev->class >> 8) == PCI_CLASS_SYSTEM_OTHER)) && (pdev->pcie_type == PCI_EXP_TYPE_ENDPOINT)) { uint32_t bl, bu; /* peer physical address */ pci_read_config_dword(pdev, 0x18, &bl); plxRemotePhy = 0; if (bl & 0x4) { #ifdef CONFIG_PHYS_ADDR_T_64BIT /* 64-bit address */ pci_read_config_dword(pdev, 0x1c, &bu); plxRemotePhy = bu; plxRemotePhy = plxRemotePhy << 32; #else BUG(); #endif } plxRemotePhy += (bl & 0xfff00000); DPRINTK("PLX remote phy %llx\n", plxRemotePhy); done = 1; break; } } pdevclass=(pdev->class >> 8); printk("pdev->class=%X\n",pdevclass); if (!done) { printk("PLX: can not find NT device\n"); return (-ENODEV); } /* map phisical to virtual address */ plxLocalRam = (uint8_t*) ioremap(PLX_NT_TRANS_ADDR, PLX_NT_BUFFER_SIZE); if (!plxLocalRam) { printk("%s(%d) ioremap failed\n", __func__, __LINE__); return (-ENOMEM); } plxRemoteRam = (uint8_t*) ioremap(plxRemotePhy, PLX_NT_BUFFER_SIZE); if (!plxRemoteRam) { printk("%s(%d) ioremap failed\n", __func__, __LINE__); iounmap(plxLocalRam); return (-ENOMEM); } return (0); } static void plxPeerIpcNotify(EIPC_MSG *msg) { switch (msg->hdr.app) { case IPC_APP_START_PLX_IPC: if (!msg->hdr.cookie) plxPeerIpcInitDone = 1; else printk("Failed to initialize peer PLX IPC(%u)\n", msg->hdr.cookie); ipcEthernetDeregister(IPC_APP_START_PLX_IPC); break; case IPC_APP_STOP_PLX_IPC: plxPeerIpcInitDone = 0; ipcEthernetDeregister(IPC_APP_STOP_PLX_IPC); break; default: BUG(); } } int plxIpcInit(void) { int err = 0; if (plxIpcInitDone) return (0); err = plxNTBInit(); if (err) return (err); localPlxMsg = (PLX_MSG*) plxLocalRam; remotePlxMsg = (PLX_MSG*) plxRemoteRam; /* clean local buffer */ memset(localPlxMsg, 0, sizeof(PLX_MSG)); plxTask = kthread_run(plxIpcTask, NULL, "plxIpc"); plxIpcInitDone = 1; return (0); } int plxPeerIpcInit(void) { IPC_MSG msg; ipcEthernetRegister(IPC_APP_START_PLX_IPC, plxPeerIpcNotify); msg.hdr.app = IPC_APP_START_PLX_IPC; msg.hdr.flag = IPC_APP_REQUEST; msg.hdr.len = 0; ipcEthernetSend((EIPC_MSG*) &msg); msleep(1000); if (!plxPeerIpcInitDone) { printk("EIPC error or PLX IPC initialize failed?\n"); return (-EFAULT); } return (0); } void plxIpcExit(void) { /* stop ipc task */ if (plxTask) { kthread_stop(plxTask); plxTask = NULL; } if (plxLocalRam) { iounmap(plxLocalRam); plxLocalRam = NULL; } if (plxRemoteRam) { iounmap(plxRemoteRam); plxRemoteRam = NULL; } plxIpcInitDone = 0; } EXPORT_SYMBOL(plxIpcExit); int plxPeerIpcExit(void) { IPC_MSG msg; ipcEthernetRegister(IPC_APP_STOP_PLX_IPC, plxPeerIpcNotify); msg.hdr.app = IPC_APP_STOP_PLX_IPC; msg.hdr.flag = IPC_APP_REQUEST; msg.hdr.len = 0; ipcEthernetSend((EIPC_MSG*) &msg); msleep(1000); if (plxPeerIpcInitDone) { printk("EIPC error or Peer PLX IPC exit failed?\n"); return (-EFAULT); } return (0); } EXPORT_SYMBOL(plxPeerIpcExit); static int plxEepromSendCommand(struct pci_dev *pdev, uint32_t cmd) { uint32_t ctrl; int timeout = 100000; #ifdef EEPROM_MMIO_MODE writel(cmd, plxMMIO + 0x260); #else pci_write_config_dword(pdev, 0x260, cmd); #endif do { #ifdef EEPROM_MMIO_MODE ctrl = readl(plxMMIO + 0x260); #else pci_read_config_dword(pdev, 0x260, &ctrl); #endif if ((ctrl & (1 << 18)) == 0) return (0); timeout--; } while (timeout); return (-ETIMEDOUT); } static int plxEepromWaitIdle(struct pci_dev *pdev) { uint32_t ctrl; int timeout = 100000; #ifdef EEPROM_MMIO_MODE ctrl = readl(plxMMIO + 0x260); #else pci_read_config_dword(pdev, 0x260, &ctrl); #endif ctrl &= ~(7 << 13); /* Clear EepCmd [15:13] */ ctrl &= ~(1 << 24); /* Clear EepRdy [24] */ ctrl &= ~(7 << 28); /* Clear EepWrStatus [30:28] */ ctrl |= (PLX_EEPROM_CMD_READ_STATUS << 13); do { plxEepromSendCommand(pdev, ctrl); #ifdef EEPROM_MMIO_MODE ctrl = readl(plxMMIO + 0x260); #else pci_read_config_dword(pdev, 0x260, &ctrl); #endif if (((ctrl & (1 << 24)) == 0) && ((ctrl & (7 << 28)) == 0)) return (0); timeout--; } while (timeout); return (-ETIMEDOUT); } /******************************************************************************* * PROCEDURE * * NAME: plxEepromWrite * SUMMARY: * * SCOPE: Public * * DESCRIPTION: * Write data into specific address of PLX eeprom * RETURNS: * * NOTES: * */ int plxEepromWrite(uint16_t addr, uint32_t data) { struct pci_dev *pdev = plxSWT.port[0].pdev; uint32_t ctrl; if (pdev == NULL) { printk("No device ? or do scan device\n"); return -ENODEV; } if (addr >= (1 << 14)) { printk("Invalid eeprom address(%x)\n", addr); return (-EINVAL); } if (plxEepromWaitIdle(pdev)) { printk("Wait eeprom ready timeout\n"); return (-ETIMEDOUT); } addr = addr/sizeof(uint32_t); #ifdef EEPROM_MMIO_MODE ctrl = readl(plxMMIO + 0x260); #else pci_read_config_dword(pdev, 0x260, &ctrl); #endif ctrl &= ~(7 << 13); /* Clear EepCmd [15:13] */ ctrl &= ~((1 << 20) | (0x1FFF << 0)); /* Clear [20, 12:0] */ plxEepromSendCommand(pdev, ctrl | PLX_EEPROM_CMD_WRITE_ENABLE << 13); #ifdef EEPROM_MMIO_MODE writel(data, plxMMIO + 0x264); #else pci_write_config_dword(pdev, 0x264, data); #endif ctrl |= (addr & 0x1FFF) | /* bits [12:0] */ (((addr >> 13) & 1) << 20) | /* bit 13 */ (PLX_EEPROM_CMD_WRITE << 13); /* bits [15:13] */ plxEepromSendCommand(pdev, ctrl); return (0); } /******************************************************************************* * PROCEDURE * * NAME: plxEepromRead * SUMMARY: * * SCOPE: Public * * DESCRIPTION: * Read data from specific address of PLX eeprom * RETURNS: * * NOTES: * */ int plxEepromRead(uint16_t addr, uint32_t *data) { struct pci_dev *pdev = plxSWT.port[0].pdev; uint32_t ctrl; if (pdev == NULL) { printk("No device ? or do scan device\n"); return -ENODEV; } if (addr >= (1 << 14)) { printk("Invalid eeprom address(%x)\n", addr); return (-EINVAL); } if (plxEepromWaitIdle(pdev)) { printk("Wait eeprom ready timeout\n"); return (-ETIMEDOUT); } addr = addr/sizeof(uint32_t); #ifdef EEPROM_MMIO_MODE ctrl = readl(plxMMIO + 0x260); #else pci_read_config_dword(pdev, 0x260, &ctrl); #endif ctrl &= ~(7 << 13); /* Clear EepCmd [15:13] */ ctrl &= ~((1 << 20) | (0x1FFF << 0)); /* Clear [20, 12:0] */ ctrl |= (addr & 0x1FFF) | /* bits [12:0] */ (((addr >> 13) & 1) << 20) | /* bit 13 */ (PLX_EEPROM_CMD_READ << 13); /* bits [15:13] */ plxEepromSendCommand(pdev, ctrl); #ifdef EEPROM_MMIO_MODE *data = readl(plxMMIO + 0x264); #else pci_read_config_dword(pdev, 0x264, data); #endif return (0); } /******************************************************************************* * PROCEDURE * * NAME: * SUMMARY: plxEepromSetCfgReg * * SCOPE: Public * * DESCRIPTION: * Write configuration register vaule into eeprom * RETURNS: * * NOTES: * */ int plxEepromSetCfgReg(int port, uint32_t reg, uint32_t value) { uint32_t hdr, tmp; uint16_t regAddr, regByteCnt, addr; /* Read signature & size */ plxEepromRead(0, &hdr); if ((hdr & 0xffff) != 0x005a) { printk("Please init EEPROM(%08x)\n", hdr); return (-EFAULT); } /* Calculate REGADDR in eeprom */ switch (port) { case 0: regAddr = 0x0000; break; case 1: regAddr = 0x0400; break; case 4: regAddr = 0x2000; break; case 8: regAddr = 0x4000; break; case 9: regAddr = 0x4400; break; case 16: regAddr = 0x8000; break; case 17: regAddr = 0x8400; break; case 20: regAddr = 0xa000; break; case 0xff: regAddr = 0xe000; break; default: printk("Invalid port(%d) for PLX\n", port); return (-EINVAL); } regAddr |= (uint16_t) (reg >> 2); DPRINTK("EEPROM hdr %08x, REGADDR %04x\n", hdr, regAddr); regByteCnt = (uint16_t) (hdr >> 16); if (regByteCnt % 4) { addr = (regByteCnt + 4) & 0xfffc; /* Read modify write */ plxEepromRead(addr, &tmp); tmp &= 0x0000ffff; tmp |= ((uint32_t) regAddr) << 16; plxEepromWrite(addr, tmp); plxEepromWrite(addr + 4, value); } else { addr = regByteCnt + 4; plxEepromWrite(addr, regAddr + (((uint16_t) value) << 16)); plxEepromWrite(addr + 4, 0xffff0000 + (value >> 16)); } /* Update REG BYTE COUNT */ regByteCnt += 6; plxEepromWrite(0, ((uint32_t) regByteCnt << 16) | 0x005a); return (0); } void plxEepromShow(void) { uint32_t hdr, data; uint16_t regByteCnt, addr; /* Read signature & size */ plxEepromRead(0, &hdr); if ((hdr & 0xffff) != 0x005a) { printk("EEPROM not initialized(%08x)\n", hdr); return; } regByteCnt = (uint16_t) (hdr >> 16); printk("\nPLX EEPROM: REG BYTE COUNT(%d)\n", regByteCnt); for (addr = 0; addr < regByteCnt + 4; addr += 4) { plxEepromRead(addr, &data); printk("(%d) %08x\n\n", addr, data); } } void plxEepromShowByOffset(uint16_t offset) { uint32_t data; plxEepromRead(offset & 0xfffc, &data); printk("\nEEPROM(%d) %08x\n", offset & 0xfffc, data); } /******************************************************************************* * PROCEDURE * * NAME: * SUMMARY: plxEepromInitBlank * * SCOPE: Public * * DESCRIPTION: * Initialize blank PLX eeprom * RETURNS: * * NOTES: * */ int plxEepromInitBlank(void) { int ret; uint32_t hdr; /* Read signature */ plxEepromRead(0, &hdr); if ((hdr & 0xffff) == 0x005a) { printk("PLX EEPROM already init(%08x)\n", hdr); return (0); } ret = plxEepromWrite(0, 0x00005a00); if (ret) return (ret); ret = plxEepromWrite(0, 0x00000001); if (ret) return (ret); printk("PLX EEPROM init done, reboot system to take effect\n"); return (0); } /******************************************************************************* * PROCEDURE * * NAME: * SUMMARY: plxEepromProgramSettings * * SCOPE: Public * * DESCRIPTION: * Setup PLX eeprom content for NTB or P-to-P * RETURNS: * * NOTES: * */ int plxEepromProgramSettings(const char *string) { PLX_PCIE_SWT *swt = &plxSWT; int ret, setNTB; uint32_t hdr; /* Read signature */ plxEepromRead(0, &hdr); if ((hdr & 0xffff) != 0x005a) { printk("PLX EEPROM not init(%08x)\n", hdr); return (-EFAULT); } /* Double check board name with controller role */ if (!strcmp(string, "NTB")) { setNTB = 1; } else if (!strcmp(string, "P2P")) { setNTB = 0; } else { printk("Invalid setting string \"%s\". Use \"NTB\" or \"P2P\"\n", string); return (-EINVAL); } switch (swt->deviceID) { case 0x8648: /* Reset REG BYTE COUNT field */ ret = plxEepromWrite(0, 0x0000005a); if (ret) return (ret); if (setNTB) { /* Debug Control Register */ plxEepromSetCfgReg(0, 0x1dc, 0xf025048f); plxEepromSetCfgReg(0, 0x1dc, 0xf025040f); } /* Port 0 settings */ plxEepromSetCfgReg(0, 0x228, 0x00000020); plxEepromSetCfgReg(0, 0xb84, 0x93939393); plxEepromSetCfgReg(0, 0xb88, 0x93939393); plxEepromSetCfgReg(0, 0xb94, 0x9f9f9f9f); plxEepromSetCfgReg(0, 0xb98, 0x9f9f9f9f); plxEepromSetCfgReg(0, 0xba4, 0x22222222); plxEepromSetCfgReg(0, 0xb8c, 0x93939393); plxEepromSetCfgReg(0, 0xb90, 0x93939393); plxEepromSetCfgReg(0, 0xba0, 0x85858585); plxEepromSetCfgReg(0, 0xb9c, 0x85858585); /* Port 4 settings */ plxEepromSetCfgReg(4, 0xb84, 0x93939393); plxEepromSetCfgReg(4, 0xb88, 0x93919091); plxEepromSetCfgReg(4, 0xb8c, 0x91909191); plxEepromSetCfgReg(4, 0xb90, 0x90919090); plxEepromSetCfgReg(4, 0xb94, 0x81818181); plxEepromSetCfgReg(4, 0xb98, 0x81818481); plxEepromSetCfgReg(4, 0xb9c, 0x81848181); plxEepromSetCfgReg(4, 0xba0, 0x84818484); /* Port 8 settings */ plxEepromSetCfgReg(8, 0xb84, 0x93939393); plxEepromSetCfgReg(8, 0xb88, 0x93939393); plxEepromSetCfgReg(8, 0xb8c, 0x93939393); plxEepromSetCfgReg(8, 0xb90, 0x93939393); plxEepromSetCfgReg(8, 0xb94, 0x8c8c8c8c); plxEepromSetCfgReg(8, 0xb98, 0x8c8c8c8c); plxEepromSetCfgReg(8, 0xb9c, 0x91919191); plxEepromSetCfgReg(8, 0xba0, 0x91919191); break; case 0x8649: /* Reset REG BYTE COUNT field */ ret = plxEepromWrite(0, 0x0000005a); if (ret) return (ret); if (setNTB) { /* VS0 Upstream (port 0)*/ plxEepromSetCfgReg(0, 0x360, 0x00002014); /* Virtual Switch Debug (upstream) */ plxEepromSetCfgReg(20, 0xa30, 0x03000000); /* PCI Classs Code */ plxEepromSetCfgReg(0, 0x8, 0x068000aa); } /* Port 0 settings */ plxEepromSetCfgReg(0, 0x204, 0x00ff00ff); plxEepromSetCfgReg(0, 0x228, 0x00000020); plxEepromSetCfgReg(0, 0x22c, 0x00f00000); plxEepromSetCfgReg(0, 0xb90, 0x13131313); plxEepromSetCfgReg(0, 0xb9c, 0x15150505); plxEepromSetCfgReg(0, 0xba4, 0x22222222); /* Port 16 settings */ plxEepromSetCfgReg(16, 0xb90, 0x13131313); plxEepromSetCfgReg(16, 0xb9c, 0x0c0c0c0c); /* Port 20 settings */ plxEepromSetCfgReg(20, 0xb90, 0x13131313); plxEepromSetCfgReg(20, 0xb9c, 0x01010101); break; default: printk("Unsupported PLX device %04x\n", swt->deviceID); return (-EINVAL); } printk("PLX EEPROM programming settings done, reboot system to take effect\n"); return (0); } /******************************************************************************* * PROCEDURE * * NAME: plxMapMMIO * SUMMARY: * * SCOPE: Public * * DESCRIPTION: * Read out & ioremap upstream port BAR0 base address * RETURNS: * * NOTES: * */ int plxMapMMIO(void) { PLX_PCIE_SWT *swt = &plxSWT; struct pci_dev *pdev = NULL; phys_addr_t membase = 0; int mapSize = 0, err = 0; uint32_t bl, bu; memset(swt, 0, sizeof(PLX_PCIE_SWT)); err = plxScanDevice(swt); if (err) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESCAN, 0, 0, 0, DG_FORMAT_NONE); return (err); } /* mmio base at upstream port */ switch (swt->deviceID) { case 0x8648: pdev = swt->port[4].pdev; mapSize = 128 * 1024; break; case 0x8649: pdev = swt->port[20].pdev; mapSize = 256 * 1024; break; case 0x8724: pdev = swt->port[0].pdev; mapSize = 256 * 1024; break; default: printk("Unsupported PLX device %04x\n", swt->deviceID); return (-EINVAL); } /* BAR0 */ pci_read_config_dword(pdev, 0x10, &bl); if (bl & 0x4) { #ifdef CONFIG_PHYS_ADDR_T_64BIT /* 64-bit address */ /* BAR1 */ pci_read_config_dword(pdev, 0x14, &bu); membase = bu; membase = membase << 32; #else BUG(); #endif } membase += (bl & 0xfff00000); /* map entire mmio size */ plxMMIO = ioremap(membase, mapSize); if (plxMMIO == NULL) { printk("ioremap failed for MMIO base\n"); return -ENOMEM; } DPRINTK("PLX membase %llx, map mmio %p\n", membase, plxMMIO); return (0); } #define PLX_VIRT_MMIO_WRITE(reg, value) \ { \ PLX_PCIE_SWT *swt = &plxSWT; \ switch (swt->deviceID) \ { \ case 0x8648: \ writel(value, plxMMIO + (64*1024) + reg); \ break; \ case 0x8649: \ writel(value, plxMMIO + (248*1024) + reg); \ break; \ default: \ printk("Unsupported PLX device %04x\n", swt->deviceID); \ break; \ } \ } #define PLX_VIRT_MMIO_READ(reg) \ ({ \ PLX_PCIE_SWT *swt = &plxSWT; \ uint32_t ret; \ switch (swt->deviceID) \ { \ case 0x8648: \ ret = readl(plxMMIO + (64*1024) + reg); \ break; \ case 0x8649: \ ret = readl(plxMMIO + (248*1024) + reg); \ break; \ default: \ printk("Unsupported PLX device %04x\n", swt->deviceID); \ ret = 0xffffffff; \ break; \ } \ ret; \ }) #define PLX_LINK_MMIO_WRITE(reg, value) \ { \ PLX_PCIE_SWT *swt = &plxSWT; \ switch (swt->deviceID) \ { \ case 0x8648: \ writel(value, plxMMIO + (68*1024) + reg); \ break; \ case 0x8649: \ writel(value, plxMMIO + (252*1024) + reg); \ break; \ default: \ printk("Unsupported PLX device %04x\n", swt->deviceID); \ break; \ } \ } #define PLX_LINK_MMIO_READ(reg) \ ({ \ PLX_PCIE_SWT *swt = &plxSWT; \ uint32_t ret; \ switch (swt->deviceID) \ { \ case 0x8648: \ ret = readl(plxMMIO + (68*1024) + reg); \ break; \ case 0x8649: \ ret = readl(plxMMIO + (252*1024) + reg); \ break; \ default: \ printk("Unsupported PLX device %04x\n", swt->deviceID); \ ret = 0xffffffff; \ break; \ } \ ret; \ }) /******************************************************************************* * PROCEDURE * * NAME: plxNTBSetup * SUMMARY: Setup NTB virtual and link interface registers * * SCOPE: Public * * DESCRIPTION: * To enable NTB, we need to setup registers of NT virtual (link) device * 1. BAR Setup Register * 2. NT Bridging Specific Regisgters - Requester ID Translation Lookup Table Entry * 3. BAR Translation Register * RETURNS: * * NOTES: * */ int plxNTBSetup(void) { PLX_PCIE_SWT *swt = &plxSWT; struct pci_dev *pdevUp, *pdevP0; uint32_t pciCommand; int err; unsigned long long MAP_BASE = PLX_NT_MAP_BASE; unsigned long long BAR_WINDOW = ~(PLX_NT_WINDOW_SIZE - 1); unsigned long long PREFETCH_LIMIT = MAP_BASE + PLX_NT_WINDOW_SIZE - 1; memset(swt, 0, sizeof(PLX_PCIE_SWT)); err = plxScanDevice(swt); if (err) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESCAN, 0, 0, 0, DG_FORMAT_NONE); return (err); } pdevP0 = swt->port[0].pdev; switch (swt->deviceID) { case 0x8648: pdevUp = swt->port[4].pdev; break; case 0x8649: pdevUp = swt->port[20].pdev; break; default: printk("Unsupported PLX device %04x\n", swt->deviceID); return (-EINVAL); } #ifndef PLX_NT_BACK_TO_BACK /* We are NTB board or P-to-P * If NTB board, setup registers */ if ((pdevP0->hdr_type == PCI_HEADER_TYPE_NORMAL) && ((pdevP0->class >> 8) == PCI_CLASS_BRIDGE_OTHER) && (pdevP0->pcie_type == PCI_EXP_TYPE_ENDPOINT)) { /***************************************************** * Virtual Interface *****************************************************/ /* BAR2/3 Setup Register, 64Bit, Prefetch */ PLX_VIRT_MMIO_WRITE(0xd4, (((uint32_t) BAR_WINDOW) & 0xffff0000) | 0xc); PLX_VIRT_MMIO_WRITE(0xd8, (uint32_t) (BAR_WINDOW >> 32)); /* Requester ID */ PLX_VIRT_MMIO_WRITE(0xd94, 0x80000000); /* entry_0 */ PLX_VIRT_MMIO_WRITE(0xd98, 0x800000b0); /* dma controller */ PLX_VIRT_MMIO_WRITE(0xd9c, 0x800000b1); /* dma controller */ PLX_VIRT_MMIO_WRITE(0xda0, 0x80000018); /* read operation */ PLX_VIRT_MMIO_WRITE(0xda4, 0x80000400); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xda8, 0x80000401); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xdac, 0x80000402); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xdb0, 0x80000403); /* fc controller */ /* BAR2/3 Register */ PLX_VIRT_MMIO_WRITE(0x18, ((uint32_t) MAP_BASE) & 0xffff0000); PLX_VIRT_MMIO_WRITE(0x1c, ((uint32_t) (MAP_BASE >> 32))); /* BAR2/3 Address Translation Register */ PLX_VIRT_MMIO_WRITE(0xc3c, (uint32_t) PLX_NT_TRANS_ADDR); PLX_VIRT_MMIO_WRITE(0xc40, (uint32_t) (PLX_NT_TRANS_ADDR >> 32)); pciCommand = PLX_VIRT_MMIO_READ(0x04); PLX_VIRT_MMIO_WRITE(0x04, pciCommand | 0x6); /***************************************************** * Link Interface *****************************************************/ /* BAR2/3 Setup Register, 64Bit, Prefetch */ PLX_LINK_MMIO_WRITE(0xe8, (((uint32_t) BAR_WINDOW) & 0xffff0000) | 0xc); PLX_LINK_MMIO_WRITE(0xec, (uint32_t) (BAR_WINDOW >> 32)); /* Requester ID */ PLX_LINK_MMIO_WRITE(0xdb4, 0x00000001); /* entry_0 */ PLX_LINK_MMIO_WRITE(0xdb8, 0x000000b1); /* dma controller */ PLX_LINK_MMIO_WRITE(0xdbc, 0x00000019); /* read operation */ PLX_LINK_MMIO_WRITE(0xdc0, 0x00000501); /* fc controller */ /* BAR2/3 Register */ PLX_LINK_MMIO_WRITE(0x18, ((uint32_t) MAP_BASE) & 0xffff0000); PLX_LINK_MMIO_WRITE(0x1c, ((uint32_t) (MAP_BASE >> 32))); /* BAR2/3 Address Translation Register */ PLX_LINK_MMIO_WRITE(0xc3c, (uint32_t) PLX_NT_TRANS_ADDR); PLX_LINK_MMIO_WRITE(0xc40, (uint32_t) (PLX_NT_TRANS_ADDR >> 32)); pciCommand = PLX_LINK_MMIO_READ(0x04); PLX_LINK_MMIO_WRITE(0x04, pciCommand | 0x6); /* Setup upstream port prefetch window */ pci_write_config_dword(pdevUp, 0x24, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); pci_write_config_dword(pdevUp, 0x28, ((uint32_t) (MAP_BASE >> 32))); pci_write_config_dword(pdevUp, 0x2c, ((uint32_t) (PREFETCH_LIMIT >> 32))); /* PCIe root */ raw_pci_write(0, 0, 0x18, 0x24, 4, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); raw_pci_write(0, 0, 0x18, 0x28, 4, ((uint32_t) (MAP_BASE >> 32))); raw_pci_write(0, 0, 0x18, 0x2c, 4, ((uint32_t) (PREFETCH_LIMIT >> 32))); } else { /* P-to-P board */ /* Setup port 0 prefetch window */ pci_write_config_dword(pdevP0, 0x24, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); pci_write_config_dword(pdevP0, 0x28, ((uint32_t) (MAP_BASE >> 32))); pci_write_config_dword(pdevP0, 0x2c, ((uint32_t) (PREFETCH_LIMIT >> 32))); /* Setup upstream port prefetch window */ pci_write_config_dword(pdevUp, 0x24, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); pci_write_config_dword(pdevUp, 0x28, ((uint32_t) (MAP_BASE >> 32))); pci_write_config_dword(pdevUp, 0x2c, ((uint32_t) (PREFETCH_LIMIT >> 32))); /* PCIe root */ raw_pci_write(0, 0, 0x18, 0x24, 4, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); raw_pci_write(0, 0, 0x18, 0x28, 4, ((uint32_t) (MAP_BASE >> 32))); raw_pci_write(0, 0, 0x18, 0x2c, 4, ((uint32_t) (PREFETCH_LIMIT >> 32))); } #else /* PLX_NT_BACK_TO_BACK */ /***************************************************** * Virtual Interface *****************************************************/ /* BAR2/3 Setup Register, 64Bit, Prefetch */ PLX_VIRT_MMIO_WRITE(0xd4, (((uint32_t) BAR_WINDOW) & 0xffff0000) | 0xc); PLX_VIRT_MMIO_WRITE(0xd8, (uint32_t) (BAR_WINDOW >> 32)); /* Requester ID */ PLX_VIRT_MMIO_WRITE(0xd94, 0x80000000); /* entry_0 */ PLX_VIRT_MMIO_WRITE(0xd98, 0x800000b0); /* dma controller */ PLX_VIRT_MMIO_WRITE(0xd9c, 0x800000b1); /* dma controller */ PLX_VIRT_MMIO_WRITE(0xda0, 0x80000018); /* read operation */ PLX_VIRT_MMIO_WRITE(0xda4, 0x80000400); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xda8, 0x80000401); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xdac, 0x80000402); /* fc controller */ PLX_VIRT_MMIO_WRITE(0xdb0, 0x80000403); /* fc controller */ /* BAR2/3 Register */ PLX_VIRT_MMIO_WRITE(0x18, ((uint32_t) MAP_BASE) & 0xffff0000); PLX_VIRT_MMIO_WRITE(0x1c, ((uint32_t) (MAP_BASE >> 32))); /* BAR2/3 Address Translation Register */ PLX_VIRT_MMIO_WRITE(0xc3c, (uint32_t) MAP_BASE); PLX_VIRT_MMIO_WRITE(0xc40, (uint32_t) (MAP_BASE >> 32)); pciCommand = PLX_VIRT_MMIO_READ(0x04); PLX_VIRT_MMIO_WRITE(0x04, pciCommand | 0x6); /***************************************************** * Link Interface *****************************************************/ /* BAR2/3 Setup Register, 64Bit, Prefetch */ PLX_LINK_MMIO_WRITE(0xe8, (((uint32_t) BAR_WINDOW) & 0xffff0000) | 0xc); PLX_LINK_MMIO_WRITE(0xec, (uint32_t) (BAR_WINDOW >> 32)); /* Requester ID */ PLX_LINK_MMIO_WRITE(0xdb4, 0x00000001); /* entry_0 */ PLX_LINK_MMIO_WRITE(0xdb8, 0x000000b1); /* dma controller */ PLX_LINK_MMIO_WRITE(0xdbc, 0x00000019); /* read operation */ PLX_LINK_MMIO_WRITE(0xdc0, 0x00000501); /* fc controller */ /* BAR2/3 Register */ PLX_LINK_MMIO_WRITE(0x18, ((uint32_t) MAP_BASE) & 0xffff0000); PLX_LINK_MMIO_WRITE(0x1c, ((uint32_t) (MAP_BASE >> 32))); /* BAR2/3 Address Translation Register */ PLX_LINK_MMIO_WRITE(0xc3c, (uint32_t) PLX_NT_TRANS_ADDR); PLX_LINK_MMIO_WRITE(0xc40, (uint32_t) (PLX_NT_TRANS_ADDR >> 32)); pciCommand = PLX_LINK_MMIO_READ(0x04); PLX_LINK_MMIO_WRITE(0x04, pciCommand | 0x6); /* Setup upstream port prefetch window */ pci_write_config_dword(pdevUp, 0x24, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); pci_write_config_dword(pdevUp, 0x28, ((uint32_t) (MAP_BASE >> 32))); pci_write_config_dword(pdevUp, 0x2c, ((uint32_t) (PREFETCH_LIMIT >> 32))); /* PCIe root */ raw_pci_write(0, 0, 0x18, 0x24, 4, (((uint32_t) PREFETCH_LIMIT) & 0xfff00000) | ((uint32_t) (MAP_BASE >> 16) & 0x0000fff0) | 0x00010001); raw_pci_write(0, 0, 0x18, 0x28, 4, ((uint32_t) (MAP_BASE >> 32))); raw_pci_write(0, 0, 0x18, 0x2c, 4, ((uint32_t) (PREFETCH_LIMIT >> 32))); #endif return (0); } #ifdef PLX_NT_BACK_TO_BACK void plxNTBSetTransAddress(unsigned long long address) { PLX_LINK_MMIO_WRITE(0xc3c, (uint32_t) address); PLX_LINK_MMIO_WRITE(0xc40, (uint32_t) (address >> 32)); } EXPORT_SYMBOL(plxNTBSetTransAddress); #endif void plxMMIORead(uint32_t cursor, uint32_t reg) { volatile uint32_t value; cursor = cursor * 1024; value = readl(plxMMIO + cursor + reg); printk("cursor %dKB, register(%xh) = %08x\n", cursor/1024, reg, value); } void plxMMIOWrite(uint32_t cursor, uint32_t reg, uint32_t value) { cursor = cursor * 1024; writel(value, plxMMIO + cursor + reg); } int plxShowErrorStat(void) { PLX_PCIE_SWT *swt = &plxSWT; PLX_PORT *port; struct pci_dev *pdev; uint16_t pcists, pciedsts; uint32_t aerues, aeruem; uint32_t aerces, aercem; int err; memset(swt, 0, sizeof(PLX_PCIE_SWT)); err = plxScanDevice(swt); if (err) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESCAN, 0, 0, 0, DG_FORMAT_NONE); return (err); } for (port = swt->port; port < &swt->port[MAX_PLX_PORT]; port++) { if (port->flag != PLX_PORT_USED) continue; pdev = port->pdev; /* PCI Status Register */ pci_read_config_word(pdev, 0x6, &pcists); printk("=== PLX port[%d]: PCI Status %s ===\n", port->portNum, pcists & PCISTS_ERROR_MASK ? "Error":"OK"); if (pcists & PCISTS_MDPED) printk("Master Data Parity Error Detected\n"); if (pcists & PCISTS_SSE) printk("Signalled System Error\n"); if (pcists & PCISTS_SSE) printk("Detected Parity Error\n"); /* PCI-E Device Status */ pci_read_config_word(port->pdev, PLX_PCIEDSTS, &pciedsts); printk("=== PLX port[%d]: PCI-E Device Status %s ===\n", port->portNum, pciedsts & PCIESTS_ERROR_MASK ? "Error":"OK"); if (pciedsts & PCIESTS_CED) printk("Correctable Error Detected\n"); if (pciedsts & PCIESTS_NFED) printk("Non-Fatal Error Detected\n"); if (pciedsts & PCIESTS_FED) printk("Fatal Error Detected\n"); if (pciedsts & PCIESTS_URD) printk("Unsupported Request Detected\n"); /* AER Uncorrectable Error Status */ pci_read_config_dword(pdev, 0xfb8, &aerues); pci_read_config_dword(pdev, 0xfbc, &aeruem); if (aerues & ~aeruem) { printk("=== PLX port[%d]: AER Uncorrectable Error ===\n", port->portNum); } if (aerues & AERUES_DLPERR & ~aeruem) printk("Data Link Protocol Error\n"); if (aerues & AERUES_SDOENERR & ~aeruem) printk("Surprise Down Error\n"); if (aerues & AERUES_POISONED & ~aeruem) printk("Poisoned TLP\n"); if (aerues & AERUES_FCPERR & ~aeruem) printk("Flow Control Protocol Error\n"); if (aerues & AERUES_CTS & ~aeruem) printk("Completion Timeout Status\n"); if (aerues & AERUES_CAS & ~aeruem) printk("Completion Abort Status\n"); if (aerues & AERUES_UECOMP & ~aeruem) printk("Unexpected Completion Status\n"); if (aerues & AERUES_RCVOVR & ~aeruem) printk("Receiver Overflow Status\n"); if (aerues & AERUES_MALFORMED & ~aeruem) printk("Malformed TLP\n"); if (aerues & AERUES_ECRC & ~aeruem) printk("ECRC Status\n"); if (aerues & AERUES_UR & ~aeruem) printk("UR Status\n"); /* AER Correctable Error Status */ pci_read_config_dword(pdev, 0xfc4, &aerces); pci_read_config_dword(pdev, 0xfc8, &aercem); if (aerces & ~aercem) { printk("=== PLX port[%d]: AER Correctable Error ===\n", port->portNum); } if (aerces & AERCES_RCVERR & ~aercem) printk("Receiver Error\n"); if (aerces & AERCES_BADTLP & ~aercem) printk("Bad TLP\n"); if (aerces & AERCES_BADDLLP & ~aercem) printk("Bad DLLP\n"); if (aerces & AERCES_RPLYROVR & ~aercem) printk("Reply Number Rollover\n"); if (aerces & AERCES_RPLYTO & ~aercem) printk("Replay Timer Time-Out\n"); if (aerces & AERCES_ADVISORYNF & ~aercem) printk("Advisory Non-Fatal Error\n"); } return (0); } int plxClearErrorStat(void) { PLX_PCIE_SWT *swt = &plxSWT; PLX_PORT *port; struct pci_dev *pdev; uint16_t pcists, pciedsts; uint32_t aerues, aeruem; uint32_t aerces, aercem; int err; memset(swt, 0, sizeof(PLX_PCIE_SWT)); err = plxScanDevice(swt); if (err) { commReportError(MEC_PCIE_SWITCH, PCIE_SWITCH_ITEM_CFG, PCIE_SWITCH_CODE_CFG, PCIE_SWITCH_MSG_ESCAN, 0, 0, 0, DG_FORMAT_NONE); return (err); } for (port = swt->port; port < &swt->port[MAX_PLX_PORT]; port++) { if (port->flag != PLX_PORT_USED) continue; pdev = port->pdev; /* PCI Status Register */ pci_read_config_word(pdev, 0x6, &pcists); if (pcists & PCISTS_ERROR_MASK) { printk("Clear PLX port[%d]: PCI Status %04x error\n", port->portNum, pcists); pci_write_config_word(pdev, 0x6, pcists); } /* PCI-E Device Status */ pci_read_config_word(port->pdev, PLX_PCIEDSTS, &pciedsts); if (pciedsts & PCIESTS_ERROR_MASK) { printk("Clear PLX port[%d]: PCI-E Device Status %04x error\n", port->portNum, pciedsts); pci_write_config_word(pdev, PLX_PCIEDSTS, pciedsts); } /* AER Uncorrectable Error Status */ pci_read_config_dword(pdev, 0xfb8, &aerues); pci_read_config_dword(pdev, 0xfbc, &aeruem); if (aerues & ~aeruem) { printk("Clear PLX port[%d]: AER Uncorrectable Error %08x/%08x\n", port->portNum, aerues, aeruem); pci_write_config_dword(pdev, 0xfb8, aerues); } /* AER Correctable Error Status */ pci_read_config_dword(pdev, 0xfc4, &aerces); pci_read_config_dword(pdev, 0xfc8, &aercem); if (aerces & ~aercem) { printk("Clear PLX port[%d]: AER Correctable Error %08x/%08x\n", port->portNum, aerces, aercem); pci_write_config_dword(pdev, 0xfc4, aerces); } } return (0); } void plxGetDeviceID(uint16_t *id) { PLX_PCIE_SWT *swt = &plxSWT; *id = swt->deviceID; } int plxProgramEeprom(uint8_t *buf, uint32_t size) { int i, ret = 0; uint8_t *__buf = NULL; uint32_t __size = size; uint32_t data = 0; uint32_t data_back = 0; if (size & 0x3) { /* align up by 4 */ __size = (size + 3) & 0xfffffffc; } __buf = kmalloc(__size, GFP_KERNEL); if (__buf == NULL) { printk("%s Out of Memory\n", __func__); return (-ENOMEM); } /* Set aligned up tail to 0xff */ memset(__buf, 0xff, __size); memcpy(__buf, buf, size); for (i = 0; i < __size; i += 4) { memcpy(&data, &__buf[i], sizeof(uint32_t)); if ((ret = plxEepromWrite(i, data))) break; /* Validation check */ data_back = 0; if ((ret = plxEepromRead(i, &data_back))) break; if (data_back != data){ printk("[PLX] Fail to Write Data to EERPOM obs:[0x%x], exp:[0x%x]!\n",data_back, data); ret = (-EINVAL); break; } } kfree(__buf); return (ret); } EXPORT_SYMBOL(plxProgramEeprom); static int __init plxInit( void ) { int ret; ret = plxMapMMIO(); if(ret) return 0; #ifdef CTRLER_PIKESPEAK plxNTBSetup(); #endif plxClearErrorStat(); return 0; } static void __exit plxExit( void ) { if( plxMMIO ) iounmap( plxMMIO ); } module_init( plxInit ); module_exit( plxExit ); MODULE_AUTHOR( "merck.hung@netapp.com" ); MODULE_DESCRIPTION( "NetApp PLX Diag Code" ); MODULE_LICENSE( "GPL" ); MODULE_VERSION( "0.1" );