@@ -54,32 +54,6 @@ struct a5xx_gpu {
#endif
/*
- * In order to do lockless preemption we use a simple state machine to progress
- * through the process.
- *
- * PREEMPT_NONE - no preemption in progress. Next state START.
- * PREEMPT_START - The trigger is evaulating if preemption is possible. Next
- * states: TRIGGERED, NONE
- * PREEMPT_ABORT - An intermediate state before moving back to NONE. Next
- * state: NONE.
- * PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next
- * states: FAULTED, PENDING
- * PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger
- * recovery. Next state: N/A
- * PREEMPT_PENDING: Preemption complete interrupt fired - the callback is
- * checking the success of the operation. Next state: FAULTED, NONE.
- */
-
-enum preempt_state {
- PREEMPT_NONE = 0,
- PREEMPT_START,
- PREEMPT_ABORT,
- PREEMPT_TRIGGERED,
- PREEMPT_FAULTED,
- PREEMPT_PENDING,
-};
-
-/*
* struct a5xx_preempt_record is a shared buffer between the microcode and the
* CPU to store the state for preemption. The record itself is much larger
* (64k) but most of that is used by the CP for storage.
@@ -14,37 +14,6 @@
#include "msm_gem.h"
#include "a5xx_gpu.h"
-/*
- * Try to transition the preemption state from old to new. Return
- * true on success or false if the original state wasn't 'old'
- */
-static inline bool try_preempt_state(struct a5xx_gpu *a5xx_gpu,
- enum preempt_state old, enum preempt_state new)
-{
- enum preempt_state cur = atomic_cmpxchg(&a5xx_gpu->preempt_state,
- old, new);
-
- return (cur == old);
-}
-
-/*
- * Force the preemption state to the specified state. This is used in cases
- * where the current state is known and won't change
- */
-static inline void set_preempt_state(struct a5xx_gpu *gpu,
- enum preempt_state new)
-{
- /*
- * preempt_state may be read by other cores trying to trigger a
- * preemption or in the interrupt handler so barriers are needed
- * before...
- */
- smp_mb__before_atomic();
- atomic_set(&gpu->preempt_state, new);
- /* ... and after*/
- smp_mb__after_atomic();
-}
-
/* Write the most recent wptr for the given ring into the hardware */
static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
@@ -89,7 +58,8 @@ static void a5xx_preempt_timer(unsigned long data)
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
- if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
+ if (!adreno_try_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_TRIGGERED, PREEMPT_FAULTED))
return;
dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
@@ -111,7 +81,8 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
* Try to start preemption by moving from NONE to START. If
* unsuccessful, a preemption is already in flight
*/
- if (!try_preempt_state(a5xx_gpu, PREEMPT_NONE, PREEMPT_START))
+ if (!adreno_try_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_NONE, PREEMPT_START))
return;
/* Get the next ring to preempt to */
@@ -134,9 +105,11 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
* and can safely update the write pointer.
*/
- set_preempt_state(a5xx_gpu, PREEMPT_ABORT);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_ABORT);
update_wptr(gpu, a5xx_gpu->cur_ring);
- set_preempt_state(a5xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_NONE);
return;
}
@@ -156,7 +129,7 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
mod_timer(&a5xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
/* Set the preemption state to triggered */
- set_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state, PREEMPT_TRIGGERED);
/* Make sure everything is written before hitting the button */
wmb();
@@ -173,7 +146,8 @@ void a5xx_preempt_irq(struct msm_gpu *gpu)
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
- if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
+ if (!adreno_try_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_TRIGGERED, PREEMPT_PENDING))
return;
/* Delete the preemption watchdog timer */
@@ -187,7 +161,8 @@ void a5xx_preempt_irq(struct msm_gpu *gpu)
*/
status = gpu_read(gpu, REG_A5XX_CP_CONTEXT_SWITCH_CNTL);
if (unlikely(status)) {
- set_preempt_state(a5xx_gpu, PREEMPT_FAULTED);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state,
+ PREEMPT_FAULTED);
dev_err(dev->dev, "%s: Preemption failed to complete\n",
gpu->name);
queue_work(priv->wq, &gpu->recover_work);
@@ -199,7 +174,7 @@ void a5xx_preempt_irq(struct msm_gpu *gpu)
update_wptr(gpu, a5xx_gpu->cur_ring);
- set_preempt_state(a5xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state, PREEMPT_NONE);
}
void a5xx_preempt_hw_init(struct msm_gpu *gpu)
@@ -219,7 +194,7 @@ void a5xx_preempt_hw_init(struct msm_gpu *gpu)
REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_HI, 0);
/* Reset the preemption state */
- set_preempt_state(a5xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a5xx_gpu->preempt_state, PREEMPT_NONE);
/* Always come up on rb 0 */
a5xx_gpu->cur_ring = gpu->rb[0];
@@ -54,32 +54,6 @@ struct a6xx_gpu {
(a6xx_gpu->scratch_iova + (ring_id * sizeof(uint64_t)))
/*
- * In order to do lockless preemption we use a simple state machine to progress
- * through the process.
- *
- * PREEMPT_NONE - no preemption in progress. Next state START.
- * PREEMPT_START - The trigger is evaluating if preemption is possible. Next
- * states: TRIGGERED, NONE
- * PREEMPT_ABORT - An intermediate state before moving back to NONE. Next
- * state: NONE.
- * PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next
- * states: FAULTED, PENDING
- * PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger
- * recovery. Next state: N/A
- * PREEMPT_PENDING: Preemption complete interrupt fired - the callback is
- * checking the success of the operation. Next state: FAULTED, NONE.
- */
-
-enum a6xx_preempt_state {
- PREEMPT_NONE = 0,
- PREEMPT_START,
- PREEMPT_ABORT,
- PREEMPT_TRIGGERED,
- PREEMPT_FAULTED,
- PREEMPT_PENDING,
-};
-
-/*
* ID values used by SET_PSEUDO_REG PM4 command. These determine which of the
* various internal CP registers to write to. Used in the save/restore
* preemption sequence.
@@ -5,37 +5,6 @@
#include "a6xx_gpu.h"
#include "a6xx_gmu.xml.h"
-/*
- * Try to transition the preemption state from old to new. Return
- * true on success or false if the original state wasn't 'old'
- */
-static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu,
- enum a6xx_preempt_state old, enum a6xx_preempt_state new)
-{
- enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state,
- old, new);
-
- return (cur == old);
-}
-
-/*
- * Force the preemption state to the specified state. This is used in cases
- * where the current state is known and won't change
- */
-static inline void set_preempt_state(struct a6xx_gpu *gpu,
- enum a6xx_preempt_state new)
-{
- /*
- * preempt_state may be read by other cores trying to trigger a
- * preemption or in the interrupt handler so barriers are needed
- * before...
- */
- smp_mb__before_atomic();
- atomic_set(&gpu->preempt_state, new);
- /* ... and after*/
- smp_mb__after_atomic();
-}
-
/* Write the most recent wptr for the given ring into the hardware */
static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
@@ -80,7 +49,8 @@ static void a6xx_preempt_timer(unsigned long data)
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
- if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
+ if (!adreno_try_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_TRIGGERED, PREEMPT_FAULTED))
return;
dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
@@ -95,7 +65,8 @@ void a6xx_preempt_irq(struct msm_gpu *gpu)
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
- if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
+ if (!adreno_try_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_TRIGGERED, PREEMPT_PENDING))
return;
/* Delete the preemption watchdog timer */
@@ -110,7 +81,8 @@ void a6xx_preempt_irq(struct msm_gpu *gpu)
*/
status = gpu_read(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL);
if (unlikely(status & 0x1)) {
- set_preempt_state(a6xx_gpu, PREEMPT_FAULTED);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_FAULTED);
dev_err(dev->dev, "%s: Preemption failed to complete\n",
gpu->name);
queue_work(priv->wq, &gpu->recover_work);
@@ -122,7 +94,7 @@ void a6xx_preempt_irq(struct msm_gpu *gpu)
update_wptr(gpu, a6xx_gpu->cur_ring);
- set_preempt_state(a6xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state, PREEMPT_NONE);
}
void a6xx_preempt_hw_init(struct msm_gpu *gpu)
@@ -151,7 +123,7 @@ void a6xx_preempt_hw_init(struct msm_gpu *gpu)
gpu_write(gpu, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE, 0x1);
/* Reset the preemption state */
- set_preempt_state(a6xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state, PREEMPT_NONE);
/* Always come up on rb 0 */
a6xx_gpu->cur_ring = gpu->rb[0];
@@ -175,7 +147,8 @@ void a6xx_preempt_trigger(struct msm_gpu *gpu)
* Try to start preemption by moving from NONE to START. If
* unsuccessful, a preemption is already in flight
*/
- if (!try_preempt_state(a6xx_gpu, PREEMPT_NONE, PREEMPT_START))
+ if (!adreno_try_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_NONE, PREEMPT_START))
return;
cntl = (((a6xx_gpu->preempt_level << 6) & 0xC0) |
@@ -190,9 +163,11 @@ void a6xx_preempt_trigger(struct msm_gpu *gpu)
* one do nothing except to update the wptr to the latest and greatest
*/
if (!ring || (a6xx_gpu->cur_ring == ring)) {
- set_preempt_state(a6xx_gpu, PREEMPT_ABORT);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_ABORT);
update_wptr(gpu, a6xx_gpu->cur_ring);
- set_preempt_state(a6xx_gpu, PREEMPT_NONE);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state,
+ PREEMPT_NONE);
return;
}
@@ -243,7 +218,7 @@ void a6xx_preempt_trigger(struct msm_gpu *gpu)
mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
/* Set the preemption state to triggered */
- set_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED);
+ adreno_set_preempt_state(&a6xx_gpu->preempt_state, PREEMPT_TRIGGERED);
/* Make sure everything is written before hitting the button */
wmb();
@@ -368,4 +368,58 @@ static inline uint32_t get_wptr(struct msm_ringbuffer *ring)
((1 << 29) \
((ilog2((_len)) & 0x1F) << 24) | (((_reg) << 2) & 0xFFFFF))
+/*
+ * In order to do lockless preemption we use a simple state machine to progress
+ * through the process.
+ *
+ * PREEMPT_NONE - no preemption in progress. Next state START.
+ * PREEMPT_START - The trigger is evaluating if preemption is possible. Next
+ * states: TRIGGERED, NONE
+ * PREEMPT_ABORT - An intermediate state before moving back to NONE. Next
+ * state: NONE.
+ * PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next
+ * states: FAULTED, PENDING
+ * PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger
+ * recovery. Next state: N/A
+ * PREEMPT_PENDING: Preemption complete interrupt fired - the callback is
+ * checking the success of the operation. Next state: FAULTED, NONE.
+ */
+enum adreno_preempt_state {
+ PREEMPT_NONE = 0,
+ PREEMPT_START,
+ PREEMPT_ABORT,
+ PREEMPT_TRIGGERED,
+ PREEMPT_FAULTED,
+ PREEMPT_PENDING,
+};
+
+/*
+ * Try to transition the preemption state from old to new. Return
+ * true on success or false if the original state wasn't 'old'
+ */
+static inline bool adreno_try_preempt_state(atomic_t *preempt_state,
+ enum adreno_preempt_state old, enum adreno_preempt_state new)
+{
+ enum adreno_preempt_state cur = atomic_cmpxchg(preempt_state, old, new);
+
+ return (cur == old);
+}
+
+/*
+ * Force the preemption state to the specified state. This is used in cases
+ * where the current state is known and won't change
+ */
+static inline void adreno_set_preempt_state(atomic_t *preempt_state,
+ enum adreno_preempt_state new)
+{
+ /*
+ * adreno_preempt_state may be read by other cores trying to trigger a
+ * preemption or in the interrupt handler so barriers are needed
+ * before...
+ */
+ smp_mb__before_atomic();
+ atomic_set(preempt_state, new);
+ /* ... and after*/
+ smp_mb__after_atomic();
+}
#endif /* __ADRENO_GPU_H__ */
The preemption state machine related code is same across Adreno targets, so move the common code to a common header file to avoid code duplication. Signed-off-by: Sharat Masetty <smasetty@codeaurora.org> --- drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 26 --------------- drivers/gpu/drm/msm/adreno/a5xx_preempt.c | 55 +++++++++---------------------- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 26 --------------- drivers/gpu/drm/msm/adreno/a6xx_preempt.c | 55 +++++++++---------------------- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 54 ++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 132 deletions(-)