From patchwork Wed Aug 9 16:53:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 13348193 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 98ACAC001B0 for ; Wed, 9 Aug 2023 16:54:03 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5BE7F10E477; Wed, 9 Aug 2023 16:53:44 +0000 (UTC) Received: from madras.collabora.co.uk (madras.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e5ab]) by gabe.freedesktop.org (Postfix) with ESMTPS id C246510E46B for ; Wed, 9 Aug 2023 16:53:37 +0000 (UTC) Received: from localhost.localdomain (unknown [IPv6:2a01:e0a:2c:6930:5cf4:84a1:2763:fe0d]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: bbrezillon) by madras.collabora.co.uk (Postfix) with ESMTPSA id 4728B6607217; Wed, 9 Aug 2023 17:53:36 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1691600016; bh=QWGaLaEm4QKXpSbFXg59xDTKMat65vL0jDVKrhOgJu8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S1XGeFCmz99Pd4dlMzPPOGmsRezawbQAbl8P72hHbGE69nKpSzIHPAhvIUPDQwMU9 9Z3BqOZG/fCmJC+LWzhEoThVJ3V2oTkNrnvSMLe+lv5nSWKl8MOHcELos3KP/u7UPB I3DKNOSKgDF/5N73PrfnkwUji4ZhqAyyZMsPSk6x8puPUWROwi/QaqIaI1ic8iwjxi mjf9qwGdeB809afqFFYdTIk8ELx4OrBEQ5SdmKWwABKb9Nt722NuOz4d3naWHlVi15 0xoJ/+YoeJtNm+FGuAeAYERBo3dH0H3h1MelzVTD+8AIaDzQrmJqCPK3pbWXJXpGJ9 sWIYecNlZ8SbQ== From: Boris Brezillon To: dri-devel@lists.freedesktop.org Subject: [PATCH v2 05/15] drm/panthor: Add the GPU logical block Date: Wed, 9 Aug 2023 18:53:18 +0200 Message-ID: <20230809165330.2451699-6-boris.brezillon@collabora.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230809165330.2451699-1-boris.brezillon@collabora.com> References: <20230809165330.2451699-1-boris.brezillon@collabora.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Nicolas Boichat , Daniel Stone , Neil Armstrong , Liviu Dudau , Steven Price , Boris Brezillon , =?utf-8?b?Q2zDqW1lbnQgUMOp?= =?utf-8?b?cm9u?= , "Marty E . Plummer" , Robin Murphy , Faith Ekstrand Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Handles everything that's not related to the FW, the MMU or the scheduler. This is the block dealing with the GPU property retrieval, the GPU block power on/off logic, and some global operations, like global cache flushing. v2: - Rename the driver (pancsf -> panthor) - Change the license (GPL2 -> MIT + GPL2) - Split the driver addition commit - Use drm_dev_{unplug,enter,exit}() to provide safe device removal - Use the panthor_irq layer to manage/process IRQs Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gpu.c | 463 ++++++++++++++++++++++++++ drivers/gpu/drm/panthor/panthor_gpu.h | 52 +++ 2 files changed, 515 insertions(+) create mode 100644 drivers/gpu/drm/panthor/panthor_gpu.c create mode 100644 drivers/gpu/drm/panthor/panthor_gpu.h diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c new file mode 100644 index 000000000000..47d15334b46e --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT +/* Copyright 2018 Marty E. Plummer */ +/* Copyright 2019 Linaro, Ltd., Rob Herring */ +/* Copyright 2019 Collabora ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "panthor_device.h" +#include "panthor_gpu.h" +#include "panthor_regs.h" + +/** + * struct panthor_gpu - GPU block management data. + */ +struct panthor_gpu { + /** @irq: GPU irq. */ + struct panthor_irq irq; + + /** @reqs_lock: Lock protecting access to pending_reqs. */ + spinlock_t reqs_lock; + + /** @pending_reqs: Pending GPU requests. */ + u32 pending_reqs; + + /** @reqs_acked: GPU request wait queue. */ + wait_queue_head_t reqs_acked; +}; + +/** + * struct panthor_model - GPU model description + */ +struct panthor_model { + /** @name: Model name. */ + const char *name; + + /** @id: Model ID. */ + u32 id; +}; + +/** + * GPU_MODEL() - Define a GPU model. + */ +#define GPU_MODEL(_name, _id, ...) \ +{\ + .name = __stringify(_name), \ + .id = _id, \ +} + +#define GPU_MODEL_ID_MASK 0xf00f0000 + +static const struct panthor_model gpu_models[] = { + GPU_MODEL(g610, 0xa0070000), + {}, +}; + +#define GPU_INTERRUPTS_MASK \ + (GPU_IRQ_FAULT | \ + GPU_IRQ_PROTM_FAULT | \ + GPU_IRQ_RESET_COMPLETED | \ + GPU_IRQ_MCU_STATUS_CHANGED | \ + GPU_IRQ_CLEAN_CACHES_COMPLETED) + +static void panthor_gpu_init_info(struct panthor_device *ptdev) +{ + const struct panthor_model *model; + u32 major, minor, status; + unsigned int i; + + ptdev->gpu_info.gpu_id = gpu_read(ptdev, GPU_ID); + ptdev->gpu_info.csf_id = gpu_read(ptdev, GPU_CSF_ID); + ptdev->gpu_info.gpu_rev = gpu_read(ptdev, GPU_REVID); + ptdev->gpu_info.l2_features = gpu_read(ptdev, GPU_L2_FEATURES); + ptdev->gpu_info.tiler_features = gpu_read(ptdev, GPU_TILER_FEATURES); + ptdev->gpu_info.mem_features = gpu_read(ptdev, GPU_MEM_FEATURES); + ptdev->gpu_info.mmu_features = gpu_read(ptdev, GPU_MMU_FEATURES); + ptdev->gpu_info.thread_features = gpu_read(ptdev, GPU_THREAD_FEATURES); + ptdev->gpu_info.max_threads = gpu_read(ptdev, GPU_THREAD_MAX_THREADS); + ptdev->gpu_info.thread_max_workgroup_size = gpu_read(ptdev, GPU_THREAD_MAX_WORKGROUP_SIZE); + ptdev->gpu_info.thread_max_barrier_size = gpu_read(ptdev, GPU_THREAD_MAX_BARRIER_SIZE); + ptdev->gpu_info.coherency_features = gpu_read(ptdev, GPU_COHERENCY_FEATURES); + for (i = 0; i < 4; i++) + ptdev->gpu_info.texture_features[i] = gpu_read(ptdev, GPU_TEXTURE_FEATURES(i)); + + ptdev->gpu_info.as_present = gpu_read(ptdev, GPU_AS_PRESENT); + + ptdev->gpu_info.shader_present = gpu_read(ptdev, GPU_SHADER_PRESENT_LO); + ptdev->gpu_info.shader_present |= (u64)gpu_read(ptdev, GPU_SHADER_PRESENT_HI) << 32; + + ptdev->gpu_info.tiler_present = gpu_read(ptdev, GPU_TILER_PRESENT_LO); + ptdev->gpu_info.tiler_present |= (u64)gpu_read(ptdev, GPU_TILER_PRESENT_HI) << 32; + + ptdev->gpu_info.l2_present = gpu_read(ptdev, GPU_L2_PRESENT_LO); + ptdev->gpu_info.l2_present |= (u64)gpu_read(ptdev, GPU_L2_PRESENT_HI) << 32; + ptdev->gpu_info.core_group_count = hweight64(ptdev->gpu_info.l2_present); + + major = (ptdev->gpu_info.gpu_id >> 12) & 0xf; + minor = (ptdev->gpu_info.gpu_id >> 4) & 0xff; + status = ptdev->gpu_info.gpu_id & 0xf; + + for (model = gpu_models; model->name; model++) { + if (model->id == (ptdev->gpu_info.gpu_id & GPU_MODEL_ID_MASK)) + break; + } + + drm_info(&ptdev->base, + "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x", + model->name ?: "unknown", ptdev->gpu_info.gpu_id >> 16, + major, minor, status); + + drm_info(&ptdev->base, + "Features: L2:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x", + ptdev->gpu_info.l2_features, + ptdev->gpu_info.tiler_features, + ptdev->gpu_info.mem_features, + ptdev->gpu_info.mmu_features, + ptdev->gpu_info.as_present); + + drm_info(&ptdev->base, + "shader_present=0x%0llx l2_present=0x%0llx tiler_present=0x%0llx", + ptdev->gpu_info.shader_present, ptdev->gpu_info.l2_present, + ptdev->gpu_info.tiler_present); +} + +static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) +{ + if (status & (GPU_IRQ_FAULT | GPU_IRQ_PROTM_FAULT)) { + u32 fault_status = gpu_read(ptdev, GPU_FAULT_STATUS); + u64 address = ((u64)gpu_read(ptdev, GPU_FAULT_ADDR_HI) << 32) | + gpu_read(ptdev, GPU_FAULT_ADDR_LO); + + drm_warn(&ptdev->base, "GPU Fault 0x%08x (%s) at 0x%016llx\n", + fault_status, panthor_exception_name(ptdev, fault_status & 0xFF), + address); + } + + spin_lock(&ptdev->gpu->reqs_lock); + if (status & ptdev->gpu->pending_reqs) { + ptdev->gpu->pending_reqs &= ~status; + wake_up_all(&ptdev->gpu->reqs_acked); + } + spin_unlock(&ptdev->gpu->reqs_lock); +} +PANTHOR_IRQ_HANDLER(gpu, GPU, panthor_gpu_irq_handler); + +/** + * panthor_gpu_unplug() - Called when the GPU is unplugged. + */ +void panthor_gpu_unplug(struct panthor_device *ptdev) +{ + unsigned long flags; + + /* Make sure the IRQ handler is not running after that point. */ + panthor_gpu_irq_suspend(&ptdev->gpu->irq); + + /* Wake-up all waiters. */ + spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); + ptdev->gpu->pending_reqs = 0; + wake_up_all(&ptdev->gpu->reqs_acked); + spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); +} + +/** + * panthor_gpu_init() - Initialize the GPU block + * @ptdev: Device. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_init(struct panthor_device *ptdev) +{ + struct panthor_gpu *gpu; + u32 pa_bits; + int ret, irq; + + gpu = drmm_kzalloc(&ptdev->base, sizeof(*gpu), GFP_KERNEL); + if (!gpu) + return -ENOMEM; + + spin_lock_init(&gpu->reqs_lock); + init_waitqueue_head(&gpu->reqs_acked); + ptdev->gpu = gpu; + panthor_gpu_init_info(ptdev); + + dma_set_max_seg_size(ptdev->base.dev, UINT_MAX); + pa_bits = GPU_MMU_FEATURES_PA_BITS(ptdev->gpu_info.mmu_features); + ret = dma_set_mask_and_coherent(ptdev->base.dev, DMA_BIT_MASK(pa_bits)); + if (ret) + return ret; + + irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu"); + if (irq <= 0) + return ret; + + ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, GPU_INTERRUPTS_MASK); + if (ret) + return ret; + + return 0; +} + +/** + * panthor_gpu_block_power_off() - Power-off a specific block of the GPU + * @ptdev: Device. + * @blk_name: Block name. + * @pwroff_reg: Power-off register for this block. + * @pwrtrans_reg: Power transition register for this block. + * @mask: Sub-elements to power-off. + * @timeout_ms: Timeout in milliseconds. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_block_power_off(struct panthor_device *ptdev, + const char *blk_name, + u32 pwroff_reg, u32 pwrtrans_reg, + u64 mask, u32 timeout_us) +{ + u32 val, i; + int ret; + + for (i = 0; i < 2; i++) { + u32 mask32 = mask >> (i * 32); + + if (!mask32) + continue; + + ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), + val, !(mask32 & val), + 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", + blk_name, mask); + return ret; + } + } + + if (mask & GENMASK(31, 0)) + gpu_write(ptdev, pwroff_reg, mask); + + if (mask >> 32) + gpu_write(ptdev, pwroff_reg, mask >> 32); + + for (i = 0; i < 2; i++) { + u32 mask32 = mask >> (i * 32); + + if (!mask32) + continue; + + ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), + val, !(mask & val), + 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", + blk_name, mask); + return ret; + } + } + + return 0; +} + +/** + * panthor_gpu_block_power_on() - Power-on a specific block of the GPU + * @ptdev: Device. + * @blk_name: Block name. + * @pwron_reg: Power-on register for this block. + * @pwrtrans_reg: Power transition register for this block. + * @mask: Sub-elements to power-on. + * @timeout_ms: Timeout in milliseconds. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_block_power_on(struct panthor_device *ptdev, + const char *blk_name, + u32 pwron_reg, u32 pwrtrans_reg, + u32 rdy_reg, u64 mask, u32 timeout_us) +{ + u32 val, i; + int ret; + + for (i = 0; i < 2; i++) { + u32 mask32 = mask >> (i * 32); + + if (!mask32) + continue; + + ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), + val, !(mask32 & val), + 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", + blk_name, mask); + return ret; + } + } + + if (mask & GENMASK(31, 0)) + gpu_write(ptdev, pwron_reg, mask); + + if (mask >> 32) + gpu_write(ptdev, pwron_reg + 4, mask >> 32); + + for (i = 0; i < 2; i++) { + u32 mask32 = mask >> (i * 32); + + if (!mask32) + continue; + + ret = readl_relaxed_poll_timeout(ptdev->iomem + rdy_reg + (i * 4), + val, (mask32 & val) == mask32, + 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, "timeout waiting on %s:%llx readyness", + blk_name, mask); + return ret; + } + } + + return 0; +} + +/** + * panthor_gpu_l2_power_on() - Power-on the L2-cache + * @ptdev: Device. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_l2_power_on(struct panthor_device *ptdev) +{ + u64 core_mask = U64_MAX; + + if (ptdev->gpu_info.l2_present != 1) { + /* + * Only support one core group now. + * ~(l2_present - 1) unsets all bits in l2_present except + * the bottom bit. (l2_present - 2) has all the bits in + * the first core group set. AND them together to generate + * a mask of cores in the first core group. + */ + core_mask = ~(ptdev->gpu_info.l2_present - 1) & + (ptdev->gpu_info.l2_present - 2); + drm_info_once(&ptdev->base, "using only 1st core group (%lu cores from %lu)\n", + hweight64(core_mask), + hweight64(ptdev->gpu_info.shader_present)); + } + + return panthor_gpu_power_on(ptdev, L2, + ptdev->gpu_info.l2_present & core_mask, + 20000); +} + +/** + * panthor_gpu_flush_caches() - Flush caches + * @ptdev: Device. + * @l2: L2 flush type. + * @lsc: LSC flush type. + * @other: Other flush type. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_flush_caches(struct panthor_device *ptdev, + u32 l2, u32 lsc, u32 other) +{ + bool timedout = false; + unsigned long flags; + + spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); + if (!drm_WARN_ON(&ptdev->base, + ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { + ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED; + gpu_write(ptdev, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); + } + spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + + if (!wait_event_timeout(ptdev->gpu->reqs_acked, + !(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED), + msecs_to_jiffies(100))) { + spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); + if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 && + !(gpu_read(ptdev, GPU_INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) + timedout = true; + spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + } + + if (timedout) { + drm_err(&ptdev->base, "Flush caches timeout"); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * panthor_gpu_soft_reset() - Issue a soft-reset + * @ptdev: Device. + * + * Return: 0 on success, a negative error code otherwise. + */ +int panthor_gpu_soft_reset(struct panthor_device *ptdev) +{ + bool timedout = false; + unsigned long flags; + + spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); + if (!drm_WARN_ON(&ptdev->base, + ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) { + ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED; + gpu_write(ptdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); + gpu_write(ptdev, GPU_CMD, GPU_SOFT_RESET); + } + spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + + if (!wait_event_timeout(ptdev->gpu->reqs_acked, + !(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED), + msecs_to_jiffies(100))) { + spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); + if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 && + !(gpu_read(ptdev, GPU_INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED)) + timedout = true; + spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); + } + + if (timedout) { + drm_err(&ptdev->base, "Soft reset timeout"); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * panthor_gpu_suspend() - Suspend the GPU block. + * @ptdev: Device. + * + * Soft reset and suspend the GPU irq. This should be called last + * in the suspend procedure, after all other blocks have been suspented. + */ +void panthor_gpu_suspend(struct panthor_device *ptdev) +{ + panthor_gpu_soft_reset(ptdev); + panthor_gpu_irq_suspend(&ptdev->gpu->irq); +} + +/** + * panthor_gpu_resume() - Resume the GPU block. + * + * Resume the IRQ handler and power-on the L2-cache. + * The FW takes care of powering the other blocks. + */ +void panthor_gpu_resume(struct panthor_device *ptdev) +{ + panthor_gpu_irq_resume(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); + panthor_gpu_l2_power_on(ptdev); +} diff --git a/drivers/gpu/drm/panthor/panthor_gpu.h b/drivers/gpu/drm/panthor/panthor_gpu.h new file mode 100644 index 000000000000..bba7555dd3c6 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_gpu.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2018 Marty E. Plummer */ +/* Copyright 2019 Collabora ltd. */ + +#ifndef __PANTHOR_GPU_H__ +#define __PANTHOR_GPU_H__ + +struct panthor_device; + +int panthor_gpu_init(struct panthor_device *ptdev); +void panthor_gpu_unplug(struct panthor_device *ptdev); +void panthor_gpu_suspend(struct panthor_device *ptdev); +void panthor_gpu_resume(struct panthor_device *ptdev); + +int panthor_gpu_block_power_on(struct panthor_device *ptdev, + const char *blk_name, + u32 pwron_reg, u32 pwrtrans_reg, + u32 rdy_reg, u64 mask, u32 timeout_us); +int panthor_gpu_block_power_off(struct panthor_device *ptdev, + const char *blk_name, + u32 pwroff_reg, u32 pwrtrans_reg, + u64 mask, u32 timeout_us); + +/** + * panthor_gpu_power_on() - Power on the GPU block. + * + * Return: 0 on success, a negative error code otherwise. + */ +#define panthor_gpu_power_on(ptdev, type, mask, timeout_us) \ + panthor_gpu_block_power_on(ptdev, #type, \ + type ## _PWRON_LO, \ + type ## _PWRTRANS_LO, \ + type ## _READY_LO, \ + mask, timeout_us) + +/** + * panthor_gpu_power_off() - Power off the GPU block. + * + * Return: 0 on success, a negative error code otherwise. + */ +#define panthor_gpu_power_off(ptdev, type, mask, timeout_us) \ + panthor_gpu_block_power_off(ptdev, #type, \ + type ## _PWROFF_LO, \ + type ## _PWRTRANS_LO, \ + mask, timeout_us) + +int panthor_gpu_l2_power_on(struct panthor_device *ptdev); +int panthor_gpu_flush_caches(struct panthor_device *ptdev, + u32 l2, u32 lsc, u32 other); +int panthor_gpu_soft_reset(struct panthor_device *ptdev); + +#endif