📄 cpqfctscontrol.c
字号:
/* Copyright 2000, Compaq Computer Corporation * Fibre Channel Host Bus Adapter * 64-bit, 66MHz PCI * Originally developed and tested on: * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... * SP# P225CXCBFIEL6T, Rev XC * SP# 161290-001, Rev XD * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * Written by Don Zimmerman*//* These functions control the host bus adapter (HBA) hardware. The main chip control takes place in the interrupt handler where we process the IMQ (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link events and state information to the driver. The Single Frame Queue (SFQ) buffers incoming FC frames for processing by the driver. References to "TL/TS UG" are for: "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed. Hewlitt Packard Manual Part Number 5968-1083E.*/#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))#include <linux/blk.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ioport.h> // request_region() prototype#include <linux/sched.h>#include <linux/malloc.h> // need "kfree" for ext. S/G pages#include <linux/types.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/unistd.h>#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O#include <asm/irq.h>#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)#include <asm/spinlock.h>#else#include <linux/spinlock.h>#endif#include "sd.h"#include "hosts.h" // Scsi_Host definition for INT handler#include "cpqfcTSchip.h"#include "cpqfcTSstructs.h"//#define IMQ_DEBUG 1static void fcParseLinkStatusCounters(TACHYON * fcChip);static void CpqTsGetSFQEntry(TACHYON * fcChip, USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); // Note special requirements for Q alignment! (TL/TS UG pg. 190)// We place critical index pointers at end of QUE elements to assist// in non-symbolic (i.e. memory dump) debugging// opcode defines placement of Queues (e.g. local/external RAM)int CpqTsCreateTachLiteQues( void* pHBA, int opcode){ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; PTACHYON fcChip = &cpqfcHBAdata->fcChip; int iStatus=0; unsigned long ulAddr; // NOTE! fcMemManager() will return system virtual addresses. // System (kernel) virtual addresses, though non-paged, still // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's // DMA use. ENTER("CreateTachLiteQues"); // Allocate primary EXCHANGES array... printk("Allocating %u for %u Exchanges ", (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL ); printk("@ %p\n", fcChip->Exchanges); if( fcChip->Exchanges == NULL ) // fatal error!! { printk("kmalloc failure on Exchanges: fatal error\n"); return -1; } // zero out the entire EXCHANGE space memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES)); printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL ); printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!! { printk("kmalloc failure on fc Link Que: fatal error\n"); return -1; } // zero out the entire EXCHANGE space memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); // Verify that basic Tach I/O registers are not NULL if( !fcChip->Registers.ReMapMemBase ) { printk("HBA base address NULL: fatal error\n"); return -1; } // Initialize the fcMemManager memory pairs (stores allocated/aligned // pairs for future freeing) memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem)); // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes) fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L ); if( !fcChip->ERQ ) { printk("kmalloc/alignment failure on ERQ: fatal error\n"); return -1; } fcChip->ERQ->length = ERQ_LEN-1; ulAddr = virt_to_bus( fcChip->ERQ);#if BITS_PER_LONG > 32 if( (ulAddr >> 32) ) { printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n", (void*)ulAddr); return -1; // failed }#endif fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference // Allocate Tach's Inbound Message Queue (32 bytes per entry) fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L ); if( !fcChip->IMQ ) { printk("kmalloc/alignment failure on IMQ: fatal error\n"); return -1; } fcChip->IMQ->length = IMQ_LEN-1; ulAddr = virt_to_bus( fcChip->IMQ);#if BITS_PER_LONG > 32 if( (ulAddr >> 32) ) { printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", (void*)ulAddr); return -1; // failed }#endif fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference // Allocate Tach's Single Frame Queue (64 bytes per entry) fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L ); if( !fcChip->SFQ ) { printk("kmalloc/alignment failure on SFQ: fatal error\n"); return -1; } fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries - // min. 32; max. 4096 (0xffff)] ulAddr = virt_to_bus( fcChip->SFQ);#if BITS_PER_LONG > 32 if( (ulAddr >> 32) ) { printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", (void*)ulAddr); return -1; // failed }#endif fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference // Allocate SCSI Exchange State Table; aligned nearest @sizeof // power-of-2 boundary // LIVE DANGEROUSLY! Assume the boundary for SEST mem will // be on physical page (e.g. 4k) boundary. printk("Allocating %u for TachSEST for %u Exchanges\n", (ULONG)sizeof(TachSEST), TACH_SEST_LEN); fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], sizeof(TachSEST), 4, 0L );// sizeof(TachSEST), 64*TACH_SEST_LEN, 0L ); if( !fcChip->SEST ) { printk("kmalloc/alignment failure on SEST: fatal error\n"); return -1; } fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one // (TL/TS UG, pg 153) ulAddr = virt_to_bus( fcChip->SEST);#if BITS_PER_LONG > 32 if( (ulAddr >> 32) ) { printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", (void*)ulAddr); return -1; // failed }#endif fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference // Now that structures are defined, // fill in Tachyon chip registers... // EEEEEEEE EXCHANGE REQUEST QUEUE writel( fcChip->ERQ->base, (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); writel( fcChip->ERQ->length, (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH)); fcChip->ERQ->producerIndex = 0L; writel( fcChip->ERQ->producerIndex, (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX)); // NOTE! write consumer index last, since the write // causes Tachyon to process the other registers ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex); // NOTE! Tachyon DMAs to the ERQ consumer Index host // address; must be correctly aligned writel( (ULONG)ulAddr, (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR)); // IIIIIIIIIIIII INBOUND MESSAGE QUEUE // Tell Tachyon where the Que starts // set the Host's pointer for Tachyon to access printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base ); writel( fcChip->IMQ->base, (fcChip->Registers.ReMapMemBase + IMQ_BASE)); writel( fcChip->IMQ->length, (fcChip->Registers.ReMapMemBase + IMQ_LENGTH)); writel( fcChip->IMQ->consumerIndex, (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); // NOTE: TachLite DMAs to the producerIndex host address // must be correctly aligned with address bits 1-0 cleared // Writing the BASE register clears the PI register, so write it last ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex);#if BITS_PER_LONG > 32 if( (ulAddr >> 32) ) { printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", (void*)ulAddr); return -1; // failed }#endif//#if DBG printk(" PI %Xh\n", (ULONG)ulAddr );//#endif writel( (ULONG)ulAddr, (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX)); // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE // Tell TachLite where the Que starts writel( fcChip->SFQ->base, (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE)); writel( fcChip->SFQ->length, (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH)); // tell TachLite where SEST table is & how long writel( fcChip->SEST->base, (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE)); printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n", fcChip->SEST, fcChip->SEST->base, fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); writel( fcChip->SEST->length, (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH)); writel( (TL_EXT_SG_PAGE_COUNT-1), (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE)); LEAVE("CreateTachLiteQues"); return iStatus;}// function to return TachLite to Power On state// 1st - reset tachyon ('SOFT' reset)// others - futureint CpqTsResetTachLite(void *pHBA, int type){ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; PTACHYON fcChip = &cpqfcHBAdata->fcChip; ULONG ulBuff, i; int ret_status=0; // def. success ENTER("ResetTach"); switch(type) { case CLEAR_FCPORTS: // in case he was running previously, mask Tach's interrupt writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); // de-allocate mem for any Logged in ports // (e.g., our module is unloading) // search the forward linked list, de-allocating // the memory we allocated when the port was initially logged in { PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort; PFC_LOGGEDIN_PORT ptr;// printk("checking for allocated LoggedInPorts...\n"); while( pLoggedInPort ) { ptr = pLoggedInPort; pLoggedInPort = ptr->pNextPort;// printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n",// ptr, ptr->port_id); kfree( ptr ); } } // (continue resetting hardware...) case 1: // RESTART Tachyon (power-up state) // in case he was running previously, mask Tach's interrupt writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); // turn OFF laser (NOTE: laser is turned // off during reset, because GPIO4 is cleared // to 0 by reset action - see TLUM, sec 7.22) // However, CPQ 64-bit HBAs have a "health // circuit" which keeps laser ON for a brief // period after it is turned off ( < 1s) fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0); // soft reset timing constraints require: // 1. set RST to 1 // 2. read SOFTRST register // (128 times per R. Callison code) // 3. clear PCI ints // 4. clear RST to 0 writel( 0xff000001L, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); for( i=0; i<128; i++) ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -