?? 3w-xxxx.c
字號:
/* 3w-xxxx.c -- 3ware Storage Controller device driver for Linux. Written By: Adam Radford <linux@3ware.com> Modifications By: Joel Jacobson <linux@3ware.com> Arnaldo Carvalho de Melo <acme@conectiva.com.br> Copyright (C) 1999-2000 3ware Inc. Kernel compatablity By: Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> Further tiny build fixes and trivial hoovering Alan Cox 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; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. NO WARRANTY THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. DISCLAIMER OF LIABILITY NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: linux@3ware.com For more information, goto: http://www.3ware.com History ------- 0.1.000 - Initial release. 0.4.000 - Added support for Asynchronous Event Notification through ioctls for 3DM. 1.0.000 - Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb to disable drive write-cache before writes. 1.1.000 - Fixed performance bug with DPO & FUA not existing for WRITE_6. 1.2.000 - Added support for clean shutdown notification/feature table. 1.02.00.001 - Added support for full command packet posts through ioctls for 3DM. Bug fix so hot spare drives don't show up. 1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some systems. 08/21/00 - release previously allocated resources on failure at tw_allocate_memory (acme)*/#include <linux/module.h>MODULE_AUTHOR ("3ware Inc.");MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");#include <linux/kernel.h>#include <linux/pci.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/blk.h>#include <linux/hdreg.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/smp.h>#include <linux/reboot.h>#include <linux/spinlock.h>#include <asm/errno.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#define __3W_C /* let 3w-xxxx.h know it is use */#include "sd.h"#include "scsi.h"#include "hosts.h"#include "3w-xxxx.h"static int tw_copy_info(TW_Info *info, char *fmt, ...);static void tw_copy_mem_info(TW_Info *info, char *data, int len);static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static int tw_halt(struct notifier_block *nb, ulong event, void *buf);/* Notifier block to get a notify on system shutdown/halt/reboot */static struct notifier_block tw_notifier = { tw_halt, NULL, 0};/* Globals */char *tw_driver_version="1.02.00.002";TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];int tw_device_extension_count = 0;/* Functions *//* This function will complete an aen request from the isr */int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) { TW_Param *param; unsigned short aen, aen_code; if (tw_dev->alignment_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n"); return 1; } param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; aen = *(unsigned short *)(param->data); aen_code = (aen & 0x0ff); dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen_code); /* Now queue the code */ tw_dev->aen_queue[tw_dev->aen_tail] = aen_code; if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { tw_dev->aen_tail = TW_Q_START; } else { tw_dev->aen_tail = tw_dev->aen_tail + 1; } if (tw_dev->aen_head == tw_dev->aen_tail) { if (tw_dev->aen_head == TW_Q_LENGTH - 1) { tw_dev->aen_head = TW_Q_START; } else { tw_dev->aen_head = tw_dev->aen_head + 1; } } tw_dev->state[request_id] = TW_S_COMPLETED; tw_state_request_finish(tw_dev, request_id); return 0;} /* End tw_aen_complete() *//* This function will drain the aen queue after a soft reset */int tw_aen_drain_queue(TW_Device_Extension *tw_dev){ TW_Command *command_packet; TW_Param *param; int tries = 0; int request_id = 0; u32 command_que_value = 0, command_que_addr; u32 status_reg_value = 0, status_reg_addr; u32 param_value; TW_Response_Queue response_queue; u32 response_que_addr; unsigned short aen; unsigned short aen_code; int finished = 0; int first_reset = 0; int queue = 0; int imax, i; int found = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n"); command_que_addr = tw_dev->registers.command_que_addr; status_reg_addr = tw_dev->registers.status_reg_addr; response_que_addr = tw_dev->registers.response_que_addr; if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d\n", tw_dev->host->host_no); return 1; } /* Initialize command packet */ if (tw_dev->command_packet_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n"); return 1; } command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; memset(command_packet, 0, sizeof(TW_Sector)); command_packet->byte0.opcode = TW_OP_GET_PARAM; command_packet->byte0.sgl_offset = 2; command_packet->size = 4; command_packet->request_id = request_id; command_packet->byte3.unit = 0; command_packet->byte3.host_id = 0; command_packet->status = 0; command_packet->flags = 0; command_packet->byte6.parameter_count = 1; command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n"); return 1; } /* Now setup the param */ if (tw_dev->alignment_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n"); return 1; } param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; memset(param, 0, sizeof(TW_Sector)); param->table_id = 0x401; /* AEN table */ param->parameter_id = 2; /* Unit code */ param->parameter_size_bytes = 2; param_value = tw_dev->alignment_physical_address[request_id]; if (param_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n"); return 1; } command_packet->byte8.param.sgl[0].address = param_value; command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); imax = TW_POLL_MAX_RETRIES; /* Now drain the controller's aen queue */ do { /* Post command packet */ outl(command_que_value, command_que_addr); /* Now poll for completion */ for (i=0;i<imax;i++) { mdelay(10); status_reg_value = inl(status_reg_addr); if (tw_check_bits(status_reg_value)) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected bits.\n"); return 1; } if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) { response_queue.value = inl(response_que_addr); request_id = (unsigned char)response_queue.u.response_id; if (request_id != 0) { /* Unexpected request id */ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n"); return 1; } if (command_packet->status != 0) { if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) { /* Bad response */ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); return 1; } else { /* We know this is a 3w-1x00, and doesn't support aen's */ return 0; } } /* Now check the aen */ aen = *(unsigned short *)(param->data); aen_code = (aen & 0x0ff); queue = 0; switch (aen_code) { case TW_AEN_QUEUE_EMPTY: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n"); if (first_reset != 1) { continue; } else { finished = 1; } break; case TW_AEN_SOFT_RESET: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n"); if (first_reset == 0) { first_reset = 1; } else { queue = 1; } break; case TW_AEN_DEGRADED_MIRROR: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n"); queue = 1; break; case TW_AEN_CONTROLLER_ERROR: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n"); queue = 1; break; case TW_AEN_REBUILD_FAIL: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n"); queue = 1; break; case TW_AEN_REBUILD_DONE: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n"); queue = 1; break; case TW_AEN_QUEUE_FULL: dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n"); queue = 1; break; default: dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code); queue = 1; } /* Now put the aen on the aen_queue */ if (queue == 1) { tw_dev->aen_queue[tw_dev->aen_tail] = aen_code; if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { tw_dev->aen_tail = TW_Q_START; } else { tw_dev->aen_tail = tw_dev->aen_tail + 1; } if (tw_dev->aen_head == tw_dev->aen_tail) { if (tw_dev->aen_head == TW_Q_LENGTH - 1) { tw_dev->aen_head = TW_Q_START; } else { tw_dev->aen_head = tw_dev->aen_head + 1; } } } found = 1; break; } } if (found == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n"); return 1; } tries++; } while ((tries < TW_MAX_AEN_TRIES) && (finished == 0)); if (tries >=TW_MAX_AEN_TRIES) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Aen queue error.\n"); return 1; } return 0;} /* End tw_aen_drain_queue() *//* This function will read the aen queue from the isr */int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) { TW_Command *command_packet; TW_Param *param; u32 command_que_value = 0, command_que_addr; u32 status_reg_value = 0, status_reg_addr; u32 param_value = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n"); command_que_addr = tw_dev->registers.command_que_addr; status_reg_addr = tw_dev->registers.status_reg_addr; status_reg_value = inl(status_reg_addr); if (tw_check_bits(status_reg_value)) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n"); return 1; } if (tw_dev->command_packet_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n"); return 1; } command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; memset(command_packet, 0, sizeof(TW_Sector)); command_packet->byte0.opcode = TW_OP_GET_PARAM; command_packet->byte0.sgl_offset = 2; command_packet->size = 4; command_packet->request_id = request_id; command_packet->byte3.unit = 0; command_packet->byte3.host_id = 0; command_packet->status = 0; command_packet->flags = 0; command_packet->byte6.parameter_count = 1; command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n"); return 1;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -