qib7322.c revision a734c64bff58bda2fa48c2795453e092167b0ff7
/*
* Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
*
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <ipxe/infiniband.h>
#include <ipxe/pcibackup.h>
#include "qib7322.h"
/**
* @file
*
* QLogic QIB7322 Infiniband HCA
*
*/
/** A QIB7322 send buffer set */
struct qib7322_send_buffers {
/** Offset within register space of the first send buffer */
unsigned long base;
/** Send buffer size */
unsigned int size;
/** Index of first send buffer */
unsigned int start;
/** Number of send buffers
*
* Must be a power of two.
*/
unsigned int count;
/** Send buffer availability producer counter */
unsigned int prod;
/** Send buffer availability consumer counter */
unsigned int cons;
/** Send buffer availability */
};
/** A QIB7322 send work queue */
struct qib7322_send_work_queue {
/** Send buffer set */
struct qib7322_send_buffers *send_bufs;
/** Send buffer usage */
/** Producer index */
unsigned int prod;
/** Consumer index */
unsigned int cons;
};
/** A QIB7322 receive work queue */
struct qib7322_recv_work_queue {
/** Receive header ring */
void *header;
/** Receive header producer offset (written by hardware) */
struct QIB_7322_scalar header_prod;
/** Receive header consumer offset */
unsigned int header_cons;
/** Offset within register space of the eager array */
unsigned long eager_array;
/** Number of entries in eager array */
unsigned int eager_entries;
/** Eager array producer index */
unsigned int eager_prod;
/** Eager array consumer index */
unsigned int eager_cons;
};
/** A QIB7322 HCA */
struct qib7322 {
/** Registers */
void *regs;
/** In-use contexts */
/** Send work queues */
/** Receive work queues */
/** Send buffer availability (reported by hardware) */
struct QIB_7322_SendBufAvail *sendbufavail;
/** Small send buffers */
struct qib7322_send_buffers *send_bufs_small;
/** VL15 port 0 send buffers */
struct qib7322_send_buffers *send_bufs_vl15_port0;
/** VL15 port 1 send buffers */
struct qib7322_send_buffers *send_bufs_vl15_port1;
/** I2C bit-bashing interface */
struct i2c_bit_basher i2c;
/** I2C serial EEPROM */
struct i2c_device eeprom;
/** Base GUID */
/** Infiniband devices */
};
/***************************************************************************
*
* QIB7322 register access
*
***************************************************************************
*
* This card requires atomic 64-bit accesses. Strange things happen
* if you try to use 32-bit accesses; sometimes they work, sometimes
* they don't, sometimes you get random data.
*
* These accessors use the "movq" MMX instruction, and so won't work
* on really old Pentiums (which won't have PCIe anyway, so this is
* something of a moot point).
*/
/**
* Read QIB7322 qword register
*
* @v qib7322 QIB7322 device
* @v dwords Register buffer to read into
* @v offset Register offset
*/
unsigned long offset ) {
"movq %%mm0, (%0)\n\t"
DBGIO ( "[%08lx] => %08x%08x\n",
}
/**
* Write QIB7322 qword register
*
* @v qib7322 QIB7322 device
* @v dwords Register buffer to write
* @v offset Register offset
*/
unsigned long offset ) {
DBGIO ( "[%08lx] <= %08x%08x\n",
"movq %%mm0, (%1)\n\t"
}
/**
* Write QIB7322 dword register
*
* @v qib7322 QIB7322 device
* @v dword Value to write
* @v offset Register offset
*/
unsigned long offset ) {
}
/***************************************************************************
*
* Link state management
*
***************************************************************************
*/
/**
* Textual representation of link state
*
* @v link_state Link state
* @ret link_text Link state text
*/
static const char * qib7322_link_state_text ( unsigned int link_state ) {
switch ( link_state ) {
case QIB7322_LINK_STATE_DOWN: return "DOWN";
case QIB7322_LINK_STATE_INIT: return "INIT";
case QIB7322_LINK_STATE_ARM: return "ARM";
case QIB7322_LINK_STATE_ACTIVE: return "ACTIVE";
case QIB7322_LINK_STATE_ACT_DEFER: return "ACT_DEFER";
default: return "UNKNOWN";
}
}
/**
* Handle link state change
*
* @v qib7322 QIB7322 device
*/
struct QIB_7322_IBCStatusA_0 ibcstatusa;
struct QIB_7322_EXTCtrl extctrl;
unsigned int link_training_state;
unsigned int link_state;
unsigned int link_width;
unsigned int link_speed;
unsigned int link_speed_qdr;
unsigned int green;
unsigned int yellow;
/* Read link state */
/* Set LEDs according to link state */
if ( port == 0 ) {
} else {
}
/* Notify Infiniband core of link state change */
}
/**
* Wait for link state change to take effect
*
* @v ibdev Infiniband device
* @v new_link_state Expected link state
* @ret rc Return status code
*/
unsigned int new_link_state ) {
struct QIB_7322_IBCStatusA_0 ibcstatusa;
unsigned int link_state;
unsigned int i;
for ( i = 0 ; i < QIB7322_LINK_STATE_MAX_WAIT_US ; i++ ) {
if ( link_state == new_link_state )
return 0;
udelay ( 1 );
}
return -ETIMEDOUT;
}
/**
* Set port information
*
* @v ibdev Infiniband device
* @v mad Set port information MAD
*/
struct QIB_7322_IBCCtrlA_0 ibcctrla;
unsigned int port_state;
unsigned int link_state;
/* Set new link state */
if ( port_state ) {
link_state );
/* Wait for link state change to take effect. Ignore
* errors; the current link state will be returned via
* the GetResponse MAD.
*/
}
/* Detect and report link state change */
return 0;
}
/**
* Set partition key table
*
* @v ibdev Infiniband device
* @v mad Set partition key table MAD
*/
/* Nothing to do */
return 0;
}
/***************************************************************************
*
* Context allocation
*
***************************************************************************
*/
/**
* Allocate a context and set queue pair number
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @ret rc Return status code
*/
struct ib_queue_pair *qp ) {
unsigned int ctx;
return 0;
}
}
return -ENOENT;
}
/**
* Get queue pair context number
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @ret ctx Context index
*/
struct ib_queue_pair *qp ) {
}
/**
* Free a context
*
* @v qib7322 QIB7322 device
* @v ctx Context index
*/
struct ib_queue_pair *qp ) {
}
/***************************************************************************
*
* Send datapath
*
***************************************************************************
*/
/** Send buffer toggle bit
*
* We encode send buffers as 15 bits of send buffer index plus a
* single bit which should match the "check" bit in the SendBufAvail
* array.
*/
#define QIB7322_SEND_BUF_TOGGLE 0x8000
/**
* Create send buffer set
*
* @v qib7322 QIB7322 device
* @v base Send buffer base offset
* @v size Send buffer size
* @v start Index of first send buffer
* @v count Number of send buffers
* @ret send_bufs Send buffer set
*/
static struct qib7322_send_buffers *
unsigned int count ) {
struct qib7322_send_buffers *send_bufs;
unsigned int i;
/* Allocate send buffer set */
if ( ! send_bufs )
return NULL;
/* Populate send buffer set */
for ( i = 0 ; i < count ; i++ )
return send_bufs;
}
/**
* Destroy send buffer set
*
* @v qib7322 QIB7322 device
* @v send_bufs Send buffer set
*/
static void
struct qib7322_send_buffers *send_bufs ) {
}
/**
* Allocate a send buffer
*
* @v qib7322 QIB7322 device
* @v send_bufs Send buffer set
* @ret send_buf Send buffer, or negative error
*/
struct qib7322_send_buffers *send_bufs ) {
unsigned int used;
unsigned int mask;
unsigned int send_buf;
return -ENOBUFS;
}
return send_buf;
}
/**
* Free a send buffer
*
* @v qib7322 QIB7322 device
* @v send_bufs Send buffer set
* @v send_buf Send buffer
*/
struct qib7322_send_buffers *send_bufs,
unsigned int send_buf ) {
unsigned int mask;
}
/**
* Check to see if send buffer is in use
*
* @v qib7322 QIB7322 device
* @v send_buf Send buffer
* @ret in_use Send buffer is in use
*/
unsigned int send_buf ) {
unsigned int send_idx;
unsigned int send_check;
unsigned int inusecheck;
unsigned int inuse;
unsigned int check;
}
/**
* Calculate starting offset for send buffer
*
* @v qib7322 QIB7322 device
* @v send_buf Send buffer
* @ret offset Starting offset
*/
static unsigned long
struct qib7322_send_buffers *send_bufs,
unsigned int send_buf ) {
unsigned int index;
}
/**
* Create send work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
/* Select send buffer set */
if ( port == 0 ) {
} else {
}
} else {
}
/* Allocate space for send buffer usage list */
sizeof ( qib7322_wq->used[0] ) );
if ( ! qib7322_wq->used )
return -ENOMEM;
/* Reset work queue */
qib7322_wq->prod = 0;
qib7322_wq->cons = 0;
return 0;
}
/**
* Destroy send work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
}
/**
* Initialise send datapath
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
struct QIB_7322_SendBufBase sendbufbase;
struct QIB_7322_SendCtrl sendctrl;
struct QIB_7322_SendCtrl_0 sendctrlp;
unsigned long baseaddr_smallpio;
unsigned long baseaddr_largepio;
unsigned long baseaddr_vl15_port0;
unsigned long baseaddr_vl15_port1;
int rc;
/* Create send buffer sets */
if ( ! qib7322->send_bufs_small ) {
}
if ( ! qib7322->send_bufs_vl15_port0 ) {
}
if ( ! qib7322->send_bufs_vl15_port1 ) {
}
/* Allocate space for the SendBufAvail array */
if ( ! qib7322->sendbufavail ) {
goto err_alloc_sendbufavail;
}
/* Program SendBufAvailAddr into the hardware */
/* Enable sending */
/* Enable DMA of SendBufAvail */
return 0;
return rc;
}
/**
* Shut down send datapath
*
* @v qib7322 QIB7322 device
*/
struct QIB_7322_SendCtrl sendctrl;
/* Disable sending and DMA of SendBufAvail */
mb();
/* Ensure hardware has seen this disable */
}
/***************************************************************************
*
* Receive datapath
*
***************************************************************************
*/
/**
* Create receive work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @ret rc Return status code
*/
struct ib_queue_pair *qp ) {
struct QIB_7322_RcvHdrAddr0 rcvhdraddr;
struct QIB_7322_RcvHdrHead0 rcvhdrhead;
struct QIB_7322_scalar rcvegrindexhead;
struct QIB_7322_RcvCtrl rcvctrl;
struct QIB_7322_RcvCtrl_P rcvctrlp;
int rc;
/* Reset context information */
sizeof ( qib7322_wq->header_prod ) );
qib7322_wq->header_cons = 0;
qib7322_wq->eager_prod = 0;
qib7322_wq->eager_cons = 0;
/* Allocate receive header buffer */
if ( ! qib7322_wq->header ) {
goto err_alloc_header;
}
/* Enable context in hardware */
return 0;
return rc;
}
/**
* Destroy receive work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
struct QIB_7322_RcvCtrl rcvctrl;
struct QIB_7322_RcvCtrl_P rcvctrlp;
/* Disable context in hardware */
/* Make sure the hardware has seen that the context is disabled */
mb();
/* Free headers ring */
}
/**
* Initialise receive datapath
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
struct QIB_7322_RcvCtrl rcvctrl;
struct QIB_7322_RcvCtrl_0 rcvctrlp;
struct QIB_7322_scalar rcvegrbase;
struct QIB_7322_scalar rcvhdrentsize;
struct QIB_7322_scalar rcvhdrcnt;
struct QIB_7322_RcvBTHQP_0 rcvbthqp;
struct QIB_7322_RxCreditVL0_0 rxcreditvl;
unsigned int contextcfg;
unsigned long egrbase;
unsigned int eager_array_size_kernel;
unsigned int eager_array_size_user;
unsigned int ctx;
/* Select configuration based on number of contexts */
switch ( QIB7322_NUM_CONTEXTS ) {
case 6:
break;
case 10:
break;
case 18:
break;
default:
return -EINVAL;
}
/* Configure number of contexts */
BIT_FILL_2 ( &rcvctrl,
TailUpd, 1,
ContextCfg, contextcfg );
/* Map QPNs to contexts */
BIT_FILL_3 ( &rcvctrlp,
RcvIBPortEnable, 1,
RcvQPMapEnable, 1,
RcvPartitionKeyDisable, 1 );
RcvQPMapContext0, 0,
RcvQPMapContext1, 2,
RcvQPMapContext2, 4,
RcvQPMapContext3, 6,
RcvQPMapContext4, 8,
RcvQPMapContext5, 10 );
RcvQPMapContext6, 12,
RcvQPMapContext7, 14,
RcvQPMapContext8, 16 );
RcvQPMapContext0, 1,
RcvQPMapContext1, 3,
RcvQPMapContext2, 5,
RcvQPMapContext3, 7,
RcvQPMapContext4, 9,
RcvQPMapContext5, 11 );
RcvQPMapContext6, 13,
RcvQPMapContext7, 15,
RcvQPMapContext8, 17 );
/* Map multicast QPNs to contexts */
/* Configure receive header buffer sizes */
/* Calculate eager array start addresses for each context */
egrbase += ( eager_array_size_kernel *
sizeof ( struct QIB_7322_RcvEgr ) );
}
egrbase += ( eager_array_size_user *
sizeof ( struct QIB_7322_RcvEgr ) );
}
}
/* Set the BTH QP for Infinipath packets to an unused value */
/* Assign initial credits */
return 0;
}
/**
* Shut down receive datapath
*
* @v qib7322 QIB7322 device
*/
/* Nothing to do; all contexts were already disabled when the
* queue pairs were destroyed
*/
}
/***************************************************************************
*
* Completion queue operations
*
***************************************************************************
*/
/**
* Create completion queue
*
* @v ibdev Infiniband device
* @v cq Completion queue
* @ret rc Return status code
*/
struct ib_completion_queue *cq ) {
static int cqn;
/* The hardware has no concept of completion queues. We
* simply use the association between CQs and WQs (already
* handled by the IB core) to decide which WQs to poll.
*
* We do set a CQN, just to avoid confusing debug messages
* from the IB core.
*/
return 0;
}
/**
* Destroy completion queue
*
* @v ibdev Infiniband device
* @v cq Completion queue
*/
struct ib_completion_queue *cq ) {
/* Nothing to do */
}
/***************************************************************************
*
* Queue pair operations
*
***************************************************************************
*/
/**
* Create queue pair
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @ret rc Return status code
*/
struct ib_queue_pair *qp ) {
unsigned int ctx;
int rc;
/* Allocate a context and QPN */
goto err_alloc_ctx;
/* Set work-queue private data pointers */
/* Create receive work queue */
goto err_create_recv_wq;
/* Create send work queue */
goto err_create_send_wq;
return 0;
return rc;
}
/**
* Modify queue pair
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @ret rc Return status code
*/
struct ib_queue_pair *qp ) {
/* Nothing to do; the hardware doesn't have a notion of queue
* keys
*/
return 0;
}
/**
* Destroy queue pair
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
}
/***************************************************************************
*
* Work request operations
*
***************************************************************************
*/
/**
* Post send work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v av Address vector
* @v iobuf I/O buffer
* @ret rc Return status code
*/
struct ib_queue_pair *qp,
struct ib_address_vector *av,
struct QIB_7322_SendPbc sendpbc;
int send_buf;
unsigned long start_offset;
unsigned long offset;
/* Allocate send buffer and calculate offset */
if ( send_buf < 0 )
return send_buf;
start_offset = offset =
send_buf );
/* Store I/O buffer and send buffer index */
/* Construct headers */
/* Calculate packet length */
/* Construct send per-buffer control word */
BIT_FILL_3 ( &sendpbc,
/* Write SendPbc */
DBG_DISABLE ( DBGLVL_IO );
/* Write headers */
}
/* Write data */
}
DBG_ENABLE ( DBGLVL_IO );
start_offset, offset );
/* Increment producer counter */
return 0;
}
/**
* Complete send work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v wqe_idx Work queue entry index
*/
struct ib_queue_pair *qp,
unsigned int wqe_idx ) {
unsigned int send_buf;
/* Parse completion */
/* Complete work queue entry */
/* Free send buffer */
}
/**
* Poll send work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
unsigned int send_buf;
/* Look for completions */
/* Check to see if send buffer has completed */
break;
/* Complete this buffer */
/* Increment consumer counter */
}
}
/**
* Post receive work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v iobuf I/O buffer
* @ret rc Return status code
*/
struct ib_queue_pair *qp,
struct QIB_7322_RcvEgr rcvegr;
struct QIB_7322_scalar rcvegrindexhead;
unsigned int wqe_idx;
unsigned int bufsize;
/* Sanity checks */
return -EINVAL;
}
if ( len != QIB7322_RECV_PAYLOAD_SIZE ) {
return -EINVAL;
}
/* Calculate eager producer index and WQE index */
/* Store I/O buffer */
/* Calculate buffer size */
switch ( QIB7322_RECV_PAYLOAD_SIZE ) {
default: linker_assert ( 0, invalid_rx_payload_size );
}
/* Post eager buffer */
BIT_FILL_2 ( &rcvegr,
qib7322_wq->eager_prod );
/* Increment producer index */
/* Update head index */
return 0;
}
/**
* Complete receive work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v header_offs Header offset
*/
struct ib_queue_pair *qp,
unsigned int header_offs ) {
struct QIB_7322_RcvHdrFlags *rcvhdrflags;
struct QIB_7322_RcvEgr rcvegr;
struct ib_queue_pair *intended_qp;
struct ib_address_vector av;
unsigned int rcvtype;
unsigned int pktlen;
unsigned int egrindex;
unsigned int useegrbfr;
unsigned int err;
unsigned int hdrqoffset;
unsigned int header_len;
unsigned int padded_payload_len;
unsigned int wqe_idx;
int qp0;
int rc;
/* RcvHdrFlags are at the end of the header entry */
QIB7322_RECV_HEADER_SIZE - sizeof ( *rcvhdrflags ) );
sizeof ( *rcvhdrflags ) );
/* IB header is placed immediately before RcvHdrFlags */
header_len, header_len );
/* Dump diagnostic information */
( header_len + sizeof ( *rcvhdrflags ) ) );
/* Parse header to generate address vector */
intended_qp = NULL;
&payload_len, &av ) ) != 0 ) {
err = 1;
}
if ( ! intended_qp )
intended_qp = qp;
/* Complete this buffer and any skipped buffers. Note that
* when the hardware runs out of buffers, it will repeatedly
* report the same buffer (the tail) as a TID error, and that
* it also has a habit of sometimes skipping over several
* buffers at once.
*/
while ( 1 ) {
/* If we have caught up to the producer counter, stop.
* This will happen when the hardware first runs out
* of buffers and starts reporting TID errors against
* the eager buffer it wants to use next.
*/
break;
/* If we have caught up to where we should be after
* completing this egrindex, stop. We phrase the test
* this way to avoid completing the entire ring when
* we receive the same egrindex twice in a row.
*/
if ( ( qib7322_wq->eager_cons ==
break;
/* Identify work queue entry and corresponding I/O
* buffer.
*/
/* Complete the eager buffer */
/* Completing the eager buffer described in
* this header entry.
*/
/* Redirect to target QP if necessary */
if ( qp != intended_qp ) {
"%ld => %ld\n",
/* Compensate for incorrect fill levels */
}
} else {
/* Completing on a skipped-over eager buffer */
}
/* Clear eager buffer */
qib7322_wq->eager_cons );
/* Increment consumer index */
}
}
/**
* Poll receive work queue
*
* @v ibdev Infiniband device
* @v qp Queue pair
*/
struct ib_queue_pair *qp ) {
struct QIB_7322_RcvHdrHead0 rcvhdrhead;
unsigned int header_prod;
/* Check for received packets */
return;
/* Process all received packets */
/* Complete the receive */
/* Increment the consumer offset */
/* QIB7322 has only one send buffer per port for VL15,
* which almost always leads to send buffer exhaustion
* and dropped MADs. Mitigate this by refusing to
* process more than one VL15 MAD per poll, which will
*/
break;
}
/* Update consumer offset */
BIT_FILL_2 ( &rcvhdrhead,
counter, 1 );
}
/**
* Poll completion queue
*
* @v ibdev Infiniband device
* @v cq Completion queue
*/
struct ib_completion_queue *cq ) {
struct ib_work_queue *wq;
/* Poll associated send and receive queues */
} else {
}
}
}
/***************************************************************************
*
* Event queues
*
***************************************************************************
*/
/**
* Poll event queue
*
* @v ibdev Infiniband device
*/
struct QIB_7322_ErrStatus_0 errstatus;
/* Check for and clear status bits */
DBG_DISABLE ( DBGLVL_IO );
}
DBG_ENABLE ( DBGLVL_IO );
/* Check for link status changes */
}
/***************************************************************************
*
* Infiniband link-layer operations
*
***************************************************************************
*/
/**
* Determine supported link speeds
*
* @v qib7322 QIB7322 device
* @ret supported Supported link speeds
*/
unsigned int port ) {
struct QIB_7322_feature_mask features;
struct QIB_7322_Revision revision;
unsigned int supported;
unsigned int boardid;
/* Read the active feature mask */
switch ( port ) {
case 0 :
break;
case 1 :
break;
default:
supported = 0;
break;
}
/* Apply hacks for specific board IDs */
switch ( boardid ) {
case QIB7322_BOARD_QMH7342 :
qib7322 );
break;
default:
/* Do nothing */
break;
}
return supported;
}
/**
* Initialise Infiniband link
*
* @v ibdev Infiniband device
* @ret rc Return status code
*/
struct QIB_7322_IBCCtrlA_0 ibcctrla;
/* Enable link */
return 0;
}
/**
* Close Infiniband link
*
* @v ibdev Infiniband device
*/
struct QIB_7322_IBCCtrlA_0 ibcctrla;
/* Disable link */
}
/***************************************************************************
*
* Multicast group operations
*
***************************************************************************
*/
/**
* Attach to multicast group
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v gid Multicast GID
* @ret rc Return status code
*/
struct ib_queue_pair *qp,
( void ) qib7322;
( void ) qp;
( void ) gid;
return 0;
}
/**
* Detach from multicast group
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v gid Multicast GID
*/
struct ib_queue_pair *qp,
( void ) qib7322;
( void ) qp;
( void ) gid;
}
/** QIB7322 Infiniband operations */
static struct ib_device_operations qib7322_ib_operations = {
.open = qib7322_open,
.close = qib7322_close,
};
/***************************************************************************
*
* I2C bus operations
*
***************************************************************************
*/
/** QIB7322 I2C bit to GPIO mappings */
static unsigned int qib7322_i2c_bits[] = {
};
/**
* Read QIB7322 I2C line status
*
* @v basher Bit-bashing interface
* @v bit_id Bit number
* @ret zero Input is a logic 0
* @ret non-zero Input is a logic 1
*/
unsigned int bit_id ) {
struct QIB_7322_EXTStatus extstatus;
unsigned int status;
DBG_DISABLE ( DBGLVL_IO );
DBG_ENABLE ( DBGLVL_IO );
return status;
}
/**
* Write QIB7322 I2C line status
*
* @v basher Bit-bashing interface
* @v bit_id Bit number
* @v data Value to write
*/
struct QIB_7322_EXTCtrl extctrl;
struct QIB_7322_GPIO gpioout;
unsigned int outputs = 0;
unsigned int output_enables = 0;
DBG_DISABLE ( DBGLVL_IO );
/* Read current GPIO mask and outputs */
/* Update outputs and output enables. I2C lines are tied
* high, so we always set the output to 0 and use the output
* enable to control the line.
*/
/* Write the output enable first; that way we avoid logic
* hazards.
*/
mb();
DBG_ENABLE ( DBGLVL_IO );
}
/** QIB7322 I2C bit-bashing interface operations */
static struct bit_basher_operations qib7322_i2c_basher_ops = {
};
/**
* Initialise QIB7322 I2C subsystem
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
unsigned int i;
int rc;
/* Initialise bus */
&qib7322_i2c_basher_ops ) ) != 0 ) {
return rc;
}
/* Probe for devices */
for ( i = 0 ; i < ( sizeof ( try_eeprom_address ) /
sizeof ( try_eeprom_address[0] ) ) ; i++ ) {
qib7322, try_eeprom_address[i] );
return 0;
}
}
return -ENODEV;
}
/**
* Read EEPROM parameters
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
int rc;
/* Read GUID */
sizeof ( *guid ) ) ) != 0 ) {
return rc;
}
/* Read serial number (debug only) */
if ( DBG_LOG ) {
( sizeof ( serial ) - 1 ) ) ) != 0 ) {
return rc;
}
}
return 0;
}
/***************************************************************************
*
* Advanced High-performance Bus (AHB) access
*
***************************************************************************
*/
/**
* Wait for AHB transaction to complete
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
unsigned int i;
/* Wait for Ready bit to be asserted */
for ( i = 0 ; i < QIB7322_AHB_MAX_WAIT_US ; i++ ) {
return 0;
udelay ( 1 );
}
qib7322 );
return -ETIMEDOUT;
}
/**
* Request ownership of the AHB
*
* @v qib7322 QIB7322 device
* @v location AHB location
* @ret rc Return status code
*/
unsigned int location ) {
struct QIB_7322_ahb_access_ctrl access;
int rc;
/* Request ownership */
BIT_FILL_2 ( &access,
sw_ahb_sel, 1,
/* Wait for ownership to be granted */
return rc;
}
return 0;
}
/**
* Release ownership of the AHB
*
* @v qib7322 QIB7322 device
*/
struct QIB_7322_ahb_access_ctrl access;
}
/**
* Read data via AHB
*
* @v qib7322 QIB7322 device
* @v location AHB location
* @v data Data to read
* @ret rc Return status code
*
* You must have already acquired ownership of the AHB.
*/
struct QIB_7322_ahb_transaction_reg xact;
int rc;
/* Initiate transaction */
BIT_FILL_2 ( &xact,
write_not_read, 0 );
/* Wait for transaction to complete */
return rc;
/* Read transaction data */
return 0;
}
/**
* Write data via AHB
*
* @v qib7322 QIB7322 device
* @v location AHB location
* @v data Data to write
* @ret rc Return status code
*
* You must have already acquired ownership of the AHB.
*/
struct QIB_7322_ahb_transaction_reg xact;
int rc;
/* Initiate transaction */
BIT_FILL_3 ( &xact,
write_not_read, 1,
/* Wait for transaction to complete */
return rc;
return 0;
}
/**
*
* @v qib7322 QIB7322 device
* @v location AHB location
* @v value Value to set
* @v mask Mask to apply to old value
* @ret rc Return status code
*/
int rc;
DBG_DISABLE ( DBGLVL_IO );
/* Sanity check */
/* Acquire bus ownership */
goto out;
/* Read existing value */
goto out_release;
/* Update value */
goto out_release;
/* Release bus */
out:
DBG_ENABLE ( DBGLVL_IO );
return rc;
}
/**
*
* @v qib7322 QIB7322 device
* @v reg AHB register
* @v value Value to set
* @v mask Mask to apply to old value
* @ret rc Return status code
*/
unsigned int port;
unsigned int channel;
unsigned int location;
int rc;
return rc;
}
}
return 0;
}
/***************************************************************************
*
* Infiniband SerDes initialisation
*
***************************************************************************
*/
/**
* Initialise the IB SerDes
*
* @v qib7322 QIB7322 device
* @ret rc Return status code
*/
struct QIB_7322_IBCCtrlA_0 ibcctrla;
struct QIB_7322_IBCCtrlB_0 ibcctrlb;
struct QIB_7322_IBPCSConfig_0 ibpcsconfig;
/* Configure sensible defaults for IBC */
FlowCtrlPeriod, 0x03,
FlowCtrlWaterMark, 0x05,
4 /* ICRC */ ) >> 2 ),
PhyerrThreshold, 0xf,
OverrunThreshold, 0xf );
/* Force SDR only to avoid needing all the DDR tuning,
* Mellanox compatibility hacks etc. SDR is plenty for
* boot-time operation.
*/
/* Tune SerDes */
/* Bring XGXS out of reset */
return 0;
}
/***************************************************************************
*
* PCI layer interface
*
***************************************************************************
*/
/**
* Reset QIB7322
*
* @v qib7322 QIB7322 device
* @v pci PCI device
* @ret rc Return status code
*/
struct QIB_7322_Control control;
struct pci_config_backup backup;
/* Back up PCI configuration space */
/* Assert reset */
/* Wait for reset to complete */
mdelay ( 1000 );
/* Restore PCI configuration space */
}
/**
* Probe PCI device
*
* @v pci PCI device
* @v id PCI ID
* @ret rc Return status code
*/
struct QIB_7322_Revision revision;
unsigned int link_speed_supported;
int i;
int rc;
/* Allocate QIB7322 device */
if ( ! qib7322 ) {
goto err_alloc_qib7322;
}
/* Fix up PCI device */
adjust_pci_device ( pci );
/* Get PCI BARs */
/* Reset device */
/* Print some general data */
/* Initialise I2C subsystem */
goto err_init_i2c;
/* Read EEPROM parameters */
goto err_read_eeprom;
/* Initialise send datapath */
goto err_init_send;
/* Initialise receive datapath */
goto err_init_recv;
/* Initialise the IB SerDes */
goto err_init_ib_serdes;
/* Allocate Infiniband devices */
for ( i = 0 ; i < QIB7322_MAX_PORTS ; i++ ) {
qib7322_link_speed_supported ( qib7322, i );
if ( ! link_speed_supported )
continue;
ibdev = alloc_ibdev ( 0 );
if ( ! ibdev ) {
goto err_alloc_ibdev;
}
IB_LINK_WIDTH_4X; /* 1x does not work */
IB_LINK_SPEED_SDR; /* to avoid need for link tuning */
}
/* Register Infiniband devices */
for ( i = 0 ; i < QIB7322_MAX_PORTS ; i++ ) {
continue;
goto err_register_ibdev;
}
}
return 0;
i = QIB7322_MAX_PORTS;
for ( i-- ; i >= 0 ; i-- ) {
}
i = QIB7322_MAX_PORTS;
for ( i-- ; i >= 0 ; i-- )
qib7322_fini_send ( qib7322 );
qib7322_fini_recv ( qib7322 );
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
int i;
for ( i = ( QIB7322_MAX_PORTS - 1 ) ; i >= 0 ; i-- ) {
}
for ( i = ( QIB7322_MAX_PORTS - 1 ) ; i >= 0 ; i-- )
qib7322_fini_send ( qib7322 );
qib7322_fini_recv ( qib7322 );
}
static struct pci_device_id qib7322_nics[] = {
};
.ids = qib7322_nics,
.probe = qib7322_probe,
.remove = qib7322_remove,
};