mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	scsi: mpi3mr: Base driver code
Implement basic pci device driver requirements: Device probing, memory allocation, mapping system registers, allocate irq lines, etc. Source is managed in mainly three different files: - mpi3mr_fw.c: Common code which interacts with underlying fw/hw. - mpi3mr_os.c: Common code which interacts with SCSI midlayer. - mpi3mr_app.c: Common code which interacts with application/ioctl. This is currently work in progress. Link: https://lore.kernel.org/r/20210520152545.2710479-3-kashyap.desai@broadcom.com Cc: sathya.prakash@broadcom.com Cc: bvanassche@acm.org Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Tomas Henzl <thenzl@redhat.com> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
		
							parent
							
								
									c4f7ac6461
								
							
						
					
					
						commit
						824a156633
					
				
					 5 changed files with 2761 additions and 0 deletions
				
			
		
							
								
								
									
										4
									
								
								drivers/scsi/mpi3mr/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								drivers/scsi/mpi3mr/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					# mpi3mr makefile
 | 
				
			||||||
 | 
					obj-m += mpi3mr.o
 | 
				
			||||||
 | 
					mpi3mr-y +=  mpi3mr_os.o     \
 | 
				
			||||||
 | 
							mpi3mr_fw.o \
 | 
				
			||||||
							
								
								
									
										528
									
								
								drivers/scsi/mpi3mr/mpi3mr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										528
									
								
								drivers/scsi/mpi3mr/mpi3mr.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,528 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Driver for Broadcom MPI3 Storage Controllers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2021 Broadcom Inc.
 | 
				
			||||||
 | 
					 *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MPI3MR_H_INCLUDED
 | 
				
			||||||
 | 
					#define MPI3MR_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/blkdev.h>
 | 
				
			||||||
 | 
					#include <linux/blk-mq.h>
 | 
				
			||||||
 | 
					#include <linux/blk-mq-pci.h>
 | 
				
			||||||
 | 
					#include <linux/delay.h>
 | 
				
			||||||
 | 
					#include <linux/dmapool.h>
 | 
				
			||||||
 | 
					#include <linux/errno.h>
 | 
				
			||||||
 | 
					#include <linux/init.h>
 | 
				
			||||||
 | 
					#include <linux/io.h>
 | 
				
			||||||
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/miscdevice.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/pci.h>
 | 
				
			||||||
 | 
					#include <linux/poll.h>
 | 
				
			||||||
 | 
					#include <linux/sched.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
 | 
					#include <linux/utsname.h>
 | 
				
			||||||
 | 
					#include <linux/version.h>
 | 
				
			||||||
 | 
					#include <linux/workqueue.h>
 | 
				
			||||||
 | 
					#include <asm/unaligned.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi_cmnd.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi_dbg.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi_device.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi_host.h>
 | 
				
			||||||
 | 
					#include <scsi/scsi_tcq.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "mpi/mpi30_transport.h"
 | 
				
			||||||
 | 
					#include "mpi/mpi30_image.h"
 | 
				
			||||||
 | 
					#include "mpi/mpi30_init.h"
 | 
				
			||||||
 | 
					#include "mpi/mpi30_ioc.h"
 | 
				
			||||||
 | 
					#include "mpi3mr_debug.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Global list and lock for storing multiple adapters managed by the driver */
 | 
				
			||||||
 | 
					extern spinlock_t mrioc_list_lock;
 | 
				
			||||||
 | 
					extern struct list_head mrioc_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_VERSION	"00.255.45.01"
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_RELDATE	"12-December-2020"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_NAME	"mpi3mr"
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_LICENSE	"GPL"
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_AUTHOR	"Broadcom Inc. <mpi3mr-linuxdrv.pdl@broadcom.com>"
 | 
				
			||||||
 | 
					#define MPI3MR_DRIVER_DESC	"MPI3 Storage Controller Device Driver"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_NAME_LENGTH	32
 | 
				
			||||||
 | 
					#define IOCNAME			"%s: "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Definitions for internal SGL and Chain SGL buffers */
 | 
				
			||||||
 | 
					#define MPI3MR_PAGE_SIZE_4K		4096
 | 
				
			||||||
 | 
					#define MPI3MR_SG_DEPTH		(MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Definitions for MAX values for shost */
 | 
				
			||||||
 | 
					#define MPI3MR_MAX_CMDS_LUN	7
 | 
				
			||||||
 | 
					#define MPI3MR_MAX_CDB_LENGTH	32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Admin queue management definitions */
 | 
				
			||||||
 | 
					#define MPI3MR_ADMIN_REQ_Q_SIZE		(2 * MPI3MR_PAGE_SIZE_4K)
 | 
				
			||||||
 | 
					#define MPI3MR_ADMIN_REPLY_Q_SIZE	(4 * MPI3MR_PAGE_SIZE_4K)
 | 
				
			||||||
 | 
					#define MPI3MR_ADMIN_REQ_FRAME_SZ	128
 | 
				
			||||||
 | 
					#define MPI3MR_ADMIN_REPLY_FRAME_SZ	16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Reserved Host Tag definitions */
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_INVALID		0xFFFF
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_INITCMDS		1
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_IOCTLCMDS	2
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_BLK_TMS		5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_NUM_DEVRMCMD		1
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_DEVRMCMD_MIN	(MPI3MR_HOSTTAG_BLK_TMS + 1)
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTTAG_DEVRMCMD_MAX	(MPI3MR_HOSTTAG_DEVRMCMD_MIN + \
 | 
				
			||||||
 | 
											MPI3MR_NUM_DEVRMCMD - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_INTERNAL_CMDS_RESVD     MPI3MR_HOSTTAG_DEVRMCMD_MAX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Reduced resource count definition for crash kernel */
 | 
				
			||||||
 | 
					#define MPI3MR_HOST_IOS_KDUMP		128
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* command/controller interaction timeout definitions in seconds */
 | 
				
			||||||
 | 
					#define MPI3MR_INTADMCMD_TIMEOUT		10
 | 
				
			||||||
 | 
					#define MPI3MR_RESETTM_TIMEOUT			30
 | 
				
			||||||
 | 
					#define MPI3MR_DEFAULT_SHUTDOWN_TIME		120
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3MR_WATCHDOG_INTERVAL		1000 /* in milli seconds */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Internal admin command state definitions*/
 | 
				
			||||||
 | 
					#define MPI3MR_CMD_NOTUSED	0x8000
 | 
				
			||||||
 | 
					#define MPI3MR_CMD_COMPLETE	0x0001
 | 
				
			||||||
 | 
					#define MPI3MR_CMD_PENDING	0x0002
 | 
				
			||||||
 | 
					#define MPI3MR_CMD_REPLY_VALID	0x0004
 | 
				
			||||||
 | 
					#define MPI3MR_CMD_RESET	0x0008
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Definitions for Event replies and sense buffer allocated per controller */
 | 
				
			||||||
 | 
					#define MPI3MR_NUM_EVT_REPLIES	64
 | 
				
			||||||
 | 
					#define MPI3MR_SENSEBUF_SZ	256
 | 
				
			||||||
 | 
					#define MPI3MR_SENSEBUF_FACTOR	3
 | 
				
			||||||
 | 
					#define MPI3MR_CHAINBUF_FACTOR	3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Invalid target device handle */
 | 
				
			||||||
 | 
					#define MPI3MR_INVALID_DEV_HANDLE	0xFFFF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Controller Reset related definitions */
 | 
				
			||||||
 | 
					#define MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT	5
 | 
				
			||||||
 | 
					#define MPI3MR_MAX_RESET_RETRY_COUNT		3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ResponseCode definitions */
 | 
				
			||||||
 | 
					#define MPI3MR_RI_MASK_RESPCODE		(0x000000FF)
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_COMPLETE		0x00
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_INVALID_FRAME	0x02
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_NOT_SUPPORTED	0x04
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_FAILED		0x05
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_SUCCEEDED		0x08
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_INVALID_LUN	0x09
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_TM_OVERLAPPED_TAG	0x0A
 | 
				
			||||||
 | 
					#define MPI3MR_RSP_IO_QUEUED_ON_IOC \
 | 
				
			||||||
 | 
								MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* SGE Flag definition */
 | 
				
			||||||
 | 
					#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
 | 
				
			||||||
 | 
						(MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
 | 
				
			||||||
 | 
						MPI3_SGE_FLAGS_END_OF_LIST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* IOC State definitions */
 | 
				
			||||||
 | 
					enum mpi3mr_iocstate {
 | 
				
			||||||
 | 
						MRIOC_STATE_READY = 1,
 | 
				
			||||||
 | 
						MRIOC_STATE_RESET,
 | 
				
			||||||
 | 
						MRIOC_STATE_FAULT,
 | 
				
			||||||
 | 
						MRIOC_STATE_BECOMING_READY,
 | 
				
			||||||
 | 
						MRIOC_STATE_RESET_REQUESTED,
 | 
				
			||||||
 | 
						MRIOC_STATE_UNRECOVERABLE,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Reset reason code definitions*/
 | 
				
			||||||
 | 
					enum mpi3mr_reset_reason {
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_BRINGUP = 1,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_FAULT_WATCH = 2,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_IOCTL = 3,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_EH_HOS = 4,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_TM_TIMEOUT = 5,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_IOCTL_TIMEOUT = 6,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_MUR_FAILURE = 7,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_CTLR_CLEANUP = 8,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_CIACTIV_FAULT = 9,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_PE_TIMEOUT = 10,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_TSU_TIMEOUT = 11,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_DELREQQ_TIMEOUT = 12,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_DELREPQ_TIMEOUT = 13,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT = 14,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT = 15,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT = 16,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_IOCINIT_TIMEOUT = 17,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT = 18,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_EVTACK_TIMEOUT = 19,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_CIACTVRST_TIMER = 20,
 | 
				
			||||||
 | 
						MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT = 21,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mpi3mr_compimg_ver - replica of component image
 | 
				
			||||||
 | 
					 * version defined in mpi30_image.h in host endianness
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mpi3mr_compimg_ver {
 | 
				
			||||||
 | 
						u16 build_num;
 | 
				
			||||||
 | 
						u16 cust_id;
 | 
				
			||||||
 | 
						u8 ph_minor;
 | 
				
			||||||
 | 
						u8 ph_major;
 | 
				
			||||||
 | 
						u8 gen_minor;
 | 
				
			||||||
 | 
						u8 gen_major;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mpi3mr_ioc_facs - replica of component image version
 | 
				
			||||||
 | 
					 * defined in mpi30_ioc.h in host endianness
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mpi3mr_ioc_facts {
 | 
				
			||||||
 | 
						u32 ioc_capabilities;
 | 
				
			||||||
 | 
						struct mpi3mr_compimg_ver fw_ver;
 | 
				
			||||||
 | 
						u32 mpi_version;
 | 
				
			||||||
 | 
						u16 max_reqs;
 | 
				
			||||||
 | 
						u16 product_id;
 | 
				
			||||||
 | 
						u16 op_req_sz;
 | 
				
			||||||
 | 
						u16 reply_sz;
 | 
				
			||||||
 | 
						u16 exceptions;
 | 
				
			||||||
 | 
						u16 max_perids;
 | 
				
			||||||
 | 
						u16 max_pds;
 | 
				
			||||||
 | 
						u16 max_sasexpanders;
 | 
				
			||||||
 | 
						u16 max_sasinitiators;
 | 
				
			||||||
 | 
						u16 max_enclosures;
 | 
				
			||||||
 | 
						u16 max_pcie_switches;
 | 
				
			||||||
 | 
						u16 max_nvme;
 | 
				
			||||||
 | 
						u16 max_vds;
 | 
				
			||||||
 | 
						u16 max_hpds;
 | 
				
			||||||
 | 
						u16 max_advhpds;
 | 
				
			||||||
 | 
						u16 max_raidpds;
 | 
				
			||||||
 | 
						u16 min_devhandle;
 | 
				
			||||||
 | 
						u16 max_devhandle;
 | 
				
			||||||
 | 
						u16 max_op_req_q;
 | 
				
			||||||
 | 
						u16 max_op_reply_q;
 | 
				
			||||||
 | 
						u16 shutdown_timeout;
 | 
				
			||||||
 | 
						u8 ioc_num;
 | 
				
			||||||
 | 
						u8 who_init;
 | 
				
			||||||
 | 
						u16 max_msix_vectors;
 | 
				
			||||||
 | 
						u8 personality;
 | 
				
			||||||
 | 
						u8 dma_mask;
 | 
				
			||||||
 | 
						u8 protocol_flags;
 | 
				
			||||||
 | 
						u8 sge_mod_mask;
 | 
				
			||||||
 | 
						u8 sge_mod_value;
 | 
				
			||||||
 | 
						u8 sge_mod_shift;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct op_req_qinfo -  Operational Request Queue Information
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @ci: consumer index
 | 
				
			||||||
 | 
					 * @pi: producer index
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct op_req_qinfo {
 | 
				
			||||||
 | 
						u16 ci;
 | 
				
			||||||
 | 
						u16 pi;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct op_reply_qinfo -  Operational Reply Queue Information
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @ci: consumer index
 | 
				
			||||||
 | 
					 * @qid: Queue Id starting from 1
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct op_reply_qinfo {
 | 
				
			||||||
 | 
						u16 ci;
 | 
				
			||||||
 | 
						u16 qid;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mpi3mr_intr_info -  Interrupt cookie information
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @mrioc: Adapter instance reference
 | 
				
			||||||
 | 
					 * @msix_index: MSIx index
 | 
				
			||||||
 | 
					 * @op_reply_q: Associated operational reply queue
 | 
				
			||||||
 | 
					 * @name: Dev name for the irq claiming device
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mpi3mr_intr_info {
 | 
				
			||||||
 | 
						struct mpi3mr_ioc *mrioc;
 | 
				
			||||||
 | 
						u16 msix_index;
 | 
				
			||||||
 | 
						struct op_reply_qinfo *op_reply_q;
 | 
				
			||||||
 | 
						char name[MPI3MR_NAME_LENGTH];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mpi3mr_drv_cmd - Internal command tracker
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @mutex: Command mutex
 | 
				
			||||||
 | 
					 * @done: Completeor for wakeup
 | 
				
			||||||
 | 
					 * @reply: Firmware reply for internal commands
 | 
				
			||||||
 | 
					 * @sensebuf: Sensebuf for SCSI IO commands
 | 
				
			||||||
 | 
					 * @state: Command State
 | 
				
			||||||
 | 
					 * @dev_handle: Firmware handle for device specific commands
 | 
				
			||||||
 | 
					 * @ioc_status: IOC status from the firmware
 | 
				
			||||||
 | 
					 * @ioc_loginfo:IOC log info from the firmware
 | 
				
			||||||
 | 
					 * @is_waiting: Is the command issued in block mode
 | 
				
			||||||
 | 
					 * @retry_count: Retry count for retriable commands
 | 
				
			||||||
 | 
					 * @host_tag: Host tag used by the command
 | 
				
			||||||
 | 
					 * @callback: Callback for non blocking commands
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mpi3mr_drv_cmd {
 | 
				
			||||||
 | 
						struct mutex mutex;
 | 
				
			||||||
 | 
						struct completion done;
 | 
				
			||||||
 | 
						void *reply;
 | 
				
			||||||
 | 
						u8 *sensebuf;
 | 
				
			||||||
 | 
						u16 state;
 | 
				
			||||||
 | 
						u16 dev_handle;
 | 
				
			||||||
 | 
						u16 ioc_status;
 | 
				
			||||||
 | 
						u32 ioc_loginfo;
 | 
				
			||||||
 | 
						u8 is_waiting;
 | 
				
			||||||
 | 
						u8 retry_count;
 | 
				
			||||||
 | 
						u16 host_tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void (*callback)(struct mpi3mr_ioc *mrioc,
 | 
				
			||||||
 | 
						    struct mpi3mr_drv_cmd *drv_cmd);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct chain_element - memory descriptor structure to store
 | 
				
			||||||
 | 
					 * virtual and dma addresses for chain elements.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @addr: virtual address
 | 
				
			||||||
 | 
					 * @dma_addr: dma address
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct chain_element {
 | 
				
			||||||
 | 
						void *addr;
 | 
				
			||||||
 | 
						dma_addr_t dma_addr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct scmd_priv - SCSI command private data
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @host_tag: Host tag specific to operational queue
 | 
				
			||||||
 | 
					 * @in_lld_scope: Command in LLD scope or not
 | 
				
			||||||
 | 
					 * @scmd: SCSI Command pointer
 | 
				
			||||||
 | 
					 * @req_q_idx: Operational request queue index
 | 
				
			||||||
 | 
					 * @chain_idx: Chain frame index
 | 
				
			||||||
 | 
					 * @mpi3mr_scsiio_req: MPI SCSI IO request
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct scmd_priv {
 | 
				
			||||||
 | 
						u16 host_tag;
 | 
				
			||||||
 | 
						u8 in_lld_scope;
 | 
				
			||||||
 | 
						struct scsi_cmnd *scmd;
 | 
				
			||||||
 | 
						u16 req_q_idx;
 | 
				
			||||||
 | 
						int chain_idx;
 | 
				
			||||||
 | 
						u8 mpi3mr_scsiio_req[MPI3MR_ADMIN_REQ_FRAME_SZ];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mpi3mr_ioc - Adapter anchor structure stored in shost
 | 
				
			||||||
 | 
					 * private data
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @list: List pointer
 | 
				
			||||||
 | 
					 * @pdev: PCI device pointer
 | 
				
			||||||
 | 
					 * @shost: Scsi_Host pointer
 | 
				
			||||||
 | 
					 * @id: Controller ID
 | 
				
			||||||
 | 
					 * @cpu_count: Number of online CPUs
 | 
				
			||||||
 | 
					 * @name: Controller ASCII name
 | 
				
			||||||
 | 
					 * @driver_name: Driver ASCII name
 | 
				
			||||||
 | 
					 * @sysif_regs: System interface registers virtual address
 | 
				
			||||||
 | 
					 * @sysif_regs_phys: System interface registers physical address
 | 
				
			||||||
 | 
					 * @bars: PCI BARS
 | 
				
			||||||
 | 
					 * @dma_mask: DMA mask
 | 
				
			||||||
 | 
					 * @msix_count: Number of MSIX vectors used
 | 
				
			||||||
 | 
					 * @intr_enabled: Is interrupts enabled
 | 
				
			||||||
 | 
					 * @num_admin_req: Number of admin requests
 | 
				
			||||||
 | 
					 * @admin_req_q_sz: Admin request queue size
 | 
				
			||||||
 | 
					 * @admin_req_pi: Admin request queue producer index
 | 
				
			||||||
 | 
					 * @admin_req_ci: Admin request queue consumer index
 | 
				
			||||||
 | 
					 * @admin_req_base: Admin request queue base virtual address
 | 
				
			||||||
 | 
					 * @admin_req_dma: Admin request queue base dma address
 | 
				
			||||||
 | 
					 * @admin_req_lock: Admin queue access lock
 | 
				
			||||||
 | 
					 * @num_admin_replies: Number of admin replies
 | 
				
			||||||
 | 
					 * @admin_reply_q_sz: Admin reply queue size
 | 
				
			||||||
 | 
					 * @admin_reply_ci: Admin reply queue consumer index
 | 
				
			||||||
 | 
					 * @admin_reply_ephase:Admin reply queue expected phase
 | 
				
			||||||
 | 
					 * @admin_reply_base: Admin reply queue base virtual address
 | 
				
			||||||
 | 
					 * @admin_reply_dma: Admin reply queue base dma address
 | 
				
			||||||
 | 
					 * @ready_timeout: Controller ready timeout
 | 
				
			||||||
 | 
					 * @intr_info: Interrupt cookie pointer
 | 
				
			||||||
 | 
					 * @intr_info_count: Number of interrupt cookies
 | 
				
			||||||
 | 
					 * @num_queues: Number of operational queues
 | 
				
			||||||
 | 
					 * @num_op_req_q: Number of operational request queues
 | 
				
			||||||
 | 
					 * @req_qinfo: Operational request queue info pointer
 | 
				
			||||||
 | 
					 * @num_op_reply_q: Number of operational reply queues
 | 
				
			||||||
 | 
					 * @op_reply_qinfo: Operational reply queue info pointer
 | 
				
			||||||
 | 
					 * @init_cmds: Command tracker for initialization commands
 | 
				
			||||||
 | 
					 * @facts: Cached IOC facts data
 | 
				
			||||||
 | 
					 * @op_reply_desc_sz: Operational reply descriptor size
 | 
				
			||||||
 | 
					 * @num_reply_bufs: Number of reply buffers allocated
 | 
				
			||||||
 | 
					 * @reply_buf_pool: Reply buffer pool
 | 
				
			||||||
 | 
					 * @reply_buf: Reply buffer base virtual address
 | 
				
			||||||
 | 
					 * @reply_buf_dma: Reply buffer DMA address
 | 
				
			||||||
 | 
					 * @reply_buf_dma_max_address: Reply DMA address max limit
 | 
				
			||||||
 | 
					 * @reply_free_qsz: Reply free queue size
 | 
				
			||||||
 | 
					 * @reply_free_q_pool: Reply free queue pool
 | 
				
			||||||
 | 
					 * @reply_free_q: Reply free queue base virtual address
 | 
				
			||||||
 | 
					 * @reply_free_q_dma: Reply free queue base DMA address
 | 
				
			||||||
 | 
					 * @reply_free_queue_lock: Reply free queue lock
 | 
				
			||||||
 | 
					 * @reply_free_queue_host_index: Reply free queue host index
 | 
				
			||||||
 | 
					 * @num_sense_bufs: Number of sense buffers
 | 
				
			||||||
 | 
					 * @sense_buf_pool: Sense buffer pool
 | 
				
			||||||
 | 
					 * @sense_buf: Sense buffer base virtual address
 | 
				
			||||||
 | 
					 * @sense_buf_dma: Sense buffer base DMA address
 | 
				
			||||||
 | 
					 * @sense_buf_q_sz: Sense buffer queue size
 | 
				
			||||||
 | 
					 * @sense_buf_q_pool: Sense buffer queue pool
 | 
				
			||||||
 | 
					 * @sense_buf_q: Sense buffer queue virtual address
 | 
				
			||||||
 | 
					 * @sense_buf_q_dma: Sense buffer queue DMA address
 | 
				
			||||||
 | 
					 * @sbq_lock: Sense buffer queue lock
 | 
				
			||||||
 | 
					 * @sbq_host_index: Sense buffer queuehost index
 | 
				
			||||||
 | 
					 * @is_driver_loading: Is driver still loading
 | 
				
			||||||
 | 
					 * @max_host_ios: Maximum host I/O count
 | 
				
			||||||
 | 
					 * @chain_buf_count: Chain buffer count
 | 
				
			||||||
 | 
					 * @chain_buf_pool: Chain buffer pool
 | 
				
			||||||
 | 
					 * @chain_sgl_list: Chain SGL list
 | 
				
			||||||
 | 
					 * @chain_bitmap_sz: Chain buffer allocator bitmap size
 | 
				
			||||||
 | 
					 * @chain_bitmap: Chain buffer allocator bitmap
 | 
				
			||||||
 | 
					 * @reset_in_progress: Reset in progress flag
 | 
				
			||||||
 | 
					 * @unrecoverable: Controller unrecoverable flag
 | 
				
			||||||
 | 
					 * @logging_level: Controller debug logging level
 | 
				
			||||||
 | 
					 * @current_event: Firmware event currently in process
 | 
				
			||||||
 | 
					 * @driver_info: Driver, Kernel, OS information to firmware
 | 
				
			||||||
 | 
					 * @change_count: Topology change count
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mpi3mr_ioc {
 | 
				
			||||||
 | 
						struct list_head list;
 | 
				
			||||||
 | 
						struct pci_dev *pdev;
 | 
				
			||||||
 | 
						struct Scsi_Host *shost;
 | 
				
			||||||
 | 
						u8 id;
 | 
				
			||||||
 | 
						int cpu_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char name[MPI3MR_NAME_LENGTH];
 | 
				
			||||||
 | 
						char driver_name[MPI3MR_NAME_LENGTH];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						volatile struct mpi3_sysif_registers __iomem *sysif_regs;
 | 
				
			||||||
 | 
						resource_size_t sysif_regs_phys;
 | 
				
			||||||
 | 
						int bars;
 | 
				
			||||||
 | 
						u64 dma_mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 msix_count;
 | 
				
			||||||
 | 
						u8 intr_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 num_admin_req;
 | 
				
			||||||
 | 
						u32 admin_req_q_sz;
 | 
				
			||||||
 | 
						u16 admin_req_pi;
 | 
				
			||||||
 | 
						u16 admin_req_ci;
 | 
				
			||||||
 | 
						void *admin_req_base;
 | 
				
			||||||
 | 
						dma_addr_t admin_req_dma;
 | 
				
			||||||
 | 
						spinlock_t admin_req_lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 num_admin_replies;
 | 
				
			||||||
 | 
						u32 admin_reply_q_sz;
 | 
				
			||||||
 | 
						u16 admin_reply_ci;
 | 
				
			||||||
 | 
						u8 admin_reply_ephase;
 | 
				
			||||||
 | 
						void *admin_reply_base;
 | 
				
			||||||
 | 
						dma_addr_t admin_reply_dma;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u32 ready_timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct mpi3mr_intr_info *intr_info;
 | 
				
			||||||
 | 
						u16 intr_info_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 num_queues;
 | 
				
			||||||
 | 
						u16 num_op_req_q;
 | 
				
			||||||
 | 
						struct op_req_qinfo *req_qinfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 num_op_reply_q;
 | 
				
			||||||
 | 
						struct op_reply_qinfo *op_reply_qinfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct mpi3mr_drv_cmd init_cmds;
 | 
				
			||||||
 | 
						struct mpi3mr_ioc_facts facts;
 | 
				
			||||||
 | 
						u16 op_reply_desc_sz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u32 num_reply_bufs;
 | 
				
			||||||
 | 
						struct dma_pool *reply_buf_pool;
 | 
				
			||||||
 | 
						u8 *reply_buf;
 | 
				
			||||||
 | 
						dma_addr_t reply_buf_dma;
 | 
				
			||||||
 | 
						dma_addr_t reply_buf_dma_max_address;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 reply_free_qsz;
 | 
				
			||||||
 | 
						struct dma_pool *reply_free_q_pool;
 | 
				
			||||||
 | 
						__le64 *reply_free_q;
 | 
				
			||||||
 | 
						dma_addr_t reply_free_q_dma;
 | 
				
			||||||
 | 
						spinlock_t reply_free_queue_lock;
 | 
				
			||||||
 | 
						u32 reply_free_queue_host_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u32 num_sense_bufs;
 | 
				
			||||||
 | 
						struct dma_pool *sense_buf_pool;
 | 
				
			||||||
 | 
						u8 *sense_buf;
 | 
				
			||||||
 | 
						dma_addr_t sense_buf_dma;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 sense_buf_q_sz;
 | 
				
			||||||
 | 
						struct dma_pool *sense_buf_q_pool;
 | 
				
			||||||
 | 
						__le64 *sense_buf_q;
 | 
				
			||||||
 | 
						dma_addr_t sense_buf_q_dma;
 | 
				
			||||||
 | 
						spinlock_t sbq_lock;
 | 
				
			||||||
 | 
						u32 sbq_host_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u8 is_driver_loading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u16 max_host_ios;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u32 chain_buf_count;
 | 
				
			||||||
 | 
						struct dma_pool *chain_buf_pool;
 | 
				
			||||||
 | 
						struct chain_element *chain_sgl_list;
 | 
				
			||||||
 | 
						u16  chain_bitmap_sz;
 | 
				
			||||||
 | 
						void *chain_bitmap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u8 reset_in_progress;
 | 
				
			||||||
 | 
						u8 unrecoverable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int logging_level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct mpi3mr_fwevt *current_event;
 | 
				
			||||||
 | 
						struct mpi3_driver_info_layout driver_info;
 | 
				
			||||||
 | 
						u16 change_count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
 | 
				
			||||||
 | 
					u16 admin_req_sz, u8 ignore_reset);
 | 
				
			||||||
 | 
					void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
 | 
				
			||||||
 | 
								  dma_addr_t dma_addr);
 | 
				
			||||||
 | 
					void mpi3mr_build_zero_len_sge(void *paddr);
 | 
				
			||||||
 | 
					void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
 | 
				
			||||||
 | 
									     dma_addr_t phys_addr);
 | 
				
			||||||
 | 
					void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
 | 
				
			||||||
 | 
									     dma_addr_t phys_addr);
 | 
				
			||||||
 | 
					void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
 | 
				
			||||||
 | 
									     u64 sense_buf_dma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
 | 
				
			||||||
 | 
								      u32 reset_reason, u8 snapdump);
 | 
				
			||||||
 | 
					void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /*MPI3MR_H_INCLUDED*/
 | 
				
			||||||
							
								
								
									
										60
									
								
								drivers/scsi/mpi3mr/mpi3mr_debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								drivers/scsi/mpi3mr/mpi3mr_debug.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Driver for Broadcom MPI3 Storage Controllers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2021 Broadcom Inc.
 | 
				
			||||||
 | 
					 *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MPI3SAS_DEBUG_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MPI3SAS_DEBUG_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * debug levels
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define MPI3_DEBUG			0x00000001
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_MSG_FRAME		0x00000002
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_SG			0x00000004
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_EVENTS		0x00000008
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_EVENT_WORK_TASK	0x00000010
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_INIT			0x00000020
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_EXIT			0x00000040
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_FAIL			0x00000080
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_TM			0x00000100
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_REPLY		0x00000200
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_HANDSHAKE		0x00000400
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_CONFIG		0x00000800
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_DL			0x00001000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_RESET		0x00002000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_SCSI			0x00004000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_IOCTL		0x00008000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_CSMISAS		0x00010000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_SAS			0x00020000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_TRANSPORT		0x00040000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_TASK_SET_FULL	0x00080000
 | 
				
			||||||
 | 
					#define MPI3_DEBUG_TRIGGER_DIAG		0x00200000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * debug macros
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ioc_err(ioc, fmt, ...) \
 | 
				
			||||||
 | 
						pr_err("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define ioc_notice(ioc, fmt, ...) \
 | 
				
			||||||
 | 
						pr_notice("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define ioc_warn(ioc, fmt, ...) \
 | 
				
			||||||
 | 
						pr_warn("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define ioc_info(ioc, fmt, ...) \
 | 
				
			||||||
 | 
						pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define dbgprint(IOC, FMT, ...) \
 | 
				
			||||||
 | 
						do { \
 | 
				
			||||||
 | 
							if (IOC->logging_level & MPI3_DEBUG) \
 | 
				
			||||||
 | 
								pr_info("%s: " FMT, (IOC)->name, ##__VA_ARGS__); \
 | 
				
			||||||
 | 
						} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* MPT3SAS_DEBUG_H_INCLUDED */
 | 
				
			||||||
							
								
								
									
										1805
									
								
								drivers/scsi/mpi3mr/mpi3mr_fw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1805
									
								
								drivers/scsi/mpi3mr/mpi3mr_fw.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										364
									
								
								drivers/scsi/mpi3mr/mpi3mr_os.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								drivers/scsi/mpi3mr/mpi3mr_os.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,364 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Driver for Broadcom MPI3 Storage Controllers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2021 Broadcom Inc.
 | 
				
			||||||
 | 
					 *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "mpi3mr.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* global driver scop variables */
 | 
				
			||||||
 | 
					LIST_HEAD(mrioc_list);
 | 
				
			||||||
 | 
					DEFINE_SPINLOCK(mrioc_list_lock);
 | 
				
			||||||
 | 
					static int mrioc_ids;
 | 
				
			||||||
 | 
					static int warn_non_secure_ctlr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR);
 | 
				
			||||||
 | 
					MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC);
 | 
				
			||||||
 | 
					MODULE_LICENSE(MPI3MR_DRIVER_LICENSE);
 | 
				
			||||||
 | 
					MODULE_VERSION(MPI3MR_DRIVER_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Module parameters*/
 | 
				
			||||||
 | 
					int logging_level;
 | 
				
			||||||
 | 
					module_param(logging_level, int, 0);
 | 
				
			||||||
 | 
					MODULE_PARM_DESC(logging_level,
 | 
				
			||||||
 | 
						" bits for enabling additional logging info (default=0)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_map_queues - Map queues callback handler
 | 
				
			||||||
 | 
					 * @shost: SCSI host reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Call the blk_mq_pci_map_queues with from which operational
 | 
				
			||||||
 | 
					 * queue the mapping has to be done
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: return of blk_mq_pci_map_queues
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mpi3mr_map_queues(struct Scsi_Host *shost)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mpi3mr_ioc *mrioc = shost_priv(shost);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
 | 
				
			||||||
 | 
						    mrioc->pdev, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_slave_destroy - Slave destroy callback handler
 | 
				
			||||||
 | 
					 * @sdev: SCSI device reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Cleanup and free per device(lun) private data.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Nothing.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void mpi3mr_slave_destroy(struct scsi_device *sdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_target_destroy - Target destroy callback handler
 | 
				
			||||||
 | 
					 * @starget: SCSI target reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Cleanup and free per target private data.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Nothing.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void mpi3mr_target_destroy(struct scsi_target *starget)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_slave_configure - Slave configure callback handler
 | 
				
			||||||
 | 
					 * @sdev: SCSI device reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Configure queue depth, max hardware sectors and virt boundary
 | 
				
			||||||
 | 
					 * as required
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 always.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mpi3mr_slave_configure(struct scsi_device *sdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int retval = 0;
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_slave_alloc -Slave alloc callback handler
 | 
				
			||||||
 | 
					 * @sdev: SCSI device reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Allocate per device(lun) private data and initialize it.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 on success -ENOMEM on memory allocation failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mpi3mr_slave_alloc(struct scsi_device *sdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int retval = 0;
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_target_alloc - Target alloc callback handler
 | 
				
			||||||
 | 
					 * @starget: SCSI target reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Allocate per target private data and initialize it.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 on success -ENOMEM on memory allocation failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mpi3mr_target_alloc(struct scsi_target *starget)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int retval = -ENODEV;
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_qcmd - I/O request despatcher
 | 
				
			||||||
 | 
					 * @shost: SCSI Host reference
 | 
				
			||||||
 | 
					 * @scmd: SCSI Command reference
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Issues the SCSI Command as an MPI3 request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 on successful queueing of the request or if the
 | 
				
			||||||
 | 
					 *         request is completed with failure.
 | 
				
			||||||
 | 
					 *         SCSI_MLQUEUE_DEVICE_BUSY when the device is busy.
 | 
				
			||||||
 | 
					 *         SCSI_MLQUEUE_HOST_BUSY when the host queue is full.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mpi3mr_qcmd(struct Scsi_Host *shost,
 | 
				
			||||||
 | 
						struct scsi_cmnd *scmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int retval = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scmd->result = DID_NO_CONNECT << 16;
 | 
				
			||||||
 | 
						scmd->scsi_done(scmd);
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct scsi_host_template mpi3mr_driver_template = {
 | 
				
			||||||
 | 
						.module				= THIS_MODULE,
 | 
				
			||||||
 | 
						.name				= "MPI3 Storage Controller",
 | 
				
			||||||
 | 
						.proc_name			= MPI3MR_DRIVER_NAME,
 | 
				
			||||||
 | 
						.queuecommand			= mpi3mr_qcmd,
 | 
				
			||||||
 | 
						.target_alloc			= mpi3mr_target_alloc,
 | 
				
			||||||
 | 
						.slave_alloc			= mpi3mr_slave_alloc,
 | 
				
			||||||
 | 
						.slave_configure		= mpi3mr_slave_configure,
 | 
				
			||||||
 | 
						.target_destroy			= mpi3mr_target_destroy,
 | 
				
			||||||
 | 
						.slave_destroy			= mpi3mr_slave_destroy,
 | 
				
			||||||
 | 
						.map_queues			= mpi3mr_map_queues,
 | 
				
			||||||
 | 
						.no_write_same			= 1,
 | 
				
			||||||
 | 
						.can_queue			= 1,
 | 
				
			||||||
 | 
						.this_id			= -1,
 | 
				
			||||||
 | 
						.sg_tablesize			= MPI3MR_SG_DEPTH,
 | 
				
			||||||
 | 
						/* max xfer supported is 1M (2K in 512 byte sized sectors)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						.max_sectors			= 2048,
 | 
				
			||||||
 | 
						.cmd_per_lun			= MPI3MR_MAX_CMDS_LUN,
 | 
				
			||||||
 | 
						.track_queue_depth		= 1,
 | 
				
			||||||
 | 
						.cmd_size			= sizeof(struct scmd_priv),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_init_drv_cmd - Initialize internal command tracker
 | 
				
			||||||
 | 
					 * @cmdptr: Internal command tracker
 | 
				
			||||||
 | 
					 * @host_tag: Host tag used for the specific command
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Initialize the internal command tracker structure with
 | 
				
			||||||
 | 
					 * specified host tag.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Nothing.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr,
 | 
				
			||||||
 | 
						u16 host_tag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_init(&cmdptr->mutex);
 | 
				
			||||||
 | 
						cmdptr->reply = NULL;
 | 
				
			||||||
 | 
						cmdptr->state = MPI3MR_CMD_NOTUSED;
 | 
				
			||||||
 | 
						cmdptr->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
 | 
				
			||||||
 | 
						cmdptr->host_tag = host_tag;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_probe - PCI probe callback
 | 
				
			||||||
 | 
					 * @pdev: PCI device instance
 | 
				
			||||||
 | 
					 * @id: PCI device ID details
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * controller initialization routine. Checks the security status
 | 
				
			||||||
 | 
					 * of the controller and if it is invalid or tampered return the
 | 
				
			||||||
 | 
					 * probe without initializing the controller. Otherwise,
 | 
				
			||||||
 | 
					 * allocate per adapter instance through shost_priv and
 | 
				
			||||||
 | 
					 * initialize controller specific data structures, initializae
 | 
				
			||||||
 | 
					 * the controller hardware, add shost to the SCSI subsystem.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 on success, non-zero on failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mpi3mr_ioc *mrioc = NULL;
 | 
				
			||||||
 | 
						struct Scsi_Host *shost = NULL;
 | 
				
			||||||
 | 
						int retval = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shost = scsi_host_alloc(&mpi3mr_driver_template,
 | 
				
			||||||
 | 
						    sizeof(struct mpi3mr_ioc));
 | 
				
			||||||
 | 
						if (!shost) {
 | 
				
			||||||
 | 
							retval = -ENODEV;
 | 
				
			||||||
 | 
							goto shost_failed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mrioc = shost_priv(shost);
 | 
				
			||||||
 | 
						mrioc->id = mrioc_ids++;
 | 
				
			||||||
 | 
						sprintf(mrioc->driver_name, "%s", MPI3MR_DRIVER_NAME);
 | 
				
			||||||
 | 
						sprintf(mrioc->name, "%s%d", mrioc->driver_name, mrioc->id);
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&mrioc->list);
 | 
				
			||||||
 | 
						spin_lock(&mrioc_list_lock);
 | 
				
			||||||
 | 
						list_add_tail(&mrioc->list, &mrioc_list);
 | 
				
			||||||
 | 
						spin_unlock(&mrioc_list_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_init(&mrioc->admin_req_lock);
 | 
				
			||||||
 | 
						spin_lock_init(&mrioc->reply_free_queue_lock);
 | 
				
			||||||
 | 
						spin_lock_init(&mrioc->sbq_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mrioc->logging_level = logging_level;
 | 
				
			||||||
 | 
						mrioc->shost = shost;
 | 
				
			||||||
 | 
						mrioc->pdev = pdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* init shost parameters */
 | 
				
			||||||
 | 
						shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH;
 | 
				
			||||||
 | 
						shost->max_lun = -1;
 | 
				
			||||||
 | 
						shost->unique_id = mrioc->id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shost->max_channel = 1;
 | 
				
			||||||
 | 
						shost->max_id = 0xFFFFFFFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mrioc->is_driver_loading = 1;
 | 
				
			||||||
 | 
						if (mpi3mr_init_ioc(mrioc)) {
 | 
				
			||||||
 | 
							ioc_err(mrioc, "failure at %s:%d/%s()!\n",
 | 
				
			||||||
 | 
							    __FILE__, __LINE__, __func__);
 | 
				
			||||||
 | 
							retval = -ENODEV;
 | 
				
			||||||
 | 
							goto out_iocinit_failed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shost->nr_hw_queues = mrioc->num_op_reply_q;
 | 
				
			||||||
 | 
						shost->can_queue = mrioc->max_host_ios;
 | 
				
			||||||
 | 
						shost->sg_tablesize = MPI3MR_SG_DEPTH;
 | 
				
			||||||
 | 
						shost->max_id = mrioc->facts.max_perids;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						retval = scsi_add_host(shost, &pdev->dev);
 | 
				
			||||||
 | 
						if (retval) {
 | 
				
			||||||
 | 
							ioc_err(mrioc, "failure at %s:%d/%s()!\n",
 | 
				
			||||||
 | 
							    __FILE__, __LINE__, __func__);
 | 
				
			||||||
 | 
							goto addhost_failed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scsi_scan_host(shost);
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addhost_failed:
 | 
				
			||||||
 | 
						mpi3mr_cleanup_ioc(mrioc);
 | 
				
			||||||
 | 
					out_iocinit_failed:
 | 
				
			||||||
 | 
						spin_lock(&mrioc_list_lock);
 | 
				
			||||||
 | 
						list_del(&mrioc->list);
 | 
				
			||||||
 | 
						spin_unlock(&mrioc_list_lock);
 | 
				
			||||||
 | 
						scsi_host_put(shost);
 | 
				
			||||||
 | 
					shost_failed:
 | 
				
			||||||
 | 
						return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_remove - PCI remove callback
 | 
				
			||||||
 | 
					 * @pdev: PCI device instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Free up all memory and resources associated with the
 | 
				
			||||||
 | 
					 * controllerand target devices, unregister the shost.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Nothing.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void mpi3mr_remove(struct pci_dev *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct Scsi_Host *shost = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						struct mpi3mr_ioc *mrioc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mrioc = shost_priv(shost);
 | 
				
			||||||
 | 
						while (mrioc->reset_in_progress || mrioc->is_driver_loading)
 | 
				
			||||||
 | 
							ssleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scsi_remove_host(shost);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mpi3mr_cleanup_ioc(mrioc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&mrioc_list_lock);
 | 
				
			||||||
 | 
						list_del(&mrioc->list);
 | 
				
			||||||
 | 
						spin_unlock(&mrioc_list_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scsi_host_put(shost);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mpi3mr_shutdown - PCI shutdown callback
 | 
				
			||||||
 | 
					 * @pdev: PCI device instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Free up all memory and resources associated with the
 | 
				
			||||||
 | 
					 * controller
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Nothing.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void mpi3mr_shutdown(struct pci_dev *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct Scsi_Host *shost = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						struct mpi3mr_ioc *mrioc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!shost)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mrioc = shost_priv(shost);
 | 
				
			||||||
 | 
						while (mrioc->reset_in_progress || mrioc->is_driver_loading)
 | 
				
			||||||
 | 
							ssleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mpi3mr_cleanup_ioc(mrioc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pci_device_id mpi3mr_pci_id_table[] = {
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							PCI_DEVICE_SUB(PCI_VENDOR_ID_LSI_LOGIC, 0x00A5,
 | 
				
			||||||
 | 
							    PCI_ANY_ID, PCI_ANY_ID)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ 0 }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct pci_driver mpi3mr_pci_driver = {
 | 
				
			||||||
 | 
						.name = MPI3MR_DRIVER_NAME,
 | 
				
			||||||
 | 
						.id_table = mpi3mr_pci_id_table,
 | 
				
			||||||
 | 
						.probe = mpi3mr_probe,
 | 
				
			||||||
 | 
						.remove = mpi3mr_remove,
 | 
				
			||||||
 | 
						.shutdown = mpi3mr_shutdown,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __init mpi3mr_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret_val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME,
 | 
				
			||||||
 | 
						    MPI3MR_DRIVER_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret_val = pci_register_driver(&mpi3mr_pci_driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret_val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void __exit mpi3mr_exit(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (warn_non_secure_ctlr)
 | 
				
			||||||
 | 
							pr_warn(
 | 
				
			||||||
 | 
							    "Unloading %s version %s while managing a non secure controller\n",
 | 
				
			||||||
 | 
							    MPI3MR_DRIVER_NAME, MPI3MR_DRIVER_VERSION);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME,
 | 
				
			||||||
 | 
							    MPI3MR_DRIVER_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pci_unregister_driver(&mpi3mr_pci_driver);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module_init(mpi3mr_init);
 | 
				
			||||||
 | 
					module_exit(mpi3mr_exit);
 | 
				
			||||||
		Loading…
	
		Reference in a new issue