IB/hfi1: add driver files
Signed-off-by: Andrew Friedley <andrew.friedley@intel.com> Signed-off-by: Arthur Kepner <arthur.kepner@intel.com> Signed-off-by: Brendan Cunningham <brendan.cunningham@intel.com> Signed-off-by: Brian Welty <brian.welty@intel.com> Signed-off-by: Caz Yokoyama <caz.yokoyama@intel.com> Signed-off-by: Dean Luick <dean.luick@intel.com> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com> Signed-off-by: Easwar Hariharan <easwar.hariharan@intel.com> Signed-off-by: Harish Chegondi <harish.chegondi@intel.com> Signed-off-by: Ira Weiny <ira.weiny@intel.com> Signed-off-by: Jim Snow <jim.m.snow@intel.com> Signed-off-by: John Gregor <john.a.gregor@intel.com> Signed-off-by: Jubin John <jubin.john@intel.com> Signed-off-by: Kaike Wan <kaike.wan@intel.com> Signed-off-by: Kevin Pine <kevin.pine@intel.com> Signed-off-by: Kyle Liddell <kyle.liddell@intel.com> Signed-off-by: Mike Marciniszyn <mike.marciniszyn@intel.com> Signed-off-by: Mitko Haralanov <mitko.haralanov@intel.com> Signed-off-by: Ravi Krishnaswamy <ravi.krishnaswamy@intel.com> Signed-off-by: Sadanand Warrier <sadanand.warrier@intel.com> Signed-off-by: Sanath Kumar <sanath.s.kumar@intel.com> Signed-off-by: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Vlad Danushevsky <vladimir.danusevsky@intel.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
parent
d4ab347005
commit
7724105686
60 changed files with 57480 additions and 0 deletions
|
|
@ -64,3 +64,23 @@ MTHCA
|
|||
fw_ver - Firmware version
|
||||
hca_type - HCA type: "MT23108", "MT25208 (MT23108 compat mode)",
|
||||
or "MT25208"
|
||||
|
||||
HFI1
|
||||
|
||||
The hfi1 driver also creates these additional files:
|
||||
|
||||
hw_rev - hardware revision
|
||||
board_id - manufacturing board id
|
||||
tempsense - thermal sense information
|
||||
serial - board serial number
|
||||
nfreectxts - number of free user contexts
|
||||
nctxts - number of allowed contexts (PSM2)
|
||||
chip_reset - diagnostic (root only)
|
||||
boardversion - board version
|
||||
ports/1/
|
||||
CMgtA/
|
||||
cc_settings_bin - CCA tables used by PSM2
|
||||
cc_table_bin
|
||||
sc2v/ - 32 files (0 - 31) used to translate sl->vl
|
||||
sl2sc/ - 32 files (0 - 31) used to translate sl->sc
|
||||
vl2mtu/ - 16 (0 - 15) files used to determine MTU for vl
|
||||
|
|
|
|||
|
|
@ -9809,6 +9809,12 @@ M: Arnaud Patard <arnaud.patard@rtp-net.org>
|
|||
S: Odd Fixes
|
||||
F: drivers/staging/xgifb/
|
||||
|
||||
HFI1 DRIVER
|
||||
M: Mike Marciniszyn <infinipath@intel.com>
|
||||
L: linux-rdma@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/staging/rdma/hfi1
|
||||
|
||||
STARFIRE/DURALAN NETWORK DRIVER
|
||||
M: Ion Badulescu <ionut@badula.org>
|
||||
S: Odd Fixes
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ if STAGING_RDMA
|
|||
|
||||
source "drivers/staging/rdma/amso1100/Kconfig"
|
||||
|
||||
source "drivers/staging/rdma/hfi1/Kconfig"
|
||||
|
||||
source "drivers/staging/rdma/ipath/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# Entries for RDMA_STAGING tree
|
||||
obj-$(CONFIG_INFINIBAND_AMSO1100) += amso1100/
|
||||
obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/
|
||||
obj-$(CONFIG_INFINIBAND_IPATH) += ipath/
|
||||
|
|
|
|||
37
drivers/staging/rdma/hfi1/Kconfig
Normal file
37
drivers/staging/rdma/hfi1/Kconfig
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
config INFINIBAND_HFI1
|
||||
tristate "Intel OPA Gen1 support"
|
||||
depends on X86_64
|
||||
default m
|
||||
---help---
|
||||
This is a low-level driver for Intel OPA Gen1 adapter.
|
||||
config HFI1_DEBUG_SDMA_ORDER
|
||||
bool "HFI1 SDMA Order debug"
|
||||
depends on INFINIBAND_HFI1
|
||||
default n
|
||||
---help---
|
||||
This is a debug flag to test for out of order
|
||||
sdma completions for unit testing
|
||||
config HFI1_VERBS_31BIT_PSN
|
||||
bool "HFI1 enable 31 bit PSN"
|
||||
depends on INFINIBAND_HFI1
|
||||
default y
|
||||
---help---
|
||||
Setting this enables 31 BIT PSN
|
||||
For verbs RC/UC
|
||||
config SDMA_VERBOSITY
|
||||
bool "Config SDMA Verbosity"
|
||||
depends on INFINIBAND_HFI1
|
||||
default n
|
||||
---help---
|
||||
This is a configuration flag to enable verbose
|
||||
SDMA debug
|
||||
config PRESCAN_RXQ
|
||||
bool "Enable prescanning of the RX queue for ECNs"
|
||||
depends on INFINIBAND_HFI1
|
||||
default n
|
||||
---help---
|
||||
This option toggles the prescanning of the receive queue for
|
||||
Explicit Congestion Notifications. If an ECN is detected, it
|
||||
is processed as quickly as possible, the ECN is toggled off.
|
||||
After the prescanning step, the receive queue is processed as
|
||||
usual.
|
||||
19
drivers/staging/rdma/hfi1/Makefile
Normal file
19
drivers/staging/rdma/hfi1/Makefile
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# HFI driver
|
||||
#
|
||||
#
|
||||
#
|
||||
# Called from the kernel module build system.
|
||||
#
|
||||
obj-$(CONFIG_INFINIBAND_HFI1) += hfi1.o
|
||||
|
||||
hfi1-y := chip.o cq.o device.o diag.o dma.o driver.o eprom.o file_ops.o firmware.o \
|
||||
init.o intr.o keys.o mad.o mmap.o mr.o pcie.o pio.o pio_copy.o \
|
||||
qp.o qsfp.o rc.o ruc.o sdma.o srq.o sysfs.o trace.o twsi.o \
|
||||
uc.o ud.o user_pages.o user_sdma.o verbs_mcast.o verbs.o
|
||||
hfi1-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
||||
CFLAGS_trace.o = -I$(src)
|
||||
ifdef MVERSION
|
||||
CFLAGS_driver.o = -DHFI_DRIVER_VERSION_BASE=\"$(MVERSION)\"
|
||||
endif
|
||||
6
drivers/staging/rdma/hfi1/TODO
Normal file
6
drivers/staging/rdma/hfi1/TODO
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
July, 2015
|
||||
|
||||
- Remove unneeded file entries in sysfs
|
||||
- Remove software processing of IB protocol and place in library for use
|
||||
by qib, ipath (if still present), hfi1, and eventually soft-roce
|
||||
|
||||
10798
drivers/staging/rdma/hfi1/chip.c
Normal file
10798
drivers/staging/rdma/hfi1/chip.c
Normal file
File diff suppressed because it is too large
Load diff
1035
drivers/staging/rdma/hfi1/chip.h
Normal file
1035
drivers/staging/rdma/hfi1/chip.h
Normal file
File diff suppressed because it is too large
Load diff
1289
drivers/staging/rdma/hfi1/chip_registers.h
Normal file
1289
drivers/staging/rdma/hfi1/chip_registers.h
Normal file
File diff suppressed because it is too large
Load diff
415
drivers/staging/rdma/hfi1/common.h
Normal file
415
drivers/staging/rdma/hfi1/common.h
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include <rdma/hfi/hfi1_user.h>
|
||||
|
||||
/*
|
||||
* This file contains defines, structures, etc. that are used
|
||||
* to communicate between kernel and user code.
|
||||
*/
|
||||
|
||||
/* version of protocol header (known to chip also). In the long run,
|
||||
* we should be able to generate and accept a range of version numbers;
|
||||
* for now we only accept one, and it's compiled in.
|
||||
*/
|
||||
#define IPS_PROTO_VERSION 2
|
||||
|
||||
/*
|
||||
* These are compile time constants that you may want to enable or disable
|
||||
* if you are trying to debug problems with code or performance.
|
||||
* HFI1_VERBOSE_TRACING define as 1 if you want additional tracing in
|
||||
* fast path code
|
||||
* HFI1_TRACE_REGWRITES define as 1 if you want register writes to be
|
||||
* traced in fast path code
|
||||
* _HFI1_TRACING define as 0 if you want to remove all tracing in a
|
||||
* compilation unit
|
||||
*/
|
||||
|
||||
/*
|
||||
* If a packet's QP[23:16] bits match this value, then it is
|
||||
* a PSM packet and the hardware will expect a KDETH header
|
||||
* following the BTH.
|
||||
*/
|
||||
#define DEFAULT_KDETH_QP 0x80
|
||||
|
||||
/* driver/hw feature set bitmask */
|
||||
#define HFI1_CAP_USER_SHIFT 24
|
||||
#define HFI1_CAP_MASK ((1UL << HFI1_CAP_USER_SHIFT) - 1)
|
||||
/* locked flag - if set, only HFI1_CAP_WRITABLE_MASK bits can be set */
|
||||
#define HFI1_CAP_LOCKED_SHIFT 63
|
||||
#define HFI1_CAP_LOCKED_MASK 0x1ULL
|
||||
#define HFI1_CAP_LOCKED_SMASK (HFI1_CAP_LOCKED_MASK << HFI1_CAP_LOCKED_SHIFT)
|
||||
/* extra bits used between kernel and user processes */
|
||||
#define HFI1_CAP_MISC_SHIFT (HFI1_CAP_USER_SHIFT * 2)
|
||||
#define HFI1_CAP_MISC_MASK ((1ULL << (HFI1_CAP_LOCKED_SHIFT - \
|
||||
HFI1_CAP_MISC_SHIFT)) - 1)
|
||||
|
||||
#define HFI1_CAP_KSET(cap) ({ hfi1_cap_mask |= HFI1_CAP_##cap; hfi1_cap_mask; })
|
||||
#define HFI1_CAP_KCLEAR(cap) \
|
||||
({ \
|
||||
hfi1_cap_mask &= ~HFI1_CAP_##cap; \
|
||||
hfi1_cap_mask; \
|
||||
})
|
||||
#define HFI1_CAP_USET(cap) \
|
||||
({ \
|
||||
hfi1_cap_mask |= (HFI1_CAP_##cap << HFI1_CAP_USER_SHIFT); \
|
||||
hfi1_cap_mask; \
|
||||
})
|
||||
#define HFI1_CAP_UCLEAR(cap) \
|
||||
({ \
|
||||
hfi1_cap_mask &= ~(HFI1_CAP_##cap << HFI1_CAP_USER_SHIFT); \
|
||||
hfi1_cap_mask; \
|
||||
})
|
||||
#define HFI1_CAP_SET(cap) \
|
||||
({ \
|
||||
hfi1_cap_mask |= (HFI1_CAP_##cap | (HFI1_CAP_##cap << \
|
||||
HFI1_CAP_USER_SHIFT)); \
|
||||
hfi1_cap_mask; \
|
||||
})
|
||||
#define HFI1_CAP_CLEAR(cap) \
|
||||
({ \
|
||||
hfi1_cap_mask &= ~(HFI1_CAP_##cap | \
|
||||
(HFI1_CAP_##cap << HFI1_CAP_USER_SHIFT)); \
|
||||
hfi1_cap_mask; \
|
||||
})
|
||||
#define HFI1_CAP_LOCK() \
|
||||
({ hfi1_cap_mask |= HFI1_CAP_LOCKED_SMASK; hfi1_cap_mask; })
|
||||
#define HFI1_CAP_LOCKED() (!!(hfi1_cap_mask & HFI1_CAP_LOCKED_SMASK))
|
||||
/*
|
||||
* The set of capability bits that can be changed after initial load
|
||||
* This set is the same for kernel and user contexts. However, for
|
||||
* user contexts, the set can be further filtered by using the
|
||||
* HFI1_CAP_RESERVED_MASK bits.
|
||||
*/
|
||||
#define HFI1_CAP_WRITABLE_MASK (HFI1_CAP_SDMA_AHG | \
|
||||
HFI1_CAP_HDRSUPP | \
|
||||
HFI1_CAP_MULTI_PKT_EGR | \
|
||||
HFI1_CAP_NODROP_RHQ_FULL | \
|
||||
HFI1_CAP_NODROP_EGR_FULL | \
|
||||
HFI1_CAP_ALLOW_PERM_JKEY | \
|
||||
HFI1_CAP_STATIC_RATE_CTRL | \
|
||||
HFI1_CAP_PRINT_UNIMPL)
|
||||
/*
|
||||
* A set of capability bits that are "global" and are not allowed to be
|
||||
* set in the user bitmask.
|
||||
*/
|
||||
#define HFI1_CAP_RESERVED_MASK ((HFI1_CAP_SDMA | \
|
||||
HFI1_CAP_USE_SDMA_HEAD | \
|
||||
HFI1_CAP_EXTENDED_PSN | \
|
||||
HFI1_CAP_PRINT_UNIMPL | \
|
||||
HFI1_CAP_QSFP_ENABLED | \
|
||||
HFI1_CAP_NO_INTEGRITY | \
|
||||
HFI1_CAP_PKEY_CHECK) << \
|
||||
HFI1_CAP_USER_SHIFT)
|
||||
/*
|
||||
* Set of capabilities that need to be enabled for kernel context in
|
||||
* order to be allowed for user contexts, as well.
|
||||
*/
|
||||
#define HFI1_CAP_MUST_HAVE_KERN (HFI1_CAP_STATIC_RATE_CTRL)
|
||||
/* Default enabled capabilities (both kernel and user) */
|
||||
#define HFI1_CAP_MASK_DEFAULT (HFI1_CAP_HDRSUPP | \
|
||||
HFI1_CAP_NODROP_RHQ_FULL | \
|
||||
HFI1_CAP_NODROP_EGR_FULL | \
|
||||
HFI1_CAP_SDMA | \
|
||||
HFI1_CAP_PRINT_UNIMPL | \
|
||||
HFI1_CAP_STATIC_RATE_CTRL | \
|
||||
HFI1_CAP_QSFP_ENABLED | \
|
||||
HFI1_CAP_PKEY_CHECK | \
|
||||
HFI1_CAP_MULTI_PKT_EGR | \
|
||||
HFI1_CAP_EXTENDED_PSN | \
|
||||
((HFI1_CAP_HDRSUPP | \
|
||||
HFI1_CAP_MULTI_PKT_EGR | \
|
||||
HFI1_CAP_STATIC_RATE_CTRL | \
|
||||
HFI1_CAP_PKEY_CHECK | \
|
||||
HFI1_CAP_EARLY_CREDIT_RETURN) << \
|
||||
HFI1_CAP_USER_SHIFT))
|
||||
/*
|
||||
* A bitmask of kernel/global capabilities that should be communicated
|
||||
* to user level processes.
|
||||
*/
|
||||
#define HFI1_CAP_K2U (HFI1_CAP_SDMA | \
|
||||
HFI1_CAP_EXTENDED_PSN | \
|
||||
HFI1_CAP_PKEY_CHECK | \
|
||||
HFI1_CAP_NO_INTEGRITY)
|
||||
|
||||
#define HFI1_USER_SWVERSION ((HFI1_USER_SWMAJOR << 16) | HFI1_USER_SWMINOR)
|
||||
|
||||
#ifndef HFI1_KERN_TYPE
|
||||
#define HFI1_KERN_TYPE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Similarly, this is the kernel version going back to the user. It's
|
||||
* slightly different, in that we want to tell if the driver was built as
|
||||
* part of a Intel release, or from the driver from openfabrics.org,
|
||||
* kernel.org, or a standard distribution, for support reasons.
|
||||
* The high bit is 0 for non-Intel and 1 for Intel-built/supplied.
|
||||
*
|
||||
* It's returned by the driver to the user code during initialization in the
|
||||
* spi_sw_version field of hfi1_base_info, so the user code can in turn
|
||||
* check for compatibility with the kernel.
|
||||
*/
|
||||
#define HFI1_KERN_SWVERSION ((HFI1_KERN_TYPE << 31) | HFI1_USER_SWVERSION)
|
||||
|
||||
/*
|
||||
* Define the driver version number. This is something that refers only
|
||||
* to the driver itself, not the software interfaces it supports.
|
||||
*/
|
||||
#ifndef HFI1_DRIVER_VERSION_BASE
|
||||
#define HFI1_DRIVER_VERSION_BASE "0.9-248"
|
||||
#endif
|
||||
|
||||
/* create the final driver version string */
|
||||
#ifdef HFI1_IDSTR
|
||||
#define HFI1_DRIVER_VERSION HFI1_DRIVER_VERSION_BASE " " HFI1_IDSTR
|
||||
#else
|
||||
#define HFI1_DRIVER_VERSION HFI1_DRIVER_VERSION_BASE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Diagnostics can send a packet by writing the following
|
||||
* struct to the diag packet special file.
|
||||
*
|
||||
* This allows a custom PBC qword, so that special modes and deliberate
|
||||
* changes to CRCs can be used.
|
||||
*/
|
||||
#define _DIAG_PKT_VERS 1
|
||||
struct diag_pkt {
|
||||
__u16 version; /* structure version */
|
||||
__u16 unit; /* which device */
|
||||
__u16 sw_index; /* send sw index to use */
|
||||
__u16 len; /* data length, in bytes */
|
||||
__u16 port; /* port number */
|
||||
__u16 unused;
|
||||
__u32 flags; /* call flags */
|
||||
__u64 data; /* user data pointer */
|
||||
__u64 pbc; /* PBC for the packet */
|
||||
};
|
||||
|
||||
/* diag_pkt flags */
|
||||
#define F_DIAGPKT_WAIT 0x1 /* wait until packet is sent */
|
||||
|
||||
/*
|
||||
* The next set of defines are for packet headers, and chip register
|
||||
* and memory bits that are visible to and/or used by user-mode software.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Receive Header Flags
|
||||
*/
|
||||
#define RHF_PKT_LEN_SHIFT 0
|
||||
#define RHF_PKT_LEN_MASK 0xfffull
|
||||
#define RHF_PKT_LEN_SMASK (RHF_PKT_LEN_MASK << RHF_PKT_LEN_SHIFT)
|
||||
|
||||
#define RHF_RCV_TYPE_SHIFT 12
|
||||
#define RHF_RCV_TYPE_MASK 0x7ull
|
||||
#define RHF_RCV_TYPE_SMASK (RHF_RCV_TYPE_MASK << RHF_RCV_TYPE_SHIFT)
|
||||
|
||||
#define RHF_USE_EGR_BFR_SHIFT 15
|
||||
#define RHF_USE_EGR_BFR_MASK 0x1ull
|
||||
#define RHF_USE_EGR_BFR_SMASK (RHF_USE_EGR_BFR_MASK << RHF_USE_EGR_BFR_SHIFT)
|
||||
|
||||
#define RHF_EGR_INDEX_SHIFT 16
|
||||
#define RHF_EGR_INDEX_MASK 0x7ffull
|
||||
#define RHF_EGR_INDEX_SMASK (RHF_EGR_INDEX_MASK << RHF_EGR_INDEX_SHIFT)
|
||||
|
||||
#define RHF_DC_INFO_SHIFT 27
|
||||
#define RHF_DC_INFO_MASK 0x1ull
|
||||
#define RHF_DC_INFO_SMASK (RHF_DC_INFO_MASK << RHF_DC_INFO_SHIFT)
|
||||
|
||||
#define RHF_RCV_SEQ_SHIFT 28
|
||||
#define RHF_RCV_SEQ_MASK 0xfull
|
||||
#define RHF_RCV_SEQ_SMASK (RHF_RCV_SEQ_MASK << RHF_RCV_SEQ_SHIFT)
|
||||
|
||||
#define RHF_EGR_OFFSET_SHIFT 32
|
||||
#define RHF_EGR_OFFSET_MASK 0xfffull
|
||||
#define RHF_EGR_OFFSET_SMASK (RHF_EGR_OFFSET_MASK << RHF_EGR_OFFSET_SHIFT)
|
||||
#define RHF_HDRQ_OFFSET_SHIFT 44
|
||||
#define RHF_HDRQ_OFFSET_MASK 0x1ffull
|
||||
#define RHF_HDRQ_OFFSET_SMASK (RHF_HDRQ_OFFSET_MASK << RHF_HDRQ_OFFSET_SHIFT)
|
||||
#define RHF_K_HDR_LEN_ERR (0x1ull << 53)
|
||||
#define RHF_DC_UNC_ERR (0x1ull << 54)
|
||||
#define RHF_DC_ERR (0x1ull << 55)
|
||||
#define RHF_RCV_TYPE_ERR_SHIFT 56
|
||||
#define RHF_RCV_TYPE_ERR_MASK 0x7ul
|
||||
#define RHF_RCV_TYPE_ERR_SMASK (RHF_RCV_TYPE_ERR_MASK << RHF_RCV_TYPE_ERR_SHIFT)
|
||||
#define RHF_TID_ERR (0x1ull << 59)
|
||||
#define RHF_LEN_ERR (0x1ull << 60)
|
||||
#define RHF_ECC_ERR (0x1ull << 61)
|
||||
#define RHF_VCRC_ERR (0x1ull << 62)
|
||||
#define RHF_ICRC_ERR (0x1ull << 63)
|
||||
|
||||
#define RHF_ERROR_SMASK 0xffe0000000000000ull /* bits 63:53 */
|
||||
|
||||
/* RHF receive types */
|
||||
#define RHF_RCV_TYPE_EXPECTED 0
|
||||
#define RHF_RCV_TYPE_EAGER 1
|
||||
#define RHF_RCV_TYPE_IB 2 /* normal IB, IB Raw, or IPv6 */
|
||||
#define RHF_RCV_TYPE_ERROR 3
|
||||
#define RHF_RCV_TYPE_BYPASS 4
|
||||
#define RHF_RCV_TYPE_INVALID5 5
|
||||
#define RHF_RCV_TYPE_INVALID6 6
|
||||
#define RHF_RCV_TYPE_INVALID7 7
|
||||
|
||||
/* RHF receive type error - expected packet errors */
|
||||
#define RHF_RTE_EXPECTED_FLOW_SEQ_ERR 0x2
|
||||
#define RHF_RTE_EXPECTED_FLOW_GEN_ERR 0x4
|
||||
|
||||
/* RHF receive type error - eager packet errors */
|
||||
#define RHF_RTE_EAGER_NO_ERR 0x0
|
||||
|
||||
/* RHF receive type error - IB packet errors */
|
||||
#define RHF_RTE_IB_NO_ERR 0x0
|
||||
|
||||
/* RHF receive type error - error packet errors */
|
||||
#define RHF_RTE_ERROR_NO_ERR 0x0
|
||||
#define RHF_RTE_ERROR_OP_CODE_ERR 0x1
|
||||
#define RHF_RTE_ERROR_KHDR_MIN_LEN_ERR 0x2
|
||||
#define RHF_RTE_ERROR_KHDR_HCRC_ERR 0x3
|
||||
#define RHF_RTE_ERROR_KHDR_KVER_ERR 0x4
|
||||
#define RHF_RTE_ERROR_CONTEXT_ERR 0x5
|
||||
#define RHF_RTE_ERROR_KHDR_TID_ERR 0x6
|
||||
|
||||
/* RHF receive type error - bypass packet errors */
|
||||
#define RHF_RTE_BYPASS_NO_ERR 0x0
|
||||
|
||||
/*
|
||||
* This structure contains the first field common to all protocols
|
||||
* that employ this chip.
|
||||
*/
|
||||
struct hfi1_message_header {
|
||||
__be16 lrh[4];
|
||||
};
|
||||
|
||||
/* IB - LRH header constants */
|
||||
#define HFI1_LRH_GRH 0x0003 /* 1. word of IB LRH - next header: GRH */
|
||||
#define HFI1_LRH_BTH 0x0002 /* 1. word of IB LRH - next header: BTH */
|
||||
|
||||
/* misc. */
|
||||
#define SIZE_OF_CRC 1
|
||||
|
||||
#define LIM_MGMT_P_KEY 0x7FFF
|
||||
#define FULL_MGMT_P_KEY 0xFFFF
|
||||
|
||||
#define DEFAULT_P_KEY LIM_MGMT_P_KEY
|
||||
#define HFI1_PERMISSIVE_LID 0xFFFF
|
||||
#define HFI1_AETH_CREDIT_SHIFT 24
|
||||
#define HFI1_AETH_CREDIT_MASK 0x1F
|
||||
#define HFI1_AETH_CREDIT_INVAL 0x1F
|
||||
#define HFI1_MSN_MASK 0xFFFFFF
|
||||
#define HFI1_QPN_MASK 0xFFFFFF
|
||||
#define HFI1_FECN_SHIFT 31
|
||||
#define HFI1_FECN_MASK 1
|
||||
#define HFI1_FECN_SMASK (1 << HFI1_FECN_SHIFT)
|
||||
#define HFI1_BECN_SHIFT 30
|
||||
#define HFI1_BECN_MASK 1
|
||||
#define HFI1_BECN_SMASK (1 << HFI1_BECN_SHIFT)
|
||||
#define HFI1_MULTICAST_LID_BASE 0xC000
|
||||
|
||||
static inline __u64 rhf_to_cpu(const __le32 *rbuf)
|
||||
{
|
||||
return __le64_to_cpu(*((__le64 *)rbuf));
|
||||
}
|
||||
|
||||
static inline u64 rhf_err_flags(u64 rhf)
|
||||
{
|
||||
return rhf & RHF_ERROR_SMASK;
|
||||
}
|
||||
|
||||
static inline u32 rhf_rcv_type(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_RCV_TYPE_SHIFT) & RHF_RCV_TYPE_MASK;
|
||||
}
|
||||
|
||||
static inline u32 rhf_rcv_type_err(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_RCV_TYPE_ERR_SHIFT) & RHF_RCV_TYPE_ERR_MASK;
|
||||
}
|
||||
|
||||
/* return size is in bytes, not DWORDs */
|
||||
static inline u32 rhf_pkt_len(u64 rhf)
|
||||
{
|
||||
return ((rhf & RHF_PKT_LEN_SMASK) >> RHF_PKT_LEN_SHIFT) << 2;
|
||||
}
|
||||
|
||||
static inline u32 rhf_egr_index(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_EGR_INDEX_SHIFT) & RHF_EGR_INDEX_MASK;
|
||||
}
|
||||
|
||||
static inline u32 rhf_rcv_seq(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_RCV_SEQ_SHIFT) & RHF_RCV_SEQ_MASK;
|
||||
}
|
||||
|
||||
/* returned offset is in DWORDS */
|
||||
static inline u32 rhf_hdrq_offset(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_HDRQ_OFFSET_SHIFT) & RHF_HDRQ_OFFSET_MASK;
|
||||
}
|
||||
|
||||
static inline u64 rhf_use_egr_bfr(u64 rhf)
|
||||
{
|
||||
return rhf & RHF_USE_EGR_BFR_SMASK;
|
||||
}
|
||||
|
||||
static inline u64 rhf_dc_info(u64 rhf)
|
||||
{
|
||||
return rhf & RHF_DC_INFO_SMASK;
|
||||
}
|
||||
|
||||
static inline u32 rhf_egr_buf_offset(u64 rhf)
|
||||
{
|
||||
return (rhf >> RHF_EGR_OFFSET_SHIFT) & RHF_EGR_OFFSET_MASK;
|
||||
}
|
||||
#endif /* _COMMON_H */
|
||||
558
drivers/staging/rdma/hfi1/cq.c
Normal file
558
drivers/staging/rdma/hfi1/cq.c
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "verbs.h"
|
||||
#include "hfi.h"
|
||||
|
||||
/**
|
||||
* hfi1_cq_enter - add a new entry to the completion queue
|
||||
* @cq: completion queue
|
||||
* @entry: work completion entry to add
|
||||
* @sig: true if @entry is a solicited entry
|
||||
*
|
||||
* This may be called with qp->s_lock held.
|
||||
*/
|
||||
void hfi1_cq_enter(struct hfi1_cq *cq, struct ib_wc *entry, int solicited)
|
||||
{
|
||||
struct hfi1_cq_wc *wc;
|
||||
unsigned long flags;
|
||||
u32 head;
|
||||
u32 next;
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
|
||||
/*
|
||||
* Note that the head pointer might be writable by user processes.
|
||||
* Take care to verify it is a sane value.
|
||||
*/
|
||||
wc = cq->queue;
|
||||
head = wc->head;
|
||||
if (head >= (unsigned) cq->ibcq.cqe) {
|
||||
head = cq->ibcq.cqe;
|
||||
next = 0;
|
||||
} else
|
||||
next = head + 1;
|
||||
if (unlikely(next == wc->tail)) {
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
if (cq->ibcq.event_handler) {
|
||||
struct ib_event ev;
|
||||
|
||||
ev.device = cq->ibcq.device;
|
||||
ev.element.cq = &cq->ibcq;
|
||||
ev.event = IB_EVENT_CQ_ERR;
|
||||
cq->ibcq.event_handler(&ev, cq->ibcq.cq_context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cq->ip) {
|
||||
wc->uqueue[head].wr_id = entry->wr_id;
|
||||
wc->uqueue[head].status = entry->status;
|
||||
wc->uqueue[head].opcode = entry->opcode;
|
||||
wc->uqueue[head].vendor_err = entry->vendor_err;
|
||||
wc->uqueue[head].byte_len = entry->byte_len;
|
||||
wc->uqueue[head].ex.imm_data =
|
||||
(__u32 __force)entry->ex.imm_data;
|
||||
wc->uqueue[head].qp_num = entry->qp->qp_num;
|
||||
wc->uqueue[head].src_qp = entry->src_qp;
|
||||
wc->uqueue[head].wc_flags = entry->wc_flags;
|
||||
wc->uqueue[head].pkey_index = entry->pkey_index;
|
||||
wc->uqueue[head].slid = entry->slid;
|
||||
wc->uqueue[head].sl = entry->sl;
|
||||
wc->uqueue[head].dlid_path_bits = entry->dlid_path_bits;
|
||||
wc->uqueue[head].port_num = entry->port_num;
|
||||
/* Make sure entry is written before the head index. */
|
||||
smp_wmb();
|
||||
} else
|
||||
wc->kqueue[head] = *entry;
|
||||
wc->head = next;
|
||||
|
||||
if (cq->notify == IB_CQ_NEXT_COMP ||
|
||||
(cq->notify == IB_CQ_SOLICITED &&
|
||||
(solicited || entry->status != IB_WC_SUCCESS))) {
|
||||
struct kthread_worker *worker;
|
||||
/*
|
||||
* This will cause send_complete() to be called in
|
||||
* another thread.
|
||||
*/
|
||||
smp_read_barrier_depends(); /* see hfi1_cq_exit */
|
||||
worker = cq->dd->worker;
|
||||
if (likely(worker)) {
|
||||
cq->notify = IB_CQ_NONE;
|
||||
cq->triggered++;
|
||||
queue_kthread_work(worker, &cq->comptask);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_poll_cq - poll for work completion entries
|
||||
* @ibcq: the completion queue to poll
|
||||
* @num_entries: the maximum number of entries to return
|
||||
* @entry: pointer to array where work completions are placed
|
||||
*
|
||||
* Returns the number of completion entries polled.
|
||||
*
|
||||
* This may be called from interrupt context. Also called by ib_poll_cq()
|
||||
* in the generic verbs code.
|
||||
*/
|
||||
int hfi1_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
|
||||
{
|
||||
struct hfi1_cq *cq = to_icq(ibcq);
|
||||
struct hfi1_cq_wc *wc;
|
||||
unsigned long flags;
|
||||
int npolled;
|
||||
u32 tail;
|
||||
|
||||
/* The kernel can only poll a kernel completion queue */
|
||||
if (cq->ip) {
|
||||
npolled = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
|
||||
wc = cq->queue;
|
||||
tail = wc->tail;
|
||||
if (tail > (u32) cq->ibcq.cqe)
|
||||
tail = (u32) cq->ibcq.cqe;
|
||||
for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
|
||||
if (tail == wc->head)
|
||||
break;
|
||||
/* The kernel doesn't need a RMB since it has the lock. */
|
||||
*entry = wc->kqueue[tail];
|
||||
if (tail >= cq->ibcq.cqe)
|
||||
tail = 0;
|
||||
else
|
||||
tail++;
|
||||
}
|
||||
wc->tail = tail;
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
|
||||
bail:
|
||||
return npolled;
|
||||
}
|
||||
|
||||
static void send_complete(struct kthread_work *work)
|
||||
{
|
||||
struct hfi1_cq *cq = container_of(work, struct hfi1_cq, comptask);
|
||||
|
||||
/*
|
||||
* The completion handler will most likely rearm the notification
|
||||
* and poll for all pending entries. If a new completion entry
|
||||
* is added while we are in this routine, queue_work()
|
||||
* won't call us again until we return so we check triggered to
|
||||
* see if we need to call the handler again.
|
||||
*/
|
||||
for (;;) {
|
||||
u8 triggered = cq->triggered;
|
||||
|
||||
/*
|
||||
* IPoIB connected mode assumes the callback is from a
|
||||
* soft IRQ. We simulate this by blocking "bottom halves".
|
||||
* See the implementation for ipoib_cm_handle_tx_wc(),
|
||||
* netif_tx_lock_bh() and netif_tx_lock().
|
||||
*/
|
||||
local_bh_disable();
|
||||
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
|
||||
local_bh_enable();
|
||||
|
||||
if (cq->triggered == triggered)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_create_cq - create a completion queue
|
||||
* @ibdev: the device this completion queue is attached to
|
||||
* @attr: creation attributes
|
||||
* @context: unused by the driver
|
||||
* @udata: user data for libibverbs.so
|
||||
*
|
||||
* Returns a pointer to the completion queue or negative errno values
|
||||
* for failure.
|
||||
*
|
||||
* Called by ib_create_cq() in the generic verbs code.
|
||||
*/
|
||||
struct ib_cq *hfi1_create_cq(
|
||||
struct ib_device *ibdev,
|
||||
const struct ib_cq_init_attr *attr,
|
||||
struct ib_ucontext *context,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct hfi1_ibdev *dev = to_idev(ibdev);
|
||||
struct hfi1_cq *cq;
|
||||
struct hfi1_cq_wc *wc;
|
||||
struct ib_cq *ret;
|
||||
u32 sz;
|
||||
unsigned int entries = attr->cqe;
|
||||
|
||||
if (attr->flags)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (entries < 1 || entries > hfi1_max_cqes)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Allocate the completion queue structure. */
|
||||
cq = kmalloc(sizeof(*cq), GFP_KERNEL);
|
||||
if (!cq)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Allocate the completion queue entries and head/tail pointers.
|
||||
* This is allocated separately so that it can be resized and
|
||||
* also mapped into user space.
|
||||
* We need to use vmalloc() in order to support mmap and large
|
||||
* numbers of entries.
|
||||
*/
|
||||
sz = sizeof(*wc);
|
||||
if (udata && udata->outlen >= sizeof(__u64))
|
||||
sz += sizeof(struct ib_uverbs_wc) * (entries + 1);
|
||||
else
|
||||
sz += sizeof(struct ib_wc) * (entries + 1);
|
||||
wc = vmalloc_user(sz);
|
||||
if (!wc) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_cq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the address of the WC as the offset to mmap.
|
||||
* See hfi1_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
int err;
|
||||
|
||||
cq->ip = hfi1_create_mmap_info(dev, sz, context, wc);
|
||||
if (!cq->ip) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_wc;
|
||||
}
|
||||
|
||||
err = ib_copy_to_udata(udata, &cq->ip->offset,
|
||||
sizeof(cq->ip->offset));
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto bail_ip;
|
||||
}
|
||||
} else
|
||||
cq->ip = NULL;
|
||||
|
||||
spin_lock(&dev->n_cqs_lock);
|
||||
if (dev->n_cqs_allocated == hfi1_max_cqs) {
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_ip;
|
||||
}
|
||||
|
||||
dev->n_cqs_allocated++;
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
|
||||
if (cq->ip) {
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
list_add(&cq->ip->pending_mmaps, &dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
|
||||
* The number of entries should be >= the number requested or return
|
||||
* an error.
|
||||
*/
|
||||
cq->dd = dd_from_dev(dev);
|
||||
cq->ibcq.cqe = entries;
|
||||
cq->notify = IB_CQ_NONE;
|
||||
cq->triggered = 0;
|
||||
spin_lock_init(&cq->lock);
|
||||
init_kthread_work(&cq->comptask, send_complete);
|
||||
wc->head = 0;
|
||||
wc->tail = 0;
|
||||
cq->queue = wc;
|
||||
|
||||
ret = &cq->ibcq;
|
||||
|
||||
goto done;
|
||||
|
||||
bail_ip:
|
||||
kfree(cq->ip);
|
||||
bail_wc:
|
||||
vfree(wc);
|
||||
bail_cq:
|
||||
kfree(cq);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_destroy_cq - destroy a completion queue
|
||||
* @ibcq: the completion queue to destroy.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*
|
||||
* Called by ib_destroy_cq() in the generic verbs code.
|
||||
*/
|
||||
int hfi1_destroy_cq(struct ib_cq *ibcq)
|
||||
{
|
||||
struct hfi1_ibdev *dev = to_idev(ibcq->device);
|
||||
struct hfi1_cq *cq = to_icq(ibcq);
|
||||
|
||||
flush_kthread_work(&cq->comptask);
|
||||
spin_lock(&dev->n_cqs_lock);
|
||||
dev->n_cqs_allocated--;
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
if (cq->ip)
|
||||
kref_put(&cq->ip->ref, hfi1_release_mmap_info);
|
||||
else
|
||||
vfree(cq->queue);
|
||||
kfree(cq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_req_notify_cq - change the notification type for a completion queue
|
||||
* @ibcq: the completion queue
|
||||
* @notify_flags: the type of notification to request
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*
|
||||
* This may be called from interrupt context. Also called by
|
||||
* ib_req_notify_cq() in the generic verbs code.
|
||||
*/
|
||||
int hfi1_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags)
|
||||
{
|
||||
struct hfi1_cq *cq = to_icq(ibcq);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
/*
|
||||
* Don't change IB_CQ_NEXT_COMP to IB_CQ_SOLICITED but allow
|
||||
* any other transitions (see C11-31 and C11-32 in ch. 11.4.2.2).
|
||||
*/
|
||||
if (cq->notify != IB_CQ_NEXT_COMP)
|
||||
cq->notify = notify_flags & IB_CQ_SOLICITED_MASK;
|
||||
|
||||
if ((notify_flags & IB_CQ_REPORT_MISSED_EVENTS) &&
|
||||
cq->queue->head != cq->queue->tail)
|
||||
ret = 1;
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_resize_cq - change the size of the CQ
|
||||
* @ibcq: the completion queue
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*/
|
||||
int hfi1_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
|
||||
{
|
||||
struct hfi1_cq *cq = to_icq(ibcq);
|
||||
struct hfi1_cq_wc *old_wc;
|
||||
struct hfi1_cq_wc *wc;
|
||||
u32 head, tail, n;
|
||||
int ret;
|
||||
u32 sz;
|
||||
|
||||
if (cqe < 1 || cqe > hfi1_max_cqes) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to use vmalloc() if we want to support large #s of entries.
|
||||
*/
|
||||
sz = sizeof(*wc);
|
||||
if (udata && udata->outlen >= sizeof(__u64))
|
||||
sz += sizeof(struct ib_uverbs_wc) * (cqe + 1);
|
||||
else
|
||||
sz += sizeof(struct ib_wc) * (cqe + 1);
|
||||
wc = vmalloc_user(sz);
|
||||
if (!wc) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Check that we can write the offset to mmap. */
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
__u64 offset = 0;
|
||||
|
||||
ret = ib_copy_to_udata(udata, &offset, sizeof(offset));
|
||||
if (ret)
|
||||
goto bail_free;
|
||||
}
|
||||
|
||||
spin_lock_irq(&cq->lock);
|
||||
/*
|
||||
* Make sure head and tail are sane since they
|
||||
* might be user writable.
|
||||
*/
|
||||
old_wc = cq->queue;
|
||||
head = old_wc->head;
|
||||
if (head > (u32) cq->ibcq.cqe)
|
||||
head = (u32) cq->ibcq.cqe;
|
||||
tail = old_wc->tail;
|
||||
if (tail > (u32) cq->ibcq.cqe)
|
||||
tail = (u32) cq->ibcq.cqe;
|
||||
if (head < tail)
|
||||
n = cq->ibcq.cqe + 1 + head - tail;
|
||||
else
|
||||
n = head - tail;
|
||||
if (unlikely((u32)cqe < n)) {
|
||||
ret = -EINVAL;
|
||||
goto bail_unlock;
|
||||
}
|
||||
for (n = 0; tail != head; n++) {
|
||||
if (cq->ip)
|
||||
wc->uqueue[n] = old_wc->uqueue[tail];
|
||||
else
|
||||
wc->kqueue[n] = old_wc->kqueue[tail];
|
||||
if (tail == (u32) cq->ibcq.cqe)
|
||||
tail = 0;
|
||||
else
|
||||
tail++;
|
||||
}
|
||||
cq->ibcq.cqe = cqe;
|
||||
wc->head = n;
|
||||
wc->tail = 0;
|
||||
cq->queue = wc;
|
||||
spin_unlock_irq(&cq->lock);
|
||||
|
||||
vfree(old_wc);
|
||||
|
||||
if (cq->ip) {
|
||||
struct hfi1_ibdev *dev = to_idev(ibcq->device);
|
||||
struct hfi1_mmap_info *ip = cq->ip;
|
||||
|
||||
hfi1_update_mmap_info(dev, ip, sz, wc);
|
||||
|
||||
/*
|
||||
* Return the offset to mmap.
|
||||
* See hfi1_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
ret = ib_copy_to_udata(udata, &ip->offset,
|
||||
sizeof(ip->offset));
|
||||
if (ret)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
if (list_empty(&ip->pending_mmaps))
|
||||
list_add(&ip->pending_mmaps, &dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto bail;
|
||||
|
||||
bail_unlock:
|
||||
spin_unlock_irq(&cq->lock);
|
||||
bail_free:
|
||||
vfree(wc);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hfi1_cq_init(struct hfi1_devdata *dd)
|
||||
{
|
||||
int ret = 0;
|
||||
int cpu;
|
||||
struct task_struct *task;
|
||||
|
||||
if (dd->worker)
|
||||
return 0;
|
||||
dd->worker = kzalloc(sizeof(*dd->worker), GFP_KERNEL);
|
||||
if (!dd->worker)
|
||||
return -ENOMEM;
|
||||
init_kthread_worker(dd->worker);
|
||||
task = kthread_create_on_node(
|
||||
kthread_worker_fn,
|
||||
dd->worker,
|
||||
dd->assigned_node_id,
|
||||
"hfi1_cq%d", dd->unit);
|
||||
if (IS_ERR(task))
|
||||
goto task_fail;
|
||||
cpu = cpumask_first(cpumask_of_node(dd->assigned_node_id));
|
||||
kthread_bind(task, cpu);
|
||||
wake_up_process(task);
|
||||
out:
|
||||
return ret;
|
||||
task_fail:
|
||||
ret = PTR_ERR(task);
|
||||
kfree(dd->worker);
|
||||
dd->worker = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void hfi1_cq_exit(struct hfi1_devdata *dd)
|
||||
{
|
||||
struct kthread_worker *worker;
|
||||
|
||||
worker = dd->worker;
|
||||
if (!worker)
|
||||
return;
|
||||
/* blocks future queuing from send_complete() */
|
||||
dd->worker = NULL;
|
||||
smp_wmb(); /* See hfi1_cq_enter */
|
||||
flush_kthread_worker(worker);
|
||||
kthread_stop(worker->task);
|
||||
kfree(worker);
|
||||
}
|
||||
899
drivers/staging/rdma/hfi1/debugfs.c
Normal file
899
drivers/staging/rdma/hfi1/debugfs.c
Normal file
|
|
@ -0,0 +1,899 @@
|
|||
#ifdef CONFIG_DEBUG_FS
|
||||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "hfi.h"
|
||||
#include "debugfs.h"
|
||||
#include "device.h"
|
||||
#include "qp.h"
|
||||
#include "sdma.h"
|
||||
|
||||
static struct dentry *hfi1_dbg_root;
|
||||
|
||||
#define private2dd(file) (file_inode(file)->i_private)
|
||||
#define private2ppd(file) (file_inode(file)->i_private)
|
||||
|
||||
#define DEBUGFS_SEQ_FILE_OPS(name) \
|
||||
static const struct seq_operations _##name##_seq_ops = { \
|
||||
.start = _##name##_seq_start, \
|
||||
.next = _##name##_seq_next, \
|
||||
.stop = _##name##_seq_stop, \
|
||||
.show = _##name##_seq_show \
|
||||
}
|
||||
#define DEBUGFS_SEQ_FILE_OPEN(name) \
|
||||
static int _##name##_open(struct inode *inode, struct file *s) \
|
||||
{ \
|
||||
struct seq_file *seq; \
|
||||
int ret; \
|
||||
ret = seq_open(s, &_##name##_seq_ops); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
seq = s->private_data; \
|
||||
seq->private = inode->i_private; \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define DEBUGFS_FILE_OPS(name) \
|
||||
static const struct file_operations _##name##_file_ops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = _##name##_open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = seq_release \
|
||||
}
|
||||
|
||||
#define DEBUGFS_FILE_CREATE(name, parent, data, ops, mode) \
|
||||
do { \
|
||||
struct dentry *ent; \
|
||||
ent = debugfs_create_file(name, mode, parent, \
|
||||
data, ops); \
|
||||
if (!ent) \
|
||||
pr_warn("create of %s failed\n", name); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define DEBUGFS_SEQ_FILE_CREATE(name, parent, data) \
|
||||
DEBUGFS_FILE_CREATE(#name, parent, data, &_##name##_file_ops, S_IRUGO)
|
||||
|
||||
static void *_opcode_stats_seq_start(struct seq_file *s, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct hfi1_opcode_stats_perctx *opstats;
|
||||
|
||||
rcu_read_lock();
|
||||
if (*pos >= ARRAY_SIZE(opstats->stats))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *_opcode_stats_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct hfi1_opcode_stats_perctx *opstats;
|
||||
|
||||
++*pos;
|
||||
if (*pos >= ARRAY_SIZE(opstats->stats))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static void _opcode_stats_seq_stop(struct seq_file *s, void *v)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int _opcode_stats_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *spos = v;
|
||||
loff_t i = *spos, j;
|
||||
u64 n_packets = 0, n_bytes = 0;
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
|
||||
for (j = 0; j < dd->first_user_ctxt; j++) {
|
||||
if (!dd->rcd[j])
|
||||
continue;
|
||||
n_packets += dd->rcd[j]->opstats->stats[i].n_packets;
|
||||
n_bytes += dd->rcd[j]->opstats->stats[i].n_bytes;
|
||||
}
|
||||
if (!n_packets && !n_bytes)
|
||||
return SEQ_SKIP;
|
||||
seq_printf(s, "%02llx %llu/%llu\n", i,
|
||||
(unsigned long long) n_packets,
|
||||
(unsigned long long) n_bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(opcode_stats);
|
||||
DEBUGFS_SEQ_FILE_OPEN(opcode_stats)
|
||||
DEBUGFS_FILE_OPS(opcode_stats);
|
||||
|
||||
static void *_ctx_stats_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
|
||||
if (!*pos)
|
||||
return SEQ_START_TOKEN;
|
||||
if (*pos >= dd->first_user_ctxt)
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *_ctx_stats_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
return pos;
|
||||
|
||||
++*pos;
|
||||
if (*pos >= dd->first_user_ctxt)
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void _ctx_stats_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
/* nothing allocated */
|
||||
}
|
||||
|
||||
static int _ctx_stats_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *spos;
|
||||
loff_t i, j;
|
||||
u64 n_packets = 0;
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_puts(s, "Ctx:npkts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spos = v;
|
||||
i = *spos;
|
||||
|
||||
if (!dd->rcd[i])
|
||||
return SEQ_SKIP;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(dd->rcd[i]->opstats->stats); j++)
|
||||
n_packets += dd->rcd[i]->opstats->stats[j].n_packets;
|
||||
|
||||
if (!n_packets)
|
||||
return SEQ_SKIP;
|
||||
|
||||
seq_printf(s, " %llu:%llu\n", i, n_packets);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(ctx_stats);
|
||||
DEBUGFS_SEQ_FILE_OPEN(ctx_stats)
|
||||
DEBUGFS_FILE_OPS(ctx_stats);
|
||||
|
||||
static void *_qp_stats_seq_start(struct seq_file *s, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct qp_iter *iter;
|
||||
loff_t n = *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
iter = qp_iter_init(s->private);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
while (n--) {
|
||||
if (qp_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void *_qp_stats_seq_next(struct seq_file *s, void *iter_ptr,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct qp_iter *iter = iter_ptr;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (qp_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void _qp_stats_seq_stop(struct seq_file *s, void *iter_ptr)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int _qp_stats_seq_show(struct seq_file *s, void *iter_ptr)
|
||||
{
|
||||
struct qp_iter *iter = iter_ptr;
|
||||
|
||||
if (!iter)
|
||||
return 0;
|
||||
|
||||
qp_iter_print(s, iter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(qp_stats);
|
||||
DEBUGFS_SEQ_FILE_OPEN(qp_stats)
|
||||
DEBUGFS_FILE_OPS(qp_stats);
|
||||
|
||||
static void *_sdes_seq_start(struct seq_file *s, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct hfi1_ibdev *ibd;
|
||||
struct hfi1_devdata *dd;
|
||||
|
||||
rcu_read_lock();
|
||||
ibd = (struct hfi1_ibdev *)s->private;
|
||||
dd = dd_from_dev(ibd);
|
||||
if (!dd->per_sdma || *pos >= dd->num_sdma)
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *_sdes_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
|
||||
++*pos;
|
||||
if (!dd->per_sdma || *pos >= dd->num_sdma)
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static void _sdes_seq_stop(struct seq_file *s, void *v)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int _sdes_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private;
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
loff_t *spos = v;
|
||||
loff_t i = *spos;
|
||||
|
||||
sdma_seqfile_dump_sde(s, &dd->per_sdma[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(sdes);
|
||||
DEBUGFS_SEQ_FILE_OPEN(sdes)
|
||||
DEBUGFS_FILE_OPS(sdes);
|
||||
|
||||
/* read the per-device counters */
|
||||
static ssize_t dev_counters_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
u64 *counters;
|
||||
size_t avail;
|
||||
struct hfi1_devdata *dd;
|
||||
ssize_t rval;
|
||||
|
||||
rcu_read_lock();
|
||||
dd = private2dd(file);
|
||||
avail = hfi1_read_cntrs(dd, *ppos, NULL, &counters);
|
||||
rval = simple_read_from_buffer(buf, count, ppos, counters, avail);
|
||||
rcu_read_unlock();
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* read the per-device counters */
|
||||
static ssize_t dev_names_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *names;
|
||||
size_t avail;
|
||||
struct hfi1_devdata *dd;
|
||||
ssize_t rval;
|
||||
|
||||
rcu_read_lock();
|
||||
dd = private2dd(file);
|
||||
avail = hfi1_read_cntrs(dd, *ppos, &names, NULL);
|
||||
rval = simple_read_from_buffer(buf, count, ppos, names, avail);
|
||||
rcu_read_unlock();
|
||||
return rval;
|
||||
}
|
||||
|
||||
struct counter_info {
|
||||
char *name;
|
||||
const struct file_operations ops;
|
||||
};
|
||||
|
||||
/*
|
||||
* Could use file_inode(file)->i_ino to figure out which file,
|
||||
* instead of separate routine for each, but for now, this works...
|
||||
*/
|
||||
|
||||
/* read the per-port names (same for each port) */
|
||||
static ssize_t portnames_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *names;
|
||||
size_t avail;
|
||||
struct hfi1_devdata *dd;
|
||||
ssize_t rval;
|
||||
|
||||
rcu_read_lock();
|
||||
dd = private2dd(file);
|
||||
/* port number n/a here since names are constant */
|
||||
avail = hfi1_read_portcntrs(dd, *ppos, 0, &names, NULL);
|
||||
rval = simple_read_from_buffer(buf, count, ppos, names, avail);
|
||||
rcu_read_unlock();
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* read the per-port counters */
|
||||
static ssize_t portcntrs_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
u64 *counters;
|
||||
size_t avail;
|
||||
struct hfi1_devdata *dd;
|
||||
struct hfi1_pportdata *ppd;
|
||||
ssize_t rval;
|
||||
|
||||
rcu_read_lock();
|
||||
ppd = private2ppd(file);
|
||||
dd = ppd->dd;
|
||||
avail = hfi1_read_portcntrs(dd, *ppos, ppd->port - 1, NULL, &counters);
|
||||
rval = simple_read_from_buffer(buf, count, ppos, counters, avail);
|
||||
rcu_read_unlock();
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* read the per-port QSFP data for ppd
|
||||
*/
|
||||
static ssize_t qsfp_debugfs_dump(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hfi1_pportdata *ppd;
|
||||
char *tmp;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ppd = private2ppd(file);
|
||||
tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
rcu_read_unlock();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qsfp_dump(ppd, tmp, PAGE_SIZE);
|
||||
if (ret > 0)
|
||||
ret = simple_read_from_buffer(buf, count, ppos, tmp, ret);
|
||||
rcu_read_unlock();
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do an i2c write operation on the chain for the given HFI. */
|
||||
static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos, u32 target)
|
||||
{
|
||||
struct hfi1_pportdata *ppd;
|
||||
char *buff;
|
||||
int ret;
|
||||
int i2c_addr;
|
||||
int offset;
|
||||
int total_written;
|
||||
|
||||
rcu_read_lock();
|
||||
ppd = private2ppd(file);
|
||||
|
||||
buff = kmalloc(count, GFP_KERNEL);
|
||||
if (!buff) {
|
||||
ret = -ENOMEM;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
ret = copy_from_user(buff, buf, count);
|
||||
if (ret > 0) {
|
||||
ret = -EFAULT;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
i2c_addr = (*ppos >> 16) & 0xff;
|
||||
offset = *ppos & 0xffff;
|
||||
|
||||
total_written = i2c_write(ppd, target, i2c_addr, offset, buff, count);
|
||||
if (total_written < 0) {
|
||||
ret = total_written;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
*ppos += total_written;
|
||||
|
||||
ret = total_written;
|
||||
|
||||
_free:
|
||||
kfree(buff);
|
||||
_return:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do an i2c write operation on chain for HFI 0. */
|
||||
static ssize_t i2c1_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __i2c_debugfs_write(file, buf, count, ppos, 0);
|
||||
}
|
||||
|
||||
/* Do an i2c write operation on chain for HFI 1. */
|
||||
static ssize_t i2c2_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __i2c_debugfs_write(file, buf, count, ppos, 1);
|
||||
}
|
||||
|
||||
/* Do an i2c read operation on the chain for the given HFI. */
|
||||
static ssize_t __i2c_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos, u32 target)
|
||||
{
|
||||
struct hfi1_pportdata *ppd;
|
||||
char *buff;
|
||||
int ret;
|
||||
int i2c_addr;
|
||||
int offset;
|
||||
int total_read;
|
||||
|
||||
rcu_read_lock();
|
||||
ppd = private2ppd(file);
|
||||
|
||||
buff = kmalloc(count, GFP_KERNEL);
|
||||
if (!buff) {
|
||||
ret = -ENOMEM;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
i2c_addr = (*ppos >> 16) & 0xff;
|
||||
offset = *ppos & 0xffff;
|
||||
|
||||
total_read = i2c_read(ppd, target, i2c_addr, offset, buff, count);
|
||||
if (total_read < 0) {
|
||||
ret = total_read;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
*ppos += total_read;
|
||||
|
||||
ret = copy_to_user(buf, buff, total_read);
|
||||
if (ret > 0) {
|
||||
ret = -EFAULT;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
ret = total_read;
|
||||
|
||||
_free:
|
||||
kfree(buff);
|
||||
_return:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do an i2c read operation on chain for HFI 0. */
|
||||
static ssize_t i2c1_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __i2c_debugfs_read(file, buf, count, ppos, 0);
|
||||
}
|
||||
|
||||
/* Do an i2c read operation on chain for HFI 1. */
|
||||
static ssize_t i2c2_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __i2c_debugfs_read(file, buf, count, ppos, 1);
|
||||
}
|
||||
|
||||
/* Do a QSFP write operation on the i2c chain for the given HFI. */
|
||||
static ssize_t __qsfp_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos, u32 target)
|
||||
{
|
||||
struct hfi1_pportdata *ppd;
|
||||
char *buff;
|
||||
int ret;
|
||||
int total_written;
|
||||
|
||||
rcu_read_lock();
|
||||
if (*ppos + count > QSFP_PAGESIZE * 4) { /* base page + page00-page03 */
|
||||
ret = -EINVAL;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
ppd = private2ppd(file);
|
||||
|
||||
buff = kmalloc(count, GFP_KERNEL);
|
||||
if (!buff) {
|
||||
ret = -ENOMEM;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
ret = copy_from_user(buff, buf, count);
|
||||
if (ret > 0) {
|
||||
ret = -EFAULT;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
total_written = qsfp_write(ppd, target, *ppos, buff, count);
|
||||
if (total_written < 0) {
|
||||
ret = total_written;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
*ppos += total_written;
|
||||
|
||||
ret = total_written;
|
||||
|
||||
_free:
|
||||
kfree(buff);
|
||||
_return:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do a QSFP write operation on i2c chain for HFI 0. */
|
||||
static ssize_t qsfp1_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __qsfp_debugfs_write(file, buf, count, ppos, 0);
|
||||
}
|
||||
|
||||
/* Do a QSFP write operation on i2c chain for HFI 1. */
|
||||
static ssize_t qsfp2_debugfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __qsfp_debugfs_write(file, buf, count, ppos, 1);
|
||||
}
|
||||
|
||||
/* Do a QSFP read operation on the i2c chain for the given HFI. */
|
||||
static ssize_t __qsfp_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos, u32 target)
|
||||
{
|
||||
struct hfi1_pportdata *ppd;
|
||||
char *buff;
|
||||
int ret;
|
||||
int total_read;
|
||||
|
||||
rcu_read_lock();
|
||||
if (*ppos + count > QSFP_PAGESIZE * 4) { /* base page + page00-page03 */
|
||||
ret = -EINVAL;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
ppd = private2ppd(file);
|
||||
|
||||
buff = kmalloc(count, GFP_KERNEL);
|
||||
if (!buff) {
|
||||
ret = -ENOMEM;
|
||||
goto _return;
|
||||
}
|
||||
|
||||
total_read = qsfp_read(ppd, target, *ppos, buff, count);
|
||||
if (total_read < 0) {
|
||||
ret = total_read;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
*ppos += total_read;
|
||||
|
||||
ret = copy_to_user(buf, buff, total_read);
|
||||
if (ret > 0) {
|
||||
ret = -EFAULT;
|
||||
goto _free;
|
||||
}
|
||||
|
||||
ret = total_read;
|
||||
|
||||
_free:
|
||||
kfree(buff);
|
||||
_return:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do a QSFP read operation on i2c chain for HFI 0. */
|
||||
static ssize_t qsfp1_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __qsfp_debugfs_read(file, buf, count, ppos, 0);
|
||||
}
|
||||
|
||||
/* Do a QSFP read operation on i2c chain for HFI 1. */
|
||||
static ssize_t qsfp2_debugfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return __qsfp_debugfs_read(file, buf, count, ppos, 1);
|
||||
}
|
||||
|
||||
#define DEBUGFS_OPS(nm, readroutine, writeroutine) \
|
||||
{ \
|
||||
.name = nm, \
|
||||
.ops = { \
|
||||
.read = readroutine, \
|
||||
.write = writeroutine, \
|
||||
.llseek = generic_file_llseek, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct counter_info cntr_ops[] = {
|
||||
DEBUGFS_OPS("counter_names", dev_names_read, NULL),
|
||||
DEBUGFS_OPS("counters", dev_counters_read, NULL),
|
||||
DEBUGFS_OPS("portcounter_names", portnames_read, NULL),
|
||||
};
|
||||
|
||||
static const struct counter_info port_cntr_ops[] = {
|
||||
DEBUGFS_OPS("port%dcounters", portcntrs_debugfs_read, NULL),
|
||||
DEBUGFS_OPS("i2c1", i2c1_debugfs_read, i2c1_debugfs_write),
|
||||
DEBUGFS_OPS("i2c2", i2c2_debugfs_read, i2c2_debugfs_write),
|
||||
DEBUGFS_OPS("qsfp_dump%d", qsfp_debugfs_dump, NULL),
|
||||
DEBUGFS_OPS("qsfp1", qsfp1_debugfs_read, qsfp1_debugfs_write),
|
||||
DEBUGFS_OPS("qsfp2", qsfp2_debugfs_read, qsfp2_debugfs_write),
|
||||
};
|
||||
|
||||
void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd)
|
||||
{
|
||||
char name[sizeof("port0counters") + 1];
|
||||
char link[10];
|
||||
struct hfi1_devdata *dd = dd_from_dev(ibd);
|
||||
struct hfi1_pportdata *ppd;
|
||||
int unit = dd->unit;
|
||||
int i, j;
|
||||
|
||||
if (!hfi1_dbg_root)
|
||||
return;
|
||||
snprintf(name, sizeof(name), "%s_%d", class_name(), unit);
|
||||
snprintf(link, sizeof(link), "%d", unit);
|
||||
ibd->hfi1_ibdev_dbg = debugfs_create_dir(name, hfi1_dbg_root);
|
||||
if (!ibd->hfi1_ibdev_dbg) {
|
||||
pr_warn("create of %s failed\n", name);
|
||||
return;
|
||||
}
|
||||
ibd->hfi1_ibdev_link =
|
||||
debugfs_create_symlink(link, hfi1_dbg_root, name);
|
||||
if (!ibd->hfi1_ibdev_link) {
|
||||
pr_warn("create of %s symlink failed\n", name);
|
||||
return;
|
||||
}
|
||||
DEBUGFS_SEQ_FILE_CREATE(opcode_stats, ibd->hfi1_ibdev_dbg, ibd);
|
||||
DEBUGFS_SEQ_FILE_CREATE(ctx_stats, ibd->hfi1_ibdev_dbg, ibd);
|
||||
DEBUGFS_SEQ_FILE_CREATE(qp_stats, ibd->hfi1_ibdev_dbg, ibd);
|
||||
DEBUGFS_SEQ_FILE_CREATE(sdes, ibd->hfi1_ibdev_dbg, ibd);
|
||||
/* dev counter files */
|
||||
for (i = 0; i < ARRAY_SIZE(cntr_ops); i++)
|
||||
DEBUGFS_FILE_CREATE(cntr_ops[i].name,
|
||||
ibd->hfi1_ibdev_dbg,
|
||||
dd,
|
||||
&cntr_ops[i].ops, S_IRUGO);
|
||||
/* per port files */
|
||||
for (ppd = dd->pport, j = 0; j < dd->num_pports; j++, ppd++)
|
||||
for (i = 0; i < ARRAY_SIZE(port_cntr_ops); i++) {
|
||||
snprintf(name,
|
||||
sizeof(name),
|
||||
port_cntr_ops[i].name,
|
||||
j + 1);
|
||||
DEBUGFS_FILE_CREATE(name,
|
||||
ibd->hfi1_ibdev_dbg,
|
||||
ppd,
|
||||
&port_cntr_ops[i].ops,
|
||||
port_cntr_ops[i].ops.write == NULL ?
|
||||
S_IRUGO : S_IRUGO|S_IWUSR);
|
||||
}
|
||||
}
|
||||
|
||||
void hfi1_dbg_ibdev_exit(struct hfi1_ibdev *ibd)
|
||||
{
|
||||
if (!hfi1_dbg_root)
|
||||
goto out;
|
||||
debugfs_remove(ibd->hfi1_ibdev_link);
|
||||
debugfs_remove_recursive(ibd->hfi1_ibdev_dbg);
|
||||
out:
|
||||
ibd->hfi1_ibdev_dbg = NULL;
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
/*
|
||||
* driver stats field names, one line per stat, single string. Used by
|
||||
* programs like hfistats to print the stats in a way which works for
|
||||
* different versions of drivers, without changing program source.
|
||||
* if hfi1_ib_stats changes, this needs to change. Names need to be
|
||||
* 12 chars or less (w/o newline), for proper display by hfistats utility.
|
||||
*/
|
||||
static const char * const hfi1_statnames[] = {
|
||||
/* must be element 0*/
|
||||
"KernIntr",
|
||||
"ErrorIntr",
|
||||
"Tx_Errs",
|
||||
"Rcv_Errs",
|
||||
"H/W_Errs",
|
||||
"NoPIOBufs",
|
||||
"CtxtsOpen",
|
||||
"RcvLen_Errs",
|
||||
"EgrBufFull",
|
||||
"EgrHdrFull"
|
||||
};
|
||||
|
||||
static void *_driver_stats_names_seq_start(struct seq_file *s, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
rcu_read_lock();
|
||||
if (*pos >= ARRAY_SIZE(hfi1_statnames))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *_driver_stats_names_seq_next(
|
||||
struct seq_file *s,
|
||||
void *v,
|
||||
loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
if (*pos >= ARRAY_SIZE(hfi1_statnames))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void _driver_stats_names_seq_stop(struct seq_file *s, void *v)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int _driver_stats_names_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *spos = v;
|
||||
|
||||
seq_printf(s, "%s\n", hfi1_statnames[*spos]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(driver_stats_names);
|
||||
DEBUGFS_SEQ_FILE_OPEN(driver_stats_names)
|
||||
DEBUGFS_FILE_OPS(driver_stats_names);
|
||||
|
||||
static void *_driver_stats_seq_start(struct seq_file *s, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
rcu_read_lock();
|
||||
if (*pos >= ARRAY_SIZE(hfi1_statnames))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *_driver_stats_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
if (*pos >= ARRAY_SIZE(hfi1_statnames))
|
||||
return NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void _driver_stats_seq_stop(struct seq_file *s, void *v)
|
||||
__releases(RCU)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static u64 hfi1_sps_ints(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hfi1_devdata *dd;
|
||||
u64 sps_ints = 0;
|
||||
|
||||
spin_lock_irqsave(&hfi1_devs_lock, flags);
|
||||
list_for_each_entry(dd, &hfi1_dev_list, list) {
|
||||
sps_ints += get_all_cpu_total(dd->int_counter);
|
||||
}
|
||||
spin_unlock_irqrestore(&hfi1_devs_lock, flags);
|
||||
return sps_ints;
|
||||
}
|
||||
|
||||
static int _driver_stats_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *spos = v;
|
||||
char *buffer;
|
||||
u64 *stats = (u64 *)&hfi1_stats;
|
||||
size_t sz = seq_get_buf(s, &buffer);
|
||||
|
||||
if (sz < sizeof(u64))
|
||||
return SEQ_SKIP;
|
||||
/* special case for interrupts */
|
||||
if (*spos == 0)
|
||||
*(u64 *)buffer = hfi1_sps_ints();
|
||||
else
|
||||
*(u64 *)buffer = stats[*spos];
|
||||
seq_commit(s, sizeof(u64));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGFS_SEQ_FILE_OPS(driver_stats);
|
||||
DEBUGFS_SEQ_FILE_OPEN(driver_stats)
|
||||
DEBUGFS_FILE_OPS(driver_stats);
|
||||
|
||||
void hfi1_dbg_init(void)
|
||||
{
|
||||
hfi1_dbg_root = debugfs_create_dir(DRIVER_NAME, NULL);
|
||||
if (!hfi1_dbg_root)
|
||||
pr_warn("init of debugfs failed\n");
|
||||
DEBUGFS_SEQ_FILE_CREATE(driver_stats_names, hfi1_dbg_root, NULL);
|
||||
DEBUGFS_SEQ_FILE_CREATE(driver_stats, hfi1_dbg_root, NULL);
|
||||
}
|
||||
|
||||
void hfi1_dbg_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(hfi1_dbg_root);
|
||||
hfi1_dbg_root = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
78
drivers/staging/rdma/hfi1/debugfs.h
Normal file
78
drivers/staging/rdma/hfi1/debugfs.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#ifndef _HFI1_DEBUGFS_H
|
||||
#define _HFI1_DEBUGFS_H
|
||||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
struct hfi1_ibdev;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd);
|
||||
void hfi1_dbg_ibdev_exit(struct hfi1_ibdev *ibd);
|
||||
void hfi1_dbg_init(void);
|
||||
void hfi1_dbg_exit(void);
|
||||
#else
|
||||
static inline void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd)
|
||||
{
|
||||
}
|
||||
|
||||
void hfi1_dbg_ibdev_exit(struct hfi1_ibdev *ibd)
|
||||
{
|
||||
}
|
||||
|
||||
void hfi1_dbg_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void hfi1_dbg_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _HFI1_DEBUGFS_H */
|
||||
142
drivers/staging/rdma/hfi1/device.c
Normal file
142
drivers/staging/rdma/hfi1/device.c
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "hfi.h"
|
||||
#include "device.h"
|
||||
|
||||
static struct class *class;
|
||||
static dev_t hfi1_dev;
|
||||
|
||||
int hfi1_cdev_init(int minor, const char *name,
|
||||
const struct file_operations *fops,
|
||||
struct cdev *cdev, struct device **devp)
|
||||
{
|
||||
const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor);
|
||||
struct device *device = NULL;
|
||||
int ret;
|
||||
|
||||
cdev_init(cdev, fops);
|
||||
cdev->owner = THIS_MODULE;
|
||||
kobject_set_name(&cdev->kobj, name);
|
||||
|
||||
ret = cdev_add(cdev, dev, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("Could not add cdev for minor %d, %s (err %d)\n",
|
||||
minor, name, -ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
device = device_create(class, NULL, dev, NULL, "%s", name);
|
||||
if (!IS_ERR(device))
|
||||
goto done;
|
||||
ret = PTR_ERR(device);
|
||||
device = NULL;
|
||||
pr_err("Could not create device for minor %d, %s (err %d)\n",
|
||||
minor, name, -ret);
|
||||
cdev_del(cdev);
|
||||
done:
|
||||
*devp = device;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp)
|
||||
{
|
||||
struct device *device = *devp;
|
||||
|
||||
if (device) {
|
||||
device_unregister(device);
|
||||
*devp = NULL;
|
||||
|
||||
cdev_del(cdev);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *hfi1_class_name = "hfi1";
|
||||
|
||||
const char *class_name(void)
|
||||
{
|
||||
return hfi1_class_name;
|
||||
}
|
||||
|
||||
int __init dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = alloc_chrdev_region(&hfi1_dev, 0, HFI1_NMINORS, DRIVER_NAME);
|
||||
if (ret < 0) {
|
||||
pr_err("Could not allocate chrdev region (err %d)\n", -ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
class = class_create(THIS_MODULE, class_name());
|
||||
if (IS_ERR(class)) {
|
||||
ret = PTR_ERR(class);
|
||||
pr_err("Could not create device class (err %d)\n", -ret);
|
||||
unregister_chrdev_region(hfi1_dev, HFI1_NMINORS);
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dev_cleanup(void)
|
||||
{
|
||||
if (class) {
|
||||
class_destroy(class);
|
||||
class = NULL;
|
||||
}
|
||||
|
||||
unregister_chrdev_region(hfi1_dev, HFI1_NMINORS);
|
||||
}
|
||||
61
drivers/staging/rdma/hfi1/device.h
Normal file
61
drivers/staging/rdma/hfi1/device.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef _HFI1_DEVICE_H
|
||||
#define _HFI1_DEVICE_H
|
||||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
int hfi1_cdev_init(int minor, const char *name,
|
||||
const struct file_operations *fops,
|
||||
struct cdev *cdev, struct device **devp);
|
||||
void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp);
|
||||
const char *class_name(void);
|
||||
int __init dev_init(void);
|
||||
void dev_cleanup(void);
|
||||
|
||||
#endif /* _HFI1_DEVICE_H */
|
||||
1873
drivers/staging/rdma/hfi1/diag.c
Normal file
1873
drivers/staging/rdma/hfi1/diag.c
Normal file
File diff suppressed because it is too large
Load diff
186
drivers/staging/rdma/hfi1/dma.c
Normal file
186
drivers/staging/rdma/hfi1/dma.c
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include "verbs.h"
|
||||
|
||||
#define BAD_DMA_ADDRESS ((u64) 0)
|
||||
|
||||
/*
|
||||
* The following functions implement driver specific replacements
|
||||
* for the ib_dma_*() functions.
|
||||
*
|
||||
* These functions return kernel virtual addresses instead of
|
||||
* device bus addresses since the driver uses the CPU to copy
|
||||
* data instead of using hardware DMA.
|
||||
*/
|
||||
|
||||
static int hfi1_mapping_error(struct ib_device *dev, u64 dma_addr)
|
||||
{
|
||||
return dma_addr == BAD_DMA_ADDRESS;
|
||||
}
|
||||
|
||||
static u64 hfi1_dma_map_single(struct ib_device *dev, void *cpu_addr,
|
||||
size_t size, enum dma_data_direction direction)
|
||||
{
|
||||
if (WARN_ON(!valid_dma_direction(direction)))
|
||||
return BAD_DMA_ADDRESS;
|
||||
|
||||
return (u64) cpu_addr;
|
||||
}
|
||||
|
||||
static void hfi1_dma_unmap_single(struct ib_device *dev, u64 addr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
/* This is a stub, nothing to be done here */
|
||||
}
|
||||
|
||||
static u64 hfi1_dma_map_page(struct ib_device *dev, struct page *page,
|
||||
unsigned long offset, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
u64 addr;
|
||||
|
||||
if (WARN_ON(!valid_dma_direction(direction)))
|
||||
return BAD_DMA_ADDRESS;
|
||||
|
||||
if (offset + size > PAGE_SIZE)
|
||||
return BAD_DMA_ADDRESS;
|
||||
|
||||
addr = (u64) page_address(page);
|
||||
if (addr)
|
||||
addr += offset;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void hfi1_dma_unmap_page(struct ib_device *dev, u64 addr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
/* This is a stub, nothing to be done here */
|
||||
}
|
||||
|
||||
static int hfi1_map_sg(struct ib_device *dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u64 addr;
|
||||
int i;
|
||||
int ret = nents;
|
||||
|
||||
if (WARN_ON(!valid_dma_direction(direction)))
|
||||
return BAD_DMA_ADDRESS;
|
||||
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
addr = (u64) page_address(sg_page(sg));
|
||||
if (!addr) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
sg->dma_address = addr + sg->offset;
|
||||
#ifdef CONFIG_NEED_SG_DMA_LENGTH
|
||||
sg->dma_length = sg->length;
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hfi1_unmap_sg(struct ib_device *dev,
|
||||
struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
/* This is a stub, nothing to be done here */
|
||||
}
|
||||
|
||||
static void hfi1_sync_single_for_cpu(struct ib_device *dev, u64 addr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
|
||||
static void hfi1_sync_single_for_device(struct ib_device *dev, u64 addr,
|
||||
size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
|
||||
static void *hfi1_dma_alloc_coherent(struct ib_device *dev, size_t size,
|
||||
u64 *dma_handle, gfp_t flag)
|
||||
{
|
||||
struct page *p;
|
||||
void *addr = NULL;
|
||||
|
||||
p = alloc_pages(flag, get_order(size));
|
||||
if (p)
|
||||
addr = page_address(p);
|
||||
if (dma_handle)
|
||||
*dma_handle = (u64) addr;
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void hfi1_dma_free_coherent(struct ib_device *dev, size_t size,
|
||||
void *cpu_addr, u64 dma_handle)
|
||||
{
|
||||
free_pages((unsigned long) cpu_addr, get_order(size));
|
||||
}
|
||||
|
||||
struct ib_dma_mapping_ops hfi1_dma_mapping_ops = {
|
||||
.mapping_error = hfi1_mapping_error,
|
||||
.map_single = hfi1_dma_map_single,
|
||||
.unmap_single = hfi1_dma_unmap_single,
|
||||
.map_page = hfi1_dma_map_page,
|
||||
.unmap_page = hfi1_dma_unmap_page,
|
||||
.map_sg = hfi1_map_sg,
|
||||
.unmap_sg = hfi1_unmap_sg,
|
||||
.sync_single_for_cpu = hfi1_sync_single_for_cpu,
|
||||
.sync_single_for_device = hfi1_sync_single_for_device,
|
||||
.alloc_coherent = hfi1_dma_alloc_coherent,
|
||||
.free_coherent = hfi1_dma_free_coherent
|
||||
};
|
||||
1241
drivers/staging/rdma/hfi1/driver.c
Normal file
1241
drivers/staging/rdma/hfi1/driver.c
Normal file
File diff suppressed because it is too large
Load diff
475
drivers/staging/rdma/hfi1/eprom.c
Normal file
475
drivers/staging/rdma/hfi1/eprom.c
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
/*
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
* - Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include "hfi.h"
|
||||
#include "common.h"
|
||||
#include "eprom.h"
|
||||
|
||||
/*
|
||||
* The EPROM is logically divided into two partitions:
|
||||
* partition 0: the first 128K, visible from PCI ROM BAR
|
||||
* partition 1: the rest
|
||||
*/
|
||||
#define P0_SIZE (128 * 1024)
|
||||
#define P1_START P0_SIZE
|
||||
|
||||
/* largest erase size supported by the controller */
|
||||
#define SIZE_32KB (32 * 1024)
|
||||
#define MASK_32KB (SIZE_32KB - 1)
|
||||
|
||||
/* controller page size, in bytes */
|
||||
#define EP_PAGE_SIZE 256
|
||||
#define EEP_PAGE_MASK (EP_PAGE_SIZE - 1)
|
||||
|
||||
/* controller commands */
|
||||
#define CMD_SHIFT 24
|
||||
#define CMD_NOP (0)
|
||||
#define CMD_PAGE_PROGRAM(addr) ((0x02 << CMD_SHIFT) | addr)
|
||||
#define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr)
|
||||
#define CMD_READ_SR1 ((0x05 << CMD_SHIFT))
|
||||
#define CMD_WRITE_ENABLE ((0x06 << CMD_SHIFT))
|
||||
#define CMD_SECTOR_ERASE_32KB(addr) ((0x52 << CMD_SHIFT) | addr)
|
||||
#define CMD_CHIP_ERASE ((0x60 << CMD_SHIFT))
|
||||
#define CMD_READ_MANUF_DEV_ID ((0x90 << CMD_SHIFT))
|
||||
#define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT))
|
||||
|
||||
/* controller interface speeds */
|
||||
#define EP_SPEED_FULL 0x2 /* full speed */
|
||||
|
||||
/* controller status register 1 bits */
|
||||
#define SR1_BUSY 0x1ull /* the BUSY bit in SR1 */
|
||||
|
||||
/* sleep length while waiting for controller */
|
||||
#define WAIT_SLEEP_US 100 /* must be larger than 5 (see usage) */
|
||||
#define COUNT_DELAY_SEC(n) ((n) * (1000000/WAIT_SLEEP_US))
|
||||
|
||||
/* GPIO pins */
|
||||
#define EPROM_WP_N (1ull << 14) /* EPROM write line */
|
||||
|
||||
/*
|
||||
* Use the EP mutex to guard against other callers from within the driver.
|
||||
* Also covers usage of eprom_available.
|
||||
*/
|
||||
static DEFINE_MUTEX(eprom_mutex);
|
||||
static int eprom_available; /* default: not available */
|
||||
|
||||
/*
|
||||
* Turn on external enable line that allows writing on the flash.
|
||||
*/
|
||||
static void write_enable(struct hfi1_devdata *dd)
|
||||
{
|
||||
/* raise signal */
|
||||
write_csr(dd, ASIC_GPIO_OUT,
|
||||
read_csr(dd, ASIC_GPIO_OUT) | EPROM_WP_N);
|
||||
/* raise enable */
|
||||
write_csr(dd, ASIC_GPIO_OE,
|
||||
read_csr(dd, ASIC_GPIO_OE) | EPROM_WP_N);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off external enable line that allows writing on the flash.
|
||||
*/
|
||||
static void write_disable(struct hfi1_devdata *dd)
|
||||
{
|
||||
/* lower signal */
|
||||
write_csr(dd, ASIC_GPIO_OUT,
|
||||
read_csr(dd, ASIC_GPIO_OUT) & ~EPROM_WP_N);
|
||||
/* lower enable */
|
||||
write_csr(dd, ASIC_GPIO_OE,
|
||||
read_csr(dd, ASIC_GPIO_OE) & ~EPROM_WP_N);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the device to become not busy. Must be called after all
|
||||
* write or erase operations.
|
||||
*/
|
||||
static int wait_for_not_busy(struct hfi1_devdata *dd)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
u64 reg;
|
||||
int ret = 0;
|
||||
|
||||
/* starts page mode */
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_SR1);
|
||||
while (1) {
|
||||
udelay(WAIT_SLEEP_US);
|
||||
usleep_range(WAIT_SLEEP_US - 5, WAIT_SLEEP_US + 5);
|
||||
count++;
|
||||
reg = read_csr(dd, ASIC_EEP_DATA);
|
||||
if ((reg & SR1_BUSY) == 0)
|
||||
break;
|
||||
/* 200s is the largest time for a 128Mb device */
|
||||
if (count > COUNT_DELAY_SEC(200)) {
|
||||
dd_dev_err(dd, "waited too long for SPI FLASH busy to clear - failing\n");
|
||||
ret = -ETIMEDOUT;
|
||||
break; /* break, not goto - must stop page mode */
|
||||
}
|
||||
}
|
||||
|
||||
/* stop page mode with a NOP */
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the device ID from the SPI controller.
|
||||
*/
|
||||
static u32 read_device_id(struct hfi1_devdata *dd)
|
||||
{
|
||||
/* read the Manufacture Device ID */
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_MANUF_DEV_ID);
|
||||
return (u32)read_csr(dd, ASIC_EEP_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase the whole flash.
|
||||
*/
|
||||
static int erase_chip(struct hfi1_devdata *dd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
write_enable(dd);
|
||||
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE);
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_CHIP_ERASE);
|
||||
ret = wait_for_not_busy(dd);
|
||||
|
||||
write_disable(dd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase a range using the 32KB erase command.
|
||||
*/
|
||||
static int erase_32kb_range(struct hfi1_devdata *dd, u32 start, u32 end)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
|
||||
if ((start & MASK_32KB) || (end & MASK_32KB)) {
|
||||
dd_dev_err(dd,
|
||||
"%s: non-aligned range (0x%x,0x%x) for a 32KB erase\n",
|
||||
__func__, start, end);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
write_enable(dd);
|
||||
|
||||
for (; start < end; start += SIZE_32KB) {
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE);
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD,
|
||||
CMD_SECTOR_ERASE_32KB(start));
|
||||
ret = wait_for_not_busy(dd);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
write_disable(dd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a 256 byte (64 dword) EPROM page.
|
||||
* All callers have verified the offset is at a page boundary.
|
||||
*/
|
||||
static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result)
|
||||
{
|
||||
int i;
|
||||
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset));
|
||||
for (i = 0; i < EP_PAGE_SIZE/sizeof(u32); i++)
|
||||
result[i] = (u32)read_csr(dd, ASIC_EEP_DATA);
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */
|
||||
}
|
||||
|
||||
/*
|
||||
* Read length bytes starting at offset. Copy to user address addr.
|
||||
*/
|
||||
static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, u64 addr)
|
||||
{
|
||||
u32 offset;
|
||||
u32 buffer[EP_PAGE_SIZE/sizeof(u32)];
|
||||
int ret = 0;
|
||||
|
||||
/* reject anything not on an EPROM page boundary */
|
||||
if ((start & EEP_PAGE_MASK) || (len & EEP_PAGE_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
for (offset = 0; offset < len; offset += EP_PAGE_SIZE) {
|
||||
read_page(dd, start + offset, buffer);
|
||||
if (copy_to_user((void __user *)(addr + offset),
|
||||
buffer, EP_PAGE_SIZE)) {
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a 256 byte (64 dword) EPROM page.
|
||||
* All callers have verified the offset is at a page boundary.
|
||||
*/
|
||||
static int write_page(struct hfi1_devdata *dd, u32 offset, u32 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE);
|
||||
write_csr(dd, ASIC_EEP_DATA, data[0]);
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_PAGE_PROGRAM(offset));
|
||||
for (i = 1; i < EP_PAGE_SIZE/sizeof(u32); i++)
|
||||
write_csr(dd, ASIC_EEP_DATA, data[i]);
|
||||
/* will close the open page */
|
||||
return wait_for_not_busy(dd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write length bytes starting at offset. Read from user address addr.
|
||||
*/
|
||||
static int write_length(struct hfi1_devdata *dd, u32 start, u32 len, u64 addr)
|
||||
{
|
||||
u32 offset;
|
||||
u32 buffer[EP_PAGE_SIZE/sizeof(u32)];
|
||||
int ret = 0;
|
||||
|
||||
/* reject anything not on an EPROM page boundary */
|
||||
if ((start & EEP_PAGE_MASK) || (len & EEP_PAGE_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
write_enable(dd);
|
||||
|
||||
for (offset = 0; offset < len; offset += EP_PAGE_SIZE) {
|
||||
if (copy_from_user(buffer, (void __user *)(addr + offset),
|
||||
EP_PAGE_SIZE)) {
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
ret = write_page(dd, start + offset, buffer);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
write_disable(dd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the given operation on the EPROM. Called from user space. The
|
||||
* user credentials have already been checked.
|
||||
*
|
||||
* Return 0 on success, -ERRNO on error
|
||||
*/
|
||||
int handle_eprom_command(const struct hfi1_cmd *cmd)
|
||||
{
|
||||
struct hfi1_devdata *dd;
|
||||
u32 dev_id;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* The EPROM is per-device, so use unit 0 as that will always
|
||||
* exist.
|
||||
*/
|
||||
dd = hfi1_lookup(0);
|
||||
if (!dd) {
|
||||
pr_err("%s: cannot find unit 0!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lock against other callers touching the ASIC block */
|
||||
mutex_lock(&eprom_mutex);
|
||||
|
||||
/* some platforms do not have an EPROM */
|
||||
if (!eprom_available) {
|
||||
ret = -ENOSYS;
|
||||
goto done_asic;
|
||||
}
|
||||
|
||||
/* lock against the other HFI on another OS */
|
||||
ret = acquire_hw_mutex(dd);
|
||||
if (ret) {
|
||||
dd_dev_err(dd,
|
||||
"%s: unable to acquire hw mutex, no EPROM support\n",
|
||||
__func__);
|
||||
goto done_asic;
|
||||
}
|
||||
|
||||
dd_dev_info(dd, "%s: cmd: type %d, len 0x%x, addr 0x%016llx\n",
|
||||
__func__, cmd->type, cmd->len, cmd->addr);
|
||||
|
||||
switch (cmd->type) {
|
||||
case HFI1_CMD_EP_INFO:
|
||||
if (cmd->len != sizeof(u32)) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
dev_id = read_device_id(dd);
|
||||
/* addr points to a u32 user buffer */
|
||||
if (copy_to_user((void __user *)cmd->addr, &dev_id,
|
||||
sizeof(u32)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case HFI1_CMD_EP_ERASE_CHIP:
|
||||
ret = erase_chip(dd);
|
||||
break;
|
||||
case HFI1_CMD_EP_ERASE_P0:
|
||||
if (cmd->len != P0_SIZE) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = erase_32kb_range(dd, 0, cmd->len);
|
||||
break;
|
||||
case HFI1_CMD_EP_ERASE_P1:
|
||||
/* check for overflow */
|
||||
if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = erase_32kb_range(dd, P1_START, P1_START + cmd->len);
|
||||
break;
|
||||
case HFI1_CMD_EP_READ_P0:
|
||||
if (cmd->len != P0_SIZE) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = read_length(dd, 0, cmd->len, cmd->addr);
|
||||
break;
|
||||
case HFI1_CMD_EP_READ_P1:
|
||||
/* check for overflow */
|
||||
if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = read_length(dd, P1_START, cmd->len, cmd->addr);
|
||||
break;
|
||||
case HFI1_CMD_EP_WRITE_P0:
|
||||
if (cmd->len > P0_SIZE) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = write_length(dd, 0, cmd->len, cmd->addr);
|
||||
break;
|
||||
case HFI1_CMD_EP_WRITE_P1:
|
||||
/* check for overflow */
|
||||
if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
ret = write_length(dd, P1_START, cmd->len, cmd->addr);
|
||||
break;
|
||||
default:
|
||||
dd_dev_err(dd, "%s: unexpected command %d\n",
|
||||
__func__, cmd->type);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
release_hw_mutex(dd);
|
||||
done_asic:
|
||||
mutex_unlock(&eprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the EPROM handler.
|
||||
*/
|
||||
int eprom_init(struct hfi1_devdata *dd)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* only the discrete chip has an EPROM, nothing to do */
|
||||
if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0)
|
||||
return 0;
|
||||
|
||||
/* lock against other callers */
|
||||
mutex_lock(&eprom_mutex);
|
||||
if (eprom_available) /* already initialized */
|
||||
goto done_asic;
|
||||
|
||||
/*
|
||||
* Lock against the other HFI on another OS - the mutex above
|
||||
* would have caught anything in this driver. It is OK if
|
||||
* both OSes reset the EPROM - as long as they don't do it at
|
||||
* the same time.
|
||||
*/
|
||||
ret = acquire_hw_mutex(dd);
|
||||
if (ret) {
|
||||
dd_dev_err(dd,
|
||||
"%s: unable to acquire hw mutex, no EPROM support\n",
|
||||
__func__);
|
||||
goto done_asic;
|
||||
}
|
||||
|
||||
/* reset EPROM to be sure it is in a good state */
|
||||
|
||||
/* set reset */
|
||||
write_csr(dd, ASIC_EEP_CTL_STAT,
|
||||
ASIC_EEP_CTL_STAT_EP_RESET_SMASK);
|
||||
/* clear reset, set speed */
|
||||
write_csr(dd, ASIC_EEP_CTL_STAT,
|
||||
EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT);
|
||||
|
||||
/* wake the device with command "release powerdown NoID" */
|
||||
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID);
|
||||
|
||||
eprom_available = 1;
|
||||
release_hw_mutex(dd);
|
||||
done_asic:
|
||||
mutex_unlock(&eprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue