summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/panfrost
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/panfrost')
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c142
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h99
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c91
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c20
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c2
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c695
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.h8
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c203
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.h5
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_regs.h3
10 files changed, 848 insertions, 420 deletions
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 125ed973feaa..bd9b7be63b0f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -54,7 +54,8 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
if (IS_ERR(pfdev->bus_clock)) {
dev_err(pfdev->dev, "get bus_clock failed %ld\n",
PTR_ERR(pfdev->bus_clock));
- return PTR_ERR(pfdev->bus_clock);
+ err = PTR_ERR(pfdev->bus_clock);
+ goto disable_clock;
}
if (pfdev->bus_clock) {
@@ -291,55 +292,100 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_clk_fini(pfdev);
}
-const char *panfrost_exception_name(struct panfrost_device *pfdev, u32 exception_code)
-{
- switch (exception_code) {
- /* Non-Fault Status code */
- case 0x00: return "NOT_STARTED/IDLE/OK";
- case 0x01: return "DONE";
- case 0x02: return "INTERRUPTED";
- case 0x03: return "STOPPED";
- case 0x04: return "TERMINATED";
- case 0x08: return "ACTIVE";
- /* Job exceptions */
- case 0x40: return "JOB_CONFIG_FAULT";
- case 0x41: return "JOB_POWER_FAULT";
- case 0x42: return "JOB_READ_FAULT";
- case 0x43: return "JOB_WRITE_FAULT";
- case 0x44: return "JOB_AFFINITY_FAULT";
- case 0x48: return "JOB_BUS_FAULT";
- case 0x50: return "INSTR_INVALID_PC";
- case 0x51: return "INSTR_INVALID_ENC";
- case 0x52: return "INSTR_TYPE_MISMATCH";
- case 0x53: return "INSTR_OPERAND_FAULT";
- case 0x54: return "INSTR_TLS_FAULT";
- case 0x55: return "INSTR_BARRIER_FAULT";
- case 0x56: return "INSTR_ALIGN_FAULT";
- case 0x58: return "DATA_INVALID_FAULT";
- case 0x59: return "TILE_RANGE_FAULT";
- case 0x5A: return "ADDR_RANGE_FAULT";
- case 0x60: return "OUT_OF_MEMORY";
- /* GPU exceptions */
- case 0x80: return "DELAYED_BUS_FAULT";
- case 0x88: return "SHAREABILITY_FAULT";
- /* MMU exceptions */
- case 0xC1: return "TRANSLATION_FAULT_LEVEL1";
- case 0xC2: return "TRANSLATION_FAULT_LEVEL2";
- case 0xC3: return "TRANSLATION_FAULT_LEVEL3";
- case 0xC4: return "TRANSLATION_FAULT_LEVEL4";
- case 0xC8: return "PERMISSION_FAULT";
- case 0xC9 ... 0xCF: return "PERMISSION_FAULT";
- case 0xD1: return "TRANSTAB_BUS_FAULT_LEVEL1";
- case 0xD2: return "TRANSTAB_BUS_FAULT_LEVEL2";
- case 0xD3: return "TRANSTAB_BUS_FAULT_LEVEL3";
- case 0xD4: return "TRANSTAB_BUS_FAULT_LEVEL4";
- case 0xD8: return "ACCESS_FLAG";
- case 0xD9 ... 0xDF: return "ACCESS_FLAG";
- case 0xE0 ... 0xE7: return "ADDRESS_SIZE_FAULT";
- case 0xE8 ... 0xEF: return "MEMORY_ATTRIBUTES_FAULT";
+#define PANFROST_EXCEPTION(id) \
+ [DRM_PANFROST_EXCEPTION_ ## id] = { \
+ .name = #id, \
}
- return "UNKNOWN";
+struct panfrost_exception_info {
+ const char *name;
+};
+
+static const struct panfrost_exception_info panfrost_exception_infos[] = {
+ PANFROST_EXCEPTION(OK),
+ PANFROST_EXCEPTION(DONE),
+ PANFROST_EXCEPTION(INTERRUPTED),
+ PANFROST_EXCEPTION(STOPPED),
+ PANFROST_EXCEPTION(TERMINATED),
+ PANFROST_EXCEPTION(KABOOM),
+ PANFROST_EXCEPTION(EUREKA),
+ PANFROST_EXCEPTION(ACTIVE),
+ PANFROST_EXCEPTION(JOB_CONFIG_FAULT),
+ PANFROST_EXCEPTION(JOB_POWER_FAULT),
+ PANFROST_EXCEPTION(JOB_READ_FAULT),
+ PANFROST_EXCEPTION(JOB_WRITE_FAULT),
+ PANFROST_EXCEPTION(JOB_AFFINITY_FAULT),
+ PANFROST_EXCEPTION(JOB_BUS_FAULT),
+ PANFROST_EXCEPTION(INSTR_INVALID_PC),
+ PANFROST_EXCEPTION(INSTR_INVALID_ENC),
+ PANFROST_EXCEPTION(INSTR_TYPE_MISMATCH),
+ PANFROST_EXCEPTION(INSTR_OPERAND_FAULT),
+ PANFROST_EXCEPTION(INSTR_TLS_FAULT),
+ PANFROST_EXCEPTION(INSTR_BARRIER_FAULT),
+ PANFROST_EXCEPTION(INSTR_ALIGN_FAULT),
+ PANFROST_EXCEPTION(DATA_INVALID_FAULT),
+ PANFROST_EXCEPTION(TILE_RANGE_FAULT),
+ PANFROST_EXCEPTION(ADDR_RANGE_FAULT),
+ PANFROST_EXCEPTION(IMPRECISE_FAULT),
+ PANFROST_EXCEPTION(OOM),
+ PANFROST_EXCEPTION(OOM_AFBC),
+ PANFROST_EXCEPTION(UNKNOWN),
+ PANFROST_EXCEPTION(DELAYED_BUS_FAULT),
+ PANFROST_EXCEPTION(GPU_SHAREABILITY_FAULT),
+ PANFROST_EXCEPTION(SYS_SHAREABILITY_FAULT),
+ PANFROST_EXCEPTION(GPU_CACHEABILITY_FAULT),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_0),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_1),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_2),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_3),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_4),
+ PANFROST_EXCEPTION(TRANSLATION_FAULT_IDENTITY),
+ PANFROST_EXCEPTION(PERM_FAULT_0),
+ PANFROST_EXCEPTION(PERM_FAULT_1),
+ PANFROST_EXCEPTION(PERM_FAULT_2),
+ PANFROST_EXCEPTION(PERM_FAULT_3),
+ PANFROST_EXCEPTION(TRANSTAB_BUS_FAULT_0),
+ PANFROST_EXCEPTION(TRANSTAB_BUS_FAULT_1),
+ PANFROST_EXCEPTION(TRANSTAB_BUS_FAULT_2),
+ PANFROST_EXCEPTION(TRANSTAB_BUS_FAULT_3),
+ PANFROST_EXCEPTION(ACCESS_FLAG_0),
+ PANFROST_EXCEPTION(ACCESS_FLAG_1),
+ PANFROST_EXCEPTION(ACCESS_FLAG_2),
+ PANFROST_EXCEPTION(ACCESS_FLAG_3),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_IN0),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_IN1),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_IN2),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_IN3),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_OUT0),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_OUT1),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_OUT2),
+ PANFROST_EXCEPTION(ADDR_SIZE_FAULT_OUT3),
+ PANFROST_EXCEPTION(MEM_ATTR_FAULT_0),
+ PANFROST_EXCEPTION(MEM_ATTR_FAULT_1),
+ PANFROST_EXCEPTION(MEM_ATTR_FAULT_2),
+ PANFROST_EXCEPTION(MEM_ATTR_FAULT_3),
+ PANFROST_EXCEPTION(MEM_ATTR_NONCACHE_0),
+ PANFROST_EXCEPTION(MEM_ATTR_NONCACHE_1),
+ PANFROST_EXCEPTION(MEM_ATTR_NONCACHE_2),
+ PANFROST_EXCEPTION(MEM_ATTR_NONCACHE_3),
+};
+
+const char *panfrost_exception_name(u32 exception_code)
+{
+ if (WARN_ON(exception_code >= ARRAY_SIZE(panfrost_exception_infos) ||
+ !panfrost_exception_infos[exception_code].name))
+ return "Unknown exception type";
+
+ return panfrost_exception_infos[exception_code].name;
+}
+
+bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
+ u32 exception_code)
+{
+ /* Right now, none of the GPU we support need a reset, but this
+ * might change.
+ */
+ return false;
}
void panfrost_device_reset(struct panfrost_device *pfdev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index f614e98771e4..8b25278f34c8 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -97,11 +97,12 @@ struct panfrost_device {
spinlock_t as_lock;
unsigned long as_in_use_mask;
unsigned long as_alloc_mask;
+ unsigned long as_faulty_mask;
struct list_head as_lru_list;
struct panfrost_job_slot *js;
- struct panfrost_job *jobs[NUM_JOB_SLOTS];
+ struct panfrost_job *jobs[NUM_JOB_SLOTS][2];
struct list_head scheduled_jobs;
struct panfrost_perfcnt *perfcnt;
@@ -109,6 +110,7 @@ struct panfrost_device {
struct mutex sched_lock;
struct {
+ struct workqueue_struct *wq;
struct work_struct work;
atomic_t pending;
} reset;
@@ -121,8 +123,12 @@ struct panfrost_device {
};
struct panfrost_mmu {
+ struct panfrost_device *pfdev;
+ struct kref refcount;
struct io_pgtable_cfg pgtbl_cfg;
struct io_pgtable_ops *pgtbl_ops;
+ struct drm_mm mm;
+ spinlock_t mm_lock;
int as;
atomic_t as_count;
struct list_head list;
@@ -133,9 +139,7 @@ struct panfrost_file_priv {
struct drm_sched_entity sched_entity[NUM_JOB_SLOTS];
- struct panfrost_mmu mmu;
- struct drm_mm mm;
- spinlock_t mm_lock;
+ struct panfrost_mmu *mmu;
};
static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev)
@@ -171,6 +175,91 @@ void panfrost_device_reset(struct panfrost_device *pfdev);
int panfrost_device_resume(struct device *dev);
int panfrost_device_suspend(struct device *dev);
-const char *panfrost_exception_name(struct panfrost_device *pfdev, u32 exception_code);
+enum drm_panfrost_exception_type {
+ DRM_PANFROST_EXCEPTION_OK = 0x00,
+ DRM_PANFROST_EXCEPTION_DONE = 0x01,
+ DRM_PANFROST_EXCEPTION_INTERRUPTED = 0x02,
+ DRM_PANFROST_EXCEPTION_STOPPED = 0x03,
+ DRM_PANFROST_EXCEPTION_TERMINATED = 0x04,
+ DRM_PANFROST_EXCEPTION_KABOOM = 0x05,
+ DRM_PANFROST_EXCEPTION_EUREKA = 0x06,
+ DRM_PANFROST_EXCEPTION_ACTIVE = 0x08,
+ DRM_PANFROST_EXCEPTION_MAX_NON_FAULT = 0x3f,
+ DRM_PANFROST_EXCEPTION_JOB_CONFIG_FAULT = 0x40,
+ DRM_PANFROST_EXCEPTION_JOB_POWER_FAULT = 0x41,
+ DRM_PANFROST_EXCEPTION_JOB_READ_FAULT = 0x42,
+ DRM_PANFROST_EXCEPTION_JOB_WRITE_FAULT = 0x43,
+ DRM_PANFROST_EXCEPTION_JOB_AFFINITY_FAULT = 0x44,
+ DRM_PANFROST_EXCEPTION_JOB_BUS_FAULT = 0x48,
+ DRM_PANFROST_EXCEPTION_INSTR_INVALID_PC = 0x50,
+ DRM_PANFROST_EXCEPTION_INSTR_INVALID_ENC = 0x51,
+ DRM_PANFROST_EXCEPTION_INSTR_TYPE_MISMATCH = 0x52,
+ DRM_PANFROST_EXCEPTION_INSTR_OPERAND_FAULT = 0x53,
+ DRM_PANFROST_EXCEPTION_INSTR_TLS_FAULT = 0x54,
+ DRM_PANFROST_EXCEPTION_INSTR_BARRIER_FAULT = 0x55,
+ DRM_PANFROST_EXCEPTION_INSTR_ALIGN_FAULT = 0x56,
+ DRM_PANFROST_EXCEPTION_DATA_INVALID_FAULT = 0x58,
+ DRM_PANFROST_EXCEPTION_TILE_RANGE_FAULT = 0x59,
+ DRM_PANFROST_EXCEPTION_ADDR_RANGE_FAULT = 0x5a,
+ DRM_PANFROST_EXCEPTION_IMPRECISE_FAULT = 0x5b,
+ DRM_PANFROST_EXCEPTION_OOM = 0x60,
+ DRM_PANFROST_EXCEPTION_OOM_AFBC = 0x61,
+ DRM_PANFROST_EXCEPTION_UNKNOWN = 0x7f,
+ DRM_PANFROST_EXCEPTION_DELAYED_BUS_FAULT = 0x80,
+ DRM_PANFROST_EXCEPTION_GPU_SHAREABILITY_FAULT = 0x88,
+ DRM_PANFROST_EXCEPTION_SYS_SHAREABILITY_FAULT = 0x89,
+ DRM_PANFROST_EXCEPTION_GPU_CACHEABILITY_FAULT = 0x8a,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_0 = 0xc0,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_1 = 0xc1,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_2 = 0xc2,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_3 = 0xc3,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_4 = 0xc4,
+ DRM_PANFROST_EXCEPTION_TRANSLATION_FAULT_IDENTITY = 0xc7,
+ DRM_PANFROST_EXCEPTION_PERM_FAULT_0 = 0xc8,
+ DRM_PANFROST_EXCEPTION_PERM_FAULT_1 = 0xc9,
+ DRM_PANFROST_EXCEPTION_PERM_FAULT_2 = 0xca,
+ DRM_PANFROST_EXCEPTION_PERM_FAULT_3 = 0xcb,
+ DRM_PANFROST_EXCEPTION_TRANSTAB_BUS_FAULT_0 = 0xd0,
+ DRM_PANFROST_EXCEPTION_TRANSTAB_BUS_FAULT_1 = 0xd1,
+ DRM_PANFROST_EXCEPTION_TRANSTAB_BUS_FAULT_2 = 0xd2,
+ DRM_PANFROST_EXCEPTION_TRANSTAB_BUS_FAULT_3 = 0xd3,
+ DRM_PANFROST_EXCEPTION_ACCESS_FLAG_0 = 0xd8,
+ DRM_PANFROST_EXCEPTION_ACCESS_FLAG_1 = 0xd9,
+ DRM_PANFROST_EXCEPTION_ACCESS_FLAG_2 = 0xda,
+ DRM_PANFROST_EXCEPTION_ACCESS_FLAG_3 = 0xdb,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_IN0 = 0xe0,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_IN1 = 0xe1,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_IN2 = 0xe2,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_IN3 = 0xe3,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_OUT0 = 0xe4,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_OUT1 = 0xe5,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_OUT2 = 0xe6,
+ DRM_PANFROST_EXCEPTION_ADDR_SIZE_FAULT_OUT3 = 0xe7,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_FAULT_0 = 0xe8,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_FAULT_1 = 0xe9,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_FAULT_2 = 0xea,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_FAULT_3 = 0xeb,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_NONCACHE_0 = 0xec,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_NONCACHE_1 = 0xed,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_NONCACHE_2 = 0xee,
+ DRM_PANFROST_EXCEPTION_MEM_ATTR_NONCACHE_3 = 0xef,
+};
+
+static inline bool
+panfrost_exception_is_fault(u32 exception_code)
+{
+ return exception_code > DRM_PANFROST_EXCEPTION_MAX_NON_FAULT;
+}
+
+const char *panfrost_exception_name(u32 exception_code);
+bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
+ u32 exception_code);
+
+static inline void
+panfrost_device_schedule_reset(struct panfrost_device *pfdev)
+{
+ atomic_set(&pfdev->reset.pending, 1);
+ queue_work(pfdev->reset.wq, &pfdev->reset.work);
+}
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 075ec0ef746c..1ffaef5ec5ff 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -138,12 +138,6 @@ panfrost_lookup_bos(struct drm_device *dev,
if (!job->bo_count)
return 0;
- job->implicit_fences = kvmalloc_array(job->bo_count,
- sizeof(struct dma_fence *),
- GFP_KERNEL | __GFP_ZERO);
- if (!job->implicit_fences)
- return -ENOMEM;
-
ret = drm_gem_objects_lookup(file_priv,
(void __user *)(uintptr_t)args->bo_handles,
job->bo_count, &job->bos);
@@ -174,7 +168,7 @@ panfrost_lookup_bos(struct drm_device *dev,
}
/**
- * panfrost_copy_in_sync() - Sets up job->in_fences[] with the sync objects
+ * panfrost_copy_in_sync() - Sets up job->deps with the sync objects
* referenced by the job.
* @dev: DRM device
* @file_priv: DRM file for this fd
@@ -194,22 +188,14 @@ panfrost_copy_in_sync(struct drm_device *dev,
{
u32 *handles;
int ret = 0;
- int i;
+ int i, in_fence_count;
- job->in_fence_count = args->in_sync_count;
+ in_fence_count = args->in_sync_count;
- if (!job->in_fence_count)
+ if (!in_fence_count)
return 0;
- job->in_fences = kvmalloc_array(job->in_fence_count,
- sizeof(struct dma_fence *),
- GFP_KERNEL | __GFP_ZERO);
- if (!job->in_fences) {
- DRM_DEBUG("Failed to allocate job in fences\n");
- return -ENOMEM;
- }
-
- handles = kvmalloc_array(job->in_fence_count, sizeof(u32), GFP_KERNEL);
+ handles = kvmalloc_array(in_fence_count, sizeof(u32), GFP_KERNEL);
if (!handles) {
ret = -ENOMEM;
DRM_DEBUG("Failed to allocate incoming syncobj handles\n");
@@ -218,16 +204,23 @@ panfrost_copy_in_sync(struct drm_device *dev,
if (copy_from_user(handles,
(void __user *)(uintptr_t)args->in_syncs,
- job->in_fence_count * sizeof(u32))) {
+ in_fence_count * sizeof(u32))) {
ret = -EFAULT;
DRM_DEBUG("Failed to copy in syncobj handles\n");
goto fail;
}
- for (i = 0; i < job->in_fence_count; i++) {
+ for (i = 0; i < in_fence_count; i++) {
+ struct dma_fence *fence;
+
ret = drm_syncobj_find_fence(file_priv, handles[i], 0, 0,
- &job->in_fences[i]);
- if (ret == -EINVAL)
+ &fence);
+ if (ret)
+ goto fail;
+
+ ret = drm_gem_fence_array_add(&job->deps, fence);
+
+ if (ret)
goto fail;
}
@@ -265,6 +258,8 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
kref_init(&job->refcount);
+ xa_init_flags(&job->deps, XA_FLAGS_ALLOC);
+
job->pfdev = pfdev;
job->jc = args->jc;
job->requirements = args->requirements;
@@ -417,7 +412,7 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
* anyway, so let's not bother.
*/
if (!list_is_singular(&bo->mappings.list) ||
- WARN_ON_ONCE(first->mmu != &priv->mmu)) {
+ WARN_ON_ONCE(first->mmu != priv->mmu)) {
ret = -EINVAL;
goto out_unlock_mappings;
}
@@ -449,32 +444,6 @@ int panfrost_unstable_ioctl_check(void)
return 0;
}
-#define PFN_4G (SZ_4G >> PAGE_SHIFT)
-#define PFN_4G_MASK (PFN_4G - 1)
-#define PFN_16M (SZ_16M >> PAGE_SHIFT)
-
-static void panfrost_drm_mm_color_adjust(const struct drm_mm_node *node,
- unsigned long color,
- u64 *start, u64 *end)
-{
- /* Executable buffers can't start or end on a 4GB boundary */
- if (!(color & PANFROST_BO_NOEXEC)) {
- u64 next_seg;
-
- if ((*start & PFN_4G_MASK) == 0)
- (*start)++;
-
- if ((*end & PFN_4G_MASK) == 0)
- (*end)--;
-
- next_seg = ALIGN(*start, PFN_4G);
- if (next_seg - *start <= PFN_16M)
- *start = next_seg + 1;
-
- *end = min(*end, ALIGN(*start, PFN_4G) - 1);
- }
-}
-
static int
panfrost_open(struct drm_device *dev, struct drm_file *file)
{
@@ -489,15 +458,11 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
panfrost_priv->pfdev = pfdev;
file->driver_priv = panfrost_priv;
- spin_lock_init(&panfrost_priv->mm_lock);
-
- /* 4G enough for now. can be 48-bit */
- drm_mm_init(&panfrost_priv->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT);
- panfrost_priv->mm.color_adjust = panfrost_drm_mm_color_adjust;
-
- ret = panfrost_mmu_pgtable_alloc(panfrost_priv);
- if (ret)
- goto err_pgtable;
+ panfrost_priv->mmu = panfrost_mmu_ctx_create(pfdev);
+ if (IS_ERR(panfrost_priv->mmu)) {
+ ret = PTR_ERR(panfrost_priv->mmu);
+ goto err_free;
+ }
ret = panfrost_job_open(panfrost_priv);
if (ret)
@@ -506,9 +471,8 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
return 0;
err_job:
- panfrost_mmu_pgtable_free(panfrost_priv);
-err_pgtable:
- drm_mm_takedown(&panfrost_priv->mm);
+ panfrost_mmu_ctx_put(panfrost_priv->mmu);
+err_free:
kfree(panfrost_priv);
return ret;
}
@@ -521,8 +485,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
panfrost_perfcnt_close(file);
panfrost_job_close(panfrost_priv);
- panfrost_mmu_pgtable_free(panfrost_priv);
- drm_mm_takedown(&panfrost_priv->mm);
+ panfrost_mmu_ctx_put(panfrost_priv->mmu);
kfree(panfrost_priv);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3e0723bc36bd..23377481f4e3 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -60,7 +60,7 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
mutex_lock(&bo->mappings.lock);
list_for_each_entry(iter, &bo->mappings.list, node) {
- if (iter->mmu == &priv->mmu) {
+ if (iter->mmu == priv->mmu) {
kref_get(&iter->refcount);
mapping = iter;
break;
@@ -74,16 +74,13 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
static void
panfrost_gem_teardown_mapping(struct panfrost_gem_mapping *mapping)
{
- struct panfrost_file_priv *priv;
-
if (mapping->active)
panfrost_mmu_unmap(mapping);
- priv = container_of(mapping->mmu, struct panfrost_file_priv, mmu);
- spin_lock(&priv->mm_lock);
+ spin_lock(&mapping->mmu->mm_lock);
if (drm_mm_node_allocated(&mapping->mmnode))
drm_mm_remove_node(&mapping->mmnode);
- spin_unlock(&priv->mm_lock);
+ spin_unlock(&mapping->mmu->mm_lock);
}
static void panfrost_gem_mapping_release(struct kref *kref)
@@ -94,6 +91,7 @@ static void panfrost_gem_mapping_release(struct kref *kref)
panfrost_gem_teardown_mapping(mapping);
drm_gem_object_put(&mapping->obj->base.base);
+ panfrost_mmu_ctx_put(mapping->mmu);
kfree(mapping);
}
@@ -143,11 +141,11 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
else
align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
- mapping->mmu = &priv->mmu;
- spin_lock(&priv->mm_lock);
- ret = drm_mm_insert_node_generic(&priv->mm, &mapping->mmnode,
+ mapping->mmu = panfrost_mmu_ctx_get(priv->mmu);
+ spin_lock(&mapping->mmu->mm_lock);
+ ret = drm_mm_insert_node_generic(&mapping->mmu->mm, &mapping->mmnode,
size >> PAGE_SHIFT, align, color, 0);
- spin_unlock(&priv->mm_lock);
+ spin_unlock(&mapping->mmu->mm_lock);
if (ret)
goto err;
@@ -176,7 +174,7 @@ void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv)
mutex_lock(&bo->mappings.lock);
list_for_each_entry(iter, &bo->mappings.list, node) {
- if (iter->mmu == &priv->mmu) {
+ if (iter->mmu == priv->mmu) {
mapping = iter;
list_del(&iter->node);
break;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 0e70e27fd8c3..bbe628b306ee 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -33,7 +33,7 @@ static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data)
address |= gpu_read(pfdev, GPU_FAULT_ADDRESS_LO);
dev_warn(pfdev->dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n",
- fault_status & 0xFF, panfrost_exception_name(pfdev, fault_status),
+ fault_status, panfrost_exception_name(fault_status & 0xFF),
address);
if (state & GPU_IRQ_MULTIPLE_FAULT)
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 2df3e999a38d..71a72fb50e6b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -4,6 +4,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
@@ -25,17 +26,8 @@
#define job_write(dev, reg, data) writel(data, dev->iomem + (reg))
#define job_read(dev, reg) readl(dev->iomem + (reg))
-enum panfrost_queue_status {
- PANFROST_QUEUE_STATUS_ACTIVE,
- PANFROST_QUEUE_STATUS_STOPPED,
- PANFROST_QUEUE_STATUS_STARTING,
- PANFROST_QUEUE_STATUS_FAULT_PENDING,
-};
-
struct panfrost_queue_state {
struct drm_gpu_scheduler sched;
- atomic_t status;
- struct mutex lock;
u64 fence_context;
u64 emit_seqno;
};
@@ -43,6 +35,7 @@ struct panfrost_queue_state {
struct panfrost_job_slot {
struct panfrost_queue_state queue[NUM_JOB_SLOTS];
spinlock_t job_lock;
+ int irq;
};
static struct panfrost_job *
@@ -148,9 +141,52 @@ static void panfrost_job_write_affinity(struct panfrost_device *pfdev,
job_write(pfdev, JS_AFFINITY_NEXT_HI(js), affinity >> 32);
}
+static u32
+panfrost_get_job_chain_flag(const struct panfrost_job *job)
+{
+ struct panfrost_fence *f = to_panfrost_fence(job->done_fence);
+
+ if (!panfrost_has_hw_feature(job->pfdev, HW_FEATURE_JOBCHAIN_DISAMBIGUATION))
+ return 0;
+
+ return (f->seqno & 1) ? JS_CONFIG_JOB_CHAIN_FLAG : 0;
+}
+
+static struct panfrost_job *
+panfrost_dequeue_job(struct panfrost_device *pfdev, int slot)
+{
+ struct panfrost_job *job = pfdev->jobs[slot][0];
+
+ WARN_ON(!job);
+ pfdev->jobs[slot][0] = pfdev->jobs[slot][1];
+ pfdev->jobs[slot][1] = NULL;
+
+ return job;
+}
+
+static unsigned int
+panfrost_enqueue_job(struct panfrost_device *pfdev, int slot,
+ struct panfrost_job *job)
+{
+ if (WARN_ON(!job))
+ return 0;
+
+ if (!pfdev->jobs[slot][0]) {
+ pfdev->jobs[slot][0] = job;
+ return 0;
+ }
+
+ WARN_ON(pfdev->jobs[slot][1]);
+ pfdev->jobs[slot][1] = job;
+ WARN_ON(panfrost_get_job_chain_flag(job) ==
+ panfrost_get_job_chain_flag(pfdev->jobs[slot][0]));
+ return 1;
+}
+
static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
{
struct panfrost_device *pfdev = job->pfdev;
+ unsigned int subslot;
u32 cfg;
u64 jc_head = job->jc;
int ret;
@@ -165,7 +201,7 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
return;
}
- cfg = panfrost_mmu_as_get(pfdev, &job->file_priv->mmu);
+ cfg = panfrost_mmu_as_get(pfdev, job->file_priv->mmu);
job_write(pfdev, JS_HEAD_NEXT_LO(js), jc_head & 0xFFFFFFFF);
job_write(pfdev, JS_HEAD_NEXT_HI(js), jc_head >> 32);
@@ -176,7 +212,8 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
* start */
cfg |= JS_CONFIG_THREAD_PRI(8) |
JS_CONFIG_START_FLUSH_CLEAN_INVALIDATE |
- JS_CONFIG_END_FLUSH_CLEAN_INVALIDATE;
+ JS_CONFIG_END_FLUSH_CLEAN_INVALIDATE |
+ panfrost_get_job_chain_flag(job);
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION))
cfg |= JS_CONFIG_ENABLE_FLUSH_REDUCTION;
@@ -190,20 +227,33 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
job_write(pfdev, JS_FLUSH_ID_NEXT(js), job->flush_id);
/* GO ! */
- dev_dbg(pfdev->dev, "JS: Submitting atom %p to js[%d] with head=0x%llx",
- job, js, jc_head);
- job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START);
+ spin_lock(&pfdev->js->job_lock);
+ subslot = panfrost_enqueue_job(pfdev, js, job);
+ /* Don't queue the job if a reset is in progress */
+ if (!atomic_read(&pfdev->reset.pending)) {
+ job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START);
+ dev_dbg(pfdev->dev,
+ "JS: Submitting atom %p to js[%d][%d] with head=0x%llx AS %d",
+ job, js, subslot, jc_head, cfg & 0xf);
+ }
+ spin_unlock(&pfdev->js->job_lock);
}
-static void panfrost_acquire_object_fences(struct drm_gem_object **bos,
- int bo_count,
- struct dma_fence **implicit_fences)
+static int panfrost_acquire_object_fences(struct drm_gem_object **bos,
+ int bo_count,
+ struct xarray *deps)
{
- int i;
+ int i, ret;
- for (i = 0; i < bo_count; i++)
- implicit_fences[i] = dma_resv_get_excl_unlocked(bos[i]->resv);
+ for (i = 0; i < bo_count; i++) {
+ /* panfrost always uses write mode in its current uapi */
+ ret = drm_gem_fence_array_add_implicit(deps, bos[i], true);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static void panfrost_attach_object_fences(struct drm_gem_object **bos,
@@ -224,14 +274,13 @@ int panfrost_job_push(struct panfrost_job *job)
struct ww_acquire_ctx acquire_ctx;
int ret = 0;
- mutex_lock(&pfdev->sched_lock);
ret = drm_gem_lock_reservations(job->bos, job->bo_count,
&acquire_ctx);
- if (ret) {
- mutex_unlock(&pfdev->sched_lock);
+ if (ret)
return ret;
- }
+
+ mutex_lock(&pfdev->sched_lock);
ret = drm_sched_job_init(&job->base, entity, NULL);
if (ret) {
@@ -241,10 +290,14 @@ int panfrost_job_push(struct panfrost_job *job)
job->render_done_fence = dma_fence_get(&job->base.s_fence->finished);
- kref_get(&job->refcount); /* put by scheduler job completion */
+ ret = panfrost_acquire_object_fences(job->bos, job->bo_count,
+ &job->deps);
+ if (ret) {
+ mutex_unlock(&pfdev->sched_lock);
+ goto unlock;
+ }
- panfrost_acquire_object_fences(job->bos, job->bo_count,
- job->implicit_fences);
+ kref_get(&job->refcount); /* put by scheduler job completion */
drm_sched_entity_push_job(&job->base, entity);
@@ -263,18 +316,15 @@ static void panfrost_job_cleanup(struct kref *ref)
{
struct panfrost_job *job = container_of(ref, struct panfrost_job,
refcount);
+ struct dma_fence *fence;
+ unsigned long index;
unsigned int i;
- if (job->in_fences) {
- for (i = 0; i < job->in_fence_count; i++)
- dma_fence_put(job->in_fences[i]);
- kvfree(job->in_fences);
- }
- if (job->implicit_fences) {
- for (i = 0; i < job->bo_count; i++)
- dma_fence_put(job->implicit_fences[i]);
- kvfree(job->implicit_fences);
+ xa_for_each(&job->deps, index, fence) {
+ dma_fence_put(fence);
}
+ xa_destroy(&job->deps);
+
dma_fence_put(job->done_fence);
dma_fence_put(job->render_done_fence);
@@ -317,26 +367,9 @@ static struct dma_fence *panfrost_job_dependency(struct drm_sched_job *sched_job
struct drm_sched_entity *s_entity)
{
struct panfrost_job *job = to_panfrost_job(sched_job);
- struct dma_fence *fence;
- unsigned int i;
- /* Explicit fences */
- for (i = 0; i < job->in_fence_count; i++) {
- if (job->in_fences[i]) {
- fence = job->in_fences[i];
- job->in_fences[i] = NULL;
- return fence;
- }
- }
-
- /* Implicit fences, max. one per BO */
- for (i = 0; i < job->bo_count; i++) {
- if (job->implicit_fences[i]) {
- fence = job->implicit_fences[i];
- job->implicit_fences[i] = NULL;
- return fence;
- }
- }
+ if (!xa_empty(&job->deps))
+ return xa_erase(&job->deps, job->last_dep++);
return NULL;
}
@@ -351,11 +384,15 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
if (unlikely(job->base.s_fence->finished.error))
return NULL;
- pfdev->jobs[slot] = job;
+ /* Nothing to execute: can happen if the job has finished while
+ * we were resetting the GPU.
+ */
+ if (!job->jc)
+ return NULL;
fence = panfrost_fence_create(pfdev, slot);
if (IS_ERR(fence))
- return NULL;
+ return fence;
if (job->done_fence)
dma_fence_put(job->done_fence);
@@ -379,57 +416,314 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
job_write(pfdev, JOB_INT_MASK, irq_mask);
}
-static bool panfrost_scheduler_stop(struct panfrost_queue_state *queue,
- struct drm_sched_job *bad)
+static void panfrost_job_handle_err(struct panfrost_device *pfdev,
+ struct panfrost_job *job,
+ unsigned int js)
{
- enum panfrost_queue_status old_status;
- bool stopped = false;
+ u32 js_status = job_read(pfdev, JS_STATUS(js));
+ const char *exception_name = panfrost_exception_name(js_status);
+ bool signal_fence = true;
+
+ if (!panfrost_exception_is_fault(js_status)) {
+ dev_dbg(pfdev->dev, "js event, js=%d, status=%s, head=0x%x, tail=0x%x",
+ js, exception_name,
+ job_read(pfdev, JS_HEAD_LO(js)),
+ job_read(pfdev, JS_TAIL_LO(js)));
+ } else {
+ dev_err(pfdev->dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
+ js, exception_name,
+ job_read(pfdev, JS_HEAD_LO(js)),
+ job_read(pfdev, JS_TAIL_LO(js)));
+ }
- mutex_lock(&queue->lock);
- old_status = atomic_xchg(&queue->status,
- PANFROST_QUEUE_STATUS_STOPPED);
- if (old_status == PANFROST_QUEUE_STATUS_STOPPED)
- goto out;
+ if (js_status == DRM_PANFROST_EXCEPTION_STOPPED) {
+ /* Update the job head so we can resume */
+ job->jc = job_read(pfdev, JS_TAIL_LO(js)) |
+ ((u64)job_read(pfdev, JS_TAIL_HI(js)) << 32);
+
+ /* The job will be resumed, don't signal the fence */
+ signal_fence = false;
+ } else if (js_status == DRM_PANFROST_EXCEPTION_TERMINATED) {
+ /* Job has been hard-stopped, flag it as canceled */
+ dma_fence_set_error(job->done_fence, -ECANCELED);
+ job->jc = 0;
+ } else if (panfrost_exception_is_fault(js_status)) {
+ /* We might want to provide finer-grained error code based on
+ * the exception type, but unconditionally setting to EINVAL
+ * is good enough for now.
+ */
+ dma_fence_set_error(job->done_fence, -EINVAL);
+ job->jc = 0;
+ }
- WARN_ON(old_status != PANFROST_QUEUE_STATUS_ACTIVE);
- drm_sched_stop(&queue->sched, bad);
- if (bad)
- drm_sched_increase_karma(bad);
+ panfrost_mmu_as_put(pfdev, job->file_priv->mmu);
+ panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
- stopped = true;
+ if (signal_fence)
+ dma_fence_signal_locked(job->done_fence);
- /*
- * Set the timeout to max so the timer doesn't get started
- * when we return from the timeout handler (restored in
- * panfrost_scheduler_start()).
+ pm_runtime_put_autosuspend(pfdev->dev);
+
+ if (panfrost_exception_needs_reset(pfdev, js_status)) {
+ atomic_set(&pfdev->reset.pending, 1);
+ drm_sched_fault(&pfdev->js->queue[js].sched);
+ }
+}
+
+static void panfrost_job_handle_done(struct panfrost_device *pfdev,
+ struct panfrost_job *job)
+{
+ /* Set ->jc to 0 to avoid re-submitting an already finished job (can
+ * happen when we receive the DONE interrupt while doing a GPU reset).
+ */
+ job->jc = 0;
+ panfrost_mmu_as_put(pfdev, job->file_priv->mmu);
+ panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
+
+ dma_fence_signal_locked(job->done_fence);
+ pm_runtime_put_autosuspend(pfdev->dev);
+}
+
+static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
+{
+ struct panfrost_job *done[NUM_JOB_SLOTS][2] = {};
+ struct panfrost_job *failed[NUM_JOB_SLOTS] = {};
+ u32 js_state = 0, js_events = 0;
+ unsigned int i, j;
+
+ /* First we collect all failed/done jobs. */
+ while (status) {
+ u32 js_state_mask = 0;
+
+ for (j = 0; j < NUM_JOB_SLOTS; j++) {
+ if (status & MK_JS_MASK(j))
+ js_state_mask |= MK_JS_MASK(j);
+
+ if (status & JOB_INT_MASK_DONE(j)) {
+ if (done[j][0])
+ done[j][1] = panfrost_dequeue_job(pfdev, j);
+ else
+ done[j][0] = panfrost_dequeue_job(pfdev, j);
+ }
+
+ if (status & JOB_INT_MASK_ERR(j)) {
+ /* Cancel the next submission. Will be submitted
+ * after we're done handling this failure if
+ * there's no reset pending.
+ */
+ job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_NOP);
+ failed[j] = panfrost_dequeue_job(pfdev, j);
+ }
+ }
+
+ /* JS_STATE is sampled when JOB_INT_CLEAR is written.
+ * For each BIT(slot) or BIT(slot + 16) bit written to
+ * JOB_INT_CLEAR, the corresponding bits in JS_STATE
+ * (BIT(slot) and BIT(slot + 16)) are updated, but this
+ * is racy. If we only have one job done at the time we
+ * read JOB_INT_RAWSTAT but the second job fails before we
+ * clear the status, we end up with a status containing
+ * only the DONE bit and consider both jobs as DONE since
+ * JS_STATE reports both NEXT and CURRENT as inactive.
+ * To prevent that, let's repeat this clear+read steps
+ * until status is 0.
+ */
+ job_write(pfdev, JOB_INT_CLEAR, status);
+ js_state &= ~js_state_mask;
+ js_state |= job_read(pfdev, JOB_INT_JS_STATE) & js_state_mask;
+ js_events |= status;
+ status = job_read(pfdev, JOB_INT_RAWSTAT);
+ }
+
+ /* Then we handle the dequeued jobs. */
+ for (j = 0; j < NUM_JOB_SLOTS; j++) {
+ if (!(js_events & MK_JS_MASK(j)))
+ continue;
+
+ if (failed[j]) {
+ panfrost_job_handle_err(pfdev, failed[j], j);
+ } else if (pfdev->jobs[j][0] && !(js_state & MK_JS_MASK(j))) {
+ /* When the current job doesn't fail, the JM dequeues
+ * the next job without waiting for an ACK, this means
+ * we can have 2 jobs dequeued and only catch the
+ * interrupt when the second one is done. If both slots
+ * are inactive, but one job remains in pfdev->jobs[j],
+ * consider it done. Of course that doesn't apply if a
+ * failure happened since we cancelled execution of the
+ * job in _NEXT (see above).
+ */
+ if (WARN_ON(!done[j][0]))
+ done[j][0] = panfrost_dequeue_job(pfdev, j);
+ else
+ done[j][1] = panfrost_dequeue_job(pfdev, j);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(done[0]) && done[j][i]; i++)
+ panfrost_job_handle_done(pfdev, done[j][i]);
+ }
+
+ /* And finally we requeue jobs that were waiting in the second slot
+ * and have been stopped if we detected a failure on the first slot.
*/
- queue->sched.timeout = MAX_SCHEDULE_TIMEOUT;
+ for (j = 0; j < NUM_JOB_SLOTS; j++) {
+ if (!(js_events & MK_JS_MASK(j)))
+ continue;
+
+ if (!failed[j] || !pfdev->jobs[j][0])
+ continue;
+
+ if (pfdev->jobs[j][0]->jc == 0) {
+ /* The job was cancelled, signal the fence now */
+ struct panfrost_job *canceled = panfrost_dequeue_job(pfdev, j);
+
+ dma_fence_set_error(canceled->done_fence, -ECANCELED);
+ panfrost_job_handle_done(pfdev, canceled);
+ } else if (!atomic_read(&pfdev->reset.pending)) {
+ /* Requeue the job we removed if no reset is pending */
+ job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_START);
+ }
+ }
+}
-out:
- mutex_unlock(&queue->lock);
+static void panfrost_job_handle_irqs(struct panfrost_device *pfdev)
+{
+ u32 status = job_read(pfdev, JOB_INT_RAWSTAT);
+
+ while (status) {
+ pm_runtime_mark_last_busy(pfdev->dev);
- return stopped;
+ spin_lock(&pfdev->js->job_lock);
+ panfrost_job_handle_irq(pfdev, status);
+ spin_unlock(&pfdev->js->job_lock);
+ status = job_read(pfdev, JOB_INT_RAWSTAT);
+ }
}
-static void panfrost_scheduler_start(struct panfrost_queue_state *queue)
+static u32 panfrost_active_slots(struct panfrost_device *pfdev,
+ u32 *js_state_mask, u32 js_state)
{
- enum panfrost_queue_status old_status;
+ u32 rawstat;
- mutex_lock(&queue->lock);
- old_status = atomic_xchg(&queue->status,
- PANFROST_QUEUE_STATUS_STARTING);
- WARN_ON(old_status != PANFROST_QUEUE_STATUS_STOPPED);
+ if (!(js_state & *js_state_mask))
+ return 0;
- /* Restore the original timeout before starting the scheduler. */
- queue->sched.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS);
- drm_sched_resubmit_jobs(&queue->sched);
- drm_sched_start(&queue->sched, true);
- old_status = atomic_xchg(&queue->status,
- PANFROST_QUEUE_STATUS_ACTIVE);
- if (old_status == PANFROST_QUEUE_STATUS_FAULT_PENDING)
- drm_sched_fault(&queue->sched);
+ rawstat = job_read(pfdev, JOB_INT_RAWSTAT);
+ if (rawstat) {
+ unsigned int i;
+
+ for (i = 0; i < NUM_JOB_SLOTS; i++) {
+ if (rawstat & MK_JS_MASK(i))
+ *js_state_mask &= ~MK_JS_MASK(i);
+ }
+ }
- mutex_unlock(&queue->lock);
+ return js_state & *js_state_mask;
+}
+
+static void
+panfrost_reset(struct panfrost_device *pfdev,
+ struct drm_sched_job *bad)
+{
+ u32 js_state, js_state_mask = 0xffffffff;
+ unsigned int i, j;
+ bool cookie;
+ int ret;
+
+ if (!atomic_read(&pfdev->reset.pending))
+ return;
+
+ /* Stop the schedulers.
+ *
+ * FIXME: We temporarily get out of the dma_fence_signalling section
+ * because the cleanup path generate lockdep splats when taking locks
+ * to release job resources. We should rework the code to follow this
+ * pattern:
+ *
+ * try_lock
+ * if (locked)
+ * release
+ * else
+ * schedule_work_to_release_later
+ */
+ for (i = 0; i < NUM_JOB_SLOTS; i++)
+ drm_sched_stop(&pfdev->js->queue[i].sched, bad);
+
+ cookie = dma_fence_begin_signalling();
+
+ if (bad)
+ drm_sched_increase_karma(bad);
+
+ /* Mask job interrupts and synchronize to make sure we won't be
+ * interrupted during our reset.
+ */
+ job_write(pfdev, JOB_INT_MASK, 0);
+ synchronize_irq(pfdev->js->irq);
+
+ for (i = 0; i < NUM_JOB_SLOTS; i++) {
+ /* Cancel the next job and soft-stop the running job. */
+ job_write(pfdev, JS_COMMAND_NEXT(i), JS_COMMAND_NOP);
+ job_write(pfdev, JS_COMMAND(i), JS_COMMAND_SOFT_STOP);
+ }
+
+ /* Wait at most 10ms for soft-stops to complete */
+ ret = readl_poll_timeout(pfdev->iomem + JOB_INT_JS_STATE, js_state,
+ !panfrost_active_slots(pfdev, &js_state_mask, js_state),
+ 10, 10000);
+
+ if (ret)
+ dev_err(pfdev->dev, "Soft-stop failed\n");
+
+ /* Handle the remaining interrupts before we reset. */
+ panfrost_job_handle_irqs(pfdev);
+
+ /* Remaining interrupts have been handled, but we might still have
+ * stuck jobs. Let's make sure the PM counters stay balanced by
+ * manually calling pm_runtime_put_noidle() and
+ * panfrost_devfreq_record_idle() for each stuck job.
+ */
+ spin_lock(&pfdev->js->job_lock);
+ for (i = 0; i < NUM_JOB_SLOTS; i++) {
+ for (j = 0; j < ARRAY_SIZE(pfdev->jobs[0]) && pfdev->jobs[i][j]; j++) {
+ pm_runtime_put_noidle(pfdev->dev);
+ panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
+ }
+ }
+ memset(pfdev->jobs, 0, sizeof(pfdev->jobs));
+ spin_unlock(&pfdev->js->job_lock);
+
+ /* Proceed with reset now. */
+ panfrost_device_reset(pfdev);
+
+ /* panfrost_device_reset() unmasks job interrupts, but we want to
+ * keep them masked a bit longer.
+ */
+ job_write(pfdev, JOB_INT_MASK, 0);
+
+ /* GPU has been reset, we can clear the reset pending bit. */
+ atomic_set(&pfdev->reset.pending, 0);
+
+ /* Now resubmit jobs that were previously queued but didn't have a
+ * chance to finish.
+ * FIXME: We temporarily get out of the DMA fence signalling section
+ * while resubmitting jobs because the job submission logic will
+ * allocate memory with the GFP_KERNEL flag which can trigger memory
+ * reclaim and exposes a lock ordering issue.
+ */
+ dma_fence_end_signalling(cookie);
+ for (i = 0; i < NUM_JOB_SLOTS; i++)
+ drm_sched_resubmit_jobs(&pfdev->js->queue[i].sched);
+ cookie = dma_fence_begin_signalling();
+
+ /* Restart the schedulers */
+ for (i = 0; i < NUM_JOB_SLOTS; i++)
+ drm_sched_start(&pfdev->js->queue[i].sched, true);
+
+ /* Re-enable job interrupts now that everything has been restarted. */
+ job_write(pfdev, JOB_INT_MASK,
+ GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
+ GENMASK(NUM_JOB_SLOTS - 1, 0));
+
+ dma_fence_end_signalling(cookie);
}
static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job
@@ -454,17 +748,20 @@ static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job
job_read(pfdev, JS_TAIL_LO(js)),
sched_job);
- /* Scheduler is already stopped, nothing to do. */
- if (!panfrost_scheduler_stop(&pfdev->js->queue[js], sched_job))
- return DRM_GPU_SCHED_STAT_NOMINAL;
-
- /* Schedule a reset if there's no reset in progress. */
- if (!atomic_xchg(&pfdev->reset.pending, 1))
- schedule_work(&pfdev->reset.work);
+ atomic_set(&pfdev->reset.pending, 1);
+ panfrost_reset(pfdev, sched_job);
return DRM_GPU_SCHED_STAT_NOMINAL;
}
+static void panfrost_reset_work(struct work_struct *work)
+{
+ struct panfrost_device *pfdev;
+
+ pfdev = container_of(work, struct panfrost_device, reset.work);
+ panfrost_reset(pfdev, NULL);
+}
+
static const struct drm_sched_backend_ops panfrost_sched_ops = {
.dependency = panfrost_job_dependency,
.run_job = panfrost_job_run,
@@ -472,161 +769,75 @@ static const struct drm_sched_backend_ops panfrost_sched_ops = {
.free_job = panfrost_job_free
};
-static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
+static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
{
struct panfrost_device *pfdev = data;
- u32 status = job_read(pfdev, JOB_INT_STAT);
- int j;
-
- dev_dbg(pfdev->dev, "jobslot irq status=%x\n", status);
-
- if (!status)
- return IRQ_NONE;
-
- pm_runtime_mark_last_busy(pfdev->dev);
-
- for (j = 0; status; j++) {
- u32 mask = MK_JS_MASK(j);
-
- if (!(status & mask))
- continue;
-
- job_write(pfdev, JOB_INT_CLEAR, mask);
-
- if (status & JOB_INT_MASK_ERR(j)) {
- enum panfrost_queue_status old_status;
-
- job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_NOP);
-
- dev_err(pfdev->dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
- j,
- panfrost_exception_name(pfdev, job_read(pfdev, JS_STATUS(j))),
- job_read(pfdev, JS_HEAD_LO(j)),
- job_read(pfdev, JS_TAIL_LO(j)));
-
- /*
- * When the queue is being restarted we don't report
- * faults directly to avoid races between the timeout
- * and reset handlers. panfrost_scheduler_start() will
- * call drm_sched_fault() after the queue has been
- * started if status == FAULT_PENDING.
- */
- old_status = atomic_cmpxchg(&pfdev->js->queue[j].status,
- PANFROST_QUEUE_STATUS_STARTING,
- PANFROST_QUEUE_STATUS_FAULT_PENDING);
- if (old_status == PANFROST_QUEUE_STATUS_ACTIVE)
- drm_sched_fault(&pfdev->js->queue[j].sched);
- }
-
- if (status & JOB_INT_MASK_DONE(j)) {
- struct panfrost_job *job;
-
- spin_lock(&pfdev->js->job_lock);
- job = pfdev->jobs[j];
- /* Only NULL if job timeout occurred */
- if (job) {
- pfdev->jobs[j] = NULL;
-
- panfrost_mmu_as_put(pfdev, &job->file_priv->mmu);
- panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
-
- dma_fence_signal_locked(job->done_fence);
- pm_runtime_put_autosuspend(pfdev->dev);
- }
- spin_unlock(&pfdev->js->job_lock);
- }
-
- status &= ~mask;
- }
+ panfrost_job_handle_irqs(pfdev);
+ job_write(pfdev, JOB_INT_MASK,
+ GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
+ GENMASK(NUM_JOB_SLOTS - 1, 0));
return IRQ_HANDLED;
}
-static void panfrost_reset(struct work_struct *work)
+static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
{
- struct panfrost_device *pfdev = container_of(work,
- struct panfrost_device,
- reset.work);
- unsigned long flags;
- unsigned int i;
- bool cookie;
-
- cookie = dma_fence_begin_signalling();
- for (i = 0; i < NUM_JOB_SLOTS; i++) {
- /*
- * We want pending timeouts to be handled before we attempt
- * to stop the scheduler. If we don't do that and the timeout
- * handler is in flight, it might have removed the bad job
- * from the list, and we'll lose this job if the reset handler
- * enters the critical section in panfrost_scheduler_stop()
- * before the timeout handler.
- *
- * Timeout is set to MAX_SCHEDULE_TIMEOUT - 1 because we need
- * something big enough to make sure the timer will not expire
- * before we manage to stop the scheduler, but we can't use
- * MAX_SCHEDULE_TIMEOUT because drm_sched_get_cleanup_job()
- * considers that as 'timer is not running' and will dequeue
- * the job without making sure the timeout handler is not
- * running.
- */
- pfdev->js->queue[i].sched.timeout = MAX_SCHEDULE_TIMEOUT - 1;
- cancel_delayed_work_sync(&pfdev->js->queue[i].sched.work_tdr);
- panfrost_scheduler_stop(&pfdev->js->queue[i], NULL);
- }
-
- /* All timers have been stopped, we can safely reset the pending state. */
- atomic_set(&pfdev->reset.pending, 0);
-
- spin_lock_irqsave(&pfdev->js->job_lock, flags);
- for (i = 0; i < NUM_JOB_SLOTS; i++) {
- if (pfdev->jobs[i]) {
- pm_runtime_put_noidle(pfdev->dev);
- panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
- pfdev->jobs[i] = NULL;
- }
- }
- spin_unlock_irqrestore(&pfdev->js->job_lock, flags);
-
- panfrost_device_reset(pfdev);
+ struct panfrost_device *pfdev = data;
+ u32 status = job_read(pfdev, JOB_INT_STAT);
- for (i = 0; i < NUM_JOB_SLOTS; i++)
- panfrost_scheduler_start(&pfdev->js->queue[i]);
+ if (!status)
+ return IRQ_NONE;
- dma_fence_end_signalling(cookie);
+ job_write(pfdev, JOB_INT_MASK, 0);
+ return IRQ_WAKE_THREAD;
}
int panfrost_job_init(struct panfrost_device *pfdev)
{
struct panfrost_job_slot *js;
- int ret, j, irq;
+ unsigned int nentries = 2;
+ int ret, j;
- INIT_WORK(&pfdev->reset.work, panfrost_reset);
+ /* All GPUs have two entries per queue, but without jobchain
+ * disambiguation stopping the right job in the close path is tricky,
+ * so let's just advertise one entry in that case.
+ */
+ if (!panfrost_has_hw_feature(pfdev, HW_FEATURE_JOBCHAIN_DISAMBIGUATION))
+ nentries = 1;
pfdev->js = js = devm_kzalloc(pfdev->dev, sizeof(*js), GFP_KERNEL);
if (!js)
return -ENOMEM;
+ INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
- irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job");
- if (irq <= 0)
+ js->irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job");
+ if (js->irq <= 0)
return -ENODEV;
- ret = devm_request_irq(pfdev->dev, irq, panfrost_job_irq_handler,
- IRQF_SHARED, KBUILD_MODNAME "-job", pfdev);
+ ret = devm_request_threaded_irq(pfdev->dev, js->irq,
+ panfrost_job_irq_handler,
+ panfrost_job_irq_handler_thread,
+ IRQF_SHARED, KBUILD_MODNAME "-job",
+ pfdev);
if (ret) {
dev_err(pfdev->dev, "failed to request job irq");
return ret;
}
- for (j = 0; j < NUM_JOB_SLOTS; j++) {
- mutex_init(&js->queue[j].lock);
+ pfdev->reset.wq = alloc_ordered_workqueue("panfrost-reset", 0);
+ if (!pfdev->reset.wq)
+ return -ENOMEM;
+ for (j = 0; j < NUM_JOB_SLOTS; j++) {
js->queue[j].fence_context = dma_fence_context_alloc(1);
ret = drm_sched_init(&js->queue[j].sched,
&panfrost_sched_ops,
- 1, 0, msecs_to_jiffies(JOB_TIMEOUT_MS),
+ nentries, 0,
+ msecs_to_jiffies(JOB_TIMEOUT_MS),
+ pfdev->reset.wq,
NULL, "pan_js");
if (ret) {
dev_err(pfdev->dev, "Failed to create scheduler: %d.", ret);
@@ -642,6 +853,7 @@ err_sched:
for (j--; j >= 0; j--)
drm_sched_fini(&js->queue[j].sched);
+ destroy_workqueue(pfdev->reset.wq);
return ret;
}
@@ -654,9 +866,10 @@ void panfrost_job_fini(struct panfrost_device *pfdev)
for (j = 0; j < NUM_JOB_SLOTS; j++) {
drm_sched_fini(&js->queue[j].sched);
- mutex_destroy(&js->queue[j].lock);
}
+ cancel_work_sync(&pfdev->reset.work);
+ destroy_workqueue(pfdev->reset.wq);
}
int panfrost_job_open(struct panfrost_file_priv *panfrost_priv)
@@ -679,10 +892,46 @@ int panfrost_job_open(struct panfrost_file_priv *panfrost_priv)
void panfrost_job_close(struct panfrost_file_priv *panfrost_priv)
{
+ struct panfrost_device *pfdev = panfrost_priv->pfdev;
int i;
for (i = 0; i < NUM_JOB_SLOTS; i++)
drm_sched_entity_destroy(&panfrost_priv->sched_entity[i]);
+
+ /* Kill in-flight jobs */
+ spin_lock(&pfdev->js->job_lock);
+ for (i = 0; i < NUM_JOB_SLOTS; i++) {
+ struct drm_sched_entity *entity = &panfrost_priv->sched_entity[i];
+ int j;
+
+ for (j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) {
+ struct panfrost_job *job = pfdev->jobs[i][j];
+ u32 cmd;
+
+ if (!job || job->base.entity != entity)
+ continue;
+
+ if (j == 1) {
+ /* Try to cancel the job before it starts */
+ job_write(pfdev, JS_COMMAND_NEXT(i), JS_COMMAND_NOP);
+ /* Reset the job head so it doesn't get restarted if
+ * the job in the first slot failed.
+ */
+ job->jc = 0;
+ }
+
+ if (panfrost_has_hw_feature(pfdev, HW_FEATURE_JOBCHAIN_DISAMBIGUATION)) {
+ cmd = panfrost_get_job_chain_flag(job) ?
+ JS_COMMAND_HARD_STOP_1 :
+ JS_COMMAND_HARD_STOP_0;
+ } else {
+ cmd = JS_COMMAND_HARD_STOP;
+ }
+
+ job_write(pfdev, JS_COMMAND(i), cmd);
+ }
+ }
+ spin_unlock(&pfdev->js->job_lock);
}
int panfrost_job_is_idle(struct panfrost_device *pfdev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index bbd3ba97ff67..82306a03b57e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -19,9 +19,9 @@ struct panfrost_job {
struct panfrost_device *pfdev;
struct panfrost_file_priv *file_priv;
- /* Optional fences userspace can pass in for the job to depend on. */
- struct dma_fence **in_fences;
- u32 in_fence_count;
+ /* Contains both explicit and implicit fences */
+ struct xarray deps;
+ unsigned long last_dep;
/* Fence to be signaled by IRQ handler when the job is complete. */
struct dma_fence *done_fence;
@@ -30,8 +30,6 @@ struct panfrost_job {
__u32 requirements;
__u32 flush_id;
- /* Exclusive fences we have taken from the BOs to wait for */
- struct dma_fence **implicit_fences;
struct panfrost_gem_mapping **mappings;
struct drm_gem_object **bos;
u32 bo_count;
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 0581186ebfb3..0da5b3100ab1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -1,5 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
+
+#include <drm/panfrost_drm.h>
+
#include <linux/atomic.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
@@ -31,10 +34,13 @@ static int wait_ready(struct panfrost_device *pfdev, u32 as_nr)
/* Wait for the MMU status to indicate there is no active command, in
* case one is pending. */
ret = readl_relaxed_poll_timeout_atomic(pfdev->iomem + AS_STATUS(as_nr),
- val, !(val & AS_STATUS_AS_ACTIVE), 10, 1000);
+ val, !(val & AS_STATUS_AS_ACTIVE), 10, 100000);
- if (ret)
+ if (ret) {
+ /* The GPU hung, let's trigger a reset */
+ panfrost_device_schedule_reset(pfdev);
dev_err(pfdev->dev, "AS_ACTIVE bit stuck\n");
+ }
return ret;
}
@@ -151,6 +157,7 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
as = mmu->as;
if (as >= 0) {
int en = atomic_inc_return(&mmu->as_count);
+ u32 mask = BIT(as) | BIT(16 + as);
/*
* AS can be retained by active jobs or a perfcnt context,
@@ -159,6 +166,18 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
WARN_ON(en >= (NUM_JOB_SLOTS + 1));
list_move(&mmu->list, &pfdev->as_lru_list);
+
+ if (pfdev->as_faulty_mask & mask) {
+ /* Unhandled pagefault on this AS, the MMU was
+ * disabled. We need to re-enable the MMU after
+ * clearing+unmasking the AS interrupts.
+ */
+ mmu_write(pfdev, MMU_INT_CLEAR, mask);
+ mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
+ pfdev->as_faulty_mask &= ~mask;
+ panfrost_mmu_enable(pfdev, mmu);
+ }
+
goto out;
}
@@ -208,6 +227,7 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
spin_lock(&pfdev->as_lock);
pfdev->as_alloc_mask = 0;
+ pfdev->as_faulty_mask = 0;
list_for_each_entry_safe(mmu, mmu_tmp, &pfdev->as_lru_list, list) {
mmu->as = -1;
@@ -337,7 +357,7 @@ static void mmu_tlb_inv_context_s1(void *cookie)
static void mmu_tlb_sync_context(void *cookie)
{
- //struct panfrost_device *pfdev = cookie;
+ //struct panfrost_mmu *mmu = cookie;
// TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X
}
@@ -352,57 +372,10 @@ static const struct iommu_flush_ops mmu_tlb_ops = {
.tlb_flush_walk = mmu_tlb_flush_walk,
};
-int panfrost_mmu_pgtable_alloc(struct panfrost_file_priv *priv)
-{
- struct panfrost_mmu *mmu = &priv->mmu;
- struct panfrost_device *pfdev = priv->pfdev;
-
- INIT_LIST_HEAD(&mmu->list);
- mmu->as = -1;
-
- mmu->pgtbl_cfg = (struct io_pgtable_cfg) {
- .pgsize_bitmap = SZ_4K | SZ_2M,
- .ias = FIELD_GET(0xff, pfdev->features.mmu_features),
- .oas = FIELD_GET(0xff00, pfdev->features.mmu_features),
- .coherent_walk = pfdev->coherent,
- .tlb = &mmu_tlb_ops,
- .iommu_dev = pfdev->dev,
- };
-
- mmu->pgtbl_ops = alloc_io_pgtable_ops(ARM_MALI_LPAE, &mmu->pgtbl_cfg,
- priv);
- if (!mmu->pgtbl_ops)
- return -EINVAL;
-
- return 0;
-}
-
-void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv)
-{
- struct panfrost_device *pfdev = priv->pfdev;
- struct panfrost_mmu *mmu = &priv->mmu;
-
- spin_lock(&pfdev->as_lock);
- if (mmu->as >= 0) {
- pm_runtime_get_noresume(pfdev->dev);
- if (pm_runtime_active(pfdev->dev))
- panfrost_mmu_disable(pfdev, mmu->as);
- pm_runtime_put_autosuspend(pfdev->dev);
-
- clear_bit(mmu->as, &pfdev->as_alloc_mask);
- clear_bit(mmu->as, &pfdev->as_in_use_mask);
- list_del(&mmu->list);
- }
- spin_unlock(&pfdev->as_lock);
-
- free_io_pgtable_ops(mmu->pgtbl_ops);
-}
-
static struct panfrost_gem_mapping *
addr_to_mapping(struct panfrost_device *pfdev, int as, u64 addr)
{
struct panfrost_gem_mapping *mapping = NULL;
- struct panfrost_file_priv *priv;
struct drm_mm_node *node;
u64 offset = addr >> PAGE_SHIFT;
struct panfrost_mmu *mmu;
@@ -415,11 +388,10 @@ addr_to_mapping(struct panfrost_device *pfdev, int as, u64 addr)
goto out;
found_mmu:
- priv = container_of(mmu, struct panfrost_file_priv, mmu);
- spin_lock(&priv->mm_lock);
+ spin_lock(&mmu->mm_lock);
- drm_mm_for_each_node(node, &priv->mm) {
+ drm_mm_for_each_node(node, &mmu->mm) {
if (offset >= node->start &&
offset < (node->start + node->size)) {
mapping = drm_mm_node_to_panfrost_mapping(node);
@@ -429,7 +401,7 @@ found_mmu:
}
}
- spin_unlock(&priv->mm_lock);
+ spin_unlock(&mmu->mm_lock);
out:
spin_unlock(&pfdev->as_lock);
return mapping;
@@ -542,6 +514,107 @@ err_bo:
return ret;
}
+static void panfrost_mmu_release_ctx(struct kref *kref)
+{
+ struct panfrost_mmu *mmu = container_of(kref, struct panfrost_mmu,
+ refcount);
+ struct panfrost_device *pfdev = mmu->pfdev;
+
+ spin_lock(&pfdev->as_lock);
+ if (mmu->as >= 0) {
+ pm_runtime_get_noresume(pfdev->dev);
+ if (pm_runtime_active(pfdev->dev))
+ panfrost_mmu_disable(pfdev, mmu->as);
+ pm_runtime_put_autosuspend(pfdev->dev);
+
+ clear_bit(mmu->as, &pfdev->as_alloc_mask);
+ clear_bit(mmu->as, &pfdev->as_in_use_mask);
+ list_del(&mmu->list);
+ }
+ spin_unlock(&pfdev->as_lock);
+
+ free_io_pgtable_ops(mmu->pgtbl_ops);
+ drm_mm_takedown(&mmu->mm);
+ kfree(mmu);
+}
+
+void panfrost_mmu_ctx_put(struct panfrost_mmu *mmu)
+{
+ kref_put(&mmu->refcount, panfrost_mmu_release_ctx);
+}
+
+struct panfrost_mmu *panfrost_mmu_ctx_get(struct panfrost_mmu *mmu)
+{
+ kref_get(&mmu->refcount);
+
+ return mmu;
+}
+
+#define PFN_4G (SZ_4G >> PAGE_SHIFT)
+#define PFN_4G_MASK (PFN_4G - 1)
+#define PFN_16M (SZ_16M >> PAGE_SHIFT)
+
+static void panfrost_drm_mm_color_adjust(const struct drm_mm_node *node,
+ unsigned long color,
+ u64 *start, u64 *end)
+{
+ /* Executable buffers can't start or end on a 4GB boundary */
+ if (!(color & PANFROST_BO_NOEXEC)) {
+ u64 next_seg;
+
+ if ((*start & PFN_4G_MASK) == 0)
+ (*start)++;
+
+ if ((*end & PFN_4G_MASK) == 0)
+ (*end)--;
+
+ next_seg = ALIGN(*start, PFN_4G);
+ if (next_seg - *start <= PFN_16M)
+ *start = next_seg + 1;
+
+ *end = min(*end, ALIGN(*start, PFN_4G) - 1);
+ }
+}
+
+struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev)
+{
+ struct panfrost_mmu *mmu;
+
+ mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
+ if (!mmu)
+ return ERR_PTR(-ENOMEM);
+
+ mmu->pfdev = pfdev;
+ spin_lock_init(&mmu->mm_lock);
+
+ /* 4G enough for now. can be 48-bit */
+ drm_mm_init(&mmu->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT);
+ mmu->mm.color_adjust = panfrost_drm_mm_color_adjust;
+
+ INIT_LIST_HEAD(&mmu->list);
+ mmu->as = -1;
+
+ mmu->pgtbl_cfg = (struct io_pgtable_cfg) {
+ .pgsize_bitmap = SZ_4K | SZ_2M,
+ .ias = FIELD_GET(0xff, pfdev->features.mmu_features),
+ .oas = FIELD_GET(0xff00, pfdev->features.mmu_features),
+ .coherent_walk = pfdev->coherent,
+ .tlb = &mmu_tlb_ops,
+ .iommu_dev = pfdev->dev,
+ };
+
+ mmu->pgtbl_ops = alloc_io_pgtable_ops(ARM_MALI_LPAE, &mmu->pgtbl_cfg,
+ mmu);
+ if (!mmu->pgtbl_ops) {
+ kfree(mmu);
+ return ERR_PTR(-EINVAL);
+ }
+
+ kref_init(&mmu->refcount);
+
+ return mmu;
+}
+
static const char *access_type_name(struct panfrost_device *pfdev,
u32 fault_status)
{
@@ -605,7 +678,7 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
if ((status & mask) == BIT(as) && (exception_type & 0xF8) == 0xC0)
ret = panfrost_mmu_map_fault_addr(pfdev, as, addr);
- if (ret)
+ if (ret) {
/* terminal fault, print info about the fault */
dev_err(pfdev->dev,
"Unhandled Page fault in AS%d at VA 0x%016llX\n"
@@ -619,18 +692,32 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
"TODO",
fault_status,
(fault_status & (1 << 10) ? "DECODER FAULT" : "SLAVE FAULT"),
- exception_type, panfrost_exception_name(pfdev, exception_type),
+ exception_type, panfrost_exception_name(exception_type),
access_type, access_type_name(pfdev, fault_status),
source_id);
+ spin_lock(&pfdev->as_lock);
+ /* Ignore MMU interrupts on this AS until it's been
+ * re-enabled.
+ */
+ pfdev->as_faulty_mask |= mask;
+
+ /* Disable the MMU to kill jobs on this AS. */
+ panfrost_mmu_disable(pfdev, as);
+ spin_unlock(&pfdev->as_lock);
+ }
+
status &= ~mask;
/* If we received new MMU interrupts, process them before returning. */
if (!status)
- status = mmu_read(pfdev, MMU_INT_RAWSTAT);
+ status = mmu_read(pfdev, MMU_INT_RAWSTAT) & ~pfdev->as_faulty_mask;
}
- mmu_write(pfdev, MMU_INT_MASK, ~0);
+ spin_lock(&pfdev->as_lock);
+ mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
+ spin_unlock(&pfdev->as_lock);
+
return IRQ_HANDLED;
};
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
index 44fc2edf63ce..cc2a0d307feb 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
@@ -18,7 +18,8 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev);
u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
-int panfrost_mmu_pgtable_alloc(struct panfrost_file_priv *priv);
-void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv);
+struct panfrost_mmu *panfrost_mmu_ctx_get(struct panfrost_mmu *mmu);
+void panfrost_mmu_ctx_put(struct panfrost_mmu *mmu);
+struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev);
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h
index dc9df5457f1c..1940ff86e49a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_regs.h
+++ b/drivers/gpu/drm/panfrost/panfrost_regs.h
@@ -262,9 +262,6 @@
#define JS_COMMAND_SOFT_STOP_1 0x06 /* Execute SOFT_STOP if JOB_CHAIN_FLAG is 1 */
#define JS_COMMAND_HARD_STOP_1 0x07 /* Execute HARD_STOP if JOB_CHAIN_FLAG is 1 */
-#define JS_STATUS_EVENT_ACTIVE 0x08
-
-
/* MMU regs */
#define MMU_INT_RAWSTAT 0x2000
#define MMU_INT_CLEAR 0x2004