2008-11-07 09:47:21 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
|
|
|
|
*
|
|
|
|
* 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 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.
|
|
|
|
*/
|
|
|
|
|
2009-05-01 16:41:06 +02:00
|
|
|
FILE_LICENCE ( GPL2_OR_LATER );
|
|
|
|
|
2008-11-07 09:47:21 +01:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <assert.h>
|
2008-11-19 20:33:05 +01:00
|
|
|
#include <gpxe/io.h>
|
2008-11-07 09:47:21 +01:00
|
|
|
#include <gpxe/pci.h>
|
|
|
|
#include <gpxe/infiniband.h>
|
|
|
|
#include <gpxe/i2c.h>
|
|
|
|
#include <gpxe/bitbash.h>
|
|
|
|
#include <gpxe/malloc.h>
|
|
|
|
#include <gpxe/iobuf.h>
|
|
|
|
#include "linda.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
*
|
|
|
|
* QLogic Linda Infiniband HCA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** A Linda send work queue */
|
|
|
|
struct linda_send_work_queue {
|
|
|
|
/** Send buffer usage */
|
|
|
|
uint8_t *send_buf;
|
|
|
|
/** Producer index */
|
|
|
|
unsigned int prod;
|
|
|
|
/** Consumer index */
|
|
|
|
unsigned int cons;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A Linda receive work queue */
|
|
|
|
struct linda_recv_work_queue {
|
|
|
|
/** Receive header ring */
|
|
|
|
void *header;
|
|
|
|
/** Receive header producer offset (written by hardware) */
|
|
|
|
struct QIB_7220_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 Linda HCA */
|
|
|
|
struct linda {
|
|
|
|
/** Registers */
|
|
|
|
void *regs;
|
|
|
|
|
|
|
|
/** In-use contexts */
|
|
|
|
uint8_t used_ctx[LINDA_NUM_CONTEXTS];
|
|
|
|
/** Send work queues */
|
|
|
|
struct linda_send_work_queue send_wq[LINDA_NUM_CONTEXTS];
|
|
|
|
/** Receive work queues */
|
|
|
|
struct linda_recv_work_queue recv_wq[LINDA_NUM_CONTEXTS];
|
|
|
|
|
|
|
|
/** Offset within register space of the first send buffer */
|
|
|
|
unsigned long send_buffer_base;
|
|
|
|
/** Send buffer availability (reported by hardware) */
|
|
|
|
struct QIB_7220_SendBufAvail *sendbufavail;
|
|
|
|
/** Send buffer availability (maintained by software) */
|
|
|
|
uint8_t send_buf[LINDA_MAX_SEND_BUFS];
|
|
|
|
/** Send buffer availability producer counter */
|
|
|
|
unsigned int send_buf_prod;
|
|
|
|
/** Send buffer availability consumer counter */
|
|
|
|
unsigned int send_buf_cons;
|
|
|
|
/** Number of reserved send buffers (across all QPs) */
|
|
|
|
unsigned int reserved_send_bufs;
|
|
|
|
|
|
|
|
/** I2C bit-bashing interface */
|
|
|
|
struct i2c_bit_basher i2c;
|
|
|
|
/** I2C serial EEPROM */
|
|
|
|
struct i2c_device eeprom;
|
|
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Linda 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 Linda qword register
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v dwords Register buffer to read into
|
|
|
|
* @v offset Register offset
|
|
|
|
*/
|
|
|
|
static void linda_readq ( struct linda *linda, uint32_t *dwords,
|
|
|
|
unsigned long offset ) {
|
|
|
|
void *addr = ( linda->regs + offset );
|
|
|
|
|
|
|
|
__asm__ __volatile__ ( "movq (%1), %%mm0\n\t"
|
|
|
|
"movq %%mm0, (%0)\n\t"
|
|
|
|
: : "r" ( dwords ), "r" ( addr ) : "memory" );
|
|
|
|
|
2008-11-19 03:22:56 +01:00
|
|
|
DBGIO ( "[%08lx] => %08x%08x\n",
|
2008-11-07 09:47:21 +01:00
|
|
|
virt_to_phys ( addr ), dwords[1], dwords[0] );
|
|
|
|
}
|
|
|
|
#define linda_readq( _linda, _ptr, _offset ) \
|
|
|
|
linda_readq ( (_linda), (_ptr)->u.dwords, (_offset) )
|
|
|
|
#define linda_readq_array8b( _linda, _ptr, _offset, _idx ) \
|
|
|
|
linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) )
|
|
|
|
#define linda_readq_array64k( _linda, _ptr, _offset, _idx ) \
|
|
|
|
linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 65536 ) ) )
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write Linda qword register
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v dwords Register buffer to write
|
|
|
|
* @v offset Register offset
|
|
|
|
*/
|
|
|
|
static void linda_writeq ( struct linda *linda, const uint32_t *dwords,
|
|
|
|
unsigned long offset ) {
|
|
|
|
void *addr = ( linda->regs + offset );
|
|
|
|
|
2008-11-19 03:22:56 +01:00
|
|
|
DBGIO ( "[%08lx] <= %08x%08x\n",
|
2008-11-07 09:47:21 +01:00
|
|
|
virt_to_phys ( addr ), dwords[1], dwords[0] );
|
|
|
|
|
|
|
|
__asm__ __volatile__ ( "movq (%0), %%mm0\n\t"
|
|
|
|
"movq %%mm0, (%1)\n\t"
|
|
|
|
: : "r" ( dwords ), "r" ( addr ) : "memory" );
|
|
|
|
}
|
|
|
|
#define linda_writeq( _linda, _ptr, _offset ) \
|
|
|
|
linda_writeq ( (_linda), (_ptr)->u.dwords, (_offset) )
|
|
|
|
#define linda_writeq_array8b( _linda, _ptr, _offset, _idx ) \
|
|
|
|
linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) )
|
|
|
|
#define linda_writeq_array64k( _linda, _ptr, _offset, _idx ) \
|
|
|
|
linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 65536 ) ) )
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write Linda dword register
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v dword Value to write
|
|
|
|
* @v offset Register offset
|
|
|
|
*/
|
|
|
|
static void linda_writel ( struct linda *linda, uint32_t dword,
|
|
|
|
unsigned long offset ) {
|
|
|
|
writel ( dword, ( linda->regs + offset ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Link state management
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Textual representation of link state
|
|
|
|
*
|
|
|
|
* @v link_state Link state
|
|
|
|
* @ret link_text Link state text
|
|
|
|
*/
|
|
|
|
static const char * linda_link_state_text ( unsigned int link_state ) {
|
|
|
|
switch ( link_state ) {
|
|
|
|
case LINDA_LINK_STATE_DOWN: return "DOWN";
|
|
|
|
case LINDA_LINK_STATE_INIT: return "INIT";
|
|
|
|
case LINDA_LINK_STATE_ARM: return "ARM";
|
|
|
|
case LINDA_LINK_STATE_ACTIVE: return "ACTIVE";
|
|
|
|
case LINDA_LINK_STATE_ACT_DEFER:return "ACT_DEFER";
|
|
|
|
default: return "UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle link state change
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
*/
|
|
|
|
static void linda_link_state_changed ( struct ib_device *ibdev ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct QIB_7220_IBCStatus ibcstatus;
|
|
|
|
struct QIB_7220_EXTCtrl extctrl;
|
|
|
|
unsigned int link_state;
|
|
|
|
unsigned int link_width;
|
|
|
|
unsigned int link_speed;
|
|
|
|
|
|
|
|
/* Read link state */
|
|
|
|
linda_readq ( linda, &ibcstatus, QIB_7220_IBCStatus_offset );
|
|
|
|
link_state = BIT_GET ( &ibcstatus, LinkState );
|
|
|
|
link_width = BIT_GET ( &ibcstatus, LinkWidthActive );
|
|
|
|
link_speed = BIT_GET ( &ibcstatus, LinkSpeedActive );
|
|
|
|
DBGC ( linda, "Linda %p link state %s (%s %s)\n", linda,
|
|
|
|
linda_link_state_text ( link_state ),
|
|
|
|
( link_speed ? "DDR" : "SDR" ), ( link_width ? "x4" : "x1" ) );
|
|
|
|
|
|
|
|
/* Set LEDs according to link state */
|
|
|
|
linda_readq ( linda, &extctrl, QIB_7220_EXTCtrl_offset );
|
|
|
|
BIT_SET ( &extctrl, LEDPriPortGreenOn,
|
|
|
|
( ( link_state >= LINDA_LINK_STATE_INIT ) ? 1 : 0 ) );
|
|
|
|
BIT_SET ( &extctrl, LEDPriPortYellowOn,
|
|
|
|
( ( link_state >= LINDA_LINK_STATE_ACTIVE ) ? 1 : 0 ) );
|
|
|
|
linda_writeq ( linda, &extctrl, QIB_7220_EXTCtrl_offset );
|
|
|
|
|
|
|
|
/* Notify Infiniband core of link state change */
|
|
|
|
ibdev->port_state = ( link_state + 1 );
|
2009-07-11 02:14:43 +02:00
|
|
|
ibdev->link_width_active =
|
2008-11-07 09:47:21 +01:00
|
|
|
( link_width ? IB_LINK_WIDTH_4X : IB_LINK_WIDTH_1X );
|
2009-07-11 02:14:43 +02:00
|
|
|
ibdev->link_speed_active =
|
2008-11-07 09:47:21 +01:00
|
|
|
( link_speed ? IB_LINK_SPEED_DDR : IB_LINK_SPEED_SDR );
|
|
|
|
ib_link_state_changed ( ibdev );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set port information
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
2009-07-17 23:40:12 +02:00
|
|
|
* @v mad Set port information MAD
|
2008-11-07 09:47:21 +01:00
|
|
|
*/
|
2009-07-17 23:40:12 +02:00
|
|
|
static int linda_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) {
|
2008-11-07 09:47:21 +01:00
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
2009-07-17 23:40:12 +02:00
|
|
|
struct ib_port_info *port_info = &mad->smp.smp_data.port_info;
|
2008-11-07 09:47:21 +01:00
|
|
|
struct QIB_7220_IBCCtrl ibcctrl;
|
|
|
|
unsigned int port_state;
|
|
|
|
unsigned int link_state;
|
|
|
|
|
|
|
|
/* Set new link state */
|
|
|
|
port_state = ( port_info->link_speed_supported__port_state & 0xf );
|
|
|
|
if ( port_state ) {
|
|
|
|
link_state = ( port_state - 1 );
|
|
|
|
DBGC ( linda, "Linda %p set link state to %s (%x)\n", linda,
|
|
|
|
linda_link_state_text ( link_state ), link_state );
|
|
|
|
linda_readq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
|
|
|
|
BIT_SET ( &ibcctrl, LinkCmd, link_state );
|
|
|
|
linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Detect and report link state change */
|
|
|
|
linda_link_state_changed ( ibdev );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-17 23:27:34 +02:00
|
|
|
/**
|
|
|
|
* Set partition key table
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v mad Set partition key table MAD
|
|
|
|
*/
|
|
|
|
static int linda_set_pkey_table ( struct ib_device *ibdev __unused,
|
|
|
|
union ib_mad *mad __unused ) {
|
|
|
|
/* Nothing to do */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-07 09:47:21 +01:00
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Context allocation
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map context number to QPN
|
|
|
|
*
|
|
|
|
* @v ctx Context index
|
|
|
|
* @ret qpn Queue pair number
|
|
|
|
*/
|
|
|
|
static int linda_ctx_to_qpn ( unsigned int ctx ) {
|
|
|
|
/* This mapping is fixed by hardware */
|
|
|
|
return ( ctx * 2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map QPN to context number
|
|
|
|
*
|
|
|
|
* @v qpn Queue pair number
|
|
|
|
* @ret ctx Context index
|
|
|
|
*/
|
|
|
|
static int linda_qpn_to_ctx ( unsigned int qpn ) {
|
|
|
|
/* This mapping is fixed by hardware */
|
|
|
|
return ( qpn / 2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a context
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret ctx Context index, or negative error
|
|
|
|
*/
|
|
|
|
static int linda_alloc_ctx ( struct linda *linda ) {
|
|
|
|
unsigned int ctx;
|
|
|
|
|
|
|
|
for ( ctx = 0 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) {
|
|
|
|
|
|
|
|
if ( ! linda->used_ctx[ctx] ) {
|
|
|
|
linda->used_ctx[ctx ] = 1;
|
|
|
|
DBGC2 ( linda, "Linda %p CTX %d allocated\n",
|
|
|
|
linda, ctx );
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p out of available contexts\n", linda );
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free a context
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v ctx Context index
|
|
|
|
*/
|
|
|
|
static void linda_free_ctx ( struct linda *linda, unsigned int ctx ) {
|
|
|
|
|
|
|
|
linda->used_ctx[ctx] = 0;
|
|
|
|
DBGC2 ( linda, "Linda %p CTX %d freed\n", linda, ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Send datapath
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Send buffer toggle bit
|
|
|
|
*
|
|
|
|
* We encode send buffers as 7 bits of send buffer index plus a single
|
|
|
|
* bit which should match the "check" bit in the SendBufAvail array.
|
|
|
|
*/
|
|
|
|
#define LINDA_SEND_BUF_TOGGLE 0x80
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a send buffer
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret send_buf Send buffer
|
|
|
|
*
|
|
|
|
* You must guarantee that a send buffer is available. This is done
|
|
|
|
* by refusing to allocate more TX WQEs in total than the number of
|
|
|
|
* available send buffers.
|
|
|
|
*/
|
|
|
|
static unsigned int linda_alloc_send_buf ( struct linda *linda ) {
|
|
|
|
unsigned int send_buf;
|
|
|
|
|
|
|
|
send_buf = linda->send_buf[linda->send_buf_cons];
|
|
|
|
send_buf ^= LINDA_SEND_BUF_TOGGLE;
|
|
|
|
linda->send_buf_cons = ( ( linda->send_buf_cons + 1 ) %
|
|
|
|
LINDA_MAX_SEND_BUFS );
|
|
|
|
return send_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free a send buffer
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v send_buf Send buffer
|
|
|
|
*/
|
|
|
|
static void linda_free_send_buf ( struct linda *linda,
|
|
|
|
unsigned int send_buf ) {
|
|
|
|
linda->send_buf[linda->send_buf_prod] = send_buf;
|
|
|
|
linda->send_buf_prod = ( ( linda->send_buf_prod + 1 ) %
|
|
|
|
LINDA_MAX_SEND_BUFS );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check to see if send buffer is in use
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v send_buf Send buffer
|
|
|
|
* @ret in_use Send buffer is in use
|
|
|
|
*/
|
|
|
|
static int linda_send_buf_in_use ( struct linda *linda,
|
|
|
|
unsigned int send_buf ) {
|
|
|
|
unsigned int send_idx;
|
|
|
|
unsigned int send_check;
|
|
|
|
unsigned int inusecheck;
|
|
|
|
unsigned int inuse;
|
|
|
|
unsigned int check;
|
|
|
|
|
|
|
|
send_idx = ( send_buf & ~LINDA_SEND_BUF_TOGGLE );
|
|
|
|
send_check = ( !! ( send_buf & LINDA_SEND_BUF_TOGGLE ) );
|
|
|
|
inusecheck = BIT_GET ( linda->sendbufavail, InUseCheck[send_idx] );
|
|
|
|
inuse = ( !! ( inusecheck & 0x02 ) );
|
|
|
|
check = ( !! ( inusecheck & 0x01 ) );
|
|
|
|
return ( inuse || ( check != send_check ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate starting offset for send buffer
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v send_buf Send buffer
|
|
|
|
* @ret offset Starting offset
|
|
|
|
*/
|
|
|
|
static unsigned long linda_send_buffer_offset ( struct linda *linda,
|
|
|
|
unsigned int send_buf ) {
|
|
|
|
return ( linda->send_buffer_base +
|
|
|
|
( ( send_buf & ~LINDA_SEND_BUF_TOGGLE ) *
|
|
|
|
LINDA_SEND_BUF_SIZE ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create send work queue
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static int linda_create_send_wq ( struct linda *linda,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct ib_work_queue *wq = &qp->send;
|
|
|
|
struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Reserve send buffers */
|
|
|
|
if ( ( linda->reserved_send_bufs + qp->send.num_wqes ) >
|
|
|
|
LINDA_MAX_SEND_BUFS ) {
|
|
|
|
DBGC ( linda, "Linda %p out of send buffers (have %d, used "
|
|
|
|
"%d, need %d)\n", linda, LINDA_MAX_SEND_BUFS,
|
|
|
|
linda->reserved_send_bufs, qp->send.num_wqes );
|
|
|
|
rc = -ENOBUFS;
|
|
|
|
goto err_reserve_bufs;
|
|
|
|
}
|
|
|
|
linda->reserved_send_bufs += qp->send.num_wqes;
|
|
|
|
|
|
|
|
/* Reset work queue */
|
|
|
|
linda_wq->prod = 0;
|
|
|
|
linda_wq->cons = 0;
|
|
|
|
|
|
|
|
/* Allocate space for send buffer uasge list */
|
|
|
|
linda_wq->send_buf = zalloc ( qp->send.num_wqes *
|
|
|
|
sizeof ( linda_wq->send_buf[0] ) );
|
|
|
|
if ( ! linda_wq->send_buf ) {
|
|
|
|
rc = -ENOBUFS;
|
|
|
|
goto err_alloc_send_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free ( linda_wq->send_buf );
|
|
|
|
err_alloc_send_buf:
|
|
|
|
linda->reserved_send_bufs -= qp->send.num_wqes;
|
|
|
|
err_reserve_bufs:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy send work queue
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static void linda_destroy_send_wq ( struct linda *linda,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct ib_work_queue *wq = &qp->send;
|
|
|
|
struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
|
|
|
|
free ( linda_wq->send_buf );
|
|
|
|
linda->reserved_send_bufs -= qp->send.num_wqes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise send datapath
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_init_send ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_SendBufBase sendbufbase;
|
|
|
|
struct QIB_7220_SendBufAvailAddr sendbufavailaddr;
|
|
|
|
struct QIB_7220_SendCtrl sendctrl;
|
|
|
|
unsigned int i;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Retrieve SendBufBase */
|
|
|
|
linda_readq ( linda, &sendbufbase, QIB_7220_SendBufBase_offset );
|
|
|
|
linda->send_buffer_base = BIT_GET ( &sendbufbase,
|
|
|
|
BaseAddr_SmallPIO );
|
|
|
|
DBGC ( linda, "Linda %p send buffers at %lx\n",
|
|
|
|
linda, linda->send_buffer_base );
|
|
|
|
|
|
|
|
/* Initialise the send_buf[] array */
|
|
|
|
for ( i = 0 ; i < LINDA_MAX_SEND_BUFS ; i++ )
|
|
|
|
linda->send_buf[i] = i;
|
|
|
|
|
|
|
|
/* Allocate space for the SendBufAvail array */
|
|
|
|
linda->sendbufavail = malloc_dma ( sizeof ( *linda->sendbufavail ),
|
|
|
|
LINDA_SENDBUFAVAIL_ALIGN );
|
|
|
|
if ( ! linda->sendbufavail ) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err_alloc_sendbufavail;
|
|
|
|
}
|
|
|
|
memset ( linda->sendbufavail, 0, sizeof ( linda->sendbufavail ) );
|
|
|
|
|
|
|
|
/* Program SendBufAvailAddr into the hardware */
|
|
|
|
memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) );
|
|
|
|
BIT_FILL_1 ( &sendbufavailaddr, SendBufAvailAddr,
|
|
|
|
( virt_to_bus ( linda->sendbufavail ) >> 6 ) );
|
|
|
|
linda_writeq ( linda, &sendbufavailaddr,
|
|
|
|
QIB_7220_SendBufAvailAddr_offset );
|
|
|
|
|
|
|
|
/* Enable sending and DMA of SendBufAvail */
|
|
|
|
memset ( &sendctrl, 0, sizeof ( sendctrl ) );
|
|
|
|
BIT_FILL_2 ( &sendctrl,
|
|
|
|
SendBufAvailUpd, 1,
|
|
|
|
SPioEnable, 1 );
|
|
|
|
linda_writeq ( linda, &sendctrl, QIB_7220_SendCtrl_offset );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_dma ( linda->sendbufavail, sizeof ( *linda->sendbufavail ) );
|
|
|
|
err_alloc_sendbufavail:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shut down send datapath
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
*/
|
|
|
|
static void linda_fini_send ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_SendCtrl sendctrl;
|
|
|
|
|
|
|
|
/* Disable sending and DMA of SendBufAvail */
|
|
|
|
memset ( &sendctrl, 0, sizeof ( sendctrl ) );
|
|
|
|
linda_writeq ( linda, &sendctrl, QIB_7220_SendCtrl_offset );
|
|
|
|
mb();
|
|
|
|
|
|
|
|
/* Ensure hardware has seen this disable */
|
|
|
|
linda_readq ( linda, &sendctrl, QIB_7220_SendCtrl_offset );
|
|
|
|
|
|
|
|
free_dma ( linda->sendbufavail, sizeof ( *linda->sendbufavail ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Receive datapath
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create receive work queue
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_create_recv_wq ( struct linda *linda,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct ib_work_queue *wq = &qp->recv;
|
|
|
|
struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_RcvHdrAddr0 rcvhdraddr;
|
|
|
|
struct QIB_7220_RcvHdrTailAddr0 rcvhdrtailaddr;
|
|
|
|
struct QIB_7220_RcvHdrHead0 rcvhdrhead;
|
|
|
|
struct QIB_7220_scalar rcvegrindexhead;
|
|
|
|
struct QIB_7220_RcvCtrl rcvctrl;
|
|
|
|
unsigned int ctx = linda_qpn_to_ctx ( qp->qpn );
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Reset context information */
|
|
|
|
memset ( &linda_wq->header_prod, 0,
|
|
|
|
sizeof ( linda_wq->header_prod ) );
|
|
|
|
linda_wq->header_cons = 0;
|
|
|
|
linda_wq->eager_prod = 0;
|
|
|
|
linda_wq->eager_cons = 0;
|
|
|
|
|
|
|
|
/* Allocate receive header buffer */
|
|
|
|
linda_wq->header = malloc_dma ( LINDA_RECV_HEADERS_SIZE,
|
|
|
|
LINDA_RECV_HEADERS_ALIGN );
|
|
|
|
if ( ! linda_wq->header ) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err_alloc_header;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable context in hardware */
|
|
|
|
memset ( &rcvhdraddr, 0, sizeof ( rcvhdraddr ) );
|
|
|
|
BIT_FILL_1 ( &rcvhdraddr, RcvHdrAddr0,
|
|
|
|
( virt_to_bus ( linda_wq->header ) >> 2 ) );
|
|
|
|
linda_writeq_array8b ( linda, &rcvhdraddr,
|
|
|
|
QIB_7220_RcvHdrAddr0_offset, ctx );
|
|
|
|
memset ( &rcvhdrtailaddr, 0, sizeof ( rcvhdrtailaddr ) );
|
|
|
|
BIT_FILL_1 ( &rcvhdrtailaddr, RcvHdrTailAddr0,
|
|
|
|
( virt_to_bus ( &linda_wq->header_prod ) >> 2 ) );
|
|
|
|
linda_writeq_array8b ( linda, &rcvhdrtailaddr,
|
|
|
|
QIB_7220_RcvHdrTailAddr0_offset, ctx );
|
|
|
|
memset ( &rcvhdrhead, 0, sizeof ( rcvhdrhead ) );
|
|
|
|
BIT_FILL_1 ( &rcvhdrhead, counter, 1 );
|
|
|
|
linda_writeq_array64k ( linda, &rcvhdrhead,
|
|
|
|
QIB_7220_RcvHdrHead0_offset, ctx );
|
|
|
|
memset ( &rcvegrindexhead, 0, sizeof ( rcvegrindexhead ) );
|
|
|
|
BIT_FILL_1 ( &rcvegrindexhead, Value, 1 );
|
|
|
|
linda_writeq_array64k ( linda, &rcvegrindexhead,
|
|
|
|
QIB_7220_RcvEgrIndexHead0_offset, ctx );
|
|
|
|
linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
BIT_SET ( &rcvctrl, PortEnable[ctx], 1 );
|
|
|
|
BIT_SET ( &rcvctrl, IntrAvail[ctx], 1 );
|
|
|
|
linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p QPN %ld CTX %d hdrs [%lx,%lx) prod %lx\n",
|
|
|
|
linda, qp->qpn, ctx, virt_to_bus ( linda_wq->header ),
|
|
|
|
( virt_to_bus ( linda_wq->header ) + LINDA_RECV_HEADERS_SIZE ),
|
|
|
|
virt_to_bus ( &linda_wq->header_prod ) );
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_dma ( linda_wq->header, LINDA_RECV_HEADERS_SIZE );
|
|
|
|
err_alloc_header:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy receive work queue
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static void linda_destroy_recv_wq ( struct linda *linda,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct ib_work_queue *wq = &qp->recv;
|
|
|
|
struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_RcvCtrl rcvctrl;
|
|
|
|
unsigned int ctx = linda_qpn_to_ctx ( qp->qpn );
|
|
|
|
|
|
|
|
/* Disable context in hardware */
|
|
|
|
linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
BIT_SET ( &rcvctrl, PortEnable[ctx], 0 );
|
|
|
|
BIT_SET ( &rcvctrl, IntrAvail[ctx], 0 );
|
|
|
|
linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
|
|
|
|
/* Make sure the hardware has seen that the context is disabled */
|
|
|
|
linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
mb();
|
|
|
|
|
|
|
|
/* Free headers ring */
|
|
|
|
free_dma ( linda_wq->header, LINDA_RECV_HEADERS_SIZE );
|
|
|
|
|
|
|
|
/* Free context */
|
|
|
|
linda_free_ctx ( linda, ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise receive datapath
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_init_recv ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_RcvCtrl rcvctrl;
|
|
|
|
struct QIB_7220_scalar rcvegrbase;
|
|
|
|
struct QIB_7220_scalar rcvhdrentsize;
|
|
|
|
struct QIB_7220_scalar rcvhdrcnt;
|
|
|
|
struct QIB_7220_RcvBTHQP rcvbthqp;
|
|
|
|
unsigned int portcfg;
|
|
|
|
unsigned long egrbase;
|
|
|
|
unsigned int eager_array_size_0;
|
|
|
|
unsigned int eager_array_size_other;
|
|
|
|
unsigned int ctx;
|
|
|
|
|
|
|
|
/* Select configuration based on number of contexts */
|
|
|
|
switch ( LINDA_NUM_CONTEXTS ) {
|
|
|
|
case 5:
|
|
|
|
portcfg = LINDA_PORTCFG_5CTX;
|
|
|
|
eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_5CTX_0;
|
|
|
|
eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_5CTX_OTHER;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
portcfg = LINDA_PORTCFG_9CTX;
|
|
|
|
eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_9CTX_0;
|
|
|
|
eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_9CTX_OTHER;
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
portcfg = LINDA_PORTCFG_17CTX;
|
|
|
|
eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_17CTX_0;
|
|
|
|
eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_17CTX_OTHER;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
linker_assert ( 0, invalid_LINDA_NUM_CONTEXTS );
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure number of contexts */
|
|
|
|
memset ( &rcvctrl, 0, sizeof ( rcvctrl ) );
|
|
|
|
BIT_FILL_3 ( &rcvctrl,
|
|
|
|
TailUpd, 1,
|
|
|
|
PortCfg, portcfg,
|
|
|
|
RcvQPMapEnable, 1 );
|
|
|
|
linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset );
|
|
|
|
|
|
|
|
/* Configure receive header buffer sizes */
|
|
|
|
memset ( &rcvhdrcnt, 0, sizeof ( rcvhdrcnt ) );
|
|
|
|
BIT_FILL_1 ( &rcvhdrcnt, Value, LINDA_RECV_HEADER_COUNT );
|
|
|
|
linda_writeq ( linda, &rcvhdrcnt, QIB_7220_RcvHdrCnt_offset );
|
|
|
|
memset ( &rcvhdrentsize, 0, sizeof ( rcvhdrentsize ) );
|
|
|
|
BIT_FILL_1 ( &rcvhdrentsize, Value, ( LINDA_RECV_HEADER_SIZE >> 2 ) );
|
|
|
|
linda_writeq ( linda, &rcvhdrentsize, QIB_7220_RcvHdrEntSize_offset );
|
|
|
|
|
|
|
|
/* Calculate eager array start addresses for each context */
|
|
|
|
linda_readq ( linda, &rcvegrbase, QIB_7220_RcvEgrBase_offset );
|
|
|
|
egrbase = BIT_GET ( &rcvegrbase, Value );
|
|
|
|
linda->recv_wq[0].eager_array = egrbase;
|
|
|
|
linda->recv_wq[0].eager_entries = eager_array_size_0;
|
|
|
|
egrbase += ( eager_array_size_0 * sizeof ( struct QIB_7220_RcvEgr ) );
|
|
|
|
for ( ctx = 1 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) {
|
|
|
|
linda->recv_wq[ctx].eager_array = egrbase;
|
|
|
|
linda->recv_wq[ctx].eager_entries = eager_array_size_other;
|
|
|
|
egrbase += ( eager_array_size_other *
|
|
|
|
sizeof ( struct QIB_7220_RcvEgr ) );
|
|
|
|
}
|
|
|
|
for ( ctx = 0 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) {
|
|
|
|
DBGC ( linda, "Linda %p CTX %d eager array at %lx (%d "
|
|
|
|
"entries)\n", linda, ctx,
|
|
|
|
linda->recv_wq[ctx].eager_array,
|
|
|
|
linda->recv_wq[ctx].eager_entries );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the BTH QP for Infinipath packets to an unused value */
|
|
|
|
memset ( &rcvbthqp, 0, sizeof ( rcvbthqp ) );
|
|
|
|
BIT_FILL_1 ( &rcvbthqp, RcvBTHQP, LINDA_QP_IDETH );
|
|
|
|
linda_writeq ( linda, &rcvbthqp, QIB_7220_RcvBTHQP_offset );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shut down receive datapath
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
*/
|
|
|
|
static void linda_fini_recv ( struct linda *linda __unused ) {
|
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
static int linda_create_cq ( struct ib_device *ibdev,
|
|
|
|
struct ib_completion_queue *cq ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
cq->cqn = ++cqn;
|
|
|
|
DBGC ( linda, "Linda %p CQN %ld created\n", linda, cq->cqn );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy completion queue
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v cq Completion queue
|
|
|
|
*/
|
|
|
|
static void linda_destroy_cq ( struct ib_device *ibdev,
|
|
|
|
struct ib_completion_queue *cq ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
/* Nothing to do */
|
|
|
|
DBGC ( linda, "Linda %p CQN %ld destroyed\n", linda, cq->cqn );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Queue pair operations
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create queue pair
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_create_qp ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
int ctx;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Locate an available context */
|
|
|
|
ctx = linda_alloc_ctx ( linda );
|
|
|
|
if ( ctx < 0 ) {
|
|
|
|
rc = ctx;
|
|
|
|
goto err_alloc_ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set queue pair number based on context index */
|
|
|
|
qp->qpn = linda_ctx_to_qpn ( ctx );
|
|
|
|
|
|
|
|
/* Set work-queue private data pointers */
|
|
|
|
ib_wq_set_drvdata ( &qp->send, &linda->send_wq[ctx] );
|
|
|
|
ib_wq_set_drvdata ( &qp->recv, &linda->recv_wq[ctx] );
|
|
|
|
|
|
|
|
/* Create receive work queue */
|
|
|
|
if ( ( rc = linda_create_recv_wq ( linda, qp ) ) != 0 )
|
|
|
|
goto err_create_recv_wq;
|
|
|
|
|
|
|
|
/* Create send work queue */
|
|
|
|
if ( ( rc = linda_create_send_wq ( linda, qp ) ) != 0 )
|
|
|
|
goto err_create_send_wq;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
linda_destroy_send_wq ( linda, qp );
|
|
|
|
err_create_send_wq:
|
|
|
|
linda_destroy_recv_wq ( linda, qp );
|
|
|
|
err_create_recv_wq:
|
|
|
|
linda_free_ctx ( linda, ctx );
|
|
|
|
err_alloc_ctx:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modify queue pair
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_modify_qp ( struct ib_device *ibdev,
|
2009-07-17 23:27:34 +02:00
|
|
|
struct ib_queue_pair *qp ) {
|
2008-11-07 09:47:21 +01:00
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
/* Nothing to do; the hardware doesn't have a notion of queue
|
|
|
|
* keys
|
|
|
|
*/
|
|
|
|
DBGC ( linda, "Linda %p QPN %ld modified\n", linda, qp->qpn );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy queue pair
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static void linda_destroy_qp ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
linda_destroy_send_wq ( linda, qp );
|
|
|
|
linda_destroy_recv_wq ( linda, 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
|
|
|
|
*/
|
|
|
|
static int linda_post_send ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
struct ib_address_vector *av,
|
|
|
|
struct io_buffer *iobuf ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->send;
|
|
|
|
struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_SendPbc sendpbc;
|
|
|
|
uint8_t header_buf[IB_MAX_HEADER_SIZE];
|
|
|
|
struct io_buffer headers;
|
|
|
|
unsigned int send_buf;
|
|
|
|
unsigned long start_offset;
|
|
|
|
unsigned long offset;
|
|
|
|
size_t len;
|
|
|
|
ssize_t frag_len;
|
|
|
|
uint32_t *data;
|
|
|
|
|
|
|
|
/* Allocate send buffer and calculate offset */
|
|
|
|
send_buf = linda_alloc_send_buf ( linda );
|
|
|
|
start_offset = offset = linda_send_buffer_offset ( linda, send_buf );
|
|
|
|
|
|
|
|
/* Store I/O buffer and send buffer index */
|
|
|
|
assert ( wq->iobufs[linda_wq->prod] == NULL );
|
|
|
|
wq->iobufs[linda_wq->prod] = iobuf;
|
|
|
|
linda_wq->send_buf[linda_wq->prod] = send_buf;
|
|
|
|
|
|
|
|
/* Construct headers */
|
|
|
|
iob_populate ( &headers, header_buf, 0, sizeof ( header_buf ) );
|
|
|
|
iob_reserve ( &headers, sizeof ( header_buf ) );
|
|
|
|
ib_push ( ibdev, &headers, qp, iob_len ( iobuf ), av );
|
|
|
|
|
|
|
|
/* Calculate packet length */
|
|
|
|
len = ( ( sizeof ( sendpbc ) + iob_len ( &headers ) +
|
|
|
|
iob_len ( iobuf ) + 3 ) & ~3 );
|
|
|
|
|
|
|
|
/* Construct send per-buffer control word */
|
|
|
|
memset ( &sendpbc, 0, sizeof ( sendpbc ) );
|
|
|
|
BIT_FILL_2 ( &sendpbc,
|
|
|
|
LengthP1_toibc, ( ( len >> 2 ) - 1 ),
|
|
|
|
VL15, 1 );
|
|
|
|
|
|
|
|
/* Write SendPbc */
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
linda_writeq ( linda, &sendpbc, offset );
|
|
|
|
offset += sizeof ( sendpbc );
|
|
|
|
|
|
|
|
/* Write headers */
|
|
|
|
for ( data = headers.data, frag_len = iob_len ( &headers ) ;
|
|
|
|
frag_len > 0 ; data++, offset += 4, frag_len -= 4 ) {
|
|
|
|
linda_writel ( linda, *data, offset );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write data */
|
|
|
|
for ( data = iobuf->data, frag_len = iob_len ( iobuf ) ;
|
|
|
|
frag_len > 0 ; data++, offset += 4, frag_len -= 4 ) {
|
|
|
|
linda_writel ( linda, *data, offset );
|
|
|
|
}
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
assert ( ( start_offset + len ) == offset );
|
|
|
|
DBGC2 ( linda, "Linda %p QPN %ld TX %d(%d) posted [%lx,%lx)\n",
|
|
|
|
linda, qp->qpn, send_buf, linda_wq->prod,
|
|
|
|
start_offset, offset );
|
|
|
|
|
|
|
|
/* Increment producer counter */
|
|
|
|
linda_wq->prod = ( ( linda_wq->prod + 1 ) & ( wq->num_wqes - 1 ) );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Complete send work queue entry
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @v wqe_idx Work queue entry index
|
|
|
|
*/
|
|
|
|
static void linda_complete_send ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
unsigned int wqe_idx ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->send;
|
|
|
|
struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct io_buffer *iobuf;
|
|
|
|
unsigned int send_buf;
|
|
|
|
|
|
|
|
/* Parse completion */
|
|
|
|
send_buf = linda_wq->send_buf[wqe_idx];
|
|
|
|
DBGC2 ( linda, "Linda %p QPN %ld TX %d(%d) complete\n",
|
|
|
|
linda, qp->qpn, send_buf, wqe_idx );
|
|
|
|
|
|
|
|
/* Complete work queue entry */
|
|
|
|
iobuf = wq->iobufs[wqe_idx];
|
|
|
|
assert ( iobuf != NULL );
|
|
|
|
ib_complete_send ( ibdev, qp, iobuf, 0 );
|
|
|
|
wq->iobufs[wqe_idx] = NULL;
|
|
|
|
|
|
|
|
/* Free send buffer */
|
|
|
|
linda_free_send_buf ( linda, send_buf );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Poll send work queue
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static void linda_poll_send_wq ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->send;
|
|
|
|
struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
unsigned int send_buf;
|
|
|
|
|
|
|
|
/* Look for completions */
|
|
|
|
while ( wq->fill ) {
|
|
|
|
|
|
|
|
/* Check to see if send buffer has completed */
|
|
|
|
send_buf = linda_wq->send_buf[linda_wq->cons];
|
|
|
|
if ( linda_send_buf_in_use ( linda, send_buf ) )
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Complete this buffer */
|
|
|
|
linda_complete_send ( ibdev, qp, linda_wq->cons );
|
|
|
|
|
|
|
|
/* Increment consumer counter */
|
|
|
|
linda_wq->cons = ( ( linda_wq->cons + 1 ) &
|
|
|
|
( wq->num_wqes - 1 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Post receive work queue entry
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @v iobuf I/O buffer
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_post_recv ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
struct io_buffer *iobuf ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->recv;
|
|
|
|
struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_RcvEgr rcvegr;
|
|
|
|
struct QIB_7220_scalar rcvegrindexhead;
|
|
|
|
unsigned int ctx = linda_qpn_to_ctx ( qp->qpn );
|
|
|
|
physaddr_t addr;
|
|
|
|
size_t len;
|
|
|
|
unsigned int wqe_idx;
|
|
|
|
unsigned int bufsize;
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
addr = virt_to_bus ( iobuf->data );
|
|
|
|
len = iob_tailroom ( iobuf );
|
|
|
|
if ( addr & ( LINDA_EAGER_BUFFER_ALIGN - 1 ) ) {
|
|
|
|
DBGC ( linda, "Linda %p QPN %ld misaligned RX buffer "
|
|
|
|
"(%08lx)\n", linda, qp->qpn, addr );
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if ( len != LINDA_RECV_PAYLOAD_SIZE ) {
|
2008-11-19 20:33:05 +01:00
|
|
|
DBGC ( linda, "Linda %p QPN %ld wrong RX buffer size (%zd)\n",
|
2008-11-07 09:47:21 +01:00
|
|
|
linda, qp->qpn, len );
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate eager producer index and WQE index */
|
|
|
|
wqe_idx = ( linda_wq->eager_prod & ( wq->num_wqes - 1 ) );
|
|
|
|
assert ( wq->iobufs[wqe_idx] == NULL );
|
|
|
|
|
|
|
|
/* Store I/O buffer */
|
|
|
|
wq->iobufs[wqe_idx] = iobuf;
|
|
|
|
|
|
|
|
/* Calculate buffer size */
|
|
|
|
switch ( LINDA_RECV_PAYLOAD_SIZE ) {
|
|
|
|
case 2048: bufsize = LINDA_EAGER_BUFFER_2K; break;
|
|
|
|
case 4096: bufsize = LINDA_EAGER_BUFFER_4K; break;
|
|
|
|
case 8192: bufsize = LINDA_EAGER_BUFFER_8K; break;
|
|
|
|
case 16384: bufsize = LINDA_EAGER_BUFFER_16K; break;
|
|
|
|
case 32768: bufsize = LINDA_EAGER_BUFFER_32K; break;
|
|
|
|
case 65536: bufsize = LINDA_EAGER_BUFFER_64K; break;
|
|
|
|
default: linker_assert ( 0, invalid_rx_payload_size );
|
|
|
|
bufsize = LINDA_EAGER_BUFFER_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Post eager buffer */
|
|
|
|
memset ( &rcvegr, 0, sizeof ( rcvegr ) );
|
|
|
|
BIT_FILL_2 ( &rcvegr,
|
|
|
|
Addr, ( addr >> 11 ),
|
|
|
|
BufSize, bufsize );
|
|
|
|
linda_writeq_array8b ( linda, &rcvegr,
|
|
|
|
linda_wq->eager_array, linda_wq->eager_prod );
|
|
|
|
DBGC2 ( linda, "Linda %p QPN %ld RX egr %d(%d) posted [%lx,%lx)\n",
|
|
|
|
linda, qp->qpn, linda_wq->eager_prod, wqe_idx,
|
|
|
|
addr, ( addr + len ) );
|
|
|
|
|
|
|
|
/* Increment producer index */
|
|
|
|
linda_wq->eager_prod = ( ( linda_wq->eager_prod + 1 ) &
|
|
|
|
( linda_wq->eager_entries - 1 ) );
|
|
|
|
|
|
|
|
/* Update head index */
|
|
|
|
memset ( &rcvegrindexhead, 0, sizeof ( rcvegrindexhead ) );
|
|
|
|
BIT_FILL_1 ( &rcvegrindexhead,
|
|
|
|
Value, ( ( linda_wq->eager_prod + 1 ) &
|
|
|
|
( linda_wq->eager_entries - 1 ) ) );
|
|
|
|
linda_writeq_array64k ( linda, &rcvegrindexhead,
|
|
|
|
QIB_7220_RcvEgrIndexHead0_offset, ctx );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Complete receive work queue entry
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @v header_offs Header offset
|
|
|
|
*/
|
|
|
|
static void linda_complete_recv ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
unsigned int header_offs ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->recv;
|
|
|
|
struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_RcvHdrFlags *rcvhdrflags;
|
|
|
|
struct QIB_7220_RcvEgr rcvegr;
|
|
|
|
struct io_buffer headers;
|
|
|
|
struct io_buffer *iobuf;
|
|
|
|
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 iberr, mkerr, tiderr, khdrerr, mtuerr;
|
|
|
|
unsigned int lenerr, parityerr, vcrcerr, icrcerr;
|
|
|
|
unsigned int err;
|
|
|
|
unsigned int hdrqoffset;
|
|
|
|
unsigned int header_len;
|
|
|
|
unsigned int padded_payload_len;
|
|
|
|
unsigned int wqe_idx;
|
|
|
|
size_t payload_len;
|
|
|
|
int qp0;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* RcvHdrFlags are at the end of the header entry */
|
|
|
|
rcvhdrflags = ( linda_wq->header + header_offs +
|
|
|
|
LINDA_RECV_HEADER_SIZE - sizeof ( *rcvhdrflags ) );
|
|
|
|
rcvtype = BIT_GET ( rcvhdrflags, RcvType );
|
|
|
|
pktlen = ( BIT_GET ( rcvhdrflags, PktLen ) << 2 );
|
|
|
|
egrindex = BIT_GET ( rcvhdrflags, EgrIndex );
|
|
|
|
useegrbfr = BIT_GET ( rcvhdrflags, UseEgrBfr );
|
|
|
|
hdrqoffset = ( BIT_GET ( rcvhdrflags, HdrqOffset ) << 2 );
|
|
|
|
iberr = BIT_GET ( rcvhdrflags, IBErr );
|
|
|
|
mkerr = BIT_GET ( rcvhdrflags, MKErr );
|
|
|
|
tiderr = BIT_GET ( rcvhdrflags, TIDErr );
|
|
|
|
khdrerr = BIT_GET ( rcvhdrflags, KHdrErr );
|
|
|
|
mtuerr = BIT_GET ( rcvhdrflags, MTUErr );
|
|
|
|
lenerr = BIT_GET ( rcvhdrflags, LenErr );
|
|
|
|
parityerr = BIT_GET ( rcvhdrflags, ParityErr );
|
|
|
|
vcrcerr = BIT_GET ( rcvhdrflags, VCRCErr );
|
|
|
|
icrcerr = BIT_GET ( rcvhdrflags, ICRCErr );
|
|
|
|
header_len = ( LINDA_RECV_HEADER_SIZE - hdrqoffset -
|
|
|
|
sizeof ( *rcvhdrflags ) );
|
|
|
|
padded_payload_len = ( pktlen - header_len - 4 /* ICRC */ );
|
|
|
|
err = ( iberr | mkerr | tiderr | khdrerr | mtuerr |
|
|
|
|
lenerr | parityerr | vcrcerr | icrcerr );
|
|
|
|
/* IB header is placed immediately before RcvHdrFlags */
|
|
|
|
iob_populate ( &headers, ( ( ( void * ) rcvhdrflags ) - header_len ),
|
|
|
|
header_len, header_len );
|
|
|
|
|
|
|
|
/* Dump diagnostic information */
|
|
|
|
if ( err || ( ! useegrbfr ) ) {
|
|
|
|
DBGC ( linda, "Linda %p QPN %ld RX egr %d%s hdr %d type %d "
|
|
|
|
"len %d(%d+%d+4)%s%s%s%s%s%s%s%s%s%s%s\n", linda,
|
|
|
|
qp->qpn, egrindex, ( useegrbfr ? "" : "(unused)" ),
|
|
|
|
( header_offs / LINDA_RECV_HEADER_SIZE ), rcvtype,
|
|
|
|
pktlen, header_len, padded_payload_len,
|
|
|
|
( err ? " [Err" : "" ), ( iberr ? " IB" : "" ),
|
|
|
|
( mkerr ? " MK" : "" ), ( tiderr ? " TID" : "" ),
|
|
|
|
( khdrerr ? " KHdr" : "" ), ( mtuerr ? " MTU" : "" ),
|
|
|
|
( lenerr ? " Len" : "" ), ( parityerr ? " Parity" : ""),
|
|
|
|
( vcrcerr ? " VCRC" : "" ), ( icrcerr ? " ICRC" : "" ),
|
|
|
|
( err ? "]" : "" ) );
|
|
|
|
} else {
|
|
|
|
DBGC2 ( linda, "Linda %p QPN %ld RX egr %d hdr %d type %d "
|
|
|
|
"len %d(%d+%d+4)\n", linda, qp->qpn, egrindex,
|
|
|
|
( header_offs / LINDA_RECV_HEADER_SIZE ), rcvtype,
|
|
|
|
pktlen, header_len, padded_payload_len );
|
|
|
|
}
|
|
|
|
DBGCP_HDA ( linda, hdrqoffset, headers.data,
|
|
|
|
( header_len + sizeof ( *rcvhdrflags ) ) );
|
|
|
|
|
|
|
|
/* Parse header to generate address vector */
|
|
|
|
qp0 = ( qp->qpn == 0 );
|
|
|
|
intended_qp = NULL;
|
|
|
|
if ( ( rc = ib_pull ( ibdev, &headers, ( qp0 ? &intended_qp : NULL ),
|
|
|
|
&payload_len, &av ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p could not parse headers: %s\n",
|
|
|
|
linda, strerror ( rc ) );
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
if ( linda_wq->eager_cons == linda_wq->eager_prod )
|
|
|
|
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 ( ( linda_wq->eager_cons ==
|
|
|
|
( ( egrindex + 1 ) & ( linda_wq->eager_entries - 1 ) )))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Identify work queue entry and corresponding I/O
|
|
|
|
* buffer.
|
|
|
|
*/
|
|
|
|
wqe_idx = ( linda_wq->eager_cons & ( wq->num_wqes - 1 ) );
|
|
|
|
iobuf = wq->iobufs[wqe_idx];
|
|
|
|
assert ( iobuf != NULL );
|
|
|
|
wq->iobufs[wqe_idx] = NULL;
|
|
|
|
|
|
|
|
/* Complete the eager buffer */
|
|
|
|
if ( linda_wq->eager_cons == egrindex ) {
|
|
|
|
/* Completing the eager buffer described in
|
|
|
|
* this header entry.
|
|
|
|
*/
|
|
|
|
iob_put ( iobuf, payload_len );
|
|
|
|
rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) );
|
|
|
|
/* Redirect to target QP if necessary */
|
|
|
|
if ( qp != intended_qp ) {
|
|
|
|
DBGC ( linda, "Linda %p redirecting QPN %ld "
|
|
|
|
"=> %ld\n",
|
|
|
|
linda, qp->qpn, intended_qp->qpn );
|
|
|
|
/* Compensate for incorrect fill levels */
|
|
|
|
qp->recv.fill--;
|
|
|
|
intended_qp->recv.fill++;
|
|
|
|
}
|
|
|
|
ib_complete_recv ( ibdev, intended_qp, &av, iobuf, rc);
|
|
|
|
} else {
|
|
|
|
/* Completing on a skipped-over eager buffer */
|
|
|
|
ib_complete_recv ( ibdev, qp, &av, iobuf, -ECANCELED );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear eager buffer */
|
|
|
|
memset ( &rcvegr, 0, sizeof ( rcvegr ) );
|
|
|
|
linda_writeq_array8b ( linda, &rcvegr, linda_wq->eager_array,
|
|
|
|
linda_wq->eager_cons );
|
|
|
|
|
|
|
|
/* Increment consumer index */
|
|
|
|
linda_wq->eager_cons = ( ( linda_wq->eager_cons + 1 ) &
|
|
|
|
( linda_wq->eager_entries - 1 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Poll receive work queue
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
*/
|
|
|
|
static void linda_poll_recv_wq ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct ib_work_queue *wq = &qp->recv;
|
|
|
|
struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq );
|
|
|
|
struct QIB_7220_RcvHdrHead0 rcvhdrhead;
|
|
|
|
unsigned int ctx = linda_qpn_to_ctx ( qp->qpn );
|
|
|
|
unsigned int header_prod;
|
|
|
|
|
|
|
|
/* Check for received packets */
|
|
|
|
header_prod = ( BIT_GET ( &linda_wq->header_prod, Value ) << 2 );
|
|
|
|
if ( header_prod == linda_wq->header_cons )
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Process all received packets */
|
|
|
|
while ( linda_wq->header_cons != header_prod ) {
|
|
|
|
|
|
|
|
/* Complete the receive */
|
|
|
|
linda_complete_recv ( ibdev, qp, linda_wq->header_cons );
|
|
|
|
|
|
|
|
/* Increment the consumer offset */
|
|
|
|
linda_wq->header_cons += LINDA_RECV_HEADER_SIZE;
|
|
|
|
linda_wq->header_cons %= LINDA_RECV_HEADERS_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update consumer offset */
|
|
|
|
memset ( &rcvhdrhead, 0, sizeof ( rcvhdrhead ) );
|
|
|
|
BIT_FILL_2 ( &rcvhdrhead,
|
|
|
|
RcvHeadPointer, ( linda_wq->header_cons >> 2 ),
|
|
|
|
counter, 1 );
|
|
|
|
linda_writeq_array64k ( linda, &rcvhdrhead,
|
|
|
|
QIB_7220_RcvHdrHead0_offset, ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Poll completion queue
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v cq Completion queue
|
|
|
|
*/
|
|
|
|
static void linda_poll_cq ( struct ib_device *ibdev,
|
|
|
|
struct ib_completion_queue *cq ) {
|
|
|
|
struct ib_work_queue *wq;
|
|
|
|
|
|
|
|
/* Poll associated send and receive queues */
|
|
|
|
list_for_each_entry ( wq, &cq->work_queues, list ) {
|
|
|
|
if ( wq->is_send ) {
|
|
|
|
linda_poll_send_wq ( ibdev, wq->qp );
|
|
|
|
} else {
|
|
|
|
linda_poll_recv_wq ( ibdev, wq->qp );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Event queues
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Poll event queue
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
*/
|
|
|
|
static void linda_poll_eq ( struct ib_device *ibdev ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct QIB_7220_ErrStatus errstatus;
|
|
|
|
struct QIB_7220_ErrClear errclear;
|
|
|
|
|
|
|
|
/* Check for link status changes */
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
linda_readq ( linda, &errstatus, QIB_7220_ErrStatus_offset );
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
if ( BIT_GET ( &errstatus, IBStatusChanged ) ) {
|
|
|
|
linda_link_state_changed ( ibdev );
|
|
|
|
memset ( &errclear, 0, sizeof ( errclear ) );
|
|
|
|
BIT_FILL_1 ( &errclear, IBStatusChangedClear, 1 );
|
|
|
|
linda_writeq ( linda, &errclear, QIB_7220_ErrClear_offset );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Infiniband link-layer operations
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise Infiniband link
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_open ( struct ib_device *ibdev ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct QIB_7220_Control control;
|
|
|
|
|
|
|
|
/* Disable link */
|
|
|
|
linda_readq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
BIT_SET ( &control, LinkEn, 1 );
|
|
|
|
linda_writeq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close Infiniband link
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
*/
|
|
|
|
static void linda_close ( struct ib_device *ibdev ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
struct QIB_7220_Control control;
|
|
|
|
|
|
|
|
/* Disable link */
|
|
|
|
linda_readq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
BIT_SET ( &control, LinkEn, 0 );
|
|
|
|
linda_writeq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Multicast group operations
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attach to multicast group
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @v gid Multicast GID
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_mcast_attach ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
struct ib_gid *gid ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
( void ) linda;
|
|
|
|
( void ) qp;
|
|
|
|
( void ) gid;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detach from multicast group
|
|
|
|
*
|
|
|
|
* @v ibdev Infiniband device
|
|
|
|
* @v qp Queue pair
|
|
|
|
* @v gid Multicast GID
|
|
|
|
*/
|
|
|
|
static void linda_mcast_detach ( struct ib_device *ibdev,
|
|
|
|
struct ib_queue_pair *qp,
|
|
|
|
struct ib_gid *gid ) {
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
( void ) linda;
|
|
|
|
( void ) qp;
|
|
|
|
( void ) gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Linda Infiniband operations */
|
|
|
|
static struct ib_device_operations linda_ib_operations = {
|
|
|
|
.create_cq = linda_create_cq,
|
|
|
|
.destroy_cq = linda_destroy_cq,
|
|
|
|
.create_qp = linda_create_qp,
|
|
|
|
.modify_qp = linda_modify_qp,
|
|
|
|
.destroy_qp = linda_destroy_qp,
|
|
|
|
.post_send = linda_post_send,
|
|
|
|
.post_recv = linda_post_recv,
|
|
|
|
.poll_cq = linda_poll_cq,
|
|
|
|
.poll_eq = linda_poll_eq,
|
|
|
|
.open = linda_open,
|
|
|
|
.close = linda_close,
|
|
|
|
.mcast_attach = linda_mcast_attach,
|
|
|
|
.mcast_detach = linda_mcast_detach,
|
2009-07-07 19:30:15 +02:00
|
|
|
.set_port_info = linda_set_port_info,
|
2009-07-17 23:27:34 +02:00
|
|
|
.set_pkey_table = linda_set_pkey_table,
|
2008-11-07 09:47:21 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* I2C bus operations
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Linda I2C bit to GPIO mappings */
|
|
|
|
static unsigned int linda_i2c_bits[] = {
|
|
|
|
[I2C_BIT_SCL] = ( 1 << LINDA_GPIO_SCL ),
|
|
|
|
[I2C_BIT_SDA] = ( 1 << LINDA_GPIO_SDA ),
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read Linda 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
|
|
|
|
*/
|
|
|
|
static int linda_i2c_read_bit ( struct bit_basher *basher,
|
|
|
|
unsigned int bit_id ) {
|
|
|
|
struct linda *linda =
|
|
|
|
container_of ( basher, struct linda, i2c.basher );
|
|
|
|
struct QIB_7220_EXTStatus extstatus;
|
|
|
|
unsigned int status;
|
|
|
|
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
linda_readq ( linda, &extstatus, QIB_7220_EXTStatus_offset );
|
|
|
|
status = ( BIT_GET ( &extstatus, GPIOIn ) & linda_i2c_bits[bit_id] );
|
|
|
|
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write Linda I2C line status
|
|
|
|
*
|
|
|
|
* @v basher Bit-bashing interface
|
|
|
|
* @v bit_id Bit number
|
|
|
|
* @v data Value to write
|
|
|
|
*/
|
|
|
|
static void linda_i2c_write_bit ( struct bit_basher *basher,
|
|
|
|
unsigned int bit_id, unsigned long data ) {
|
|
|
|
struct linda *linda =
|
|
|
|
container_of ( basher, struct linda, i2c.basher );
|
|
|
|
struct QIB_7220_EXTCtrl extctrl;
|
|
|
|
struct QIB_7220_GPIO gpioout;
|
|
|
|
unsigned int bit = linda_i2c_bits[bit_id];
|
|
|
|
unsigned int outputs = 0;
|
|
|
|
unsigned int output_enables = 0;
|
|
|
|
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
/* Read current GPIO mask and outputs */
|
|
|
|
linda_readq ( linda, &extctrl, QIB_7220_EXTCtrl_offset );
|
|
|
|
linda_readq ( linda, &gpioout, QIB_7220_GPIOOut_offset );
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
output_enables = BIT_GET ( &extctrl, GPIOOe );
|
|
|
|
output_enables = ( ( output_enables & ~bit ) | ( ~data & bit ) );
|
|
|
|
outputs = BIT_GET ( &gpioout, GPIO );
|
|
|
|
outputs = ( outputs & ~bit );
|
|
|
|
BIT_SET ( &extctrl, GPIOOe, output_enables );
|
|
|
|
BIT_SET ( &gpioout, GPIO, outputs );
|
|
|
|
|
|
|
|
/* Write the output enable first; that way we avoid logic
|
|
|
|
* hazards.
|
|
|
|
*/
|
|
|
|
linda_writeq ( linda, &extctrl, QIB_7220_EXTCtrl_offset );
|
|
|
|
linda_writeq ( linda, &gpioout, QIB_7220_GPIOOut_offset );
|
|
|
|
mb();
|
|
|
|
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Linda I2C bit-bashing interface operations */
|
|
|
|
static struct bit_basher_operations linda_i2c_basher_ops = {
|
|
|
|
.read = linda_i2c_read_bit,
|
|
|
|
.write = linda_i2c_write_bit,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise Linda I2C subsystem
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_init_i2c ( struct linda *linda ) {
|
|
|
|
static int try_eeprom_address[] = { 0x51, 0x50 };
|
|
|
|
unsigned int i;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Initialise bus */
|
|
|
|
if ( ( rc = init_i2c_bit_basher ( &linda->i2c,
|
|
|
|
&linda_i2c_basher_ops ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p could not initialise I2C bus: %s\n",
|
|
|
|
linda, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Probe for devices */
|
|
|
|
for ( i = 0 ; i < ( sizeof ( try_eeprom_address ) /
|
|
|
|
sizeof ( try_eeprom_address[0] ) ) ; i++ ) {
|
|
|
|
init_i2c_eeprom ( &linda->eeprom, try_eeprom_address[i] );
|
|
|
|
if ( ( rc = i2c_check_presence ( &linda->i2c.i2c,
|
|
|
|
&linda->eeprom ) ) == 0 ) {
|
|
|
|
DBGC2 ( linda, "Linda %p found EEPROM at %02x\n",
|
|
|
|
linda, try_eeprom_address[i] );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p could not find EEPROM\n", linda );
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read EEPROM parameters
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v guid GUID to fill in
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_read_eeprom ( struct linda *linda,
|
|
|
|
struct ib_gid_half *guid ) {
|
|
|
|
struct i2c_interface *i2c = &linda->i2c.i2c;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Read GUID */
|
|
|
|
if ( ( rc = i2c->read ( i2c, &linda->eeprom, LINDA_EEPROM_GUID_OFFSET,
|
|
|
|
guid->bytes, sizeof ( *guid ) ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p could not read GUID: %s\n",
|
|
|
|
linda, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
DBGC2 ( linda, "Linda %p has GUID %02x:%02x:%02x:%02x:%02x:%02x:"
|
|
|
|
"%02x:%02x\n", linda, guid->bytes[0], guid->bytes[1],
|
|
|
|
guid->bytes[2], guid->bytes[3], guid->bytes[4],
|
|
|
|
guid->bytes[5], guid->bytes[6], guid->bytes[7] );
|
|
|
|
|
|
|
|
/* Read serial number (debug only) */
|
|
|
|
if ( DBG_LOG ) {
|
|
|
|
uint8_t serial[LINDA_EEPROM_SERIAL_SIZE + 1];
|
|
|
|
|
|
|
|
serial[ sizeof ( serial ) - 1 ] = '\0';
|
|
|
|
if ( ( rc = i2c->read ( i2c, &linda->eeprom,
|
|
|
|
LINDA_EEPROM_SERIAL_OFFSET, serial,
|
|
|
|
( sizeof ( serial ) - 1 ) ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p could not read serial: %s\n",
|
|
|
|
linda, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
DBGC2 ( linda, "Linda %p has serial number \"%s\"\n",
|
|
|
|
linda, serial );
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* External parallel bus access
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request ownership of the IB external parallel bus
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_request ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_ibsd_epb_access_ctrl access;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* Request ownership */
|
|
|
|
memset ( &access, 0, sizeof ( access ) );
|
|
|
|
BIT_FILL_1 ( &access, sw_ib_epb_req, 1 );
|
|
|
|
linda_writeq ( linda, &access, QIB_7220_ibsd_epb_access_ctrl_offset );
|
|
|
|
|
|
|
|
/* Wait for ownership to be granted */
|
|
|
|
for ( i = 0 ; i < LINDA_EPB_REQUEST_MAX_WAIT_US ; i++ ) {
|
|
|
|
linda_readq ( linda, &access,
|
|
|
|
QIB_7220_ibsd_epb_access_ctrl_offset );
|
|
|
|
if ( BIT_GET ( &access, sw_ib_epb_req_granted ) )
|
|
|
|
return 0;
|
|
|
|
udelay ( 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p timed out waiting for IB EPB request\n",
|
|
|
|
linda );
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for IB external parallel bus transaction to complete
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v xact Buffer to hold transaction result
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_wait ( struct linda *linda,
|
|
|
|
struct QIB_7220_ibsd_epb_transaction_reg *xact ) {
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* Discard first read to allow for signals crossing clock domains */
|
|
|
|
linda_readq ( linda, xact, QIB_7220_ibsd_epb_transaction_reg_offset );
|
|
|
|
|
|
|
|
for ( i = 0 ; i < LINDA_EPB_XACT_MAX_WAIT_US ; i++ ) {
|
|
|
|
linda_readq ( linda, xact,
|
|
|
|
QIB_7220_ibsd_epb_transaction_reg_offset );
|
|
|
|
if ( BIT_GET ( xact, ib_epb_rdy ) ) {
|
|
|
|
if ( BIT_GET ( xact, ib_epb_req_error ) ) {
|
|
|
|
DBGC ( linda, "Linda %p EPB transaction "
|
|
|
|
"failed\n", linda );
|
|
|
|
return -EIO;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
udelay ( 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p timed out waiting for IB EPB transaction\n",
|
|
|
|
linda );
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release ownership of the IB external parallel bus
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
*/
|
|
|
|
static void linda_ib_epb_release ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_ibsd_epb_access_ctrl access;
|
|
|
|
|
|
|
|
memset ( &access, 0, sizeof ( access ) );
|
|
|
|
BIT_FILL_1 ( &access, sw_ib_epb_req, 0 );
|
|
|
|
linda_writeq ( linda, &access, QIB_7220_ibsd_epb_access_ctrl_offset );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read data via IB external parallel bus
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v location EPB location
|
|
|
|
* @ret data Data read, or negative error
|
|
|
|
*
|
|
|
|
* You must have already acquired ownership of the IB external
|
|
|
|
* parallel bus.
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_read ( struct linda *linda, unsigned int location ) {
|
|
|
|
struct QIB_7220_ibsd_epb_transaction_reg xact;
|
|
|
|
unsigned int data;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Ensure no transaction is currently in progress */
|
|
|
|
if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Process data */
|
|
|
|
memset ( &xact, 0, sizeof ( xact ) );
|
|
|
|
BIT_FILL_3 ( &xact,
|
|
|
|
ib_epb_address, LINDA_EPB_LOC_ADDRESS ( location ),
|
|
|
|
ib_epb_read_write, LINDA_EPB_READ,
|
|
|
|
ib_epb_cs, LINDA_EPB_LOC_CS ( location ) );
|
|
|
|
linda_writeq ( linda, &xact,
|
|
|
|
QIB_7220_ibsd_epb_transaction_reg_offset );
|
|
|
|
|
|
|
|
/* Wait for transaction to complete */
|
|
|
|
if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
data = BIT_GET ( &xact, ib_epb_data );
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write data via IB external parallel bus
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v location EPB location
|
|
|
|
* @v data Data to write
|
|
|
|
* @ret rc Return status code
|
|
|
|
*
|
|
|
|
* You must have already acquired ownership of the IB external
|
|
|
|
* parallel bus.
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_write ( struct linda *linda, unsigned int location,
|
|
|
|
unsigned int data ) {
|
|
|
|
struct QIB_7220_ibsd_epb_transaction_reg xact;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Ensure no transaction is currently in progress */
|
|
|
|
if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Process data */
|
|
|
|
memset ( &xact, 0, sizeof ( xact ) );
|
|
|
|
BIT_FILL_4 ( &xact,
|
|
|
|
ib_epb_data, data,
|
|
|
|
ib_epb_address, LINDA_EPB_LOC_ADDRESS ( location ),
|
|
|
|
ib_epb_read_write, LINDA_EPB_WRITE,
|
|
|
|
ib_epb_cs, LINDA_EPB_LOC_CS ( location ) );
|
|
|
|
linda_writeq ( linda, &xact,
|
|
|
|
QIB_7220_ibsd_epb_transaction_reg_offset );
|
|
|
|
|
|
|
|
/* Wait for transaction to complete */
|
|
|
|
if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read/modify/write EPB register
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v cs Chip select
|
|
|
|
* @v channel Channel
|
|
|
|
* @v element Element
|
|
|
|
* @v reg Register
|
|
|
|
* @v value Value to set
|
|
|
|
* @v mask Mask to apply to old value
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_mod_reg ( struct linda *linda, unsigned int cs,
|
|
|
|
unsigned int channel, unsigned int element,
|
|
|
|
unsigned int reg, unsigned int value,
|
|
|
|
unsigned int mask ) {
|
|
|
|
unsigned int location;
|
|
|
|
int old_value;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
assert ( ( value & mask ) == value );
|
|
|
|
|
|
|
|
/* Acquire bus ownership */
|
|
|
|
if ( ( rc = linda_ib_epb_request ( linda ) ) != 0 )
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Read existing value, if necessary */
|
|
|
|
location = LINDA_EPB_LOC ( cs, channel, element, reg );
|
|
|
|
if ( (~mask) & 0xff ) {
|
|
|
|
old_value = linda_ib_epb_read ( linda, location );
|
|
|
|
if ( old_value < 0 ) {
|
|
|
|
rc = old_value;
|
|
|
|
goto out_release;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
old_value = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update value */
|
|
|
|
value = ( ( old_value & ~mask ) | value );
|
|
|
|
DBGCP ( linda, "Linda %p CS %d EPB(%d,%d,%#02x) %#02x => %#02x\n",
|
|
|
|
linda, cs, channel, element, reg, old_value, value );
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda, location, value ) ) != 0 )
|
|
|
|
goto out_release;
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
/* Release bus */
|
|
|
|
linda_ib_epb_release ( linda );
|
|
|
|
out:
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transfer data to/from microcontroller RAM
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v address Starting address
|
|
|
|
* @v write Data to write, or NULL
|
|
|
|
* @v read Data to read, or NULL
|
|
|
|
* @v len Length of data
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_ib_epb_ram_xfer ( struct linda *linda, unsigned int address,
|
|
|
|
const void *write, void *read,
|
|
|
|
size_t len ) {
|
|
|
|
unsigned int control;
|
|
|
|
unsigned int address_hi;
|
|
|
|
unsigned int address_lo;
|
|
|
|
int data;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
DBG_DISABLE ( DBGLVL_IO );
|
|
|
|
|
|
|
|
assert ( ! ( write && read ) );
|
|
|
|
assert ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 );
|
|
|
|
assert ( ( len % LINDA_EPB_UC_CHUNK_SIZE ) == 0 );
|
|
|
|
|
|
|
|
/* Acquire bus ownership */
|
|
|
|
if ( ( rc = linda_ib_epb_request ( linda ) ) != 0 )
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Process data */
|
|
|
|
while ( len ) {
|
|
|
|
|
|
|
|
/* Reset the address for each new chunk */
|
|
|
|
if ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ) {
|
|
|
|
|
|
|
|
/* Write the control register */
|
|
|
|
control = ( read ? LINDA_EPB_UC_CTL_READ :
|
|
|
|
LINDA_EPB_UC_CTL_WRITE );
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda,
|
|
|
|
LINDA_EPB_UC_CTL,
|
|
|
|
control ) ) != 0 )
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Write the address registers */
|
|
|
|
address_hi = ( address >> 8 );
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda,
|
|
|
|
LINDA_EPB_UC_ADDR_HI,
|
|
|
|
address_hi ) ) != 0 )
|
|
|
|
break;
|
|
|
|
address_lo = ( address & 0xff );
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda,
|
|
|
|
LINDA_EPB_UC_ADDR_LO,
|
|
|
|
address_lo ) ) != 0 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read or write the data */
|
|
|
|
if ( read ) {
|
|
|
|
data = linda_ib_epb_read ( linda, LINDA_EPB_UC_DATA );
|
|
|
|
if ( data < 0 ) {
|
|
|
|
rc = data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*( ( uint8_t * ) read++ ) = data;
|
|
|
|
} else {
|
|
|
|
data = *( ( uint8_t * ) write++ );
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda,
|
|
|
|
LINDA_EPB_UC_DATA,
|
|
|
|
data ) ) != 0 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
address++;
|
|
|
|
len--;
|
|
|
|
|
|
|
|
/* Reset the control byte after each chunk */
|
|
|
|
if ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ) {
|
|
|
|
if ( ( rc = linda_ib_epb_write ( linda,
|
|
|
|
LINDA_EPB_UC_CTL,
|
|
|
|
0 ) ) != 0 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release bus */
|
|
|
|
linda_ib_epb_release ( linda );
|
|
|
|
|
|
|
|
out:
|
|
|
|
DBG_ENABLE ( DBGLVL_IO );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Infiniband SerDes initialisation
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** A Linda SerDes parameter */
|
|
|
|
struct linda_serdes_param {
|
|
|
|
/** EPB address as constructed by LINDA_EPB_ADDRESS() */
|
|
|
|
uint16_t address;
|
|
|
|
/** Value to set */
|
|
|
|
uint8_t value;
|
|
|
|
/** Mask to apply to old value */
|
|
|
|
uint8_t mask;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/** Magic "all channels" channel number */
|
|
|
|
#define LINDA_EPB_ALL_CHANNELS 31
|
|
|
|
|
|
|
|
/** End of SerDes parameter list marker */
|
|
|
|
#define LINDA_SERDES_PARAM_END { 0, 0, 0 }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Program IB SerDes register(s)
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v param SerDes parameter
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_set_serdes_param ( struct linda *linda,
|
|
|
|
struct linda_serdes_param *param ) {
|
|
|
|
unsigned int channel;
|
|
|
|
unsigned int channel_start;
|
|
|
|
unsigned int channel_end;
|
|
|
|
unsigned int element;
|
|
|
|
unsigned int reg;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Break down the EPB address and determine channels */
|
|
|
|
channel = LINDA_EPB_ADDRESS_CHANNEL ( param->address );
|
|
|
|
element = LINDA_EPB_ADDRESS_ELEMENT ( param->address );
|
|
|
|
reg = LINDA_EPB_ADDRESS_REG ( param->address );
|
|
|
|
if ( channel == LINDA_EPB_ALL_CHANNELS ) {
|
|
|
|
channel_start = 0;
|
|
|
|
channel_end = 3;
|
|
|
|
} else {
|
|
|
|
channel_start = channel_end = channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Modify register for each specified channel */
|
|
|
|
for ( channel = channel_start ; channel <= channel_end ; channel++ ) {
|
|
|
|
if ( ( rc = linda_ib_epb_mod_reg ( linda, LINDA_EPB_CS_SERDES,
|
|
|
|
channel, element, reg,
|
|
|
|
param->value,
|
|
|
|
param->mask ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Program IB SerDes registers
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @v param SerDes parameters
|
|
|
|
* @v count Number of parameters
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_set_serdes_params ( struct linda *linda,
|
|
|
|
struct linda_serdes_param *params ) {
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
for ( ; params->mask != 0 ; params++ ){
|
|
|
|
if ( ( rc = linda_set_serdes_param ( linda,
|
|
|
|
params ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define LINDA_DDS_VAL( amp_d, main_d, ipst_d, ipre_d, \
|
|
|
|
amp_s, main_s, ipst_s, ipre_s ) \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x00 ), \
|
|
|
|
( ( ( amp_d & 0x1f ) << 1 ) | 1 ), 0xff }, \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x01 ), \
|
|
|
|
( ( ( amp_s & 0x1f ) << 1 ) | 1 ), 0xff }, \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x09 ), \
|
|
|
|
( ( main_d << 3 ) | 4 | ( ipre_d >> 2 ) ), 0xff }, \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x0a ), \
|
|
|
|
( ( main_s << 3 ) | 4 | ( ipre_s >> 2 ) ), 0xff }, \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x06 ), \
|
|
|
|
( ( ( ipst_d & 0xf ) << 1 ) | \
|
|
|
|
( ( ipre_d & 3 ) << 6 ) | 0x21 ), 0xff }, \
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x07 ), \
|
|
|
|
( ( ( ipst_s & 0xf ) << 1 ) | \
|
|
|
|
( ( ipre_s & 3 ) << 6) | 0x21 ), 0xff }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Linda SerDes default parameters
|
|
|
|
*
|
|
|
|
* These magic start-of-day values are taken from the Linux driver.
|
|
|
|
*/
|
|
|
|
static struct linda_serdes_param linda_serdes_defaults1[] = {
|
|
|
|
/* RXHSCTRL0 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x00 ), 0xd4, 0xff },
|
|
|
|
/* VCDL_DAC2 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x05 ), 0x2d, 0xff },
|
|
|
|
/* VCDL_CTRL2 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x08 ), 0x03, 0x0f },
|
|
|
|
/* START_EQ1 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x10, 0xff },
|
|
|
|
/* START_EQ2 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x28 ), 0x30, 0xff },
|
|
|
|
/* BACTRL */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x0e ), 0x40, 0xff },
|
|
|
|
/* LDOUTCTRL1 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x06 ), 0x04, 0xff },
|
|
|
|
/* RXHSSTATUS */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x0f ), 0x04, 0xff },
|
|
|
|
/* End of this block */
|
|
|
|
LINDA_SERDES_PARAM_END
|
|
|
|
};
|
|
|
|
static struct linda_serdes_param linda_serdes_defaults2[] = {
|
|
|
|
/* LDOUTCTRL1 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x06 ), 0x00, 0xff },
|
|
|
|
/* DDS values */
|
|
|
|
LINDA_DDS_VAL ( 31, 19, 12, 0, 29, 22, 9, 0 ),
|
|
|
|
/* Set Rcv Eq. to Preset node */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x10, 0xff },
|
|
|
|
/* DFELTHFDR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x08 ), 0x00, 0xff },
|
|
|
|
/* DFELTHHDR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x21 ), 0x00, 0xff },
|
|
|
|
/* TLTHFDR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x09 ), 0x02, 0xff },
|
|
|
|
/* TLTHHDR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x23 ), 0x02, 0xff },
|
|
|
|
/* ZFR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1b ), 0x0c, 0xff },
|
|
|
|
/* ZCNT) */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1c ), 0x0c, 0xff },
|
|
|
|
/* GFR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1e ), 0x10, 0xff },
|
|
|
|
/* GHR */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1f ), 0x10, 0xff },
|
|
|
|
/* VCDL_CTRL0 toggle */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x06 ), 0x20, 0xff },
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x06 ), 0x00, 0xff },
|
|
|
|
/* CMUCTRL5 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( 7, 0, 0x15 ), 0x80, 0xff },
|
|
|
|
/* End of this block */
|
|
|
|
LINDA_SERDES_PARAM_END
|
|
|
|
};
|
|
|
|
static struct linda_serdes_param linda_serdes_defaults3[] = {
|
|
|
|
/* START_EQ1 */
|
|
|
|
{ LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x00, 0x38 },
|
|
|
|
/* End of this block */
|
|
|
|
LINDA_SERDES_PARAM_END
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2008-11-18 00:55:43 +01:00
|
|
|
* Program the microcontroller RAM
|
2008-11-07 09:47:21 +01:00
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2008-11-18 00:55:43 +01:00
|
|
|
static int linda_program_uc_ram ( struct linda *linda ) {
|
2008-11-07 09:47:21 +01:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ( ( rc = linda_ib_epb_ram_xfer ( linda, 0, linda_ib_fw, NULL,
|
|
|
|
sizeof ( linda_ib_fw ) ) ) != 0 ){
|
|
|
|
DBGC ( linda, "Linda %p could not load IB firmware: %s\n",
|
|
|
|
linda, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-11-18 00:55:43 +01:00
|
|
|
* Verify the microcontroller RAM
|
2008-11-07 09:47:21 +01:00
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2008-11-18 00:55:43 +01:00
|
|
|
static int linda_verify_uc_ram ( struct linda *linda ) {
|
2008-11-07 09:47:21 +01:00
|
|
|
uint8_t verify[LINDA_EPB_UC_CHUNK_SIZE];
|
|
|
|
unsigned int offset;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
for ( offset = 0 ; offset < sizeof ( linda_ib_fw );
|
|
|
|
offset += sizeof ( verify ) ) {
|
|
|
|
if ( ( rc = linda_ib_epb_ram_xfer ( linda, offset,
|
|
|
|
NULL, verify,
|
|
|
|
sizeof (verify) )) != 0 ){
|
|
|
|
DBGC ( linda, "Linda %p could not read back IB "
|
|
|
|
"firmware: %s\n", linda, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if ( memcmp ( ( linda_ib_fw + offset ), verify,
|
|
|
|
sizeof ( verify ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p firmware verification failed "
|
|
|
|
"at offset %#x\n", linda, offset );
|
|
|
|
DBGC_HDA ( linda, offset, ( linda_ib_fw + offset ),
|
|
|
|
sizeof ( verify ) );
|
|
|
|
DBGC_HDA ( linda, offset, verify, sizeof ( verify ) );
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC2 ( linda, "Linda %p firmware verified ok\n", linda );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-11-18 00:55:43 +01:00
|
|
|
* Use the microcontroller to trim the IB link
|
2008-11-07 09:47:21 +01:00
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_trim_ib ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_IBSerDesCtrl ctrl;
|
|
|
|
struct QIB_7220_IntStatus intstatus;
|
|
|
|
unsigned int i;
|
|
|
|
int rc;
|
|
|
|
|
2008-11-18 00:55:43 +01:00
|
|
|
/* Bring the microcontroller out of reset */
|
2008-11-07 09:47:21 +01:00
|
|
|
linda_readq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset );
|
|
|
|
BIT_SET ( &ctrl, ResetIB_uC_Core, 0 );
|
|
|
|
linda_writeq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset );
|
|
|
|
|
|
|
|
/* Wait for the "trim done" signal */
|
|
|
|
for ( i = 0 ; i < LINDA_TRIM_DONE_MAX_WAIT_MS ; i++ ) {
|
|
|
|
linda_readq ( linda, &intstatus, QIB_7220_IntStatus_offset );
|
|
|
|
if ( BIT_GET ( &intstatus, IBSerdesTrimDone ) ) {
|
|
|
|
rc = 0;
|
|
|
|
goto out_reset;
|
|
|
|
}
|
|
|
|
mdelay ( 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( linda, "Linda %p timed out waiting for trim done\n", linda );
|
|
|
|
rc = -ETIMEDOUT;
|
|
|
|
out_reset:
|
2008-11-18 00:55:43 +01:00
|
|
|
/* Put the microcontroller back into reset */
|
2008-11-07 09:47:21 +01:00
|
|
|
BIT_SET ( &ctrl, ResetIB_uC_Core, 1 );
|
|
|
|
linda_writeq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset );
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise the IB SerDes
|
|
|
|
*
|
|
|
|
* @v linda Linda device
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_init_ib_serdes ( struct linda *linda ) {
|
|
|
|
struct QIB_7220_Control control;
|
|
|
|
struct QIB_7220_IBCCtrl ibcctrl;
|
|
|
|
struct QIB_7220_IBCDDRCtrl ibcddrctrl;
|
|
|
|
struct QIB_7220_XGXSCfg xgxscfg;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Disable link */
|
|
|
|
linda_readq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
BIT_SET ( &control, LinkEn, 0 );
|
|
|
|
linda_writeq ( linda, &control, QIB_7220_Control_offset );
|
|
|
|
|
|
|
|
/* Configure sensible defaults for IBC */
|
|
|
|
memset ( &ibcctrl, 0, sizeof ( ibcctrl ) );
|
|
|
|
BIT_FILL_6 ( &ibcctrl, /* Tuning values taken from Linux driver */
|
|
|
|
FlowCtrlPeriod, 0x03,
|
|
|
|
FlowCtrlWaterMark, 0x05,
|
|
|
|
MaxPktLen, ( ( LINDA_RECV_HEADER_SIZE +
|
|
|
|
LINDA_RECV_PAYLOAD_SIZE +
|
|
|
|
4 /* ICRC */ ) >> 2 ),
|
|
|
|
PhyerrThreshold, 0xf,
|
|
|
|
OverrunThreshold, 0xf,
|
|
|
|
CreditScale, 0x4 );
|
|
|
|
linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
|
|
|
|
|
|
|
|
/* Force SDR only to avoid needing all the DDR tuning,
|
2009-07-11 02:14:43 +02:00
|
|
|
* Mellanox compatibility hacks etc. SDR is plenty for
|
2008-11-07 09:47:21 +01:00
|
|
|
* boot-time operation.
|
|
|
|
*/
|
|
|
|
linda_readq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset );
|
|
|
|
BIT_SET ( &ibcddrctrl, IB_ENHANCED_MODE, 0 );
|
|
|
|
BIT_SET ( &ibcddrctrl, SD_SPEED_SDR, 1 );
|
|
|
|
BIT_SET ( &ibcddrctrl, SD_SPEED_DDR, 0 );
|
|
|
|
BIT_SET ( &ibcddrctrl, SD_SPEED_QDR, 0 );
|
|
|
|
BIT_SET ( &ibcddrctrl, HRTBT_ENB, 0 );
|
|
|
|
BIT_SET ( &ibcddrctrl, HRTBT_AUTO, 0 );
|
|
|
|
linda_writeq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset );
|
|
|
|
|
|
|
|
/* Set default SerDes parameters */
|
|
|
|
if ( ( rc = linda_set_serdes_params ( linda,
|
|
|
|
linda_serdes_defaults1 ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
udelay ( 415 ); /* Magic delay while SerDes sorts itself out */
|
|
|
|
if ( ( rc = linda_set_serdes_params ( linda,
|
|
|
|
linda_serdes_defaults2 ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Program the microcontroller RAM */
|
2008-11-18 00:55:43 +01:00
|
|
|
if ( ( rc = linda_program_uc_ram ( linda ) ) != 0 )
|
2008-11-07 09:47:21 +01:00
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Verify the microcontroller RAM contents */
|
|
|
|
if ( DBGLVL_LOG ) {
|
2008-11-18 00:55:43 +01:00
|
|
|
if ( ( rc = linda_verify_uc_ram ( linda ) ) != 0 )
|
2008-11-07 09:47:21 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* More SerDes tuning */
|
|
|
|
if ( ( rc = linda_set_serdes_params ( linda,
|
|
|
|
linda_serdes_defaults3 ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Use the microcontroller to trim the IB link */
|
|
|
|
if ( ( rc = linda_trim_ib ( linda ) ) != 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Bring XGXS out of reset */
|
|
|
|
linda_readq ( linda, &xgxscfg, QIB_7220_XGXSCfg_offset );
|
|
|
|
BIT_SET ( &xgxscfg, tx_rx_reset, 0 );
|
|
|
|
BIT_SET ( &xgxscfg, xcv_reset, 0 );
|
|
|
|
linda_writeq ( linda, &xgxscfg, QIB_7220_XGXSCfg_offset );
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* PCI layer interface
|
|
|
|
*
|
|
|
|
***************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Probe PCI device
|
|
|
|
*
|
|
|
|
* @v pci PCI device
|
|
|
|
* @v id PCI ID
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int linda_probe ( struct pci_device *pci,
|
|
|
|
const struct pci_device_id *id __unused ) {
|
|
|
|
struct ib_device *ibdev;
|
|
|
|
struct linda *linda;
|
|
|
|
struct QIB_7220_Revision revision;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Allocate Infiniband device */
|
|
|
|
ibdev = alloc_ibdev ( sizeof ( *linda ) );
|
|
|
|
if ( ! ibdev ) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err_alloc_ibdev;
|
|
|
|
}
|
|
|
|
pci_set_drvdata ( pci, ibdev );
|
|
|
|
linda = ib_get_drvdata ( ibdev );
|
|
|
|
ibdev->op = &linda_ib_operations;
|
|
|
|
ibdev->dev = &pci->dev;
|
|
|
|
ibdev->port = 1;
|
|
|
|
|
|
|
|
/* Fix up PCI device */
|
|
|
|
adjust_pci_device ( pci );
|
|
|
|
|
|
|
|
/* Get PCI BARs */
|
|
|
|
linda->regs = ioremap ( pci->membase, LINDA_BAR0_SIZE );
|
|
|
|
DBGC2 ( linda, "Linda %p has BAR at %08lx\n", linda, pci->membase );
|
|
|
|
|
|
|
|
/* Print some general data */
|
|
|
|
linda_readq ( linda, &revision, QIB_7220_Revision_offset );
|
|
|
|
DBGC2 ( linda, "Linda %p board %02lx v%ld.%ld.%ld.%ld\n", linda,
|
|
|
|
BIT_GET ( &revision, BoardID ),
|
|
|
|
BIT_GET ( &revision, R_SW ),
|
|
|
|
BIT_GET ( &revision, R_Arch ),
|
|
|
|
BIT_GET ( &revision, R_ChipRevMajor ),
|
|
|
|
BIT_GET ( &revision, R_ChipRevMinor ) );
|
|
|
|
|
2009-07-11 02:14:43 +02:00
|
|
|
/* Record link capabilities. Note that we force SDR only to
|
|
|
|
* avoid having to carry extra code for DDR tuning etc.
|
|
|
|
*/
|
|
|
|
ibdev->link_width_enabled = ibdev->link_width_supported =
|
|
|
|
( IB_LINK_WIDTH_4X | IB_LINK_WIDTH_1X );
|
|
|
|
ibdev->link_speed_enabled = ibdev->link_speed_supported =
|
|
|
|
IB_LINK_SPEED_SDR;
|
|
|
|
|
2008-11-07 09:47:21 +01:00
|
|
|
/* Initialise I2C subsystem */
|
|
|
|
if ( ( rc = linda_init_i2c ( linda ) ) != 0 )
|
|
|
|
goto err_init_i2c;
|
|
|
|
|
|
|
|
/* Read EEPROM parameters */
|
|
|
|
if ( ( rc = linda_read_eeprom ( linda, &ibdev->gid.u.half[1] ) ) != 0 )
|
|
|
|
goto err_read_eeprom;
|
|
|
|
|
|
|
|
/* Initialise send datapath */
|
|
|
|
if ( ( rc = linda_init_send ( linda ) ) != 0 )
|
|
|
|
goto err_init_send;
|
|
|
|
|
|
|
|
/* Initialise receive datapath */
|
|
|
|
if ( ( rc = linda_init_recv ( linda ) ) != 0 )
|
|
|
|
goto err_init_recv;
|
|
|
|
|
|
|
|
/* Initialise the IB SerDes */
|
|
|
|
if ( ( rc = linda_init_ib_serdes ( linda ) ) != 0 )
|
|
|
|
goto err_init_ib_serdes;
|
|
|
|
|
|
|
|
/* Register Infiniband device */
|
|
|
|
if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) {
|
|
|
|
DBGC ( linda, "Linda %p could not register IB "
|
|
|
|
"device: %s\n", linda, strerror ( rc ) );
|
|
|
|
goto err_register_ibdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
unregister_ibdev ( ibdev );
|
|
|
|
err_register_ibdev:
|
|
|
|
linda_fini_recv ( linda );
|
|
|
|
err_init_recv:
|
|
|
|
linda_fini_send ( linda );
|
|
|
|
err_init_send:
|
|
|
|
err_init_ib_serdes:
|
|
|
|
err_read_eeprom:
|
|
|
|
err_init_i2c:
|
|
|
|
ibdev_put ( ibdev );
|
|
|
|
err_alloc_ibdev:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove PCI device
|
|
|
|
*
|
|
|
|
* @v pci PCI device
|
|
|
|
*/
|
|
|
|
static void linda_remove ( struct pci_device *pci ) {
|
|
|
|
struct ib_device *ibdev = pci_get_drvdata ( pci );
|
|
|
|
struct linda *linda = ib_get_drvdata ( ibdev );
|
|
|
|
|
|
|
|
unregister_ibdev ( ibdev );
|
|
|
|
linda_fini_recv ( linda );
|
|
|
|
linda_fini_send ( linda );
|
|
|
|
ibdev_put ( ibdev );
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_device_id linda_nics[] = {
|
2009-03-26 11:19:03 +01:00
|
|
|
PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver", 0 ),
|
2008-11-07 09:47:21 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pci_driver linda_driver __pci_driver = {
|
|
|
|
.ids = linda_nics,
|
|
|
|
.id_count = ( sizeof ( linda_nics ) / sizeof ( linda_nics[0] ) ),
|
|
|
|
.probe = linda_probe,
|
|
|
|
.remove = linda_remove,
|
|
|
|
};
|