From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/scsi/ipr.c | 209 +++++++++++++++++++++++++++++----------------------- 1 files changed, 117 insertions(+), 92 deletions(-) diff --git a/kernel/drivers/scsi/ipr.c b/kernel/drivers/scsi/ipr.c index 1b04a82..8c37673 100644 --- a/kernel/drivers/scsi/ipr.c +++ b/kernel/drivers/scsi/ipr.c @@ -1,24 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * ipr.c -- driver for IBM Power Linux RAID adapters * * Written By: Brian King <brking@us.ibm.com>, IBM Corporation * * Copyright (C) 2003, 2004 IBM 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. - * - * 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. - * - * 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 - * */ /* @@ -684,6 +670,7 @@ /** * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block * @ipr_cmd: ipr command struct + * @fast_done: fast done function call-back * * Return value: * none @@ -701,7 +688,7 @@ /** * __ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block - * @ioa_cfg: ioa config struct + * @hrrq: hrr queue * * Return value: * pointer to ipr command struct @@ -751,7 +738,6 @@ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg, u32 clr_ints) { - volatile u32 int_reg; int i; /* Stop new interrupts */ @@ -771,7 +757,7 @@ if (ioa_cfg->sis64) writel(~0, ioa_cfg->regs.clr_interrupt_reg); writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg32); - int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + readl(ioa_cfg->regs.sense_interrupt_reg); } /** @@ -1178,7 +1164,7 @@ default: res->ata_class = ATA_DEV_UNKNOWN; break; - }; + } } /** @@ -1301,7 +1287,7 @@ /** * __ipr_format_res_path - Format the resource path for printing. * @res_path: resource path - * @buf: buffer + * @buffer: buffer * @len: length of buffer provided * * Return value: @@ -1313,9 +1299,9 @@ char *p = buffer; *p = '\0'; - p += snprintf(p, buffer + len - p, "%02X", res_path[0]); + p += scnprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) - p += snprintf(p, buffer + len - p, "-%02X", res_path[i]); + p += scnprintf(p, buffer + len - p, "-%02X", res_path[i]); return buffer; } @@ -1324,7 +1310,7 @@ * ipr_format_res_path - Format the resource path for printing. * @ioa_cfg: ioa config struct * @res_path: resource path - * @buf: buffer + * @buffer: buffer * @len: length of buffer provided * * Return value: @@ -1336,7 +1322,7 @@ char *p = buffer; *p = '\0'; - p += snprintf(p, buffer + len - p, "%d/", ioa_cfg->host->host_no); + p += scnprintf(p, buffer + len - p, "%d/", ioa_cfg->host->host_no); __ipr_format_res_path(res_path, p, len - (buffer - p)); return buffer; } @@ -1405,7 +1391,6 @@ * ipr_clear_res_target - Clear the bit in the bit map representing the target * for the resource. * @res: resource entry struct - * @cfgtew: config table entry wrapper struct * * Return value: * none @@ -1531,23 +1516,22 @@ } /** - * strip_and_pad_whitespace - Strip and pad trailing whitespace. - * @i: index into buffer - * @buf: string to modify + * strip_whitespace - Strip and pad trailing whitespace. + * @i: size of buffer + * @buf: string to modify * - * This function will strip all trailing whitespace, pad the end - * of the string with a single space, and NULL terminate the string. + * This function will strip all trailing whitespace and + * NUL terminate the string. * - * Return value: - * new length of string **/ -static int strip_and_pad_whitespace(int i, char *buf) +static void strip_whitespace(int i, char *buf) { + if (i < 1) + return; + i--; while (i && buf[i] == ' ') i--; - buf[i+1] = ' '; - buf[i+2] = '\0'; - return i + 2; + buf[i+1] = '\0'; } /** @@ -1562,19 +1546,21 @@ static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb, struct ipr_vpd *vpd) { - char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3]; - int i = 0; + char vendor_id[IPR_VENDOR_ID_LEN + 1]; + char product_id[IPR_PROD_ID_LEN + 1]; + char sn[IPR_SERIAL_NUM_LEN + 1]; - memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); - i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer); + memcpy(vendor_id, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); + strip_whitespace(IPR_VENDOR_ID_LEN, vendor_id); - memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN); - i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer); + memcpy(product_id, vpd->vpids.product_id, IPR_PROD_ID_LEN); + strip_whitespace(IPR_PROD_ID_LEN, product_id); - memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN); - buffer[IPR_SERIAL_NUM_LEN + i] = '\0'; + memcpy(sn, vpd->sn, IPR_SERIAL_NUM_LEN); + strip_whitespace(IPR_SERIAL_NUM_LEN, sn); - ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer); + ipr_hcam_err(hostrcb, "%s VPID/SN: %s %s %s\n", prefix, + vendor_id, product_id, sn); } /** @@ -2681,7 +2667,7 @@ /** * ipr_timeout - An internally generated op has timed out. - * @ipr_cmd: ipr command struct + * @t: Timer context used to fetch ipr command struct * * This function blocks host requests and initiates an * adapter reset. @@ -2714,7 +2700,7 @@ /** * ipr_oper_timeout - Adapter timed out transitioning to operational - * @ipr_cmd: ipr command struct + * @t: Timer context used to fetch ipr command struct * * This function blocks host requests and initiates an * adapter reset. @@ -3498,6 +3484,7 @@ /** * ipr_show_fw_version - Show the firmware version * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -3532,6 +3519,7 @@ /** * ipr_show_log_level - Show the adapter's error logging level * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -3554,7 +3542,9 @@ /** * ipr_store_log_level - Change the adapter's error logging level * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer + * @count: buffer size * * Return value: * number of bytes printed to buffer @@ -3585,6 +3575,7 @@ /** * ipr_store_diagnostics - IOA Diagnostics interface * @dev: device struct + * @attr: device attribute (unused) * @buf: buffer * @count: buffer size * @@ -3645,7 +3636,8 @@ /** * ipr_show_adapter_state - Show the adapter's state - * @class_dev: device struct + * @dev: device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -3671,6 +3663,7 @@ /** * ipr_store_adapter_state - Change adapter state * @dev: device struct + * @attr: device attribute (unused) * @buf: buffer * @count: buffer size * @@ -3722,6 +3715,7 @@ /** * ipr_store_reset_adapter - Reset the adapter * @dev: device struct + * @attr: device attribute (unused) * @buf: buffer * @count: buffer size * @@ -3763,6 +3757,7 @@ /** * ipr_show_iopoll_weight - Show ipr polling mode * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -3786,7 +3781,9 @@ /** * ipr_store_iopoll_weight - Change the adapter's polling mode * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer + * @count: buffer size * * Return value: * number of bytes printed to buffer @@ -3885,7 +3882,7 @@ /** * ipr_free_ucode_buffer - Frees a microcode download buffer - * @p_dnld: scatter/gather list pointer + * @sglist: scatter/gather list pointer * * Free a DMA'able ucode download buffer previously allocated with * ipr_alloc_ucode_buffer @@ -3915,22 +3912,23 @@ u8 *buffer, u32 len) { int bsize_elem, i, result = 0; - struct scatterlist *scatterlist; + struct scatterlist *sg; void *kaddr; /* Determine the actual number of bytes per element */ bsize_elem = PAGE_SIZE * (1 << sglist->order); - scatterlist = sglist->scatterlist; + sg = sglist->scatterlist; - for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) { - struct page *page = sg_page(&scatterlist[i]); + for (i = 0; i < (len / bsize_elem); i++, sg = sg_next(sg), + buffer += bsize_elem) { + struct page *page = sg_page(sg); kaddr = kmap(page); memcpy(kaddr, buffer, bsize_elem); kunmap(page); - scatterlist[i].length = bsize_elem; + sg->length = bsize_elem; if (result != 0) { ipr_trace; @@ -3939,13 +3937,13 @@ } if (len % bsize_elem) { - struct page *page = sg_page(&scatterlist[i]); + struct page *page = sg_page(sg); kaddr = kmap(page); memcpy(kaddr, buffer, len % bsize_elem); kunmap(page); - scatterlist[i].length = len % bsize_elem; + sg->length = len % bsize_elem; } sglist->buffer_len = len; @@ -3966,6 +3964,7 @@ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64; struct scatterlist *scatterlist = sglist->scatterlist; + struct scatterlist *sg; int i; ipr_cmd->dma_use_sg = sglist->num_dma_sg; @@ -3974,10 +3973,10 @@ ioarcb->ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg); - for (i = 0; i < ipr_cmd->dma_use_sg; i++) { + for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) { ioadl64[i].flags = cpu_to_be32(IPR_IOADL_FLAGS_WRITE); - ioadl64[i].data_len = cpu_to_be32(sg_dma_len(&scatterlist[i])); - ioadl64[i].address = cpu_to_be64(sg_dma_address(&scatterlist[i])); + ioadl64[i].data_len = cpu_to_be32(sg_dma_len(sg)); + ioadl64[i].address = cpu_to_be64(sg_dma_address(sg)); } ioadl64[i-1].flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST); @@ -3997,6 +3996,7 @@ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl; struct scatterlist *scatterlist = sglist->scatterlist; + struct scatterlist *sg; int i; ipr_cmd->dma_use_sg = sglist->num_dma_sg; @@ -4006,11 +4006,11 @@ ioarcb->ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); - for (i = 0; i < ipr_cmd->dma_use_sg; i++) { + for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) { ioadl[i].flags_and_data_len = - cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(&scatterlist[i])); + cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(sg)); ioadl[i].address = - cpu_to_be32(sg_dma_address(&scatterlist[i])); + cpu_to_be32(sg_dma_address(sg)); } ioadl[i-1].flags_and_data_len |= @@ -4070,7 +4070,8 @@ /** * ipr_store_update_fw - Update the firmware on the adapter - * @class_dev: device struct + * @dev: device struct + * @attr: device attribute (unused) * @buf: buffer * @count: buffer size * @@ -4150,6 +4151,7 @@ /** * ipr_show_fw_type - Show the adapter's firmware type. * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -4491,7 +4493,6 @@ * ipr_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set - * @reason: calling context * * Return value: * actual depth set @@ -4661,6 +4662,7 @@ /** * ipr_show_raw_mode - Show the adapter's raw mode * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer * * Return value: @@ -4688,7 +4690,9 @@ /** * ipr_store_raw_mode - Change the adapter's raw mode * @dev: class device struct + * @attr: device attribute (unused) * @buf: buffer + * @count: buffer size * * Return value: * number of bytes printed to buffer @@ -5071,7 +5075,7 @@ /** * ipr_cmnd_is_free - Check if a command is free or not - * @ipr_cmd ipr command struct + * @ipr_cmd: ipr command struct * * Returns: * true / false @@ -5107,7 +5111,7 @@ /** * ipr_wait_for_ops - Wait for matching commands to complete - * @ipr_cmd: ipr command struct + * @ioa_cfg: ioa config struct * @device: device to match (sdev) * @match: match function to use * @@ -5272,6 +5276,7 @@ * ipr_sata_reset - Reset the SATA port * @link: SATA link to reset * @classes: class of the attached device + * @deadline: unused * * This function issues a SATA phy reset to the affected ATA link. * @@ -5451,7 +5456,7 @@ /** * ipr_abort_timeout - An abort task has timed out - * @ipr_cmd: ipr command struct + * @t: Timer context used to fetch ipr command struct * * This function handles when an abort task times out. If this * happens we issue a bus reset since we have resources tied @@ -5505,7 +5510,7 @@ struct ipr_ioa_cfg *ioa_cfg; struct ipr_resource_entry *res; struct ipr_cmd_pkt *cmd_pkt; - u32 ioasc, int_reg; + u32 ioasc; int i, op_found = 0; struct ipr_hrr_queue *hrrq; @@ -5528,7 +5533,7 @@ * by a still not detected EEH error. In such cases, reading a register will * trigger the EEH recovery infrastructure. */ - int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + readl(ioa_cfg->regs.sense_interrupt_reg); if (!ipr_is_gscsi(res)) return FAILED; @@ -5580,7 +5585,8 @@ /** * ipr_eh_abort - Abort a single op - * @scsi_cmd: scsi command struct + * @shost: scsi host struct + * @elapsed_time: elapsed time * * Return value: * 0 if scan in progress / 1 if scan is complete @@ -5707,6 +5713,7 @@ * ipr_isr_eh - Interrupt service routine error handler * @ioa_cfg: ioa config struct * @msg: message to log + * @number: various meanings depending on the caller/message * * Return value: * none @@ -5773,7 +5780,6 @@ static int ipr_iopoll(struct irq_poll *iop, int budget) { - struct ipr_ioa_cfg *ioa_cfg; struct ipr_hrr_queue *hrrq; struct ipr_cmnd *ipr_cmd, *temp; unsigned long hrrq_flags; @@ -5781,7 +5787,6 @@ LIST_HEAD(doneq); hrrq = container_of(iop, struct ipr_hrr_queue, iopoll); - ioa_cfg = hrrq->ioa_cfg; spin_lock_irqsave(hrrq->lock, hrrq_flags); completed_ops = ipr_process_hrrq(hrrq, budget, &doneq); @@ -6279,8 +6284,7 @@ /** * ipr_gen_sense - Generate SCSI sense data from an IOASA - * @ioasa: IOASA - * @sense_buf: sense data buffer + * @ipr_cmd: ipr command struct * * Return value: * none @@ -6696,7 +6700,8 @@ * Return value: * 0 on success / other on failure **/ -static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +static int ipr_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg) { struct ipr_resource_entry *res; @@ -6712,7 +6717,7 @@ /** * ipr_info - Get information about the card/driver - * @scsi_host: scsi host struct + * @host: scsi host struct * * Return value: * pointer to buffer with description string @@ -6737,7 +6742,11 @@ .name = "IPR", .info = ipr_ioa_info, .ioctl = ipr_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ipr_ioctl, +#endif .queuecommand = ipr_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .eh_abort_handler = ipr_eh_abort, .eh_device_reset_handler = ipr_eh_dev_reset, .eh_host_reset_handler = ipr_eh_host_reset, @@ -6754,7 +6763,6 @@ .sg_tablesize = IPR_MAX_SGLIST, .max_sectors = IPR_IOA_MAX_SECTORS, .cmd_per_lun = IPR_MAX_CMD_PER_LUN, - .use_clustering = ENABLE_CLUSTERING, .shost_attrs = ipr_ioa_attrs, .sdev_attrs = ipr_dev_attrs, .proc_name = IPR_NAME, @@ -7599,7 +7607,7 @@ /** * ipr_build_mode_sense - Builds a mode sense command * @ipr_cmd: ipr command struct - * @res: resource entry struct + * @res_handle: resource entry struct * @parm: Byte 2 of mode sense command * @dma_addr: DMA address of mode sense buffer * @xfer_len: Size of DMA buffer @@ -7946,6 +7954,7 @@ /** * ipr_ioafp_set_caching_parameters - Issue Set Cache parameters service * action + * @ipr_cmd: ipr command struct * * Return value: * none @@ -7982,6 +7991,10 @@ /** * ipr_ioafp_inquiry - Send an Inquiry to the adapter. * @ipr_cmd: ipr command struct + * @flags: flags to send + * @page: page to inquire + * @dma_addr: DMA address + * @xfer_len: transfer data length * * This utility function sends an inquiry to the adapter. * @@ -8272,7 +8285,7 @@ /** * ipr_reset_timer_done - Adapter reset timer function - * @ipr_cmd: ipr command struct + * @t: Timer context used to fetch ipr command struct * * Description: This function is used in adapter reset processing * for timing events. If the reset_cmd pointer in the IOA @@ -8666,7 +8679,6 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; - u32 int_reg; ENTER; ioa_cfg->pdev->state_saved = true; @@ -8682,7 +8694,7 @@ if (ioa_cfg->sis64) { /* Set the adapter to the correct endian mode. */ writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg); - int_reg = readl(ioa_cfg->regs.endian_swap_reg); + readl(ioa_cfg->regs.endian_swap_reg); } if (ioa_cfg->ioa_unit_checked) { @@ -9490,7 +9502,6 @@ * Description: This is the second phase of adapter initialization * This function takes care of initilizing the adapter to the point * where it can accept new commands. - * Return value: * 0 on success / -EIO on failure **/ @@ -9537,8 +9548,7 @@ } } - if (ioa_cfg->ipr_cmd_pool) - dma_pool_destroy(ioa_cfg->ipr_cmd_pool); + dma_pool_destroy(ioa_cfg->ipr_cmd_pool); kfree(ioa_cfg->ipr_cmnd_list); kfree(ioa_cfg->ipr_cmnd_list_dma); @@ -9605,7 +9615,7 @@ /** * ipr_free_all_resources - Free all allocated resources for an adapter. - * @ipr_cmd: ipr command struct + * @ioa_cfg: ioa config struct * * This function frees all allocated resources for the * specified adapter. @@ -9783,7 +9793,7 @@ GFP_KERNEL); if (!ioa_cfg->hrrq[i].host_rrq) { - while (--i > 0) + while (--i >= 0) dma_free_coherent(&pdev->dev, sizeof(u32) * ioa_cfg->hrrq[i].size, ioa_cfg->hrrq[i].host_rrq, @@ -10056,7 +10066,7 @@ ioa_cfg->vectors_info[i].desc, &ioa_cfg->hrrq[i]); if (rc) { - while (--i >= 0) + while (--i > 0) free_irq(pci_irq_vector(pdev, i), &ioa_cfg->hrrq[i]); return rc; @@ -10067,7 +10077,8 @@ /** * ipr_test_intr - Handle the interrupt generated in ipr_test_msi(). - * @pdev: PCI device struct + * @devp: PCI device struct + * @irq: IRQ number * * Description: Simply set the msi_received flag to 1 indicating that * Message Signaled Interrupts are supported. @@ -10093,6 +10104,7 @@ /** * ipr_test_msi - Test for Message Signaled Interrupt (MSI) support. + * @ioa_cfg: ioa config struct * @pdev: PCI device struct * * Description: This routine sets up and initiates a test interrupt to determine @@ -10105,7 +10117,6 @@ static int ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg, struct pci_dev *pdev) { int rc; - volatile u32 int_reg; unsigned long lock_flags = 0; int irq = pci_irq_vector(pdev, 0); @@ -10116,7 +10127,7 @@ ioa_cfg->msi_received = 0; ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_mask_reg32); - int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg); + readl(ioa_cfg->regs.sense_interrupt_mask_reg); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); rc = request_irq(irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg); @@ -10127,7 +10138,7 @@ dev_info(&pdev->dev, "IRQ assigned: %d\n", irq); writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.sense_interrupt_reg32); - int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + readl(ioa_cfg->regs.sense_interrupt_reg); wait_event_timeout(ioa_cfg->msi_wait_q, ioa_cfg->msi_received, HZ); spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); @@ -10538,6 +10549,8 @@ /** * ipr_probe - Adapter hot plug add entry point + * @pdev: pci device struct + * @dev_id: pci device ID * * Return value: * 0 on success / non-zero on failure @@ -10794,6 +10807,7 @@ /** * ipr_halt_done - Shutdown prepare completion + * @ipr_cmd: ipr command struct * * Return value: * none @@ -10805,6 +10819,9 @@ /** * ipr_halt - Issue shutdown prepare to all adapters + * @nb: Notifier block + * @event: Notifier event + * @buf: Notifier data (unused) * * Return value: * NOTIFY_OK on success / NOTIFY_DONE on failure @@ -10854,11 +10871,19 @@ **/ static int __init ipr_init(void) { + int rc; + ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n", IPR_DRIVER_VERSION, IPR_DRIVER_DATE); register_reboot_notifier(&ipr_notifier); - return pci_register_driver(&ipr_driver); + rc = pci_register_driver(&ipr_driver); + if (rc) { + unregister_reboot_notifier(&ipr_notifier); + return rc; + } + + return 0; } /** -- Gitblit v1.6.2