summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/megaraid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/megaraid')
-rw-r--r--drivers/scsi/megaraid/Kconfig.megaraid78
-rw-r--r--drivers/scsi/megaraid/Makefile2
-rw-r--r--drivers/scsi/megaraid/mbox_defs.h790
-rw-r--r--drivers/scsi/megaraid/mega_common.h286
-rw-r--r--drivers/scsi/megaraid/megaraid_ioctl.h296
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c4276
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.h288
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.c1255
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.h102
9 files changed, 7373 insertions, 0 deletions
diff --git a/drivers/scsi/megaraid/Kconfig.megaraid b/drivers/scsi/megaraid/Kconfig.megaraid
new file mode 100644
index 00000000000..917d591d90b
--- /dev/null
+++ b/drivers/scsi/megaraid/Kconfig.megaraid
@@ -0,0 +1,78 @@
+config MEGARAID_NEWGEN
+ bool "LSI Logic New Generation RAID Device Drivers"
+ depends on PCI && SCSI
+ help
+ LSI Logic RAID Device Drivers
+
+config MEGARAID_MM
+ tristate "LSI Logic Management Module (New Driver)"
+ depends on PCI && SCSI && MEGARAID_NEWGEN
+ help
+ Management Module provides ioctl, sysfs support for LSI Logic
+ RAID controllers.
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mm
+
+
+config MEGARAID_MAILBOX
+ tristate "LSI Logic MegaRAID Driver (New Driver)"
+ depends on PCI && SCSI && MEGARAID_MM
+ help
+ List of supported controllers
+
+ OEM Product Name VID :DID :SVID:SSID
+ --- ------------ ---- ---- ---- ----
+ Dell PERC3/QC 101E:1960:1028:0471
+ Dell PERC3/DC 101E:1960:1028:0493
+ Dell PERC3/SC 101E:1960:1028:0475
+ Dell PERC3/Di 1028:000E:1028:0123
+ Dell PERC4/SC 1000:1960:1028:0520
+ Dell PERC4/DC 1000:1960:1028:0518
+ Dell PERC4/QC 1000:0407:1028:0531
+ Dell PERC4/Di 1028:000F:1028:014A
+ Dell PERC 4e/Si 1028:0013:1028:016c
+ Dell PERC 4e/Di 1028:0013:1028:016d
+ Dell PERC 4e/Di 1028:0013:1028:016e
+ Dell PERC 4e/Di 1028:0013:1028:016f
+ Dell PERC 4e/Di 1028:0013:1028:0170
+ Dell PERC 4e/DC 1000:0408:1028:0002
+ Dell PERC 4e/SC 1000:0408:1028:0001
+ LSI MegaRAID SCSI 320-0 1000:1960:1000:A520
+ LSI MegaRAID SCSI 320-1 1000:1960:1000:0520
+ LSI MegaRAID SCSI 320-2 1000:1960:1000:0518
+ LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530
+ LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532
+ LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531
+ LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001
+ LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002
+ LSI MegaRAID SATA 150-4 1000:1960:1000:4523
+ LSI MegaRAID SATA 150-6 1000:1960:1000:0523
+ LSI MegaRAID SATA 300-4X 1000:0409:1000:3004
+ LSI MegaRAID SATA 300-8X 1000:0409:1000:3008
+ INTEL RAID Controller SRCU42X 1000:0407:8086:0532
+ INTEL RAID Controller SRCS16 1000:1960:8086:0523
+ INTEL RAID Controller SRCU42E 1000:0408:8086:0002
+ INTEL RAID Controller SRCZCRX 1000:0407:8086:0530
+ INTEL RAID Controller SRCS28X 1000:0409:8086:3008
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3431
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3499
+ INTEL RAID Controller SRCU51L 1000:1960:8086:0520
+ FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065
+ ACER MegaRAID ROMB-2E 1000:0408:1025:004D
+ NEC MegaRAID PCI Express ROMB 1000:0408:1033:8287
+
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mbox
+
+if MEGARAID_NEWGEN=n
+config MEGARAID_LEGACY
+ tristate "LSI Logic Legacy MegaRAID Driver"
+ depends on PCI && SCSI
+ help
+ This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490
+ and 467 SCSI host adapters. This driver also support the all U320
+ RAID controllers
+
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid
+endif
diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile
new file mode 100644
index 00000000000..6dd99f27572
--- /dev/null
+++ b/drivers/scsi/megaraid/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o
+obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o
diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h
new file mode 100644
index 00000000000..3052869f51f
--- /dev/null
+++ b/drivers/scsi/megaraid/mbox_defs.h
@@ -0,0 +1,790 @@
+/*
+ *
+ * Linux MegaRAID Unified device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : mbox_defs.h
+ *
+ */
+#ifndef _MRAID_MBOX_DEFS_H_
+#define _MRAID_MBOX_DEFS_H_
+
+#include <linux/types.h>
+
+/*
+ * Commands and states for mailbox based controllers
+ */
+
+#define MBOXCMD_LREAD 0x01
+#define MBOXCMD_LWRITE 0x02
+#define MBOXCMD_PASSTHRU 0x03
+#define MBOXCMD_ADPEXTINQ 0x04
+#define MBOXCMD_ADAPTERINQ 0x05
+#define MBOXCMD_LREAD64 0xA7
+#define MBOXCMD_LWRITE64 0xA8
+#define MBOXCMD_PASSTHRU64 0xC3
+#define MBOXCMD_EXTPTHRU 0xE3
+
+#define MAIN_MISC_OPCODE 0xA4
+#define GET_MAX_SG_SUPPORT 0x01
+#define SUPPORT_EXT_CDB 0x16
+
+#define FC_NEW_CONFIG 0xA1
+#define NC_SUBOP_PRODUCT_INFO 0x0E
+#define NC_SUBOP_ENQUIRY3 0x0F
+#define ENQ3_GET_SOLICITED_FULL 0x02
+#define OP_DCMD_READ_CONFIG 0x04
+#define NEW_READ_CONFIG_8LD 0x67
+#define READ_CONFIG_8LD 0x07
+#define FLUSH_ADAPTER 0x0A
+#define FLUSH_SYSTEM 0xFE
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV 0xA4
+#define OP_SUP_DEL_LOGDRV 0x2A
+#define OP_GET_LDID_MAP 0x18
+#define OP_DEL_LOGDRV 0x1C
+
+/*
+ * BIOS commands
+ */
+#define IS_BIOS_ENABLED 0x62
+#define GET_BIOS 0x01
+#define CHNL_CLASS 0xA9
+#define GET_CHNL_CLASS 0x00
+#define SET_CHNL_CLASS 0x01
+#define CH_RAID 0x01
+#define CH_SCSI 0x00
+#define BIOS_PVT_DATA 0x40
+#define GET_BIOS_PVT_DATA 0x00
+
+
+/*
+ * Commands to support clustering
+ */
+#define GET_TARGET_ID 0x7D
+#define CLUSTER_OP 0x70
+#define GET_CLUSTER_MODE 0x02
+#define CLUSTER_CMD 0x6E
+#define RESERVE_LD 0x01
+#define RELEASE_LD 0x02
+#define RESET_RESERVATIONS 0x03
+#define RESERVATION_STATUS 0x04
+#define RESERVE_PD 0x05
+#define RELEASE_PD 0x06
+
+
+/*
+ * Module battery status
+ */
+#define BATTERY_MODULE_MISSING 0x01
+#define BATTERY_LOW_VOLTAGE 0x02
+#define BATTERY_TEMP_HIGH 0x04
+#define BATTERY_PACK_MISSING 0x08
+#define BATTERY_CHARGE_MASK 0x30
+#define BATTERY_CHARGE_DONE 0x00
+#define BATTERY_CHARGE_INPROG 0x10
+#define BATTERY_CHARGE_FAIL 0x20
+#define BATTERY_CYCLES_EXCEEDED 0x40
+
+/*
+ * Physical drive states.
+ */
+#define PDRV_UNCNF 0
+#define PDRV_ONLINE 3
+#define PDRV_FAILED 4
+#define PDRV_RBLD 5
+#define PDRV_HOTSPARE 6
+
+
+/*
+ * Raid logical drive states.
+ */
+#define RDRV_OFFLINE 0
+#define RDRV_DEGRADED 1
+#define RDRV_OPTIMAL 2
+#define RDRV_DELETED 3
+
+/*
+ * Read, write and cache policies
+ */
+#define NO_READ_AHEAD 0
+#define READ_AHEAD 1
+#define ADAP_READ_AHEAD 2
+#define WRMODE_WRITE_THRU 0
+#define WRMODE_WRITE_BACK 1
+#define CACHED_IO 0
+#define DIRECT_IO 1
+
+#define MAX_LOGICAL_DRIVES_8LD 8
+#define MAX_LOGICAL_DRIVES_40LD 40
+#define FC_MAX_PHYSICAL_DEVICES 256
+#define MAX_MBOX_CHANNELS 5
+#define MAX_MBOX_TARGET 15
+#define MBOX_MAX_PHYSICAL_DRIVES MAX_MBOX_CHANNELS*MAX_MBOX_TARGET
+#define MAX_ROW_SIZE_40LD 32
+#define MAX_ROW_SIZE_8LD 8
+#define SPAN_DEPTH_8_SPANS 8
+#define SPAN_DEPTH_4_SPANS 4
+#define MAX_REQ_SENSE_LEN 0x20
+
+
+
+/**
+ * struct mbox_t - Driver and f/w handshake structure.
+ * @cmd : firmware command
+ * @cmdid : command id
+ * @numsectors : number of sectors to be transferred
+ * @lba : Logical Block Address on LD
+ * @xferaddr : DMA address for data transfer
+ * @logdrv : logical drive number
+ * @numsge : number of scatter gather elements in sg list
+ * @resvd : reserved
+ * @busy : f/w busy, must wait to issue more commands.
+ * @numstatus : number of commands completed.
+ * @status : status of the commands completed
+ * @completed : array of completed command ids.
+ * @poll : poll and ack sequence
+ * @ack : poll and ack sequence
+ *
+ * The central handshake structure between the driver and the firmware. This
+ * structure must be allocated by the driver and aligned at 8-byte boundary.
+ */
+#define MBOX_MAX_FIRMWARE_STATUS 46
+typedef struct {
+ uint8_t cmd;
+ uint8_t cmdid;
+ uint16_t numsectors;
+ uint32_t lba;
+ uint32_t xferaddr;
+ uint8_t logdrv;
+ uint8_t numsge;
+ uint8_t resvd;
+ uint8_t busy;
+ uint8_t numstatus;
+ uint8_t status;
+ uint8_t completed[MBOX_MAX_FIRMWARE_STATUS];
+ uint8_t poll;
+ uint8_t ack;
+} __attribute__ ((packed)) mbox_t;
+
+
+/**
+ * mbox64_t - 64-bit extension for the mailbox
+ * @segment_lo : the low 32-bits of the address of the scatter-gather list
+ * @segment_hi : the upper 32-bits of the address of the scatter-gather list
+ * @mbox : 32-bit mailbox, whose xferadder field must be set to
+ * 0xFFFFFFFF
+ *
+ * This is the extension of the 32-bit mailbox to be able to perform DMA
+ * beyond 4GB address range.
+ */
+typedef struct {
+ uint32_t xferaddr_lo;
+ uint32_t xferaddr_hi;
+ mbox_t mbox32;
+} __attribute__ ((packed)) mbox64_t;
+
+/*
+ * mailbox structure used for internal commands
+ */
+typedef struct {
+ u8 cmd;
+ u8 cmdid;
+ u8 opcode;
+ u8 subopcode;
+ u32 lba;
+ u32 xferaddr;
+ u8 logdrv;
+ u8 rsvd[3];
+ u8 numstatus;
+ u8 status;
+} __attribute__ ((packed)) int_mbox_t;
+
+/**
+ * mraid_passthru_t - passthru structure to issue commands to physical devices
+ * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr
+ * @ars : set if ARS required after check condition
+ * @islogical : set if command meant for logical devices
+ * @logdrv : logical drive number if command for LD
+ * @channel : Channel on which physical device is located
+ * @target : SCSI target of the device
+ * @queuetag : unused
+ * @queueaction : unused
+ * @cdb : SCSI CDB
+ * @cdblen : length of the CDB
+ * @reqsenselen : amount of request sense data to be returned
+ * @reqsensearea : Sense information buffer
+ * @numsge : number of scatter-gather elements in the sg list
+ * @scsistatus : SCSI status of the command completed.
+ * @dataxferaddr : DMA data transfer address
+ * @dataxferlen : amount of the data to be transferred.
+ */
+typedef struct {
+ uint8_t timeout :3;
+ uint8_t ars :1;
+ uint8_t reserved :3;
+ uint8_t islogical :1;
+ uint8_t logdrv;
+ uint8_t channel;
+ uint8_t target;
+ uint8_t queuetag;
+ uint8_t queueaction;
+ uint8_t cdb[10];
+ uint8_t cdblen;
+ uint8_t reqsenselen;
+ uint8_t reqsensearea[MAX_REQ_SENSE_LEN];
+ uint8_t numsge;
+ uint8_t scsistatus;
+ uint32_t dataxferaddr;
+ uint32_t dataxferlen;
+} __attribute__ ((packed)) mraid_passthru_t;
+
+typedef struct {
+
+ uint32_t dataxferaddr_lo;
+ uint32_t dataxferaddr_hi;
+ mraid_passthru_t pthru32;
+
+} __attribute__ ((packed)) mega_passthru64_t;
+
+/**
+ * mraid_epassthru_t - passthru structure to issue commands to physical devices
+ * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr
+ * @ars : set if ARS required after check condition
+ * @rsvd1 : reserved field
+ * @cd_rom : (?)
+ * @rsvd2 : reserved field
+ * @islogical : set if command meant for logical devices
+ * @logdrv : logical drive number if command for LD
+ * @channel : Channel on which physical device is located
+ * @target : SCSI target of the device
+ * @queuetag : unused
+ * @queueaction : unused
+ * @cdblen : length of the CDB
+ * @rsvd3 : reserved field
+ * @cdb : SCSI CDB
+ * @numsge : number of scatter-gather elements in the sg list
+ * @status : SCSI status of the command completed.
+ * @reqsenselen : amount of request sense data to be returned
+ * @reqsensearea : Sense information buffer
+ * @rsvd4 : reserved field
+ * @dataxferaddr : DMA data transfer address
+ * @dataxferlen : amount of the data to be transferred.
+ */
+typedef struct {
+ uint8_t timeout :3;
+ uint8_t ars :1;
+ uint8_t rsvd1 :1;
+ uint8_t cd_rom :1;
+ uint8_t rsvd2 :1;
+ uint8_t islogical :1;
+ uint8_t logdrv;
+ uint8_t channel;
+ uint8_t target;
+ uint8_t queuetag;
+ uint8_t queueaction;
+ uint8_t cdblen;
+ uint8_t rsvd3;
+ uint8_t cdb[16];
+ uint8_t numsge;
+ uint8_t status;
+ uint8_t reqsenselen;
+ uint8_t reqsensearea[MAX_REQ_SENSE_LEN];
+ uint8_t rsvd4;
+ uint32_t dataxferaddr;
+ uint32_t dataxferlen;
+} __attribute__ ((packed)) mraid_epassthru_t;
+
+
+/**
+ * mraid_pinfo_t - product info, static information about the controller
+ * @data_size : current size in bytes (not including resvd)
+ * @config_signature : Current value is 0x00282008
+ * @fw_version : Firmware version
+ * @bios_version : version of the BIOS
+ * @product_name : Name given to the controller
+ * @max_commands : Maximum concurrent commands supported
+ * @nchannels : Number of SCSI Channels detected
+ * @fc_loop_present : Number of Fibre Loops detected
+ * @mem_type : EDO, FPM, SDRAM etc
+ * @signature :
+ * @dram_size : In terms of MB
+ * @subsysid : device PCI subsystem ID
+ * @subsysvid : device PCI subsystem vendor ID
+ * @notify_counters :
+ * @pad1k : 135 + 889 resvd = 1024 total size
+ *
+ * This structures holds the information about the controller which is not
+ * expected to change dynamically.
+ *
+ * The current value of config signature is 0x00282008:
+ * 0x28 = MAX_LOGICAL_DRIVES,
+ * 0x20 = Number of stripes and
+ * 0x08 = Number of spans
+ */
+typedef struct {
+ uint32_t data_size;
+ uint32_t config_signature;
+ uint8_t fw_version[16];
+ uint8_t bios_version[16];
+ uint8_t product_name[80];
+ uint8_t max_commands;
+ uint8_t nchannels;
+ uint8_t fc_loop_present;
+ uint8_t mem_type;
+ uint32_t signature;
+ uint16_t dram_size;
+ uint16_t subsysid;
+ uint16_t subsysvid;
+ uint8_t notify_counters;
+ uint8_t pad1k[889];
+} __attribute__ ((packed)) mraid_pinfo_t;
+
+
+/**
+ * mraid_notify_t - the notification structure
+ * @global_counter : Any change increments this counter
+ * @param_counter : Indicates any params changed
+ * @param_id : Param modified - defined below
+ * @param_val : New val of last param modified
+ * @write_config_counter : write config occurred
+ * @write_config_rsvd :
+ * @ldrv_op_counter : Indicates ldrv op started/completed
+ * @ldrv_opid : ldrv num
+ * @ldrv_opcmd : ldrv operation - defined below
+ * @ldrv_opstatus : status of the operation
+ * @ldrv_state_counter : Indicates change of ldrv state
+ * @ldrv_state_id : ldrv num
+ * @ldrv_state_new : New state
+ * @ldrv_state_old : old state
+ * @pdrv_state_counter : Indicates change of ldrv state
+ * @pdrv_state_id : pdrv id
+ * @pdrv_state_new : New state
+ * @pdrv_state_old : old state
+ * @pdrv_fmt_counter : Indicates pdrv format started/over
+ * @pdrv_fmt_id : pdrv id
+ * @pdrv_fmt_val : format started/over
+ * @pdrv_fmt_rsvd :
+ * @targ_xfer_counter : Indicates SCSI-2 Xfer rate change
+ * @targ_xfer_id : pdrv Id
+ * @targ_xfer_val : new Xfer params of last pdrv
+ * @targ_xfer_rsvd :
+ * @fcloop_id_chg_counter : Indicates loopid changed
+ * @fcloopid_pdrvid : pdrv id
+ * @fcloop_id0 : loopid on fc loop 0
+ * @fcloop_id1 : loopid on fc loop 1
+ * @fcloop_state_counter : Indicates loop state changed
+ * @fcloop_state0 : state of fc loop 0
+ * @fcloop_state1 : state of fc loop 1
+ * @fcloop_state_rsvd :
+ */
+typedef struct {
+ uint32_t global_counter;
+ uint8_t param_counter;
+ uint8_t param_id;
+ uint16_t param_val;
+ uint8_t write_config_counter;
+ uint8_t write_config_rsvd[3];
+ uint8_t ldrv_op_counter;
+ uint8_t ldrv_opid;
+ uint8_t ldrv_opcmd;
+ uint8_t ldrv_opstatus;
+ uint8_t ldrv_state_counter;
+ uint8_t ldrv_state_id;
+ uint8_t ldrv_state_new;
+ uint8_t ldrv_state_old;
+ uint8_t pdrv_state_counter;
+ uint8_t pdrv_state_id;
+ uint8_t pdrv_state_new;
+ uint8_t pdrv_state_old;
+ uint8_t pdrv_fmt_counter;
+ uint8_t pdrv_fmt_id;
+ uint8_t pdrv_fmt_val;
+ uint8_t pdrv_fmt_rsvd;
+ uint8_t targ_xfer_counter;
+ uint8_t targ_xfer_id;
+ uint8_t targ_xfer_val;
+ uint8_t targ_xfer_rsvd;
+ uint8_t fcloop_id_chg_counter;
+ uint8_t fcloopid_pdrvid;
+ uint8_t fcloop_id0;
+ uint8_t fcloop_id1;
+ uint8_t fcloop_state_counter;
+ uint8_t fcloop_state0;
+ uint8_t fcloop_state1;
+ uint8_t fcloop_state_rsvd;
+} __attribute__ ((packed)) mraid_notify_t;
+
+
+/**
+ * mraid_inquiry3_t - enquiry for device information
+ *
+ * @data_size : current size in bytes (not including resvd)
+ * @notify :
+ * @notify_rsvd :
+ * @rebuild_rate : rebuild rate (0% - 100%)
+ * @cache_flush_int : cache flush interval in seconds
+ * @sense_alert :
+ * @drive_insert_count : drive insertion count
+ * @battery_status :
+ * @num_ldrv : no. of Log Drives configured
+ * @recon_state : state of reconstruct
+ * @ldrv_op_status : logdrv Status
+ * @ldrv_size : size of each log drv
+ * @ldrv_prop :
+ * @ldrv_state : state of log drives
+ * @pdrv_state : state of phys drvs.
+ * @pdrv_format :
+ * @targ_xfer : phys device transfer rate
+ * @pad1k : 761 + 263reserved = 1024 bytes total size
+ */
+#define MAX_NOTIFY_SIZE 0x80
+#define CUR_NOTIFY_SIZE sizeof(mraid_notify_t)
+
+typedef struct {
+ uint32_t data_size;
+
+ mraid_notify_t notify;
+
+ uint8_t notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE];
+
+ uint8_t rebuild_rate;
+ uint8_t cache_flush_int;
+ uint8_t sense_alert;
+ uint8_t drive_insert_count;
+
+ uint8_t battery_status;
+ uint8_t num_ldrv;
+ uint8_t recon_state[MAX_LOGICAL_DRIVES_40LD / 8];
+ uint16_t ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8];
+
+ uint32_t ldrv_size[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t ldrv_prop[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t ldrv_state[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t pdrv_state[FC_MAX_PHYSICAL_DEVICES];
+ uint16_t pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16];
+
+ uint8_t targ_xfer[80];
+ uint8_t pad1k[263];
+} __attribute__ ((packed)) mraid_inquiry3_t;
+
+
+/**
+ * mraid_adapinfo_t - information about the adapter
+ * @max_commands : max concurrent commands supported
+ * @rebuild_rate : rebuild rate - 0% thru 100%
+ * @max_targ_per_chan : max targ per channel
+ * @nchannels : number of channels on HBA
+ * @fw_version : firmware version
+ * @age_of_flash : number of times FW has been flashed
+ * @chip_set_value : contents of 0xC0000832
+ * @dram_size : in MB
+ * @cache_flush_interval : in seconds
+ * @bios_version :
+ * @board_type :
+ * @sense_alert :
+ * @write_config_count : increase with every configuration change
+ * @drive_inserted_count : increase with every drive inserted
+ * @inserted_drive : channel:Id of inserted drive
+ * @battery_status : bit 0: battery module missing
+ * bit 1: VBAD
+ * bit 2: temprature high
+ * bit 3: battery pack missing
+ * bit 4,5:
+ * 00 - charge complete
+ * 01 - fast charge in progress
+ * 10 - fast charge fail
+ * 11 - undefined
+ * bit 6: counter > 1000
+ * bit 7: Undefined
+ * @dec_fault_bus_info :
+ */
+typedef struct {
+ uint8_t max_commands;
+ uint8_t rebuild_rate;
+ uint8_t max_targ_per_chan;
+ uint8_t nchannels;
+ uint8_t fw_version[4];
+ uint16_t age_of_flash;
+ uint8_t chip_set_value;
+ uint8_t dram_size;
+ uint8_t cache_flush_interval;
+ uint8_t bios_version[4];
+ uint8_t board_type;
+ uint8_t sense_alert;
+ uint8_t write_config_count;
+ uint8_t battery_status;
+ uint8_t dec_fault_bus_info;
+} __attribute__ ((packed)) mraid_adapinfo_t;
+
+
+/**
+ * mraid_ldrv_info_t - information about the logical drives
+ * @nldrv : Number of logical drives configured
+ * @rsvd :
+ * @size : size of each logical drive
+ * @prop :
+ * @state : state of each logical drive
+ */
+typedef struct {
+ uint8_t nldrv;
+ uint8_t rsvd[3];
+ uint32_t size[MAX_LOGICAL_DRIVES_8LD];
+ uint8_t prop[MAX_LOGICAL_DRIVES_8LD];
+ uint8_t state[MAX_LOGICAL_DRIVES_8LD];
+} __attribute__ ((packed)) mraid_ldrv_info_t;
+
+
+/**
+ * mraid_pdrv_info_t - information about the physical drives
+ * @pdrv_state : state of each physical drive
+ */
+typedef struct {
+ uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES];
+ uint8_t rsvd;
+} __attribute__ ((packed)) mraid_pdrv_info_t;
+
+
+/**
+ * mraid_inquiry_t - RAID inquiry, mailbox command 0x05
+ * @mraid_adapinfo_t : adapter information
+ * @mraid_ldrv_info_t : logical drives information
+ * @mraid_pdrv_info_t : physical drives information
+ */
+typedef struct {
+ mraid_adapinfo_t adapter_info;
+ mraid_ldrv_info_t logdrv_info;
+ mraid_pdrv_info_t pdrv_info;
+} __attribute__ ((packed)) mraid_inquiry_t;
+
+
+/**
+ * mraid_extinq_t - RAID extended inquiry, mailbox command 0x04
+ *
+ * @raid_inq : raid inquiry
+ * @phys_drv_format :
+ * @stack_attn :
+ * @modem_status :
+ * @rsvd :
+ */
+typedef struct {
+ mraid_inquiry_t raid_inq;
+ uint16_t phys_drv_format[MAX_MBOX_CHANNELS];
+ uint8_t stack_attn;
+ uint8_t modem_status;
+ uint8_t rsvd[2];
+} __attribute__ ((packed)) mraid_extinq_t;
+
+
+/**
+ * adap_device_t - device information
+ * @channel : channel fpor the device
+ * @target : target ID of the device
+ */
+typedef struct {
+ uint8_t channel;
+ uint8_t target;
+}__attribute__ ((packed)) adap_device_t;
+
+
+/**
+ * adap_span_40ld_t - 40LD span
+ * @start_blk : starting block
+ * @num_blks : number of blocks
+ */
+typedef struct {
+ uint32_t start_blk;
+ uint32_t num_blks;
+ adap_device_t device[MAX_ROW_SIZE_40LD];
+}__attribute__ ((packed)) adap_span_40ld_t;
+
+
+/**
+ * adap_span_8ld_t - 8LD span
+ * @start_blk : starting block
+ * @num_blks : number of blocks
+ */
+typedef struct {
+ uint32_t start_blk;
+ uint32_t num_blks;
+ adap_device_t device[MAX_ROW_SIZE_8LD];
+}__attribute__ ((packed)) adap_span_8ld_t;
+
+
+/**
+ * logdrv_param_t - logical drives parameters
+ *
+ * @span_depth : total number of spans
+ * @level : RAID level
+ * @read_ahead : read ahead, no read ahead, adaptive read ahead
+ * @stripe_sz : encoded stripe size
+ * @status : status of the logical drive
+ * @write_mode : write mode, write_through/write_back
+ * @direct_io : direct io or through cache
+ * @row_size : number of stripes in a row
+ */
+typedef struct {
+ uint8_t span_depth;
+ uint8_t level;
+ uint8_t read_ahead;
+ uint8_t stripe_sz;
+ uint8_t status;
+ uint8_t write_mode;
+ uint8_t direct_io;
+ uint8_t row_size;
+} __attribute__ ((packed)) logdrv_param_t;
+
+
+/**
+ * logdrv_40ld_t - logical drive definition for 40LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_40ld_t span[SPAN_DEPTH_8_SPANS];
+}__attribute__ ((packed)) logdrv_40ld_t;
+
+
+/**
+ * logdrv_8ld_span8_t - logical drive definition for 8LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ *
+ * 8-LD logical drive with upto 8 spans
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_8ld_t span[SPAN_DEPTH_8_SPANS];
+}__attribute__ ((packed)) logdrv_8ld_span8_t;
+
+
+/**
+ * logdrv_8ld_span4_t - logical drive definition for 8LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ *
+ * 8-LD logical drive with upto 4 spans
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_8ld_t span[SPAN_DEPTH_4_SPANS];
+}__attribute__ ((packed)) logdrv_8ld_span4_t;
+
+
+/**
+ * phys_drive_t - physical device information
+ * @type : Type of the device
+ * @cur_status : current status of the device
+ * @tag_depth : Level of tagging
+ * @sync_neg : sync negotiation - ENABLE or DISBALE
+ * @size : configurable size in terms of 512 byte
+ */
+typedef struct {
+ uint8_t type;
+ uint8_t cur_status;
+ uint8_t tag_depth;
+ uint8_t sync_neg;
+ uint32_t size;
+}__attribute__ ((packed)) phys_drive_t;
+
+
+/**
+ * disk_array_40ld_t - disk array for 40LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_40ld_t ldrv[MAX_LOGICAL_DRIVES_40LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_40ld_t;
+
+
+/**
+ * disk_array_8ld_span8_t - disk array for 8LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ *
+ * Disk array for 8LD logical drives with upto 8 spans
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_8ld_span8_t ldrv[MAX_LOGICAL_DRIVES_8LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_8ld_span8_t;
+
+
+/**
+ * disk_array_8ld_span4_t - disk array for 8LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ *
+ * Disk array for 8LD logical drives with upto 4 spans
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_8ld_span4_t ldrv[MAX_LOGICAL_DRIVES_8LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_8ld_span4_t;
+
+
+/**
+ * private_bios_data - bios private data for boot devices
+ * @geometry : bits 0-3 - BIOS geometry, 0x0001 - 1GB, 0x0010 - 2GB,
+ * 0x1000 - 8GB, Others values are invalid
+ * @unused : bits 4-7 are unused
+ * @boot_drv : logical drive set as boot drive, 0..7 - for 8LD cards,
+ * 0..39 - for 40LD cards
+ * @cksum : 0-(sum of first 13 bytes of this structure)
+ */
+struct private_bios_data {
+ uint8_t geometry :4;
+ uint8_t unused :4;
+ uint8_t boot_drv;
+ uint8_t rsvd[12];
+ uint16_t cksum;
+} __attribute__ ((packed));
+
+
+/**
+ * mbox_sgl64 - 64-bit scatter list for mailbox based controllers
+ * @address : address of the buffer
+ * @length : data transfer length
+ */
+typedef struct {
+ uint64_t address;
+ uint32_t length;
+} __attribute__ ((packed)) mbox_sgl64;
+
+/**
+ * mbox_sgl32 - 32-bit scatter list for mailbox based controllers
+ * @address : address of the buffer
+ * @length : data transfer length
+ */
+typedef struct {
+ uint32_t address;
+ uint32_t length;
+} __attribute__ ((packed)) mbox_sgl32;
+
+#endif // _MRAID_MBOX_DEFS_H_
+
+/* vim: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h
new file mode 100644
index 00000000000..18969a4946b
--- /dev/null
+++ b/drivers/scsi/megaraid/mega_common.h
@@ -0,0 +1,286 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : mega_common.h
+ *
+ * Libaray of common routine used by all low-level megaraid drivers
+ */
+
+#ifndef _MEGA_COMMON_H_
+#define _MEGA_COMMON_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/list.h>
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <asm/semaphore.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+
+#define LSI_MAX_CHANNELS 16
+#define LSI_MAX_LOGICAL_DRIVES_64LD (64+1)
+
+
+/**
+ * scb_t - scsi command control block
+ * @param ccb : command control block for individual driver
+ * @param list : list of control blocks
+ * @param gp : general purpose field for LLDs
+ * @param sno : all SCBs have a serial number
+ * @param scp : associated scsi command
+ * @param state : current state of scb
+ * @param dma_dir : direction of data transfer
+ * @param dma_type : transfer with sg list, buffer, or no data transfer
+ * @param dev_channel : actual channel on the device
+ * @param dev_target : actual target on the device
+ * @param status : completion status
+ *
+ * This is our central data structure to issue commands the each driver.
+ * Driver specific data structures are maintained in the ccb field.
+ * scb provides a field 'gp', which can be used by LLD for its own purposes
+ *
+ * dev_channel and dev_target must be initialized with the actual channel and
+ * target on the controller.
+ */
+typedef struct {
+ caddr_t ccb;
+ struct list_head list;
+ unsigned long gp;
+ unsigned int sno;
+ struct scsi_cmnd *scp;
+ uint32_t state;
+ uint32_t dma_direction;
+ uint32_t dma_type;
+ uint16_t dev_channel;
+ uint16_t dev_target;
+ uint32_t status;
+} scb_t;
+
+/*
+ * SCB states as it transitions from one state to another
+ */
+#define SCB_FREE 0x0000 /* on the free list */
+#define SCB_ACTIVE 0x0001 /* off the free list */
+#define SCB_PENDQ 0x0002 /* on the pending queue */
+#define SCB_ISSUED 0x0004 /* issued - owner f/w */
+#define SCB_ABORT 0x0008 /* Got an abort for this one */
+#define SCB_RESET 0x0010 /* Got a reset for this one */
+
+/*
+ * DMA types for scb
+ */
+#define MRAID_DMA_NONE 0x0000 /* no data transfer for this command */
+#define MRAID_DMA_WSG 0x0001 /* data transfer using a sg list */
+#define MRAID_DMA_WBUF 0x0002 /* data transfer using a contiguous buffer */
+
+
+/**
+ * struct adapter_t - driver's initialization structure
+ * @param dpc_h : tasklet handle
+ * @param pdev : pci configuration pointer for kernel
+ * @param host : pointer to host structure of mid-layer
+ * @param host_lock : pointer to appropriate lock
+ * @param lock : synchronization lock for mid-layer and driver
+ * @param quiescent : driver is quiescent for now.
+ * @param outstanding_cmds : number of commands pending in the driver
+ * @param kscb_list : pointer to the bulk of SCBs pointers for IO
+ * @param kscb_pool : pool of free scbs for IO
+ * @param kscb_pool_lock : lock for pool of free scbs
+ * @param pend_list : pending commands list
+ * @param pend_list_lock : exlusion lock for pending commands list
+ * @param completed_list : list of completed commands
+ * @param completed_list_lock : exclusion lock for list of completed commands
+ * @param sglen : max sg elements supported
+ * @param device_ids : to convert kernel device addr to our devices.
+ * @param raid_device : raid adapter specific pointer
+ * @param max_channel : maximum channel number supported - inclusive
+ * @param max_target : max target supported - inclusive
+ * @param max_lun : max lun supported - inclusive
+ * @param unique_id : unique identifier for each adapter
+ * @param irq : IRQ for this adapter
+ * @param ito : internal timeout value, (-1) means no timeout
+ * @param ibuf : buffer to issue internal commands
+ * @param ibuf_dma_h : dma handle for the above buffer
+ * @param uscb_list : SCB pointers for user cmds, common mgmt module
+ * @param uscb_pool : pool of SCBs for user commands
+ * @param uscb_pool_lock : exclusion lock for these SCBs
+ * @param max_cmds : max outstanding commands
+ * @param fw_version : firmware version
+ * @param bios_version : bios version
+ * @param max_cdb_sz : biggest CDB size supported.
+ * @param ha : is high availability present - clustering
+ * @param init_id : initiator ID, the default value should be 7
+ * @param max_sectors : max sectors per request
+ * @param cmd_per_lun : max outstanding commands per LUN
+ * @param being_detached : set when unloading, no more mgmt calls
+ *
+ *
+ * mraid_setup_device_map() can be called anytime after the device map is
+ * available and MRAID_GET_DEVICE_MAP() can be called whenever the mapping is
+ * required, usually from LLD's queue entry point. The formar API sets up the
+ * MRAID_IS_LOGICAL(adapter_t *, struct scsi_cmnd *) to find out if the
+ * device in question is a logical drive.
+ *
+ * quiescent flag should be set by the driver if it is not accepting more
+ * commands
+ *
+ * NOTE: The fields of this structures are placed to minimize cache misses
+ */
+
+// amount of space required to store the bios and firmware version strings
+#define VERSION_SIZE 16
+
+typedef struct {
+ struct tasklet_struct dpc_h;
+ struct pci_dev *pdev;
+ struct Scsi_Host *host;
+ spinlock_t *host_lock;
+ spinlock_t lock;
+ uint8_t quiescent;
+ int outstanding_cmds;
+ scb_t *kscb_list;
+ struct list_head kscb_pool;
+ spinlock_t kscb_pool_lock;
+ struct list_head pend_list;
+ spinlock_t pend_list_lock;
+ struct list_head completed_list;
+ spinlock_t completed_list_lock;
+ uint16_t sglen;
+ int device_ids[LSI_MAX_CHANNELS]
+ [LSI_MAX_LOGICAL_DRIVES_64LD];
+ caddr_t raid_device;
+ uint8_t max_channel;
+ uint16_t max_target;
+ uint8_t max_lun;
+
+ uint32_t unique_id;
+ uint8_t irq;
+ uint8_t ito;
+ caddr_t ibuf;
+ dma_addr_t ibuf_dma_h;
+ scb_t *uscb_list;
+ struct list_head uscb_pool;
+ spinlock_t uscb_pool_lock;
+ int max_cmds;
+ uint8_t fw_version[VERSION_SIZE];
+ uint8_t bios_version[VERSION_SIZE];
+ uint8_t max_cdb_sz;
+ uint8_t ha;
+ uint16_t init_id;
+ uint16_t max_sectors;
+ uint16_t cmd_per_lun;
+ atomic_t being_detached;
+} adapter_t;
+
+#define SCSI_FREE_LIST_LOCK(adapter) (&adapter->kscb_pool_lock)
+#define USER_FREE_LIST_LOCK(adapter) (&adapter->uscb_pool_lock)
+#define PENDING_LIST_LOCK(adapter) (&adapter->pend_list_lock)
+#define COMPLETED_LIST_LOCK(adapter) (&adapter->completed_list_lock)
+
+
+// conversion from scsi command
+#define SCP2HOST(scp) (scp)->device->host // to host
+#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state
+#define SCP2CHANNEL(scp) (scp)->device->channel // to channel
+#define SCP2TARGET(scp) (scp)->device->id // to target
+#define SCP2LUN(scp) (scp)->device->lun // to LUN
+
+// generic macro to convert scsi command and host to controller's soft state
+#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0])
+#define SCP2ADAPTER(scp) (adapter_t *)SCSIHOST2ADAP(SCP2HOST(scp))
+
+
+/**
+ * MRAID_GET_DEVICE_MAP - device ids
+ * @param adp - Adapter's soft state
+ * @param scp - mid-layer scsi command pointer
+ * @param p_chan - physical channel on the controller
+ * @param target - target id of the device or logical drive number
+ * @param islogical - set if the command is for the logical drive
+ *
+ * Macro to retrieve information about device class, logical or physical and
+ * the corresponding physical channel and target or logical drive number
+ **/
+#define MRAID_IS_LOGICAL(adp, scp) \
+ (SCP2CHANNEL(scp) == (adp)->max_channel) ? 1 : 0
+
+#define MRAID_IS_LOGICAL_SDEV(adp, sdev) \
+ (sdev->channel == (adp)->max_channel) ? 1 : 0
+
+#define MRAID_GET_DEVICE_MAP(adp, scp, p_chan, target, islogical) \
+ /* \
+ * Is the request coming for the virtual channel \
+ */ \
+ islogical = MRAID_IS_LOGICAL(adp, scp); \
+ \
+ /* \
+ * Get an index into our table of drive ids mapping \
+ */ \
+ if (islogical) { \
+ p_chan = 0xFF; \
+ target = \
+ (adp)->device_ids[(adp)->max_channel][SCP2TARGET(scp)]; \
+ } \
+ else { \
+ p_chan = ((adp)->device_ids[SCP2CHANNEL(scp)] \
+ [SCP2TARGET(scp)] >> 8) & 0xFF; \
+ target = ((adp)->device_ids[SCP2CHANNEL(scp)] \
+ [SCP2TARGET(scp)] & 0xFF); \
+ }
+
+/*
+ * ### Helper routines ###
+ */
+#define LSI_DBGLVL mraid_debug_level // each LLD must define a global
+ // mraid_debug_level
+
+#ifdef DEBUG
+#if defined (_ASSERT_PANIC)
+#define ASSERT_ACTION panic
+#else
+#define ASSERT_ACTION printk
+#endif
+
+#define ASSERT(expression) \
+ if (!(expression)) { \
+ ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \
+ #expression, __FILE__, __LINE__, __FUNCTION__); \
+ }
+#else
+#define ASSERT(expression)
+#endif
+
+/*
+ * struct mraid_pci_blk - structure holds DMA memory block info
+ * @param vaddr : virtual address to a memory block
+ * @param dma_addr : DMA handle to a memory block
+ *
+ * This structure is filled up for the caller. It is the responsibilty of the
+ * caller to allocate this array big enough to store addresses for all
+ * requested elements
+ */
+struct mraid_pci_blk {
+ caddr_t vaddr;
+ dma_addr_t dma_addr;
+};
+
+#endif // _MEGA_COMMON_H_
+
+// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_ioctl.h b/drivers/scsi/megaraid/megaraid_ioctl.h
new file mode 100644
index 00000000000..bdaee144a1c
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_ioctl.h
@@ -0,0 +1,296 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_ioctl.h
+ *
+ * Definitions to interface with user level applications
+ */
+
+#ifndef _MEGARAID_IOCTL_H_
+#define _MEGARAID_IOCTL_H_
+
+#include <linux/types.h>
+#include <asm/semaphore.h>
+
+#include "mbox_defs.h"
+
+/**
+ * con_log() - console log routine
+ * @param level : indicates the severity of the message.
+ * @fparam mt : format string
+ *
+ * con_log displays the error messages on the console based on the current
+ * debug level. Also it attaches the appropriate kernel severity level with
+ * the message.
+ *
+ *
+ * consolge messages debug levels
+ */
+#define CL_ANN 0 /* print unconditionally, announcements */
+#define CL_DLEVEL1 1 /* debug level 1, informative */
+#define CL_DLEVEL2 2 /* debug level 2, verbose */
+#define CL_DLEVEL3 3 /* debug level 3, very verbose */
+
+#define con_log(level, fmt) if (LSI_DBGLVL >= level) printk fmt;
+
+/*
+ * Definitions & Declarations needed to use common management module
+ */
+
+#define MEGAIOC_MAGIC 'm'
+#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, mimd_t)
+
+#define MEGAIOC_QNADAP 'm' /* Query # of adapters */
+#define MEGAIOC_QDRVRVER 'e' /* Query driver version */
+#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */
+
+#define USCSICMD 0x80
+#define UIOC_RD 0x00001
+#define UIOC_WR 0x00002
+
+#define MBOX_CMD 0x00000
+#define GET_DRIVER_VER 0x10000
+#define GET_N_ADAP 0x20000
+#define GET_ADAP_INFO 0x30000
+#define GET_CAP 0x40000
+#define GET_STATS 0x50000
+#define GET_IOCTL_VERSION 0x01
+
+#define EXT_IOCTL_SIGN_SZ 16
+#define EXT_IOCTL_SIGN "$$_EXTD_IOCTL_$$"
+
+#define MBOX_LEGACY 0x00 /* ioctl has legacy mbox*/
+#define MBOX_HPE 0x01 /* ioctl has hpe mbox */
+
+#define APPTYPE_MIMD 0x00 /* old existing apps */
+#define APPTYPE_UIOC 0x01 /* new apps using uioc */
+
+#define IOCTL_ISSUE 0x00000001 /* Issue ioctl */
+#define IOCTL_ABORT 0x00000002 /* Abort previous ioctl */
+
+#define DRVRTYPE_MBOX 0x00000001 /* regular mbox driver */
+#define DRVRTYPE_HPE 0x00000002 /* new hpe driver */
+
+#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) )
+#define GETADAP(mkadap) ((mkadap) ^ MEGAIOC_MAGIC << 8)
+
+#define MAX_DMA_POOLS 5 /* 4k, 8k, 16k, 32k, 64k*/
+
+
+/**
+ * struct uioc_t - the common ioctl packet structure
+ *
+ * @signature : Must be "$$_EXTD_IOCTL_$$"
+ * @mb_type : Type of the mail box (MB_LEGACY or MB_HPE)
+ * @app_type : Type of the issuing application (existing or new)
+ * @opcode : Opcode of the command
+ * @adapno : Adapter number
+ * @cmdbuf : Pointer to buffer - can point to mbox or plain data buffer
+ * @xferlen : xferlen for DCMD and non mailbox commands
+ * @data_dir : Direction of the data transfer
+ * @status : Status from the driver
+ * @reserved : reserved bytes for future expansion
+ *
+ * @user_data : user data transfer address is saved in this
+ * @user_data_len: length of the data buffer sent by user app
+ * @user_pthru : user passthru address is saves in this (null if DCMD)
+ * @pthru32 : kernel address passthru (allocated per kioc)
+ * @pthru32_h : physicall address of @pthru32
+ * @list : for kioc free pool list maintenance
+ * @done : call back routine for llds to call when kioc is completed
+ * @buf_vaddr : dma pool buffer attached to kioc for data transfer
+ * @buf_paddr : physical address of the dma pool buffer
+ * @pool_index : index of the dma pool that @buf_vaddr is taken from
+ * @free_buf : indicates if buffer needs to be freed after kioc completes
+ *
+ * Note : All LSI drivers understand only this packet. Any other
+ * : format sent by applications would be converted to this.
+ */
+typedef struct uioc {
+
+/* User Apps: */
+
+ uint8_t signature[EXT_IOCTL_SIGN_SZ];
+ uint16_t mb_type;
+ uint16_t app_type;
+ uint32_t opcode;
+ uint32_t adapno;
+ uint64_t cmdbuf;
+ uint32_t xferlen;
+ uint32_t data_dir;
+ int32_t status;
+ uint8_t reserved[128];
+
+/* Driver Data: */
+ void __user * user_data;
+ uint32_t user_data_len;
+ mraid_passthru_t __user *user_pthru;
+
+ mraid_passthru_t *pthru32;
+ dma_addr_t pthru32_h;
+
+ struct list_head list;
+ void (*done)(struct uioc*);
+
+ caddr_t buf_vaddr;
+ dma_addr_t buf_paddr;
+ int8_t pool_index;
+ uint8_t free_buf;
+
+ uint8_t timedout;
+
+} __attribute__ ((aligned(1024),packed)) uioc_t;
+
+
+/**
+ * struct mraid_hba_info - information about the controller
+ *
+ * @param pci_vendor_id : PCI vendor id
+ * @param pci_device_id : PCI device id
+ * @param subsystem_vendor_id : PCI subsystem vendor id
+ * @param subsystem_device_id : PCI subsystem device id
+ * @param baseport : base port of hba memory
+ * @param pci_bus : PCI bus
+ * @param pci_dev_fn : PCI device/function values
+ * @param irq : interrupt vector for the device
+ *
+ * Extended information of 256 bytes about the controller. Align on the single
+ * byte boundary so that 32-bit applications can be run on 64-bit platform
+ * drivers withoug re-compilation.
+ * NOTE: reduce the number of reserved bytes whenever new field are added, so
+ * that total size of the structure remains 256 bytes.
+ */
+typedef struct mraid_hba_info {
+
+ uint16_t pci_vendor_id;
+ uint16_t pci_device_id;
+ uint16_t subsys_vendor_id;
+ uint16_t subsys_device_id;
+
+ uint64_t baseport;
+ uint8_t pci_bus;
+ uint8_t pci_dev_fn;
+ uint8_t pci_slot;
+ uint8_t irq;
+
+ uint32_t unique_id;
+ uint32_t host_no;
+
+ uint8_t num_ldrv;
+} __attribute__ ((aligned(256), packed)) mraid_hba_info_t;
+
+
+/**
+ * mcontroller : adapter info structure for old mimd_t apps
+ *
+ * @base : base address
+ * @irq : irq number
+ * @numldrv : number of logical drives
+ * @pcibus : pci bus
+ * @pcidev : pci device
+ * @pcifun : pci function
+ * @pciid : pci id
+ * @pcivendor : vendor id
+ * @pcislot : slot number
+ * @uid : unique id
+ */
+typedef struct mcontroller {
+
+ uint64_t base;
+ uint8_t irq;
+ uint8_t numldrv;
+ uint8_t pcibus;
+ uint16_t pcidev;
+ uint8_t pcifun;
+ uint16_t pciid;
+ uint16_t pcivendor;
+ uint8_t pcislot;
+ uint32_t uid;
+
+} __attribute__ ((packed)) mcontroller_t;
+
+
+/**
+ * mm_dmapool_t : Represents one dma pool with just one buffer
+ *
+ * @vaddr : Virtual address
+ * @paddr : DMA physicall address
+ * @bufsize : In KB - 4 = 4k, 8 = 8k etc.
+ * @handle : Handle to the dma pool
+ * @lock : lock to synchronize access to the pool
+ * @in_use : If pool already in use, attach new block
+ */
+typedef struct mm_dmapool {
+ caddr_t vaddr;
+ dma_addr_t paddr;
+ uint32_t buf_size;
+ struct dma_pool *handle;
+ spinlock_t lock;
+ uint8_t in_use;
+} mm_dmapool_t;
+
+
+/**
+ * mraid_mmadp_t: Structure that drivers pass during (un)registration
+ *
+ * @unique_id : Any unique id (usually PCI bus+dev+fn)
+ * @drvr_type : megaraid or hpe (DRVRTYPE_MBOX or DRVRTYPE_HPE)
+ * @drv_data : Driver specific; not touched by the common module
+ * @timeout : timeout for issued kiocs
+ * @max_kioc : Maximum ioctl packets acceptable by the lld
+ * @pdev : pci dev; used for allocating dma'ble memory
+ * @issue_uioc : Driver supplied routine to issue uioc_t commands
+ * : issue_uioc(drvr_data, kioc, ISSUE/ABORT, uioc_done)
+ * @quiescent : flag to indicate if ioctl can be issued to this adp
+ * @list : attach with the global list of adapters
+ * @kioc_list : block of mem for @max_kioc number of kiocs
+ * @kioc_pool : pool of free kiocs
+ * @kioc_pool_lock : protection for free pool
+ * @kioc_semaphore : so as not to exceed @max_kioc parallel ioctls
+ * @mbox_list : block of mem for @max_kioc number of mboxes
+ * @pthru_dma_pool : DMA pool to allocate passthru packets
+ * @dma_pool_list : array of dma pools
+ */
+
+typedef struct mraid_mmadp {
+
+/* Filled by driver */
+
+ uint32_t unique_id;
+ uint32_t drvr_type;
+ unsigned long drvr_data;
+ uint16_t timeout;
+ uint8_t max_kioc;
+
+ struct pci_dev *pdev;
+
+ int(*issue_uioc)(unsigned long, uioc_t *, uint32_t);
+
+/* Maintained by common module */
+ uint32_t quiescent;
+
+ struct list_head list;
+ uioc_t *kioc_list;
+ struct list_head kioc_pool;
+ spinlock_t kioc_pool_lock;
+ struct semaphore kioc_semaphore;
+
+ mbox64_t *mbox_list;
+ struct dma_pool *pthru_dma_pool;
+ mm_dmapool_t dma_pool_list[MAX_DMA_POOLS];
+
+} mraid_mmadp_t;
+
+int mraid_mm_register_adp(mraid_mmadp_t *);
+int mraid_mm_unregister_adp(uint32_t);
+uint32_t mraid_mm_adapter_app_handle(uint32_t);
+
+#endif /* _MEGARAID_IOCTL_H_ */
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
new file mode 100644
index 00000000000..138fa481583
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -0,0 +1,4276 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mbox.c
+ * Version : v2.20.4.5 (Feb 03 2005)
+ *
+ * Authors:
+ * Atul Mukker <Atul.Mukker@lsil.com>
+ * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com>
+ * Manoj Jose <Manoj.Jose@lsil.com>
+ *
+ * List of supported controllers
+ *
+ * OEM Product Name VID DID SSVID SSID
+ * --- ------------ --- --- ---- ----
+ * Dell PERC3/QC 101E 1960 1028 0471
+ * Dell PERC3/DC 101E 1960 1028 0493
+ * Dell PERC3/SC 101E 1960 1028 0475
+ * Dell PERC3/Di 1028 1960 1028 0123
+ * Dell PERC4/SC 1000 1960 1028 0520
+ * Dell PERC4/DC 1000 1960 1028 0518
+ * Dell PERC4/QC 1000 0407 1028 0531
+ * Dell PERC4/Di 1028 000F 1028 014A
+ * Dell PERC 4e/Si 1028 0013 1028 016c
+ * Dell PERC 4e/Di 1028 0013 1028 016d
+ * Dell PERC 4e/Di 1028 0013 1028 016e
+ * Dell PERC 4e/Di 1028 0013 1028 016f
+ * Dell PERC 4e/Di 1028 0013 1028 0170
+ * Dell PERC 4e/DC 1000 0408 1028 0002
+ * Dell PERC 4e/SC 1000 0408 1028 0001
+ *
+ *
+ * LSI MegaRAID SCSI 320-0 1000 1960 1000 A520
+ * LSI MegaRAID SCSI 320-1 1000 1960 1000 0520
+ * LSI MegaRAID SCSI 320-2 1000 1960 1000 0518
+ * LSI MegaRAID SCSI 320-0X 1000 0407 1000 0530
+ * LSI MegaRAID SCSI 320-2X 1000 0407 1000 0532
+ * LSI MegaRAID SCSI 320-4X 1000 0407 1000 0531
+ * LSI MegaRAID SCSI 320-1E 1000 0408 1000 0001
+ * LSI MegaRAID SCSI 320-2E 1000 0408 1000 0002
+ * LSI MegaRAID SATA 150-4 1000 1960 1000 4523
+ * LSI MegaRAID SATA 150-6 1000 1960 1000 0523
+ * LSI MegaRAID SATA 300-4X 1000 0409 1000 3004
+ * LSI MegaRAID SATA 300-8X 1000 0409 1000 3008
+ *
+ * INTEL RAID Controller SRCU42X 1000 0407 8086 0532
+ * INTEL RAID Controller SRCS16 1000 1960 8086 0523
+ * INTEL RAID Controller SRCU42E 1000 0408 8086 0002
+ * INTEL RAID Controller SRCZCRX 1000 0407 8086 0530
+ * INTEL RAID Controller SRCS28X 1000 0409 8086 3008
+ * INTEL RAID Controller SROMBU42E 1000 0408 8086 3431
+ * INTEL RAID Controller SROMBU42E 1000 0408 8086 3499
+ * INTEL RAID Controller SRCU51L 1000 1960 8086 0520
+ *
+ * FSC MegaRAID PCI Express ROMB 1000 0408 1734 1065
+ *
+ * ACER MegaRAID ROMB-2E 1000 0408 1025 004D
+ *
+ * NEC MegaRAID PCI Express ROMB 1000 0408 1033 8287
+ *
+ * For history of changes, see Documentation/ChangeLog.megaraid
+ */
+
+#include "megaraid_mbox.h"
+
+static int megaraid_init(void);
+static void megaraid_exit(void);
+
+static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *);
+static void megaraid_detach_one(struct pci_dev *);
+static void megaraid_mbox_shutdown(struct device *);
+
+static int megaraid_io_attach(adapter_t *);
+static void megaraid_io_detach(adapter_t *);
+
+static int megaraid_init_mbox(adapter_t *);
+static void megaraid_fini_mbox(adapter_t *);
+
+static int megaraid_alloc_cmd_packets(adapter_t *);
+static void megaraid_free_cmd_packets(adapter_t *);
+
+static int megaraid_mbox_setup_dma_pools(adapter_t *);
+static void megaraid_mbox_teardown_dma_pools(adapter_t *);
+
+static int megaraid_sysfs_alloc_resources(adapter_t *);
+static void megaraid_sysfs_free_resources(adapter_t *);
+
+static int megaraid_abort_handler(struct scsi_cmnd *);
+static int megaraid_reset_handler(struct scsi_cmnd *);
+
+static int mbox_post_sync_cmd(adapter_t *, uint8_t []);
+static int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []);
+static int megaraid_busywait_mbox(mraid_device_t *);
+static int megaraid_mbox_product_info(adapter_t *);
+static int megaraid_mbox_extended_cdb(adapter_t *);
+static int megaraid_mbox_support_ha(adapter_t *, uint16_t *);
+static int megaraid_mbox_support_random_del(adapter_t *);
+static int megaraid_mbox_get_max_sg(adapter_t *);
+static void megaraid_mbox_enum_raid_scsi(adapter_t *);
+static void megaraid_mbox_flush_cache(adapter_t *);
+
+static void megaraid_mbox_display_scb(adapter_t *, scb_t *);
+static void megaraid_mbox_setup_device_map(adapter_t *);
+
+static int megaraid_queue_command(struct scsi_cmnd *,
+ void (*)(struct scsi_cmnd *));
+static scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *);
+static void megaraid_mbox_runpendq(adapter_t *, scb_t *);
+static void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *,
+ struct scsi_cmnd *);
+static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *,
+ struct scsi_cmnd *);
+
+static irqreturn_t megaraid_isr(int, void *, struct pt_regs *);
+
+static void megaraid_mbox_dpc(unsigned long);
+
+static ssize_t megaraid_sysfs_show_app_hndl(struct class_device *, char *);
+static ssize_t megaraid_sysfs_show_ldnum(struct device *, char *);
+
+static int megaraid_cmm_register(adapter_t *);
+static int megaraid_cmm_unregister(adapter_t *);
+static int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t);
+static int megaraid_mbox_mm_command(adapter_t *, uioc_t *);
+static void megaraid_mbox_mm_done(adapter_t *, scb_t *);
+static int gather_hbainfo(adapter_t *, mraid_hba_info_t *);
+static int wait_till_fw_empty(adapter_t *);
+
+
+
+MODULE_AUTHOR("LSI Logic Corporation");
+MODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MEGARAID_VERSION);
+
+/*
+ * ### modules parameters for driver ###
+ */
+
+/**
+ * Set to enable driver to expose unconfigured disk to kernel
+ */
+static int megaraid_expose_unconf_disks = 0;
+module_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0);
+MODULE_PARM_DESC(unconf_disks,
+ "Set to expose unconfigured disks to kernel (default=0)");
+
+/**
+ * driver wait time if the adapter's mailbox is busy
+ */
+static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT;
+module_param_named(busy_wait, max_mbox_busy_wait, int, 0);
+MODULE_PARM_DESC(busy_wait,
+ "Max wait for mailbox in microseconds if busy (default=10)");
+
+/**
+ * number of sectors per IO command
+ */
+static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS;
+module_param_named(max_sectors, megaraid_max_sectors, int, 0);
+MODULE_PARM_DESC(max_sectors,
+ "Maximum number of sectors per IO command (default=128)");
+
+/**
+ * number of commands per logical unit
+ */
+static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN;
+module_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0);
+MODULE_PARM_DESC(cmd_per_lun,
+ "Maximum number of commands per logical unit (default=64)");
+
+
+/**
+ * Fast driver load option, skip scanning for physical devices during load.
+ * This would result in non-disk devices being skipped during driver load
+ * time. These can be later added though, using /proc/scsi/scsi
+ */
+static unsigned int megaraid_fast_load = 0;
+module_param_named(fast_load, megaraid_fast_load, int, 0);
+MODULE_PARM_DESC(fast_load,
+ "Faster loading of the driver, skips physical devices! (default=0)");
+
+
+/**
+ * mraid_debug level - threshold for amount of information to be displayed by
+ * the driver. This level can be changed through modules parameters, ioctl or
+ * sysfs/proc interface. By default, print the announcement messages only.
+ */
+int mraid_debug_level = CL_ANN;
+module_param_named(debug_level, mraid_debug_level, int, 0);
+MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)");
+
+/*
+ * ### global data ###
+ */
+static uint8_t megaraid_mbox_version[8] =
+ { 0x02, 0x20, 0x04, 0x05, 2, 3, 20, 5 };
+
+
+/*
+ * PCI table for all supported controllers.
+ */
+static struct pci_device_id pci_id_table_g[] = {
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4_DI_DISCOVERY,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DI_DISCOVERY,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_SC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_SC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_DC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_QC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_QC,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4_DI_EVERGLADES,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DI_EVERGLADES,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_SI_BIGBEND,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_SI_BIGBEND,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_KOBUK,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_KOBUK,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_CORVETTE,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_CORVETTE,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_EXPEDITION,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_GUADALUPE,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4E_DC_320_2E,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DC_320_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4E_SC_320_1E,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_SC_320_1E,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_QC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_DC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_SC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_AMI,
+ PCI_SUBSYS_ID_PERC3_SC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_AMI,
+ PCI_SUBSYS_ID_PERC3_DC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_0,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_0,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_1,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_1,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_0x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_4x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_1E,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2E,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_I4_133_RAID,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_I4_133_RAID,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_150_4,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_150_4,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_150_6,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_150_6,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_300_4x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_300_4x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_300_8x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_300_8x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU42X,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU42X,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCS16,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCS16,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU42E,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU42E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCZCRX,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCS28X,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCS28X,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB,
+ PCI_SUBSYS_ID_FSC,
+ PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E,
+ PCI_VENDOR_ID_AI,
+ PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_NEC_ROMB_2E,
+ PCI_VENDOR_ID_NEC,
+ PCI_SUBSYS_ID_MEGARAID_NEC_ROMB_2E,
+ },
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, pci_id_table_g);
+
+
+static struct pci_driver megaraid_pci_driver_g = {
+ .name = "megaraid",
+ .id_table = pci_id_table_g,
+ .probe = megaraid_probe_one,
+ .remove = __devexit_p(megaraid_detach_one),
+ .driver = {
+ .shutdown = megaraid_mbox_shutdown,
+ }
+};
+
+
+
+// definitions for the device attributes for exporting logical drive number
+// for a scsi address (Host, Channel, Id, Lun)
+
+CLASS_DEVICE_ATTR(megaraid_mbox_app_hndl, S_IRUSR, megaraid_sysfs_show_app_hndl,
+ NULL);
+
+// Host template initializer for megaraid mbox sysfs device attributes
+static struct class_device_attribute *megaraid_shost_attrs[] = {
+ &class_device_attr_megaraid_mbox_app_hndl,
+ NULL,
+};
+
+
+DEVICE_ATTR(megaraid_mbox_ld, S_IRUSR, megaraid_sysfs_show_ldnum, NULL);
+
+// Host template initializer for megaraid mbox sysfs device attributes
+static struct device_attribute *megaraid_sdev_attrs[] = {
+ &dev_attr_megaraid_mbox_ld,
+ NULL,
+};
+
+
+/*
+ * Scsi host template for megaraid unified driver
+ */
+static struct scsi_host_template megaraid_template_g = {
+ .module = THIS_MODULE,
+ .name = "LSI Logic MegaRAID driver",
+ .proc_name = "megaraid",
+ .queuecommand = megaraid_queue_command,
+ .eh_abort_handler = megaraid_abort_handler,
+ .eh_device_reset_handler = megaraid_reset_handler,
+ .eh_bus_reset_handler = megaraid_reset_handler,
+ .eh_host_reset_handler = megaraid_reset_handler,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sdev_attrs = megaraid_sdev_attrs,
+ .shost_attrs = megaraid_shost_attrs,
+};
+
+
+/**
+ * megaraid_init - module load hook
+ *
+ * We register ourselves as hotplug enabled module and let PCI subsystem
+ * discover our adaters
+ **/
+static int __init
+megaraid_init(void)
+{
+ int rval;
+
+ // Announce the driver version
+ con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION,
+ MEGARAID_EXT_VERSION));
+
+ // check validity of module parameters
+ if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: max commands per lun reset to %d\n",
+ MBOX_MAX_SCSI_CMDS));
+
+ megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS;
+ }
+
+
+ // register as a PCI hot-plug driver module
+ if ((rval = pci_module_init(&megaraid_pci_driver_g))) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not register hotplug support.\n"));
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_exit - driver unload entry point
+ *
+ * We simply unwrap the megaraid_init routine here
+ */
+static void __exit
+megaraid_exit(void)
+{
+ con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n"));
+
+ // unregister as PCI hotplug driver
+ pci_unregister_driver(&megaraid_pci_driver_g);
+
+ return;
+}
+
+
+/**
+ * megaraid_probe_one - PCI hotplug entry point
+ * @param pdev : handle to this controller's PCI configuration space
+ * @param id : pci device id of the class of controllers
+ *
+ * This routine should be called whenever a new adapter is detected by the
+ * PCI hotplug susbsytem.
+ **/
+static int __devinit
+megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ adapter_t *adapter;
+
+
+ // detected a new controller
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+
+ con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)));
+
+ if (pci_enable_device(pdev)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: pci_enable_device failed\n"));
+
+ return -ENODEV;
+ }
+
+ // Enable bus-mastering on this controller
+ pci_set_master(pdev);
+
+ // Allocate the per driver initialization structure
+ adapter = kmalloc(sizeof(adapter_t), GFP_KERNEL);
+
+ if (adapter == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__));
+
+ goto out_probe_one;
+ }
+ memset(adapter, 0, sizeof(adapter_t));
+
+
+ // set up PCI related soft state and other pre-known parameters
+ adapter->unique_id = pdev->bus->number << 8 | pdev->devfn;
+ adapter->irq = pdev->irq;
+ adapter->pdev = pdev;
+
+ atomic_set(&adapter->being_detached, 0);
+
+ // Setup the default DMA mask. This would be changed later on
+ // depending on hardware capabilities
+ if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFF) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: pci_set_dma_mask failed:%d\n", __LINE__));
+
+ goto out_free_adapter;
+ }
+
+
+ // Initialize the synchronization lock for kernel and LLD
+ spin_lock_init(&adapter->lock);
+ adapter->host_lock = &adapter->lock;
+
+
+ // Initialize the command queues: the list of free SCBs and the list
+ // of pending SCBs.
+ INIT_LIST_HEAD(&adapter->kscb_pool);
+ spin_lock_init(SCSI_FREE_LIST_LOCK(adapter));
+
+ INIT_LIST_HEAD(&adapter->pend_list);
+ spin_lock_init(PENDING_LIST_LOCK(adapter));
+
+ INIT_LIST_HEAD(&adapter->completed_list);
+ spin_lock_init(COMPLETED_LIST_LOCK(adapter));
+
+
+ // Start the mailbox based controller
+ if (megaraid_init_mbox(adapter) != 0) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: maibox adapter did not initialize\n"));
+
+ goto out_free_adapter;
+ }
+
+ // Register with LSI Common Management Module
+ if (megaraid_cmm_register(adapter) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not register with management module\n"));
+
+ goto out_fini_mbox;
+ }
+
+ // setup adapter handle in PCI soft state
+ pci_set_drvdata(pdev, adapter);
+
+ // attach with scsi mid-layer
+ if (megaraid_io_attach(adapter) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n"));
+
+ goto out_cmm_unreg;
+ }
+
+ return 0;
+
+out_cmm_unreg:
+ pci_set_drvdata(pdev, NULL);
+ megaraid_cmm_unregister(adapter);
+out_fini_mbox:
+ megaraid_fini_mbox(adapter);
+out_free_adapter:
+ kfree(adapter);
+out_probe_one:
+ pci_disable_device(pdev);
+
+ return -ENODEV;
+}
+
+
+/**
+ * megaraid_detach_one - release the framework resources and call LLD release
+ * routine
+ * @param pdev : handle for our PCI cofiguration space
+ *
+ * This routine is called during driver unload. We free all the allocated
+ * resources and call the corresponding LLD so that it can also release all
+ * its resources.
+ *
+ * This routine is also called from the PCI hotplug system
+ **/
+static void
+megaraid_detach_one(struct pci_dev *pdev)
+{
+ adapter_t *adapter;
+ struct Scsi_Host *host;
+
+
+ // Start a rollback on this adapter
+ adapter = pci_get_drvdata(pdev);
+
+ if (!adapter) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+
+ return;
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+ }
+
+
+ host = adapter->host;
+
+ // do not allow any more requests from the management module for this
+ // adapter.
+ // FIXME: How do we account for the request which might still be
+ // pending with us?
+ atomic_set(&adapter->being_detached, 1);
+
+ // detach from the IO sub-system
+ megaraid_io_detach(adapter);
+
+ // reset the device state in the PCI structure. We check this
+ // condition when we enter here. If the device state is NULL,
+ // that would mean the device has already been removed
+ pci_set_drvdata(pdev, NULL);
+
+ // Unregister from common management module
+ //
+ // FIXME: this must return success or failure for conditions if there
+ // is a command pending with LLD or not.
+ megaraid_cmm_unregister(adapter);
+
+ // finalize the mailbox based controller and release all resources
+ megaraid_fini_mbox(adapter);
+
+ kfree(adapter);
+
+ scsi_host_put(host);
+
+ pci_disable_device(pdev);
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA
+ * @param device : generice driver model device
+ *
+ * Shutdown notification, perform flush cache
+ */
+static void
+megaraid_mbox_shutdown(struct device *device)
+{
+ adapter_t *adapter = pci_get_drvdata(to_pci_dev(device));
+ static int counter;
+
+ if (!adapter) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: null device in shutdown\n"));
+ return;
+ }
+
+ // flush caches now
+ con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...",
+ counter++));
+
+ megaraid_mbox_flush_cache(adapter);
+
+ con_log(CL_ANN, ("done\n"));
+}
+
+
+/**
+ * megaraid_io_attach - attach a device with the IO subsystem
+ * @param adapter : controller's soft state
+ *
+ * Attach this device with the IO subsystem
+ **/
+static int
+megaraid_io_attach(adapter_t *adapter)
+{
+ struct Scsi_Host *host;
+
+ // Initialize SCSI Host structure
+ host = scsi_host_alloc(&megaraid_template_g, 8);
+ if (!host) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: scsi_register failed\n"));
+
+ return -1;
+ }
+
+ SCSIHOST2ADAP(host) = (caddr_t)adapter;
+ adapter->host = host;
+
+ // export the parameters required by the mid-layer
+ scsi_assign_lock(host, adapter->host_lock);
+ scsi_set_device(host, &adapter->pdev->dev);
+
+ host->irq = adapter->irq;
+ host->unique_id = adapter->unique_id;
+ host->can_queue = adapter->max_cmds;
+ host->this_id = adapter->init_id;
+ host->sg_tablesize = adapter->sglen;
+ host->max_sectors = adapter->max_sectors;
+ host->cmd_per_lun = adapter->cmd_per_lun;
+ host->max_channel = adapter->max_channel;
+ host->max_id = adapter->max_target;
+ host->max_lun = adapter->max_lun;
+
+
+ // notify mid-layer about the new controller
+ if (scsi_add_host(host, &adapter->pdev->dev)) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: scsi_add_host failed\n"));
+
+ scsi_host_put(host);
+
+ return -1;
+ }
+
+ scsi_scan_host(host);
+
+ return 0;
+}
+
+
+/**
+ * megaraid_io_detach - detach a device from the IO subsystem
+ * @param adapter : controller's soft state
+ *
+ * Detach this device from the IO subsystem
+ **/
+static void
+megaraid_io_detach(adapter_t *adapter)
+{
+ struct Scsi_Host *host;
+
+ con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n"));
+
+ host = adapter->host;
+
+ scsi_remove_host(host);
+
+ return;
+}
+
+
+/*
+ * START: Mailbox Low Level Driver
+ *
+ * This is section specific to the single mailbox based controllers
+ */
+
+/**
+ * megaraid_init_mbox - initialize controller
+ * @param adapter - our soft state
+ *
+ * . Allocate 16-byte aligned mailbox memory for firmware handshake
+ * . Allocate controller's memory resources
+ * . Find out all initialization data
+ * . Allocate memory required for all the commands
+ * . Use internal library of FW routines, build up complete soft state
+ */
+static int __init
+megaraid_init_mbox(adapter_t *adapter)
+{
+ struct pci_dev *pdev;
+ mraid_device_t *raid_dev;
+ int i;
+
+
+ adapter->ito = MBOX_TIMEOUT;
+ pdev = adapter->pdev;
+
+ /*
+ * Allocate and initialize the init data structure for mailbox
+ * controllers
+ */
+ raid_dev = kmalloc(sizeof(mraid_device_t), GFP_KERNEL);
+ if (raid_dev == NULL) return -1;
+
+ memset(raid_dev, 0, sizeof(mraid_device_t));
+
+ /*
+ * Attach the adapter soft state to raid device soft state
+ */
+ adapter->raid_device = (caddr_t)raid_dev;
+ raid_dev->fast_load = megaraid_fast_load;
+
+
+ // our baseport
+ raid_dev->baseport = pci_resource_start(pdev, 0);
+
+ if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: mem region busy\n"));
+
+ goto out_free_raid_dev;
+ }
+
+ raid_dev->baseaddr = ioremap_nocache(raid_dev->baseport, 128);
+
+ if (!raid_dev->baseaddr) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not map hba memory\n") );
+
+ goto out_release_regions;
+ }
+
+ //
+ // Setup the rest of the soft state using the library of FW routines
+ //
+
+ // request IRQ and register the interrupt service routine
+ if (request_irq(adapter->irq, megaraid_isr, SA_SHIRQ, "megaraid",
+ adapter)) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: Couldn't register IRQ %d!\n", adapter->irq));
+
+ goto out_iounmap;
+ }
+
+
+ // initialize the mutual exclusion lock for the mailbox
+ spin_lock_init(&raid_dev->mailbox_lock);
+
+ // allocate memory required for commands
+ if (megaraid_alloc_cmd_packets(adapter) != 0) {
+ goto out_free_irq;
+ }
+
+ // Product info
+ if (megaraid_mbox_product_info(adapter) != 0) {
+ goto out_alloc_cmds;
+ }
+
+ // Do we support extended CDBs
+ adapter->max_cdb_sz = 10;
+ if (megaraid_mbox_extended_cdb(adapter) == 0) {
+ adapter->max_cdb_sz = 16;
+ }
+
+ /*
+ * Do we support cluster environment, if we do, what is the initiator
+ * id.
+ * NOTE: In a non-cluster aware firmware environment, the LLD should
+ * return 7 as initiator id.
+ */
+ adapter->ha = 0;
+ adapter->init_id = -1;
+ if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) {
+ adapter->ha = 1;
+ }
+
+ /*
+ * Prepare the device ids array to have the mapping between the kernel
+ * device address and megaraid device address.
+ * We export the physical devices on their actual addresses. The
+ * logical drives are exported on a virtual SCSI channel
+ */
+ megaraid_mbox_setup_device_map(adapter);
+
+ // If the firmware supports random deletion, update the device id map
+ if (megaraid_mbox_support_random_del(adapter)) {
+
+ // Change the logical drives numbers in device_ids array one
+ // slot in device_ids is reserved for target id, that's why
+ // "<=" below
+ for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) {
+ adapter->device_ids[adapter->max_channel][i] += 0x80;
+ }
+ adapter->device_ids[adapter->max_channel][adapter->init_id] =
+ 0xFF;
+
+ raid_dev->random_del_supported = 1;
+ }
+
+ /*
+ * find out the maximum number of scatter-gather elements supported by
+ * this firmware
+ */
+ adapter->sglen = megaraid_mbox_get_max_sg(adapter);
+
+ // enumerate RAID and SCSI channels so that all devices on SCSI
+ // channels can later be exported, including disk devices
+ megaraid_mbox_enum_raid_scsi(adapter);
+
+ /*
+ * Other parameters required by upper layer
+ *
+ * maximum number of sectors per IO command
+ */
+ adapter->max_sectors = megaraid_max_sectors;
+
+ /*
+ * number of queued commands per LUN.
+ */
+ adapter->cmd_per_lun = megaraid_cmd_per_lun;
+
+ /*
+ * Allocate resources required to issue FW calls, when sysfs is
+ * accessed
+ */
+ if (megaraid_sysfs_alloc_resources(adapter) != 0) {
+ goto out_alloc_cmds;
+ }
+
+ // Set the DMA mask to 64-bit. All supported controllers as capable of
+ // DMA in this range
+ if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not set DMA mask for 64-bit.\n"));
+
+ goto out_free_sysfs_res;
+ }
+
+ // setup tasklet for DPC
+ tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc,
+ (unsigned long)adapter);
+
+ con_log(CL_DLEVEL1, (KERN_INFO
+ "megaraid mbox hba successfully initialized\n"));
+
+ return 0;
+
+out_free_sysfs_res:
+ megaraid_sysfs_free_resources(adapter);
+out_alloc_cmds:
+ megaraid_free_cmd_packets(adapter);
+out_free_irq:
+ free_irq(adapter->irq, adapter);
+out_iounmap:
+ iounmap(raid_dev->baseaddr);
+out_release_regions:
+ pci_release_regions(pdev);
+out_free_raid_dev:
+ kfree(raid_dev);
+
+ return -1;
+}
+
+
+/**
+ * megaraid_fini_mbox - undo controller initialization
+ * @param adapter : our soft state
+ */
+static void
+megaraid_fini_mbox(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ // flush all caches
+ megaraid_mbox_flush_cache(adapter);
+
+ tasklet_kill(&adapter->dpc_h);
+
+ megaraid_sysfs_free_resources(adapter);
+
+ megaraid_free_cmd_packets(adapter);
+
+ free_irq(adapter->irq, adapter);
+
+ iounmap(raid_dev->baseaddr);
+
+ pci_release_regions(adapter->pdev);
+
+ kfree(raid_dev);
+
+ return;
+}
+
+
+/**
+ * megaraid_alloc_cmd_packets - allocate shared mailbox
+ * @param adapter : soft state of the raid controller
+ *
+ * Allocate and align the shared mailbox. This maibox is used to issue
+ * all the commands. For IO based controllers, the mailbox is also regsitered
+ * with the FW. Allocate memory for all commands as well.
+ * This is our big allocator
+ */
+static int
+megaraid_alloc_cmd_packets(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct pci_dev *pdev;
+ unsigned long align;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+ pdev = adapter->pdev;
+
+ /*
+ * Setup the mailbox
+ * Allocate the common 16-byte aligned memory for the handshake
+ * mailbox.
+ */
+ raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev,
+ sizeof(mbox64_t), &raid_dev->una_mbox64_dma);
+
+ if (!raid_dev->una_mbox64) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ return -1;
+ }
+ memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t));
+
+ /*
+ * Align the mailbox at 16-byte boundary
+ */
+ raid_dev->mbox = &raid_dev->una_mbox64->mbox32;
+
+ raid_dev->mbox = (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) &
+ (~0UL ^ 0xFUL));
+
+ raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8);
+
+ align = ((void *)raid_dev->mbox -
+ ((void *)&raid_dev->una_mbox64->mbox32));
+
+ raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 +
+ align;
+
+ // Allocate memory for commands issued internally
+ adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE,
+ &adapter->ibuf_dma_h);
+ if (!adapter->ibuf) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ goto out_free_common_mbox;
+ }
+ memset(adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ // Allocate memory for our SCSI Command Blocks and their associated
+ // memory
+
+ /*
+ * Allocate memory for the base list of scb. Later allocate memory for
+ * CCBs and embedded components of each CCB and point the pointers in
+ * scb to the allocated components
+ * NOTE: The code to allocate SCB will be duplicated in all the LLD
+ * since the calling routine does not yet know the number of available
+ * commands.
+ */
+ adapter->kscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_SCSI_CMDS,
+ GFP_KERNEL);
+
+ if (adapter->kscb_list == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ goto out_free_ibuf;
+ }
+ memset(adapter->kscb_list, 0, sizeof(scb_t) * MBOX_MAX_SCSI_CMDS);
+
+ // memory allocation for our command packets
+ if (megaraid_mbox_setup_dma_pools(adapter) != 0) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ goto out_free_scb_list;
+ }
+
+ // Adjust the scb pointers and link in the free pool
+ epthru_pci_blk = raid_dev->epthru_pool;
+ sg_pci_blk = raid_dev->sg_pool;
+ mbox_pci_blk = raid_dev->mbox_pool;
+
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ scb = adapter->kscb_list + i;
+ ccb = raid_dev->ccb_list + i;
+
+ ccb->mbox = (mbox_t *)(mbox_pci_blk[i].vaddr + 16);
+ ccb->raw_mbox = (uint8_t *)ccb->mbox;
+ ccb->mbox64 = (mbox64_t *)(mbox_pci_blk[i].vaddr + 8);
+ ccb->mbox_dma_h = (unsigned long)mbox_pci_blk[i].dma_addr + 16;
+
+ // make sure the mailbox is aligned properly
+ if (ccb->mbox_dma_h & 0x0F) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid mbox: not aligned on 16-bytes\n"));
+
+ goto out_teardown_dma_pools;
+ }
+
+ ccb->epthru = (mraid_epassthru_t *)
+ epthru_pci_blk[i].vaddr;
+ ccb->epthru_dma_h = epthru_pci_blk[i].dma_addr;
+ ccb->pthru = (mraid_passthru_t *)ccb->epthru;
+ ccb->pthru_dma_h = ccb->epthru_dma_h;
+
+
+ ccb->sgl64 = (mbox_sgl64 *)sg_pci_blk[i].vaddr;
+ ccb->sgl_dma_h = sg_pci_blk[i].dma_addr;
+ ccb->sgl32 = (mbox_sgl32 *)ccb->sgl64;
+
+ scb->ccb = (caddr_t)ccb;
+ scb->gp = 0;
+
+ scb->sno = i; // command index
+
+ scb->scp = NULL;
+ scb->state = SCB_FREE;
+ scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dev_channel = -1;
+ scb->dev_target = -1;
+
+ // put scb in the free pool
+ list_add_tail(&scb->list, &adapter->kscb_pool);
+ }
+
+ return 0;
+
+out_teardown_dma_pools:
+ megaraid_mbox_teardown_dma_pools(adapter);
+out_free_scb_list:
+ kfree(adapter->kscb_list);
+out_free_ibuf:
+ pci_free_consistent(pdev, MBOX_IBUF_SIZE, (void *)adapter->ibuf,
+ adapter->ibuf_dma_h);
+out_free_common_mbox:
+ pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
+
+ return -1;
+}
+
+
+/**
+ * megaraid_free_cmd_packets - free memory
+ * @param adapter : soft state of the raid controller
+ *
+ * Release memory resources allocated for commands
+ */
+static void
+megaraid_free_cmd_packets(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ megaraid_mbox_teardown_dma_pools(adapter);
+
+ kfree(adapter->kscb_list);
+
+ pci_free_consistent(adapter->pdev, MBOX_IBUF_SIZE,
+ (void *)adapter->ibuf, adapter->ibuf_dma_h);
+
+ pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
+ return;
+}
+
+
+/**
+ * megaraid_mbox_setup_dma_pools - setup dma pool for command packets
+ * @param adapter : HBA soft state
+ *
+ * setup the dma pools for mailbox, passthru and extended passthru structures,
+ * and scatter-gather lists
+ */
+static int
+megaraid_mbox_setup_dma_pools(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+
+
+ // Allocate memory for 16-bytes aligned mailboxes
+ raid_dev->mbox_pool_handle = pci_pool_create("megaraid mbox pool",
+ adapter->pdev,
+ sizeof(mbox64_t) + 16,
+ 16, 0);
+
+ if (raid_dev->mbox_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ mbox_pci_blk = raid_dev->mbox_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ mbox_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->mbox_pool_handle,
+ GFP_KERNEL,
+ &mbox_pci_blk[i].dma_addr);
+ if (!mbox_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+ /*
+ * Allocate memory for each embedded passthru strucuture pointer
+ * Request for a 128 bytes aligned structure for each passthru command
+ * structure
+ * Since passthru and extended passthru commands are exclusive, they
+ * share common memory pool. Passthru structures piggyback on memory
+ * allocted to extended passthru since passthru is smaller of the two
+ */
+ raid_dev->epthru_pool_handle = pci_pool_create("megaraid mbox pthru",
+ adapter->pdev, sizeof(mraid_epassthru_t), 128, 0);
+
+ if (raid_dev->epthru_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ epthru_pci_blk = raid_dev->epthru_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ epthru_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->epthru_pool_handle,
+ GFP_KERNEL,
+ &epthru_pci_blk[i].dma_addr);
+ if (!epthru_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+
+ // Allocate memory for each scatter-gather list. Request for 512 bytes
+ // alignment for each sg list
+ raid_dev->sg_pool_handle = pci_pool_create("megaraid mbox sg",
+ adapter->pdev,
+ sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE,
+ 512, 0);
+
+ if (raid_dev->sg_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ sg_pci_blk = raid_dev->sg_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ sg_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->sg_pool_handle,
+ GFP_KERNEL,
+ &sg_pci_blk[i].dma_addr);
+ if (!sg_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+ return 0;
+
+fail_setup_dma_pool:
+ megaraid_mbox_teardown_dma_pools(adapter);
+ return -1;
+}
+
+
+/**
+ * megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets
+ * @param adapter : HBA soft state
+ *
+ * teardown the dma pool for mailbox, passthru and extended passthru
+ * structures, and scatter-gather lists
+ */
+static void
+megaraid_mbox_teardown_dma_pools(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+
+ sg_pci_blk = raid_dev->sg_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr,
+ sg_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->sg_pool_handle)
+ pci_pool_destroy(raid_dev->sg_pool_handle);
+
+
+ epthru_pci_blk = raid_dev->epthru_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->epthru_pool_handle,
+ epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->epthru_pool_handle)
+ pci_pool_destroy(raid_dev->epthru_pool_handle);
+
+
+ mbox_pci_blk = raid_dev->mbox_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->mbox_pool_handle,
+ mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->mbox_pool_handle)
+ pci_pool_destroy(raid_dev->mbox_pool_handle);
+
+ return;
+}
+
+
+/**
+ * megaraid_alloc_scb - detach and return a scb from the free list
+ * @adapter : controller's soft state
+ *
+ * return the scb from the head of the free list. NULL if there are none
+ * available
+ **/
+static inline scb_t *
+megaraid_alloc_scb(adapter_t *adapter, struct scsi_cmnd *scp)
+{
+ struct list_head *head = &adapter->kscb_pool;
+ scb_t *scb = NULL;
+ unsigned long flags;
+
+ // detach scb from free pool
+ spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ if (list_empty(head)) {
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+ return NULL;
+ }
+
+ scb = list_entry(head->next, scb_t, list);
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_ACTIVE;
+ scb->scp = scp;
+ scb->dma_type = MRAID_DMA_NONE;
+
+ return scb;
+}
+
+
+/**
+ * megaraid_dealloc_scb - return the scb to the free pool
+ * @adapter : controller's soft state
+ * @scb : scb to be freed
+ *
+ * return the scb back to the free list of scbs. The caller must 'flush' the
+ * SCB before calling us. E.g., performing pci_unamp and/or pci_sync etc.
+ * NOTE NOTE: Make sure the scb is not on any list before calling this
+ * routine.
+ **/
+static inline void
+megaraid_dealloc_scb(adapter_t *adapter, scb_t *scb)
+{
+ unsigned long flags;
+
+ // put scb in the free pool
+ scb->state = SCB_FREE;
+ scb->scp = NULL;
+ spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ list_add(&scb->list, &adapter->kscb_pool);
+
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_mksgl - make the scatter-gather list
+ * @adapter - controller's soft state
+ * @scb - scsi control block
+ *
+ * prepare the scatter-gather list
+ */
+static inline int
+megaraid_mbox_mksgl(adapter_t *adapter, scb_t *scb)
+{
+ struct scatterlist *sgl;
+ mbox_ccb_t *ccb;
+ struct page *page;
+ unsigned long offset;
+ struct scsi_cmnd *scp;
+ int sgcnt;
+ int i;
+
+
+ scp = scb->scp;
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ // no mapping required if no data to be transferred
+ if (!scp->request_buffer || !scp->request_bufflen)
+ return 0;
+
+ if (!scp->use_sg) { /* scatter-gather list not used */
+
+ page = virt_to_page(scp->request_buffer);
+
+ offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
+
+ ccb->buf_dma_h = pci_map_page(adapter->pdev, page, offset,
+ scp->request_bufflen,
+ scb->dma_direction);
+ scb->dma_type = MRAID_DMA_WBUF;
+
+ /*
+ * We need to handle special 64-bit commands that need a
+ * minimum of 1 SG
+ */
+ sgcnt = 1;
+ ccb->sgl64[0].address = ccb->buf_dma_h;
+ ccb->sgl64[0].length = scp->request_bufflen;
+
+ return sgcnt;
+ }
+
+ sgl = (struct scatterlist *)scp->request_buffer;
+
+ // The number of sg elements returned must not exceed our limit
+ sgcnt = pci_map_sg(adapter->pdev, sgl, scp->use_sg,
+ scb->dma_direction);
+
+ if (sgcnt > adapter->sglen) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid critical: too many sg elements:%d\n",
+ sgcnt));
+ BUG();
+ }
+
+ scb->dma_type = MRAID_DMA_WSG;
+
+ for (i = 0; i < sgcnt; i++, sgl++) {
+ ccb->sgl64[i].address = sg_dma_address(sgl);
+ ccb->sgl64[i].length = sg_dma_len(sgl);
+ }
+
+ // Return count of SG nodes
+ return sgcnt;
+}
+
+
+/**
+ * mbox_post_cmd - issue a mailbox command
+ * @adapter - controller's soft state
+ * @scb - command to be issued
+ *
+ * post the command to the controller if mailbox is availble.
+ */
+static inline int
+mbox_post_cmd(adapter_t *adapter, scb_t *scb)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ mbox_ccb_t *ccb;
+ unsigned long flags;
+ unsigned int i = 0;
+
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ mbox = raid_dev->mbox;
+ mbox64 = raid_dev->mbox64;
+
+ /*
+ * Check for busy mailbox. If it is, return failure - the caller
+ * should retry later.
+ */
+ spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
+
+ if (unlikely(mbox->busy)) {
+ do {
+ udelay(1);
+ i++;
+ rmb();
+ } while(mbox->busy && (i < max_mbox_busy_wait));
+
+ if (mbox->busy) {
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+ return -1;
+ }
+ }
+
+
+ // Copy this command's mailbox data into "adapter's" mailbox
+ memcpy((caddr_t)mbox64, (caddr_t)ccb->mbox64, 22);
+ mbox->cmdid = scb->sno;
+
+ adapter->outstanding_cmds++;
+
+ if (scb->dma_direction == PCI_DMA_TODEVICE) {
+ if (!scb->scp->use_sg) { // sg list not used
+ pci_dma_sync_single_for_device(adapter->pdev,
+ ccb->buf_dma_h,
+ scb->scp->request_bufflen,
+ PCI_DMA_TODEVICE);
+ }
+ else {
+ pci_dma_sync_sg_for_device(adapter->pdev,
+ scb->scp->request_buffer,
+ scb->scp->use_sg, PCI_DMA_TODEVICE);
+ }
+ }
+
+ mbox->busy = 1; // Set busy
+ mbox->poll = 0;
+ mbox->ack = 0;
+ wmb();
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+ return 0;
+}
+
+
+/**
+ * megaraid_queue_command - generic queue entry point for all LLDs
+ * @scp : pointer to the scsi command to be executed
+ * @done : callback routine to be called after the cmd has be completed
+ *
+ * Queue entry point for mailbox based controllers.
+ */
+static int
+megaraid_queue_command(struct scsi_cmnd *scp, void (* done)(struct scsi_cmnd *))
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ int if_busy;
+
+ adapter = SCP2ADAPTER(scp);
+ scp->scsi_done = done;
+ scp->result = 0;
+
+ assert_spin_locked(adapter->host_lock);
+
+ spin_unlock(adapter->host_lock);
+
+ /*
+ * Allocate and build a SCB request
+ * if_busy flag will be set if megaraid_mbox_build_cmd() command could
+ * not allocate scb. We will return non-zero status in that case.
+ * NOTE: scb can be null even though certain commands completed
+ * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, it would
+ * return 0 in that case, and we would do the callback right away.
+ */
+ if_busy = 0;
+ scb = megaraid_mbox_build_cmd(adapter, scp, &if_busy);
+
+ if (scb) {
+ megaraid_mbox_runpendq(adapter, scb);
+ }
+
+ spin_lock(adapter->host_lock);
+
+ if (!scb) { // command already completed
+ done(scp);
+ return 0;
+ }
+
+ return if_busy;
+}
+
+
+/**
+ * megaraid_mbox_build_cmd - transform the mid-layer scsi command to megaraid
+ * firmware lingua
+ * @adapter - controller's soft state
+ * @scp - mid-layer scsi command pointer
+ * @busy - set if request could not be completed because of lack of
+ * resources
+ *
+ * convert the command issued by mid-layer to format understood by megaraid
+ * firmware. We also complete certain command without sending them to firmware
+ */
+static scb_t *
+megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
+{
+ mraid_device_t *rdev = ADAP2RAIDDEV(adapter);
+ int channel;
+ int target;
+ int islogical;
+ mbox_ccb_t *ccb;
+ mraid_passthru_t *pthru;
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ scb_t *scb;
+ char skip[] = "skipping";
+ char scan[] = "scanning";
+ char *ss;
+
+
+ /*
+ * Get the appropriate device map for the device this command is
+ * intended for
+ */
+ MRAID_GET_DEVICE_MAP(adapter, scp, channel, target, islogical);
+
+ /*
+ * Logical drive commands
+ */
+ if (islogical) {
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ /*
+ * Do we support clustering and is the support enabled
+ * If no, return success always
+ */
+ if (!adapter->ha) {
+ scp->result = (DID_OK << 16);
+ return NULL;
+ }
+
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->dma_direction = scp->sc_data_direction;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ /*
+ * The command id will be provided by the command
+ * issuance routine
+ */
+ ccb->raw_mbox[0] = CLUSTER_CMD;
+ ccb->raw_mbox[2] = RESERVATION_STATUS;
+ ccb->raw_mbox[3] = target;
+
+ return scb;
+
+ case MODE_SENSE:
+ if (scp->use_sg) {
+ struct scatterlist *sgl;
+ caddr_t vaddr;
+
+ sgl = (struct scatterlist *)scp->request_buffer;
+ if (sgl->page) {
+ vaddr = (caddr_t)
+ (page_address((&sgl[0])->page)
+ + (&sgl[0])->offset);
+
+ memset(vaddr, 0, scp->cmnd[4]);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: invalid sg:%d\n",
+ __LINE__));
+ }
+ }
+ else {
+ memset(scp->request_buffer, 0, scp->cmnd[4]);
+ }
+ scp->result = (DID_OK << 16);
+ return NULL;
+
+ case INQUIRY:
+ /*
+ * Display the channel scan for logical drives
+ * Do not display scan for a channel if already done.
+ */
+ if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
+
+ con_log(CL_ANN, (KERN_INFO
+ "scsi[%d]: scanning scsi channel %d",
+ adapter->host->host_no,
+ SCP2CHANNEL(scp)));
+
+ con_log(CL_ANN, (
+ " [virtual] for logical drives\n"));
+
+ rdev->last_disp |= (1L << SCP2CHANNEL(scp));
+ }
+
+ /* Fall through */
+
+ case READ_CAPACITY:
+ /*
+ * Do not allow LUN > 0 for logical drives and
+ * requests for more than 40 logical drives
+ */
+ if (SCP2LUN(scp)) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+ if ((target % 0x80) >= MAX_LOGICAL_DRIVES_40LD) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+
+ /* Allocate a SCB and initialize passthru */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ pthru = ccb->pthru;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+
+ pthru->timeout = 0;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 1;
+ pthru->logdrv = target;
+ pthru->cdblen = scp->cmd_len;
+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
+
+ mbox->cmd = MBOXCMD_PASSTHRU64;
+ scb->dma_direction = scp->sc_data_direction;
+
+ pthru->dataxferlen = scp->request_bufflen;
+ pthru->dataxferaddr = ccb->sgl_dma_h;
+ pthru->numsge = megaraid_mbox_mksgl(adapter,
+ scb);
+
+ mbox->xferaddr = 0xFFFFFFFF;
+ mbox64->xferaddr_lo = (uint32_t )ccb->pthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+
+ return scb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+
+ /*
+ * Allocate a SCB and initialize mailbox
+ */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+ mbox->logdrv = target;
+
+ /*
+ * A little HACK: 2nd bit is zero for all scsi read
+ * commands and is set for all scsi write commands
+ */
+ mbox->cmd = (scp->cmnd[0] & 0x02) ? MBOXCMD_LWRITE64:
+ MBOXCMD_LREAD64 ;
+
+ /*
+ * 6-byte READ(0x08) or WRITE(0x0A) cdb
+ */
+ if (scp->cmd_len == 6) {
+ mbox->numsectors = (uint32_t)scp->cmnd[4];
+ mbox->lba =
+ ((uint32_t)scp->cmnd[1] << 16) |
+ ((uint32_t)scp->cmnd[2] << 8) |
+ (uint32_t)scp->cmnd[3];
+
+ mbox->lba &= 0x1FFFFF;
+ }
+
+ /*
+ * 10-byte READ(0x28) or WRITE(0x2A) cdb
+ */
+ else if (scp->cmd_len == 10) {
+ mbox->numsectors =
+ (uint32_t)scp->cmnd[8] |
+ ((uint32_t)scp->cmnd[7] << 8);
+ mbox->lba =
+ ((uint32_t)scp->cmnd[2] << 24) |
+ ((uint32_t)scp->cmnd[3] << 16) |
+ ((uint32_t)scp->cmnd[4] << 8) |
+ (uint32_t)scp->cmnd[5];
+ }
+
+ /*
+ * 12-byte READ(0xA8) or WRITE(0xAA) cdb
+ */
+ else if (scp->cmd_len == 12) {
+ mbox->lba =
+ ((uint32_t)scp->cmnd[2] << 24) |
+ ((uint32_t)scp->cmnd[3] << 16) |
+ ((uint32_t)scp->cmnd[4] << 8) |
+ (uint32_t)scp->cmnd[5];
+
+ mbox->numsectors =
+ ((uint32_t)scp->cmnd[6] << 24) |
+ ((uint32_t)scp->cmnd[7] << 16) |
+ ((uint32_t)scp->cmnd[8] << 8) |
+ (uint32_t)scp->cmnd[9];
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: unsupported CDB length\n"));
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ scp->result = (DID_ERROR << 16);
+ return NULL;
+ }
+
+ scb->dma_direction = scp->sc_data_direction;
+
+ // Calculate Scatter-Gather info
+ mbox64->xferaddr_lo = (uint32_t )ccb->sgl_dma_h;
+ mbox->numsge = megaraid_mbox_mksgl(adapter,
+ scb);
+ mbox->xferaddr = 0xFFFFFFFF;
+ mbox64->xferaddr_hi = 0;
+
+ return scb;
+
+ case RESERVE:
+ case RELEASE:
+ /*
+ * Do we support clustering and is the support enabled
+ */
+ if (!adapter->ha) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ /*
+ * Allocate a SCB and initialize mailbox
+ */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ ccb->raw_mbox[0] = CLUSTER_CMD;
+ ccb->raw_mbox[2] = (scp->cmnd[0] == RESERVE) ?
+ RESERVE_LD : RELEASE_LD;
+
+ ccb->raw_mbox[3] = target;
+ scb->dma_direction = scp->sc_data_direction;
+
+ return scb;
+
+ default:
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+ }
+ else { // Passthru device commands
+
+ // Do not allow access to target id > 15 or LUN > 7
+ if (target > 15 || SCP2LUN(scp) > 7) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ // if fast load option was set and scan for last device is
+ // over, reset the fast_load flag so that during a possible
+ // next scan, devices can be made available
+ if (rdev->fast_load && (target == 15) &&
+ (SCP2CHANNEL(scp) == adapter->max_channel -1)) {
+
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid[%d]: physical device scan re-enabled\n",
+ adapter->host->host_no));
+ rdev->fast_load = 0;
+ }
+
+ /*
+ * Display the channel scan for physical devices
+ */
+ if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
+
+ ss = rdev->fast_load ? skip : scan;
+
+ con_log(CL_ANN, (KERN_INFO
+ "scsi[%d]: %s scsi channel %d [Phy %d]",
+ adapter->host->host_no, ss, SCP2CHANNEL(scp),
+ channel));
+
+ con_log(CL_ANN, (
+ " for non-raid devices\n"));
+
+ rdev->last_disp |= (1L << SCP2CHANNEL(scp));
+ }
+
+ // disable channel sweep if fast load option given
+ if (rdev->fast_load) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ // Allocate a SCB and initialize passthru
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = channel;
+ scb->dev_target = target;
+ scb->dma_direction = scp->sc_data_direction;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+
+ // Does this firmware support extended CDBs
+ if (adapter->max_cdb_sz == 16) {
+ mbox->cmd = MBOXCMD_EXTPTHRU;
+
+ megaraid_mbox_prepare_epthru(adapter, scb, scp);
+
+ mbox64->xferaddr_lo = (uint32_t)ccb->epthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+ mbox->xferaddr = 0xFFFFFFFF;
+ }
+ else {
+ mbox->cmd = MBOXCMD_PASSTHRU64;
+
+ megaraid_mbox_prepare_pthru(adapter, scb, scp);
+
+ mbox64->xferaddr_lo = (uint32_t)ccb->pthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+ mbox->xferaddr = 0xFFFFFFFF;
+ }
+ return scb;
+ }
+
+ // NOT REACHED
+}
+
+
+/**
+ * megaraid_mbox_runpendq - execute commands queued in the pending queue
+ * @adapter : controller's soft state
+ * @scb : SCB to be queued in the pending list
+ *
+ * scan the pending list for commands which are not yet issued and try to
+ * post to the controller. The SCB can be a null pointer, which would indicate
+ * no SCB to be queue, just try to execute the ones in the pending list.
+ *
+ * NOTE: We do not actually traverse the pending list. The SCBs are plucked
+ * out from the head of the pending list. If it is successfully issued, the
+ * next SCB is at the head now.
+ */
+static void
+megaraid_mbox_runpendq(adapter_t *adapter, scb_t *scb_q)
+{
+ scb_t *scb;
+ unsigned long flags;
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+
+ if (scb_q) {
+ scb_q->state = SCB_PENDQ;
+ list_add_tail(&scb_q->list, &adapter->pend_list);
+ }
+
+ // if the adapter in not in quiescent mode, post the commands to FW
+ if (adapter->quiescent) {
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+ return;
+ }
+
+ while (!list_empty(&adapter->pend_list)) {
+
+ assert_spin_locked(PENDING_LIST_LOCK(adapter));
+
+ scb = list_entry(adapter->pend_list.next, scb_t, list);
+
+ // remove the scb from the pending list and try to
+ // issue. If we are unable to issue it, put back in
+ // the pending list and return
+
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+ // if mailbox was busy, return SCB back to pending
+ // list. Make sure to add at the head, since that's
+ // where it would have been removed from
+
+ scb->state = SCB_ISSUED;
+
+ if (mbox_post_cmd(adapter, scb) != 0) {
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_PENDQ;
+
+ list_add(&scb->list, &adapter->pend_list);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
+ flags);
+
+ return;
+ }
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ }
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_prepare_pthru - prepare a command for physical devices
+ * @adapter - pointer to controller's soft state
+ * @scb - scsi control block
+ * @scp - scsi command from the mid-layer
+ *
+ * prepare a command for the scsi physical devices
+ */
+static void
+megaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb,
+ struct scsi_cmnd *scp)
+{
+ mbox_ccb_t *ccb;
+ mraid_passthru_t *pthru;
+ uint8_t channel;
+ uint8_t target;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ pthru = ccb->pthru;
+ channel = scb->dev_channel;
+ target = scb->dev_target;
+
+ // 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
+ pthru->timeout = 4;
+ pthru->ars = 1;
+ pthru->islogical = 0;
+ pthru->channel = 0;
+ pthru->target = (channel << 4) | target;
+ pthru->logdrv = SCP2LUN(scp);
+ pthru->reqsenselen = 14;
+ pthru->cdblen = scp->cmd_len;
+
+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
+
+ if (scp->request_bufflen) {
+ pthru->dataxferlen = scp->request_bufflen;
+ pthru->dataxferaddr = ccb->sgl_dma_h;
+ pthru->numsge = megaraid_mbox_mksgl(adapter, scb);
+ }
+ else {
+ pthru->dataxferaddr = 0;
+ pthru->dataxferlen = 0;
+ pthru->numsge = 0;
+ }
+ return;
+}
+
+
+/**
+ * megaraid_mbox_prepare_epthru - prepare a command for physical devices
+ * @adapter - pointer to controller's soft state
+ * @scb - scsi control block
+ * @scp - scsi command from the mid-layer
+ *
+ * prepare a command for the scsi physical devices. This rountine prepares
+ * commands for devices which can take extended CDBs (>10 bytes)
+ */
+static void
+megaraid_mbox_prepare_epthru(adapter_t *adapter, scb_t *scb,
+ struct scsi_cmnd *scp)
+{
+ mbox_ccb_t *ccb;
+ mraid_epassthru_t *epthru;
+ uint8_t channel;
+ uint8_t target;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ epthru = ccb->epthru;
+ channel = scb->dev_channel;
+ target = scb->dev_target;
+
+ // 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
+ epthru->timeout = 4;
+ epthru->ars = 1;
+ epthru->islogical = 0;
+ epthru->channel = 0;
+ epthru->target = (channel << 4) | target;
+ epthru->logdrv = SCP2LUN(scp);
+ epthru->reqsenselen = 14;
+ epthru->cdblen = scp->cmd_len;
+
+ memcpy(epthru->cdb, scp->cmnd, scp->cmd_len);
+
+ if (scp->request_bufflen) {
+ epthru->dataxferlen = scp->request_bufflen;
+ epthru->dataxferaddr = ccb->sgl_dma_h;
+ epthru->numsge = megaraid_mbox_mksgl(adapter, scb);
+ }
+ else {
+ epthru->dataxferaddr = 0;
+ epthru->dataxferlen = 0;
+ epthru->numsge = 0;
+ }
+ return;
+}
+
+
+/**
+ * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs
+ * @adapter - controller's soft state
+ *
+ * Interrupt ackrowledgement sequence for memory mapped HBAs. Find out the
+ * completed command and put them on the completed list for later processing.
+ *
+ * Returns: 1 if the interrupt is valid, 0 otherwise
+ */
+static inline int
+megaraid_ack_sequence(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ scb_t *scb;
+ uint8_t nstatus;
+ uint8_t completed[MBOX_MAX_FIRMWARE_STATUS];
+ struct list_head clist;
+ int handled;
+ uint32_t dword;
+ unsigned long flags;
+ int i, j;
+
+
+ mbox = raid_dev->mbox;
+
+ // move the SCBs from the firmware completed array to our local list
+ INIT_LIST_HEAD(&clist);
+
+ // loop till F/W has more commands for us to complete
+ handled = 0;
+ spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
+ do {
+ /*
+ * Check if a valid interrupt is pending. If found, force the
+ * interrupt line low.
+ */
+ dword = RDOUTDOOR(raid_dev);
+ if (dword != 0x10001234) break;
+
+ handled = 1;
+
+ WROUTDOOR(raid_dev, 0x10001234);
+
+ nstatus = 0;
+ // wait for valid numstatus to post
+ for (i = 0; i < 0xFFFFF; i++) {
+ if (mbox->numstatus != 0xFF) {
+ nstatus = mbox->numstatus;
+ break;
+ }
+ rmb();
+ }
+ mbox->numstatus = 0xFF;
+
+ adapter->outstanding_cmds -= nstatus;
+
+ for (i = 0; i < nstatus; i++) {
+
+ // wait for valid command index to post
+ for (j = 0; j < 0xFFFFF; j++) {
+ if (mbox->completed[i] != 0xFF) break;
+ rmb();
+ }
+ completed[i] = mbox->completed[i];
+ mbox->completed[i] = 0xFF;
+
+ if (completed[i] == 0xFF) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: command posting timed out\n"));
+
+ BUG();
+ continue;
+ }
+
+ // Get SCB associated with this command id
+ if (completed[i] >= MBOX_MAX_SCSI_CMDS) {
+ // a cmm command
+ scb = adapter->uscb_list + (completed[i] -
+ MBOX_MAX_SCSI_CMDS);
+ }
+ else {
+ // an os command
+ scb = adapter->kscb_list + completed[i];
+ }
+
+ scb->status = mbox->status;
+ list_add_tail(&scb->list, &clist);
+ }
+
+ // Acknowledge interrupt
+ WRINDOOR(raid_dev, 0x02);
+
+ } while(1);
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+
+ // put the completed commands in the completed list. DPC would
+ // complete these commands later
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+
+ list_splice(&clist, &adapter->completed_list);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ // schedule the DPC if there is some work for it
+ if (handled)
+ tasklet_schedule(&adapter->dpc_h);
+
+ return handled;
+}
+
+
+/**
+ * megaraid_isr - isr for memory based mailbox based controllers
+ * @irq - irq
+ * @devp - pointer to our soft state
+ * @regs - unused
+ *
+ * Interrupt service routine for memory-mapped mailbox controllers.
+ */
+static irqreturn_t
+megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+{
+ adapter_t *adapter = devp;
+ int handled;
+
+ handled = megaraid_ack_sequence(adapter);
+
+ /* Loop through any pending requests */
+ if (!adapter->quiescent) {
+ megaraid_mbox_runpendq(adapter, NULL);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/**
+ * megaraid_mbox_sync_scb - sync kernel buffers
+ * @adapter : controller's soft state
+ * @scb : pointer to the resource packet
+ *
+ * DMA sync if required.
+ */
+static inline void
+megaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb)
+{
+ mbox_ccb_t *ccb;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ switch (scb->dma_type) {
+
+ case MRAID_DMA_WBUF:
+ if (scb->dma_direction == PCI_DMA_FROMDEVICE) {
+ pci_dma_sync_single_for_cpu(adapter->pdev,
+ ccb->buf_dma_h,
+ scb->scp->request_bufflen,
+ PCI_DMA_FROMDEVICE);
+ }
+
+ pci_unmap_page(adapter->pdev, ccb->buf_dma_h,
+ scb->scp->request_bufflen, scb->dma_direction);
+
+ break;
+
+ case MRAID_DMA_WSG:
+ if (scb->dma_direction == PCI_DMA_FROMDEVICE) {
+ pci_dma_sync_sg_for_cpu(adapter->pdev,
+ scb->scp->request_buffer,
+ scb->scp->use_sg, PCI_DMA_FROMDEVICE);
+ }
+
+ pci_unmap_sg(adapter->pdev, scb->scp->request_buffer,
+ scb->scp->use_sg, scb->dma_direction);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_dpc - the tasklet to complete the commands from completed list
+ * @devp : pointer to HBA soft state
+ *
+ * Pick up the commands from the completed list and send back to the owners.
+ * This is a reentrant function and does not assume any locks are held while
+ * it is being called.
+ */
+static void
+megaraid_mbox_dpc(unsigned long devp)
+{
+ adapter_t *adapter = (adapter_t *)devp;
+ mraid_device_t *raid_dev;
+ struct list_head clist;
+ struct scatterlist *sgl;
+ scb_t *scb;
+ scb_t *tmp;
+ struct scsi_cmnd *scp;
+ mraid_passthru_t *pthru;
+ mraid_epassthru_t *epthru;
+ mbox_ccb_t *ccb;
+ int islogical;
+ int pdev_index;
+ int pdev_state;
+ mbox_t *mbox;
+ unsigned long flags;
+ uint8_t c;
+ int status;
+
+
+ if (!adapter) return;
+
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ // move the SCBs from the completed list to our local list
+ INIT_LIST_HEAD(&clist);
+
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+
+ list_splice_init(&adapter->completed_list, &clist);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ list_for_each_entry_safe(scb, tmp, &clist, list) {
+
+ status = scb->status;
+ scp = scb->scp;
+ ccb = (mbox_ccb_t *)scb->ccb;
+ pthru = ccb->pthru;
+ epthru = ccb->epthru;
+ mbox = ccb->mbox;
+
+ // Make sure f/w has completed a valid command
+ if (scb->state != SCB_ISSUED) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid critical err: invalid command %d:%d:%p\n",
+ scb->sno, scb->state, scp));
+ BUG();
+ continue; // Must never happen!
+ }
+
+ // check for the management command and complete it right away
+ if (scb->sno >= MBOX_MAX_SCSI_CMDS) {
+ scb->state = SCB_FREE;
+ scb->status = status;
+
+ // remove from local clist
+ list_del_init(&scb->list);
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ continue;
+ }
+
+ // Was an abort issued for this command earlier
+ if (scb->state & SCB_ABORT) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: aborted cmd %lx[%x] completed\n",
+ scp->serial_number, scb->sno));
+ }
+
+ /*
+ * If the inquiry came of a disk drive which is not part of
+ * any RAID array, expose it to the kernel. For this to be
+ * enabled, user must set the "megaraid_expose_unconf_disks"
+ * flag to 1 by specifying it on module parameter list.
+ * This would enable data migration off drives from other
+ * configurations.
+ */
+ islogical = MRAID_IS_LOGICAL(adapter, scp);
+ if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0
+ && IS_RAID_CH(raid_dev, scb->dev_channel)) {
+
+ if (scp->use_sg) {
+ sgl = (struct scatterlist *)
+ scp->request_buffer;
+
+ if (sgl->page) {
+ c = *(unsigned char *)
+ (page_address((&sgl[0])->page) +
+ (&sgl[0])->offset);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: invalid sg:%d\n",
+ __LINE__));
+ c = 0;
+ }
+ }
+ else {
+ c = *(uint8_t *)scp->request_buffer;
+ }
+
+ if ((c & 0x1F ) == TYPE_DISK) {
+ pdev_index = (scb->dev_channel * 16) +
+ scb->dev_target;
+ pdev_state =
+ raid_dev->pdrv_state[pdev_index] & 0x0F;
+
+ if (pdev_state == PDRV_ONLINE ||
+ pdev_state == PDRV_FAILED ||
+ pdev_state == PDRV_RBLD ||
+ pdev_state == PDRV_HOTSPARE ||
+ megaraid_expose_unconf_disks == 0) {
+
+ status = 0xF0;
+ }
+ }
+ }
+
+ // Convert MegaRAID status to Linux error code
+ switch (status) {
+
+ case 0x00:
+
+ scp->result = (DID_OK << 16);
+ break;
+
+ case 0x02:
+
+ /* set sense_buffer and result fields */
+ if (mbox->cmd == MBOXCMD_PASSTHRU ||
+ mbox->cmd == MBOXCMD_PASSTHRU64) {
+
+ memcpy(scp->sense_buffer, pthru->reqsensearea,
+ 14);
+
+ scp->result = DRIVER_SENSE << 24 |
+ DID_OK << 16 | CHECK_CONDITION << 1;
+ }
+ else {
+ if (mbox->cmd == MBOXCMD_EXTPTHRU) {
+
+ memcpy(scp->sense_buffer,
+ epthru->reqsensearea, 14);
+
+ scp->result = DRIVER_SENSE << 24 |
+ DID_OK << 16 |
+ CHECK_CONDITION << 1;
+ } else {
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = ABORTED_COMMAND;
+ scp->result = CHECK_CONDITION << 1;
+ }
+ }
+ break;
+
+ case 0x08:
+
+ scp->result = DID_BUS_BUSY << 16 | status;
+ break;
+
+ default:
+
+ /*
+ * If TEST_UNIT_READY fails, we know RESERVATION_STATUS
+ * failed
+ */
+ if (scp->cmnd[0] == TEST_UNIT_READY) {
+ scp->result = DID_ERROR << 16 |
+ RESERVATION_CONFLICT << 1;
+ }
+ else
+ /*
+ * Error code returned is 1 if Reserve or Release
+ * failed or the input parameter is invalid
+ */
+ if (status == 1 && (scp->cmnd[0] == RESERVE ||
+ scp->cmnd[0] == RELEASE)) {
+
+ scp->result = DID_ERROR << 16 |
+ RESERVATION_CONFLICT << 1;
+ }
+ else {
+ scp->result = DID_BAD_TARGET << 16 | status;
+ }
+ }
+
+ // print a debug message for all failed commands
+ if (status) {
+ megaraid_mbox_display_scb(adapter, scb);
+ }
+
+ // Free our internal resources and call the mid-layer callback
+ // routine
+ megaraid_mbox_sync_scb(adapter, scb);
+
+ // remove from local clist
+ list_del_init(&scb->list);
+
+ // put back in free list
+ megaraid_dealloc_scb(adapter, scb);
+
+ // send the scsi packet back to kernel
+ spin_lock(adapter->host_lock);
+ scp->scsi_done(scp);
+ spin_unlock(adapter->host_lock);
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_abort_handler - abort the scsi command
+ * @scp : command to be aborted
+ *
+ * Abort a previous SCSI request. Only commands on the pending list can be
+ * aborted. All the commands issued to the F/W must complete.
+ **/
+static int
+megaraid_abort_handler(struct scsi_cmnd *scp)
+{
+ adapter_t *adapter;
+ mraid_device_t *raid_dev;
+ scb_t *scb;
+ scb_t *tmp;
+ int found;
+ unsigned long flags;
+ int i;
+
+
+ adapter = SCP2ADAPTER(scp);
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ assert_spin_locked(adapter->host_lock);
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: aborting-%ld cmd=%x <c=%d t=%d l=%d>\n",
+ scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp),
+ SCP2TARGET(scp), SCP2LUN(scp)));
+
+ // If FW has stopped responding, simply return failure
+ if (raid_dev->hw_error) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: hw error, not aborting\n"));
+ return FAILED;
+ }
+
+ // There might a race here, where the command was completed by the
+ // firmware and now it is on the completed list. Before we could
+ // complete the command to the kernel in dpc, the abort came.
+ // Find out if this is the case to avoid the race.
+ scb = NULL;
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) {
+
+ if (scb->scp == scp) { // Found command
+
+ list_del_init(&scb->list); // from completed list
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: %ld:%d[%d:%d], abort from completed list\n",
+ scp->serial_number, scb->sno,
+ scb->dev_channel, scb->dev_target));
+
+ scp->result = (DID_ABORT << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter),
+ flags);
+
+ return SUCCESS;
+ }
+ }
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ // Find out if this command is still on the pending list. If it is and
+ // was never issued, abort and return success. If the command is owned
+ // by the firmware, we must wait for it to complete by the FW.
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
+
+ if (scb->scp == scp) { // Found command
+
+ list_del_init(&scb->list); // from pending list
+
+ ASSERT(!(scb->state & SCB_ISSUED));
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld[%d:%d], driver owner\n",
+ scp->serial_number, scb->dev_channel,
+ scb->dev_target));
+
+ scp->result = (DID_ABORT << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
+ flags);
+
+ return SUCCESS;
+ }
+ }
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+
+ // Check do we even own this command, in which case this would be
+ // owned by the firmware. The only way to locate the FW scb is to
+ // traverse through the list of all SCB, since driver does not
+ // maintain these SCBs on any list
+ found = 0;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ scb = adapter->kscb_list + i;
+
+ if (scb->scp == scp) {
+
+ found = 1;
+
+ if (!(scb->state & SCB_ISSUED)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld%d[%d:%d], invalid state\n",
+ scp->serial_number, scb->sno, scb->dev_channel,
+ scb->dev_target));
+ BUG();
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld:%d[%d:%d], fw owner\n",
+ scp->serial_number, scb->sno, scb->dev_channel,
+ scb->dev_target));
+ }
+ }
+ }
+
+ if (!found) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: scsi cmd:%ld, do now own\n",
+ scp->serial_number));
+
+ // FIXME: Should there be a callback for this command?
+ return SUCCESS;
+ }
+
+ // We cannot actually abort a command owned by firmware, return
+ // failure and wait for reset. In host reset handler, we will find out
+ // if the HBA is still live
+ return FAILED;
+}
+
+
+/**
+ * megaraid_reset_handler - device reset hadler for mailbox based driver
+ * @scp : reference command
+ *
+ * Reset handler for the mailbox based controller. First try to find out if
+ * the FW is still live, in which case the outstanding commands counter mut go
+ * down to 0. If that happens, also issue the reservation reset command to
+ * relinquish (possible) reservations on the logical drives connected to this
+ * host
+ **/
+static int
+megaraid_reset_handler(struct scsi_cmnd *scp)
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ scb_t *tmp;
+ mraid_device_t *raid_dev;
+ unsigned long flags;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+ int recovery_window;
+ int recovering;
+ int i;
+
+ adapter = SCP2ADAPTER(scp);
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ assert_spin_locked(adapter->host_lock);
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: reseting the host...\n"));
+
+ // return failure if adapter is not responding
+ if (raid_dev->hw_error) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: hw error, cannot reset\n"));
+ return FAILED;
+ }
+
+
+ // Under exceptional conditions, FW can take up to 3 minutes to
+ // complete command processing. Wait for additional 2 minutes for the
+ // pending commands counter to go down to 0. If it doesn't, let the
+ // controller be marked offline
+ // Also, reset all the commands currently owned by the driver
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
+
+ list_del_init(&scb->list); // from pending list
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: %ld:%d[%d:%d], reset from pending list\n",
+ scp->serial_number, scb->sno,
+ scb->dev_channel, scb->dev_target));
+
+ scp->result = (DID_RESET << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+ }
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+ if (adapter->outstanding_cmds) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: %d outstanding commands. Max wait %d sec\n",
+ adapter->outstanding_cmds, MBOX_RESET_WAIT));
+ }
+
+ spin_unlock(adapter->host_lock);
+
+ recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT;
+
+ recovering = adapter->outstanding_cmds;
+
+ for (i = 0; i < recovery_window && adapter->outstanding_cmds; i++) {
+
+ megaraid_ack_sequence(adapter);
+
+ // print a message once every 5 seconds only
+ if (!(i % 5)) {
+ con_log(CL_ANN, (
+ "megaraid mbox: Wait for %d commands to complete:%d\n",
+ adapter->outstanding_cmds,
+ MBOX_RESET_WAIT - i));
+ }
+
+ // bailout if no recovery happended in reset time
+ if ((i == MBOX_RESET_WAIT) &&
+ (recovering == adapter->outstanding_cmds)) {
+ break;
+ }
+
+ msleep(1000);
+ }
+
+ spin_lock(adapter->host_lock);
+
+ // If still outstanding commands, bail out
+ if (adapter->outstanding_cmds) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: critical hardware error!\n"));
+
+ raid_dev->hw_error = 1;
+
+ return FAILED;
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: reset sequence completed sucessfully\n"));
+ }
+
+
+ // If the controller supports clustering, reset reservations
+ if (!adapter->ha) return SUCCESS;
+
+ // clear reservations if any
+ raw_mbox[0] = CLUSTER_CMD;
+ raw_mbox[2] = RESET_RESERVATIONS;
+
+ rval = SUCCESS;
+ if (mbox_post_sync_cmd_fast(adapter, raw_mbox) == 0) {
+ con_log(CL_ANN,
+ (KERN_INFO "megaraid: reservation reset\n"));
+ }
+ else {
+ rval = FAILED;
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: reservation reset failed\n"));
+ }
+
+ return rval;
+}
+
+
+/*
+ * START: internal commands library
+ *
+ * This section of the driver has the common routine used by the driver and
+ * also has all the FW routines
+ */
+
+/**
+ * mbox_post_sync_cmd() - blocking command to the mailbox based controllers
+ * @adapter - controller's soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode for mailbox based
+ * controllers
+ */
+static int
+mbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[])
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ uint8_t status;
+ int i;
+
+
+ mbox64 = raid_dev->mbox64;
+ mbox = raid_dev->mbox;
+
+ /*
+ * Wait until mailbox is free
+ */
+ if (megaraid_busywait_mbox(raid_dev) != 0)
+ goto blocked_mailbox;
+
+ /*
+ * Copy mailbox data into host structure
+ */
+ memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16);
+ mbox->cmdid = 0xFE;
+ mbox->busy = 1;
+ mbox->poll = 0;
+ mbox->ack = 0;
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+
+ wmb();
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ // wait for maximum 1 second for status to post. If the status is not
+ // available within 1 second, assume FW is initializing and wait
+ // for an extended amount of time
+ if (mbox->numstatus == 0xFF) { // status not yet available
+ udelay(25);;
+
+ for (i = 0; mbox->numstatus == 0xFF && i < 1000; i++) {
+ rmb();
+ msleep(1);
+ }
+
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mailbox: wait for FW to boot "));
+
+ for (i = 0; (mbox->numstatus == 0xFF) &&
+ (i < MBOX_RESET_WAIT); i++) {
+ rmb();
+ con_log(CL_ANN, ("\b\b\b\b\b[%03d]",
+ MBOX_RESET_WAIT - i));
+ msleep(1000);
+ }
+
+ if (i == MBOX_RESET_WAIT) {
+
+ con_log(CL_ANN, (
+ "\nmegaraid mailbox: status not available\n"));
+
+ return -1;
+ }
+ con_log(CL_ANN, ("\b\b\b\b\b[ok] \n"));
+ }
+ }
+
+ // wait for maximum 1 second for poll semaphore
+ if (mbox->poll != 0x77) {
+ udelay(25);
+
+ for (i = 0; (mbox->poll != 0x77) && (i < 1000); i++) {
+ rmb();
+ msleep(1);
+ }
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: could not get poll semaphore\n"));
+ return -1;
+ }
+ }
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
+ wmb();
+
+ // wait for maximum 1 second for acknowledgement
+ if (RDINDOOR(raid_dev) & 0x2) {
+ udelay(25);
+
+ for (i = 0; (RDINDOOR(raid_dev) & 0x2) && (i < 1000); i++) {
+ rmb();
+ msleep(1);
+ }
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: could not acknowledge\n"));
+ return -1;
+ }
+ }
+ mbox->poll = 0;
+ mbox->ack = 0x77;
+
+ status = mbox->status;
+
+ // invalidate the completed command id array. After command
+ // completion, firmware would write the valid id.
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+ for (i = 0; i < MBOX_MAX_FIRMWARE_STATUS; i++) {
+ mbox->completed[i] = 0xFF;
+ }
+
+ return status;
+
+blocked_mailbox:
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n") );
+ return -1;
+}
+
+
+/**
+ * mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers
+ * @adapter - controller's soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode for mailbox based
+ * controllers. This is a faster version of the synchronous command and
+ * therefore can be called in interrupt-context as well
+ */
+static int
+mbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[])
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ long i;
+
+
+ mbox = raid_dev->mbox;
+
+ // return immediately if the mailbox is busy
+ if (mbox->busy) return -1;
+
+ // Copy mailbox data into host structure
+ memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 14);
+ mbox->cmdid = 0xFE;
+ mbox->busy = 1;
+ mbox->poll = 0;
+ mbox->ack = 0;
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+
+ wmb();
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ for (i = 0; i < 0xFFFFF; i++) {
+ if (mbox->numstatus != 0xFF) break;
+ }
+
+ if (i == 0xFFFFF) {
+ // We may need to re-calibrate the counter
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: fast sync command timed out\n"));
+ }
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
+ wmb();
+
+ return mbox->status;
+}
+
+
+/**
+ * megaraid_busywait_mbox() - Wait until the controller's mailbox is available
+ * @raid_dev - RAID device (HBA) soft state
+ *
+ * wait until the controller's mailbox is available to accept more commands.
+ * wait for at most 1 second
+ */
+static int
+megaraid_busywait_mbox(mraid_device_t *raid_dev)
+{
+ mbox_t *mbox = raid_dev->mbox;
+ int i = 0;
+
+ if (mbox->busy) {
+ udelay(25);
+ for (i = 0; mbox->busy && i < 1000; i++)
+ msleep(1);
+ }
+
+ if (i < 1000) return 0;
+ else return -1;
+}
+
+
+/**
+ * megaraid_mbox_product_info - some static information about the controller
+ * @adapter - our soft state
+ *
+ * issue commands to the controller to grab some parameters required by our
+ * caller.
+ */
+static int
+megaraid_mbox_product_info(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ mraid_pinfo_t *pinfo;
+ dma_addr_t pinfo_dma_h;
+ mraid_inquiry3_t *mraid_inq3;
+ int i;
+
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox = (mbox_t *)raw_mbox;
+
+ /*
+ * Issue an ENQUIRY3 command to find out certain adapter parameters,
+ * e.g., max channels, max commands etc.
+ */
+ pinfo = pci_alloc_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ &pinfo_dma_h);
+
+ if (pinfo == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ return -1;
+ }
+ memset(pinfo, 0, sizeof(mraid_pinfo_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = FC_NEW_CONFIG;
+ raw_mbox[2] = NC_SUBOP_ENQUIRY3;
+ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL;
+
+ // Issue the command
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n"));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ pinfo, pinfo_dma_h);
+
+ return -1;
+ }
+
+ /*
+ * Collect information about state of each physical drive
+ * attached to the controller. We will expose all the disks
+ * which are not part of RAID
+ */
+ mraid_inq3 = (mraid_inquiry3_t *)adapter->ibuf;
+ for (i = 0; i < MBOX_MAX_PHYSICAL_DRIVES; i++) {
+ raid_dev->pdrv_state[i] = mraid_inq3->pdrv_state[i];
+ }
+
+ /*
+ * Get product info for information like number of channels,
+ * maximum commands supported.
+ */
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox->xferaddr = (uint32_t)pinfo_dma_h;
+
+ raw_mbox[0] = FC_NEW_CONFIG;
+ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: product info failed\n"));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ pinfo, pinfo_dma_h);
+
+ return -1;
+ }
+
+ /*
+ * Setup some parameters for host, as required by our caller
+ */
+ adapter->max_channel = pinfo->nchannels;
+
+ /*
+ * we will export all the logical drives on a single channel.
+ * Add 1 since inquires do not come for inititor ID
+ */
+ adapter->max_target = MAX_LOGICAL_DRIVES_40LD + 1;
+ adapter->max_lun = 8; // up to 8 LUNs for non-disk devices
+
+ /*
+ * These are the maximum outstanding commands for the scsi-layer
+ */
+ adapter->max_cmds = MBOX_MAX_SCSI_CMDS;
+
+ memset(adapter->fw_version, 0, VERSION_SIZE);
+ memset(adapter->bios_version, 0, VERSION_SIZE);
+
+ memcpy(adapter->fw_version, pinfo->fw_version, 4);
+ adapter->fw_version[4] = 0;
+
+ memcpy(adapter->bios_version, pinfo->bios_version, 4);
+ adapter->bios_version[4] = 0;
+
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: fw version:[%s] bios version:[%s]\n",
+ adapter->fw_version, adapter->bios_version));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), pinfo,
+ pinfo_dma_h);
+
+ return 0;
+}
+
+
+
+/**
+ * megaraid_mbox_extended_cdb - check for support for extended CDBs
+ * @adapter - soft state for the controller
+ *
+ * this routine check whether the controller in question supports extended
+ * ( > 10 bytes ) CDBs
+ */
+static int
+megaraid_mbox_extended_cdb(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = MAIN_MISC_OPCODE;
+ raw_mbox[2] = SUPPORT_EXT_CDB;
+
+ /*
+ * Issue the command
+ */
+ rval = 0;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ rval = -1;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_support_ha - Do we support clustering
+ * @adapter - soft state for the controller
+ * @init_id - ID of the initiator
+ *
+ * Determine if the firmware supports clustering and the ID of the initiator.
+ */
+static int
+megaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = GET_TARGET_ID;
+
+ // Issue the command
+ *init_id = 7;
+ rval = -1;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+
+ *init_id = *(uint8_t *)adapter->ibuf;
+
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid: cluster firmware, initiator ID: %d\n",
+ *init_id));
+
+ rval = 0;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_support_random_del - Do we support random deletion
+ * @adapter - soft state for the controller
+ *
+ * Determine if the firmware supports random deletion
+ * Return: 1 is operation supported, 0 otherwise
+ */
+static int
+megaraid_mbox_support_random_del(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ raw_mbox[0] = FC_DEL_LOGDRV;
+ raw_mbox[2] = OP_SUP_DEL_LOGDRV;
+
+ // Issue the command
+ rval = 0;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+
+ con_log(CL_DLEVEL1, ("megaraid: supports random deletion\n"));
+
+ rval = 1;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware
+ * @adapter - soft state for the controller
+ *
+ * Find out the maximum number of scatter-gather elements supported by the
+ * firmware
+ */
+static int
+megaraid_mbox_get_max_sg(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int nsg;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = MAIN_MISC_OPCODE;
+ raw_mbox[2] = GET_MAX_SG_SUPPORT;
+
+ // Issue the command
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+ nsg = *(uint8_t *)adapter->ibuf;
+ }
+ else {
+ nsg = MBOX_DEFAULT_SG_SIZE;
+ }
+
+ if (nsg > MBOX_MAX_SG_SIZE) nsg = MBOX_MAX_SG_SIZE;
+
+ return nsg;
+}
+
+
+/**
+ * megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels
+ * @adapter - soft state for the controller
+ *
+ * Enumerate the RAID and SCSI channels for ROMB platoforms so that channels
+ * can be exported as regular SCSI channels
+ */
+static void
+megaraid_mbox_enum_raid_scsi(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = CHNL_CLASS;
+ raw_mbox[2] = GET_CHNL_CLASS;
+
+ // Issue the command. If the command fails, all channels are RAID
+ // channels
+ raid_dev->channel_class = 0xFF;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+ raid_dev->channel_class = *(uint8_t *)adapter->ibuf;
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_flush_cache - flush adapter and disks cache
+ * @param adapter : soft state for the controller
+ *
+ * Flush adapter cache followed by disks cache
+ */
+static void
+megaraid_mbox_flush_cache(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ raw_mbox[0] = FLUSH_ADAPTER;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ con_log(CL_ANN, ("megaraid: flush adapter failed\n"));
+ }
+
+ raw_mbox[0] = FLUSH_SYSTEM;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ con_log(CL_ANN, ("megaraid: flush disks cache failed\n"));
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_display_scb - display SCB information, mostly debug purposes
+ * @param adapter : controllers' soft state
+ * @param scb : SCB to be displayed
+ * @param level : debug level for console print
+ *
+ * Diplay information about the given SCB iff the current debug level is
+ * verbose
+ */
+static void
+megaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb)
+{
+ mbox_ccb_t *ccb;
+ struct scsi_cmnd *scp;
+ mbox_t *mbox;
+ int level;
+ int i;
+
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scp = scb->scp;
+ mbox = ccb->mbox;
+
+ level = CL_DLEVEL3;
+
+ con_log(level, (KERN_NOTICE
+ "megaraid mailbox: status:%#x cmd:%#x id:%#x ", scb->status,
+ mbox->cmd, scb->sno));
+
+ con_log(level, ("sec:%#x lba:%#x addr:%#x ld:%d sg:%d\n",
+ mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv,
+ mbox->numsge));
+
+ if (!scp) return;
+
+ con_log(level, (KERN_NOTICE "scsi cmnd: "));
+
+ for (i = 0; i < scp->cmd_len; i++) {
+ con_log(level, ("%#2.02x ", scp->cmnd[i]));
+ }
+
+ con_log(level, ("\n"));
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_setup_device_map - manage device ids
+ * @adapter : Driver's soft state
+ *
+ * Manange the device ids to have an appropraite mapping between the kernel
+ * scsi addresses and megaraid scsi and logical drive addresses. We export
+ * scsi devices on their actual addresses, whereas the logical drives are
+ * exported on a virtual scsi channel.
+ **/
+static void
+megaraid_mbox_setup_device_map(adapter_t *adapter)
+{
+ uint8_t c;
+ uint8_t t;
+
+ /*
+ * First fill the values on the logical drive channel
+ */
+ for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
+ adapter->device_ids[adapter->max_channel][t] =
+ (t < adapter->init_id) ? t : t - 1;
+
+ adapter->device_ids[adapter->max_channel][adapter->init_id] = 0xFF;
+
+ /*
+ * Fill the values on the physical devices channels
+ */
+ for (c = 0; c < adapter->max_channel; c++)
+ for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
+ adapter->device_ids[c][t] = (c << 8) | t;
+}
+
+
+/*
+ * END: internal commands library
+ */
+
+/*
+ * START: Interface for the common management module
+ *
+ * This is the module, which interfaces with the common mangement module to
+ * provide support for ioctl and sysfs
+ */
+
+/**
+ * megaraid_cmm_register - register with the mangement module
+ * @param adapter : HBA soft state
+ *
+ * Register with the management module, which allows applications to issue
+ * ioctl calls to the drivers. This interface is used by the management module
+ * to setup sysfs support as well.
+ */
+static int
+megaraid_cmm_register(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mraid_mmadp_t adp;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ int rval;
+ int i;
+
+ // Allocate memory for the base list of scb for management module.
+ adapter->uscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_USER_CMDS,
+ GFP_KERNEL);
+
+ if (adapter->uscb_list == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ return -1;
+ }
+ memset(adapter->uscb_list, 0, sizeof(scb_t) * MBOX_MAX_USER_CMDS);
+
+
+ // Initialize the synchronization parameters for resources for
+ // commands for management module
+ INIT_LIST_HEAD(&adapter->uscb_pool);
+
+ spin_lock_init(USER_FREE_LIST_LOCK(adapter));
+
+
+
+ // link all the packets. Note, CCB for commands, coming from the
+ // commom management module, mailbox physical address are already
+ // setup by it. We just need placeholder for that in our local command
+ // control blocks
+ for (i = 0; i < MBOX_MAX_USER_CMDS; i++) {
+
+ scb = adapter->uscb_list + i;
+ ccb = raid_dev->uccb_list + i;
+
+ scb->ccb = (caddr_t)ccb;
+ ccb->mbox64 = raid_dev->umbox64 + i;
+ ccb->mbox = &ccb->mbox64->mbox32;
+ ccb->raw_mbox = (uint8_t *)ccb->mbox;
+
+ scb->gp = 0;
+
+ // COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR
+ // COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER)
+ scb->sno = i + MBOX_MAX_SCSI_CMDS;
+
+ scb->scp = NULL;
+ scb->state = SCB_FREE;
+ scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dev_channel = -1;
+ scb->dev_target = -1;
+
+ // put scb in the free pool
+ list_add_tail(&scb->list, &adapter->uscb_pool);
+ }
+
+ adp.unique_id = adapter->unique_id;
+ adp.drvr_type = DRVRTYPE_MBOX;
+ adp.drvr_data = (unsigned long)adapter;
+ adp.pdev = adapter->pdev;
+ adp.issue_uioc = megaraid_mbox_mm_handler;
+ adp.timeout = 300;
+ adp.max_kioc = MBOX_MAX_USER_CMDS;
+
+ if ((rval = mraid_mm_register_adp(&adp)) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: did not register with CMM\n"));
+
+ kfree(adapter->uscb_list);
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_cmm_unregister - un-register with the mangement module
+ * @param adapter : HBA soft state
+ *
+ * Un-register with the management module.
+ * FIXME: mgmt module must return failure for unregister if it has pending
+ * commands in LLD
+ */
+static int
+megaraid_cmm_unregister(adapter_t *adapter)
+{
+ kfree(adapter->uscb_list);
+ mraid_mm_unregister_adp(adapter->unique_id);
+ return 0;
+}
+
+
+/**
+ * megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD
+ * @param drvr_data : LLD specific data
+ * @param kioc : CMM interface packet
+ * @param action : command action
+ *
+ * This routine is invoked whenever the Common Mangement Module (CMM) has a
+ * command for us. The 'action' parameter specifies if this is a new command
+ * or otherwise.
+ */
+static int
+megaraid_mbox_mm_handler(unsigned long drvr_data, uioc_t *kioc, uint32_t action)
+{
+ adapter_t *adapter;
+
+ if (action != IOCTL_ISSUE) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: unsupported management action:%#2x\n",
+ action));
+ return (-ENOTSUPP);
+ }
+
+ adapter = (adapter_t *)drvr_data;
+
+ // make sure this adapter is not being detached right now.
+ if (atomic_read(&adapter->being_detached)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: reject management request, detaching\n"));
+ return (-ENODEV);
+ }
+
+ switch (kioc->opcode) {
+
+ case GET_ADAP_INFO:
+
+ kioc->status = gather_hbainfo(adapter, (mraid_hba_info_t *)
+ (unsigned long)kioc->buf_vaddr);
+
+ kioc->done(kioc);
+
+ return kioc->status;
+
+ case MBOX_CMD:
+
+ return megaraid_mbox_mm_command(adapter, kioc);
+
+ default:
+ kioc->status = (-EINVAL);
+ kioc->done(kioc);
+ return (-EINVAL);
+ }
+
+ return 0; // not reached
+}
+
+/**
+ * megaraid_mbox_mm_command - issues commands routed through CMM
+ * @param adapter : HBA soft state
+ * @param kioc : management command packet
+ *
+ * Issues commands, which are routed through the management module.
+ */
+static int
+megaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc)
+{
+ struct list_head *head = &adapter->uscb_pool;
+ mbox64_t *mbox64;
+ uint8_t *raw_mbox;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ unsigned long flags;
+
+ // detach one scb from free pool
+ spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
+
+ if (list_empty(head)) { // should never happen because of CMM
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: bug in cmm handler, lost resources\n"));
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ return (-EINVAL);
+ }
+
+ scb = list_entry(head->next, scb_t, list);
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_ACTIVE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dma_direction = PCI_DMA_NONE;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+ raw_mbox = (uint8_t *)&mbox64->mbox32;
+
+ memcpy(ccb->mbox64, mbox64, sizeof(mbox64_t));
+
+ scb->gp = (unsigned long)kioc;
+
+ /*
+ * If it is a logdrv random delete operation, we have to wait till
+ * there are no outstanding cmds at the fw and then issue it directly
+ */
+ if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
+
+ if (wait_till_fw_empty(adapter)) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: LD delete, timed out\n"));
+
+ kioc->status = -ETIME;
+
+ scb->status = -1;
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ return (-ETIME);
+ }
+
+ INIT_LIST_HEAD(&scb->list);
+
+ scb->state = SCB_ISSUED;
+ if (mbox_post_cmd(adapter, scb) != 0) {
+
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: LD delete, mailbox busy\n"));
+
+ kioc->status = -EBUSY;
+
+ scb->status = -1;
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ return (-EBUSY);
+ }
+
+ return 0;
+ }
+
+ // put the command on the pending list and execute
+ megaraid_mbox_runpendq(adapter, scb);
+
+ return 0;
+}
+
+
+static int
+wait_till_fw_empty(adapter_t *adapter)
+{
+ unsigned long flags = 0;
+ int i;
+
+
+ /*
+ * Set the quiescent flag to stop issuing cmds to FW.
+ */
+ spin_lock_irqsave(adapter->host_lock, flags);
+ adapter->quiescent++;
+ spin_unlock_irqrestore(adapter->host_lock, flags);
+
+ /*
+ * Wait till there are no more cmds outstanding at FW. Try for at most
+ * 60 seconds
+ */
+ for (i = 0; i < 60 && adapter->outstanding_cmds; i++) {
+ con_log(CL_DLEVEL1, (KERN_INFO
+ "megaraid: FW has %d pending commands\n",
+ adapter->outstanding_cmds));
+
+ msleep(1000);
+ }
+
+ return adapter->outstanding_cmds;
+}
+
+
+/**
+ * megaraid_mbox_mm_done - callback for CMM commands
+ * @adapter : HBA soft state
+ * @scb : completed command
+ *
+ * Callback routine for internal commands originated from the management
+ * module.
+ */
+static void
+megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb)
+{
+ uioc_t *kioc;
+ mbox64_t *mbox64;
+ uint8_t *raw_mbox;
+ unsigned long flags;
+
+ kioc = (uioc_t *)scb->gp;
+ kioc->status = 0;
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+ mbox64->mbox32.status = scb->status;
+ raw_mbox = (uint8_t *)&mbox64->mbox32;
+
+
+ // put scb in the free pool
+ scb->state = SCB_FREE;
+ scb->scp = NULL;
+
+ spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
+
+ list_add(&scb->list, &adapter->uscb_pool);
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ // if a delete logical drive operation succeeded, restart the
+ // controller
+ if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
+
+ adapter->quiescent--;
+
+ megaraid_mbox_runpendq(adapter, NULL);
+ }
+
+ kioc->done(kioc);
+
+ return;
+}
+
+
+/**
+ * gather_hbainfo - HBA characteristics for the applications
+ * @param adapter : HBA soft state
+ * @param hinfo : pointer to the caller's host info strucuture
+ */
+static int
+gather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo)
+{
+ uint8_t dmajor;
+
+ dmajor = megaraid_mbox_version[0];
+
+ hinfo->pci_vendor_id = adapter->pdev->vendor;
+ hinfo->pci_device_id = adapter->pdev->device;
+ hinfo->subsys_vendor_id = adapter->pdev->subsystem_vendor;
+ hinfo->subsys_device_id = adapter->pdev->subsystem_device;
+
+ hinfo->pci_bus = adapter->pdev->bus->number;
+ hinfo->pci_dev_fn = adapter->pdev->devfn;
+ hinfo->pci_slot = PCI_SLOT(adapter->pdev->devfn);
+ hinfo->irq = adapter->host->irq;
+ hinfo->baseport = ADAP2RAIDDEV(adapter)->baseport;
+
+ hinfo->unique_id = (hinfo->pci_bus << 8) | adapter->pdev->devfn;
+ hinfo->host_no = adapter->host->host_no;
+
+ return 0;
+}
+
+/*
+ * END: Interface for the common management module
+ */
+
+
+
+/**
+ * megaraid_sysfs_alloc_resources - allocate sysfs related resources
+ *
+ * Allocate packets required to issue FW calls whenever the sysfs attributes
+ * are read. These attributes would require up-to-date information from the
+ * FW. Also set up resources for mutual exclusion to share these resources and
+ * the wait queue.
+ *
+ * @param adapter : controller's soft state
+ *
+ * @return 0 on success
+ * @return -ERROR_CODE on failure
+ */
+static int
+megaraid_sysfs_alloc_resources(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ int rval = 0;
+
+ raid_dev->sysfs_uioc = kmalloc(sizeof(uioc_t), GFP_KERNEL);
+
+ raid_dev->sysfs_mbox64 = kmalloc(sizeof(mbox64_t), GFP_KERNEL);
+
+ raid_dev->sysfs_buffer = pci_alloc_consistent(adapter->pdev,
+ PAGE_SIZE, &raid_dev->sysfs_buffer_dma);
+
+ if (!raid_dev->sysfs_uioc || !raid_dev->sysfs_mbox64 ||
+ !raid_dev->sysfs_buffer) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ rval = -ENOMEM;
+
+ megaraid_sysfs_free_resources(adapter);
+ }
+
+ sema_init(&raid_dev->sysfs_sem, 1);
+
+ init_waitqueue_head(&raid_dev->sysfs_wait_q);
+
+ return rval;
+}
+
+
+/**
+ * megaraid_sysfs_free_resources - free sysfs related resources
+ *
+ * Free packets allocated for sysfs FW commands
+ *
+ * @param adapter : controller's soft state
+ */
+static void
+megaraid_sysfs_free_resources(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ if (raid_dev->sysfs_uioc) kfree(raid_dev->sysfs_uioc);
+
+ if (raid_dev->sysfs_mbox64) kfree(raid_dev->sysfs_mbox64);
+
+ if (raid_dev->sysfs_buffer) {
+ pci_free_consistent(adapter->pdev, PAGE_SIZE,
+ raid_dev->sysfs_buffer, raid_dev->sysfs_buffer_dma);
+ }
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap_done - callback for get ldmap
+ *
+ * Callback routine called in the ISR/tasklet context for get ldmap call
+ *
+ * @param uioc : completed packet
+ */
+static void
+megaraid_sysfs_get_ldmap_done(uioc_t *uioc)
+{
+ adapter_t *adapter = (adapter_t *)uioc->buf_vaddr;
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ uioc->status = 0;
+
+ wake_up(&raid_dev->sysfs_wait_q);
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap_timeout - timeout handling for get ldmap
+ *
+ * Timeout routine to recover and return to application, in case the adapter
+ * has stopped responding. A timeout of 60 seconds for this command seem like
+ * a good value
+ *
+ * @param uioc : timed out packet
+ */
+static void
+megaraid_sysfs_get_ldmap_timeout(unsigned long data)
+{
+ uioc_t *uioc = (uioc_t *)data;
+ adapter_t *adapter = (adapter_t *)uioc->buf_vaddr;
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ uioc->status = -ETIME;
+
+ wake_up(&raid_dev->sysfs_wait_q);
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap - get update logical drive map
+ *
+ * This routine will be called whenever user reads the logical drive
+ * attributes, go get the current logical drive mapping table from the
+ * firmware. We use the managment API's to issue commands to the controller.
+ *
+ * NOTE: The commands issuance functionality is not generalized and
+ * implemented in context of "get ld map" command only. If required, the
+ * command issuance logical can be trivially pulled out and implemented as a
+ * standalone libary. For now, this should suffice since there is no other
+ * user of this interface.
+ *
+ * @param adapter : controller's soft state
+ *
+ * @return 0 on success
+ * @return -1 on failure
+ */
+static int
+megaraid_sysfs_get_ldmap(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ uioc_t *uioc;
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ char *raw_mbox;
+ struct timer_list sysfs_timer;
+ struct timer_list *timerp;
+ caddr_t ldmap;
+ int rval = 0;
+
+ /*
+ * Allow only one read at a time to go through the sysfs attributes
+ */
+ down(&raid_dev->sysfs_sem);
+
+ uioc = raid_dev->sysfs_uioc;
+ mbox64 = raid_dev->sysfs_mbox64;
+ ldmap = raid_dev->sysfs_buffer;
+
+ memset(uioc, 0, sizeof(uioc_t));
+ memset(mbox64, 0, sizeof(mbox64_t));
+ memset(ldmap, 0, sizeof(raid_dev->curr_ldmap));
+
+ mbox = &mbox64->mbox32;
+ raw_mbox = (char *)mbox;
+ uioc->cmdbuf = (uint64_t)(unsigned long)mbox64;
+ uioc->buf_vaddr = (caddr_t)adapter;
+ uioc->status = -ENODATA;
+ uioc->done = megaraid_sysfs_get_ldmap_done;
+
+ /*
+ * Prepare the mailbox packet to get the current logical drive mapping
+ * table
+ */
+ mbox->xferaddr = (uint32_t)raid_dev->sysfs_buffer_dma;
+
+ raw_mbox[0] = FC_DEL_LOGDRV;
+ raw_mbox[2] = OP_GET_LDID_MAP;
+
+ /*
+ * Setup a timer to recover from a non-responding controller
+ */
+ timerp = &sysfs_timer;
+ init_timer(timerp);
+
+ timerp->function = megaraid_sysfs_get_ldmap_timeout;
+ timerp->data = (unsigned long)uioc;
+ timerp->expires = jiffies + 60 * HZ;
+
+ add_timer(timerp);
+
+ /*
+ * Send the command to the firmware
+ */
+ rval = megaraid_mbox_mm_command(adapter, uioc);
+
+ if (rval == 0) { // command successfully issued
+ wait_event(raid_dev->sysfs_wait_q, (uioc->status != -ENODATA));
+
+ /*
+ * Check if the command timed out
+ */
+ if (uioc->status == -ETIME) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: sysfs get ld map timed out\n"));
+
+ rval = -ETIME;
+ }
+ else {
+ rval = mbox->status;
+ }
+
+ if (rval == 0) {
+ memcpy(raid_dev->curr_ldmap, ldmap,
+ sizeof(raid_dev->curr_ldmap));
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: get ld map failed with %x\n", rval));
+ }
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: could not issue ldmap command:%x\n", rval));
+ }
+
+
+ del_timer_sync(timerp);
+
+ up(&raid_dev->sysfs_sem);
+
+ return rval;
+}
+
+
+/**
+ * megaraid_sysfs_show_app_hndl - display application handle for this adapter
+ *
+ * Display the handle used by the applications while executing management
+ * tasks on the adapter. We invoke a management module API to get the adapter
+ * handle, since we do not interface with applications directly.
+ *
+ * @param cdev : class device object representation for the host
+ * @param buf : buffer to send data to
+ */
+static ssize_t
+megaraid_sysfs_show_app_hndl(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(shost);
+ uint32_t app_hndl;
+
+ app_hndl = mraid_mm_adapter_app_handle(adapter->unique_id);
+
+ return snprintf(buf, 8, "%u\n", app_hndl);
+}
+
+
+/**
+ * megaraid_sysfs_show_ldnum - display the logical drive number for this device
+ *
+ * Display the logical drive number for the device in question, if it a valid
+ * logical drive. For physical devices, "-1" is returned
+ * The logical drive number is displayed in following format
+ *
+ * <SCSI ID> <LD NUM> <LD STICKY ID> <APP ADAPTER HANDLE>
+ * <int> <int> <int> <int>
+ *
+ * @param dev : device object representation for the scsi device
+ * @param buf : buffer to send data to
+ */
+static ssize_t
+megaraid_sysfs_show_ldnum(struct device *dev, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(sdev->host);
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ int scsi_id = -1;
+ int logical_drv = -1;
+ int ldid_map = -1;
+ uint32_t app_hndl = 0;
+ int mapped_sdev_id;
+ int rval;
+ int i;
+
+ if (raid_dev->random_del_supported &&
+ MRAID_IS_LOGICAL_SDEV(adapter, sdev)) {
+
+ rval = megaraid_sysfs_get_ldmap(adapter);
+ if (rval == 0) {
+
+ for (i = 0; i < MAX_LOGICAL_DRIVES_40LD; i++) {
+
+ mapped_sdev_id = sdev->id;
+
+ if (sdev->id > adapter->init_id) {
+ mapped_sdev_id -= 1;
+ }
+
+ if (raid_dev->curr_ldmap[i] == mapped_sdev_id) {
+
+ scsi_id = sdev->id;
+
+ logical_drv = i;
+
+ ldid_map = raid_dev->curr_ldmap[i];
+
+ app_hndl = mraid_mm_adapter_app_handle(
+ adapter->unique_id);
+
+ break;
+ }
+ }
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: sysfs get ld map failed: %x\n",
+ rval));
+ }
+ }
+
+ return snprintf(buf, 36, "%d %d %d %d\n", scsi_id, logical_drv,
+ ldid_map, app_hndl);
+}
+
+
+/*
+ * END: Mailbox Low Level Driver
+ */
+module_init(megaraid_init);
+module_exit(megaraid_exit);
+
+/* vim: set ts=8 sw=8 tw=78 ai si: */
diff --git a/drivers/scsi/megaraid/megaraid_mbox.h b/drivers/scsi/megaraid/megaraid_mbox.h
new file mode 100644
index 00000000000..07510009d11
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mbox.h
@@ -0,0 +1,288 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mbox.h
+ */
+
+#ifndef _MEGARAID_H_
+#define _MEGARAID_H_
+
+
+#include "mega_common.h"
+#include "mbox_defs.h"
+#include "megaraid_ioctl.h"
+
+
+#define MEGARAID_VERSION "2.20.4.5"
+#define MEGARAID_EXT_VERSION "(Release Date: Thu Feb 03 12:27:22 EST 2005)"
+
+
+/*
+ * Define some PCI values here until they are put in the kernel
+ */
+#define PCI_DEVICE_ID_PERC4_DI_DISCOVERY 0x000E
+#define PCI_SUBSYS_ID_PERC4_DI_DISCOVERY 0x0123
+
+#define PCI_DEVICE_ID_PERC4_SC 0x1960
+#define PCI_SUBSYS_ID_PERC4_SC 0x0520
+
+#define PCI_DEVICE_ID_PERC4_DC 0x1960
+#define PCI_SUBSYS_ID_PERC4_DC 0x0518
+
+#define PCI_DEVICE_ID_PERC4_QC 0x0407
+#define PCI_SUBSYS_ID_PERC4_QC 0x0531
+
+#define PCI_DEVICE_ID_PERC4_DI_EVERGLADES 0x000F
+#define PCI_SUBSYS_ID_PERC4_DI_EVERGLADES 0x014A
+
+#define PCI_DEVICE_ID_PERC4E_SI_BIGBEND 0x0013
+#define PCI_SUBSYS_ID_PERC4E_SI_BIGBEND 0x016c
+
+#define PCI_DEVICE_ID_PERC4E_DI_KOBUK 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_KOBUK 0x016d
+
+#define PCI_DEVICE_ID_PERC4E_DI_CORVETTE 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_CORVETTE 0x016e
+
+#define PCI_DEVICE_ID_PERC4E_DI_EXPEDITION 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION 0x016f
+
+#define PCI_DEVICE_ID_PERC4E_DI_GUADALUPE 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE 0x0170
+
+#define PCI_DEVICE_ID_PERC4E_DC_320_2E 0x0408
+#define PCI_SUBSYS_ID_PERC4E_DC_320_2E 0x0002
+
+#define PCI_DEVICE_ID_PERC4E_SC_320_1E 0x0408
+#define PCI_SUBSYS_ID_PERC4E_SC_320_1E 0x0001
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0 0xA520
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1 0x0520
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2 0x0518
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x 0x0530
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x 0x0532
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_4x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x 0x0531
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E 0x0001
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E 0x0002
+
+#define PCI_DEVICE_ID_MEGARAID_I4_133_RAID 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_I4_133_RAID 0x0522
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_150_4 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SATA_150_4 0x4523
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_150_6 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SATA_150_6 0x0523
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_300_4x 0x0409
+#define PCI_SUBSYS_ID_MEGARAID_SATA_300_4x 0x3004
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_300_8x 0x0409
+#define PCI_SUBSYS_ID_MEGARAID_SATA_300_8x 0x3008
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU42X 0x0407
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42X 0x0532
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCS16 0x1960
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCS16 0x0523
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU42E 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42E 0x0002
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCZCRX 0x0407
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX 0x0530
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCS28X 0x0409
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCS28X 0x3008
+
+#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF 0x3431
+
+#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH 0x3499
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x1960
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x0520
+
+#define PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x0408
+#define PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x1065
+
+#define PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E 0x004D
+
+#define PCI_SUBSYS_ID_PERC3_QC 0x0471
+#define PCI_SUBSYS_ID_PERC3_DC 0x0493
+#define PCI_SUBSYS_ID_PERC3_SC 0x0475
+
+#define PCI_DEVICE_ID_MEGARAID_NEC_ROMB_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_NEC_ROMB_2E 0x8287
+
+#ifndef PCI_SUBSYS_ID_FSC
+#define PCI_SUBSYS_ID_FSC 0x1734
+#endif
+
+#define MBOX_MAX_SCSI_CMDS 128 // number of cmds reserved for kernel
+#define MBOX_MAX_USER_CMDS 32 // number of cmds for applications
+#define MBOX_DEF_CMD_PER_LUN 64 // default commands per lun
+#define MBOX_DEFAULT_SG_SIZE 26 // default sg size supported by all fw
+#define MBOX_MAX_SG_SIZE 32 // maximum scatter-gather list size
+#define MBOX_MAX_SECTORS 128 // maximum sectors per IO
+#define MBOX_TIMEOUT 30 // timeout value for internal cmds
+#define MBOX_BUSY_WAIT 10 // max usec to wait for busy mailbox
+#define MBOX_RESET_WAIT 180 // wait these many seconds in reset
+#define MBOX_RESET_EXT_WAIT 120 // extended wait reset
+
+/*
+ * maximum transfer that can happen through the firmware commands issued
+ * internnaly from the driver.
+ */
+#define MBOX_IBUF_SIZE 4096
+
+
+/**
+ * mbox_ccb_t - command control block specific to mailbox based controllers
+ * @raw_mbox : raw mailbox pointer
+ * @mbox : mailbox
+ * @mbox64 : extended mailbox
+ * @mbox_dma_h : maibox dma address
+ * @sgl64 : 64-bit scatter-gather list
+ * @sgl32 : 32-bit scatter-gather list
+ * @sgl_dma_h : dma handle for the scatter-gather list
+ * @pthru : passthru structure
+ * @pthru_dma_h : dma handle for the passthru structure
+ * @epthru : extended passthru structure
+ * @epthru_dma_h : dma handle for extended passthru structure
+ * @buf_dma_h : dma handle for buffers w/o sg list
+ *
+ * command control block specific to the mailbox based controllers
+ */
+typedef struct {
+ uint8_t *raw_mbox;
+ mbox_t *mbox;
+ mbox64_t *mbox64;
+ dma_addr_t mbox_dma_h;
+ mbox_sgl64 *sgl64;
+ mbox_sgl32 *sgl32;
+ dma_addr_t sgl_dma_h;
+ mraid_passthru_t *pthru;
+ dma_addr_t pthru_dma_h;
+ mraid_epassthru_t *epthru;
+ dma_addr_t epthru_dma_h;
+ dma_addr_t buf_dma_h;
+} mbox_ccb_t;
+
+
+/**
+ * mraid_device_t - adapter soft state structure for mailbox controllers
+ * @param una_mbox64 : 64-bit mbox - unaligned
+ * @param una_mbox64_dma : mbox dma addr - unaligned
+ * @param mbox : 32-bit mbox - aligned
+ * @param mbox64 : 64-bit mbox - aligned
+ * @param mbox_dma : mbox dma addr - aligned
+ * @param mailbox_lock : exclusion lock for the mailbox
+ * @param baseport : base port of hba memory
+ * @param baseaddr : mapped addr of hba memory
+ * @param mbox_pool : pool of mailboxes
+ * @param mbox_pool_handle : handle for the mailbox pool memory
+ * @param epthru_pool : a pool for extended passthru commands
+ * @param epthru_pool_handle : handle to the pool above
+ * @param sg_pool : pool of scatter-gather lists for this driver
+ * @param sg_pool_handle : handle to the pool above
+ * @param ccb_list : list of our command control blocks
+ * @param uccb_list : list of cmd control blocks for mgmt module
+ * @param umbox64 : array of mailbox for user commands (cmm)
+ * @param pdrv_state : array for state of each physical drive.
+ * @param last_disp : flag used to show device scanning
+ * @param hw_error : set if FW not responding
+ * @param fast_load : If set, skip physical device scanning
+ * @channel_class : channel class, RAID or SCSI
+ * @sysfs_sem : semaphore to serialize access to sysfs res.
+ * @sysfs_uioc : management packet to issue FW calls from sysfs
+ * @sysfs_mbox64 : mailbox packet to issue FW calls from sysfs
+ * @sysfs_buffer : data buffer for FW commands issued from sysfs
+ * @sysfs_buffer_dma : DMA buffer for FW commands issued from sysfs
+ * @sysfs_wait_q : wait queue for sysfs operations
+ * @random_del_supported : set if the random deletion is supported
+ * @curr_ldmap : current LDID map
+ *
+ * Initialization structure for mailbox controllers: memory based and IO based
+ * All the fields in this structure are LLD specific and may be discovered at
+ * init() or start() time.
+ *
+ * NOTE: The fields of this structures are placed to minimize cache misses
+ */
+#define MAX_LD_EXTENDED64 64
+typedef struct {
+ mbox64_t *una_mbox64;
+ dma_addr_t una_mbox64_dma;
+ mbox_t *mbox;
+ mbox64_t *mbox64;
+ dma_addr_t mbox_dma;
+ spinlock_t mailbox_lock;
+ unsigned long baseport;
+ void __iomem * baseaddr;
+ struct mraid_pci_blk mbox_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *mbox_pool_handle;
+ struct mraid_pci_blk epthru_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *epthru_pool_handle;
+ struct mraid_pci_blk sg_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *sg_pool_handle;
+ mbox_ccb_t ccb_list[MBOX_MAX_SCSI_CMDS];
+ mbox_ccb_t uccb_list[MBOX_MAX_USER_CMDS];
+ mbox64_t umbox64[MBOX_MAX_USER_CMDS];
+
+ uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES];
+ uint32_t last_disp;
+ int hw_error;
+ int fast_load;
+ uint8_t channel_class;
+ struct semaphore sysfs_sem;
+ uioc_t *sysfs_uioc;
+ mbox64_t *sysfs_mbox64;
+ caddr_t sysfs_buffer;
+ dma_addr_t sysfs_buffer_dma;
+ wait_queue_head_t sysfs_wait_q;
+ int random_del_supported;
+ uint16_t curr_ldmap[MAX_LD_EXTENDED64];
+} mraid_device_t;
+
+// route to raid device from adapter
+#define ADAP2RAIDDEV(adp) ((mraid_device_t *)((adp)->raid_device))
+
+#define MAILBOX_LOCK(rdev) (&(rdev)->mailbox_lock)
+
+// Find out if this channel is a RAID or SCSI
+#define IS_RAID_CH(rdev, ch) (((rdev)->channel_class >> (ch)) & 0x01)
+
+
+#define RDINDOOR(rdev) readl((rdev)->baseaddr + 0x20)
+#define RDOUTDOOR(rdev) readl((rdev)->baseaddr + 0x2C)
+#define WRINDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x20)
+#define WROUTDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x2C)
+
+#endif // _MEGARAID_H_
+
+// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c
new file mode 100644
index 00000000000..9f1b550713e
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mm.c
@@ -0,0 +1,1255 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mm.c
+ * Version : v2.20.2.5 (Jan 21 2005)
+ *
+ * Common management module
+ */
+
+#include "megaraid_mm.h"
+#include <linux/smp_lock.h>
+
+
+// Entry points for char node driver
+static int mraid_mm_open(struct inode *, struct file *);
+static int mraid_mm_ioctl(struct inode *, struct file *, uint, unsigned long);
+
+
+// routines to convert to and from the old the format
+static int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *);
+static int kioc_to_mimd(uioc_t *, mimd_t __user *);
+
+
+// Helper functions
+static int handle_drvrcmd(void __user *, uint8_t, int *);
+static int lld_ioctl(mraid_mmadp_t *, uioc_t *);
+static void ioctl_done(uioc_t *);
+static void lld_timedout(unsigned long);
+static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
+static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
+static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
+static void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *);
+static int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int);
+static int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
+static void mraid_mm_free_adp_resources(mraid_mmadp_t *);
+static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);
+
+#ifdef CONFIG_COMPAT
+static long mraid_mm_compat_ioctl(struct file *, unsigned int, unsigned long);
+#endif
+
+MODULE_AUTHOR("LSI Logic Corporation");
+MODULE_DESCRIPTION("LSI Logic Management Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LSI_COMMON_MOD_VERSION);
+
+static int dbglevel = CL_ANN;
+module_param_named(dlevel, dbglevel, int, 0);
+MODULE_PARM_DESC(dlevel, "Debug level (default=0)");
+
+EXPORT_SYMBOL(mraid_mm_register_adp);
+EXPORT_SYMBOL(mraid_mm_unregister_adp);
+EXPORT_SYMBOL(mraid_mm_adapter_app_handle);
+
+static int majorno;
+static uint32_t drvr_ver = 0x02200201;
+
+static int adapters_count_g;
+static struct list_head adapters_list_g;
+
+static wait_queue_head_t wait_q;
+
+static struct file_operations lsi_fops = {
+ .open = mraid_mm_open,
+ .ioctl = mraid_mm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mraid_mm_compat_ioctl,
+#endif
+ .owner = THIS_MODULE,
+};
+
+/**
+ * mraid_mm_open - open routine for char node interface
+ * @inod : unused
+ * @filep : unused
+ *
+ * allow ioctl operations by apps only if they superuser privilege
+ */
+static int
+mraid_mm_open(struct inode *inode, struct file *filep)
+{
+ /*
+ * Only allow superuser to access private ioctl interface
+ */
+ if (!capable(CAP_SYS_ADMIN)) return (-EACCES);
+
+ return 0;
+}
+
+/**
+ * mraid_mm_ioctl - module entry-point for ioctls
+ * @inode : inode (ignored)
+ * @filep : file operations pointer (ignored)
+ * @cmd : ioctl command
+ * @arg : user ioctl packet
+ */
+static int
+mraid_mm_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ uioc_t *kioc;
+ char signature[EXT_IOCTL_SIGN_SZ] = {0};
+ int rval;
+ mraid_mmadp_t *adp;
+ uint8_t old_ioctl;
+ int drvrcmd_rval;
+ void __user *argp = (void __user *)arg;
+
+ /*
+ * Make sure only USCSICMD are issued through this interface.
+ * MIMD application would still fire different command.
+ */
+
+ if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) {
+ return (-EINVAL);
+ }
+
+ /*
+ * Look for signature to see if this is the new or old ioctl format.
+ */
+ if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: copy from usr addr failed\n"));
+ return (-EFAULT);
+ }
+
+ if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0)
+ old_ioctl = 0;
+ else
+ old_ioctl = 1;
+
+ /*
+ * At present, we don't support the new ioctl packet
+ */
+ if (!old_ioctl )
+ return (-EINVAL);
+
+ /*
+ * If it is a driver ioctl (as opposed to fw ioctls), then we can
+ * handle the command locally. rval > 0 means it is not a drvr cmd
+ */
+ rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval);
+
+ if (rval < 0)
+ return rval;
+ else if (rval == 0)
+ return drvrcmd_rval;
+
+ rval = 0;
+ if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) {
+ return rval;
+ }
+
+ /*
+ * Check if adapter can accept ioctl. We may have marked it offline
+ * if any previous kioc had timedout on this controller.
+ */
+ if (!adp->quiescent) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: controller cannot accept cmds due to "
+ "earlier errors\n" ));
+ return -EFAULT;
+ }
+
+ /*
+ * The following call will block till a kioc is available
+ */
+ kioc = mraid_mm_alloc_kioc(adp);
+
+ /*
+ * User sent the old mimd_t ioctl packet. Convert it to uioc_t.
+ */
+ if ((rval = mimd_to_kioc(argp, adp, kioc))) {
+ mraid_mm_dealloc_kioc(adp, kioc);
+ return rval;
+ }
+
+ kioc->done = ioctl_done;
+
+ /*
+ * Issue the IOCTL to the low level driver. After the IOCTL completes
+ * release the kioc if and only if it was _not_ timedout. If it was
+ * timedout, that means that resources are still with low level driver.
+ */
+ if ((rval = lld_ioctl(adp, kioc))) {
+
+ if (!kioc->timedout)
+ mraid_mm_dealloc_kioc(adp, kioc);
+
+ return rval;
+ }
+
+ /*
+ * Convert the kioc back to user space
+ */
+ rval = kioc_to_mimd(kioc, argp);
+
+ /*
+ * Return the kioc to free pool
+ */
+ mraid_mm_dealloc_kioc(adp, kioc);
+
+ return rval;
+}
+
+
+/**
+ * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet
+ * @umimd : User space mimd_t ioctl packet
+ * @adapter : pointer to the adapter (OUT)
+ */
+static mraid_mmadp_t *
+mraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
+{
+ mraid_mmadp_t *adapter;
+ mimd_t mimd;
+ uint32_t adapno;
+ int iterator;
+
+
+ if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
+ *rval = -EFAULT;
+ return NULL;
+ }
+
+ adapno = GETADAP(mimd.ui.fcs.adapno);
+
+ if (adapno >= adapters_count_g) {
+ *rval = -ENODEV;
+ return NULL;
+ }
+
+ adapter = NULL;
+ iterator = 0;
+
+ list_for_each_entry(adapter, &adapters_list_g, list) {
+ if (iterator++ == adapno) break;
+ }
+
+ if (!adapter) {
+ *rval = -ENODEV;
+ return NULL;
+ }
+
+ return adapter;
+}
+
+/*
+ * handle_drvrcmd - This routine checks if the opcode is a driver
+ * cmd and if it is, handles it.
+ * @arg : packet sent by the user app
+ * @old_ioctl : mimd if 1; uioc otherwise
+ */
+static int
+handle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval)
+{
+ mimd_t __user *umimd;
+ mimd_t kmimd;
+ uint8_t opcode;
+ uint8_t subopcode;
+
+ if (old_ioctl)
+ goto old_packet;
+ else
+ goto new_packet;
+
+new_packet:
+ return (-ENOTSUPP);
+
+old_packet:
+ *rval = 0;
+ umimd = arg;
+
+ if (copy_from_user(&kmimd, umimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ opcode = kmimd.ui.fcs.opcode;
+ subopcode = kmimd.ui.fcs.subopcode;
+
+ /*
+ * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or
+ * GET_NUMADP, then we can handle. Otherwise we should return 1 to
+ * indicate that we cannot handle this.
+ */
+ if (opcode != 0x82)
+ return 1;
+
+ switch (subopcode) {
+
+ case MEGAIOC_QDRVRVER:
+
+ if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t)))
+ return (-EFAULT);
+
+ return 0;
+
+ case MEGAIOC_QNADAP:
+
+ *rval = adapters_count_g;
+
+ if (copy_to_user(kmimd.data, &adapters_count_g,
+ sizeof(uint32_t)))
+ return (-EFAULT);
+
+ return 0;
+
+ default:
+ /* cannot handle */
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * mimd_to_kioc - Converter from old to new ioctl format
+ *
+ * @umimd : user space old MIMD IOCTL
+ * @kioc : kernel space new format IOCTL
+ *
+ * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The
+ * new packet is in kernel space so that driver can perform operations on it
+ * freely.
+ */
+
+static int
+mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ mraid_passthru_t *pthru32;
+ uint32_t adapno;
+ uint8_t opcode;
+ uint8_t subopcode;
+ mimd_t mimd;
+
+ if (copy_from_user(&mimd, umimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ /*
+ * Applications are not allowed to send extd pthru
+ */
+ if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) ||
+ (mimd.mbox[0] == MBOXCMD_EXTPTHRU))
+ return (-EINVAL);
+
+ opcode = mimd.ui.fcs.opcode;
+ subopcode = mimd.ui.fcs.subopcode;
+ adapno = GETADAP(mimd.ui.fcs.adapno);
+
+ if (adapno >= adapters_count_g)
+ return (-ENODEV);
+
+ kioc->adapno = adapno;
+ kioc->mb_type = MBOX_LEGACY;
+ kioc->app_type = APPTYPE_MIMD;
+
+ switch (opcode) {
+
+ case 0x82:
+
+ if (subopcode == MEGAIOC_QADAPINFO) {
+
+ kioc->opcode = GET_ADAP_INFO;
+ kioc->data_dir = UIOC_RD;
+ kioc->xferlen = sizeof(mraid_hba_info_t);
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: Invalid subop\n"));
+ return (-EINVAL);
+ }
+
+ break;
+
+ case 0x81:
+
+ kioc->opcode = MBOX_CMD;
+ kioc->xferlen = mimd.ui.fcs.length;
+ kioc->user_data_len = kioc->xferlen;
+ kioc->user_data = mimd.ui.fcs.buffer;
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+
+ if (mimd.outlen) kioc->data_dir = UIOC_RD;
+ if (mimd.inlen) kioc->data_dir |= UIOC_WR;
+
+ break;
+
+ case 0x80:
+
+ kioc->opcode = MBOX_CMD;
+ kioc->xferlen = (mimd.outlen > mimd.inlen) ?
+ mimd.outlen : mimd.inlen;
+ kioc->user_data_len = kioc->xferlen;
+ kioc->user_data = mimd.data;
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+
+ if (mimd.outlen) kioc->data_dir = UIOC_RD;
+ if (mimd.inlen) kioc->data_dir |= UIOC_WR;
+
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ /*
+ * If driver command, nothing else to do
+ */
+ if (opcode == 0x82)
+ return 0;
+
+ /*
+ * This is a mailbox cmd; copy the mailbox from mimd
+ */
+ mbox64 = (mbox64_t *)((unsigned long)kioc->cmdbuf);
+ mbox = &mbox64->mbox32;
+ memcpy(mbox, mimd.mbox, 14);
+
+ if (mbox->cmd != MBOXCMD_PASSTHRU) { // regular DCMD
+
+ mbox->xferaddr = (uint32_t)kioc->buf_paddr;
+
+ if (kioc->data_dir & UIOC_WR) {
+ if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
+ kioc->xferlen)) {
+ return (-EFAULT);
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * This is a regular 32-bit pthru cmd; mbox points to pthru struct.
+ * Just like in above case, the beginning for memblk is treated as
+ * a mailbox. The passthru will begin at next 1K boundary. And the
+ * data will start 1K after that.
+ */
+ pthru32 = kioc->pthru32;
+ kioc->user_pthru = &umimd->pthru;
+ mbox->xferaddr = (uint32_t)kioc->pthru32_h;
+
+ if (copy_from_user(pthru32, kioc->user_pthru,
+ sizeof(mraid_passthru_t))) {
+ return (-EFAULT);
+ }
+
+ pthru32->dataxferaddr = kioc->buf_paddr;
+ if (kioc->data_dir & UIOC_WR) {
+ if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
+ pthru32->dataxferlen)) {
+ return (-EFAULT);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mraid_mm_attch_buf - Attach a free dma buffer for required size
+ *
+ * @adp : Adapter softstate
+ * @kioc : kioc that the buffer needs to be attached to
+ * @xferlen : required length for buffer
+ *
+ * First we search for a pool with smallest buffer that is >= @xferlen. If
+ * that pool has no free buffer, we will try for the next bigger size. If none
+ * is available, we will try to allocate the smallest buffer that is >=
+ * @xferlen and attach it the pool.
+ */
+static int
+mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
+{
+ mm_dmapool_t *pool;
+ int right_pool = -1;
+ unsigned long flags;
+ int i;
+
+ kioc->pool_index = -1;
+ kioc->buf_vaddr = NULL;
+ kioc->buf_paddr = 0;
+ kioc->free_buf = 0;
+
+ /*
+ * We need xferlen amount of memory. See if we can get it from our
+ * dma pools. If we don't get exact size, we will try bigger buffer
+ */
+
+ for (i = 0; i < MAX_DMA_POOLS; i++) {
+
+ pool = &adp->dma_pool_list[i];
+
+ if (xferlen > pool->buf_size)
+ continue;
+
+ if (right_pool == -1)
+ right_pool = i;
+
+ spin_lock_irqsave(&pool->lock, flags);
+
+ if (!pool->in_use) {
+
+ pool->in_use = 1;
+ kioc->pool_index = i;
+ kioc->buf_vaddr = pool->vaddr;
+ kioc->buf_paddr = pool->paddr;
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return 0;
+ }
+ else {
+ spin_unlock_irqrestore(&pool->lock, flags);
+ continue;
+ }
+ }
+
+ /*
+ * If xferlen doesn't match any of our pools, return error
+ */
+ if (right_pool == -1)
+ return -EINVAL;
+
+ /*
+ * We did not get any buffer from the preallocated pool. Let us try
+ * to allocate one new buffer. NOTE: This is a blocking call.
+ */
+ pool = &adp->dma_pool_list[right_pool];
+
+ spin_lock_irqsave(&pool->lock, flags);
+
+ kioc->pool_index = right_pool;
+ kioc->free_buf = 1;
+ kioc->buf_vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ &kioc->buf_paddr);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ if (!kioc->buf_vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * mraid_mm_alloc_kioc - Returns a uioc_t from free list
+ * @adp : Adapter softstate for this module
+ *
+ * The kioc_semaphore is initialized with number of kioc nodes in the
+ * free kioc pool. If the kioc pool is empty, this function blocks till
+ * a kioc becomes free.
+ */
+static uioc_t *
+mraid_mm_alloc_kioc(mraid_mmadp_t *adp)
+{
+ uioc_t *kioc;
+ struct list_head* head;
+ unsigned long flags;
+
+ down(&adp->kioc_semaphore);
+
+ spin_lock_irqsave(&adp->kioc_pool_lock, flags);
+
+ head = &adp->kioc_pool;
+
+ if (list_empty(head)) {
+ up(&adp->kioc_semaphore);
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n"));
+ return NULL;
+ }
+
+ kioc = list_entry(head->next, uioc_t, list);
+ list_del_init(&kioc->list);
+
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t));
+ memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t));
+
+ kioc->buf_vaddr = NULL;
+ kioc->buf_paddr = 0;
+ kioc->pool_index =-1;
+ kioc->free_buf = 0;
+ kioc->user_data = NULL;
+ kioc->user_data_len = 0;
+ kioc->user_pthru = NULL;
+ kioc->timedout = 0;
+
+ return kioc;
+}
+
+/**
+ * mraid_mm_dealloc_kioc - Return kioc to free pool
+ *
+ * @adp : Adapter softstate
+ * @kioc : uioc_t node to be returned to free pool
+ */
+static void
+mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ mm_dmapool_t *pool;
+ unsigned long flags;
+
+ if (kioc->pool_index != -1) {
+ pool = &adp->dma_pool_list[kioc->pool_index];
+
+ /* This routine may be called in non-isr context also */
+ spin_lock_irqsave(&pool->lock, flags);
+
+ /*
+ * While attaching the dma buffer, if we didn't get the
+ * required buffer from the pool, we would have allocated
+ * it at the run time and set the free_buf flag. We must
+ * free that buffer. Otherwise, just mark that the buffer is
+ * not in use
+ */
+ if (kioc->free_buf == 1)
+ pci_pool_free(pool->handle, kioc->buf_vaddr,
+ kioc->buf_paddr);
+ else
+ pool->in_use = 0;
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+ }
+
+ /* Return the kioc to the free pool */
+ spin_lock_irqsave(&adp->kioc_pool_lock, flags);
+ list_add(&kioc->list, &adp->kioc_pool);
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ /* increment the free kioc count */
+ up(&adp->kioc_semaphore);
+
+ return;
+}
+
+/**
+ * lld_ioctl - Routine to issue ioctl to low level drvr
+ *
+ * @adp : The adapter handle
+ * @kioc : The ioctl packet with kernel addresses
+ */
+static int
+lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ int rval;
+ struct timer_list timer;
+ struct timer_list *tp = NULL;
+
+ kioc->status = -ENODATA;
+ rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);
+
+ if (rval) return rval;
+
+ /*
+ * Start the timer
+ */
+ if (adp->timeout > 0) {
+ tp = &timer;
+ init_timer(tp);
+
+ tp->function = lld_timedout;
+ tp->data = (unsigned long)kioc;
+ tp->expires = jiffies + adp->timeout * HZ;
+
+ add_timer(tp);
+ }
+
+ /*
+ * Wait till the low level driver completes the ioctl. After this
+ * call, the ioctl either completed successfully or timedout.
+ */
+ wait_event(wait_q, (kioc->status != -ENODATA));
+ if (tp) {
+ del_timer_sync(tp);
+ }
+
+ /*
+ * If the command had timedout, we mark the controller offline
+ * before returning
+ */
+ if (kioc->timedout) {
+ adp->quiescent = 0;
+ }
+
+ return kioc->status;
+}
+
+
+/**
+ * ioctl_done - callback from the low level driver
+ *
+ * @kioc : completed ioctl packet
+ */
+static void
+ioctl_done(uioc_t *kioc)
+{
+ uint32_t adapno;
+ int iterator;
+ mraid_mmadp_t* adapter;
+
+ /*
+ * When the kioc returns from driver, make sure it still doesn't
+ * have ENODATA in status. Otherwise, driver will hang on wait_event
+ * forever
+ */
+ if (kioc->status == -ENODATA) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: lld didn't change status!\n"));
+
+ kioc->status = -EINVAL;
+ }
+
+ /*
+ * Check if this kioc was timedout before. If so, nobody is waiting
+ * on this kioc. We don't have to wake up anybody. Instead, we just
+ * have to free the kioc
+ */
+ if (kioc->timedout) {
+ iterator = 0;
+ adapter = NULL;
+ adapno = kioc->adapno;
+
+ con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
+ "ioctl that was timedout before\n"));
+
+ list_for_each_entry(adapter, &adapters_list_g, list) {
+ if (iterator++ == adapno) break;
+ }
+
+ kioc->timedout = 0;
+
+ if (adapter) {
+ mraid_mm_dealloc_kioc( adapter, kioc );
+ }
+ }
+ else {
+ wake_up(&wait_q);
+ }
+}
+
+
+/*
+ * lld_timedout : callback from the expired timer
+ *
+ * @ptr : ioctl packet that timed out
+ */
+static void
+lld_timedout(unsigned long ptr)
+{
+ uioc_t *kioc = (uioc_t *)ptr;
+
+ kioc->status = -ETIME;
+ kioc->timedout = 1;
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n"));
+
+ wake_up(&wait_q);
+}
+
+
+/**
+ * kioc_to_mimd : Converter from new back to old format
+ *
+ * @kioc : Kernel space IOCTL packet (successfully issued)
+ * @mimd : User space MIMD packet
+ */
+static int
+kioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd)
+{
+ mimd_t kmimd;
+ uint8_t opcode;
+ uint8_t subopcode;
+
+ mbox64_t *mbox64;
+ mraid_passthru_t __user *upthru32;
+ mraid_passthru_t *kpthru32;
+ mcontroller_t cinfo;
+ mraid_hba_info_t *hinfo;
+
+
+ if (copy_from_user(&kmimd, mimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ opcode = kmimd.ui.fcs.opcode;
+ subopcode = kmimd.ui.fcs.subopcode;
+
+ if (opcode == 0x82) {
+ switch (subopcode) {
+
+ case MEGAIOC_QADAPINFO:
+
+ hinfo = (mraid_hba_info_t *)(unsigned long)
+ kioc->buf_vaddr;
+
+ hinfo_to_cinfo(hinfo, &cinfo);
+
+ if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo)))
+ return (-EFAULT);
+
+ return 0;
+
+ default:
+ return (-EINVAL);
+ }
+
+ return 0;
+ }
+
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+
+ if (kioc->user_pthru) {
+
+ upthru32 = kioc->user_pthru;
+ kpthru32 = kioc->pthru32;
+
+ if (copy_to_user(&upthru32->scsistatus,
+ &kpthru32->scsistatus,
+ sizeof(uint8_t))) {
+ return (-EFAULT);
+ }
+ }
+
+ if (kioc->user_data) {
+ if (copy_to_user(kioc->user_data, kioc->buf_vaddr,
+ kioc->user_data_len)) {
+ return (-EFAULT);
+ }
+ }
+
+ if (copy_to_user(&mimd->mbox[17],
+ &mbox64->mbox32.status, sizeof(uint8_t))) {
+ return (-EFAULT);
+ }
+
+ return 0;
+}
+
+
+/**
+ * hinfo_to_cinfo - Convert new format hba info into old format
+ *
+ * @hinfo : New format, more comprehensive adapter info
+ * @cinfo : Old format adapter info to support mimd_t apps
+ */
+static void
+hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
+{
+ if (!hinfo || !cinfo)
+ return;
+
+ cinfo->base = hinfo->baseport;
+ cinfo->irq = hinfo->irq;
+ cinfo->numldrv = hinfo->num_ldrv;
+ cinfo->pcibus = hinfo->pci_bus;
+ cinfo->pcidev = hinfo->pci_slot;
+ cinfo->pcifun = PCI_FUNC(hinfo->pci_dev_fn);
+ cinfo->pciid = hinfo->pci_device_id;
+ cinfo->pcivendor = hinfo->pci_vendor_id;
+ cinfo->pcislot = hinfo->pci_slot;
+ cinfo->uid = hinfo->unique_id;
+}
+
+
+/*
+ * mraid_mm_register_adp - Registration routine for low level drvrs
+ *
+ * @adp : Adapter objejct
+ */
+int
+mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
+{
+ mraid_mmadp_t *adapter;
+ mbox64_t *mbox_list;
+ uioc_t *kioc;
+ uint32_t rval;
+ int i;
+
+
+ if (lld_adp->drvr_type != DRVRTYPE_MBOX)
+ return (-EINVAL);
+
+ adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL);
+
+ if (!adapter) {
+ rval = -ENOMEM;
+ goto memalloc_error;
+ }
+
+ memset(adapter, 0, sizeof(mraid_mmadp_t));
+
+ adapter->unique_id = lld_adp->unique_id;
+ adapter->drvr_type = lld_adp->drvr_type;
+ adapter->drvr_data = lld_adp->drvr_data;
+ adapter->pdev = lld_adp->pdev;
+ adapter->issue_uioc = lld_adp->issue_uioc;
+ adapter->timeout = lld_adp->timeout;
+ adapter->max_kioc = lld_adp->max_kioc;
+ adapter->quiescent = 1;
+
+ /*
+ * Allocate single blocks of memory for all required kiocs,
+ * mailboxes and passthru structures.
+ */
+ adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc,
+ GFP_KERNEL);
+ adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc,
+ GFP_KERNEL);
+ adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool",
+ adapter->pdev,
+ sizeof(mraid_passthru_t),
+ 16, 0);
+
+ if (!adapter->kioc_list || !adapter->mbox_list ||
+ !adapter->pthru_dma_pool) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ rval = (-ENOMEM);
+
+ goto memalloc_error;
+ }
+
+ /*
+ * Slice kioc_list and make a kioc_pool with the individiual kiocs
+ */
+ INIT_LIST_HEAD(&adapter->kioc_pool);
+ spin_lock_init(&adapter->kioc_pool_lock);
+ sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc);
+
+ mbox_list = (mbox64_t *)adapter->mbox_list;
+
+ for (i = 0; i < lld_adp->max_kioc; i++) {
+
+ kioc = adapter->kioc_list + i;
+ kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i);
+ kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool,
+ GFP_KERNEL, &kioc->pthru32_h);
+
+ if (!kioc->pthru32) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: out of memory, %s %d\n",
+ __FUNCTION__, __LINE__));
+
+ rval = (-ENOMEM);
+
+ goto pthru_dma_pool_error;
+ }
+
+ list_add_tail(&kioc->list, &adapter->kioc_pool);
+ }
+
+ // Setup the dma pools for data buffers
+ if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) {
+ goto dma_pool_error;
+ }
+
+ list_add_tail(&adapter->list, &adapters_list_g);
+
+ adapters_count_g++;
+
+ return 0;
+
+dma_pool_error:
+ /* Do nothing */
+
+pthru_dma_pool_error:
+
+ for (i = 0; i < lld_adp->max_kioc; i++) {
+ kioc = adapter->kioc_list + i;
+ if (kioc->pthru32) {
+ pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
+ kioc->pthru32_h);
+ }
+ }
+
+memalloc_error:
+
+ if (adapter->kioc_list)
+ kfree(adapter->kioc_list);
+
+ if (adapter->mbox_list)
+ kfree(adapter->mbox_list);
+
+ if (adapter->pthru_dma_pool)
+ pci_pool_destroy(adapter->pthru_dma_pool);
+
+ if (adapter)
+ kfree(adapter);
+
+ return rval;
+}
+
+
+/**
+ * mraid_mm_adapter_app_handle - return the application handle for this adapter
+ *
+ * For the given driver data, locate the adadpter in our global list and
+ * return the corresponding handle, which is also used by applications to
+ * uniquely identify an adapter.
+ *
+ * @param unique_id : adapter unique identifier
+ *
+ * @return adapter handle if found in the list
+ * @return 0 if adapter could not be located, should never happen though
+ */
+uint32_t
+mraid_mm_adapter_app_handle(uint32_t unique_id)
+{
+ mraid_mmadp_t *adapter;
+ mraid_mmadp_t *tmp;
+ int index = 0;
+
+ list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
+
+ if (adapter->unique_id == unique_id) {
+
+ return MKADAP(index);
+ }
+
+ index++;
+ }
+
+ return 0;
+}
+
+
+/**
+ * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter
+ *
+ * @adp : Adapter softstate
+ *
+ * We maintain a pool of dma buffers per each adapter. Each pool has one
+ * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers.
+ * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We
+ * dont' want to waste too much memory by allocating more buffers per each
+ * pool.
+ */
+static int
+mraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
+{
+ mm_dmapool_t *pool;
+ int bufsize;
+ int i;
+
+ /*
+ * Create MAX_DMA_POOLS number of pools
+ */
+ bufsize = MRAID_MM_INIT_BUFF_SIZE;
+
+ for (i = 0; i < MAX_DMA_POOLS; i++){
+
+ pool = &adp->dma_pool_list[i];
+
+ pool->buf_size = bufsize;
+ spin_lock_init(&pool->lock);
+
+ pool->handle = pci_pool_create("megaraid mm data buffer",
+ adp->pdev, bufsize, 16, 0);
+
+ if (!pool->handle) {
+ goto dma_pool_setup_error;
+ }
+
+ pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ &pool->paddr);
+
+ if (!pool->vaddr)
+ goto dma_pool_setup_error;
+
+ bufsize = bufsize * 2;
+ }
+
+ return 0;
+
+dma_pool_setup_error:
+
+ mraid_mm_teardown_dma_pools(adp);
+ return (-ENOMEM);
+}
+
+
+/*
+ * mraid_mm_unregister_adp - Unregister routine for low level drivers
+ * Assume no outstanding ioctls to llds.
+ *
+ * @unique_id : UID of the adpater
+ */
+int
+mraid_mm_unregister_adp(uint32_t unique_id)
+{
+ mraid_mmadp_t *adapter;
+ mraid_mmadp_t *tmp;
+
+ list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
+
+
+ if (adapter->unique_id == unique_id) {
+
+ adapters_count_g--;
+
+ list_del_init(&adapter->list);
+
+ mraid_mm_free_adp_resources(adapter);
+
+ kfree(adapter);
+
+ con_log(CL_ANN, (
+ "megaraid cmm: Unregistered one adapter:%#x\n",
+ unique_id));
+
+ return 0;
+ }
+ }
+
+ return (-ENODEV);
+}
+
+/**
+ * mraid_mm_free_adp_resources - Free adapter softstate
+ *
+ * @adp : Adapter softstate
+ */
+static void
+mraid_mm_free_adp_resources(mraid_mmadp_t *adp)
+{
+ uioc_t *kioc;
+ int i;
+
+ mraid_mm_teardown_dma_pools(adp);
+
+ for (i = 0; i < adp->max_kioc; i++) {
+
+ kioc = adp->kioc_list + i;
+
+ pci_pool_free(adp->pthru_dma_pool, kioc->pthru32,
+ kioc->pthru32_h);
+ }
+
+ kfree(adp->kioc_list);
+
+ kfree(adp->mbox_list);
+
+ pci_pool_destroy(adp->pthru_dma_pool);
+
+
+ return;
+}
+
+
+/**
+ * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers
+ *
+ * @adp : Adapter softstate
+ */
+static void
+mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
+{
+ int i;
+ mm_dmapool_t *pool;
+
+ for (i = 0; i < MAX_DMA_POOLS; i++) {
+
+ pool = &adp->dma_pool_list[i];
+
+ if (pool->handle) {
+
+ if (pool->vaddr)
+ pci_pool_free(pool->handle, pool->vaddr,
+ pool->paddr);
+
+ pci_pool_destroy(pool->handle);
+ pool->handle = NULL;
+ }
+ }
+
+ return;
+}
+
+/**
+ * mraid_mm_init : Module entry point
+ */
+static int __init
+mraid_mm_init(void)
+{
+ // Announce the driver version
+ con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n",
+ LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION));
+
+ majorno = register_chrdev(0, "megadev", &lsi_fops);
+
+ if (majorno < 0) {
+ con_log(CL_ANN, ("megaraid cmm: cannot get major\n"));
+ return majorno;
+ }
+
+ init_waitqueue_head(&wait_q);
+
+ INIT_LIST_HEAD(&adapters_list_g);
+
+ return 0;
+}
+
+
+/**
+ * mraid_mm_compat_ioctl : 32bit to 64bit ioctl conversion routine
+ */
+#ifdef CONFIG_COMPAT
+static long
+mraid_mm_compat_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ lock_kernel();
+ err = mraid_mm_ioctl(NULL, filep, cmd, arg);
+ unlock_kernel();
+ return err;
+}
+#endif
+
+/**
+ * mraid_mm_exit : Module exit point
+ */
+static void __exit
+mraid_mm_exit(void)
+{
+ con_log(CL_DLEVEL1 , ("exiting common mod\n"));
+
+ unregister_chrdev(majorno, "megadev");
+}
+
+module_init(mraid_mm_init);
+module_exit(mraid_mm_exit);
+
+/* vi: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/megaraid_mm.h b/drivers/scsi/megaraid/megaraid_mm.h
new file mode 100644
index 00000000000..948a0012ab8
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mm.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mm.h
+ */
+
+#ifndef MEGARAID_MM_H
+#define MEGARAID_MM_H
+
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/ioctl32.h>
+
+#include "mbox_defs.h"
+#include "megaraid_ioctl.h"
+
+
+#define LSI_COMMON_MOD_VERSION "2.20.2.5"
+#define LSI_COMMON_MOD_EXT_VERSION \
+ "(Release Date: Fri Jan 21 00:01:03 EST 2005)"
+
+
+#define LSI_DBGLVL dbglevel
+
+// The smallest dma pool
+#define MRAID_MM_INIT_BUFF_SIZE 4096
+
+/**
+ * mimd_t : Old style ioctl packet structure (deprecated)
+ *
+ * @inlen :
+ * @outlen :
+ * @fca :
+ * @opcode :
+ * @subopcode :
+ * @adapno :
+ * @buffer :
+ * @pad :
+ * @length :
+ * @mbox :
+ * @pthru :
+ * @data :
+ * @pad :
+ *
+ * Note : This structure is DEPRECATED. New applications must use
+ * : uioc_t structure instead. All new hba drivers use the new
+ * : format. If we get this mimd packet, we will convert it into
+ * : new uioc_t format and send it to the hba drivers.
+ */
+
+typedef struct mimd {
+
+ uint32_t inlen;
+ uint32_t outlen;
+
+ union {
+ uint8_t fca[16];
+ struct {
+ uint8_t opcode;
+ uint8_t subopcode;
+ uint16_t adapno;
+#if BITS_PER_LONG == 32
+ uint8_t __user *buffer;
+ uint8_t pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ uint8_t __user *buffer;
+#endif
+ uint32_t length;
+ } __attribute__ ((packed)) fcs;
+ } __attribute__ ((packed)) ui;
+
+ uint8_t mbox[18]; /* 16 bytes + 2 status bytes */
+ mraid_passthru_t pthru;
+
+#if BITS_PER_LONG == 32
+ char __user *data; /* buffer <= 4096 for 0x80 commands */
+ char pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ char __user *data;
+#endif
+
+} __attribute__ ((packed))mimd_t;
+
+#endif // MEGARAID_MM_H
+
+// vi: set ts=8 sw=8 tw=78: