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
Controllers supported by this driver:
LSI 3ware 9750 6Gb/s SAS/SATA-RAID
Bugs/Comments/Suggestions should be mailed to: aradford@gmail.com
History ------- 3.26.02.000 - Initial driver release.
*/
/* This function will attempt to post a command packet to the board */ staticint twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
{
dma_addr_t command_que_value;
/* This function hands scsi cdb's to the firmware */ staticint twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, unsignedchar *cdb, int use_sg,
TW_SG_Entry_ISO *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet; int i, sg_count; struct scsi_cmnd *srb = NULL; struct scatterlist *sg; int retval = 1;
if (tw_dev->srb[request_id])
srb = tw_dev->srb[request_id];
/* Update some stats */ if (srb) {
tw_dev->sector_count = scsi_bufflen(srb) / 512; if (tw_dev->sector_count > tw_dev->max_sector_count)
tw_dev->max_sector_count = tw_dev->sector_count;
tw_dev->sgl_entries = scsi_sg_count(srb); if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
tw_dev->max_sgl_entries = tw_dev->sgl_entries;
}
/* Now post the command to the board */
retval = twl_post_command_packet(tw_dev, request_id);
out: return retval;
} /* End twl_scsiop_execute_scsi() */
/* This function will read the aen queue from the isr */ staticint twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar cdb[TW_MAX_CDB_LEN];
TW_SG_Entry_ISO sglist[1];
TW_Command_Full *full_command_packet; int retval = 1;
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command packet */ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue"); goto out;
}
retval = 0;
out: return retval;
} /* End twl_aen_read_queue() */
/* This function will sync firmware time with the host time */ staticvoid twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
time64_t local_time;
/* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime);
schedulertime = cpu_to_le32(schedulertime);
memcpy(param->data, &schedulertime, sizeof(u32));
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command */
twl_post_command_packet(tw_dev, request_id);
} /* End twl_aen_sync_time() */
/* This function will assign an available request id */ staticvoid twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
{
*request_id = tw_dev->free_queue[tw_dev->free_head];
tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
tw_dev->state[*request_id] = TW_S_STARTED;
} /* End twl_get_request_id() */
/* This function will free a request id */ staticvoid twl_free_request_id(TW_Device_Extension *tw_dev, int request_id)
{
tw_dev->free_queue[tw_dev->free_tail] = request_id;
tw_dev->state[request_id] = TW_S_FINISHED;
tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
} /* End twl_free_request_id() */
/* This function will complete an aen request from the isr */ staticint twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Command_Apache_Header *header; unsignedshort aen; int retval = 1;
/* First check for internal completion of set param for time sync */ if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) { /* Keep reading the queue in case there are more aen's */ if (twl_aen_read_queue(tw_dev, request_id)) goto out2; else {
retval = 0; goto out;
}
}
switch (aen) { case TW_AEN_QUEUE_EMPTY: /* Quit reading the queue if this is the last one */ break; case TW_AEN_SYNC_TIME_WITH_HOST:
twl_aen_sync_time(tw_dev, request_id);
retval = 0; goto out; default:
twl_aen_queue_event(tw_dev, header);
/* If there are more aen's, keep reading the queue */ if (twl_aen_read_queue(tw_dev, request_id)) goto out2; else {
retval = 0; goto out;
}
}
retval = 0;
out2:
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
out: return retval;
} /* End twl_aen_complete() */
/* This function will poll for a response */ staticint twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{ unsignedlong before;
dma_addr_t mfa;
u32 regh, regl;
u32 response; int retval = 1; int found = 0;
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
do { /* Send command to the board */ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense"); goto out;
}
/* Now poll for completion */ if (twl_poll_response(tw_dev, request_id, 30)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue");
tw_dev->posted_request_count--; goto out;
}
/* This function will allocate memory and check if it is correctly aligned */ staticint twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{ int i;
dma_addr_t dma_handle; unsignedlong *cpu_addr; int retval = 1;
for (i = 0; i < TW_Q_LENGTH; i++) { switch(which) { case 0:
tw_dev->command_packet_phys[i] = dma_handle+(i*size);
tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsignedchar *)cpu_addr + (i*size)); break; case 1:
tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
tw_dev->generic_buffer_virt[i] = (unsignedlong *)((unsignedchar *)cpu_addr + (i*size)); break; case 2:
tw_dev->sense_buffer_phys[i] = dma_handle+(i*size);
tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsignedchar *)cpu_addr + (i*size)); break;
}
}
retval = 0;
out: return retval;
} /* End twl_allocate_memory() */
/* This function will load the request id and various sgls for ioctls */ staticvoid twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
{
TW_Command *oldcommand;
TW_Command_Apache *newcommand;
TW_SG_Entry_ISO *sgl; unsignedint pae = 0;
/* Now copy down the entire ioctl */ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache))) goto out3;
/* See which ioctl we are doing */ switch (cmd) { case TW_IOCTL_FIRMWARE_PASS_THROUGH:
spin_lock_irqsave(tw_dev->host->host_lock, flags);
twl_get_request_id(tw_dev, &request_id);
/* Flag internal command */
tw_dev->srb[request_id] = NULL;
/* Flag chrdev ioctl */
tw_dev->chrdev_request_id = request_id;
/* Load request id and sglist for both command types */
twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
/* Now post the command packet to the controller */
twl_post_command_packet(tw_dev, request_id);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
/* Now wait for command to complete */
timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
/* We timed out, and didn't get an interrupt */ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */
printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
tw_dev->host->host_no, TW_DRIVER, 0x6,
cmd);
retval = -EIO;
twl_reset_device_extension(tw_dev, 1); goto out3;
}
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
/* Now copy the entire response to userspace */ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0)
retval = 0;
out3: /* Now free ioctl buf memory */
dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_Ioctl_Buf_Apache), cpu_addr, dma_handle);
out2:
mutex_unlock(&tw_dev->ioctl_lock);
out:
mutex_unlock(&twl_chrdev_mutex); return retval;
} /* End twl_chrdev_ioctl() */
/* This function handles open for the character device */ staticint twl_chrdev_open(struct inode *inode, struct file *file)
{ unsignedint minor_number; int retval = -ENODEV;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EACCES; goto out;
}
minor_number = iminor(inode); if (minor_number >= twl_device_extension_count) goto out;
retval = 0;
out: return retval;
} /* End twl_chrdev_open() */
/* This function passes sense data from firmware to scsi layer */ staticint twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host)
{
TW_Command_Apache_Header *header;
TW_Command_Full *full_command_packet; unsignedshort error; char *error_str;
/* This function will free up device extension resources */ staticvoid twl_free_device_extension(TW_Device_Extension *tw_dev)
{ if (tw_dev->command_packet_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Command_Full)*TW_Q_LENGTH,
tw_dev->command_packet_virt[0],
tw_dev->command_packet_phys[0]);
if (tw_dev->generic_buffer_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev,
TW_SECTOR_SIZE*TW_Q_LENGTH,
tw_dev->generic_buffer_virt[0],
tw_dev->generic_buffer_phys[0]);
if (tw_dev->sense_buffer_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Command_Apache_Header)*
TW_Q_LENGTH,
tw_dev->sense_buffer_virt[0],
tw_dev->sense_buffer_phys[0]);
kfree(tw_dev->event_queue[0]);
} /* End twl_free_device_extension() */
/* This function will get parameter table entries from the firmware */ staticvoid *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param; void *retval = NULL;
/* Post the command packet to the board */
twl_post_command_packet(tw_dev, request_id);
/* Poll for completion */ if (twl_poll_response(tw_dev, request_id, 30))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param") else
retval = (void *)&(param->data[0]);
/* This function will initialize the fields of a device extension */ staticint twl_initialize_device_extension(TW_Device_Extension *tw_dev)
{ int i, retval = 1;
/* This function will handle attention interrupts */ staticint twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
{ int retval = 1;
u32 request_id, doorbell;
/* Read doorbell status */
doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev));
/* Check for controller errors */ if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing"); goto out;
}
/* Check if we need to perform an AEN drain */ if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) { if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
twl_get_request_id(tw_dev, &request_id); if (twl_aen_read_queue(tw_dev, request_id)) {
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
}
}
}
/* Check for correct state */ if (tw_dev->state[request_id] != TW_S_POSTED) { if (tw_dev->srb[request_id] != NULL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted");
TWL_MASK_INTERRUPTS(tw_dev); goto twl_interrupt_bail;
}
}
/* Check for internal command completion */ if (tw_dev->srb[request_id] == NULL) { if (request_id != tw_dev->chrdev_request_id) { if (twl_aen_complete(tw_dev, request_id))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt");
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
wake_up(&tw_dev->ioctl_wqueue);
}
} else {
cmd = tw_dev->srb[request_id];
if (!error)
cmd->result = (DID_OK << 16);
/* Report residual bytes for single sgl */ if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) { if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length);
}
/* Now complete the io */
scsi_dma_unmap(cmd);
scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
}
/* Check for another response interrupt */
reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
}
twl_interrupt_bail:
spin_unlock(tw_dev->host->host_lock); return IRQ_RETVAL(handled);
} /* End twl_interrupt() */
/* This function will poll for a register change */ staticint twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds)
{ unsignedlong before; int retval = 1;
u32 reg_value;
reg_value = readl(reg);
before = jiffies;
while ((reg_value & value) != result) {
reg_value = readl(reg); if (time_after(jiffies, before + HZ * seconds)) goto out;
msleep(50);
}
retval = 0;
out: return retval;
} /* End twl_poll_register() */
/* This function will reset a controller */ staticint twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
{ int retval = 1; int i = 0;
u32 status = 0; unsignedshort fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; unsignedshort fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
u32 init_connect_result = 0; int tries = 0; int do_soft_reset = soft_reset;
while (tries < TW_MAX_RESET_TRIES) { /* Do a soft reset if one is needed */ if (do_soft_reset) {
TWL_SOFT_RESET(tw_dev);
/* Make sure controller is in a good state */ if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence");
tries++; continue;
} if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence");
tries++; continue;
}
}
/* Load sense buffers */ while (i < TW_Q_LENGTH) {
writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
/* Check status for over-run after each write */
status = readl(TWL_STATUS_REG_ADDR(tw_dev)); if (!(status & TWL_STATUS_OVERRUN_SUBMIT))
i++;
}
/* Now check status */
status = readl(TWL_STATUS_REG_ADDR(tw_dev)); if (status) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers");
do_soft_reset = 1;
tries++; continue;
}
/* Drain the AEN queue */ if (twl_aen_drain_queue(tw_dev, soft_reset)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
/* If we got here, controller is in a good state */
retval = 0; goto out;
}
out: return retval;
} /* End twl_reset_sequence() */
/* This function will reset a device extension */ staticint twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
{ int i = 0, retval = 1; unsignedlong flags = 0;
/* Block SCSI requests while we are resetting */ if (ioctl_reset)
scsi_block_requests(tw_dev->host);
/* Abort all requests that are in progress */ for (i = 0; i < TW_Q_LENGTH; i++) { if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) { struct scsi_cmnd *cmd = tw_dev->srb[i];
retval = 0;
out: if (ioctl_reset)
scsi_unblock_requests(tw_dev->host); return retval;
} /* End twl_reset_device_extension() */
/* This funciton returns unit geometry in cylinders/heads/sectors */ staticint twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
{ int heads, sectors;
/* This is the new scsi eh reset function */ staticint twl_scsi_eh_reset(struct scsi_cmnd *SCpnt)
{
TW_Device_Extension *tw_dev = NULL; int retval = FAILED;
/* Make sure we are not issuing an ioctl or resetting from ioctl */
mutex_lock(&tw_dev->ioctl_lock);
/* Now reset the card and some of the device extension data */ if (twl_reset_device_extension(tw_dev, 0)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset"); goto out;
}
/* This is the main scsi queue function to handle scsi opcodes */ staticint twl_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{ void (*done)(struct scsi_cmnd *) = scsi_done; int request_id, retval;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
/* If we are resetting due to timed out ioctl, report as busy */ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
retval = SCSI_MLQUEUE_HOST_BUSY; goto out;
}
/* Get a free request id */
twl_get_request_id(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
/* This function tells the controller to shut down */ staticvoid __twl_shutdown(TW_Device_Extension *tw_dev)
{ /* Disable interrupts */
TWL_MASK_INTERRUPTS(tw_dev);
/* Free up the IRQ */
free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no);
/* Tell the card we are shutting down */ if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed");
} else {
printk(KERN_WARNING "3w-sas: Shutdown complete.\n");
}
/* Clear doorbell interrupt just before exit */
TWL_CLEAR_DB_INTERRUPT(tw_dev);
} /* End __twl_shutdown() */
if (tw_dev->online)
__twl_shutdown(tw_dev);
} /* End twl_shutdown() */
/* This function configures unit settings when a unit is coming on-line */ staticint twl_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{ /* Force 60 second timeout */
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
/* This function will probe and initialize a card */ staticint twl_probe(struct pci_dev *pdev, conststruct pci_device_id *dev_id)
{ struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev; int retval = -ENODEV; int *ptr_phycount, phycount=0;
retval = pci_enable_device(pdev); if (retval) {
TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device"); goto out_disable_device;
}
pci_set_master(pdev);
pci_try_set_mwi(pdev);
retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (retval) {
TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
retval = -ENODEV; goto out_disable_device;
}
host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) {
TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension");
retval = -ENOMEM; goto out_disable_device;
}
tw_dev = shost_priv(host);
/* Save values to device extension */
tw_dev->host = host;
tw_dev->tw_pci_dev = pdev;
if (twl_initialize_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension");
retval = -ENOMEM; goto out_free_device_extension;
}
/* Request IO regions */
retval = pci_request_regions(pdev, "3w-sas"); if (retval) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region"); goto out_free_device_extension;
}
/* Save base address, use region 1 */
tw_dev->base_addr = pci_iomap(pdev, 1, 0); if (!tw_dev->base_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap");
retval = -ENOMEM; goto out_release_mem_region;
}
/* Disable interrupts on the card */
TWL_MASK_INTERRUPTS(tw_dev);
/* Initialize the card */ if (twl_reset_sequence(tw_dev, 0)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe");
retval = -ENOMEM; goto out_iounmap;
}
/* Set host specific parameters */
host->max_id = TW_MAX_UNITS;
host->max_cmd_len = TW_MAX_CDB_LEN;
host->max_lun = TW_MAX_LUNS;
host->max_channel = 0;
/* Register the card with the kernel SCSI layer */
retval = scsi_add_host(host, &pdev->dev); if (retval) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed"); goto out_iounmap;
}
pci_set_drvdata(pdev, host);
printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n",
host->host_no,
(char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH),
(u64)pci_resource_start(pdev, 1), pdev->irq);
/* This function is called to remove a device */ staticvoid twl_remove(struct pci_dev *pdev)
{ struct Scsi_Host *host = pci_get_drvdata(pdev);
TW_Device_Extension *tw_dev;
/* Free up the mem region */
pci_release_regions(pdev);
/* Free up device extension resources */
twl_free_device_extension(tw_dev);
scsi_host_put(tw_dev->host);
pci_disable_device(pdev);
twl_device_extension_count--;
} /* End twl_remove() */
/* This function is called on PCI suspend */ staticint __maybe_unused twl_suspend(struct device *dev)
{ struct Scsi_Host *host = dev_get_drvdata(dev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
/* This function is called on driver initialization */ staticint __init twl_init(void)
{
printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
return pci_register_driver(&twl_driver);
} /* End twl_init() */
/* This function is called on driver exit */ staticvoid __exit twl_exit(void)
{
pci_unregister_driver(&twl_driver);
} /* End twl_exit() */
module_init(twl_init);
module_exit(twl_exit);
Messung V0.5
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.