[SCSI] pmcraid: add support for set timestamp command and other fixes

The following are the fixes in this patch:

1. Added support of set timestamp command in the driver
2. Pass all status code to mgmt application. Earlier we were passing
   only failed ones.
3. Call class_destroy after unregister_chrdev and pci_unregister_driver

Signed-off-by: Anil Ravindranath <anil_ravindranath@pmc-sierra.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 4b87657..cf89091 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -1594,10 +1594,12 @@
 	cfg_entry = &ccn_hcam->cfg_entry;
 	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
 
-	pmcraid_info
-		("CCN(%x): %x type: %x lost: %x flags: %x res: %x:%x:%x:%x\n",
+	pmcraid_info("CCN(%x): %x timestamp: %llx type: %x lost: %x flags: %x \
+		 res: %x:%x:%x:%x\n",
 		 pinstance->ccn.hcam->ilid,
 		 pinstance->ccn.hcam->op_code,
+		((pinstance->ccn.hcam->timestamp1) |
+		((pinstance->ccn.hcam->timestamp2 & 0xffffffffLL) << 32)),
 		 pinstance->ccn.hcam->notification_type,
 		 pinstance->ccn.hcam->notification_lost,
 		 pinstance->ccn.hcam->flags,
@@ -1850,6 +1852,7 @@
  *   none
  */
 static void pmcraid_initiate_reset(struct pmcraid_instance *);
+static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd);
 
 static void pmcraid_process_ldn(struct pmcraid_cmd *cmd)
 {
@@ -1881,6 +1884,10 @@
 					       lock_flags);
 			return;
 		}
+		if (fd_ioasc == PMCRAID_IOASC_TIME_STAMP_OUT_OF_SYNC) {
+			pinstance->timestamp_error = 1;
+			pmcraid_set_timestamp(cmd);
+		}
 	} else {
 		dev_info(&pinstance->pdev->dev,
 			"Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc);
@@ -3363,7 +3370,7 @@
 	sg_size = buflen;
 
 	for (i = 0; i < num_elem; i++) {
-		page = alloc_pages(GFP_KERNEL|GFP_DMA, order);
+		page = alloc_pages(GFP_KERNEL|GFP_DMA|__GFP_ZERO, order);
 		if (!page) {
 			for (j = i - 1; j >= 0; j--)
 				__free_pages(sg_page(&scatterlist[j]), order);
@@ -3739,6 +3746,7 @@
 	unsigned long request_buffer;
 	unsigned long request_offset;
 	unsigned long lock_flags;
+	void *ioasa;
 	u32 ioasc;
 	int request_size;
 	int buffer_size;
@@ -3780,6 +3788,11 @@
 	rc = __copy_from_user(buffer,
 			     (struct pmcraid_passthrough_ioctl_buffer *) arg,
 			     sizeof(struct pmcraid_passthrough_ioctl_buffer));
+
+	ioasa =
+	(void *)(arg +
+		offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa));
+
 	if (rc) {
 		pmcraid_err("ioctl: can't copy passthrough buffer\n");
 		rc = -EFAULT;
@@ -3947,22 +3960,14 @@
 	}
 
 out_handle_response:
-	/* If the command failed for any reason, copy entire IOASA buffer and
-	 * return IOCTL success. If copying IOASA to user-buffer fails, return
+	/* copy entire IOASA buffer and return IOCTL success.
+	 * If copying IOASA to user-buffer fails, return
 	 * EFAULT
 	 */
-	if (PMCRAID_IOASC_SENSE_KEY(le32_to_cpu(cmd->ioa_cb->ioasa.ioasc))) {
-		void *ioasa =
-		    (void *)(arg +
-		    offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa));
-
-		pmcraid_info("command failed with %x\n",
-			     le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
-		if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa,
-				 sizeof(struct pmcraid_ioasa))) {
-			pmcraid_err("failed to copy ioasa buffer to user\n");
-			rc = -EFAULT;
-		}
+	if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa,
+		sizeof(struct pmcraid_ioasa))) {
+		pmcraid_err("failed to copy ioasa buffer to user\n");
+		rc = -EFAULT;
 	}
 
 	/* If the data transfer was from device, copy the data onto user
@@ -5147,6 +5152,16 @@
 		pinstance->inq_data = NULL;
 		pinstance->inq_data_baddr = 0;
 	}
+
+	if (pinstance->timestamp_data != NULL) {
+		pci_free_consistent(pinstance->pdev,
+				    sizeof(struct pmcraid_timestamp_data),
+				    pinstance->timestamp_data,
+				    pinstance->timestamp_data_baddr);
+
+		pinstance->timestamp_data = NULL;
+		pinstance->timestamp_data_baddr = 0;
+	}
 }
 
 /**
@@ -5205,6 +5220,20 @@
 		return -ENOMEM;
 	}
 
+	/* allocate DMAable memory for set timestamp data buffer */
+	pinstance->timestamp_data = pci_alloc_consistent(
+					pinstance->pdev,
+					sizeof(struct pmcraid_timestamp_data),
+					&pinstance->timestamp_data_baddr);
+
+	if (pinstance->timestamp_data == NULL) {
+		pmcraid_err("couldn't allocate DMA memory for \
+				set time_stamp \n");
+		pmcraid_release_buffers(pinstance);
+		return -ENOMEM;
+	}
+
+
 	/* Initialize all the command blocks and add them to free pool. No
 	 * need to lock (free_pool_lock) as this is done in initialization
 	 * itself
@@ -5610,6 +5639,68 @@
 }
 
 /**
+ * pmcraid_set_timestamp - set the timestamp to IOAFP
+ *
+ * @cmd: pointer to pmcraid_cmd structure
+ *
+ * Return Value
+ *  0 for success or non-zero for failure cases
+ */
+static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
+{
+	struct pmcraid_instance *pinstance = cmd->drv_inst;
+	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+	__be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN);
+	struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+
+	struct timeval tv;
+	__le64 timestamp;
+
+	do_gettimeofday(&tv);
+	timestamp = tv.tv_sec * 1000;
+
+	pinstance->timestamp_data->timestamp[0] = (__u8)(timestamp);
+	pinstance->timestamp_data->timestamp[1] = (__u8)((timestamp) >> 8);
+	pinstance->timestamp_data->timestamp[2] = (__u8)((timestamp) >> 16);
+	pinstance->timestamp_data->timestamp[3] = (__u8)((timestamp) >> 24);
+	pinstance->timestamp_data->timestamp[4] = (__u8)((timestamp) >> 32);
+	pinstance->timestamp_data->timestamp[5] = (__u8)((timestamp)  >> 40);
+
+	pmcraid_reinit_cmdblk(cmd);
+	ioarcb->request_type = REQ_TYPE_SCSI;
+	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+	ioarcb->cdb[0] = PMCRAID_SCSI_SET_TIMESTAMP;
+	ioarcb->cdb[1] = PMCRAID_SCSI_SERVICE_ACTION;
+	memcpy(&(ioarcb->cdb[6]), &time_stamp_len, sizeof(time_stamp_len));
+
+	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
+					offsetof(struct pmcraid_ioarcb,
+						add_data.u.ioadl[0]));
+	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
+	ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+
+	ioarcb->request_flags0 |= NO_LINK_DESCS;
+	ioarcb->request_flags0 |= TRANSFER_DIR_WRITE;
+	ioarcb->data_transfer_length =
+		cpu_to_le32(sizeof(struct pmcraid_timestamp_data));
+	ioadl = &(ioarcb->add_data.u.ioadl[0]);
+	ioadl->flags = IOADL_FLAGS_LAST_DESC;
+	ioadl->address = cpu_to_le64(pinstance->timestamp_data_baddr);
+	ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_timestamp_data));
+
+	if (!pinstance->timestamp_error) {
+		pinstance->timestamp_error = 0;
+		pmcraid_send_cmd(cmd, pmcraid_set_supported_devs,
+			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
+	} else {
+		pmcraid_send_cmd(cmd, pmcraid_return_cmd,
+			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
+		return;
+	}
+}
+
+
+/**
  * pmcraid_init_res_table - Initialize the resource table
  * @cmd:  pointer to pmcraid command struct
  *
@@ -5720,7 +5811,7 @@
 
 	/* release the resource list lock */
 	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
-	pmcraid_set_supported_devs(cmd);
+	pmcraid_set_timestamp(cmd);
 }
 
 /**
@@ -6054,10 +6145,10 @@
 static void __exit pmcraid_exit(void)
 {
 	pmcraid_netlink_release();
-	class_destroy(pmcraid_class);
 	unregister_chrdev_region(MKDEV(pmcraid_major, 0),
 				 PMCRAID_MAX_ADAPTERS);
 	pci_unregister_driver(&pmcraid_driver);
+	class_destroy(pmcraid_class);
 }
 
 module_init(pmcraid_init);