2007-05-08 18:00:38 -07:00
/*
* Copyright ( c ) 2004 , 2005 Topspin Communications . All rights reserved .
* Copyright ( c ) 2005 Mellanox Technologies . All rights reserved .
* Copyright ( c ) 2005 , 2006 , 2007 Cisco Systems , Inc . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/sched.h>
# include <linux/pci.h>
# include <linux/errno.h>
# include <linux/mlx4/cmd.h>
# include <asm/io.h>
# include "mlx4.h"
# define CMD_POLL_TOKEN 0xffff
enum {
/* command completed successfully: */
CMD_STAT_OK = 0x00 ,
/* Internal error (such as a bus error) occurred while processing command: */
CMD_STAT_INTERNAL_ERR = 0x01 ,
/* Operation/command not supported or opcode modifier not supported: */
CMD_STAT_BAD_OP = 0x02 ,
/* Parameter not supported or parameter out of range: */
CMD_STAT_BAD_PARAM = 0x03 ,
/* System not enabled or bad system state: */
CMD_STAT_BAD_SYS_STATE = 0x04 ,
/* Attempt to access reserved or unallocaterd resource: */
CMD_STAT_BAD_RESOURCE = 0x05 ,
/* Requested resource is currently executing a command, or is otherwise busy: */
CMD_STAT_RESOURCE_BUSY = 0x06 ,
/* Required capability exceeds device limits: */
CMD_STAT_EXCEED_LIM = 0x08 ,
/* Resource is not in the appropriate state or ownership: */
CMD_STAT_BAD_RES_STATE = 0x09 ,
/* Index out of range: */
CMD_STAT_BAD_INDEX = 0x0a ,
/* FW image corrupted: */
CMD_STAT_BAD_NVMEM = 0x0b ,
/* Attempt to modify a QP/EE which is not in the presumed state: */
CMD_STAT_BAD_QP_STATE = 0x10 ,
/* Bad segment parameters (Address/Size): */
CMD_STAT_BAD_SEG_PARAM = 0x20 ,
/* Memory Region has Memory Windows bound to: */
CMD_STAT_REG_BOUND = 0x21 ,
/* HCA local attached memory not present: */
CMD_STAT_LAM_NOT_PRE = 0x22 ,
/* Bad management packet (silently discarded): */
CMD_STAT_BAD_PKT = 0x30 ,
/* More outstanding CQEs in CQ than new CQ size: */
CMD_STAT_BAD_SIZE = 0x40
} ;
enum {
HCR_IN_PARAM_OFFSET = 0x00 ,
HCR_IN_MODIFIER_OFFSET = 0x08 ,
HCR_OUT_PARAM_OFFSET = 0x0c ,
HCR_TOKEN_OFFSET = 0x14 ,
HCR_STATUS_OFFSET = 0x18 ,
HCR_OPMOD_SHIFT = 12 ,
HCR_T_BIT = 21 ,
HCR_E_BIT = 22 ,
HCR_GO_BIT = 23
} ;
enum {
2007-08-07 11:18:52 +03:00
GO_BIT_TIMEOUT_MSECS = 10000
2007-05-08 18:00:38 -07:00
} ;
struct mlx4_cmd_context {
struct completion done ;
int result ;
int next ;
u64 out_param ;
u16 token ;
} ;
2008-04-16 21:01:04 -07:00
static int mlx4_status_to_errno ( u8 status )
{
2007-05-08 18:00:38 -07:00
static const int trans_table [ ] = {
[ CMD_STAT_INTERNAL_ERR ] = - EIO ,
[ CMD_STAT_BAD_OP ] = - EPERM ,
[ CMD_STAT_BAD_PARAM ] = - EINVAL ,
[ CMD_STAT_BAD_SYS_STATE ] = - ENXIO ,
[ CMD_STAT_BAD_RESOURCE ] = - EBADF ,
[ CMD_STAT_RESOURCE_BUSY ] = - EBUSY ,
[ CMD_STAT_EXCEED_LIM ] = - ENOMEM ,
[ CMD_STAT_BAD_RES_STATE ] = - EBADF ,
[ CMD_STAT_BAD_INDEX ] = - EBADF ,
[ CMD_STAT_BAD_NVMEM ] = - EFAULT ,
[ CMD_STAT_BAD_QP_STATE ] = - EINVAL ,
[ CMD_STAT_BAD_SEG_PARAM ] = - EFAULT ,
[ CMD_STAT_REG_BOUND ] = - EBUSY ,
[ CMD_STAT_LAM_NOT_PRE ] = - EAGAIN ,
[ CMD_STAT_BAD_PKT ] = - EINVAL ,
[ CMD_STAT_BAD_SIZE ] = - ENOMEM ,
} ;
if ( status > = ARRAY_SIZE ( trans_table ) | |
( status ! = CMD_STAT_OK & & trans_table [ status ] = = 0 ) )
return - EIO ;
return trans_table [ status ] ;
}
static int cmd_pending ( struct mlx4_dev * dev )
{
u32 status = readl ( mlx4_priv ( dev ) - > cmd . hcr + HCR_STATUS_OFFSET ) ;
return ( status & swab32 ( 1 < < HCR_GO_BIT ) ) | |
( mlx4_priv ( dev ) - > cmd . toggle = =
! ! ( status & swab32 ( 1 < < HCR_T_BIT ) ) ) ;
}
static int mlx4_cmd_post ( struct mlx4_dev * dev , u64 in_param , u64 out_param ,
u32 in_modifier , u8 op_modifier , u16 op , u16 token ,
int event )
{
struct mlx4_cmd * cmd = & mlx4_priv ( dev ) - > cmd ;
u32 __iomem * hcr = cmd - > hcr ;
int ret = - EAGAIN ;
unsigned long end ;
mutex_lock ( & cmd - > hcr_mutex ) ;
end = jiffies ;
if ( event )
2007-08-07 11:18:52 +03:00
end + = msecs_to_jiffies ( GO_BIT_TIMEOUT_MSECS ) ;
2007-05-08 18:00:38 -07:00
while ( cmd_pending ( dev ) ) {
if ( time_after_eq ( jiffies , end ) )
goto out ;
cond_resched ( ) ;
}
/*
* We use writel ( instead of something like memcpy_toio )
* because writes of less than 32 bits to the HCR don ' t work
* ( and some architectures such as ia64 implement memcpy_toio
* in terms of writeb ) .
*/
__raw_writel ( ( __force u32 ) cpu_to_be32 ( in_param > > 32 ) , hcr + 0 ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( in_param & 0xfffffffful ) , hcr + 1 ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( in_modifier ) , hcr + 2 ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( out_param > > 32 ) , hcr + 3 ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( out_param & 0xfffffffful ) , hcr + 4 ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( token < < 16 ) , hcr + 5 ) ;
/* __raw_writel may not order writes. */
wmb ( ) ;
__raw_writel ( ( __force u32 ) cpu_to_be32 ( ( 1 < < HCR_GO_BIT ) |
( cmd - > toggle < < HCR_T_BIT ) |
( event ? ( 1 < < HCR_E_BIT ) : 0 ) |
( op_modifier < < HCR_OPMOD_SHIFT ) |
op ) , hcr + 6 ) ;
2007-10-09 19:59:18 -07:00
/*
* Make sure that our HCR writes don ' t get mixed in with
* writes from another CPU starting a FW command .
*/
mmiowb ( ) ;
2007-05-08 18:00:38 -07:00
cmd - > toggle = cmd - > toggle ^ 1 ;
ret = 0 ;
out :
mutex_unlock ( & cmd - > hcr_mutex ) ;
return ret ;
}
static int mlx4_cmd_poll ( struct mlx4_dev * dev , u64 in_param , u64 * out_param ,
int out_is_imm , u32 in_modifier , u8 op_modifier ,
u16 op , unsigned long timeout )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
void __iomem * hcr = priv - > cmd . hcr ;
int err = 0 ;
unsigned long end ;
down ( & priv - > cmd . poll_sem ) ;
err = mlx4_cmd_post ( dev , in_param , out_param ? * out_param : 0 ,
in_modifier , op_modifier , op , CMD_POLL_TOKEN , 0 ) ;
if ( err )
goto out ;
end = msecs_to_jiffies ( timeout ) + jiffies ;
while ( cmd_pending ( dev ) & & time_before ( jiffies , end ) )
cond_resched ( ) ;
if ( cmd_pending ( dev ) ) {
err = - ETIMEDOUT ;
goto out ;
}
if ( out_is_imm )
* out_param =
( u64 ) be32_to_cpu ( ( __force __be32 )
__raw_readl ( hcr + HCR_OUT_PARAM_OFFSET ) ) < < 32 |
( u64 ) be32_to_cpu ( ( __force __be32 )
__raw_readl ( hcr + HCR_OUT_PARAM_OFFSET + 4 ) ) ;
err = mlx4_status_to_errno ( be32_to_cpu ( ( __force __be32 )
__raw_readl ( hcr + HCR_STATUS_OFFSET ) ) > > 24 ) ;
out :
up ( & priv - > cmd . poll_sem ) ;
return err ;
}
void mlx4_cmd_event ( struct mlx4_dev * dev , u16 token , u8 status , u64 out_param )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
struct mlx4_cmd_context * context =
& priv - > cmd . context [ token & priv - > cmd . token_mask ] ;
/* previously timed out command completing at long last */
if ( token ! = context - > token )
return ;
context - > result = mlx4_status_to_errno ( status ) ;
context - > out_param = out_param ;
complete ( & context - > done ) ;
}
static int mlx4_cmd_wait ( struct mlx4_dev * dev , u64 in_param , u64 * out_param ,
int out_is_imm , u32 in_modifier , u8 op_modifier ,
u16 op , unsigned long timeout )
{
struct mlx4_cmd * cmd = & mlx4_priv ( dev ) - > cmd ;
struct mlx4_cmd_context * context ;
int err = 0 ;
down ( & cmd - > event_sem ) ;
spin_lock ( & cmd - > context_lock ) ;
BUG_ON ( cmd - > free_head < 0 ) ;
context = & cmd - > context [ cmd - > free_head ] ;
2007-07-20 21:19:43 -07:00
context - > token + = cmd - > token_mask + 1 ;
2007-05-08 18:00:38 -07:00
cmd - > free_head = context - > next ;
spin_unlock ( & cmd - > context_lock ) ;
init_completion ( & context - > done ) ;
mlx4_cmd_post ( dev , in_param , out_param ? * out_param : 0 ,
in_modifier , op_modifier , op , context - > token , 1 ) ;
if ( ! wait_for_completion_timeout ( & context - > done , msecs_to_jiffies ( timeout ) ) ) {
err = - EBUSY ;
goto out ;
}
err = context - > result ;
if ( err )
goto out ;
if ( out_is_imm )
* out_param = context - > out_param ;
out :
spin_lock ( & cmd - > context_lock ) ;
context - > next = cmd - > free_head ;
cmd - > free_head = context - cmd - > context ;
spin_unlock ( & cmd - > context_lock ) ;
up ( & cmd - > event_sem ) ;
return err ;
}
int __mlx4_cmd ( struct mlx4_dev * dev , u64 in_param , u64 * out_param ,
int out_is_imm , u32 in_modifier , u8 op_modifier ,
u16 op , unsigned long timeout )
{
if ( mlx4_priv ( dev ) - > cmd . use_events )
return mlx4_cmd_wait ( dev , in_param , out_param , out_is_imm ,
in_modifier , op_modifier , op , timeout ) ;
else
return mlx4_cmd_poll ( dev , in_param , out_param , out_is_imm ,
in_modifier , op_modifier , op , timeout ) ;
}
EXPORT_SYMBOL_GPL ( __mlx4_cmd ) ;
int mlx4_cmd_init ( struct mlx4_dev * dev )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
mutex_init ( & priv - > cmd . hcr_mutex ) ;
sema_init ( & priv - > cmd . poll_sem , 1 ) ;
priv - > cmd . use_events = 0 ;
priv - > cmd . toggle = 1 ;
priv - > cmd . hcr = ioremap ( pci_resource_start ( dev - > pdev , 0 ) + MLX4_HCR_BASE ,
MLX4_HCR_SIZE ) ;
if ( ! priv - > cmd . hcr ) {
mlx4_err ( dev , " Couldn't map command register. " ) ;
return - ENOMEM ;
}
priv - > cmd . pool = pci_pool_create ( " mlx4_cmd " , dev - > pdev ,
MLX4_MAILBOX_SIZE ,
MLX4_MAILBOX_SIZE , 0 ) ;
if ( ! priv - > cmd . pool ) {
iounmap ( priv - > cmd . hcr ) ;
return - ENOMEM ;
}
return 0 ;
}
void mlx4_cmd_cleanup ( struct mlx4_dev * dev )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
pci_pool_destroy ( priv - > cmd . pool ) ;
iounmap ( priv - > cmd . hcr ) ;
}
/*
* Switch to using events to issue FW commands ( can only be called
* after event queue for command events has been initialized ) .
*/
int mlx4_cmd_use_events ( struct mlx4_dev * dev )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
int i ;
priv - > cmd . context = kmalloc ( priv - > cmd . max_cmds *
sizeof ( struct mlx4_cmd_context ) ,
GFP_KERNEL ) ;
if ( ! priv - > cmd . context )
return - ENOMEM ;
for ( i = 0 ; i < priv - > cmd . max_cmds ; + + i ) {
priv - > cmd . context [ i ] . token = i ;
priv - > cmd . context [ i ] . next = i + 1 ;
}
priv - > cmd . context [ priv - > cmd . max_cmds - 1 ] . next = - 1 ;
priv - > cmd . free_head = 0 ;
sema_init ( & priv - > cmd . event_sem , priv - > cmd . max_cmds ) ;
spin_lock_init ( & priv - > cmd . context_lock ) ;
for ( priv - > cmd . token_mask = 1 ;
priv - > cmd . token_mask < priv - > cmd . max_cmds ;
priv - > cmd . token_mask < < = 1 )
; /* nothing */
- - priv - > cmd . token_mask ;
priv - > cmd . use_events = 1 ;
down ( & priv - > cmd . poll_sem ) ;
return 0 ;
}
/*
* Switch back to polling ( used when shutting down the device )
*/
void mlx4_cmd_use_polling ( struct mlx4_dev * dev )
{
struct mlx4_priv * priv = mlx4_priv ( dev ) ;
int i ;
priv - > cmd . use_events = 0 ;
for ( i = 0 ; i < priv - > cmd . max_cmds ; + + i )
down ( & priv - > cmd . event_sem ) ;
kfree ( priv - > cmd . context ) ;
up ( & priv - > cmd . poll_sem ) ;
}
struct mlx4_cmd_mailbox * mlx4_alloc_cmd_mailbox ( struct mlx4_dev * dev )
{
struct mlx4_cmd_mailbox * mailbox ;
mailbox = kmalloc ( sizeof * mailbox , GFP_KERNEL ) ;
if ( ! mailbox )
return ERR_PTR ( - ENOMEM ) ;
mailbox - > buf = pci_pool_alloc ( mlx4_priv ( dev ) - > cmd . pool , GFP_KERNEL ,
& mailbox - > dma ) ;
if ( ! mailbox - > buf ) {
kfree ( mailbox ) ;
return ERR_PTR ( - ENOMEM ) ;
}
return mailbox ;
}
EXPORT_SYMBOL_GPL ( mlx4_alloc_cmd_mailbox ) ;
void mlx4_free_cmd_mailbox ( struct mlx4_dev * dev , struct mlx4_cmd_mailbox * mailbox )
{
if ( ! mailbox )
return ;
pci_pool_free ( mlx4_priv ( dev ) - > cmd . pool , mailbox - > buf , mailbox - > dma ) ;
kfree ( mailbox ) ;
}
EXPORT_SYMBOL_GPL ( mlx4_free_cmd_mailbox ) ;