Commit c27787f2 authored by Karunika Choo's avatar Karunika Choo Committed by Boris Brezillon
Browse files

drm/panthor: Introduce panthor_pwr API and power control framework



Add the new panthor_pwr module, which provides basic power control
management for Mali-G1 GPUs. The initial implementation includes
infrastructure for initializing the PWR_CONTROL block, requesting and
handling its IRQ, and checking for PWR_CONTROL support based on GPU
architecture.

The patch also integrates panthor_pwr with the device lifecycle (init,
suspend, resume, and unplug) through the new API functions. It also
registers the IRQ handler under the 'gpu' IRQ as the PWR_CONTROL block
is located within the GPU_CONTROL block.

Reviewed-by: default avatarSteven Price <steven.price@arm.com>
Signed-off-by: default avatarKarunika Choo <karunika.choo@arm.com>
Link: https://patch.msgid.link/20251125125548.3282320-4-karunika.choo@arm.com


Signed-off-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
parent 7d334f5c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ panthor-y := \
	panthor_heap.o \
	panthor_hw.o \
	panthor_mmu.o \
	panthor_pwr.o \
	panthor_sched.o

obj-$(CONFIG_DRM_PANTHOR) += panthor.o
+13 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include "panthor_gpu.h"
#include "panthor_hw.h"
#include "panthor_mmu.h"
#include "panthor_pwr.h"
#include "panthor_regs.h"
#include "panthor_sched.h"

@@ -113,6 +114,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
	panthor_fw_unplug(ptdev);
	panthor_mmu_unplug(ptdev);
	panthor_gpu_unplug(ptdev);
	panthor_pwr_unplug(ptdev);

	pm_runtime_dont_use_autosuspend(ptdev->base.dev);
	pm_runtime_put_sync_suspend(ptdev->base.dev);
@@ -268,10 +270,14 @@ int panthor_device_init(struct panthor_device *ptdev)
	if (ret)
		goto err_rpm_put;

	ret = panthor_gpu_init(ptdev);
	ret = panthor_pwr_init(ptdev);
	if (ret)
		goto err_rpm_put;

	ret = panthor_gpu_init(ptdev);
	if (ret)
		goto err_unplug_pwr;

	ret = panthor_gpu_coherency_init(ptdev);
	if (ret)
		goto err_unplug_gpu;
@@ -312,6 +318,9 @@ int panthor_device_init(struct panthor_device *ptdev)
err_unplug_gpu:
	panthor_gpu_unplug(ptdev);

err_unplug_pwr:
	panthor_pwr_unplug(ptdev);

err_rpm_put:
	pm_runtime_put_sync_suspend(ptdev->base.dev);
	return ret;
@@ -465,6 +474,7 @@ static int panthor_device_resume_hw_components(struct panthor_device *ptdev)
{
	int ret;

	panthor_pwr_resume(ptdev);
	panthor_gpu_resume(ptdev);
	panthor_mmu_resume(ptdev);

@@ -474,6 +484,7 @@ static int panthor_device_resume_hw_components(struct panthor_device *ptdev)

	panthor_mmu_suspend(ptdev);
	panthor_gpu_suspend(ptdev);
	panthor_pwr_suspend(ptdev);
	return ret;
}

@@ -587,6 +598,7 @@ int panthor_device_suspend(struct device *dev)
		panthor_fw_suspend(ptdev);
		panthor_mmu_suspend(ptdev);
		panthor_gpu_suspend(ptdev);
		panthor_pwr_suspend(ptdev);
		drm_dev_exit(cookie);
	}

+4 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ struct panthor_job;
struct panthor_mmu;
struct panthor_fw;
struct panthor_perfcnt;
struct panthor_pwr;
struct panthor_vm;
struct panthor_vm_pool;

@@ -138,6 +139,9 @@ struct panthor_device {
	/** @hw: GPU-specific data. */
	struct panthor_hw *hw;

	/** @pwr: Power control management data. */
	struct panthor_pwr *pwr;

	/** @gpu: GPU management data. */
	struct panthor_gpu *gpu;

+6 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#define __PANTHOR_HW_H__

#include "panthor_device.h"
#include "panthor_regs.h"

/**
 * struct panthor_hw_ops - HW operations that are specific to a GPU
@@ -47,4 +48,9 @@ static inline void panthor_hw_l2_power_off(struct panthor_device *ptdev)
	ptdev->hw->ops.l2_power_off(ptdev);
}

static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
{
	return GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14;
}

#endif /* __PANTHOR_HW_H__ */
+121 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 or MIT
/* Copyright 2025 ARM Limited. All rights reserved. */

#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/wait.h>

#include <drm/drm_managed.h>
#include <drm/drm_print.h>

#include "panthor_device.h"
#include "panthor_hw.h"
#include "panthor_pwr.h"
#include "panthor_regs.h"

#define PWR_INTERRUPTS_MASK \
	(PWR_IRQ_POWER_CHANGED_SINGLE | \
	 PWR_IRQ_POWER_CHANGED_ALL | \
	 PWR_IRQ_DELEGATION_CHANGED | \
	 PWR_IRQ_RESET_COMPLETED | \
	 PWR_IRQ_RETRACT_COMPLETED | \
	 PWR_IRQ_INSPECT_COMPLETED | \
	 PWR_IRQ_COMMAND_NOT_ALLOWED | \
	 PWR_IRQ_COMMAND_INVALID)

/**
 * struct panthor_pwr - PWR_CONTROL block management data.
 */
struct panthor_pwr {
	/** @irq: PWR irq. */
	struct panthor_irq irq;

	/** @reqs_lock: Lock protecting access to pending_reqs. */
	spinlock_t reqs_lock;

	/** @pending_reqs: Pending PWR requests. */
	u32 pending_reqs;

	/** @reqs_acked: PWR request wait queue. */
	wait_queue_head_t reqs_acked;
};

static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status)
{
	spin_lock(&ptdev->pwr->reqs_lock);
	gpu_write(ptdev, PWR_INT_CLEAR, status);

	if (unlikely(status & PWR_IRQ_COMMAND_NOT_ALLOWED))
		drm_err(&ptdev->base, "PWR_IRQ: COMMAND_NOT_ALLOWED");

	if (unlikely(status & PWR_IRQ_COMMAND_INVALID))
		drm_err(&ptdev->base, "PWR_IRQ: COMMAND_INVALID");

	if (status & ptdev->pwr->pending_reqs) {
		ptdev->pwr->pending_reqs &= ~status;
		wake_up_all(&ptdev->pwr->reqs_acked);
	}
	spin_unlock(&ptdev->pwr->reqs_lock);
}
PANTHOR_IRQ_HANDLER(pwr, PWR, panthor_pwr_irq_handler);

void panthor_pwr_unplug(struct panthor_device *ptdev)
{
	unsigned long flags;

	if (!ptdev->pwr)
		return;

	/* Make sure the IRQ handler is not running after that point. */
	panthor_pwr_irq_suspend(&ptdev->pwr->irq);

	/* Wake-up all waiters. */
	spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags);
	ptdev->pwr->pending_reqs = 0;
	wake_up_all(&ptdev->pwr->reqs_acked);
	spin_unlock_irqrestore(&ptdev->pwr->reqs_lock, flags);
}

int panthor_pwr_init(struct panthor_device *ptdev)
{
	struct panthor_pwr *pwr;
	int err, irq;

	if (!panthor_hw_has_pwr_ctrl(ptdev))
		return 0;

	pwr = drmm_kzalloc(&ptdev->base, sizeof(*pwr), GFP_KERNEL);
	if (!pwr)
		return -ENOMEM;

	spin_lock_init(&pwr->reqs_lock);
	init_waitqueue_head(&pwr->reqs_acked);
	ptdev->pwr = pwr;

	irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu");
	if (irq < 0)
		return irq;

	err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK);
	if (err)
		return err;

	return 0;
}

void panthor_pwr_suspend(struct panthor_device *ptdev)
{
	if (!ptdev->pwr)
		return;

	panthor_pwr_irq_suspend(&ptdev->pwr->irq);
}

void panthor_pwr_resume(struct panthor_device *ptdev)
{
	if (!ptdev->pwr)
		return;

	panthor_pwr_irq_resume(&ptdev->pwr->irq, PWR_INTERRUPTS_MASK);
}
Loading