Merge android12-gs-pixel-5.10-sc-qpr2 into android12-gs-pixel-5.10-sc-v2

SBMerger: 410055097
Change-Id: I052d9573ac8ae5995792cff6e9e78759f9f2fd4b
Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
diff --git a/kernel/drivers/net/ieee802154/dw3000.h b/kernel/drivers/net/ieee802154/dw3000.h
index e29ab03..97bd33f 100644
--- a/kernel/drivers/net/ieee802154/dw3000.h
+++ b/kernel/drivers/net/ieee802154/dw3000.h
@@ -100,6 +100,22 @@
 	 DW3000_CHIP_PER_DTU)
 
 /**
+ * typedef dw3000_wakeup_done_cb - Wake up done handler.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+typedef int (*dw3000_wakeup_done_cb)(struct dw3000 *dw);
+
+/**
+ * typedef dw3000_idle_timeout_cb - Idle timeout handler.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+typedef int (*dw3000_idle_timeout_cb)(struct dw3000 *dw);
+
+/**
  * struct dw3000_otp_data - data read from OTP memory of DW3000 device
  * @partID: device part ID
  * @lotID: device lot ID
@@ -469,9 +485,12 @@
  * @sys_time_sync: device SYS_TIME immediately after wakeup
  * @sleep_enter_dtu: DTU when entered sleep
  * @deep_sleep_state: state related to the deep sleep
- * @deep_sleep_timer: timer to wake up the chip after deep sleep
- * @timer_expired_work: work to call timer expired callback
- * @call_timer_expired: should mcps802154_timer_expired be called?
+ * @idle_timeout: true when idle_timeout_dtu is a valid date.
+ * @idle_timeout_dtu: timestamp requested to leave idle mode.
+ * @idle_timer: timer to exiting after an idle call.
+ * @timer_expired_work: call mcps802154_timer_expired outside driver kthread.
+ * @wakeup_done_cb: callback called on wakeup done.
+ * @idle_timeout_cb: callback when idle timer expired
  * @auto_sleep_margin_us: configurable automatic deep sleep margin
  * @need_ranging_clock: true if next operation need ranging clock
  *			and deep sleep cannot be used
@@ -552,9 +571,12 @@
 	u32 sleep_enter_dtu;
 	/* Deep Sleep & MCPS Idle management */
 	struct dw3000_deep_sleep_state deep_sleep_state;
-	struct hrtimer deep_sleep_timer;
+	bool idle_timeout;
+	u32 idle_timeout_dtu;
+	struct hrtimer idle_timer;
 	struct work_struct timer_expired_work;
-	bool call_timer_expired;
+	dw3000_wakeup_done_cb wakeup_done_cb;
+	dw3000_idle_timeout_cb idle_timeout_cb;
 	bool need_ranging_clock;
 	int auto_sleep_margin_us;
 	/* NFCC coexistence specific context. */
diff --git a/kernel/drivers/net/ieee802154/dw3000_core.c b/kernel/drivers/net/ieee802154/dw3000_core.c
index 7d6b227..0236a44 100644
--- a/kernel/drivers/net/ieee802154/dw3000_core.c
+++ b/kernel/drivers/net/ieee802154/dw3000_core.c
@@ -366,6 +366,8 @@
 	},
 };
 
+static int dw3000_wakeup_done_to_tx(struct dw3000 *dw);
+static int dw3000_wakeup_done_to_rx(struct dw3000 *dw);
 /* sysfs variables handling */
 static ssize_t dw3000_sysfs_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *buf);
@@ -1972,7 +1974,7 @@
 	int rc;
 
 	/* Avoid race condition with dw3000_poweroff while chip is stopped just
-	   when dw3000_wakeup_timer() HR timer callback is executed. Do nothing
+	   when dw3000_idle_timeout() HR timer callback is executed. Do nothing
 	   if not in DEEP-SLEEP state. */
 	if (dw->current_operational_state != DW3000_OP_STATE_DEEP_SLEEP)
 		return 0;
@@ -2003,27 +2005,55 @@
 	return dw3000_wakeup(dw);
 }
 
-static void dw3000_timer_expired(struct work_struct *work)
+int dw3000_deepsleep_wakeup_now(struct dw3000 *dw,
+				dw3000_idle_timeout_cb idle_timeout_cb,
+				enum operational_state next_operational_state)
 {
-	struct dw3000 *dw =
-		container_of(work, struct dw3000, timer_expired_work);
-	/* Entered DEEP SLEEP from mcps802154_ops.idle() */
-	mcps802154_timer_expired(dw->llhw);
+	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
+	int r;
+
+	r = dw3000_wakeup(dw);
+	if (r)
+		return r;
+
+	dss->next_operational_state = next_operational_state;
+	dw->idle_timeout_cb = idle_timeout_cb;
+	return 0;
 }
 
 /**
- * dw3000_wakeup_timer() - wake-up timer handler
- * @timer: the deep_sleep_timer field in struct dw3000
+ * dw3000_handle_idle_timeout() - Idle expired handler
+ * @dw: the DW device.
+ * @in: ignored input.
+ * @out: ignored output.
  *
- * Return: 0 on success, else a negative error code.
+ * Return: 0 on success, -errno otherwise.
  */
-enum hrtimer_restart dw3000_wakeup_timer(struct hrtimer *timer)
+static int dw3000_handle_idle_timeout(struct dw3000 *dw, const void *in,
+				      void *out)
 {
-	struct dw3000 *dw =
-		container_of(timer, struct dw3000, deep_sleep_timer);
-	if (dw->nfcc_coex.enabled ||
-	    (dw->deep_sleep_state.next_operational_state >
-	     DW3000_OP_STATE_IDLE_PLL)) {
+	dw3000_idle_timeout_cb idle_timeout_cb = dw->idle_timeout_cb;
+
+	/* Consume/remove registered handler before call it.*/
+	dw->idle_timeout_cb = NULL;
+	/* Call handler registered. */
+	if (idle_timeout_cb)
+		return idle_timeout_cb(dw);
+	return 0;
+}
+
+/**
+ * dw3000_deepsleep_wakeup() - Handle wake-up.
+ * @dw: the DW device.
+ *
+ * Return: True when the wakeup is started, false otherwise.
+ */
+bool dw3000_deepsleep_wakeup(struct dw3000 *dw)
+{
+	trace_dw3000_deepsleep_wakeup(dw);
+	if (dw->current_operational_state == DW3000_OP_STATE_DEEP_SLEEP &&
+	    dw->deep_sleep_state.next_operational_state >
+		    DW3000_OP_STATE_IDLE_PLL) {
 		struct dw3000_stm_command cmd = { do_wakeup, NULL, NULL };
 		/* The chip is about to wake up, let's request the best QoS
 		   latency early */
@@ -2031,21 +2061,42 @@
 		/* Must wakeup to execute stored operation, so run the
 		   wake up function in state machine thread. */
 		dw3000_enqueue_timer(dw, &cmd);
-	} else if (dw->call_timer_expired) {
-		/* Timer launched by idle(), just inform MCPS timer has expired */
-		schedule_work(&dw->timer_expired_work);
+		return true;
 	}
-	hrtimer_try_to_cancel(timer);
+	return false;
+}
+
+/**
+ * dw3000_idle_timeout() - idle timeout handler
+ * @timer: the idle_timer field in struct dw3000
+ *
+ * Return: always timer not restarted.
+ */
+enum hrtimer_restart dw3000_idle_timeout(struct hrtimer *timer)
+{
+	struct dw3000 *dw = container_of(timer, struct dw3000, idle_timer);
+	bool wakeup_started;
+
+	trace_dw3000_idle_timeout(dw);
+	wakeup_started = dw3000_deepsleep_wakeup(dw);
+	if (!wakeup_started && dw->idle_timeout_cb) {
+		struct dw3000_stm_command cmd = { dw3000_handle_idle_timeout,
+						  NULL, NULL };
+		dw3000_enqueue_timer(dw, &cmd);
+	}
 	return HRTIMER_NORESTART;
 }
 
 /**
- * dw3000_wakeup_cancel() - Cancel wake-up timer
+ * dw3000_idle_cancel_timer() - Cancel idle timer.
  * @dw: the DW device
  */
-void dw3000_wakeup_cancel(struct dw3000 *dw)
+static void dw3000_idle_cancel_timer(struct dw3000 *dw)
 {
-	hrtimer_try_to_cancel(&dw->deep_sleep_timer);
+	trace_dw3000_idle_cancel_timer(dw);
+	hrtimer_try_to_cancel(&dw->idle_timer);
+	/* Ensure wakeup ISR don't call the mcps802154_timer_expired() */
+	dw->idle_timeout_cb = NULL;
 }
 
 /**
@@ -2057,15 +2108,16 @@
  */
 void dw3000_wakeup_and_wait(struct dw3000 *dw)
 {
+	trace_dw3000_wakeup_and_wait(dw, dw->current_operational_state);
 	if (dw->current_operational_state >= DW3000_OP_STATE_IDLE_PLL)
 		return;
 	/* Ensure wakeup ISR don't call the mcps802154_timer_expired() since
 	   this will result in dead lock! */
-	dw->call_timer_expired = false;
+	dw3000_idle_cancel_timer(dw);
 	/* Ensure force call to dw3000_wakeup() is made. */
 	dw->deep_sleep_state.next_operational_state = DW3000_OP_STATE_MAX;
 	/* Now wakeup device, and stop timer if running */
-	dw3000_wakeup_timer(&dw->deep_sleep_timer);
+	dw3000_deepsleep_wakeup(dw);
 	dw->deep_sleep_state.next_operational_state = DW3000_OP_STATE_IDLE_PLL;
 	/* And wait for good state */
 	if (current != dw->stm.mthread)
@@ -2122,7 +2174,9 @@
 		}
 		/* The chip is about to wake up, request the best QoS latency */
 		dw3000_pm_qos_update_request(dw, dw3000_qos_latency);
-		/* Wakeup now since delay isn't enough. */
+		/* Cancel wakeup timer launch by idle() */
+		dw3000_idle_cancel_timer(dw);
+		/* Wakeup now since delay isn't enough */
 		rc = dw3000_wakeup(dw);
 		if (unlikely(rc))
 			return rc;
@@ -2146,6 +2200,8 @@
 		/* Failure to enter deep sleep, continue without it */
 		break;
 	}
+	/* Cancel wakeup timer launch by idle() */
+	dw3000_idle_cancel_timer(dw);
 	return 0;
 }
 
@@ -2330,6 +2386,8 @@
 			return rc;
 		/* Save parameters to activate RX delayed when
 		   wakeup later */
+
+		dw->wakeup_done_cb = dw3000_wakeup_done_to_rx;
 		dss->next_operational_state = DW3000_OP_STATE_RX;
 		dss->rx_info = *info;
 		dss->frame_idx = frame_idx;
@@ -3112,6 +3170,7 @@
 			return rc;
 		/* Save parameters to activate TX delayed when
 		   wakeup later */
+		dw->wakeup_done_cb = dw3000_wakeup_done_to_tx;
 		dss->next_operational_state = DW3000_OP_STATE_TX;
 		dss->tx_info = *info;
 		dss->tx_skb = skb;
@@ -4129,9 +4188,8 @@
  */
 void dw3000_wakeup_timer_start(struct dw3000 *dw, int delay_us)
 {
-	hrtimer_start(&dw->deep_sleep_timer, ns_to_ktime(delay_us * 1000ull),
+	hrtimer_start(&dw->idle_timer, ns_to_ktime(delay_us * 1000ull),
 		      HRTIMER_MODE_REL);
-	dw->call_timer_expired = true;
 	trace_dw3000_wakeup_timer_start(dw, delay_us);
 }
 
@@ -4168,6 +4226,153 @@
 }
 
 /**
+ * dw3000_wakeup_done_to_tx() - Wakeup done, continue to program TX.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+static int dw3000_wakeup_done_to_tx(struct dw3000 *dw)
+{
+	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
+
+	trace_dw3000_wakeup_done_to_tx(dw);
+	dss->next_operational_state = dw->current_operational_state;
+	return dw3000_do_tx_frame(dw, &dss->tx_info, dss->tx_skb,
+				  dss->frame_idx);
+}
+
+/**
+ * dw3000_wakeup_done_to_rx() - Wakeup done, continue to program RX.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+static int dw3000_wakeup_done_to_rx(struct dw3000 *dw)
+{
+	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
+
+	trace_dw3000_wakeup_done_to_rx(dw);
+	dss->next_operational_state = dw->current_operational_state;
+	return dw3000_do_rx_enable(dw, &dss->rx_info, dss->frame_idx);
+}
+
+/**
+ * dw3000_wakeup_done_to_idle() - Wakeup done, continue on idle timeout.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+static int dw3000_wakeup_done_to_idle(struct dw3000 *dw)
+{
+	struct dw3000_stm_command cmd = { dw3000_handle_idle_timeout, NULL,
+					  NULL };
+
+	trace_dw3000_wakeup_done_to_idle(dw);
+	if (dw->idle_timeout) {
+		u32 now_dtu = dw3000_get_dtu_time(dw);
+		int idle_duration_us =
+			DTU_TO_US(dw->idle_timeout_dtu - now_dtu);
+
+		/* TODO:
+		 * 2 timers implemented (idle, deep_sleep),
+		 * or a better FSM (enter, leave, events, state)
+		 * should help to follow/repect timestamps expected. */
+		dw3000_wakeup_timer_start(dw, idle_duration_us);
+		return 0;
+	}
+	return dw3000_enqueue_generic(dw, &cmd);
+}
+
+/**
+ * dw3000_idle() - Go into idle.
+ * @dw: Driver context.
+ * @timestamp: Idle duration have a end date (timestamp_dtu).
+ * @timestamp_dtu: End date for this idle duration.
+ * @idle_timeout_cb: idle timeout callback.
+ * @next_operational_state: next operation state wanted.
+ *
+ * Return: 0 on success, else an error.
+ */
+int dw3000_idle(struct dw3000 *dw, bool timestamp, u32 timestamp_dtu,
+		dw3000_idle_timeout_cb idle_timeout_cb,
+		enum operational_state next_operational_state)
+{
+	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
+	u32 cur_time_dtu = 0;
+	int delay_us = 0, rc;
+
+	trace_dw3000_idle(dw, timestamp, timestamp_dtu, next_operational_state);
+
+	if (WARN_ON(next_operational_state < DW3000_OP_STATE_IDLE_PLL))
+		return -EINVAL;
+
+	dw->wakeup_done_cb = dw3000_wakeup_done_to_idle;
+	/* Reset ranging clock requirement */
+	dw->need_ranging_clock = false;
+	dw3000_reset_rctu_conv_state(dw);
+	/* Ensure dw3000_idle_timeout() handler does the right thing. */
+	dss->next_operational_state = next_operational_state;
+
+	/* Release Wifi coexistence. */
+	dw3000_coex_stop(dw);
+	/* Check if enough idle time to enter DEEP SLEEP */
+	dw->idle_timeout = timestamp;
+	dw->idle_timeout_dtu = timestamp_dtu;
+	if (timestamp) {
+		int deepsleep_delay_us;
+		int idle_delay_us;
+		bool is_sleeping = dw->current_operational_state ==
+				   DW3000_OP_STATE_DEEP_SLEEP;
+
+		cur_time_dtu = dw3000_get_dtu_time(dw);
+		idle_delay_us = DTU_TO_US(timestamp_dtu - cur_time_dtu);
+		if (idle_delay_us < 0) {
+			rc = -ETIME;
+			goto eof;
+		}
+		/* Warning: timestamp_dtu have already taken in consideration
+		 * WakeUp latency! */
+		deepsleep_delay_us = idle_delay_us;
+		if (is_sleeping)
+			deepsleep_delay_us -= DW3000_WAKEUP_LATENCY_US;
+
+		/* TODO/FIXME:
+		 * Timer is used for idle timeout and deepsleep timeout,
+		 * which haven't the same timeout_dtu! */
+		if (deepsleep_delay_us < 0) {
+			dw3000_deepsleep_wakeup_now(dw, idle_timeout_cb,
+						    DW3000_OP_STATE_MAX);
+			rc = 0;
+			goto eof;
+		}
+
+		delay_us = dw3000_can_deep_sleep(dw, deepsleep_delay_us);
+		if (!delay_us) {
+			u32 timer_duration_dtu = is_sleeping ?
+							 deepsleep_delay_us :
+							 idle_delay_us;
+			/* Provided date isn't far enough to enter deep-sleep,
+			   just launch the timer to have it call the MCPS timer
+			   expired event function. */
+			dw->idle_timeout_cb = idle_timeout_cb;
+			dw3000_wakeup_timer_start(dw, timer_duration_dtu);
+			rc = 0;
+			goto eof;
+		}
+	} else if (dw->auto_sleep_margin_us < 0) {
+		/* Deep-sleep is completly disable, so do nothing here! */
+		rc = 0;
+		goto eof;
+	}
+	/* Enter DEEP SLEEP */
+	rc = dw3000_deep_sleep_and_wakeup(dw, delay_us);
+	if (!rc)
+		dw->idle_timeout_cb = idle_timeout_cb;
+eof:
+	return rc;
+}
+
+/**
  * dw3000_lock_pll() - Auto calibrate the PLL and change to IDLE_PLL state
  * @dw: the DW device on which the SPI transfer will occurs
  * @sys_status: the last known sys_status register LSB value
@@ -5621,6 +5826,13 @@
 	return rc;
 }
 
+static void dw3000_mcps_timer_expired(struct work_struct *work)
+{
+	struct dw3000 *dw =
+		container_of(work, struct dw3000, timer_expired_work);
+	mcps802154_timer_expired(dw->llhw);
+}
+
 /**
  * dw3000_initialise() - Initialise the DW local data.
  * @dw: The DW device.
@@ -5646,8 +5858,8 @@
 	 * parameter on load, or via testmode */
 	stats->enabled = dw3000_stats_enabled;
 	memset(stats->count, 0, sizeof(stats->count));
-	/* Reset Deep Sleep state */
-	INIT_WORK(&dw->timer_expired_work, dw3000_timer_expired);
+	INIT_WORK(&dw->timer_expired_work, dw3000_mcps_timer_expired);
+
 #ifdef CONFIG_DW3000_DEBUG
 	if (dss->regbackup)
 		kfree(dss->regbackup);
@@ -5960,7 +6172,7 @@
 	if (dw->current_operational_state < DW3000_OP_STATE_INIT_RC) {
 		/* Seems chip is sleeping or already power-off. Just ensure
 		   wake-up timer will not fire since not required anymore. */
-		dw3000_wakeup_cancel(dw);
+		dw3000_idle_cancel_timer(dw);
 		/* No need to call disable_irq() since already called and this
 		   will require calling two times enable_irq() later because
 		   enable/disable_irq are nested! */
@@ -6061,6 +6273,7 @@
 					      struct dw3000_isr_data *isr)
 {
 	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
+	const dw3000_wakeup_done_cb wakeup_done_cb = dw->wakeup_done_cb;
 	int rc;
 
 	if (dw->current_operational_state != DW3000_OP_STATE_WAKE_UP) {
@@ -6204,21 +6417,10 @@
 	if (rc)
 		return rc;
 #endif
-	/* Do the required operation after wakeup according states */
-	if (dw->nfcc_coex.enabled) {
-		rc = dw3000_nfcc_coex_handle_spi1_ready_isr(dw);
-	} else if (dss->next_operational_state == DW3000_OP_STATE_RX) {
-		/* Entered DEEP SLEEP from dw3000_do_rx_enable() */
-		dss->next_operational_state = dw->current_operational_state;
-		rc = dw3000_do_rx_enable(dw, &dss->rx_info, dss->frame_idx);
-	} else if (dss->next_operational_state == DW3000_OP_STATE_TX) {
-		/* Entered DEEP SLEEP from dw3000_do_tx_frame() */
-		dss->next_operational_state = dw->current_operational_state;
-		rc = dw3000_do_tx_frame(dw, &dss->tx_info, dss->tx_skb,
-					dss->frame_idx);
-	} else if (dw->call_timer_expired) {
-		/* Entered DEEP SLEEP from do_idle() */
-		schedule_work(&dw->timer_expired_work);
+	if (wakeup_done_cb) {
+		/* Consume the callback handler before execution. */
+		dw->wakeup_done_cb = NULL;
+		rc = wakeup_done_cb(dw);
 	}
 	return rc;
 }
diff --git a/kernel/drivers/net/ieee802154/dw3000_core.h b/kernel/drivers/net/ieee802154/dw3000_core.h
index e8c7dbd..d6d6efa 100644
--- a/kernel/drivers/net/ieee802154/dw3000_core.h
+++ b/kernel/drivers/net/ieee802154/dw3000_core.h
@@ -359,10 +359,16 @@
 void dw3000_sysfs_remove(struct dw3000 *dw);
 
 void dw3000_isr(struct dw3000 *dw);
-enum hrtimer_restart dw3000_wakeup_timer(struct hrtimer *timer);
+enum hrtimer_restart dw3000_idle_timeout(struct hrtimer *timer);
 void dw3000_wakeup_timer_start(struct dw3000 *dw, int delay_us);
 void dw3000_wakeup_and_wait(struct dw3000 *dw);
 int dw3000_deep_sleep_and_wakeup(struct dw3000 *dw, int delay_us);
+int dw3000_idle(struct dw3000 *dw, bool timestamp, u32 timestamp_dtu,
+		dw3000_idle_timeout_cb idle_timeout_cb,
+		enum operational_state next_operational_state);
+int dw3000_deepsleep_wakeup_now(struct dw3000 *dw,
+				dw3000_idle_timeout_cb idle_timeout_cb,
+				enum operational_state next_operational_state);
 int dw3000_can_deep_sleep(struct dw3000 *dw, int delay_us);
 int dw3000_trace_rssi_info(struct dw3000 *dw, u32 regid, char *chipver);
 
diff --git a/kernel/drivers/net/ieee802154/dw3000_mcps.c b/kernel/drivers/net/ieee802154/dw3000_mcps.c
index 133d5b2..e46b1c3 100644
--- a/kernel/drivers/net/ieee802154/dw3000_mcps.c
+++ b/kernel/drivers/net/ieee802154/dw3000_mcps.c
@@ -478,47 +478,23 @@
 	return ret;
 }
 
+static int dw3000_handle_idle_timeout(struct dw3000 *dw)
+{
+	/* MCPS feeback must be done outside driver kthread. */
+	schedule_work(&dw->timer_expired_work);
+	return 0;
+}
+
 static int do_idle(struct dw3000 *dw, const void *in, void *out)
 {
-	struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state;
-	u32 cur_time_dtu = 0;
-	int delay_us = 0, rc;
+	bool timestamp = !!in;
+	u32 timestamp_dtu = timestamp ? *(const u32 *)in : 0;
 
-	trace_dw3000_mcps_idle(dw, in != NULL, in ? *(const u32 *)in : 0);
-
-	/* Reset ranging clock requirement */
-	dw->need_ranging_clock = false;
-	dw3000_reset_rctu_conv_state(dw);
-	/* Ensure dw3000_wakeup_timer() handler does the right thing */
-	dss->next_operational_state = DW3000_OP_STATE_IDLE_PLL;
-
-	/* Release Wifi coexistence. */
-	dw3000_coex_stop(dw);
-	/* Check if enough idle time to enter DEEP SLEEP */
-	if (in) {
-		u32 date_dtu = *(const u32 *)in;
-		int effective_delay_us;
-		cur_time_dtu = dw3000_get_dtu_time(dw);
-		effective_delay_us = DTU_TO_US(date_dtu - cur_time_dtu);
-		delay_us = dw3000_can_deep_sleep(dw, effective_delay_us);
-		if (!delay_us) {
-			/* Provided date isn't far enough to enter deep-sleep,
-			   just launch the timer to have it call the MCPS timer
-			   expired event function. */
-			dw3000_wakeup_timer_start(dw, effective_delay_us);
-			rc = 0;
-			goto eof;
-		}
-	} else if (dw->auto_sleep_margin_us < 0) {
-		/* Deep-sleep is completly disable, so do nothing here! */
-		rc = 0;
-		goto eof;
-	}
-	/* Enter DEEP SLEEP */
-	rc = dw3000_deep_sleep_and_wakeup(dw, delay_us);
-eof:
-	trace_dw3000_return_int(dw, rc);
-	return rc;
+	int r = dw3000_idle(dw, timestamp, timestamp_dtu,
+			    dw3000_handle_idle_timeout,
+			    DW3000_OP_STATE_IDLE_PLL);
+	trace_dw3000_return_int(dw, r);
+	return r;
 }
 
 static int idle(struct mcps802154_llhw *llhw, bool timestamp, u32 timestamp_dtu)
diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h
index 682c7bd..98107c5 100644
--- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h
+++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h
@@ -169,6 +169,10 @@
 	 */
 	bool enabled;
 	/**
+	 * @configured: True when nfcc coex is configured.
+	 */
+	bool configured;
+	/**
 	 * @sync_time_needed: True when clock_sync frame must be send.
 	 */
 	bool sync_time_needed;
diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c
index 2fa642f..0cb475f 100644
--- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c
+++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c
@@ -136,6 +136,41 @@
 }
 
 /**
+ * dw3000_nfcc_coex_configure() - Configure the nfcc_coex.
+ * @dw: Driver context.
+ *
+ * Return: 0 on success, else an error.
+ */
+int dw3000_nfcc_coex_configure(struct dw3000 *dw)
+{
+	int r;
+
+	if (dw->nfcc_coex.configured)
+		return 0;
+
+	trace_dw3000_nfcc_coex_configure(dw);
+	r = dw3000_configure_chan(dw);
+	if (r) {
+		trace_dw3000_nfcc_coex_err(dw, "configure channel fails");
+		return r;
+	}
+	r = dw3000_rx_disable(dw);
+	if (r) {
+		trace_dw3000_nfcc_coex_warn(dw, "rx disable failed");
+		return r;
+	}
+	r = dw3000_nfcc_coex_enable_SPIxMAVAIL_interrupts(dw);
+	if (r) {
+		trace_dw3000_nfcc_coex_err(
+			dw, "SPIxMAVAIL interrupts enable failed");
+		return r;
+	}
+
+	dw->nfcc_coex.configured = true;
+	return 0;
+}
+
+/**
  * dw3000_nfcc_coex_do_watchdog_timeout() - Do watchdog timeout event in workqueue.
  * @dw: Driver context.
  * @in: Data to read.
@@ -215,24 +250,40 @@
 }
 
 /**
- * dw3000_nfcc_coex_handle_spi1_ready_isr() - Handle SPI ready interrupt.
+ * dw3000_nfcc_coex_idle_timeout() - Idle expired.
  * @dw: Driver context.
  *
  * Return: 0 on success, else an error.
  */
-int dw3000_nfcc_coex_handle_spi1_ready_isr(struct dw3000 *dw)
+int dw3000_nfcc_coex_idle_timeout(struct dw3000 *dw)
 {
 	int r;
 
-	if (__dw3000_chip_version == DW3000_C0_VERSION)
-		return -EOPNOTSUPP;
-
+	trace_dw3000_nfcc_coex_idle_timeout(dw);
+	if (!dw->nfcc_coex.configured) {
+		r = dw3000_nfcc_coex_configure(dw);
+		if (r) {
+			trace_dw3000_nfcc_coex_err(
+				dw, "dw3000_nfcc_coex_configured failed");
+			return r;
+		}
+	}
 	/* NFCC was enabled before sleeping. Enable the NFCC mailbox
 	 * interrupt and send the right message to the NFCC. */
 	r = dw3000_nfcc_coex_enable_SPIxMAVAIL_interrupts(dw);
 	if (r)
-		return r;
-	return dw3000_nfcc_coex_message_send(dw);
+		goto idle_timeout_failure;
+	r = dw3000_nfcc_coex_message_send(dw);
+	if (r) {
+		trace_dw3000_nfcc_coex_err(dw, "nfcc_coex_message_send failed");
+		goto idle_timeout_failure;
+	}
+
+	return 0;
+
+idle_timeout_failure:
+	// Call mcps802154_broken(dw->llhw) in wq ?
+	return r;
 }
 
 /**
@@ -259,8 +310,6 @@
 int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel,
 			    dw3000_nfcc_coex_spi_avail_cb cb)
 {
-	int rc;
-
 	trace_dw3000_nfcc_coex_enable(dw, channel);
 
 	/* NFCC needs a D0 chip or above. C0 does not have 2 SPI interfaces. */
@@ -268,34 +317,17 @@
 		trace_dw3000_nfcc_coex_err(dw, "C0 chip is not supported");
 		return -EOPNOTSUPP;
 	}
-
-	/* Need to wake-up device and wait it before continuing. */
-	dw3000_wakeup_and_wait(dw);
+	if (dw->nfcc_coex.enabled)
+		return -EBUSY;
 
 	/* Set the channel for NFCC and save current config. */
-	/* FIXME: original_channel lost on second call. */
 	dw->nfcc_coex.original_channel = dw->config.chan;
 	dw->nfcc_coex.sync_time_needed = true;
 	dw->config.chan = channel;
-	rc = dw3000_configure_chan(dw);
-	if (rc) {
-		trace_dw3000_nfcc_coex_err(dw, "configure channel fails");
-		return rc;
-	}
-
-	/* Disable rx during NFCC. */
-	rc = dw3000_rx_disable(dw);
-	if (rc)
-		trace_dw3000_nfcc_coex_warn(dw, "rx disable failed");
-
-	rc = dw3000_nfcc_coex_enable_SPIxMAVAIL_interrupts(dw);
-	if (rc)
-		trace_dw3000_nfcc_coex_err(
-			dw, "SPIxMAVAIL interrupts enable failed");
-
+	dw->nfcc_coex.configured = false;
 	dw->nfcc_coex.enabled = true;
 	dw->nfcc_coex.spi_avail_cb = cb;
-	return rc;
+	return 0;
 }
 
 /**
@@ -309,6 +341,9 @@
 	int rc;
 	bool val = false;
 
+	if (!dw->nfcc_coex.configured)
+		return 0;
+
 	trace_dw3000_nfcc_coex_disable(dw);
 
 	if (__dw3000_chip_version == DW3000_C0_VERSION)
@@ -341,22 +376,9 @@
 		}
 	}
 
+	dw->nfcc_coex.configured = false;
 	/* TODO: Inform HAL that NFCC session is complete. */
 	dw->nfcc_coex.enabled = false;
 	dw->nfcc_coex.spi_avail_cb = NULL;
 	return rc;
 }
-
-/**
- * dw3000_nfcc_coex_sleep() - Entering in deep sleep.
- * @dw: Driver context.
- * @sleep_dtu: Sleep duration in dtu.
- *
- * Return: 0 on success, else an error.
- */
-int dw3000_nfcc_coex_sleep(struct dw3000 *dw, u32 sleep_dtu)
-{
-	trace_dw3000_nfcc_coex_sleep(dw, sleep_dtu);
-	dw->deep_sleep_state.next_operational_state = DW3000_OP_STATE_IDLE_PLL;
-	return dw3000_deep_sleep_and_wakeup(dw, DTU_TO_US(sleep_dtu));
-}
diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h
index 1970817..b233fdc 100644
--- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h
+++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h
@@ -32,12 +32,12 @@
 
 extern unsigned dw3000_nfcc_coex_margin_dtu;
 
-int dw3000_nfcc_coex_handle_spi1_ready_isr(struct dw3000 *dw);
 int dw3000_nfcc_coex_handle_spi1_avail_isr(struct dw3000 *dw);
+int dw3000_nfcc_coex_idle_timeout(struct dw3000 *dw);
 void dw3000_nfcc_coex_init(struct dw3000 *dw);
 int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel,
 			    dw3000_nfcc_coex_spi_avail_cb cb);
 int dw3000_nfcc_coex_disable(struct dw3000 *dw);
-int dw3000_nfcc_coex_sleep(struct dw3000 *dw, u32 sleep_dtu);
+int dw3000_nfcc_coex_configure(struct dw3000 *dw);
 
 #endif /*  __DW3000_NFCC_COEX_CORE_H */
diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c
index 17229b9..16d9145 100644
--- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c
+++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c
@@ -111,7 +111,8 @@
 	const struct dw3000_vendor_cmd_nfcc_coex_handle_access *info = data;
 	struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex;
 	const u32 dtu_per_ms = dw->llhw->dtu_freq_hz / 1000;
-	u32 diff_dtu, diff_ms;
+	u32 now_dtu, message_send_timestamp_dtu;
+	s32 idle_duration_dtu;
 	int r;
 
 	if (!info || data_len != sizeof(*info))
@@ -128,22 +129,35 @@
 		if (r)
 			return r;
 	}
+	now_dtu = dw3000_get_dtu_time(dw);
+	message_send_timestamp_dtu =
+		info->timestamp_dtu - dw3000_nfcc_coex_margin_dtu;
+	idle_duration_dtu = message_send_timestamp_dtu - now_dtu;
 
-	trace_dw3000_nfcc_coex_handle_access(dw, info);
+	trace_dw3000_nfcc_coex_handle_access(dw, info, idle_duration_dtu);
 	/* Save start session date, to retrieve MSB bits lost for next date. */
 	nfcc_coex->access_start_dtu = info->timestamp_dtu;
-	/* Setup watchdog timer. */
-	diff_dtu = info->timestamp_dtu - dw3000_get_dtu_time(dw);
-	diff_ms = diff_dtu / dtu_per_ms;
+	/* Build the when spi must be released. */
 	nfcc_coex->watchdog_timer.expires =
 		jiffies +
-		msecs_to_jiffies(diff_ms +
+		msecs_to_jiffies((info->timestamp_dtu - now_dtu) / dtu_per_ms +
 				 DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS);
 	add_timer(&nfcc_coex->watchdog_timer);
 
-	if (diff_dtu > dw3000_nfcc_coex_margin_dtu)
-		return dw3000_nfcc_coex_sleep(
-			dw, diff_dtu - dw3000_nfcc_coex_margin_dtu);
+	/* Send message and so release the SPI close to the nfc_coex_margin. */
+	message_send_timestamp_dtu =
+		info->timestamp_dtu - dw3000_nfcc_coex_margin_dtu;
+	if (idle_duration_dtu > 0)
+		return dw3000_idle(dw, true, message_send_timestamp_dtu,
+				   dw3000_nfcc_coex_idle_timeout,
+				   DW3000_OP_STATE_MAX);
+	else if (dw->current_operational_state == DW3000_OP_STATE_DEEP_SLEEP)
+		return dw3000_deepsleep_wakeup_now(
+			dw, dw3000_nfcc_coex_idle_timeout, DW3000_OP_STATE_MAX);
+
+	r = dw3000_nfcc_coex_configure(dw);
+	if (r)
+		return r;
 	return dw3000_nfcc_coex_message_send(dw);
 }
 
diff --git a/kernel/drivers/net/ieee802154/dw3000_spi.c b/kernel/drivers/net/ieee802154/dw3000_spi.c
index 52f9432..6505db0 100644
--- a/kernel/drivers/net/ieee802154/dw3000_spi.c
+++ b/kernel/drivers/net/ieee802154/dw3000_spi.c
@@ -113,9 +113,9 @@
 	dw->bw_comp = dw3000_bw_comp;
 	dw->current_operational_state = DW3000_OP_STATE_OFF;
 	init_waitqueue_head(&dw->operational_state_wq);
-	/* Initialization of the deep sleep timer */
-	hrtimer_init(&dw->deep_sleep_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	dw->deep_sleep_timer.function = dw3000_wakeup_timer;
+	/* Initialization of the idle timer for wakeup. */
+	hrtimer_init(&dw->idle_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dw->idle_timer.function = dw3000_idle_timeout;
 
 	if (dw3000_sp0_rx_antenna >= ANTMAX) {
 		dev_warn(dw->dev, "sp0 rx antenna is out of antenna range");
@@ -123,7 +123,7 @@
 	}
 	dw->sp0_rx_antenna = dw3000_sp0_rx_antenna;
 
-	dev_info(dw->dev, "Loading driver UWBM-2.0.0-21112301");
+	dev_info(dw->dev, "Loading driver UWBM-2.0.0-21121401");
 	dw3000_sysfs_init(dw);
 
 	/* Setup SPI parameters */
diff --git a/kernel/drivers/net/ieee802154/dw3000_trc.h b/kernel/drivers/net/ieee802154/dw3000_trc.h
index a69ab40..5c8d23e 100644
--- a/kernel/drivers/net/ieee802154/dw3000_trc.h
+++ b/kernel/drivers/net/ieee802154/dw3000_trc.h
@@ -93,7 +93,7 @@
 	}
 
 /* clang-format off */
-#define DW3000_OP_STATE_FLAGS             \
+#define DW3000_OP_STATE_SYMBOLS           \
 	dw3000_op_state_name(OFF),        \
 	dw3000_op_state_name(DEEP_SLEEP), \
 	dw3000_op_state_name(SLEEP),      \
@@ -276,6 +276,18 @@
 #define DW3000_NFCC_COEX_TLV_TYPE_ARG \
 	__print_symbolic(__entry->type, DW3000_NFCC_COEX_TLV_TYPE_SYMBOLS)
 
+#define dw3000_dss_stats_name(name)                      \
+	{                                                \
+		DW3000_DSS_STAT_##name##_BIT_MASK, #name \
+	}
+/* clang-format off */
+#define DW3000_DSS_STATS_SYMBOLS                           \
+	dw3000_dss_stats_name(SPI1_AVAIL),                 \
+	dw3000_dss_stats_name(SPI2_AVAIL)
+/* clang-format on */
+TRACE_DEFINE_ENUM(DW3000_DSS_STAT_SPI1_AVAIL_BIT_MASK);
+TRACE_DEFINE_ENUM(DW3000_DSS_STAT_SPI2_AVAIL_BIT_MASK);
+
 /* We don't want clang-format to modify the following events definition!
    Look at net/wireless/trace.h for the required format. */
 /* clang-format off */
@@ -467,21 +479,42 @@
 		  DW_PR_ARG, RX_FRAME_INFO_FLAGS_PR_ARG)
 	);
 
-TRACE_EVENT(dw3000_mcps_idle,
-	TP_PROTO(struct dw3000 *dw, bool timeout, u32 timeout_dtu),
-	TP_ARGS(dw, timeout, timeout_dtu),
+DEFINE_EVENT(dw_only_evt, dw3000_wakeup_done_to_tx,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
+DEFINE_EVENT(dw_only_evt, dw3000_wakeup_done_to_rx,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
+DEFINE_EVENT(dw_only_evt, dw3000_wakeup_done_to_idle,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
+TRACE_EVENT(dw3000_idle,
+	TP_PROTO(struct dw3000 *dw, bool timeout, u32 timeout_dtu,
+		 enum operational_state next_operational_state),
+	TP_ARGS(dw, timeout, timeout_dtu, next_operational_state),
 	TP_STRUCT__entry(
 		DW_ENTRY
 		__field(bool, timeout)
 		__field(u32, timeout_dtu)
+		__field(enum operational_state, next_operational_state)
 	),
 	TP_fast_assign(
 		DW_ASSIGN;
 		__entry->timeout = timeout;
 		__entry->timeout_dtu = timeout_dtu;
+		__entry->next_operational_state = next_operational_state;
 	),
-	TP_printk(DW_PR_FMT ", timeout: %s, timeout_dtu: %#x", DW_PR_ARG,
-		__entry->timeout ? "true" : "false", __entry->timeout_dtu)
+	TP_printk(DW_PR_FMT ", timeout: %s, timeout_dtu: %#x, "
+		 "next_operational_state: %s" , DW_PR_ARG,
+		__entry->timeout ? "true" : "false", __entry->timeout_dtu,
+		__print_symbolic(__entry->next_operational_state,
+				 DW3000_OP_STATE_SYMBOLS))
 );
 
 DEFINE_EVENT(dw_only_evt, dw3000_mcps_reset,
@@ -651,6 +684,22 @@
 		  DW_PR_ARG, __entry->low_sys_status)
 );
 
+TRACE_EVENT(dw3000_wakeup_and_wait,
+	TP_PROTO(struct dw3000 *dw, enum operational_state operational_state),
+	TP_ARGS(dw, operational_state),
+	TP_STRUCT__entry(
+		DW_ENTRY
+		__field(enum operational_state, operational_state)
+	),
+	TP_fast_assign(
+		DW_ASSIGN;
+		__entry->operational_state = operational_state;
+	),
+	TP_printk(DW_PR_FMT ", operational_state: %s", DW_PR_ARG,
+		  __print_symbolic(__entry->operational_state,
+				   DW3000_OP_STATE_SYMBOLS))
+);
+
 TRACE_EVENT(dw3000_set_operational_state,
 	TP_PROTO(struct dw3000 *dw, enum operational_state operational_state),
 	TP_ARGS(dw, operational_state),
@@ -664,7 +713,22 @@
 	),
 	TP_printk(DW_PR_FMT ", operational_state: %s", DW_PR_ARG,
 		  __print_symbolic(__entry->operational_state,
-				   DW3000_OP_STATE_FLAGS))
+				   DW3000_OP_STATE_SYMBOLS))
+);
+
+DEFINE_EVENT(dw_only_evt, dw3000_deepsleep_wakeup,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
+DEFINE_EVENT(dw_only_evt, dw3000_idle_timeout,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
+DEFINE_EVENT(dw_only_evt, dw3000_idle_cancel_timer,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
 );
 
 TRACE_EVENT(dw3000_check_operational_state,
@@ -688,9 +752,9 @@
 	TP_printk(DW_PR_FMT ", delay_dtu: %d, current_operational_state: %s, "
 		  "next_operational_state: %s", DW_PR_ARG, __entry->delay_dtu,
 		  __print_symbolic(__entry->current_operational_state,
-				   DW3000_OP_STATE_FLAGS),
+				   DW3000_OP_STATE_SYMBOLS),
 		  __print_symbolic(__entry->next_operational_state,
-				   DW3000_OP_STATE_FLAGS))
+				   DW3000_OP_STATE_SYMBOLS))
 );
 
 DEFINE_EVENT(dw_only_evt, dw3000_read_rx_timestamp,
@@ -785,7 +849,7 @@
 		  "next_op_date: %#x, next_op: %s", DW_PR_ARG,
 		  __entry->sleep_time_us, __entry->sleep_enter_dtu,
 		  __entry->dtu_next_op,
-		  __print_symbolic(__entry->next_op, DW3000_OP_STATE_FLAGS))
+		  __print_symbolic(__entry->next_op, DW3000_OP_STATE_SYMBOLS))
 );
 
 TRACE_EVENT(dw3000_power_stats,
@@ -958,21 +1022,9 @@
 		DW_ASSIGN;
 		__entry->dss_stat = dss_stat;
 	),
-	TP_printk(DW_PR_FMT ", dss_stat: 0x%x", DW_PR_ARG, __entry->dss_stat)
-);
-
-TRACE_EVENT(dw3000_nfcc_coex_sleep,
-	TP_PROTO(struct dw3000 *dw, u32 sleep_dtu),
-	TP_ARGS(dw, sleep_dtu),
-	TP_STRUCT__entry(
-		DW_ENTRY
-		__field(u32, sleep_dtu)
-	),
-	TP_fast_assign(
-		DW_ASSIGN;
-		__entry->sleep_dtu = sleep_dtu;
-	),
-	TP_printk(DW_PR_FMT ", sleep_dtu: 0x%08x", DW_PR_ARG, __entry->sleep_dtu)
+	TP_printk(DW_PR_FMT ", dss_stat: %s", DW_PR_ARG,
+		  __print_flags(__entry->dss_stat, "|",
+				DW3000_DSS_STATS_SYMBOLS))
 );
 
 TRACE_EVENT(dw3000_nfcc_coex_header_put,
@@ -1062,8 +1114,8 @@
 	),
 	TP_printk(DW_PR_FMT ", signature: %s, ver_id: %u, seq_num: %u"
 		  ", nb_tlv: %u", DW_PR_ARG,
-		  __print_array(__entry->signature,
-				DW3000_NFCC_COEX_SIGNATURE_LEN, sizeof(u8)),
+		  __print_hex(__entry->signature,
+			      DW3000_NFCC_COEX_SIGNATURE_LEN),
 		  __entry->ver_id, __entry->seq_num, __entry->nb_tlv)
 );
 
@@ -1088,11 +1140,13 @@
 );
 
 TRACE_EVENT(dw3000_nfcc_coex_handle_access,
-	TP_PROTO(struct dw3000 *dw, const struct dw3000_vendor_cmd_nfcc_coex_handle_access *info),
-	TP_ARGS(dw, info),
+	TP_PROTO(struct dw3000 *dw, const struct dw3000_vendor_cmd_nfcc_coex_handle_access *info,
+		 s32 idle_duration_dtu),
+	TP_ARGS(dw, info, idle_duration_dtu),
 	TP_STRUCT__entry(
 		DW_ENTRY
 		__field(bool, start)
+		__field(s32, idle_duration_dtu)
 		__field(u32, timestamp_dtu)
 		__field(int, duration_dtu)
 		__field(int, chan)
@@ -1100,13 +1154,17 @@
 	TP_fast_assign(
 		DW_ASSIGN;
 		__entry->start = info->start;
+		__entry->idle_duration_dtu = idle_duration_dtu;
 		__entry->timestamp_dtu = info->timestamp_dtu;
 		__entry->duration_dtu = info->duration_dtu;
 		__entry->chan = info->chan;
 	),
-	TP_printk(DW_PR_FMT ", start: %s, timestamp_dtu: 0x%08x"
-		  ", duration_dtu: 0x%08x, chan: %d", DW_PR_ARG,
-		  __entry->start ? "true" : "false", __entry->timestamp_dtu,
+	TP_printk(DW_PR_FMT ", start: %s, idle_duration_dtu: 0x%08x"
+		  ", timestamp_dtu: 0x%08x, duration_dtu: 0x%08x,"
+		  " chan: %d", DW_PR_ARG,
+		  __entry->start ? "true" : "false",
+		  __entry->idle_duration_dtu,
+		  __entry->timestamp_dtu,
 		  __entry->duration_dtu, __entry->chan)
 );
 
@@ -1138,11 +1196,21 @@
 	TP_printk(DW_PR_FMT ", warn: \"%s\"", DW_PR_ARG, __get_str(warn))
 );
 
+DEFINE_EVENT(dw_only_evt, dw3000_nfcc_coex_configure,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
 DEFINE_EVENT(dw_only_evt, dw3000_nfcc_coex_watchdog,
 	TP_PROTO(struct dw3000 *dw),
 	TP_ARGS(dw)
 );
 
+DEFINE_EVENT(dw_only_evt, dw3000_nfcc_coex_idle_timeout,
+	TP_PROTO(struct dw3000 *dw),
+	TP_ARGS(dw)
+);
+
 TRACE_EVENT(dw3000_nfcc_coex_enable,
 	TP_PROTO(struct dw3000 *dw, int channel),
 	TP_ARGS(dw, channel),
diff --git a/kernel/net/mcps802154/mcps802154_qorvo.h b/kernel/net/mcps802154/mcps802154_qorvo.h
new file mode 120000
index 0000000..48ea31d
--- /dev/null
+++ b/kernel/net/mcps802154/mcps802154_qorvo.h
@@ -0,0 +1 @@
+../../../mac/mcps802154_qorvo.h
\ No newline at end of file
diff --git a/kernel/net/mcps802154/nfcc_coex_trace.h b/kernel/net/mcps802154/nfcc_coex_trace.h
index 42f9e7b..7a3a3ef 100644
--- a/kernel/net/mcps802154/nfcc_coex_trace.h
+++ b/kernel/net/mcps802154/nfcc_coex_trace.h
@@ -28,51 +28,178 @@
 #define NFCC_COEX_TRACE_H
 
 #include <linux/tracepoint.h>
+#include <net/nfcc_coex_region_nl.h>
 #include "mcps802154_i.h"
-#include <net/vendor_cmd.h>
+#include "nfcc_coex_region.h"
+#include "nfcc_coex_session.h"
 
 /* clang-format off */
+#define nfcc_coex_call_name(name)                       \
+	{                                               \
+		NFCC_COEX_CALL_##name, #name            \
+	}
+#define NFCC_COEX_CALL_SYMBOLS                          \
+	nfcc_coex_call_name(CCC_SESSION_START),         \
+	nfcc_coex_call_name(CCC_SESSION_STOP),          \
+	nfcc_coex_call_name(CCC_SESSION_NOTIFICATION)
+TRACE_DEFINE_ENUM(NFCC_COEX_CALL_CCC_SESSION_START);
+TRACE_DEFINE_ENUM(NFCC_COEX_CALL_CCC_SESSION_STOP);
+TRACE_DEFINE_ENUM(NFCC_COEX_CALL_CCC_SESSION_NOTIFICATION);
 
-#define LOCAL_ENTRY __field(int, hw_idx)
-#define LOCAL_ASSIGN __entry->hw_idx = local->hw_idx
-#define LOCAL_PR_FMT "hw%d"
-#define LOCAL_PR_ARG __entry->hw_idx
+#define nfcc_coex_state_name(name)                       \
+	{                                                \
+		NFCC_COEX_STATE_##name, #name            \
+	}
+#define NFCC_COEX_STATE_SYMBOLS                          \
+	nfcc_coex_state_name(IDLE),                      \
+	nfcc_coex_state_name(ACCESSING),                 \
+	nfcc_coex_state_name(STOPPING)
+TRACE_DEFINE_ENUM(NFCC_COEX_STATE_IDLE);
+TRACE_DEFINE_ENUM(NFCC_COEX_STATE_ACCESSING);
+TRACE_DEFINE_ENUM(NFCC_COEX_STATE_STOPPING);
 
-TRACE_EVENT(nfcc_coex_llhw_return_int,
-	TP_PROTO(const struct mcps802154_local *local, int ret),
-	TP_ARGS(local, ret),
+#define NFCC_COEX_LOCAL_ENTRY __field(enum nfcc_coex_state, state)
+#define NFCC_COEX_LOCAL_ASSIGN __entry->state = local->session.state
+#define NFCC_COEX_LOCAL_PR_FMT "state=%s"
+#define NFCC_COEX_LOCAL_PR_ARG \
+	__print_symbolic(__entry->state, NFCC_COEX_STATE_SYMBOLS)
+
+DECLARE_EVENT_CLASS(
+	local_only_evt,
+	TP_PROTO(const struct nfcc_coex_local *local),
+	TP_ARGS(local),
 	TP_STRUCT__entry(
-		LOCAL_ENTRY
-		__field(int, ret)
+		NFCC_COEX_LOCAL_ENTRY
+	),
+	TP_fast_assign(
+		NFCC_COEX_LOCAL_ASSIGN;
+	),
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT, NFCC_COEX_LOCAL_PR_ARG)
+);
+
+TRACE_EVENT(
+	region_nfcc_coex_session_start,
+	TP_PROTO(const struct nfcc_coex_local *local,
+		 const struct nfcc_coex_session_params *p),
+	TP_ARGS(local, p),
+	TP_STRUCT__entry(
+		NFCC_COEX_LOCAL_ENTRY
+		__field(u64, time0_ns)
+		__field(u8, channel_number)
+	),
+	TP_fast_assign(
+		NFCC_COEX_LOCAL_ASSIGN;
+		__entry->time0_ns = p->time0_ns;
+		__entry->channel_number = p->channel_number;
+	),
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT " time0_ns=%llu channel_number=%d",
+		  NFCC_COEX_LOCAL_PR_ARG, __entry->time0_ns,
+		  __entry->channel_number)
+);
+
+DEFINE_EVENT(
+	local_only_evt, region_nfcc_coex_session_stop,
+	TP_PROTO(const struct nfcc_coex_local *local),
+	TP_ARGS(local)
+);
+
+DEFINE_EVENT(
+	local_only_evt, region_nfcc_coex_notify_stop,
+	TP_PROTO(const struct nfcc_coex_local *local),
+	TP_ARGS(local)
+);
+
+TRACE_EVENT(
+	region_nfcc_coex_call,
+	TP_PROTO(const struct nfcc_coex_local *local,
+		 enum nfcc_coex_call call_id),
+	TP_ARGS(local, call_id),
+	TP_STRUCT__entry(
+		NFCC_COEX_LOCAL_ENTRY
+		__field(enum nfcc_coex_call, call_id)
 		),
 	TP_fast_assign(
-		LOCAL_ASSIGN;
-		__entry->ret = ret;
+		NFCC_COEX_LOCAL_ASSIGN;
+		__entry->call_id = call_id;
 		),
-	TP_printk(LOCAL_PR_FMT " returned=%d", LOCAL_PR_ARG, __entry->ret)
-	);
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT " call_id=%s",
+		  NFCC_COEX_LOCAL_PR_ARG,
+		  __print_symbolic(__entry->call_id, NFCC_COEX_CALL_SYMBOLS))
+);
 
-TRACE_EVENT(nfcc_coex_llhw_vendor_cmd,
-	TP_PROTO(const struct mcps802154_local *local, u32 vendor_id,
-		 u32 subcmd),
-	TP_ARGS(local, vendor_id, subcmd),
+TRACE_EVENT(
+	region_nfcc_coex_get_demand,
+	TP_PROTO(const struct nfcc_coex_local *local,
+		 u32 next_timestamp_dtu,
+		 const struct mcps802154_region_demand *rd),
+	TP_ARGS(local, next_timestamp_dtu, rd),
 	TP_STRUCT__entry(
-		LOCAL_ENTRY
-		__field(u32, vendor_id)
-		__field(u32, subcmd)
+		NFCC_COEX_LOCAL_ENTRY
+		__field(u32, next_timestamp_dtu)
+		__field(u32, timestamp_dtu)
+		__field(int, duration_dtu)
 		),
 	TP_fast_assign(
-		LOCAL_ASSIGN;
-		__entry->vendor_id = vendor_id;
-		__entry->subcmd = subcmd;
+		NFCC_COEX_LOCAL_ASSIGN;
+		__entry->next_timestamp_dtu = next_timestamp_dtu;
+		__entry->timestamp_dtu = rd->timestamp_dtu;
+		__entry->duration_dtu = rd->duration_dtu;
 		),
-	TP_printk(LOCAL_PR_FMT " vendor_id=%06x subcmd=%s", LOCAL_PR_ARG,
-		  __entry->vendor_id,
-		  __print_symbolic(__entry->subcmd,
-			{ DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS, "HANDLE_ACCESS"},
-			{ DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION, "GET_ACCESS_INFORMATION"},
-			{ DW3000_VENDOR_CMD_NFCC_COEX_STOP, "STOP"}))
-	);
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT " next_timestamp_dtu=0x%08x "
+		  "rd.timestamp_dtu=0x%08x rd.duration_dtu=0x%08x",
+		  NFCC_COEX_LOCAL_PR_ARG,
+		  __entry->next_timestamp_dtu,
+		  __entry->timestamp_dtu,
+		  __entry->duration_dtu)
+);
+
+TRACE_EVENT(
+	region_nfcc_coex_set_state,
+	TP_PROTO(const struct nfcc_coex_local *local,
+		 enum nfcc_coex_state new_state),
+	TP_ARGS(local, new_state),
+	TP_STRUCT__entry(
+		NFCC_COEX_LOCAL_ENTRY
+		__field(enum nfcc_coex_state, new_state)
+		),
+	TP_fast_assign(
+		NFCC_COEX_LOCAL_ASSIGN;
+		__entry->new_state = new_state;
+		),
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT " new_state=%s",
+		  NFCC_COEX_LOCAL_PR_ARG,
+		  __print_symbolic(__entry->new_state,
+				       NFCC_COEX_STATE_SYMBOLS))
+);
+
+TRACE_EVENT(
+	region_nfcc_coex_report,
+	TP_PROTO(const struct nfcc_coex_local *local,
+		 const struct dw3000_vendor_cmd_nfcc_coex_get_access_info *info),
+	TP_ARGS(local, info),
+	TP_STRUCT__entry(
+		NFCC_COEX_LOCAL_ENTRY
+		__field(bool, watchdog_timeout)
+		__field(bool, stop)
+		),
+	TP_fast_assign(
+		NFCC_COEX_LOCAL_ASSIGN;
+		__entry->watchdog_timeout = info->watchdog_timeout;
+		__entry->stop = info->stop;
+		),
+	TP_printk(NFCC_COEX_LOCAL_PR_FMT " watchdog_timeout=%s stop=%s",
+		  NFCC_COEX_LOCAL_PR_ARG,
+		  __entry->watchdog_timeout ? "true": "false",
+		  __entry->stop ? "true": "false")
+);
+
+DEFINE_EVENT(
+	local_only_evt, region_nfcc_coex_report_nla_put_failure,
+	TP_PROTO(const struct nfcc_coex_local *local),
+	TP_ARGS(local)
+);
+
+/* clang-format on */
 
 #endif /* !NFCC_COEX_TRACE_H || TRACE_HEADER_MULTI_READ */
 
diff --git a/kernel/net/mcps802154/trace.h b/kernel/net/mcps802154/trace.h
index 2620675..d618abc 100644
--- a/kernel/net/mcps802154/trace.h
+++ b/kernel/net/mcps802154/trace.h
@@ -598,6 +598,27 @@
 	TP_ARGS(local)
 	);
 
+TRACE_EVENT(llhw_vendor_cmd,
+	TP_PROTO(const struct mcps802154_local *local, u32 vendor_id,
+		 u32 subcmd, size_t data_len),
+	TP_ARGS(local, vendor_id, subcmd, data_len),
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		__field(u32, vendor_id)
+		__field(u32, subcmd)
+		__field(u32, data_len)
+		),
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		__entry->vendor_id = vendor_id;
+		__entry->subcmd = subcmd;
+		__entry->data_len = data_len;
+		),
+	TP_printk(LOCAL_PR_FMT " vendor_id=0x%06x subcmd=0x%x data_len=%d",
+		  LOCAL_PR_ARG, __entry->vendor_id, __entry->subcmd,
+		  __entry->data_len)
+	);
+
 TRACE_EVENT(llhw_event_rx_error,
 	TP_PROTO(const struct mcps802154_local *local,
 		 enum mcps802154_rx_error_type error),
diff --git a/mac/fproc_vendor.c b/mac/fproc_vendor.c
index 0875cb5..f4441d7 100644
--- a/mac/fproc_vendor.c
+++ b/mac/fproc_vendor.c
@@ -34,13 +34,14 @@
 	 * Avoid usage of access pointer after access done call. */
 	int duration_dtu = access->duration_dtu;
 	u32 next_access_dtu = access->timestamp_dtu + duration_dtu;
+	/* Filter-out the 'stop' request as error. */
+	int error = r == 1 ? 0 : r;
 
 	if (!r)
-		/* TODO_MR : call access_done(false) ?? */
 		return;
 
-	mcps802154_fproc_access_done(local, r);
-	if (r != 1) {
+	mcps802154_fproc_access_done(local, error);
+	if (error) {
 		mcps802154_fproc_broken_handle(local);
 	} else if (duration_dtu) {
 		mcps802154_fproc_access(local, next_access_dtu);
diff --git a/mac/llhw-ops.h b/mac/llhw-ops.h
index eadb52f..fb6aefe 100644
--- a/mac/llhw-ops.h
+++ b/mac/llhw-ops.h
@@ -28,7 +28,6 @@
 
 #include "mcps802154_i.h"
 #include "trace.h"
-#include "nfcc_coex_trace.h"
 
 static inline int llhw_start(struct mcps802154_local *local)
 {
@@ -311,13 +310,13 @@
 {
 	int r;
 
-	trace_nfcc_coex_llhw_vendor_cmd(local, vendor_id, subcmd);
+	trace_llhw_vendor_cmd(local, vendor_id, subcmd, data_len);
 	if (local->ops->vendor_cmd)
 		r = local->ops->vendor_cmd(&local->llhw, vendor_id, subcmd,
 					   data, data_len);
 	else
 		r = -EOPNOTSUPP;
-	trace_nfcc_coex_llhw_return_int(local, r);
+	trace_llhw_return_int(local, r);
 	return r;
 }
 
diff --git a/mac/mcps802154_qorvo.h b/mac/mcps802154_qorvo.h
new file mode 100644
index 0000000..170ea06
--- /dev/null
+++ b/mac/mcps802154_qorvo.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the UWB stack for linux.
+ *
+ * Copyright (c) 2021 Qorvo US, Inc.
+ *
+ * This software is provided under the GNU General Public License, version 2
+ * (GPLv2), as well as under a Qorvo commercial license.
+ *
+ * You may choose to use this software under the terms of the GPLv2 License,
+ * version 2 ("GPLv2"), as published by the Free Software Foundation.
+ * You should have received a copy of the GPLv2 along with this program.  If
+ * not, see <http://www.gnu.org/licenses/>.
+ *
+ * This program is distributed under the GPLv2 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 GPLv2 for more
+ * details.
+ *
+ * If you cannot meet the requirements of the GPLv2, you may not use this
+ * software for any purpose without first obtaining a commercial license from
+ * Qorvo. Please contact Qorvo to inquire about licensing terms.
+ */
+
+#ifndef MCPS802154_QORVO_H
+#define MCPS802154_QORVO_H
+
+/* Qorvo OUI in big endian.
+ * The define can't be declared in include/net/vendor_cmd.h because
+ * it's not generic to other mac provider. */
+#define VENDOR_QORVO_OUI 0xc8b1ee00
+
+#endif /* MCPS802154_QORVO_H */
diff --git a/mac/mcps_main.c b/mac/mcps_main.c
index fc09787..cc992b9 100644
--- a/mac/mcps_main.c
+++ b/mac/mcps_main.c
@@ -227,6 +227,15 @@
 }
 EXPORT_SYMBOL(mcps802154_difference_timestamp_rctu);
 
+int mcps802154_vendor_cmd(struct mcps802154_llhw *llhw, u32 vendor_id,
+			  u32 subcmd, void *data, size_t data_len)
+{
+	struct mcps802154_local *local = llhw_to_local(llhw);
+
+	return llhw_vendor_cmd(local, vendor_id, subcmd, data, data_len);
+}
+EXPORT_SYMBOL(mcps802154_vendor_cmd);
+
 struct mcps802154_local *mcps802154_get_first_by_idx(int hw_idx)
 {
 	struct mcps802154_local *result = NULL, *local;
diff --git a/mac/nfcc_coex_access.c b/mac/nfcc_coex_access.c
index bd4f90a..e9617ae 100644
--- a/mac/nfcc_coex_access.c
+++ b/mac/nfcc_coex_access.c
@@ -24,7 +24,9 @@
 #include "nfcc_coex_access.h"
 #include "nfcc_coex_session.h"
 #include "nfcc_coex_region.h"
+#include "nfcc_coex_trace.h"
 #include "llhw-ops.h"
+#include "mcps802154_qorvo.h"
 
 #include <linux/string.h>
 #include <linux/ieee802154.h>
@@ -33,47 +35,38 @@
 
 #include "warn_return.h"
 
-static void nfcc_coex_stop_by_vendor_cmd_failure(struct nfcc_coex_local *local)
-{
-	static const struct dw3000_vendor_cmd_nfcc_coex_get_access_info error = {
-		.stop = 1
-	};
-
-	local->session.get_access_info = error;
-	local->state = NFCC_COEX_STATE_STOPPING;
-}
-
-static int nfcc_coex_vendor_cmd(struct mcps802154_llhw *llhw,
-				enum dw3000_vendor_cmd subcmd, void *data,
-				size_t data_len)
-{
-	struct mcps802154_local *local = llhw_to_local(llhw);
-	/* Qorvo OUI in big endian. */
-	static const u32 qorvo_oui = 0xc8b1ee00;
-
-	return llhw_vendor_cmd(local, qorvo_oui, subcmd, data, data_len);
-}
-
 static void nfcc_coex_access_done(struct mcps802154_access *access, int error)
 {
 	struct nfcc_coex_local *local = access_to_local(access);
 	struct nfcc_coex_session *session = &local->session;
 
-	switch (local->state) {
+	if (error) {
+		const struct dw3000_vendor_cmd_nfcc_coex_get_access_info stop = {
+			.stop = true,
+		};
+
+		local->session.get_access_info = stop;
+		nfcc_coex_set_state(local, NFCC_COEX_STATE_STOPPING);
+	}
+
+	switch (session->state) {
 	case NFCC_COEX_STATE_STOPPING:
-		nfcc_coex_vendor_cmd(local->llhw,
-				     DW3000_VENDOR_CMD_NFCC_COEX_STOP, NULL, 0);
+		mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI,
+				      DW3000_VENDOR_CMD_NFCC_COEX_STOP, NULL,
+				      0);
 		nfcc_coex_report(local);
+		session->started = false;
 		break;
 	case NFCC_COEX_STATE_ACCESSING:
 		if (session->get_access_info.stop ||
 		    session->get_access_info.watchdog_timeout)
-			local->state = NFCC_COEX_STATE_STOPPING;
+			session->started = false;
 		nfcc_coex_report(local);
 		break;
 	default:
 		WARN_UNREACHABLE_DEFAULT();
 	}
+	nfcc_coex_set_state(local, NFCC_COEX_STATE_IDLE);
 }
 
 static int nfcc_coex_handle(struct mcps802154_access *access)
@@ -81,21 +74,18 @@
 	struct nfcc_coex_local *local = access_to_local(access);
 	struct nfcc_coex_session *session = &local->session;
 	struct dw3000_vendor_cmd_nfcc_coex_handle_access handle_access = {};
-	int r;
 
 	handle_access.start = session->first_access;
 	handle_access.timestamp_dtu = access->timestamp_dtu;
 	handle_access.duration_dtu = access->duration_dtu;
 	handle_access.chan = session->params.channel_number;
 
+	nfcc_coex_set_state(local, NFCC_COEX_STATE_ACCESSING);
 	session->first_access = false;
 
-	r = nfcc_coex_vendor_cmd(local->llhw,
-				 DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS,
-				 &handle_access, sizeof(handle_access));
-	if (r)
-		nfcc_coex_stop_by_vendor_cmd_failure(local);
-	return r;
+	return mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI,
+				     DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS,
+				     &handle_access, sizeof(handle_access));
 }
 
 static int nfcc_coex_tx_done(struct mcps802154_access *access)
@@ -107,17 +97,16 @@
 	struct mcps802154_region_demand *rd = &session->region_demand;
 	int r;
 
-	r = nfcc_coex_vendor_cmd(
-		local->llhw, DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION,
+	r = mcps802154_vendor_cmd(
+		local->llhw, VENDOR_QORVO_OUI,
+		DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION,
 		get_access_info, sizeof(*get_access_info));
-	if (r) {
-		nfcc_coex_stop_by_vendor_cmd_failure(local);
+	if (r)
 		return r;
-	}
 
+	/* Update region demand for next access. */
 	rd->timestamp_dtu = get_access_info->next_timestamp_dtu;
 	rd->duration_dtu = get_access_info->next_duration_dtu;
-
 	/* Request end of current access. */
 	return 1;
 }
@@ -135,11 +124,12 @@
 			    struct nfcc_coex_session *session)
 {
 	struct mcps802154_access *access = &local->access;
+	const struct mcps802154_region_demand *rd = &session->region_demand;
 
 	access->method = MCPS802154_ACCESS_METHOD_VENDOR;
 	access->vendor_ops = &nfcc_coex_ops;
-	access->duration_dtu = session->region_demand.duration_dtu;
-	access->timestamp_dtu = session->region_demand.timestamp_dtu;
+	access->duration_dtu = rd->duration_dtu;
+	access->timestamp_dtu = rd->timestamp_dtu;
 	access->n_frames = 0;
 	access->frames = NULL;
 
@@ -152,17 +142,12 @@
 					       int region_duration_dtu)
 {
 	struct nfcc_coex_local *local = region_to_local(region);
-	struct nfcc_coex_session *session;
+	struct nfcc_coex_session *session = &local->session;
 
-	/* Get unique session. */
-	session = nfcc_coex_session_next(local, next_timestamp_dtu,
+	if (session->started) {
+		nfcc_coex_session_update(session, next_timestamp_dtu,
 					 region_duration_dtu);
-
-	if (!session) {
-		local->state = NFCC_COEX_STATE_UNUSED;
-		return NULL;
-	} else {
-		local->state = NFCC_COEX_STATE_ACCESSING;
 		return nfcc_coex_access_controller(local, session);
 	}
+	return NULL;
 }
diff --git a/mac/nfcc_coex_region.c b/mac/nfcc_coex_region.c
index 719b7de..786aa26 100644
--- a/mac/nfcc_coex_region.c
+++ b/mac/nfcc_coex_region.c
@@ -36,6 +36,7 @@
 #include "nfcc_coex_region_call.h"
 #include "nfcc_coex_access.h"
 #include "nfcc_coex_session.h"
+#include "nfcc_coex_trace.h"
 
 static struct mcps802154_region_ops nfcc_coex_region_ops;
 
@@ -43,13 +44,13 @@
 {
 	struct nfcc_coex_local *local;
 
-	local = kmalloc(sizeof(*local), GFP_KERNEL);
+	local = kzalloc(sizeof(*local), GFP_KERNEL);
 	if (!local)
 		return NULL;
 
 	local->llhw = llhw;
 	local->region.ops = &nfcc_coex_region_ops;
-	local->state = NFCC_COEX_STATE_UNUSED;
+	local->session.state = NFCC_COEX_STATE_IDLE;
 
 	return &local->region;
 }
@@ -64,13 +65,15 @@
 static void nfcc_coex_notify_stop(struct mcps802154_region *region)
 {
 	struct nfcc_coex_local *local = region_to_local(region);
+	struct nfcc_coex_session *session = &local->session;
 
+	trace_region_nfcc_coex_notify_stop(local);
 	nfcc_coex_session_control(local, NFCC_COEX_CALL_CCC_SESSION_STOP, NULL,
 				  NULL);
-	if (local->state != NFCC_COEX_STATE_UNUSED) {
+	if (session->started) {
 		pr_err("device stopped while nfcc coex not stopped state=%d",
-		       local->state);
-		local->state = NFCC_COEX_STATE_UNUSED;
+		       local->session.state);
+		session->started = false;
 	}
 }
 
@@ -80,6 +83,7 @@
 {
 	struct nfcc_coex_local *local = region_to_local(region);
 
+	trace_region_nfcc_coex_call(local, call_id);
 	switch (call_id) {
 	case NFCC_COEX_CALL_CCC_SESSION_START:
 	case NFCC_COEX_CALL_CCC_SESSION_STOP:
@@ -95,34 +99,39 @@
 {
 	struct nfcc_coex_local *local = region_to_local(region);
 	const struct nfcc_coex_session *session = &local->session;
+	const struct mcps802154_region_demand *rd = &session->region_demand;
 
-	switch (local->state) {
-	case NFCC_COEX_STATE_UNUSED:
-		return 0;
-	default:
+	trace_region_nfcc_coex_get_demand(local, next_timestamp_dtu, rd);
+	if (session->started) {
 		if (session->first_access) {
 			/* region_demand have never been set.
 			 * Duration is set at 12ms, value catched during test. */
 			demand->timestamp_dtu = next_timestamp_dtu;
 			demand->duration_dtu = 187200;
-		} else if (is_before_dtu(session->region_demand.timestamp_dtu,
+		} else if (is_before_dtu(rd->timestamp_dtu,
 					 next_timestamp_dtu)) {
 			/* Date is late. */
-			int shift = next_timestamp_dtu -
-				    session->region_demand.timestamp_dtu;
-			int duration =
-				session->region_demand.duration_dtu - shift;
+			int shift = next_timestamp_dtu - rd->timestamp_dtu;
+			int duration = rd->duration_dtu - shift;
 			demand->timestamp_dtu = next_timestamp_dtu;
 			demand->duration_dtu = duration > 0 ? duration : 1;
 		} else {
-			memcpy(demand, &session->region_demand,
-			       sizeof(*demand));
+			memcpy(demand, rd, sizeof(*demand));
 		}
 		return 1;
 	}
 	return 0;
 }
 
+void nfcc_coex_set_state(struct nfcc_coex_local *local,
+			 enum nfcc_coex_state new_state)
+{
+	struct nfcc_coex_session *session = &local->session;
+
+	trace_region_nfcc_coex_set_state(local, new_state);
+	session->state = new_state;
+}
+
 void nfcc_coex_report(struct nfcc_coex_local *local)
 {
 	struct nfcc_coex_session *session = &local->session;
@@ -131,6 +140,7 @@
 	struct sk_buff *msg;
 	int r;
 
+	trace_region_nfcc_coex_report(local, get_access_info);
 	msg = mcps802154_region_event_alloc_skb(
 		local->llhw, &local->region,
 		NFCC_COEX_CALL_CCC_SESSION_NOTIFICATION, session->event_portid,
@@ -157,6 +167,7 @@
 	return;
 
 nla_put_failure:
+	trace_region_nfcc_coex_report_nla_put_failure(local);
 	kfree_skb(msg);
 }
 
diff --git a/mac/nfcc_coex_region.h b/mac/nfcc_coex_region.h
index f39d9a0..b9828df 100644
--- a/mac/nfcc_coex_region.h
+++ b/mac/nfcc_coex_region.h
@@ -29,24 +29,6 @@
 #include "nfcc_coex_session.h"
 
 /**
- * enum nfcc_coex_state - State of the unique session.
- * @NFCC_COEX_STATE_UNUSED:
- *	Session is unused by access and not started too.
- * @NFCC_COEX_STATE_STARTED:
- *	Session is started but not used by access right now.
- * @NFCC_COEX_STATE_ACCESSING:
- *	Session is currently used on an access.
- * @NFCC_COEX_STATE_STOPPING:
- *	Session is currently used for the last access.
- */
-enum nfcc_coex_state {
-	NFCC_COEX_STATE_UNUSED,
-	NFCC_COEX_STATE_STARTED,
-	NFCC_COEX_STATE_ACCESSING,
-	NFCC_COEX_STATE_STOPPING,
-};
-
-/**
  * struct nfcc_coex_local - Local context.
  */
 struct nfcc_coex_local {
@@ -66,10 +48,6 @@
 	 * @session: Unique session on the NFCC controller.
 	 */
 	struct nfcc_coex_session session;
-	/**
-	 * @state: State of the unique session.
-	 */
-	enum nfcc_coex_state state;
 };
 
 static inline struct nfcc_coex_local *
@@ -85,6 +63,14 @@
 }
 
 /**
+ * nfcc_coex_set_state() - Set the new state.
+ * @local: NFCC coex context.
+ * @new_state: New nfcc_coex state.
+ */
+void nfcc_coex_set_state(struct nfcc_coex_local *local,
+			 enum nfcc_coex_state new_state);
+
+/**
  * nfcc_coex_report() - Send notification to upper layer.
  * @local: Local nfcc coex context.
  */
diff --git a/mac/nfcc_coex_region_call.c b/mac/nfcc_coex_region_call.c
index 5d310d4..f207006 100644
--- a/mac/nfcc_coex_region_call.c
+++ b/mac/nfcc_coex_region_call.c
@@ -29,6 +29,7 @@
 
 #include "nfcc_coex_session.h"
 #include "nfcc_coex_region_call.h"
+#include "nfcc_coex_trace.h"
 
 static const struct nla_policy nfcc_coex_call_nla_policy[NFCC_COEX_CALL_ATTR_MAX +
 							 1] = {
@@ -92,27 +93,27 @@
 				   const struct genl_info *info)
 {
 	struct nfcc_coex_session *session = &local->session;
+	const struct nfcc_coex_session_params *p = &session->params;
 	int initiation_time_dtu;
 	u32 now_dtu;
 	int r;
 
-	WARN_ON(local->state != NFCC_COEX_STATE_UNUSED);
+	WARN_ON(session->started);
 
+	trace_region_nfcc_coex_session_start(local, p);
 	r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu);
 	if (r)
 		return r;
 
 	initiation_time_dtu =
-		(session->params.time0_ns * local->llhw->dtu_freq_hz) /
-		NS_PER_SECOND;
+		(p->time0_ns * local->llhw->dtu_freq_hz) / NS_PER_SECOND;
 	session->region_demand.timestamp_dtu = now_dtu + initiation_time_dtu;
 	session->region_demand.duration_dtu = 0;
 	session->event_portid = info->snd_portid;
 	session->first_access = true;
-	local->state = NFCC_COEX_STATE_STARTED;
+	session->started = true;
 
 	mcps802154_reschedule(local->llhw);
-
 	return 0;
 }
 
@@ -139,7 +140,7 @@
 	if (r)
 		return r;
 
-	if (local->state != NFCC_COEX_STATE_UNUSED)
+	if (local->session.started)
 		return -EBUSY;
 
 	nfcc_coex_session_init(local);
@@ -164,17 +165,23 @@
  */
 static int nfcc_coex_session_stop(struct nfcc_coex_local *local)
 {
-	switch (local->state) {
-	case NFCC_COEX_STATE_STARTED:
-		local->state = NFCC_COEX_STATE_UNUSED;
-		break;
-	case NFCC_COEX_STATE_ACCESSING:
-		/* The transition from ACCESSING to UNUSED will be done
-		 * in nfcc_coex_get_access function. */
-		local->state = NFCC_COEX_STATE_STOPPING;
-		break;
-	default:
-		break;
+	struct nfcc_coex_session *session = &local->session;
+
+	trace_region_nfcc_coex_session_stop(local);
+
+	if (session->started) {
+		switch (session->state) {
+		case NFCC_COEX_STATE_IDLE:
+			session->started = false;
+			break;
+		case NFCC_COEX_STATE_ACCESSING:
+			/* The transition from ACCESSING to UNUSED will be done
+			 * in nfcc_coex_get_access function. */
+			nfcc_coex_set_state(local, NFCC_COEX_STATE_STOPPING);
+			break;
+		default:
+			break;
+		}
 	}
 
 	return 0;
diff --git a/mac/nfcc_coex_session.c b/mac/nfcc_coex_session.c
index 7f6d9e1..e84ef1c 100644
--- a/mac/nfcc_coex_session.c
+++ b/mac/nfcc_coex_session.c
@@ -37,9 +37,8 @@
 		      local->llhw->dtu_freq_hz;
 }
 
-static void nfcc_coex_session_update(struct nfcc_coex_session *session,
-				     u32 next_timestamp_dtu,
-				     int region_duration_dtu)
+void nfcc_coex_session_update(struct nfcc_coex_session *session,
+			      u32 next_timestamp_dtu, int region_duration_dtu)
 {
 	s32 diff_dtu =
 		session->region_demand.timestamp_dtu - next_timestamp_dtu;
@@ -63,24 +62,3 @@
 #endif
 	}
 }
-
-struct nfcc_coex_session *nfcc_coex_session_next(struct nfcc_coex_local *local,
-						 u32 next_timestamp_dtu,
-						 int region_duration_dtu)
-{
-	struct nfcc_coex_session *session;
-
-	switch (local->state) {
-	case NFCC_COEX_STATE_STARTED:
-	case NFCC_COEX_STATE_ACCESSING:
-		/* Get unique session. */
-		session = &local->session;
-
-		nfcc_coex_session_update(session, next_timestamp_dtu,
-					 region_duration_dtu);
-		return session;
-
-	default:
-		return NULL;
-	}
-}
diff --git a/mac/nfcc_coex_session.h b/mac/nfcc_coex_session.h
index 5106d5f..351b07f 100644
--- a/mac/nfcc_coex_session.h
+++ b/mac/nfcc_coex_session.h
@@ -45,6 +45,21 @@
 };
 
 /**
+ * enum nfcc_coex_state - State of the unique session.
+ * @NFCC_COEX_STATE_IDLE:
+ *     Session is not used by access right now.
+ * @NFCC_COEX_STATE_ACCESSING:
+ *     Session is currently used on an access.
+ * @NFCC_COEX_STATE_STOPPING:
+ *     Session is currently used for the last access.
+ */
+enum nfcc_coex_state {
+	NFCC_COEX_STATE_IDLE,
+	NFCC_COEX_STATE_ACCESSING,
+	NFCC_COEX_STATE_STOPPING,
+};
+
+/**
  * struct nfcc_coex_session - Session information.
  */
 struct nfcc_coex_session {
@@ -69,6 +84,14 @@
 	 * @first_access: True on the first access.
 	 */
 	bool first_access;
+	/**
+	 * @state: State of the unique session.
+	 */
+	enum nfcc_coex_state state;
+	/**
+	 * @started: Session is currently started.
+	 */
+	bool started;
 };
 
 /* Forward declaration. */
@@ -81,15 +104,12 @@
 void nfcc_coex_session_init(struct nfcc_coex_local *local);
 
 /**
- * nfcc_coex_session_next() - Find the next session to use after the given timestamp.
- * @local: NFCC coex context.
+ * nfcc_coex_session_update() - Update session timestamps.
+ * @session: Session context.
  * @next_timestamp_dtu: Next start access opportunity.
  * @region_duration_dtu: Region duration, or 0 for endless region.
- *
- * Return: The session or NULL if none.
  */
-struct nfcc_coex_session *nfcc_coex_session_next(struct nfcc_coex_local *local,
-						 u32 next_timestamp_dtu,
-						 int region_duration_dtu);
+void nfcc_coex_session_update(struct nfcc_coex_session *session,
+			      u32 next_timestamp_dtu, int region_duration_dtu);
 
 #endif /* NET_MCPS802154_NFCC_COEX_SESSION_H */