/******************************************************************************* NAME arbelIbChip.c SUMMARY %description% VERSION %version: BDC~33 % UPDATE DATE %date_modified: Wed Apr 28 12:07:47 2010 % PROGRAMMER %created_by: rratnaku % Copyright 2005-2008 LSI Corporation. All Rights Reserved. DESCRIPTION: Contains routines specific to do I/O directly to 25208 chip registers on the PCI bus. *******************************************************************************/ //#include "vkiWrap.h" //#include "ibChip.h" #include /* Required for linux version of stopIbChip only */ #include #include #include //#include "srpniUpRefs.h" //extern upReference_t upReference; #define ULONG unsigned long #define INT32 int32_t /* Variables used for setting HCA LEDs volatile ULONG led1EnableData = 0; volatile ULONG led2EnableData = 0; */ //volatile ULONG ibHcaLedTaskRunning = 0; ULONG gpioEnableOffset = 0xf0064; // 0xf00d4; ULONG gpioDisableOffset = 0xf005c; // 0xf00dc; #define ibLED_S struct ibLED_Structure ibLED_S { struct task_struct *task; phys_addr_t base; volatile ULONG *enableAddr; volatile ULONG *disableAddr; ULONG enableData; int ms; }; ibLED_S ibLED; //static int ibResetHookCalled = 0; //void (*srp_stop_cmd)(void); //void stopInfiniBandTasks( void ); //extern unsigned int getIbChipInitFlag(int devIndex); extern int pciConfigWriteDWord(uint16_t bus, uint8_t dev, uint8_t func, uint16_t offset, uint32_t value); #define PCIV_MELLANOX 0x15b3 #define PCID_INFINIHOST_CONNECTX 0x673c //#define PCID_INFINIHOST_ARBEL inline ULONG get26428BaseAddr( int devIndex, int ignoreError ) { struct pci_dev *pci_dev = NULL; uint32_t barSize; phys_addr_t memBase=0, base = 0; // // printk("%s: enter\n", __FUNCTION__); pci_dev = pci_get_device(PCIV_MELLANOX, PCID_INFINIHOST_CONNECTX, pci_dev); if (!pci_dev) { printk("No mellanox IB device ???\n"); return 0; } memBase = pci_resource_start(pci_dev, 0); barSize = pci_resource_len(pci_dev, 0); // printk("%s: Base=0x%016llx, Size=0x%08x\n", __FUNCTION__, memBase, barSize); /* map entire mmio size */ base = (phys_addr_t)ioremap(memBase, barSize); if (( base == 0 ) && (!ignoreError)) { printk("Could not find the base address for IB device Index passed - %d", devIndex); } // printk("%s: iomap base=0x%016llx\n", __FUNCTION__, base); return base; } /******************************************************************************* The IB HCA Leds are controlled by the firmware that runs on the HCA board itself. The LEDs are set based on the actual link state of both ports. This implies that a single register write to set or reset the LEDs isn't good enough since the LEDs will be reset to the link state a few milliseconds after the register is written. In order to force the LEDs to the desired state the register must be continually rewritten with the desired value. To further complicate things the loop must be fairly tight so that the LED appears to remain in the desired state. Two GPIO registers are used to control the states of the LEDs. The 'enable' register is at offset 0xf00d4 and the 'disable' register is at offset 0xf00dc. Both registers use bits 24-27 to control the LED state. Writing a 1 to the 'enable' register causes a LED to turn on, writing a 1 to the 'disable' register causes the LED to turn off. Writing to both registers allows us to override the link status whether or not IB cables are connected. A very tight loop will prevent other processes from running and eventually cause the watchdog timer to fire. To work around this, we spawn a VKI task that sets the LED enable/disable registers, then calls VKI_PREEMPT, which will allow other VKI tasks to run when needed. This could cause problems if another task starves this one, but so far that has not been observed. The task will end and delete itself when stopIbHcaLedTask() is called. This will cause the LEDs to return to reporting the actual link up/down status. The current implementation only allows one LED state to be modified at a time. Assume the controller is "right side up" i.e. in the bottom slot of the controller module. Port 1 is on the left side. Port 2 is on the right. Logical Link up - Amber LED (on top) Physical Link up - Green LED (on bottom) The LEDS are numbered as follows: HCA1 HCA2 LED Port 1 Port 2 Port 1 Port 2 Color LED Num LED Num LED Num LED Num Amber 2 0 6 4 Green 3 1 7 5 Values for the ports are: Hex Value Port 2 Amber LED - 0x08000000 Port 2 Green LED - 0x04000000 Port 1 Amber LED - 0x02000000 Port 1 Green LED - 0x01000000 We'd prefer to do arithmetic on the register to set the bits correctly however the 25208 register values for the LEDs aren't setup for simple arithmetic, so an array is used instead. HCA Leds are controlled by bits 24-27 of the GPIO data registers 0xf00d4 and 0xf00dc of the 25208 chip. ********************************************************************************/ int ibLED_TaskRun(void *td) { ibLED_S *pd=(ibLED_S *)td; ULONG data; /* Read the register value first. Get the device index for the 25208 chip. Use the device index to get the BAR 0 base address. Add in the register offset to the base address for the GPIO address. */ /* This is for the IB card in Slot-1 */ pd->base = get26428BaseAddr(0, 1); if (! pd->base) { printk("IB device not found!!!\n"); return 0; } pd->enableAddr = (ULONG*) (pd->base + gpioEnableOffset); pd->disableAddr = (ULONG*) (pd->base + gpioDisableOffset); /* Loop as fast as possible, continuously setting the bits to enable/disable the LEDs. This allows us to set the LED states faster than the HCA FW can reset the LEDs based on the current link up/down status. */ while (! kthread_should_stop()) { if (pd->base) { /* Port 1 LED Enable */ data = *pd->enableAddr; data = (data & 0xf0ffffff) | pd->enableData; *pd->enableAddr = data; /* Port 1 LED Disable (set the inverse of Enable bits) */ data = *pd->disableAddr; data = (data & 0xf0ffffff) | (pd->enableData ^ 0x0f000000); *pd->disableAddr = data; } msleep(pd->ms); } return 0; } /* Calling this will set/clear the appropriate bits for the LED control registers. * If will also spawn 'ibLED_TaskRun' if it has not been started yet. * LEDs 0-3 are on the first HCA; 4-7 are on the second HCA. * State 0 = OFF; State 1 = ON */ int ibLED_SetOp(int ledNum, int state, int ms) { ULONG byteValue[4] = { 0x08000000, /* 0000 1000 0000 0000 0000 0000 0000 0000 */ 0x04000000, /* 0000 0100 0000 0000 0000 0000 0000 0000 */ 0x02000000, /* 0000 0010 0000 0000 0000 0000 0000 0000 */ 0x01000000 /* 0000 0001 0000 0000 0000 0000 0000 0000 */ }; if (ledNum < 4) { if(state) ibLED.enableData |= byteValue[ledNum]; else ibLED.enableData &= ~byteValue[ledNum]; } if(ms) ibLED.ms=ms; return 0; } /******************************************************************************* * PROCEDURE * * NAME: ibLED_Start * SUMMARY: Entry point to start stress test on IB LED * * SCOPE: Public * * DESCRIPTION: * * RETURNS: * * NOTES: * */ int ibLED_Start(void) { if(ibLED.task != NULL) return 0; ibLED.task = kthread_run(ibLED_TaskRun, &ibLED, "ibLEDTask"); if (IS_ERR(ibLED.task)) { printk("Create IB LED Task Failed!!!\n"); /* TODO */ return (-EFAULT); } return (0); } void ibLED_Stop(void) { int ret; if(ibLED.task != NULL) { ret = kthread_stop(ibLED.task); ibLED.task=NULL; } } void ibLED_Init(void) { ibLED.base =0; ibLED.enableAddr=NULL; ibLED.disableAddr=NULL; ibLED.enableData=0; ibLED.ms=5; ibLED.task = NULL; } MODULE_AUTHOR( "merck.hung@netapp.com" ); MODULE_DESCRIPTION( "NetApp Infiniband Diag Code" ); MODULE_LICENSE( "GPL" ); MODULE_VERSION( "0.1" );