diff --git a/drivers/gpu/drm/panthor-rs/gpu.rs b/drivers/gpu/drm/panthor-rs/gpu.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3600379e5aca2234e59a90d9815c61d2fa965b16
--- /dev/null
+++ b/drivers/gpu/drm/panthor-rs/gpu.rs
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+
+use core::ffi;
+
+use kernel::bindings;
+use kernel::error::Result;
+use kernel::pr_err;
+use kernel::pr_info;
+use kernel::sync::Arc;
+use kernel::types::ForeignOwnable;
+
+use crate::driver::PanthorData;
+use crate::regs::GpuIdV;
+
+/// Define a GPU model. A GPU product can be uniquely identified by a
+/// combination of the major architecture version and the major product version.
+struct PanthorModel {
+    name: &'static str,
+    arch_major: u32,
+    product_major: u32,
+}
+
+const GPU_MODELS: &[PanthorModel] = &[PanthorModel {
+    name: "g610",
+    arch_major: 10,
+    product_major: 7,
+}];
+
+fn read_gpu_info(ptdev: *mut bindings::panthor_device) -> Result {
+    // Safety: `ptdev` is a valid pointer to a `panthor_device` instance. The
+    // `rust_data` field was populated using `into_foreign` in `driver.rs`.
+    let data: Arc<PanthorData> = unsafe { Arc::from_foreign((*ptdev).rust_data) };
+    let iomem = &data.regs.iomem;
+
+    let gpu_id = data.regs.gpu_id.read_u32(iomem)?;
+    let csf_id = data.regs.csf_id.read_u32(iomem)?;
+    let gpu_rev = data.regs.rev_idr.read(iomem)?;
+    let core_features = data.regs.core_features.read_u32(iomem)?;
+    let l2_features = data.regs.l2_features.read_u32(iomem)?;
+    let tiler_features = data.regs.tiler_features.read_u32(iomem)?;
+    let mem_features = data.regs.mem_features.read_u32(iomem)?;
+    let mmu_features = data.regs.mmu_features.read_u32(iomem)?;
+    let thread_features = data.regs.thread_features.read_u32(iomem)?;
+    let max_threads = data.regs.max_threads.read(iomem)?;
+    let thread_max_workgroup_size = data.regs.max_workgroup_size.read(iomem)?;
+    let thread_max_barrier_size = data.regs.max_barrier_size.read(iomem)?;
+    let coherency_features = data.regs.coherency_features.read_u32(iomem)?;
+    let texture_features = data.regs.texture_features.read(iomem)?;
+
+    let as_present = data.regs.as_present.read(iomem)?;
+    let shader_present = data.regs.shader_present.read(iomem)?;
+    let tiler_present = data.regs.tiler_present.read(iomem)?;
+    let l2_present = data.regs.l2_present.read(iomem)?;
+
+    // Safety: we trust the pointer passed from the C side.
+    unsafe {
+        (*ptdev).gpu_info.gpu_id = gpu_id;
+        (*ptdev).gpu_info.csf_id = csf_id;
+        (*ptdev).gpu_info.gpu_rev = gpu_rev;
+        (*ptdev).gpu_info.core_features = core_features;
+        (*ptdev).gpu_info.l2_features = l2_features;
+        (*ptdev).gpu_info.tiler_features = tiler_features;
+        (*ptdev).gpu_info.mem_features = mem_features;
+        (*ptdev).gpu_info.mmu_features = mmu_features;
+        (*ptdev).gpu_info.thread_features = thread_features;
+        (*ptdev).gpu_info.max_threads = max_threads;
+        (*ptdev).gpu_info.thread_max_workgroup_size = thread_max_workgroup_size;
+        (*ptdev).gpu_info.thread_max_barrier_size = thread_max_barrier_size;
+        (*ptdev).gpu_info.coherency_features = coherency_features;
+        for i in 0..4 {
+            (*ptdev).gpu_info.texture_features[i] = texture_features.value(i);
+        }
+    }
+
+    let GpuIdV {
+        arch_major,
+        arch_minor,
+        arch_rev,
+        prod_major,
+        ver_major,
+        ver_minor,
+        ver_status,
+    } = data.regs.gpu_id.read(iomem)?;
+
+    let model_name = GPU_MODELS
+        .iter()
+        .find(|model| model.arch_major == arch_major && model.product_major == prod_major)
+        .map_or("unknown", |model| model.name);
+
+    pr_info!(
+        "mali-{} id 0x{:x} major 0x{:x} minor 0x{:x} status 0x{:x}",
+        model_name,
+        prod_major,
+        arch_major,
+        arch_minor,
+        ver_status
+    );
+
+    pr_info!(
+        "Features: L2:{:#x} Tiler:{:#x} Mem:{:#x} MMU:{:#x} AS:{:#x}",
+        l2_features,
+        tiler_features,
+        mem_features,
+        mmu_features,
+        as_present
+    );
+
+    pr_info!(
+        "shader_present=0x{:0x} l2_present=0x{:0x} tiler_present=0x{:0x}",
+        shader_present,
+        l2_present,
+        tiler_present
+    );
+
+    Ok(())
+}
+
+#[no_mangle]
+pub(crate) extern "C" fn panthor_gpu_init_info(ptdev: *mut bindings::panthor_device) -> ffi::c_int {
+    match read_gpu_info(ptdev) {
+        Ok(_) => 0,
+        Err(e) => {
+            pr_err!("Failed to read GPU info: {:?}", e);
+            e.to_errno()
+        }
+    }
+}
diff --git a/drivers/gpu/drm/panthor-rs/panthor.rs b/drivers/gpu/drm/panthor-rs/panthor.rs
index 7734b4b1e20dfba90fce963b55ac86c3c7bbece3..06588c55f1681b4b81b9a5a0364532d435eec51e 100644
--- a/drivers/gpu/drm/panthor-rs/panthor.rs
+++ b/drivers/gpu/drm/panthor-rs/panthor.rs
@@ -10,6 +10,7 @@
 mod driver;
 mod file;
 mod gem;
+mod gpu;
 mod regs;
 
 use kernel::module_platform_driver;
diff --git a/drivers/gpu/drm/panthor-rs/panthor_device.h b/drivers/gpu/drm/panthor-rs/panthor_device.h
index cfc832110b3ecf654cb298ccc7c2de1c80eec024..8ac8624016ac6c6d707b8a1a89fefc5b4d8b26d2 100644
--- a/drivers/gpu/drm/panthor-rs/panthor_device.h
+++ b/drivers/gpu/drm/panthor-rs/panthor_device.h
@@ -163,6 +163,12 @@ struct panthor_device {
 		 */
 		struct page *dummy_latest_flush;
 	} pm;
+	
+	/**
+	* @rust_data: An opaque pointer to data interpreted by the Rust layer of
+	* the driver.
+	*/
+	void *rust_data;
 };
 
 /**
diff --git a/drivers/gpu/drm/panthor-rs/panthor_gpu.c b/drivers/gpu/drm/panthor-rs/panthor_gpu.c
index ae29403468d879eaaaf561dd3019af205c8480e7..adafb70d8882cfb4870972d212a8b6d7bcf56cc0 100644
--- a/drivers/gpu/drm/panthor-rs/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor-rs/panthor_gpu.c
@@ -66,81 +66,14 @@ struct panthor_model {
 	.product_major = _product_major,			\
 }
 
-static const struct panthor_model gpu_models[] = {
-	GPU_MODEL(g610, 10, 7),
-	{},
-};
-
 #define GPU_INTERRUPTS_MASK	\
 	(GPU_IRQ_FAULT | \
 	 GPU_IRQ_PROTM_FAULT | \
 	 GPU_IRQ_RESET_COMPLETED | \
 	 GPU_IRQ_CLEAN_CACHES_COMPLETED)
 
-static void panthor_gpu_init_info(struct panthor_device *ptdev)
-{
-	const struct panthor_model *model;
-	u32 arch_major, product_major;
-	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.core_features = gpu_read(ptdev, GPU_CORE_FEATURES);
-	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;
-
-	arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
-	product_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
-	major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
-	minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
-	status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
-
-	for (model = gpu_models; model->name; model++) {
-		if (model->arch_major == arch_major &&
-		    model->product_major == product_major)
-			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:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#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);
-}
+/* defined in gpu.rs */
+void panthor_gpu_init_info(struct panthor_device *ptdev);
 
 static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status)
 {