Re: [PATCH v3 05/17] drm/imagination: Get GPU resources

2023-07-14 Thread Frank Binns
Hi Maxime,

On Fri, 2023-07-07 at 14:47 +0200, Maxime Ripard wrote:
> On Tue, Jun 13, 2023 at 03:47:48PM +0100, Sarah Walker wrote:
> > Acquire clock, regulator and register resources, and enable/map as
> > appropriate.
> > 
> > Signed-off-by: Sarah Walker 
> > ---
> >  drivers/gpu/drm/imagination/Makefile |   1 +
> >  drivers/gpu/drm/imagination/pvr_device.c | 271 +++
> >  drivers/gpu/drm/imagination/pvr_device.h | 214 ++
> >  drivers/gpu/drm/imagination/pvr_drv.c|  11 +-
> >  4 files changed, 496 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/imagination/pvr_device.c
> > 
> > diff --git a/drivers/gpu/drm/imagination/Makefile 
> > b/drivers/gpu/drm/imagination/Makefile
> > index 62ccf0ccbd51..186f920d615b 100644
> > --- a/drivers/gpu/drm/imagination/Makefile
> > +++ b/drivers/gpu/drm/imagination/Makefile
> > @@ -4,6 +4,7 @@
> >  subdir-ccflags-y := -I$(srctree)/$(src)
> > 
> >  powervr-y := \
> > + pvr_device.o \
> >   pvr_drv.o \
> > 
> >  obj-$(CONFIG_DRM_POWERVR) += powervr.o
> > diff --git a/drivers/gpu/drm/imagination/pvr_device.c 
> > b/drivers/gpu/drm/imagination/pvr_device.c
> > new file mode 100644
> > index ..790c36cebec1
> > --- /dev/null
> > +++ b/drivers/gpu/drm/imagination/pvr_device.c
> > @@ -0,0 +1,271 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +/* Copyright (c) 2022 Imagination Technologies Ltd. */
> > +
> > +#include "pvr_device.h"
> > +
> > +#include 
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +/**
> > + * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
> > + * control registers.
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * Sets struct pvr_device->regs.
> > + *
> > + * This method of mapping the device control registers into memory ensures 
> > that
> > + * they are unmapped when the driver is detached (i.e. no explicit cleanup 
> > is
> > + * required).
> > + *
> > + * Return:
> > + *  * 0 on success, or
> > + *  * Any error returned by devm_platform_ioremap_resource().
> > + */
> > +static int
> > +pvr_device_reg_init(struct pvr_device *pvr_dev)
> > +{
> > + struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> > + struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
> > + struct resource *regs_resource;
> > + void __iomem *regs;
> > + int err;
> > +
> > + pvr_dev->regs_resource = NULL;
> > + pvr_dev->regs = NULL;
> > +
> > + regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, 
> > _resource);
> > + if (IS_ERR(regs)) {
> > + err = PTR_ERR(regs);
> > + drm_err(drm_dev, "failed to ioremap gpu registers (err=%d)\n",
> > + err);
> > + return err;
> > + }
> > +
> > + pvr_dev->regs = regs;
> > + pvr_dev->regs_resource = regs_resource;
> 
> Do you actually need the resources somewhere?

This is needed when setting up the boot data for the MIPS firmware processor in
patch 11 (drm/imagination: Implement MIPS firmware processor and MMU support).
We'll move it to that patch instead.

> 
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * pvr_device_reg_fini() - Deinitialize kernel access to a PowerVR device's
> > + * control registers.
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * This is essentially a no-op, since pvr_device_reg_init() already 
> > ensures that
> > + * struct pvr_device->regs is unmapped when the device is detached. This
> > + * function just sets struct pvr_device->regs to %NULL.
> > + */
> > +static __always_inline void
> > +pvr_device_reg_fini(struct pvr_device *pvr_dev)
> > +{
> > + pvr_dev->regs = NULL;
> 
> But if you do, I guess clearing the regs_resource pointer would be nice too.

pvr_device_reg_fini() will be gone in the next version.

> 
> > +}
> > +
> > +/**
> > + * pvr_device_clk_init() - Initialize clocks required by a PowerVR device
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
> > + * struct pvr_device->mem_clk.
> > + *
> > + * Three clocks are required by the PowerVR device: core, sys and mem. On
> > + * return, this function guarantees that the clocks are in one of the 
> > following
> > + * states:
> > + *
> > + *  * All successfully initialized,
> > + *  * Core errored, sys and mem uninitialized,
> > + *  * Core deinitialized, sys errored, mem uninitialized, or
> > + *  * Core and sys deinitialized, mem errored.
> > + *
> > + * Return:
> > + *  * 0 on success,
> > + *  * Any error returned by devm_clk_get(), or
> > + *  * Any error returned by clk_prepare_enable().
> > + */
> > +static int pvr_device_clk_init(struct pvr_device *pvr_dev)
> > +{
> > + struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> > + struct clk *core_clk;
> > + struct clk *sys_clk;
> 

Re: [PATCH v3 05/17] drm/imagination: Get GPU resources

2023-07-07 Thread Maxime Ripard
On Tue, Jun 13, 2023 at 03:47:48PM +0100, Sarah Walker wrote:
> Acquire clock, regulator and register resources, and enable/map as
> appropriate.
>
> Signed-off-by: Sarah Walker 
> ---
>  drivers/gpu/drm/imagination/Makefile |   1 +
>  drivers/gpu/drm/imagination/pvr_device.c | 271 +++
>  drivers/gpu/drm/imagination/pvr_device.h | 214 ++
>  drivers/gpu/drm/imagination/pvr_drv.c|  11 +-
>  4 files changed, 496 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/imagination/pvr_device.c
>
> diff --git a/drivers/gpu/drm/imagination/Makefile 
> b/drivers/gpu/drm/imagination/Makefile
> index 62ccf0ccbd51..186f920d615b 100644
> --- a/drivers/gpu/drm/imagination/Makefile
> +++ b/drivers/gpu/drm/imagination/Makefile
> @@ -4,6 +4,7 @@
>  subdir-ccflags-y := -I$(srctree)/$(src)
>
>  powervr-y := \
> + pvr_device.o \
>   pvr_drv.o \
>
>  obj-$(CONFIG_DRM_POWERVR) += powervr.o
> diff --git a/drivers/gpu/drm/imagination/pvr_device.c 
> b/drivers/gpu/drm/imagination/pvr_device.c
> new file mode 100644
> index ..790c36cebec1
> --- /dev/null
> +++ b/drivers/gpu/drm/imagination/pvr_device.c
> @@ -0,0 +1,271 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +/* Copyright (c) 2022 Imagination Technologies Ltd. */
> +
> +#include "pvr_device.h"
> +
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +/**
> + * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
> + * control registers.
> + * @pvr_dev: Target PowerVR device.
> + *
> + * Sets struct pvr_device->regs.
> + *
> + * This method of mapping the device control registers into memory ensures 
> that
> + * they are unmapped when the driver is detached (i.e. no explicit cleanup is
> + * required).
> + *
> + * Return:
> + *  * 0 on success, or
> + *  * Any error returned by devm_platform_ioremap_resource().
> + */
> +static int
> +pvr_device_reg_init(struct pvr_device *pvr_dev)
> +{
> + struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> + struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
> + struct resource *regs_resource;
> + void __iomem *regs;
> + int err;
> +
> + pvr_dev->regs_resource = NULL;
> + pvr_dev->regs = NULL;
> +
> + regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, 
> _resource);
> + if (IS_ERR(regs)) {
> + err = PTR_ERR(regs);
> + drm_err(drm_dev, "failed to ioremap gpu registers (err=%d)\n",
> + err);
> + return err;
> + }
> +
> + pvr_dev->regs = regs;
> + pvr_dev->regs_resource = regs_resource;

Do you actually need the resources somewhere?

> +
> + return 0;
> +}
> +
> +/**
> + * pvr_device_reg_fini() - Deinitialize kernel access to a PowerVR device's
> + * control registers.
> + * @pvr_dev: Target PowerVR device.
> + *
> + * This is essentially a no-op, since pvr_device_reg_init() already ensures 
> that
> + * struct pvr_device->regs is unmapped when the device is detached. This
> + * function just sets struct pvr_device->regs to %NULL.
> + */
> +static __always_inline void
> +pvr_device_reg_fini(struct pvr_device *pvr_dev)
> +{
> + pvr_dev->regs = NULL;

But if you do, I guess clearing the regs_resource pointer would be nice too.

> +}
> +
> +/**
> + * pvr_device_clk_init() - Initialize clocks required by a PowerVR device
> + * @pvr_dev: Target PowerVR device.
> + *
> + * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
> + * struct pvr_device->mem_clk.
> + *
> + * Three clocks are required by the PowerVR device: core, sys and mem. On
> + * return, this function guarantees that the clocks are in one of the 
> following
> + * states:
> + *
> + *  * All successfully initialized,
> + *  * Core errored, sys and mem uninitialized,
> + *  * Core deinitialized, sys errored, mem uninitialized, or
> + *  * Core and sys deinitialized, mem errored.
> + *
> + * Return:
> + *  * 0 on success,
> + *  * Any error returned by devm_clk_get(), or
> + *  * Any error returned by clk_prepare_enable().
> + */
> +static int pvr_device_clk_init(struct pvr_device *pvr_dev)
> +{
> + struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> + struct clk *core_clk;
> + struct clk *sys_clk;
> + struct clk *mem_clk;
> + int err;
> +
> + pvr_dev->core_clk = NULL;
> + pvr_dev->sys_clk = NULL;
> + pvr_dev->mem_clk = NULL;
> +
> + core_clk = devm_clk_get(drm_dev->dev, "core");
> + if (IS_ERR(core_clk)) {
> + err = PTR_ERR(core_clk);
> + drm_err(drm_dev, "failed to get core clock (err=%d)\n", err);
> + goto err_out;
> + }
> +
> + sys_clk = devm_clk_get(drm_dev->dev, "sys");
> + if (IS_ERR(sys_clk))
> + sys_clk = NULL;
> +
> + mem_clk = devm_clk_get(drm_dev->dev, "mem");
> + if (IS_ERR(mem_clk))
> + 

Re: [PATCH v3 05/17] drm/imagination: Get GPU resources

2023-06-16 Thread Frank Binns
Hi Andrew,

On Tue, 2023-06-13 at 13:12 -0500, Andrew Davis wrote:
> On 6/13/23 9:47 AM, Sarah Walker wrote:
> > Acquire clock, regulator and register resources, and enable/map as
> > appropriate.
> > 
> > Signed-off-by: Sarah Walker 
> > ---
> >   drivers/gpu/drm/imagination/Makefile |   1 +
> >   drivers/gpu/drm/imagination/pvr_device.c | 271 +++
> >   drivers/gpu/drm/imagination/pvr_device.h | 214 ++
> >   drivers/gpu/drm/imagination/pvr_drv.c|  11 +-
> >   4 files changed, 496 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/gpu/drm/imagination/pvr_device.c
> > 
> > diff --git a/drivers/gpu/drm/imagination/Makefile 
> > b/drivers/gpu/drm/imagination/Makefile
> > index 62ccf0ccbd51..186f920d615b 100644
> > --- a/drivers/gpu/drm/imagination/Makefile
> > +++ b/drivers/gpu/drm/imagination/Makefile
> > @@ -4,6 +4,7 @@
> >   subdir-ccflags-y := -I$(srctree)/$(src)
> >   
> >   powervr-y := \
> > +   pvr_device.o \
> > pvr_drv.o \
> >   
> >   obj-$(CONFIG_DRM_POWERVR) += powervr.o
> > diff --git a/drivers/gpu/drm/imagination/pvr_device.c 
> > b/drivers/gpu/drm/imagination/pvr_device.c
> > new file mode 100644
> > index ..790c36cebec1
> > --- /dev/null
> > +++ b/drivers/gpu/drm/imagination/pvr_device.c
> > @@ -0,0 +1,271 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +/* Copyright (c) 2022 Imagination Technologies Ltd. */
> > +
> > +#include "pvr_device.h"
> > +
> > +#include 
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +/**
> > + * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
> > + * control registers.
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * Sets struct pvr_device->regs.
> > + *
> > + * This method of mapping the device control registers into memory ensures 
> > that
> > + * they are unmapped when the driver is detached (i.e. no explicit cleanup 
> > is
> > + * required).
> > + *
> > + * Return:
> > + *  * 0 on success, or
> > + *  * Any error returned by devm_platform_ioremap_resource().
> > + */
> > +static int
> > +pvr_device_reg_init(struct pvr_device *pvr_dev)
> > +{
> > +   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> > +   struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
> > +   struct resource *regs_resource;
> > +   void __iomem *regs;
> > +   int err;
> > +
> > +   pvr_dev->regs_resource = NULL;
> > +   pvr_dev->regs = NULL;
> > +
> > +   regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, 
> > _resource);
> > +   if (IS_ERR(regs)) {
> > +   err = PTR_ERR(regs);
> > +   drm_err(drm_dev, "failed to ioremap gpu registers (err=%d)\n",
> > +   err);
> > +   return err;
> > +   }
> > +
> > +   pvr_dev->regs = regs;
> > +   pvr_dev->regs_resource = regs_resource;
> > +
> > +   return 0;
> > +}
> > +
> > +/**
> > + * pvr_device_reg_fini() - Deinitialize kernel access to a PowerVR device's
> > + * control registers.
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * This is essentially a no-op, since pvr_device_reg_init() already 
> > ensures that
> > + * struct pvr_device->regs is unmapped when the device is detached. This
> > + * function just sets struct pvr_device->regs to %NULL.
> > + */
> > +static __always_inline void
> > +pvr_device_reg_fini(struct pvr_device *pvr_dev)
> > +{
> > +   pvr_dev->regs = NULL;
> 
> This function isn't needed, kinda defeats the purpose of using devm_*()
> if you go an manually have no-op functions to unwind it..

Thank you for the suggestions. We'll fix this, as well as your other suggestions
below.

Thanks
Frank

> 
> > +}
> > +
> > +/**
> > + * pvr_device_clk_init() - Initialize clocks required by a PowerVR device
> > + * @pvr_dev: Target PowerVR device.
> > + *
> > + * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
> > + * struct pvr_device->mem_clk.
> > + *
> > + * Three clocks are required by the PowerVR device: core, sys and mem. On
> > + * return, this function guarantees that the clocks are in one of the 
> > following
> > + * states:
> > + *
> > + *  * All successfully initialized,
> > + *  * Core errored, sys and mem uninitialized,
> > + *  * Core deinitialized, sys errored, mem uninitialized, or
> > + *  * Core and sys deinitialized, mem errored.
> > + *
> > + * Return:
> > + *  * 0 on success,
> > + *  * Any error returned by devm_clk_get(), or
> > + *  * Any error returned by clk_prepare_enable().
> > + */
> > +static int pvr_device_clk_init(struct pvr_device *pvr_dev)
> > +{
> > +   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> > +   struct clk *core_clk;
> > +   struct clk *sys_clk;
> > +   struct clk *mem_clk;
> > +   int err;
> > +
> > +   pvr_dev->core_clk = NULL;
> > +   pvr_dev->sys_clk = NULL;
> > +   pvr_dev->mem_clk = NULL;
> 
> You could NULL these out on the error path, but is 

Re: [PATCH v3 05/17] drm/imagination: Get GPU resources

2023-06-13 Thread Andrew Davis

On 6/13/23 9:47 AM, Sarah Walker wrote:

Acquire clock, regulator and register resources, and enable/map as
appropriate.

Signed-off-by: Sarah Walker 
---
  drivers/gpu/drm/imagination/Makefile |   1 +
  drivers/gpu/drm/imagination/pvr_device.c | 271 +++
  drivers/gpu/drm/imagination/pvr_device.h | 214 ++
  drivers/gpu/drm/imagination/pvr_drv.c|  11 +-
  4 files changed, 496 insertions(+), 1 deletion(-)
  create mode 100644 drivers/gpu/drm/imagination/pvr_device.c

diff --git a/drivers/gpu/drm/imagination/Makefile 
b/drivers/gpu/drm/imagination/Makefile
index 62ccf0ccbd51..186f920d615b 100644
--- a/drivers/gpu/drm/imagination/Makefile
+++ b/drivers/gpu/drm/imagination/Makefile
@@ -4,6 +4,7 @@
  subdir-ccflags-y := -I$(srctree)/$(src)
  
  powervr-y := \

+   pvr_device.o \
pvr_drv.o \
  
  obj-$(CONFIG_DRM_POWERVR) += powervr.o

diff --git a/drivers/gpu/drm/imagination/pvr_device.c 
b/drivers/gpu/drm/imagination/pvr_device.c
new file mode 100644
index ..790c36cebec1
--- /dev/null
+++ b/drivers/gpu/drm/imagination/pvr_device.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/* Copyright (c) 2022 Imagination Technologies Ltd. */
+
+#include "pvr_device.h"
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
+ * control registers.
+ * @pvr_dev: Target PowerVR device.
+ *
+ * Sets struct pvr_device->regs.
+ *
+ * This method of mapping the device control registers into memory ensures that
+ * they are unmapped when the driver is detached (i.e. no explicit cleanup is
+ * required).
+ *
+ * Return:
+ *  * 0 on success, or
+ *  * Any error returned by devm_platform_ioremap_resource().
+ */
+static int
+pvr_device_reg_init(struct pvr_device *pvr_dev)
+{
+   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
+   struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
+   struct resource *regs_resource;
+   void __iomem *regs;
+   int err;
+
+   pvr_dev->regs_resource = NULL;
+   pvr_dev->regs = NULL;
+
+   regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, 
_resource);
+   if (IS_ERR(regs)) {
+   err = PTR_ERR(regs);
+   drm_err(drm_dev, "failed to ioremap gpu registers (err=%d)\n",
+   err);
+   return err;
+   }
+
+   pvr_dev->regs = regs;
+   pvr_dev->regs_resource = regs_resource;
+
+   return 0;
+}
+
+/**
+ * pvr_device_reg_fini() - Deinitialize kernel access to a PowerVR device's
+ * control registers.
+ * @pvr_dev: Target PowerVR device.
+ *
+ * This is essentially a no-op, since pvr_device_reg_init() already ensures 
that
+ * struct pvr_device->regs is unmapped when the device is detached. This
+ * function just sets struct pvr_device->regs to %NULL.
+ */
+static __always_inline void
+pvr_device_reg_fini(struct pvr_device *pvr_dev)
+{
+   pvr_dev->regs = NULL;


This function isn't needed, kinda defeats the purpose of using devm_*()
if you go an manually have no-op functions to unwind it..


+}
+
+/**
+ * pvr_device_clk_init() - Initialize clocks required by a PowerVR device
+ * @pvr_dev: Target PowerVR device.
+ *
+ * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
+ * struct pvr_device->mem_clk.
+ *
+ * Three clocks are required by the PowerVR device: core, sys and mem. On
+ * return, this function guarantees that the clocks are in one of the following
+ * states:
+ *
+ *  * All successfully initialized,
+ *  * Core errored, sys and mem uninitialized,
+ *  * Core deinitialized, sys errored, mem uninitialized, or
+ *  * Core and sys deinitialized, mem errored.
+ *
+ * Return:
+ *  * 0 on success,
+ *  * Any error returned by devm_clk_get(), or
+ *  * Any error returned by clk_prepare_enable().
+ */
+static int pvr_device_clk_init(struct pvr_device *pvr_dev)
+{
+   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
+   struct clk *core_clk;
+   struct clk *sys_clk;
+   struct clk *mem_clk;
+   int err;
+
+   pvr_dev->core_clk = NULL;
+   pvr_dev->sys_clk = NULL;
+   pvr_dev->mem_clk = NULL;


You could NULL these out on the error path, but is that even needed, looks
like if this functions fails we bail on the whole init where this is all
deallocated (plus NULL'd out again in that path).


+
+   core_clk = devm_clk_get(drm_dev->dev, "core");
+   if (IS_ERR(core_clk)) {
+   err = PTR_ERR(core_clk);
+   drm_err(drm_dev, "failed to get core clock (err=%d)\n", err);
+   goto err_out;
+   }
+
+   sys_clk = devm_clk_get(drm_dev->dev, "sys");
+   if (IS_ERR(sys_clk))
+   sys_clk = NULL;
+
+   mem_clk = devm_clk_get(drm_dev->dev, "mem");
+   if (IS_ERR(mem_clk))
+   mem_clk = NULL;
+
+   err = 

[PATCH v3 05/17] drm/imagination: Get GPU resources

2023-06-13 Thread Sarah Walker
Acquire clock, regulator and register resources, and enable/map as
appropriate.

Signed-off-by: Sarah Walker 
---
 drivers/gpu/drm/imagination/Makefile |   1 +
 drivers/gpu/drm/imagination/pvr_device.c | 271 +++
 drivers/gpu/drm/imagination/pvr_device.h | 214 ++
 drivers/gpu/drm/imagination/pvr_drv.c|  11 +-
 4 files changed, 496 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/imagination/pvr_device.c

diff --git a/drivers/gpu/drm/imagination/Makefile 
b/drivers/gpu/drm/imagination/Makefile
index 62ccf0ccbd51..186f920d615b 100644
--- a/drivers/gpu/drm/imagination/Makefile
+++ b/drivers/gpu/drm/imagination/Makefile
@@ -4,6 +4,7 @@
 subdir-ccflags-y := -I$(srctree)/$(src)
 
 powervr-y := \
+   pvr_device.o \
pvr_drv.o \
 
 obj-$(CONFIG_DRM_POWERVR) += powervr.o
diff --git a/drivers/gpu/drm/imagination/pvr_device.c 
b/drivers/gpu/drm/imagination/pvr_device.c
new file mode 100644
index ..790c36cebec1
--- /dev/null
+++ b/drivers/gpu/drm/imagination/pvr_device.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/* Copyright (c) 2022 Imagination Technologies Ltd. */
+
+#include "pvr_device.h"
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
+ * control registers.
+ * @pvr_dev: Target PowerVR device.
+ *
+ * Sets struct pvr_device->regs.
+ *
+ * This method of mapping the device control registers into memory ensures that
+ * they are unmapped when the driver is detached (i.e. no explicit cleanup is
+ * required).
+ *
+ * Return:
+ *  * 0 on success, or
+ *  * Any error returned by devm_platform_ioremap_resource().
+ */
+static int
+pvr_device_reg_init(struct pvr_device *pvr_dev)
+{
+   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
+   struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
+   struct resource *regs_resource;
+   void __iomem *regs;
+   int err;
+
+   pvr_dev->regs_resource = NULL;
+   pvr_dev->regs = NULL;
+
+   regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, 
_resource);
+   if (IS_ERR(regs)) {
+   err = PTR_ERR(regs);
+   drm_err(drm_dev, "failed to ioremap gpu registers (err=%d)\n",
+   err);
+   return err;
+   }
+
+   pvr_dev->regs = regs;
+   pvr_dev->regs_resource = regs_resource;
+
+   return 0;
+}
+
+/**
+ * pvr_device_reg_fini() - Deinitialize kernel access to a PowerVR device's
+ * control registers.
+ * @pvr_dev: Target PowerVR device.
+ *
+ * This is essentially a no-op, since pvr_device_reg_init() already ensures 
that
+ * struct pvr_device->regs is unmapped when the device is detached. This
+ * function just sets struct pvr_device->regs to %NULL.
+ */
+static __always_inline void
+pvr_device_reg_fini(struct pvr_device *pvr_dev)
+{
+   pvr_dev->regs = NULL;
+}
+
+/**
+ * pvr_device_clk_init() - Initialize clocks required by a PowerVR device
+ * @pvr_dev: Target PowerVR device.
+ *
+ * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
+ * struct pvr_device->mem_clk.
+ *
+ * Three clocks are required by the PowerVR device: core, sys and mem. On
+ * return, this function guarantees that the clocks are in one of the following
+ * states:
+ *
+ *  * All successfully initialized,
+ *  * Core errored, sys and mem uninitialized,
+ *  * Core deinitialized, sys errored, mem uninitialized, or
+ *  * Core and sys deinitialized, mem errored.
+ *
+ * Return:
+ *  * 0 on success,
+ *  * Any error returned by devm_clk_get(), or
+ *  * Any error returned by clk_prepare_enable().
+ */
+static int pvr_device_clk_init(struct pvr_device *pvr_dev)
+{
+   struct drm_device *drm_dev = from_pvr_device(pvr_dev);
+   struct clk *core_clk;
+   struct clk *sys_clk;
+   struct clk *mem_clk;
+   int err;
+
+   pvr_dev->core_clk = NULL;
+   pvr_dev->sys_clk = NULL;
+   pvr_dev->mem_clk = NULL;
+
+   core_clk = devm_clk_get(drm_dev->dev, "core");
+   if (IS_ERR(core_clk)) {
+   err = PTR_ERR(core_clk);
+   drm_err(drm_dev, "failed to get core clock (err=%d)\n", err);
+   goto err_out;
+   }
+
+   sys_clk = devm_clk_get(drm_dev->dev, "sys");
+   if (IS_ERR(sys_clk))
+   sys_clk = NULL;
+
+   mem_clk = devm_clk_get(drm_dev->dev, "mem");
+   if (IS_ERR(mem_clk))
+   mem_clk = NULL;
+
+   err = clk_prepare(core_clk);
+   if (err)
+   goto err_out;
+
+   if (sys_clk) {
+   err = clk_prepare(sys_clk);
+   if (err)
+   goto err_deinit_core_clk;
+   }
+
+   if (mem_clk) {
+   err = clk_prepare(mem_clk);
+   if (err)
+   goto err_deinit_sys_clk;
+   }
+
+