git: e80317babdb9 - main - mpi3mr: Add NVData Parameter for Host Timestamp Synchronization

From: Warner Losh <imp_at_FreeBSD.org>
Date: Mon, 28 Apr 2025 03:25:22 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=e80317babdb9044962dcbf16c69633579cd90b00

commit e80317babdb9044962dcbf16c69633579cd90b00
Author:     Chandrakanth patil <chandrakanth.patil@broadcom.com>
AuthorDate: 2025-04-27 23:39:23 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2025-04-28 03:22:55 +0000

    mpi3mr: Add NVData Parameter for Host Timestamp Synchronization
    
    The driver now retrieves the Time Stamp value from Driver Page 1
    during load and after controller reset. If the value is valid, it
    is used to enable periodic host timestamp synchronization.
    
    This adds a tunable NVData parameter to control the behavior of
    host time sync, enhancing flexibility and platform-specific control.
    
    Reviewed by:    ssaxena, imp
    Differential Revision:  https://reviews.freebsd.org/D49748
---
 sys/dev/mpi3mr/mpi3mr.c     | 325 ++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/mpi3mr/mpi3mr.h     |  14 +-
 sys/dev/mpi3mr/mpi3mr_pci.c |  25 +++-
 3 files changed, 362 insertions(+), 2 deletions(-)

diff --git a/sys/dev/mpi3mr/mpi3mr.c b/sys/dev/mpi3mr/mpi3mr.c
index c92d05d972de..398569a3963c 100644
--- a/sys/dev/mpi3mr/mpi3mr.c
+++ b/sys/dev/mpi3mr/mpi3mr.c
@@ -1338,6 +1338,7 @@ static const struct {
 		"diagnostic buffer post timeout"
 	},
 	{ MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronus reset" },
+	{ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout" },
 	{ MPI3MR_RESET_REASON_COUNT, "Reset reason count" },
 };
 
@@ -1915,6 +1916,15 @@ static int mpi3mr_reply_alloc(struct mpi3mr_softc *sc)
 		goto out_failed;
 	}
 
+	sc->cfg_cmds.reply = malloc(sc->reply_sz,
+		M_MPI3MR, M_NOWAIT | M_ZERO);
+
+	if (!sc->cfg_cmds.reply) {
+		printf(IOCNAME "Cannot allocate memory for cfg_cmds.reply\n",
+		    sc->name);
+		goto out_failed;
+	}
+
 	sc->ioctl_cmds.reply = malloc(sc->reply_sz, M_MPI3MR, M_NOWAIT | M_ZERO);
 	if (!sc->ioctl_cmds.reply) {
 		printf(IOCNAME "Cannot allocate memory for ioctl_cmds.reply\n",
@@ -2877,6 +2887,12 @@ retry_init:
 		sc->init_cmds.dev_handle = MPI3MR_INVALID_DEV_HANDLE;
 		sc->init_cmds.host_tag = MPI3MR_HOSTTAG_INITCMDS;
 
+		mtx_init(&sc->cfg_cmds.completion.lock, "CFG commands lock", NULL, MTX_DEF);
+		sc->cfg_cmds.reply = NULL;
+		sc->cfg_cmds.state = MPI3MR_CMD_NOTUSED;
+		sc->cfg_cmds.dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+		sc->cfg_cmds.host_tag = MPI3MR_HOSTTAG_CFGCMDS;
+
 		mtx_init(&sc->ioctl_cmds.completion.lock, "IOCTL commands lock", NULL, MTX_DEF);
 		sc->ioctl_cmds.reply = NULL;
 		sc->ioctl_cmds.state = MPI3MR_CMD_NOTUSED;
@@ -3042,6 +3058,9 @@ retry_init:
 		goto err;
 	}
 
+	if (mpi3mr_cfg_get_driver_pg1(sc) != 0)
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Failed to get the cfg driver page1\n");
+
 	return retval;
 
 err_retry:
@@ -3119,6 +3138,116 @@ out:
 	return retval;
 }
 
+static int mpi3mr_timestamp_sync(struct mpi3mr_softc *sc)
+{
+	int retval = 0;
+	struct timeval current_time;
+	int64_t time_in_msec;
+	Mpi3IoUnitControlRequest_t iou_ctrl = {0};
+
+	mtx_lock(&sc->init_cmds.completion.lock);
+	if (sc->init_cmds.state & MPI3MR_CMD_PENDING) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Issue timestamp sync: command is in use\n");
+		mtx_unlock(&sc->init_cmds.completion.lock);
+		return -1;
+	}
+
+	sc->init_cmds.state = MPI3MR_CMD_PENDING;
+	sc->init_cmds.is_waiting = 1;
+	sc->init_cmds.callback = NULL;
+	iou_ctrl.HostTag = htole64(MPI3MR_HOSTTAG_INITCMDS);
+	iou_ctrl.Function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+	iou_ctrl.Operation = MPI3_CTRL_OP_UPDATE_TIMESTAMP;
+	getmicrotime(&current_time);
+	time_in_msec = (int64_t)current_time.tv_sec * 1000 + current_time.tv_usec/1000;
+	iou_ctrl.Param64[0] = htole64(time_in_msec);
+
+	init_completion(&sc->init_cmds.completion);
+
+	retval = mpi3mr_submit_admin_cmd(sc, &iou_ctrl, sizeof(iou_ctrl));
+	if (retval) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "timestamp sync: Admin Post failed\n");
+		goto out_unlock;
+	}
+
+	wait_for_completion_timeout(&sc->init_cmds.completion,
+				    (MPI3MR_INTADMCMD_TIMEOUT));
+
+	if (!(sc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Issue timestamp sync: command timed out\n");
+		sc->init_cmds.is_waiting = 0;
+
+		if (!(sc->init_cmds.state & MPI3MR_CMD_RESET))
+			mpi3mr_check_rh_fault_ioc(sc, MPI3MR_RESET_FROM_TSU_TIMEOUT);
+
+		retval = -1;
+		goto out_unlock;
+	}
+
+	if (((sc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) != MPI3_IOCSTATUS_SUCCESS) &&
+	     (sc->init_cmds.ioc_status != MPI3_IOCSTATUS_SUPERVISOR_ONLY)) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Issue timestamp sync: Failed IOCStatus(0x%04x) "
+			      " Loginfo(0x%08x) \n", (sc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+			      sc->init_cmds.ioc_loginfo);
+		retval = -1;
+	}
+
+out_unlock:
+	sc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+	mtx_unlock(&sc->init_cmds.completion.lock);
+
+	return retval;
+}
+
+void
+mpi3mr_timestamp_thread(void *arg)
+{
+	struct mpi3mr_softc *sc = (struct mpi3mr_softc *)arg;
+	U64 elapsed_time = 0;
+
+	sc->timestamp_thread_active = 1;
+	mtx_lock(&sc->reset_mutex);
+	while (1) {
+
+		if (sc->mpi3mr_flags & MPI3MR_FLAGS_SHUTDOWN ||
+		    (sc->unrecoverable == 1)) {
+			mpi3mr_dprint(sc, MPI3MR_INFO,
+				      "Exit due to %s from %s\n",
+				      sc->mpi3mr_flags & MPI3MR_FLAGS_SHUTDOWN ? "Shutdown" :
+				      "Hardware critical error", __func__);
+			break;
+		}
+		mtx_unlock(&sc->reset_mutex);
+
+		while (sc->reset_in_progress) {
+			if (elapsed_time)
+				elapsed_time = 0;
+			if (sc->unrecoverable)
+				break;
+			pause("mpi3mr_timestamp_thread", hz / 5);
+		}
+
+		if (elapsed_time++ >= sc->ts_update_interval * 60) {
+			mpi3mr_timestamp_sync(sc);
+			elapsed_time = 0;
+		}
+
+		/*
+		 * Sleep for 1 second if we're not exiting, then loop to top
+		 * to poll exit status and hardware health.
+		 */
+		mtx_lock(&sc->reset_mutex);
+		if (((sc->mpi3mr_flags & MPI3MR_FLAGS_SHUTDOWN) == 0) &&
+		    (!sc->unrecoverable) && (!sc->reset_in_progress)) {
+			msleep(&sc->timestamp_chan, &sc->reset_mutex, PRIBIO,
+			       "mpi3mr_timestamp", 1 * hz);
+		}
+	}
+	mtx_unlock(&sc->reset_mutex);
+	sc->timestamp_thread_active = 0;
+	kproc_exit(0);
+}
+
 void
 mpi3mr_watchdog_thread(void *arg)
 {
@@ -4398,6 +4527,9 @@ static void mpi3mr_process_admin_reply_desc(struct mpi3mr_softc *sc,
 	case MPI3MR_HOSTTAG_INITCMDS:
 		cmdptr = &sc->init_cmds;
 		break;
+	case MPI3MR_HOSTTAG_CFGCMDS:
+		cmdptr = &sc->cfg_cmds;
+		break;
 	case MPI3MR_HOSTTAG_IOCTLCMDS:
 		cmdptr = &sc->ioctl_cmds;
 		break;
@@ -5303,6 +5435,184 @@ out_failed:
 	mpi3mr_free_ioctl_dma_memory(sc);
 }
 
+static void inline
+mpi3mr_free_dma_mem(struct mpi3mr_softc *sc,
+		    struct dma_memory_desc *mem_desc)
+{
+	if (mem_desc->dma_addr)
+		bus_dmamap_unload(mem_desc->tag, mem_desc->dmamap);
+
+	if (mem_desc->addr != NULL) {
+		bus_dmamem_free(mem_desc->tag, mem_desc->addr, mem_desc->dmamap);
+		mem_desc->addr = NULL;
+	}
+
+	if (mem_desc->tag != NULL)
+		bus_dma_tag_destroy(mem_desc->tag);
+}
+
+static int
+mpi3mr_alloc_dma_mem(struct mpi3mr_softc *sc,
+		     struct dma_memory_desc *mem_desc)
+{
+	int retval;
+
+	if (bus_dma_tag_create(sc->mpi3mr_parent_dmat,  /* parent */
+				4, 0,			/* algnmnt, boundary */
+				sc->dma_loaddr,		/* lowaddr */
+				sc->dma_hiaddr,		/* highaddr */
+				NULL, NULL,		/* filter, filterarg */
+				mem_desc->size,		/* maxsize */
+				1,			/* nsegments */
+				mem_desc->size,		/* maxsize */
+				0,			/* flags */
+				NULL, NULL,		/* lockfunc, lockarg */
+				&mem_desc->tag)) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "%s: Cannot allocate DMA tag\n", __func__);
+		return ENOMEM;
+	}
+
+	if (bus_dmamem_alloc(mem_desc->tag, (void **)&mem_desc->addr,
+			     BUS_DMA_NOWAIT, &mem_desc->dmamap)) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "%s: Cannot allocate DMA memory\n", __func__);
+		retval = ENOMEM;
+		goto out;
+	}
+
+	bzero(mem_desc->addr, mem_desc->size);
+
+	bus_dmamap_load(mem_desc->tag, mem_desc->dmamap, mem_desc->addr, mem_desc->size,
+			mpi3mr_memaddr_cb, &mem_desc->dma_addr, BUS_DMA_NOWAIT);
+
+	if (!mem_desc->addr) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "%s: Cannot load DMA map\n", __func__);
+		retval = ENOMEM;
+		goto out;
+	}
+	return 0;
+out:
+	mpi3mr_free_dma_mem(sc, mem_desc);
+	return retval;
+}
+
+static int
+mpi3mr_post_cfg_req(struct mpi3mr_softc *sc, Mpi3ConfigRequest_t *cfg_req)
+{
+	int retval;
+
+	mtx_lock(&sc->cfg_cmds.completion.lock);
+	if (sc->cfg_cmds.state & MPI3MR_CMD_PENDING) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Issue cfg request: cfg command is in use\n");
+		mtx_unlock(&sc->cfg_cmds.completion.lock);
+		return -1;
+	}
+
+	sc->cfg_cmds.state = MPI3MR_CMD_PENDING;
+	sc->cfg_cmds.is_waiting = 1;
+	sc->cfg_cmds.callback = NULL;
+	sc->cfg_cmds.ioc_status = 0;
+	sc->cfg_cmds.ioc_loginfo = 0;
+
+	cfg_req->HostTag = htole16(MPI3MR_HOSTTAG_CFGCMDS);
+	cfg_req->Function = MPI3_FUNCTION_CONFIG;
+	cfg_req->PageType = MPI3_CONFIG_PAGETYPE_DRIVER;
+	cfg_req->PageNumber = 1;
+	cfg_req->PageAddress = 0;
+
+	init_completion(&sc->cfg_cmds.completion);
+
+	retval = mpi3mr_submit_admin_cmd(sc, cfg_req, sizeof(*cfg_req));
+	if (retval) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "Issue cfg request: Admin Post failed\n");
+		goto out;
+	}
+
+	wait_for_completion_timeout(&sc->cfg_cmds.completion,
+				   (MPI3MR_INTADMCMD_TIMEOUT));
+
+	if (!(sc->cfg_cmds.state & MPI3MR_CMD_COMPLETE)) {
+		if (!(sc->cfg_cmds.state & MPI3MR_CMD_RESET)) {
+			mpi3mr_dprint(sc, MPI3MR_ERROR, "config request command timed out\n");
+			mpi3mr_check_rh_fault_ioc(sc, MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT);
+		}
+		retval = -1;
+		sc->cfg_cmds.is_waiting = 0;
+		goto out;
+	}
+
+	if ((sc->cfg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) !=
+	     MPI3_IOCSTATUS_SUCCESS ) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "config request failed, IOCStatus(0x%04x) "
+			      " Loginfo(0x%08x) \n",(sc->cfg_cmds.ioc_status &
+			      MPI3_IOCSTATUS_STATUS_MASK), sc->cfg_cmds.ioc_loginfo);
+		retval = -1;
+	}
+
+out:
+	sc->cfg_cmds.state = MPI3MR_CMD_NOTUSED;
+	mtx_unlock(&sc->cfg_cmds.completion.lock);
+	return retval;
+}
+
+static int mpi3mr_process_cfg_req(struct mpi3mr_softc *sc,
+				  Mpi3ConfigRequest_t *cfg_req,
+				  Mpi3ConfigPageHeader_t *cfg_hdr,
+				  void *cfg_buf, U32 cfg_buf_sz)
+{
+	int retval;
+	struct dma_memory_desc mem_desc = {0};
+
+	if (cfg_req->Action == MPI3_CONFIG_ACTION_PAGE_HEADER)
+		mem_desc.size = sizeof(Mpi3ConfigPageHeader_t);
+	else {
+		mem_desc.size = le16toh(cfg_hdr->PageLength) * 4;
+		cfg_req->PageLength = cfg_hdr->PageLength;
+		cfg_req->PageVersion = cfg_hdr->PageVersion;
+	}
+
+	retval = mpi3mr_alloc_dma_mem(sc, &mem_desc);
+	if (retval) {
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "%s: Failed to allocate DMA memory\n", __func__);
+		return retval;
+	}
+
+	mpi3mr_add_sg_single(&cfg_req->SGL, MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST,
+			     mem_desc.size, mem_desc.dma_addr);
+
+	retval = mpi3mr_post_cfg_req(sc, cfg_req);
+	if (retval)
+		mpi3mr_dprint(sc, MPI3MR_ERROR, "%s: Failed to post config request\n", __func__);
+	else
+		memcpy(cfg_buf, mem_desc.addr, min(mem_desc.size, cfg_buf_sz));
+
+	mpi3mr_free_dma_mem(sc, &mem_desc);
+	return retval;
+}
+
+int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_softc *sc)
+{
+	int retval;
+	Mpi3DriverPage1_t driver_pg1 = {0};
+	Mpi3ConfigPageHeader_t cfg_hdr = {0};
+	Mpi3ConfigRequest_t cfg_req = {0};
+
+	cfg_req.Action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+	retval = mpi3mr_process_cfg_req(sc, &cfg_req, NULL, &cfg_hdr, sizeof(cfg_hdr));
+	if (retval)
+		goto error;
+
+	cfg_req.Action = MPI3_CONFIG_ACTION_READ_CURRENT;
+	retval = mpi3mr_process_cfg_req(sc, &cfg_req, &cfg_hdr, &driver_pg1, sizeof(driver_pg1));
+
+error:
+	if (!retval && driver_pg1.TimeStampUpdate)
+		sc->ts_update_interval = driver_pg1.TimeStampUpdate;
+	else
+		sc->ts_update_interval = MPI3MR_TSUPDATE_INTERVAL;
+
+	return retval;
+}
+
 void
 mpi3mr_destory_mtx(struct mpi3mr_softc *sc)
 {
@@ -5334,6 +5644,9 @@ mpi3mr_destory_mtx(struct mpi3mr_softc *sc)
 	if (mtx_initialized(&sc->init_cmds.completion.lock))
 		mtx_destroy(&sc->init_cmds.completion.lock);
 	
+	if (mtx_initialized(&sc->cfg_cmds.completion.lock))
+		mtx_destroy(&sc->cfg_cmds.completion.lock);
+
 	if (mtx_initialized(&sc->ioctl_cmds.completion.lock))
 		mtx_destroy(&sc->ioctl_cmds.completion.lock);
 	
@@ -5512,6 +5825,11 @@ mpi3mr_free_mem(struct mpi3mr_softc *sc)
 		sc->init_cmds.reply = NULL;
 	}
 	
+	if (sc->cfg_cmds.reply) {
+		free(sc->cfg_cmds.reply, M_MPI3MR);
+		sc->cfg_cmds.reply = NULL;
+	}
+
 	if (sc->ioctl_cmds.reply) {
 		free(sc->ioctl_cmds.reply, M_MPI3MR);
 		sc->ioctl_cmds.reply = NULL;
@@ -5629,6 +5947,9 @@ static void mpi3mr_flush_drv_cmds(struct mpi3mr_softc *sc)
 	cmdptr = &sc->init_cmds;
 	mpi3mr_drv_cmd_comp_reset(sc, cmdptr);
 
+	cmdptr = &sc->cfg_cmds;
+	mpi3mr_drv_cmd_comp_reset(sc, cmdptr);
+
 	cmdptr = &sc->ioctl_cmds;
 	mpi3mr_drv_cmd_comp_reset(sc, cmdptr);
 
@@ -5672,6 +5993,7 @@ static void mpi3mr_memset_buffers(struct mpi3mr_softc *sc)
 	memset(sc->admin_reply, 0, sc->admin_reply_q_sz);
 
 	memset(sc->init_cmds.reply, 0, sc->reply_sz);
+	memset(sc->cfg_cmds.reply, 0, sc->reply_sz);
 	memset(sc->ioctl_cmds.reply, 0, sc->reply_sz);
 	memset(sc->host_tm_cmds.reply, 0, sc->reply_sz);
 	memset(sc->pel_cmds.reply, 0, sc->reply_sz);
@@ -6014,6 +6336,9 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_softc *sc,
 	sc->reset_in_progress = 1;
 	sc->block_ioctls = 1;
 
+	if (sc->timestamp_thread_active)
+		wakeup(&sc->timestamp_chan);
+
 	while (mpi3mr_atomic_read(&sc->pend_ioctls) && (i < PEND_IOCTLS_COMP_WAIT_TIME)) {
 		ioc_state = mpi3mr_get_iocstate(sc);
 		if (ioc_state == MRIOC_STATE_FAULT)
diff --git a/sys/dev/mpi3mr/mpi3mr.h b/sys/dev/mpi3mr/mpi3mr.h
index 1ab6b8815f59..f48d58ee85d2 100644
--- a/sys/dev/mpi3mr/mpi3mr.h
+++ b/sys/dev/mpi3mr/mpi3mr.h
@@ -141,6 +141,7 @@
 #define MPI3MR_HOSTTAG_PELABORT         3
 #define MPI3MR_HOSTTAG_PELWAIT          4
 #define MPI3MR_HOSTTAG_TMS		5
+#define MPI3MR_HOSTTAG_CFGCMDS		6
 
 #define MAX_MGMT_ADAPTERS 8
 #define MPI3MR_WAIT_BEFORE_CTRL_RESET 5
@@ -163,7 +164,7 @@ extern char fmt_os_ver[16];
 								raw_os_ver[3], raw_os_ver[4], raw_os_ver[5],\
 								raw_os_ver[6]);
 #define MPI3MR_NUM_DEVRMCMD             1
-#define MPI3MR_HOSTTAG_DEVRMCMD_MIN     (MPI3MR_HOSTTAG_TMS + 1)
+#define MPI3MR_HOSTTAG_DEVRMCMD_MIN     (MPI3MR_HOSTTAG_CFGCMDS + 1)
 #define MPI3MR_HOSTTAG_DEVRMCMD_MAX     (MPI3MR_HOSTTAG_DEVRMCMD_MIN + \
                                                 MPI3MR_NUM_DEVRMCMD - 1)
 #define MPI3MR_INTERNALCMDS_RESVD       MPI3MR_HOSTTAG_DEVRMCMD_MAX
@@ -237,6 +238,8 @@ extern char fmt_os_ver[16];
 
 #define	WRITE_SAME_32	0x0d
 
+#define MPI3MR_TSUPDATE_INTERVAL	900
+
 struct completion {
 	unsigned int done;
 	struct mtx lock;
@@ -313,6 +316,7 @@ enum mpi3mr_reset_reason {
 	MPI3MR_RESET_FROM_SCSIIO_TIMEOUT = 26,
 	MPI3MR_RESET_FROM_FIRMWARE = 27,
 	MPI3MR_DEFAULT_RESET_REASON = 28,
+	MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29,
 	MPI3MR_RESET_REASON_COUNT,
 };
 
@@ -555,6 +559,7 @@ struct mpi3mr_softc {
 	char driver_name[MPI3MR_NAME_LENGTH];
 	int bars;
 	bus_addr_t dma_loaddr;
+	bus_addr_t dma_hiaddr;
 	u_int mpi3mr_debug;
 	struct mpi3mr_reset reset;
 	int max_msix_vectors;
@@ -688,6 +693,7 @@ struct mpi3mr_softc {
 	struct mpi3mr_drvr_cmd host_tm_cmds;
 	struct mpi3mr_drvr_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD];
 	struct mpi3mr_drvr_cmd evtack_cmds[MPI3MR_NUM_EVTACKCMD];
+	struct mpi3mr_drvr_cmd cfg_cmds;
 
 	U16 devrem_bitmap_sz;
 	void *devrem_bitmap;
@@ -765,6 +771,10 @@ struct mpi3mr_softc {
 	struct dma_memory_desc ioctl_chain_sge;
 	struct dma_memory_desc ioctl_resp_sge;
 	bool ioctl_sges_allocated;
+	struct proc *timestamp_thread_proc;
+	void   *timestamp_chan;
+	u_int8_t timestamp_thread_active;
+	U32 ts_update_interval;
 };
 
 static __inline uint64_t
@@ -977,6 +987,7 @@ void
 mpi3mrsas_release_simq_reinit(struct mpi3mr_cam_softc *cam_sc);
 void
 mpi3mr_watchdog_thread(void *arg);
+void mpi3mr_timestamp_thread(void *arg);
 void mpi3mr_add_device(struct mpi3mr_softc *sc, U16 per_id);
 int mpi3mr_remove_device(struct mpi3mr_softc *sc, U16 handle);
 int
@@ -996,4 +1007,5 @@ void mpi3mr_poll_pend_io_completions(struct mpi3mr_softc *sc);
 void int_to_lun(unsigned int lun, U8 *req_lun);
 void trigger_reset_from_watchdog(struct mpi3mr_softc *sc, U8 reset_type, U16 reset_reason);
 void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_softc *sc);
+int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_softc *sc);
 #endif /*MPI3MR_H_INCLUDED*/
diff --git a/sys/dev/mpi3mr/mpi3mr_pci.c b/sys/dev/mpi3mr/mpi3mr_pci.c
index 194401c5a847..808349f26827 100644
--- a/sys/dev/mpi3mr/mpi3mr_pci.c
+++ b/sys/dev/mpi3mr/mpi3mr_pci.c
@@ -332,6 +332,13 @@ mpi3mr_ich_startup(void *arg)
 
 	mtx_unlock(&sc->mpi3mr_mtx);
 
+	error = mpi3mr_kproc_create(mpi3mr_timestamp_thread, sc,
+				    &sc->timestamp_thread_proc, 0, 0,
+				    "mpi3mr_timestamp_thread%d",
+				    device_get_unit(sc->mpi3mr_dev));
+	if (error)
+		device_printf(sc->mpi3mr_dev, "Error %d starting timestamp thread\n", error);
+
 	error = mpi3mr_kproc_create(mpi3mr_watchdog_thread, sc,
 	    &sc->watchdog_thread, 0, 0, "mpi3mr_watchdog%d",
 	    device_get_unit(sc->mpi3mr_dev));
@@ -474,7 +481,7 @@ mpi3mr_pci_attach(device_t dev)
 		mpi3mr_dprint(sc, MPI3MR_ERROR, "CAM attach failed\n");
 		goto load_failed;
 	}
-	
+
 	sc->mpi3mr_ich.ich_func = mpi3mr_ich_startup;
 	sc->mpi3mr_ich.ich_arg = sc;
 	if (config_intrhook_establish(&sc->mpi3mr_ich) != 0) {
@@ -664,10 +671,26 @@ mpi3mr_pci_detach(device_t dev)
 	
 	mtx_lock(&sc->reset_mutex);
 	sc->mpi3mr_flags |= MPI3MR_FLAGS_SHUTDOWN;
+	if (sc->timestamp_thread_active)
+		wakeup(&sc->timestamp_chan);
+
 	if (sc->watchdog_thread_active)
 		wakeup(&sc->watchdog_chan);
 	mtx_unlock(&sc->reset_mutex);
 	
+	i = 0;
+	while (sc->timestamp_thread_active && (i < 180)) {
+		i++;
+		if (!(i % 5)) {
+			mpi3mr_dprint(sc, MPI3MR_INFO,
+			    "[%2d]waiting for "
+			    "timestamp thread to quit reset %d\n", i,
+			    sc->timestamp_thread_active);
+		}
+		pause("mpi3mr_shutdown", hz);
+	}
+
+	i = 0;
 	while (sc->reset_in_progress && (i < PEND_IOCTLS_COMP_WAIT_TIME)) {
 		i++;
 		if (!(i % 5)) {