5441 lines · 151.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters * * Written By: Anil Ravindranath<anil_ravindranath@pmc-sierra.com> *             PMC-Sierra Inc * * Copyright (C) 2008, 2009 PMC Sierra Inc */#include <linux/fs.h>#include <linux/init.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/wait.h>#include <linux/spinlock.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/firmware.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/hdreg.h>#include <linux/io.h>#include <linux/slab.h>#include <asm/irq.h>#include <asm/processor.h>#include <linux/libata.h>#include <linux/mutex.h>#include <linux/ktime.h>#include <scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsicam.h> #include "pmcraid.h" /* *   Module configuration parameters */static unsigned int pmcraid_debug_log;static unsigned int pmcraid_disable_aen;static unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST;static unsigned int pmcraid_enable_msix; /* * Data structures to support multiple adapters by the LLD. * pmcraid_adapter_count - count of configured adapters */static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0); /* * Supporting user-level control interface through IOCTL commands. * pmcraid_major - major number to use * pmcraid_minor - minor number(s) to use */static unsigned int pmcraid_major;static const struct class pmcraid_class = {	.name = PMCRAID_DEVFILE,};static DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS); /* * Module parameters */MODULE_AUTHOR("Anil Ravindranath<anil_ravindranath@pmc-sierra.com>");MODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver");MODULE_LICENSE("GPL");MODULE_VERSION(PMCRAID_DRIVER_VERSION); module_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(log_level,		 "Enables firmware error code logging, default :1 high-severity"		 " errors, 2: all errors including high-severity errors,"		 " 0: disables logging"); module_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(debug,		 "Enable driver verbose message logging. Set 1 to enable."		 "(default: 0)"); module_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(disable_aen,		 "Disable driver aen notifications to apps. Set 1 to disable."		 "(default: 0)"); /* chip specific constants for PMC MaxRAID controllers (same for * 0x5220 and 0x8010 */static struct pmcraid_chip_details pmcraid_chip_cfg[] = {	{	 .ioastatus = 0x0,	 .ioarrin = 0x00040,	 .mailbox = 0x7FC30,	 .global_intr_mask = 0x00034,	 .ioa_host_intr = 0x0009C,	 .ioa_host_intr_clr = 0x000A0,	 .ioa_host_msix_intr = 0x7FC40,	 .ioa_host_mask = 0x7FC28,	 .ioa_host_mask_clr = 0x7FC28,	 .host_ioa_intr = 0x00020,	 .host_ioa_intr_clr = 0x00020,	 .transop_timeout = 300	 }}; /* * PCI device ids supported by pmcraid driver */static const struct pci_device_id pmcraid_pci_table[] = {	{ PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID),	  0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0]	},	{}}; MODULE_DEVICE_TABLE(pci, pmcraid_pci_table);   /** * pmcraid_sdev_init - Prepare for commands to a device * @scsi_dev: scsi device struct * * This function is called by mid-layer prior to sending any command to the new * device. Stores resource entry details of the device in scsi_device struct. * Queuecommand uses the resource handle and other details to fill up IOARCB * while sending commands to the device. * * Return value: *	  0 on success / -ENXIO if device does not exist */static int pmcraid_sdev_init(struct scsi_device *scsi_dev){	struct pmcraid_resource_entry *temp, *res = NULL;	struct pmcraid_instance *pinstance;	u8 target, bus, lun;	unsigned long lock_flags;	int rc = -ENXIO;	u16 fw_version; 	pinstance = shost_priv(scsi_dev->host); 	fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 	/* Driver exposes VSET and GSCSI resources only; all other device types	 * are not exposed. Resource list is synchronized using resource lock	 * so any traversal or modifications to the list should be done inside	 * this lock	 */	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);	list_for_each_entry(temp, &pinstance->used_res_q, queue) { 		/* do not expose VSETs with order-ids > MAX_VSET_TARGETS */		if (RES_IS_VSET(temp->cfg_entry)) {			if (fw_version <= PMCRAID_FW_VERSION_1)				target = temp->cfg_entry.unique_flags1;			else				target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF; 			if (target > PMCRAID_MAX_VSET_TARGETS)				continue;			bus = PMCRAID_VSET_BUS_ID;			lun = 0;		} else if (RES_IS_GSCSI(temp->cfg_entry)) {			target = RES_TARGET(temp->cfg_entry.resource_address);			bus = PMCRAID_PHYS_BUS_ID;			lun = RES_LUN(temp->cfg_entry.resource_address);		} else {			continue;		} 		if (bus == scsi_dev->channel &&		    target == scsi_dev->id &&		    lun == scsi_dev->lun) {			res = temp;			break;		}	} 	if (res) {		res->scsi_dev = scsi_dev;		scsi_dev->hostdata = res;		res->change_detected = 0;		atomic_set(&res->read_failures, 0);		atomic_set(&res->write_failures, 0);		rc = 0;	}	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);	return rc;} /** * pmcraid_sdev_configure - Configures a SCSI device * @scsi_dev: scsi device struct * @lim: queue limits * * This function is executed by SCSI mid layer just after a device is first * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the * timeout value (default 30s) will be over-written to a higher value (60s) * and max_sectors value will be over-written to 512. It also sets queue depth * to host->cmd_per_lun value * * Return value: *	  0 on success */static int pmcraid_sdev_configure(struct scsi_device *scsi_dev,				  struct queue_limits *lim){	struct pmcraid_resource_entry *res = scsi_dev->hostdata; 	if (!res)		return 0; 	/* LLD exposes VSETs and Enclosure devices only */	if (RES_IS_GSCSI(res->cfg_entry) &&	    scsi_dev->type != TYPE_ENCLOSURE)		return -ENXIO; 	pmcraid_info("configuring %x:%x:%x:%x\n",		     scsi_dev->host->unique_id,		     scsi_dev->channel,		     scsi_dev->id,		     (u8)scsi_dev->lun); 	if (RES_IS_GSCSI(res->cfg_entry)) {		scsi_dev->allow_restart = 1;	} else if (RES_IS_VSET(res->cfg_entry)) {		scsi_dev->allow_restart = 1;		blk_queue_rq_timeout(scsi_dev->request_queue,				     PMCRAID_VSET_IO_TIMEOUT);		lim->max_hw_sectors = PMCRAID_VSET_MAX_SECTORS;	} 	/*	 * We never want to report TCQ support for these types of devices.	 */	if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry))		scsi_dev->tagged_supported = 0; 	return 0;} /** * pmcraid_sdev_destroy - Unconfigure a SCSI device before removing it * * @scsi_dev: scsi device struct * * This is called by mid-layer before removing a device. Pointer assignments * done in pmcraid_sdev_init will be reset to NULL here. * * Return value *   none */static void pmcraid_sdev_destroy(struct scsi_device *scsi_dev){	struct pmcraid_resource_entry *res; 	res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; 	if (res)		res->scsi_dev = NULL; 	scsi_dev->hostdata = NULL;} /** * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set * * Return value *	actual depth set */static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth){	if (depth > PMCRAID_MAX_CMD_PER_LUN)		depth = PMCRAID_MAX_CMD_PER_LUN;	return scsi_change_queue_depth(scsi_dev, depth);} /** * pmcraid_init_cmdblk - initializes a command block * * @cmd: pointer to struct pmcraid_cmd to be initialized * @index: if >=0 first time initialization; otherwise reinitialization * * Return Value *	 None */static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index){	struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);	dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; 	if (index >= 0) {		/* first time initialization (called from  probe) */		u32 ioasa_offset =			offsetof(struct pmcraid_control_block, ioasa); 		cmd->index = index;		ioarcb->response_handle = cpu_to_le32(index << 2);		ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr);		ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset);		ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa));	} else {		/* re-initialization of various lengths, called once command is		 * processed by IOA		 */		memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN);		ioarcb->hrrq_id = 0;		ioarcb->request_flags0 = 0;		ioarcb->request_flags1 = 0;		ioarcb->cmd_timeout = 0;		ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);		ioarcb->ioadl_bus_addr = 0;		ioarcb->ioadl_length = 0;		ioarcb->data_transfer_length = 0;		ioarcb->add_cmd_param_length = 0;		ioarcb->add_cmd_param_offset = 0;		cmd->ioa_cb->ioasa.ioasc = 0;		cmd->ioa_cb->ioasa.residual_data_length = 0;		cmd->time_left = 0;	} 	cmd->cmd_done = NULL;	cmd->scsi_cmd = NULL;	cmd->release = 0;	cmd->completion_req = 0;	cmd->sense_buffer = NULL;	cmd->sense_buffer_dma = 0;	cmd->dma_handle = 0;	timer_setup(&cmd->timer, NULL, 0);} /** * pmcraid_reinit_cmdblk - reinitialize a command block * * @cmd: pointer to struct pmcraid_cmd to be reinitialized * * Return Value *	 None */static void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd){	pmcraid_init_cmdblk(cmd, -1);} /** * pmcraid_get_free_cmd - get a free cmd block from command block pool * @pinstance: adapter instance structure * * Return Value: *	returns pointer to cmd block or NULL if no blocks are available */static struct pmcraid_cmd *pmcraid_get_free_cmd(	struct pmcraid_instance *pinstance){	struct pmcraid_cmd *cmd = NULL;	unsigned long lock_flags; 	/* free cmd block list is protected by free_pool_lock */	spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); 	if (!list_empty(&pinstance->free_cmd_pool)) {		cmd = list_entry(pinstance->free_cmd_pool.next,				 struct pmcraid_cmd, free_list);		list_del(&cmd->free_list);	}	spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); 	/* Initialize the command block before giving it the caller */	if (cmd != NULL)		pmcraid_reinit_cmdblk(cmd);	return cmd;} /** * pmcraid_return_cmd - return a completed command block back into free pool * @cmd: pointer to the command block * * Return Value: *	nothing */static void pmcraid_return_cmd(struct pmcraid_cmd *cmd){	struct pmcraid_instance *pinstance = cmd->drv_inst;	unsigned long lock_flags; 	spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags);	list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool);	spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);} /** * pmcraid_read_interrupts -  reads IOA interrupts * * @pinstance: pointer to adapter instance structure * * Return value *	 interrupts read from IOA */static u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance){	return (pinstance->interrupt_mode) ?		ioread32(pinstance->int_regs.ioa_host_msix_interrupt_reg) :		ioread32(pinstance->int_regs.ioa_host_interrupt_reg);} /** * pmcraid_disable_interrupts - Masks and clears all specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to disable * * Return Value *	 None */static void pmcraid_disable_interrupts(	struct pmcraid_instance *pinstance,	u32 intrs){	u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);	u32 nmask = gmask | GLOBAL_INTERRUPT_MASK; 	iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg);	iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg);	ioread32(pinstance->int_regs.global_interrupt_mask_reg); 	if (!pinstance->interrupt_mode) {		iowrite32(intrs,			pinstance->int_regs.ioa_host_interrupt_mask_reg);		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);	}} /** * pmcraid_enable_interrupts - Enables specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to enable * * Return Value *	 None */static void pmcraid_enable_interrupts(	struct pmcraid_instance *pinstance,	u32 intrs){	u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);	u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK); 	iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); 	if (!pinstance->interrupt_mode) {		iowrite32(~intrs,			 pinstance->int_regs.ioa_host_interrupt_mask_reg);		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);	} 	pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n",		ioread32(pinstance->int_regs.global_interrupt_mask_reg),		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg));} /** * pmcraid_clr_trans_op - clear trans to op interrupt * * @pinstance: pointer to per adapter instance structure * * Return Value *	 None */static void pmcraid_clr_trans_op(	struct pmcraid_instance *pinstance){	unsigned long lock_flags; 	if (!pinstance->interrupt_mode) {		iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,			pinstance->int_regs.ioa_host_interrupt_mask_reg);		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);		iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,			pinstance->int_regs.ioa_host_interrupt_clr_reg);		ioread32(pinstance->int_regs.ioa_host_interrupt_clr_reg);	} 	if (pinstance->reset_cmd != NULL) {		timer_delete(&pinstance->reset_cmd->timer);		spin_lock_irqsave(			pinstance->host->host_lock, lock_flags);

Lines 1–500 of 5441.