From 7c86950368223dd62f1b664a0014826572cb3caa Mon Sep 17 00:00:00 2001 From: Magnus Wendt Date: Wed, 9 Feb 2011 12:40:30 +0100 Subject: mali: Add mali400 kernel module Suppress previous mali symlink to external driver/gpu/mali/mali400ko folder. Add mali400ko from tag SNOWBALL_BSP_111012_2.1. Signed-off-by: John Fredriksson Change-Id: I82d24760247f8a5a5b786fc10187f32d9048f9a9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33811 Reviewed-by: Philippe LANGLAIS --- drivers/gpu/Makefile | 1 + drivers/gpu/mali/Kconfig | 15 + drivers/gpu/mali/Makefile | 10 + drivers/gpu/mali/mali400ko/.gitignore | 32 + drivers/gpu/mali/mali400ko/Makefile | 102 + .../driver/include/ump/ump_kernel_interface.h | 236 ++ .../include/ump/ump_kernel_interface_ref_drv.h | 31 + .../driver/include/ump/ump_kernel_platform.h | 48 + .../mali400ko/driver/src/devicedrv/mali/Makefile | 346 +++ .../driver/src/devicedrv/mali/Makefile.common | 56 + .../driver/src/devicedrv/mali/Makefile.platform | 45 + .../devicedrv/mali/arch-pb-virtex5-m300/config.h | 87 + .../mali/arch-pb-virtex5-m400-1-direct/config.h | 94 + .../mali/arch-pb-virtex5-m400-1-pmu/config.h | 87 + .../devicedrv/mali/arch-pb-virtex5-m400-1/config.h | 79 + .../devicedrv/mali/arch-pb-virtex5-m400-2/config.h | 93 + .../devicedrv/mali/arch-pb-virtex5-m400-3/config.h | 107 + .../devicedrv/mali/arch-pb-virtex5-m400-4/config.h | 121 + .../driver/src/devicedrv/mali/arch-ux500/config.h | 109 + .../devicedrv/mali/common/mali_block_allocator.c | 370 +++ .../devicedrv/mali/common/mali_block_allocator.h | 18 + .../src/devicedrv/mali/common/mali_kernel_GP2.c | 1418 ++++++++++ .../devicedrv/mali/common/mali_kernel_MALI200.c | 1187 ++++++++ .../src/devicedrv/mali/common/mali_kernel_common.h | 171 ++ .../src/devicedrv/mali/common/mali_kernel_core.c | 892 ++++++ .../src/devicedrv/mali/common/mali_kernel_core.h | 134 + .../mali/common/mali_kernel_descriptor_mapping.c | 183 ++ .../mali/common/mali_kernel_descriptor_mapping.h | 99 + .../src/devicedrv/mali/common/mali_kernel_gp.h | 21 + .../devicedrv/mali/common/mali_kernel_l2_cache.c | 515 ++++ .../devicedrv/mali/common/mali_kernel_l2_cache.h | 25 + .../src/devicedrv/mali/common/mali_kernel_mem.h | 17 + .../devicedrv/mali/common/mali_kernel_mem_buddy.c | 1425 ++++++++++ .../devicedrv/mali/common/mali_kernel_mem_mmu.c | 2924 ++++++++++++++++++++ .../devicedrv/mali/common/mali_kernel_mem_mmu.h | 66 + .../src/devicedrv/mali/common/mali_kernel_mem_os.c | 309 +++ .../src/devicedrv/mali/common/mali_kernel_mem_os.h | 37 + .../mali/common/mali_kernel_memory_engine.c | 348 +++ .../mali/common/mali_kernel_memory_engine.h | 145 + .../src/devicedrv/mali/common/mali_kernel_pp.h | 21 + .../devicedrv/mali/common/mali_kernel_profiling.c | 240 ++ .../devicedrv/mali/common/mali_kernel_profiling.h | 46 + .../devicedrv/mali/common/mali_kernel_rendercore.c | 2032 ++++++++++++++ .../devicedrv/mali/common/mali_kernel_rendercore.h | 355 +++ .../mali/common/mali_kernel_session_manager.h | 19 + .../devicedrv/mali/common/mali_kernel_subsystem.h | 107 + .../mali/common/mali_kernel_utilization.c | 207 ++ .../mali/common/mali_kernel_utilization.h | 44 + .../src/devicedrv/mali/common/mali_kernel_vsync.c | 29 + .../driver/src/devicedrv/mali/common/mali_osk.h | 1620 +++++++++++ .../src/devicedrv/mali/common/mali_osk_bitops.h | 166 ++ .../src/devicedrv/mali/common/mali_osk_list.h | 184 ++ .../src/devicedrv/mali/common/mali_osk_mali.h | 252 ++ .../src/devicedrv/mali/common/mali_uk_types.h | 1148 ++++++++ .../driver/src/devicedrv/mali/common/mali_ukk.h | 710 +++++ .../src/devicedrv/mali/common/pmm/mali_pmm.c | 921 ++++++ .../src/devicedrv/mali/common/pmm/mali_pmm.h | 323 +++ .../devicedrv/mali/common/pmm/mali_pmm_policy.c | 243 ++ .../devicedrv/mali/common/pmm/mali_pmm_policy.h | 155 ++ .../mali/common/pmm/mali_pmm_policy_alwayson.c | 81 + .../mali/common/pmm/mali_pmm_policy_alwayson.h | 62 + .../mali/common/pmm/mali_pmm_policy_jobcontrol.c | 461 +++ .../mali/common/pmm/mali_pmm_policy_jobcontrol.h | 80 + .../src/devicedrv/mali/common/pmm/mali_pmm_state.c | 718 +++++ .../src/devicedrv/mali/common/pmm/mali_pmm_state.h | 296 ++ .../devicedrv/mali/common/pmm/mali_pmm_system.h | 66 + .../mali/linux/license/gpl/mali_kernel_license.h | 31 + .../mali/linux/mali_device_pause_resume.c | 82 + .../mali/linux/mali_device_pause_resume.h | 19 + .../src/devicedrv/mali/linux/mali_kernel_ioctl.h | 77 + .../src/devicedrv/mali/linux/mali_kernel_linux.c | 565 ++++ .../src/devicedrv/mali/linux/mali_kernel_linux.h | 29 + .../src/devicedrv/mali/linux/mali_kernel_pm.c | 785 ++++++ .../src/devicedrv/mali/linux/mali_kernel_pm.h | 19 + .../mali/linux/mali_linux_dvfs_pause_resume.c | 72 + .../src/devicedrv/mali/linux/mali_linux_pm.h | 57 + .../devicedrv/mali/linux/mali_linux_pm_testsuite.h | 37 + .../src/devicedrv/mali/linux/mali_osk_atomics.c | 55 + .../src/devicedrv/mali/linux/mali_osk_indir_mmap.c | 86 + .../src/devicedrv/mali/linux/mali_osk_indir_mmap.h | 48 + .../driver/src/devicedrv/mali/linux/mali_osk_irq.c | 228 ++ .../src/devicedrv/mali/linux/mali_osk_locks.c | 271 ++ .../devicedrv/mali/linux/mali_osk_low_level_mem.c | 578 ++++ .../src/devicedrv/mali/linux/mali_osk_mali.c | 98 + .../src/devicedrv/mali/linux/mali_osk_math.c | 22 + .../src/devicedrv/mali/linux/mali_osk_memory.c | 50 + .../src/devicedrv/mali/linux/mali_osk_misc.c | 51 + .../devicedrv/mali/linux/mali_osk_notification.c | 191 ++ .../driver/src/devicedrv/mali/linux/mali_osk_pm.c | 195 ++ .../src/devicedrv/mali/linux/mali_osk_specific.h | 32 + .../src/devicedrv/mali/linux/mali_osk_time.c | 51 + .../src/devicedrv/mali/linux/mali_osk_timers.c | 65 + .../src/devicedrv/mali/linux/mali_ukk_core.c | 142 + .../driver/src/devicedrv/mali/linux/mali_ukk_gp.c | 128 + .../driver/src/devicedrv/mali/linux/mali_ukk_mem.c | 336 +++ .../driver/src/devicedrv/mali/linux/mali_ukk_pp.c | 103 + .../src/devicedrv/mali/linux/mali_ukk_profiling.c | 135 + .../src/devicedrv/mali/linux/mali_ukk_vsync.c | 41 + .../src/devicedrv/mali/linux/mali_ukk_wrappers.h | 70 + .../mali/platform/default/mali_platform.c | 50 + .../mali/platform/mali-runtimepm/mali_platform.c | 61 + .../mali/platform/mali400-pmu/mali_platform.c | 388 +++ .../src/devicedrv/mali/platform/mali_platform.h | 100 + .../devicedrv/mali/platform/ux500/mali_platform.c | 190 ++ .../mali/platform/ux500/ump_kernel_api_hwmem.c | 159 ++ .../mali400ko/driver/src/devicedrv/mali/readme.txt | 28 + .../driver/src/devicedrv/mali/regs/mali_200_regs.h | 170 ++ .../driver/src/devicedrv/mali/regs/mali_gp_regs.h | 219 ++ .../mali/timestamp-arm11-cc/mali_timestamp.c | 13 + .../mali/timestamp-arm11-cc/mali_timestamp.h | 48 + .../mali/timestamp-default/mali_timestamp.c | 13 + .../mali/timestamp-default/mali_timestamp.h | 26 + .../mali400ko/driver/src/devicedrv/ump/Makefile | 115 + .../driver/src/devicedrv/ump/Makefile.common | 20 + .../src/devicedrv/ump/arch-pb-virtex5/config.h | 18 + .../src/devicedrv/ump/common/ump_kernel_api.c | 329 +++ .../src/devicedrv/ump/common/ump_kernel_common.c | 387 +++ .../src/devicedrv/ump/common/ump_kernel_common.h | 126 + .../ump/common/ump_kernel_descriptor_mapping.c | 166 ++ .../ump/common/ump_kernel_descriptor_mapping.h | 91 + .../ump/common/ump_kernel_memory_backend.h | 49 + .../src/devicedrv/ump/common/ump_kernel_ref_drv.c | 194 ++ .../src/devicedrv/ump/common/ump_kernel_types.h | 35 + .../driver/src/devicedrv/ump/common/ump_osk.h | 48 + .../driver/src/devicedrv/ump/common/ump_uk_types.h | 141 + .../driver/src/devicedrv/ump/common/ump_ukk.h | 51 + .../ump/linux/license/gpl/ump_kernel_license.h | 31 + .../driver/src/devicedrv/ump/linux/ump_ioctl.h | 49 + .../src/devicedrv/ump/linux/ump_kernel_linux.c | 409 +++ .../src/devicedrv/ump/linux/ump_kernel_linux.h | 18 + .../linux/ump_kernel_memory_backend_dedicated.c | 273 ++ .../linux/ump_kernel_memory_backend_dedicated.h | 23 + .../ump/linux/ump_kernel_memory_backend_os.c | 245 ++ .../ump/linux/ump_kernel_memory_backend_os.h | 23 + .../src/devicedrv/ump/linux/ump_memory_backend.c | 70 + .../src/devicedrv/ump/linux/ump_osk_atomics.c | 27 + .../devicedrv/ump/linux/ump_osk_low_level_mem.c | 243 ++ .../driver/src/devicedrv/ump/linux/ump_osk_misc.c | 37 + .../src/devicedrv/ump/linux/ump_ukk_ref_wrappers.c | 76 + .../src/devicedrv/ump/linux/ump_ukk_ref_wrappers.h | 35 + .../src/devicedrv/ump/linux/ump_ukk_wrappers.c | 173 ++ .../src/devicedrv/ump/linux/ump_ukk_wrappers.h | 41 + .../mali400ko/driver/src/devicedrv/ump/readme.txt | 17 + drivers/gpu/mali/mali400ko/mali.spec | 57 + drivers/gpu/mali/mali400ko/x11/mali_drm/README.txt | 24 + .../gpu/mali/mali400ko/x11/mali_drm/mali/Makefile | 17 + .../mali/mali400ko/x11/mali_drm/mali/mali_drv.c | 154 ++ .../mali/mali400ko/x11/mali_drm/mali/mali_drv.h | 25 + drivers/video/Kconfig | 2 + 149 files changed, 32642 insertions(+) create mode 100644 drivers/gpu/mali/Kconfig create mode 100644 drivers/gpu/mali/Makefile create mode 100644 drivers/gpu/mali/mali400ko/.gitignore create mode 100644 drivers/gpu/mali/mali400ko/Makefile create mode 100644 drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface.h create mode 100644 drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface_ref_drv.h create mode 100644 drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_platform.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.platform create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m300/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-direct/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-pmu/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-2/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-3/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-4/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-ux500/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_GP2.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_MALI200.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_common.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_gp.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_buddy.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_pp.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_session_manager.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_subsystem.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_vsync.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_bitops.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_list.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_mali.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_uk_types.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_ukk.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_system.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/license/gpl/mali_kernel_license.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_ioctl.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_dvfs_pause_resume.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm_testsuite.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_atomics.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_irq.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_locks.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_low_level_mem.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_mali.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_math.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_memory.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_misc.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_notification.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_pm.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_specific.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_time.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_timers.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_core.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_gp.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_mem.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_pp.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_profiling.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_vsync.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_wrappers.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/default/mali_platform.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali-runtimepm/mali_platform.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali400-pmu/mali_platform.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali_platform.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/readme.txt create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_200_regs.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_gp_regs.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/arch-pb-virtex5/config.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_api.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_memory_backend.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_ref_drv.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_types.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_osk.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_uk_types.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_ukk.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/license/gpl/ump_kernel_license.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ioctl.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_memory_backend.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_atomics.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_low_level_mem.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_misc.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.c create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.h create mode 100644 drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/readme.txt create mode 100644 drivers/gpu/mali/mali400ko/mali.spec create mode 100644 drivers/gpu/mali/mali400ko/x11/mali_drm/README.txt create mode 100644 drivers/gpu/mali/mali400ko/x11/mali_drm/mali/Makefile create mode 100644 drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c create mode 100644 drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.h diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index cc9277885dd..b2ae09b4cc7 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1,2 @@ obj-y += drm/ vga/ stub/ +obj-y += mali/ diff --git a/drivers/gpu/mali/Kconfig b/drivers/gpu/mali/Kconfig new file mode 100644 index 00000000000..0781bcbd854 --- /dev/null +++ b/drivers/gpu/mali/Kconfig @@ -0,0 +1,15 @@ +config GPU_MALI + tristate "ARM Mali 200/300/400 support" + depends on ARM + ---help--- + This enables support for the ARM Mali 200/300/400 family of GPUs. + Platform specific configuration is made in configuration files in the + drivers/gpu/mali/src folder + +config GPU_MALI_DEBUG + boolean "Debug mode (required for instrumentation)" + default y + depends on GPU_MALI + ---help--- + This enables debug prints in the mali device driver. Debug mode must be + enabled in order to use the intrumentation feature of the mali libraries. diff --git a/drivers/gpu/mali/Makefile b/drivers/gpu/mali/Makefile new file mode 100644 index 00000000000..93c95aca19a --- /dev/null +++ b/drivers/gpu/mali/Makefile @@ -0,0 +1,10 @@ +MALI_SUBFOLDER := mali400ko/driver/src/devicedrv/mali +MALIDRM_SUBFOLDER := mali400ko/x11/mali_drm/mali +MALI_FOLDER := $(srctree)/$(src)/$(MALI_SUBFOLDER) +ifeq ($(shell [ -d $(MALI_FOLDER) ] && echo "OK"), OK) +obj-$(CONFIG_GPU_MALI) += $(MALI_SUBFOLDER)/ +obj-y += $(MALIDRM_SUBFOLDER)/ +else +$(warning WARNING: mali: Could not find $(MALI_FOLDER) - mali device driver will not be built) +obj-n += ./ +endif diff --git a/drivers/gpu/mali/mali400ko/.gitignore b/drivers/gpu/mali/mali400ko/.gitignore new file mode 100644 index 00000000000..e2d66d55882 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/.gitignore @@ -0,0 +1,32 @@ +#symlinks created during build +driver/src/devicedrv/mali/arch +driver/bin +driver/devdrv +driver/lib + +#build output +driver/build +.tmp_versions +*.cmd +*.o +*.d +driver/src/devicedrv/linux/__mali_build_info.* +driver/src/devicedrv/linux/modules.order +driver/src/devicedrv/linux/Module.symvers +driver/src/devicedrv/mali/Module.symvers +driver/src/devicedrv/mali/__malidrv_build_info.c +driver/src/devicedrv/mali/mali.ko +driver/src/devicedrv/mali/mali.mod.c +driver/src/devicedrv/mali/modules.order +driver/out* +driver/src/shared/essl_compiler/src/shadergen_maligp2/shader_pieces.* + +out +kernel + +#temp files from editors and Eclipse project files +*~ +.cproject +.project +.settings + diff --git a/drivers/gpu/mali/mali400ko/Makefile b/drivers/gpu/mali/mali400ko/Makefile new file mode 100644 index 00000000000..9176f9f3795 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/Makefile @@ -0,0 +1,102 @@ +#This Makefile can be called to do an out-of-kernel build of tha mali kernel module. +#It is not used when mali is built from the kernel build system. + +CROSS_COMPILE ?= arm-eabi- + +ifeq ($(TARGET_PRODUCT),ste_u5500) +CONFIG_UX500_SOC_DB5500=y +export CONFIG_UX500_SOC_DB5500 +endif + +PWD:=$(shell pwd) + +#if KERNEL_BUILD_DIR parameter is not set, assume there is a symlink to the kernel +KERNEL_BUILD_DIR?=$(PWD)/kernel + +# android +ifneq (,$(findstring -android,$(PLATFORM))) +INSTALL_MOD_PATH?=$(ANDROID_OUT_TARGET_PRODUCT_DIRECTORY)/system +endif + +# lbp +ifneq (,$(findstring -linux,$(PLATFORM))) +PREFIX?=$(MMROOT)/linux/install/cortexA9-linux-gnu-href_v1 +INSTALL_MOD_PATH?=$(PREFIX) +endif + +#if still no kernel module path is set, install the modules in a local folder +ifeq (,$(INSTALL_MOD_PATH)) +INSTALL_MOD_PATH?=$(PWD)/out +endif + +KO_FLAGS=KDIR=$(KERNEL_BUILD_DIR) CROSS_COMPILE=$(CROSS_COMPILE) + +.PHONY: all entry check + +all: mali-devicedrv install-mali + +check: + @if [ ! -d $(KERNEL_BUILD_DIR) ] ; then echo "Error: provide a path to your linux kernel through KERNEL_BUILD_DIR"; exit 1 ; fi + +## Build ############### + +mali-devicedrv: check + $(MAKE) -C driver/src/devicedrv/mali $(KO_FLAGS) all + +ump-devicedrv: check + $(MAKE) -C driver/src/devicedrv/ump $(KO_FLAGS) all + +mali_drm-devicedrv: check + $(MAKE) -C x11/mali_drm/mali $(KO_FLAGS) all + +## Clean ############### + +clean-mali: + $(MAKE) clean -C driver/src/devicedrv/mali $(KO_FLAGS) + +clean-ump: + $(MAKE) clean -C driver/src/devicedrv/ump $(KO_FLAGS) + +clean-mali_drm: + $(MAKE) clean -C x11/mali_drm/mali $(KO_FLAGS) + +clean: clean-mali + +realclean: + rm -f driver/src/devicedrv/mali/common/*.o + rm -f driver/src/devicedrv/mali/linux/*.o + rm -f driver/src/devicedrv/mali/common/.*.cmd + rm -f driver/src/devicedrv/mali/linux/.*.cmd + rm -f driver/src/devicedrv/mali/.*.cmd + rm -f driver/src/devicedrv/mali/*.c + +distclean: realclean + +## Install ############# + +install: install-mali + +install-mali: mali-devicedrv + $(MAKE) -C $(KERNEL_BUILD_DIR) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) KERNELDIR=$(KERNEL_BUILD_DIR) INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) M=$(PWD)/driver/src/devicedrv/mali modules_install + +install-ump: ump-devicedrv + $(MAKE) -C $(KERNEL_BUILD_DIR) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) KERNELDIR=$(KERNEL_BUILD_DIR) INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) M=$(PWD)/driver/src/devicedrv/ump modules_install + +install-mali_drm: mali_drm-devicedrv + $(MAKE) -C $(KERNEL_BUILD_DIR) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) KERNELDIR=$(KERNEL_BUILD_DIR) INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) M=$(PWD)/x11/mali_drm/mali modules_install + + +printenv: + @echo "\033[0;44m" + @echo "ANDROID_OUT_TARGET_PRODUCT_DIRECTORY: " $(ANDROID_OUT_TARGET_PRODUCT_DIRECTORY) + @echo "ANDROID_BSP_ROOT: " $(ANDROID_BSP_ROOT) + @echo "KERNEL_BUILD_DIR: " $(KERNEL_BUILD_DIR) + @echo "MM_INSTALL_DIR: " $(MM_INSTALL_DIR) + @echo "PLATFORM: " $(PLATFORM) + @echo "PREFIX: " $(PREFIX) + @echo "MMROOT: " $(MMROOT) + @echo "INSTALL_MOD_PATH: " $(INSTALL_MOD_PATH) + @echo "FOO: " $(FOO) + @echo "VARIANT: " $(VARIANT) + @echo "\033[0m" + diff --git a/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface.h b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface.h new file mode 100644 index 00000000000..3bd928aefdb --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + * + * This file contains the kernel space part of the UMP API. + */ + +#ifndef __UMP_KERNEL_INTERFACE_H__ +#define __UMP_KERNEL_INTERFACE_H__ + + +/** @defgroup ump_kernel_space_api UMP Kernel Space API + * @{ */ + + +#include "ump_kernel_platform.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * External representation of a UMP handle in kernel space. + */ +typedef void * ump_dd_handle; + +/** + * Typedef for a secure ID, a system wide identificator for UMP memory buffers. + */ +typedef unsigned int ump_secure_id; + + +/** + * Value to indicate an invalid UMP memory handle. + */ +#define UMP_DD_HANDLE_INVALID ((ump_dd_handle)0) + + +/** + * Value to indicate an invalid secure Id. + */ +#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1) + + +/** + * UMP error codes for kernel space. + */ +typedef enum +{ + UMP_DD_SUCCESS, /**< indicates success */ + UMP_DD_INVALID, /**< indicates failure */ +} ump_dd_status_code; + + +/** + * Struct used to describe a physical block used by UMP memory + */ +typedef struct ump_dd_physical_block +{ + unsigned long addr; /**< The physical address of the block */ + unsigned long size; /**< The length of the block, typically page aligned */ +} ump_dd_physical_block; + + +/** + * Retrieves the secure ID for the specified UMP memory. + * + * This identificator is unique across the entire system, and uniquely identifies + * the specified UMP memory. This identificator can later be used through the + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id" or + * @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * functions in order to access this UMP memory, for instance from another process. + * + * @note There is a user space equivalent function called @ref ump_secure_id_get "ump_secure_id_get" + * + * @see ump_dd_handle_create_from_secure_id + * @see ump_handle_create_from_secure_id + * @see ump_secure_id_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the secure ID for the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle mem); + + +/** + * Retrieves a handle to allocated UMP memory. + * + * The usage of UMP memory is reference counted, so this will increment the reference + * count by one for the specified UMP memory. + * Use @ref ump_dd_reference_release "ump_dd_reference_release" when there is no longer any + * use for the retrieved handle. + * + * @note There is a user space equivalent function called @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * + * @see ump_dd_reference_release + * @see ump_handle_create_from_secure_id + * + * @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function. + * + * @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned. + */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id); + + +/** + * Retrieves the number of physical blocks used by the specified UMP memory. + * + * This function retrieves the number of @ref ump_dd_physical_block "ump_dd_physical_block" structs needed + * to describe the physical memory layout of the given UMP memory. This can later be used when calling + * the functions @ref ump_dd_phys_blocks_get "ump_dd_phys_blocks_get" and + * @ref ump_dd_phys_block_get "ump_dd_phys_block_get". + * + * @see ump_dd_phys_blocks_get + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * + * @return The number of ump_dd_physical_block structs required to describe the physical memory layout of the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem); + + +/** + * Retrieves all physical memory block information for specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will fail if the num_blocks parameter is either to large or to small. + * + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * @param blocks An array of @ref ump_dd_physical_block "ump_dd_physical_block" structs that will receive the physical description. + * @param num_blocks The number of blocks to return in the blocks array. Use the function + * @ref ump_dd_phys_block_count_get "ump_dd_phys_block_count_get" first to determine the number of blocks required. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block * blocks, unsigned long num_blocks); + + +/** + * Retrieves the physical memory block information for specified block for the specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will return UMP_DD_INVALID if the specified index is out of range. + * + * @see ump_dd_phys_blocks_get + * + * @param mem Handle to UMP memory. + * @param index Which physical info block to retrieve. + * @param block Pointer to a @ref ump_dd_physical_block "ump_dd_physical_block" struct which will receive the requested information. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block * block); + + +/** + * Retrieves the actual size of the specified UMP memory. + * + * The size is reported in bytes, and is typically page aligned. + * + * @note There is a user space equivalent function called @ref ump_size_get "ump_size_get" + * + * @see ump_size_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the allocated size of the specified UMP memory, in bytes. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle mem); + + +/** + * Adds an extra reference to the specified UMP memory. + * + * This function adds an extra reference to the specified UMP memory. This function should + * be used every time a UMP memory handle is duplicated, that is, assigned to another ump_dd_handle + * variable. The function @ref ump_dd_reference_release "ump_dd_reference_release" must then be used + * to release each copy of the UMP memory handle. + * + * @note You are not required to call @ref ump_dd_reference_add "ump_dd_reference_add" + * for UMP handles returned from + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id", + * because these handles are already reference counted by this function. + * + * @note There is a user space equivalent function called @ref ump_reference_add "ump_reference_add" + * + * @see ump_reference_add + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle mem); + + +/** + * Releases a reference from the specified UMP memory. + * + * This function should be called once for every reference to the UMP memory handle. + * When the last reference is released, all resources associated with this UMP memory + * handle are freed. + * + * @note There is a user space equivalent function called @ref ump_reference_release "ump_reference_release" + * + * @see ump_reference_release + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle mem); + + +#ifdef __cplusplus +} +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_INTERFACE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface_ref_drv.h b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface_ref_drv.h new file mode 100644 index 00000000000..70eea22d3c3 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_interface_ref_drv.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + */ + +#ifndef __UMP_KERNEL_INTERFACE_REF_DRV_H__ +#define __UMP_KERNEL_INTERFACE_REF_DRV_H__ + +#include "ump_kernel_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Turn specified physical memory into UMP memory. */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_INTERFACE_REF_DRV_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_platform.h b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_platform.h new file mode 100644 index 00000000000..55c49885f06 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/include/ump/ump_kernel_platform.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_platform.h + * + * This file should define UMP_KERNEL_API_EXPORT, + * which dictates how the UMP kernel API should be exported/imported. + * Modify this file, if needed, to match your platform setup. + */ + +#ifndef __UMP_KERNEL_PLATFORM_H__ +#define __UMP_KERNEL_PLATFORM_H__ + +/** @addtogroup ump_kernel_space_api + * @{ */ + +/** + * A define which controls how UMP kernel space API functions are imported and exported. + * This define should be set by the implementor of the UMP API. + */ + +#if defined(_WIN32) + +#if defined(UMP_BUILDING_UMP_LIBRARY) +#define UMP_KERNEL_API_EXPORT __declspec(dllexport) +#else +#define UMP_KERNEL_API_EXPORT __declspec(dllimport) +#endif + +#else + +#define UMP_KERNEL_API_EXPORT + +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_PLATFORM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile new file mode 100644 index 00000000000..20b57359167 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile @@ -0,0 +1,346 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +OSKOS=linux +FILES_PREFIX= + +ifneq ($(KBUILD_EXTMOD),) +DRIVER_DIR=$(KBUILD_EXTMOD) +else +src?=. +srctree?=. +DRIVER_DIR?=$(srctree)/$(src) +M?=$(DRIVER_DIR) +endif +MALI_RELEASE_NAME=$(shell cat $(DRIVER_DIR)/.version 2> /dev/null) +include $(DRIVER_DIR)/Makefile.platform +include $(DRIVER_DIR)/Makefile.common + +# set up defaults if not defined by the user +ARCH ?= arm +USING_MMU ?= 1 +USING_UMP ?= 0 +USING_HWMEM ?= 0 +USING_OS_MEMORY ?= 0 +USING_PMM ?= 0 +USING_GPU_UTILIZATION ?= 0 +USING_MALI_RUN_TIME_PM ?= 0 +USING_MALI_PMM_TESTSUITE ?= 0 +OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB ?= 6 +USING_PROFILING ?= 0 +TIMESTAMP ?= default +BUILD ?= debug +TARGET_PLATFORM ?= default + +ifeq ($(USING_UMP),1) +ifneq ($(USING_HWMEM),1) + UMP_SYMVERS_FILE = ../ump/Module.symvers + KBUILD_EXTRA_SYMBOLS = $(KBUILD_EXTMOD)/$(UMP_SYMVERS_FILE) +endif +endif + +# Check if a Mali Core sub module should be enabled, true or false returned +submodule_enabled = $(shell gcc $(DEFINES) -E $1/arch/config.h | grep type | grep -c $(2)) + +# linux build system integration +ifneq ($(KERNELRELEASE),) +# Inside the kernel build system + +# This conditional makefile exports the global definition ARM_INTERNAL_BUILD. Customer releases will not include arm_internal.mak +-include ../../../arm_internal.mak + +# Set up our defines, which will be passed to gcc +DEFINES += -DUSING_OS_MEMORY=$(USING_OS_MEMORY) +DEFINES += -DUSING_MMU=$(USING_MMU) +DEFINES += -DUSING_UMP=$(USING_UMP) +DEFINES += -DUSING_HWMEM=$(USING_HWMEM) +DEFINES += -D_MALI_OSK_SPECIFIC_INDIRECT_MMAP +DEFINES += -DMALI_TIMELINE_PROFILING_ENABLED=$(USING_PROFILING) +DEFINES += -DMALI_POWER_MGMT_TEST_SUITE=$(USING_MALI_PMM_TESTSUITE) +DEFINES += -DMALI_PMM_RUNTIME_JOB_CONTROL_ON=$(USING_MALI_RUN_TIME_PM) +DEFINES += -DMALI_STATE_TRACKING=1 +DEFINES += -DMALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB) + +ifneq ($(call submodule_enabled, $M, PMU),0) + MALI_PLATFORM_FILE = platform/mali400-pmu/mali_platform.c +else + MALI_PLATFORM_FILE = platform/$(TARGET_PLATFORM)/mali_platform.c +endif + +DEFINES += -DUSING_MALI_PMM=$(USING_PMM) +DEFINES += -DMALI_GPU_UTILIZATION=$(USING_GPU_UTILIZATION) + +ifeq ($(BUILD), debug) +DEFINES += -DDEBUG +endif +DEFINES += -DSVN_REV=$(SVN_REV) +DEFINES += -DSVN_REV_STRING=\"$(SVN_REV)\" + +# Linux has its own mmap cleanup handlers (see mali_kernel_mem_mmu.c) +DEFINES += -DMALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP + +ifeq ($(USING_UMP),1) + DEFINES += -DMALI_USE_UNIFIED_MEMORY_PROVIDER=1 + EXTRA_CFLAGS += -I$(DRIVER_DIR)/../../../include/ump +else + DEFINES += -DMALI_USE_UNIFIED_MEMORY_PROVIDER=0 +endif + +# Use our defines when compiling +EXTRA_CFLAGS += $(DEFINES) -I$(DRIVER_DIR) -I$(DRIVER_DIR)/common -I$(DRIVER_DIR)/linux -I$(DRIVER_DIR)/platform + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +ifeq ($(wildcard $(DRIVER_DIR)/linux/license/gpl/*),) +EXTRA_CFLAGS += -I$(DRIVER_DIR)/linux/license/proprietary +else +EXTRA_CFLAGS += -I$(DRIVER_DIR)/linux/license/gpl +endif +EXTRA_CFLAGS += -I$(DRIVER_DIR)/common/pmm + +# Source files which always are included in a build +SRC = \ + common/mali_kernel_core.c \ + linux/mali_kernel_linux.c \ + $(OSKOS)/mali_osk_indir_mmap.c \ + common/mali_kernel_rendercore.c \ + common/mali_kernel_descriptor_mapping.c \ + common/mali_kernel_vsync.c \ + linux/mali_ukk_vsync.c \ + $(MALI_PLATFORM_FILE) \ + $(OSKFILES) \ + $(UKKFILES) + #__malidrv_build_info.c + +ifeq ($(USING_PROFILING),1) +SRC += \ + common/mali_kernel_profiling.c \ + timestamp-$(TIMESTAMP)/mali_timestamp.c +EXTRA_CFLAGS += -I$(DRIVER_DIR)/timestamp-$(TIMESTAMP) +endif + +# Selecting files to compile by parsing the config file + +ifeq ($(USING_PMM),1) +SRC += \ + common/pmm/mali_pmm.c \ + common/pmm/mali_pmm_policy.c \ + common/pmm/mali_pmm_policy_alwayson.c \ + common/pmm/mali_pmm_policy_jobcontrol.c \ + common/pmm/mali_pmm_state.c \ + linux/mali_kernel_pm.c \ + linux/mali_osk_pm.c \ + linux/mali_device_pause_resume.c +endif + +ifeq ($(USING_GPU_UTILIZATION),1) +SRC += \ + common/mali_kernel_utilization.c +endif + +ifneq ($(call submodule_enabled, $M, MALI400PP),0) + # Mali-400 PP in use + EXTRA_CFLAGS += -DUSING_MALI400 + SRC += common/mali_kernel_MALI200.c +endif + +ifneq ($(call submodule_enabled, $M, MALI400GP),0) + # Mali-400 GP in use + SRC += common/mali_kernel_GP2.c +endif + +ifneq ($(call submodule_enabled, $M, MALI300PP),0) + # Mali-400 PP in use + EXTRA_CFLAGS += -DUSING_MALI400 + SRC += common/mali_kernel_MALI200.c +endif + +ifneq ($(call submodule_enabled, $M, MALI300GP),0) + # Mali-400 GP in use + SRC += common/mali_kernel_GP2.c +endif + +ifneq ($(call submodule_enabled, $M, MALI200),0) + # Mali200 in use + EXTRA_CFLAGS += -DUSING_MALI200 + SRC += common/mali_kernel_MALI200.c +endif + +ifneq ($(call submodule_enabled, $M, MALIGP2),0) + # MaliGP2 in use + SRC += common/mali_kernel_GP2.c +endif + +ifneq ($(call submodule_enabled, $M, MMU),0) + # Mali MMU in use + SRC += common/mali_kernel_mem_mmu.c common/mali_kernel_memory_engine.c common/mali_block_allocator.c common/mali_kernel_mem_os.c +else + # No Mali MMU in use + SRC += common/mali_kernel_mem_buddy.c +endif + +ifneq ($(call submodule_enabled, $M, MALI400L2)$(),0) + # Mali Level2 cache in use + EXTRA_CFLAGS += -DUSING_MALI400_L2_CACHE + SRC += common/mali_kernel_l2_cache.c +endif + +ifneq ($(call submodule_enabled, $M, MALI300L2)$(),0) + # Mali Level2 cache in use + EXTRA_CFLAGS += -DUSING_MALI400_L2_CACHE + SRC += common/mali_kernel_l2_cache.c +endif + +ifeq ($(USING_HWMEM),1) + # HWMEM used as backend for UMP api + EXTRA_CFLAGS += -I$(DRIVER_DIR)/../ump/common + SRC += platform/ux500/ump_kernel_api_hwmem.c +endif + +# Target build file +MODULE:=mali.ko + +# Tell the Linux build system from which .o file to create the kernel module +obj-$(CONFIG_GPU_MALI) := $(MODULE:.ko=.o) +# Tell the Linux build system to enable building of our .c files +$(MODULE:.ko=-y) := $(SRC:.c=.o) + +else +# Outside the kernel build system + +# Get any user defined KDIR- or maybe even a hardcoded KDIR +-include KDIR_CONFIGURATION + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +ifeq ($(ARCH), arm) + # when compiling for ARM we're cross compiling + export CROSS_COMPILE ?= arm-none-linux-gnueabi- + # default to Virtex5 + CONFIG ?= pb-virtex5-m200 +else + # Compiling for the host + CONFIG ?= $(shell uname -m) +endif + +# default cpu to select +CPU ?= pb11mp + +# look up KDIR based om CPU selection +KDIR ?= $(KDIR-$(CPU)) + +# validate lookup result +ifeq ($(KDIR),) +$(error No KDIR found for platform $(CPU)) +endif + +# report detected/selected settings +ifdef ARM_INTERNAL_BUILD +$(warning Config $(CONFIG)) +$(warning Host CPU $(CPU)) +$(warning MMU $(USING_MMU)) +$(warning OS_MEMORY $(USING_OS_MEMORY)) +endif + +# Validate selected config +ifneq ($(shell [ -d $(DRIVER_DIR)/arch-$(CONFIG) ] && [ -f $(DRIVER_DIR)/arch-$(CONFIG)/config.h ] && echo "OK"), OK) +$(warning Current directory is $(shell pwd)) +$(error No configuration found for config $(CONFIG). Check that $(DRIVER_DIR)/arch-$(CONFIG)/config.h exists) +else +# Link arch to the selected arch-config directory +$(shell [ -L $(DRIVER_DIR)/arch ] && rm $(DRIVER_DIR)/arch) +$(shell ln -sf $(DRIVER_DIR)/arch-$(CONFIG) $(DRIVER_DIR)/arch) +$(shell touch $(DRIVER_DIR)/arch/config.h) + +# Register number of Mali400 cores for routing test jobs to the right Mali-400 +ifeq ($CONFIG, pb-virtex5-m400-1) +VERSION_STRINGS += USING_MALI400_PP_CORES=1 +endif +ifeq ($CONFIG, pb-virtex5-m400-2) +VERSION_STRINGS += USING_MALI400_PP_CORES=2 +endif +ifeq ($CONFIG, pb-virtex5-m400-3) +VERSION_STRINGS += USING_MALI400_PP_CORES=3 +endif +ifeq ($CONFIG, pb-virtex5-m400-4) +VERSION_STRINGS += USING_MALI400_PP_CORES=4 +endif + +endif + +# Filename to use when patching. Original will be saved as this +PATCH_BACKUP_FILE:=config_makefile_patch_backup.h + +ifdef ARM_INTERNAL_BUILD +$(warning Looking for previous backup) +endif +# Look for previous backup +shell_output:=$(shell [ -f arch/$(PATCH_BACKUP_FILE) ] && mv -vf arch/$(PATCH_BACKUP_FILE) arch/config.h && echo "Patch backup restored") +ifneq ($(shell_output),) +ifdef ARM_INTERNAL_BUILD +$(warning $(shell_output)) +endif +endif + +ifdef PATCH +ifdef ARM_INTERNAL_BUILD +$(warning Patching using: $(PATCH) ) +endif +ifneq ($(shell [ -f arch/$(PATCH).diff ] && echo "OK"), OK) +# The patch file does not exist +shell_output:=$(shell echo "Possible PATCH arguments are:" ; find arch/ -name "*.diff" |sed -r 's/.*\/(.*)\.diff/\1/') +ifdef ARM_INTERNAL_BUILD +$(warning $(shell_output)) +endif +$(error Could not find file arch-$(CONFIG)/$(PATCH).diff ) +else +# Patch file found, do patching +shell_output:=$(shell cp -f arch/config.h arch/$(PATCH_BACKUP_FILE) && patch --no-backup-if-mismatch -st -p0 arch/config.h -i arch/$(PATCH).diff && echo "OK") +ifneq ($(shell_output), OK) +$(warning Output from failed patch: $(shell_output)) +$(shell mv -f arch/$(PATCH_BACKUP_FILE) arch/config.h) +$(error Could not patch file arch-$(CONFIG)/config.h with arch-$(CONFIG)/$(PATCH).diff.) +endif +endif +endif + +# Extend common version-string +VERSION_STRINGS += BUILD=$(shell echo $(BUILD) | tr a-z A-Z) +VERSION_STRINGS += CPU=$(CPU) +VERSION_STRINGS += USING_MMU=$(USING_MMU) +VERSION_STRINGS += USING_UMP=$(USING_UMP) +VERSION_STRINGS += USING_HWMEM=$(USING_HWMEM) +VERSION_STRINGS += USING_PMM=$(USING_PMM) +VERSION_STRINGS += USING_MALI200=$(call submodule_enabled, ., MALI200) +VERSION_STRINGS += USING_MALI400=$(call submodule_enabled, ., MALI400) +VERSION_STRINGS += USING_MALI400_L2_CACHE=$(call submodule_enabled, ., MALI400L2) +VERSION_STRINGS += USING_GP2=$(call submodule_enabled, ., MALIGP2) +VERSION_STRINGS += KDIR=$(KDIR) + +all: make-build-info-file $(UMP_SYMVERS_FILE) + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) CONFIG_GPU_MALI=m modules + @([ "$(PATCH)" != "" ] && [ -f arch/$(PATCH_BACKUP_FILE) ] && mv -f arch/$(PATCH_BACKUP_FILE) arch/config.h && echo "Patch backup restored") || echo No Unpatching needed + @rm -f $(FILES_PREFIX)__malidrv_build_info.c $(FILES_PREFIX)__malidrv_build_info.o + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + @([ "$(PATCH)" != "" ] && [ -f arch/$(PATCH_BACKUP_FILE) ] && mv -f arch/$(PATCH_BACKUP_FILE) arch/config.h && echo "Patch backup restored") || echo No Unpatching needed + +make-build-info-file: + @echo 'char *__malidrv_build_info(void) { return "malidrv: $(VERSION_STRINGS)";}' > $(FILES_PREFIX)__malidrv_build_info.c + +kernelrelease: + $(MAKE) -C $(KDIR) kernelrelease + +# end of outside kernel build system block +endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common new file mode 100644 index 00000000000..ebe3b9e3399 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common @@ -0,0 +1,56 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +OSKFILES=\ + $(FILES_PREFIX)$(OSKOS)/mali_osk_atomics.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_irq.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_locks.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_low_level_mem.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_math.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_memory.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_misc.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_mali.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_notification.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_time.c \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_timers.c + +UKKFILES=\ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_mem.c \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_gp.c \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_pp.c \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_core.c + +ifeq ($(USING_PROFILING),1) +UKKFILES+=\ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_profiling.c +endif + +ifeq ($(MALI_PLATFORM_FILE),) +MALI_PLATFORM_FILE=platform/default/mali_platform.c +endif + +# Get subversion revision number, fall back to only ${MALI_RELEASE_NAME} if no svn info is available +SVN_REV := $(shell (cd $(DRIVER_DIR); (svnversion | grep -qv exported && svnversion) || git svn info | grep '^Revision: '| sed -e 's/^Revision: //' ) 2>/dev/null ) +ifeq ($(SVN_REV),) +SVN_REV := $(MALI_RELEASE_NAME) +else +SVN_REV := $(MALI_RELEASE_NAME)-r$(SVN_REV) +endif + +# Common version-string, will be extended by OS-specifc sections +VERSION_STRINGS = +VERSION_STRINGS += CONFIG=$(CONFIG) +VERSION_STRINGS += USING_OS_MEMORY=$(USING_OS_MEMORY) +VERSION_STRINGS += API_VERSION=$(shell cd $(DRIVER_DIR); grep "\#define _MALI_API_VERSION" $(FILES_PREFIX)common\/mali_uk_types.h | cut -d' ' -f 3 ) +VERSION_STRINGS += REPO_URL=$(shell cd $(DRIVER_DIR); (svn info || git svn info || echo 'URL: $(MALI_RELEASE_NAME)') 2>/dev/null | grep '^URL: ' | cut -d: -f2- | cut -b2-) +VERSION_STRINGS += REVISION=$(SVN_REV) +VERSION_STRINGS += CHANGED_REVISION=$(shell cd $(DRIVER_DIR); (svn info || git svn info || echo 'Last Changed Rev: $(MALI_RELEASE_NAME)') 2>/dev/null | grep '^Last Changed Rev: ' | cut -d: -f2- | cut -b2-) +VERSION_STRINGS += CHANGE_DATE=$(shell cd $(DRIVER_DIR); (svn info || git svn info || echo 'Last Changed Date: $(MALI_RELEASE_NAME)') 2>/dev/null | grep '^Last Changed Date: ' | cut -d: -f2- | cut -b2-) +VERSION_STRINGS += BUILD_DATE=$(shell date) diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.platform b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.platform new file mode 100644 index 00000000000..1f8068e25de --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.platform @@ -0,0 +1,45 @@ +#these are the build options for the ST-Ericsson Ux500 platforms. + +ifeq ($(CONFIG_UX500_SOC_DB5500),y) +TARGET_PLATFORM = default +USING_GPU_UTILIZATION = 0 +DEFINES += -DSOC_DB5500=1 +endif + +CONFIG = ux500 +CPU = $(CONFIG) +TARGET_PLATFORM ?= $(CONFIG) +ARCH ?= arm +USING_MMU ?= 1 +USING_PMM ?= 1 +USING_UMP ?= 1 +USING_HWMEM ?= 1 +USING_OS_MEMORY ?= 1 +USING_GPU_UTILIZATION ?= 1 + +ifeq ($(CONFIG_GPU_MALI_DEBUG),y) +BUILD ?= debug +else +BUILD ?= release +endif + +KDIR-$(CPU)=$(srctree) + +#these are paths relative to the mali400ko/driver/src/devicedrv/mali folder +#not to be confused with the drivers/gpu/mali symlink in the kernel tree +EXTRA_CFLAGS += -I$(realpath $(DRIVER_DIR)/../../../include/ump) +EXTRA_CFLAGS += -I$(realpath $(DRIVER_DIR)/../ump/common) + +#The following is duplicated from the main Makefile to ensure that the 'arch' +#link is created even during an in-kernel build. + +# Validate selected config +ifneq ($(shell [ -d $(DRIVER_DIR)/arch-$(CONFIG) ] && [ -f $(DRIVER_DIR)/arch-$(CONFIG)/config.h ] && echo "OK"), OK) +$(warning Current directory is $(shell pwd)) +$(error No configuration found for config $(CONFIG). Check that $(DRIVER_DIR)/arch-$(CONFIG)/config.h exists) +else +# Link arch to the selected arch-config directory +$(shell [ -L $(DRIVER_DIR)/arch ] && rm $(DRIVER_DIR)/arch) +$(shell ln -sf $(DRIVER_DIR)/arch-$(CONFIG) $(DRIVER_DIR)/arch) +$(shell touch $(DRIVER_DIR)/arch/config.h) +endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m300/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m300/config.h new file mode 100644 index 00000000000..5011c97a28c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m300/config.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = PMU, + .description = "Mali-300 PMU", + .base = 0xC0002000, + .irq = -1, + .mmu_id = 0 + + }, + { + .type = MALI300GP, + .description = "Mali-300 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI300PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-300 PP", + .mmu_id = 2 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = -1, + .description = "Mali-300 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = -1, + .description = "Mali-300 MMU for PP", + .mmu_id = 2 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI300L2, + .base = 0xC0001000, + .description = "Mali-300 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-direct/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-direct/config.h new file mode 100644 index 00000000000..ee53f49bfe0 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-direct/config.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + + { + .type = PMU, + .description = "Mali-400 PMU", + .base = 0xC0002000, + .irq = -1, + .mmu_id = 0 + }, + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP", + .mmu_id = 2 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = -1, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = -1, + .description = "Mali-400 MMU for PP", + .mmu_id = 2 + }, +#endif + { + .type = OS_MEMORY, + .description = "OS Memory", + .alloc_order = 10, /* Lowest preference for this memory */ + .size = 96 * 1024 * 1024, /* 96 MB */ + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = 0, + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0x80000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-pmu/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-pmu/config.h new file mode 100644 index 00000000000..5c7c7f3ef13 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1-pmu/config.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = PMU, + .description = "Mali-400 PMU", + .base = 0xC0002000, + .irq = -1, + .mmu_id = 0 + + }, + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP", + .mmu_id = 2 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = -1, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = -1, + .description = "Mali-400 MMU for PP", + .mmu_id = 2 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1/config.h new file mode 100644 index 00000000000..9f675fd1d23 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-1/config.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP", + .mmu_id = 2 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = -1, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = -1, + .description = "Mali-400 MMU for PP", + .mmu_id = 2 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-2/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-2/config.h new file mode 100644 index 00000000000..cc1f05cb5f4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-2/config.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP 0", + .mmu_id = 2 + }, + { + .type = MALI400PP, + .base = 0xc000A000, + .irq = -1, + .description = "Mali-400 PP 1", + .mmu_id = 3 +}, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = -1, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = -1, + .description = "Mali-400 MMU for PP 0", + .mmu_id = 2 + }, + { + .type = MMU, + .base = 0xC0005000, + .irq = -1, + .description = "Mali-400 MMU for PP 1", + .mmu_id = 3 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-3/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-3/config.h new file mode 100644 index 00000000000..a672e7d22ef --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-3/config.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the PB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP 0", + .mmu_id = 2 + }, + { + .type = MALI400PP, + .base = 0xc000A000, + .irq = -1, + .description = "Mali-400 PP 1", + .mmu_id = 3 + }, + { + .type = MALI400PP, + .base = 0xc000C000, + .irq = -1, + .description = "Mali-400 PP 2", + .mmu_id = 4 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = 102, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = 102, + .description = "Mali-400 MMU for PP 0", + .mmu_id = 2 + }, + { + .type = MMU, + .base = 0xC0005000, + .irq = 102, + .description = "Mali-400 MMU for PP 1", + .mmu_id = 3 + }, + { + .type = MMU, + .base = 0xC0006000, + .irq = 102, + .description = "Mali-400 MMU for PP 2", + .mmu_id = 4 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-4/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-4/config.h new file mode 100644 index 00000000000..ae9100c1ae4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-pb-virtex5-m400-4/config.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the EB platform with ZBT memory enabled */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP 0", + .mmu_id = 2 + }, + { + .type = MALI400PP, + .base = 0xc000A000, + .irq = -1, + .description = "Mali-400 PP 1", + .mmu_id = 3 + }, + { + .type = MALI400PP, + .base = 0xc000C000, + .irq = -1, + .description = "Mali-400 PP 2", + .mmu_id = 4 + }, + { + .type = MALI400PP, + .base = 0xc000E000, + .irq = -1, + .description = "Mali-400 PP 3", + .mmu_id = 5 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = 102, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = 102, + .description = "Mali-400 MMU for PP 0", + .mmu_id = 2 + }, + { + .type = MMU, + .base = 0xC0005000, + .irq = 102, + .description = "Mali-400 MMU for PP 1", + .mmu_id = 3 + }, + { + .type = MMU, + .base = 0xC0006000, + .irq = 102, + .description = "Mali-400 MMU for PP 2", + .mmu_id = 4 + }, + { + .type = MMU, + .base = 0xC0007000, + .irq = 102, + .description = "Mali-400 MMU for PP 3", + .mmu_id = 5 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ + .base = 0xD0000000, + .size = 0x10000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-ux500/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-ux500/config.h new file mode 100644 index 00000000000..dc324cf0d39 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/arch-ux500/config.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Architecture configuration for ST-Ericsson Ux500 platforms + * + * Author: Magnus Wendt for + * ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ + +#ifndef __ARCH_UX500_CONFIG_H__ +#define __ARCH_UX500_CONFIG_H__ + +/* #include */ + +#define U5500_SGA_BASE 0x801D0000 +#define U8500_SGA_BASE 0xA0300000 + +#if defined(SOC_DB5500) && (1 == SOC_DB5500) +#define UX500_SGA_BASE U5500_SGA_BASE +#else +#define UX500_SGA_BASE U8500_SGA_BASE +#endif + +#define MEGABYTE (1024*1024) +#define MALI_MEM_BASE (128 * MEGABYTE) +#define MALI_MEM_SIZE ( 32 * MEGABYTE) +#define OS_MEM_SIZE (128 * MEGABYTE) + +/* Hardware revision u8500 v1: GX570-BU-00000-r0p1 + * Hardware revision u8500 v2: GX570-BU-00000-r1p0 + * Hardware revision u5500: GX570-BU-00000-r1p0 + * configuration registers: 0xA0300000-0xA031FFFFF (stw8500v1_usermanual p269) + * + * Shared Peripheral Interrupt assignments: (stw8500v1_usermanual p265-266) + * Nb | Interrupt Source + * 116 | Mali400 combined + * 115 | Mali400 geometry processor + * 114 | Mali400 geometry processor MMU + * 113 | Mali400 pixel processor + * 112 | Mali400 pixel processor MMU + * + * irq offset: 32 + */ + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = UX500_SGA_BASE + 0x0000, + .irq = 115+32, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = UX500_SGA_BASE + 0x8000, + .irq = 113+32, + .description = "Mali-400 PP", + .mmu_id = 2 + }, +#if USING_MMU + { + .type = MMU, + .base = UX500_SGA_BASE + 0x3000, + .irq = 114+32, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = UX500_SGA_BASE + 0x4000, + .irq = 112+32, + .description = "Mali-400 MMU for PP", + .mmu_id = 2 + }, +#endif + { + .type = MEMORY, + .description = "Mali SDRAM", + .alloc_order = 0, /* Highest preference for this memory */ + .base = MALI_MEM_BASE, + .size = MALI_MEM_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, +#if USING_OS_MEMORY + { + .type = OS_MEMORY, + .description = "Linux kernel memory", + .alloc_order = 5, /* Medium preference for this memory */ + .size = OS_MEM_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_MMU_READABLE | _MALI_MMU_WRITEABLE + }, +#endif + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0x00000000, /* Validate all memory for now */ + .size = 2047 * MEGABYTE, /* "2GB ought to be enough for anyone" */ + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = UX500_SGA_BASE + 0x1000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_UX500_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.c new file mode 100644 index 00000000000..fc80a20c813 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_block_allocator.h" +#include "mali_osk.h" + +#define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */ + +typedef struct block_info +{ + struct block_info * next; +} block_info; + +/* The structure used as the handle produced by block_allocator_allocate, + * and removed by block_allocator_release */ +typedef struct block_allocator_allocation +{ + /* The list will be released in reverse order */ + block_info *last_allocated; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 start_offset; + u32 mapping_length; +} block_allocator_allocation; + + +typedef struct block_allocator +{ + _mali_osk_lock_t *mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 cpu_usage_adjust; + u32 num_blocks; +} block_allocator; + +MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block); +static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +static void block_allocator_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block); +static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block ); +static void block_allocator_destroy(mali_physical_memory_allocator * allocator); + +mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name) +{ + mali_physical_memory_allocator * allocator; + block_allocator * info; + u32 usable_size; + u32 num_blocks; + + usable_size = size & ~(MALI_BLOCK_SIZE - 1); + MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size)); + MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size)); + num_blocks = usable_size / MALI_BLOCK_SIZE; + MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks)); + + if (usable_size == 0) + { + MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size)); + return NULL; + } + + allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator)); + if (NULL != allocator) + { + info = _mali_osk_malloc(sizeof(block_allocator)); + if (NULL != info) + { + info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED, 0, 105); + if (NULL != info->mutex) + { + info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks); + if (NULL != info->all_blocks) + { + u32 i; + info->first_free = NULL; + info->num_blocks = num_blocks; + + info->base = base_address; + info->cpu_usage_adjust = cpu_usage_adjust; + + for ( i = 0; i < num_blocks; i++) + { + info->all_blocks[i].next = info->first_free; + info->first_free = &info->all_blocks[i]; + } + + allocator->allocate = block_allocator_allocate; + allocator->allocate_page_table_block = block_allocator_allocate_page_table_block; + allocator->destroy = block_allocator_destroy; + allocator->ctx = info; + allocator->name = name; + + return allocator; + } + _mali_osk_lock_term(info->mutex); + } + _mali_osk_free(info); + } + _mali_osk_free(allocator); + } + + return NULL; +} + +static void block_allocator_destroy(mali_physical_memory_allocator * allocator) +{ + block_allocator * info; + MALI_DEBUG_ASSERT_POINTER(allocator); + MALI_DEBUG_ASSERT_POINTER(allocator->ctx); + info = (block_allocator*)allocator->ctx; + + _mali_osk_free(info->all_blocks); + _mali_osk_lock_term(info->mutex); + _mali_osk_free(info); + _mali_osk_free(allocator); +} + +MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block) +{ + return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE); +} + +static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + block_allocator * info; + u32 left; + block_info * last_allocated = NULL; + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE; + block_allocator_allocation *ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(offset); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + info = (block_allocator*)ctx; + left = descriptor->size - *offset; + MALI_DEBUG_ASSERT(0 != left); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + ret_allocation = _mali_osk_malloc( sizeof(block_allocator_allocation) ); + + if ( NULL == ret_allocation ) + { + /* Failure; try another allocator by returning MALI_MEM_ALLOC_NONE */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return result; + } + + ret_allocation->start_offset = *offset; + ret_allocation->mapping_length = 0; + + while ((left > 0) && (info->first_free)) + { + block_info * block; + u32 phys_addr; + u32 padding; + u32 current_mapping_size; + + block = info->first_free; + info->first_free = info->first_free->next; + block->next = last_allocated; + last_allocated = block; + + phys_addr = get_phys(info, block); + + padding = *offset & (MALI_BLOCK_SIZE-1); + + if (MALI_BLOCK_SIZE - padding < left) + { + current_mapping_size = MALI_BLOCK_SIZE - padding; + } + else + { + current_mapping_size = left; + } + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, phys_addr + padding, info->cpu_usage_adjust, current_mapping_size)) + { + MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n")); + result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->start_offset, ret_allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0); + + /* release all memory back to the pool */ + while (last_allocated) + { + /* This relinks every block we've just allocated back into the free-list */ + block = last_allocated->next; + last_allocated->next = info->first_free; + info->first_free = last_allocated; + last_allocated = block; + } + + break; + } + + *offset += current_mapping_size; + left -= current_mapping_size; + ret_allocation->mapping_length += current_mapping_size; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + if (last_allocated) + { + if (left) result = MALI_MEM_ALLOC_PARTIAL; + else result = MALI_MEM_ALLOC_FINISHED; + + /* Record all the information about this allocation */ + ret_allocation->last_allocated = last_allocated; + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + + alloc_info->ctx = info; + alloc_info->handle = ret_allocation; + alloc_info->release = block_allocator_release; + } + else + { + /* Free the allocation information - nothing to be passed back */ + _mali_osk_free( ret_allocation ); + } + + return result; +} + +static void block_allocator_release(void * ctx, void * handle) +{ + block_allocator * info; + block_info * block, * next; + block_allocator_allocation *allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(handle); + + info = (block_allocator*)ctx; + allocation = (block_allocator_allocation*)handle; + block = allocation->last_allocated; + + MALI_DEBUG_ASSERT_POINTER(block); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + /* unmap */ + mali_allocation_engine_unmap_physical(allocation->engine, allocation->descriptor, allocation->start_offset, allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0); + + while (block) + { + MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks)))); + + next = block->next; + + /* relink into free-list */ + block->next = info->first_free; + info->first_free = block; + + /* advance the loop */ + block = next; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_free( allocation );} + + +static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block) +{ + block_allocator * info; + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(block); + info = (block_allocator*)ctx; + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + if (NULL != info->first_free) + { + void * virt; + u32 phys; + u32 size; + block_info * alloc; + alloc = info->first_free; + + phys = get_phys(info, alloc); /* Does not modify info or alloc */ + size = MALI_BLOCK_SIZE; /* Must be multiple of MALI_MMU_PAGE_SIZE */ + virt = _mali_osk_mem_mapioregion( phys, size, "Mali block allocator page tables" ); + + /* Failure of _mali_osk_mem_mapioregion will result in MALI_MEM_ALLOC_INTERNAL_FAILURE, + * because it's unlikely another allocator will be able to map in. */ + + if ( NULL != virt ) + { + block->ctx = info; /* same as incoming ctx */ + block->handle = alloc; + block->phys_base = phys; + block->size = size; + block->release = block_allocator_release_page_table_block; + block->mapping = virt; + + info->first_free = alloc->next; + + alloc->next = NULL; /* Could potentially link many blocks together instead */ + + result = MALI_MEM_ALLOC_FINISHED; + } + } + else result = MALI_MEM_ALLOC_NONE; + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return result; +} + + +static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block ) +{ + block_allocator * info; + block_info * block, * next; + + MALI_DEBUG_ASSERT_POINTER( page_table_block ); + + info = (block_allocator*)page_table_block->ctx; + block = (block_info*)page_table_block->handle; + + MALI_DEBUG_ASSERT_POINTER(info); + MALI_DEBUG_ASSERT_POINTER(block); + + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + /* Unmap all the physical memory at once */ + _mali_osk_mem_unmapioregion( page_table_block->phys_base, page_table_block->size, page_table_block->mapping ); + + /** @note This loop handles the case where more than one block_info was linked. + * Probably unnecssary for page table block releasing. */ + while (block) + { + next = block->next; + + MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks)))); + + block->next = info->first_free; + info->first_free = block; + + block = next; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.h new file mode 100644 index 00000000000..c3b5761e23a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_block_allocator.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_BLOCK_ALLOCATOR_H__ +#define __MALI_BLOCK_ALLOCATOR_H__ + +#include "mali_kernel_memory_engine.h" + +mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name); + +#endif /* __MALI_BLOCK_ALLOCATOR_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_GP2.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_GP2.c new file mode 100644 index 00000000000..e91abe120cb --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_GP2.c @@ -0,0 +1,1418 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_subsystem.h" +#include "regs/mali_gp_regs.h" +#include "mali_kernel_rendercore.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_kernel_profiling.h" +#endif +#if defined(USING_MALI400_L2_CACHE) +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" /* Needed for mali_kernel_mmu_force_bus_reset() */ +#endif + +#if defined(USING_MALI200) +#define MALI_GP_SUBSYSTEM_NAME "MaliGP2" +#define MALI_GP_CORE_TYPE _MALI_GP2 +#elif defined(USING_MALI400) +#define MALI_GP_SUBSYSTEM_NAME "Mali-400 GP" +#define MALI_GP_CORE_TYPE _MALI_400_GP +#else +#error "No supported mali core defined" +#endif + +#define GET_JOB_EMBEDDED_PTR(job) (&((job)->embedded_core_job)) +#define GET_JOBGP2_PTR(job_extern) _MALI_OSK_CONTAINER_OF(job_extern, maligp_job, embedded_core_job) + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_gp_id = -1; + +static mali_core_renderunit * last_gp_core_cookie = NULL; + +/* Describing a maligp job settings */ +typedef struct maligp_job +{ + /* The general job struct common for all mali cores */ + mali_core_job embedded_core_job; + _mali_uk_gp_start_job_s user_input; + + u32 irq_status; + u32 status_reg_on_stop; + u32 perf_counter0; + u32 perf_counter1; + u32 vscl_stop_addr; + u32 plbcl_stop_addr; + u32 heap_current_addr; + + /* The data we will return back to the user */ + _mali_osk_notification_t *notification_obj; + + int is_stalled_waiting_for_more_memory; + + u32 active_mask; + /* progress checking */ + u32 last_vscl; + u32 last_plbcl; + /* extended progress checking, only enabled when we can use one of the performance counters */ + u32 have_extended_progress_checking; + u32 vertices; + +#if defined(USING_MALI400_L2_CACHE) + u32 perf_counter_l2_src0; + u32 perf_counter_l2_src1; + u32 perf_counter_l2_val0; + u32 perf_counter_l2_val1; +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + u32 pid; + u32 tid; +#endif +} maligp_job; + +/*Functions Exposed to the General External System through + function pointers.*/ + +static _mali_osk_errcode_t maligp_subsystem_startup(mali_kernel_subsystem_identifier id); +#if USING_MMU +static _mali_osk_errcode_t maligp_subsystem_mmu_connect(mali_kernel_subsystem_identifier id); +#endif +static void maligp_subsystem_terminate(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t maligp_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); +static void maligp_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); +static _mali_osk_errcode_t maligp_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t maligp_renderunit_create(_mali_osk_resource_t * resource); +#if USING_MMU +static void maligp_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif +#if MALI_STATE_TRACKING +void maligp_subsystem_dump_state(void); +#endif + +/* Internal support functions */ +static _mali_osk_errcode_t maligp_core_version_legal( mali_core_renderunit *core ); +static void maligp_raw_reset( mali_core_renderunit *core); +static void maligp_reset_hard(struct mali_core_renderunit * core); +static void maligp_reset(mali_core_renderunit *core); +static void maligp_initialize_registers_mgmt(mali_core_renderunit *core ); + +#ifdef DEBUG +static void maligp_print_regs(int debug_level, mali_core_renderunit *core); +#endif + +/* Functions exposed to mali_core system through functionpointers + in the subsystem struct. */ +static _mali_osk_errcode_t subsystem_maligp_start_job(mali_core_job * job, mali_core_renderunit * core); +static u32 subsystem_maligp_irq_handler_upper_half(mali_core_renderunit * core); +static int subsystem_maligp_irq_handler_bottom_half(mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_maligp_get_new_job_from_user(struct mali_core_session * session, void * argument); +static _mali_osk_errcode_t subsystem_maligp_suspend_response(struct mali_core_session * session, void * argument); +static void subsystem_maligp_return_job_to_user(mali_core_job * job, mali_subsystem_job_end_code end_status); +static void subsystem_maligp_renderunit_delete(mali_core_renderunit * core); +static void subsystem_maligp_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style ); +static void subsystem_maligp_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_maligp_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core); +static void subsystem_maligp_renderunit_stop_bus(struct mali_core_renderunit* core); + +/* Variables */ +static register_address_and_value default_mgmt_regs[] = +{ + { MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED } +}; + + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ + +struct mali_kernel_subsystem mali_subsystem_gp2= +{ + maligp_subsystem_startup, /* startup */ + maligp_subsystem_terminate, /* shutdown */ +#if USING_MMU + maligp_subsystem_mmu_connect, /* load_complete */ +#else + NULL, +#endif + maligp_subsystem_core_system_info_fill, /* system_info_fill */ + maligp_subsystem_session_begin, /* session_begin */ + maligp_subsystem_session_end, /* session_end */ +#if USING_MMU + maligp_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + maligp_subsystem_dump_state, /* dump_state */ +#endif +} ; + +static mali_core_subsystem subsystem_maligp ; + +static _mali_osk_errcode_t maligp_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem * subsystem; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_startup\n") ) ; + + mali_subsystem_gp_id = id; + + /* All values get 0 as default */ + _mali_osk_memset(&subsystem_maligp, 0, sizeof(*subsystem)); + + subsystem = &subsystem_maligp; + subsystem->start_job = &subsystem_maligp_start_job; + subsystem->irq_handler_upper_half = &subsystem_maligp_irq_handler_upper_half; + subsystem->irq_handler_bottom_half = &subsystem_maligp_irq_handler_bottom_half; + subsystem->get_new_job_from_user = &subsystem_maligp_get_new_job_from_user; + subsystem->suspend_response = &subsystem_maligp_suspend_response; + subsystem->return_job_to_user = &subsystem_maligp_return_job_to_user; + subsystem->renderunit_delete = &subsystem_maligp_renderunit_delete; + subsystem->reset_core = &subsystem_maligp_renderunit_reset_core; + subsystem->stop_bus = &subsystem_maligp_renderunit_stop_bus; + subsystem->probe_core_irq_trigger = &subsystem_maligp_renderunit_probe_core_irq_trigger; + subsystem->probe_core_irq_acknowledge = &subsystem_maligp_renderunit_probe_core_irq_finished; + + /* Setting variables in the general core part of the subsystem.*/ + subsystem->name = MALI_GP_SUBSYSTEM_NAME; + subsystem->core_type = MALI_GP_CORE_TYPE; + subsystem->id = id; + + /* Initiates the rest of the general core part of the subsystem */ + MALI_CHECK_NO_ERROR(mali_core_subsystem_init( subsystem )); + + /* This will register the function for adding MALIGP2 cores to the subsystem */ +#if defined(USING_MALI200) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALIGP2, maligp_renderunit_create)); +#endif +#if defined(USING_MALI400) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI400GP, maligp_renderunit_create)); +#endif + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_startup\n") ) ; + + MALI_SUCCESS; +} + +#if USING_MMU +static _mali_osk_errcode_t maligp_subsystem_mmu_connect(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem_attach_mmu(&subsystem_maligp); + MALI_SUCCESS; /* OK */ +} +#endif + +static void maligp_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_terminate\n") ) ; + mali_core_subsystem_cleanup(&subsystem_maligp); +} + +static _mali_osk_errcode_t maligp_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_session_begin\n") ) ; + MALI_CHECK_NON_NULL(session = _mali_osk_malloc( sizeof(*session) ), _MALI_OSK_ERR_FAULT); + + _mali_osk_memset(session, 0, sizeof(*session) ); + *slot = (mali_kernel_subsystem_session_slot)session; + + session->subsystem = &subsystem_maligp; + + session->notification_queue = queue; + +#if USING_MMU + session->mmu_session = mali_session_data; +#endif + + mali_core_session_begin(session); + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_session_begin\n") ) ; + + MALI_SUCCESS; +} + +static void maligp_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + mali_core_session * session; + /** @note mali_session_data not needed here */ + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_session_end\n") ) ; + if ( NULL==slot || NULL==*slot) + { + MALI_PRINT_ERROR(("Input slot==NULL")); + return; + } + session = (mali_core_session *)*slot; + mali_core_session_close(session); + + _mali_osk_free(session); + *slot = NULL; + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_session_end\n") ) ; +} + +/** + * We fill in info about all the cores we have + * @param info Pointer to system info struct to update + * @return _MALI_OSK_ERR_OK on success, or another _mali_osk_errcode_t for errors. + */ +static _mali_osk_errcode_t maligp_subsystem_core_system_info_fill(_mali_system_info* info) +{ + return mali_core_subsystem_system_info_fill(&subsystem_maligp, info); +} + +static _mali_osk_errcode_t maligp_renderunit_create(_mali_osk_resource_t * resource) +{ + mali_core_renderunit *core; + int err; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_renderunit_create\n") ) ; + /* Checking that the resource settings are correct */ +#if defined(USING_MALI200) + if(MALIGP2 != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_GP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#elif defined(USING_MALI400) + if(MALI400GP != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_GP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + if ( 0 != resource->size ) + { + MALI_PRINT_ERROR(("Memory size set to " MALI_GP_SUBSYSTEM_NAME " core should be zero.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if ( NULL == resource->description ) + { + MALI_PRINT_ERROR(("A " MALI_GP_SUBSYSTEM_NAME " core needs a unique description field")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Create a new core object */ + core = (mali_core_renderunit*) _mali_osk_malloc(sizeof(*core)); + if ( NULL == core ) + { + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Variables set to be able to open and register the core */ + core->subsystem = &subsystem_maligp ; + core->registers_base_addr = resource->base ; + core->size = MALIGP2_REGISTER_ADDRESS_SPACE_SIZE ; + core->description = resource->description; + core->irq_nr = resource->irq ; +#if USING_MMU + core->mmu_id = resource->mmu_id; + core->mmu = NULL; +#endif +#if USING_MALI_PMM + /* Set up core's PMM id */ + core->pmm_id = MALI_PMM_CORE_GP; +#endif + + err = mali_core_renderunit_init( core ); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to initialize renderunit\n")); + goto exit_on_error0; + } + + /* Map the new core object, setting: core->registers_mapped */ + err = mali_core_renderunit_map_registers(core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error1; + + /* Check that the register mapping of the core works. + Return 0 if maligp core is present and accessible. */ + if (mali_benchmark) { + core->core_version = MALI_GP_PRODUCT_ID << 16; + } else { + core->core_version = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VERSION); + } + + err = maligp_core_version_legal(core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error2; + + /* Reset the core. Put the core into a state where it can start to render. */ + maligp_reset(core); + + /* Registering IRQ, init the work_queue_irq_handle */ + /* Adding this core as an available renderunit in the subsystem. */ + err = mali_core_subsystem_register_renderunit(&subsystem_maligp, core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error2; + +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Initial Register settings:\n")); + maligp_print_regs(4, core); +#endif + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_renderunit_create\n") ) ; + + MALI_SUCCESS; + +exit_on_error2: + mali_core_renderunit_unmap_registers(core); +exit_on_error1: + mali_core_renderunit_term(core); +exit_on_error0: + _mali_osk_free( core ) ; + MALI_PRINT_ERROR(("Renderunit NOT created.")); + MALI_ERROR((_mali_osk_errcode_t)err); +} + +#if USING_MMU +/* Used currently only for signalling when MMU has a pagefault */ +static void maligp_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + mali_core_subsystem_broadcast_notification(&subsystem_maligp, message, data); +} +#endif + +#ifdef DEBUG +static void maligp_print_regs(int debug_level, mali_core_renderunit *core) +{ + if (debug_level <= mali_debug_level) + { + MALI_DEBUG_PRINT(1, (" VS 0x%08X 0x%08X, PLBU 0x%08X 0x%08X ALLOC 0x%08X 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_END_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_END_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + MALI_DEBUG_PRINT(1, (" IntRaw 0x%08X IntMask 0x%08X, Status 0x%02X Ver: 0x%08X \n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_MASK), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VERSION))); + + MALI_DEBUG_PRINT(1, (" PERF_CNT Enbl:%d %d Src: %02d %02d VAL: 0x%08X 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE))); + + MALI_DEBUG_PRINT(1, (" VS_START 0x%08X PLBU_START 0x%08X AXI_ERR 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR_READ), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBCL_START_ADDR_READ), + mali_core_renderunit_register_read(core, MALIGP2_CONTR_AXI_BUS_ERROR_STAT))); + } +} +#endif + +static _mali_osk_errcode_t maligp_core_version_legal( mali_core_renderunit *core ) +{ + u32 mali_type; + + mali_type = core->core_version >> 16; + +#if defined(USING_MALI400) + if ( MALI400_GP_PRODUCT_ID != mali_type && MALI300_GP_PRODUCT_ID != mali_type ) +#else + if ( MALI_GP_PRODUCT_ID != mali_type ) +#endif + { + MALI_PRINT_ERROR(("Error: reading this from maligp version register: 0x%x\n", core->core_version)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + MALI_DEBUG_PRINT(3, ("Mali GP: core_version_legal: Reads correct mali version: %d\n", core->core_version )) ; + MALI_SUCCESS; +} + +static void subsystem_maligp_renderunit_stop_bus(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS); +} + +static void maligp_reset( mali_core_renderunit *core ) +{ + if (!mali_benchmark) { + maligp_raw_reset(core); + maligp_initialize_registers_mgmt(core); + } +} + + +static void maligp_reset_hard( mali_core_renderunit *core ) +{ + const int reset_finished_loop_count = 15; + const u32 reset_wait_target_register = MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW; + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + const u32 reset_default_value = 0; + int i; + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_invalid_value); + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_RESET); + + for (i = 0; i < reset_finished_loop_count; i++) + { + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_check_value); + if (reset_check_value == mali_core_renderunit_register_read(core, reset_wait_target_register)) + { + MALI_DEBUG_PRINT(5, ("Reset loop exiting after %d iterations\n", i)); + break; + } + } + + if (i == reset_finished_loop_count) + { + MALI_DEBUG_PRINT(1, ("The reset loop didn't work\n")); + } + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_default_value); /* set it back to the default */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + + +} + +static void maligp_raw_reset( mali_core_renderunit *core ) +{ + int i; + const int request_loop_count = 20; + + MALI_DEBUG_PRINT(4, ("Mali GP: maligp_raw_reset: %s\n", core->description)) ; + if (mali_benchmark) return; + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */ + +#if defined(USING_MALI200) + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS) & MALIGP2_REG_VAL_STATUS_BUS_STOPPED) break; + _mali_osk_time_ubusydelay(10); + } + + MALI_DEBUG_PRINT_IF(1, request_loop_count == i, ("Mali GP: Bus was never stopped during core reset\n")); + + if (request_loop_count==i) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + MALI_DEBUG_PRINT(1, ("Mali GP: Forcing MMU bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow GP to stop its bus, system failure, unable to recover\n")); + return; + } + + /* the bus was stopped OK, complete the reset */ + /* use the hard reset routine to do the actual reset */ + maligp_reset_hard(core); + +#elif defined(USING_MALI400) + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALI400GP_REG_VAL_IRQ_RESET_COMPLETED); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALI400GP_REG_VAL_CMD_SOFT_RESET); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & /*Bitwise OR*/ + MALI400GP_REG_VAL_IRQ_RESET_COMPLETED) break; + _mali_osk_time_ubusydelay(10); + } + + if ( request_loop_count==i ) + { +#if USING_MMU + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + MALI_DEBUG_PRINT(1, ("Mali GP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow GP to stop its bus, system failure, unable to recover\n")); + } + else + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + } + +#else +#error "no supported mali core defined" +#endif +} + +/* Sets the registers on maligp according to the const default_mgmt_regs array. */ +static void maligp_initialize_registers_mgmt(mali_core_renderunit *core ) +{ + int i; + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_initialize_registers_mgmt: %s\n", core->description)) ; + for(i=0 ; i< (sizeof(default_mgmt_regs)/sizeof(*default_mgmt_regs)) ; ++i) + { + mali_core_renderunit_register_write(core, default_mgmt_regs[i].address, default_mgmt_regs[i].value); + } +} + + +/* Start this job on this core. Return MALI_TRUE if the job was started. */ +static _mali_osk_errcode_t subsystem_maligp_start_job(mali_core_job * job, mali_core_renderunit * core) +{ + maligp_job *jobgp; + u32 startcmd; + /* The local extended version of the general structs */ + jobgp = _MALI_OSK_CONTAINER_OF(job, maligp_job, embedded_core_job); + + startcmd = 0; + if ( jobgp->user_input.frame_registers[0] != jobgp->user_input.frame_registers[1] ) + { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_VS; + } + + if ( jobgp->user_input.frame_registers[2] != jobgp->user_input.frame_registers[3] ) + { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_PLBU; + } + + if(0 == startcmd) + { + MALI_DEBUG_PRINT(4, ("Mali GP: Job: 0x%08x WILL NOT START SINCE JOB HAS ILLEGAL ADDRESSES\n", + (u32)jobgp->user_input.user_job_ptr)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Registers Start\n")); + maligp_print_regs(4, core); +#endif + + + mali_core_renderunit_register_write_array( + core, + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR, + &(jobgp->user_input.frame_registers[0]), + sizeof(jobgp->user_input.frame_registers)/sizeof(jobgp->user_input.frame_registers[0])); + + /* This selects which performance counters we are reading */ + if ( 0 != jobgp->user_input.perf_counter_flag ) + { + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) + { + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC, + jobgp->user_input.perf_counter_src0); + + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) + { + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, + jobgp->user_input.perf_counter_src1); + + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + +#if defined(USING_MALI400_L2_CACHE) + if ( jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + int force_reset = ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET ) ? 1 : 0; + u32 src0 = 0; + u32 src1 = 0; + + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE ) + { + src0 = jobgp->user_input.perf_counter_l2_src0; + } + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE ) + { + src1 = jobgp->user_input.perf_counter_l2_src1; + } + + mali_kernel_l2_cache_set_perf_counters(src0, src1, force_reset); /* will activate and possibly reset counters */ + + /* Now, retrieve the current values, so we can substract them when the job has completed */ + mali_kernel_l2_cache_get_perf_counters(&jobgp->perf_counter_l2_src0, + &jobgp->perf_counter_l2_val0, + &jobgp->perf_counter_l2_src1, + &jobgp->perf_counter_l2_val1); + } +#endif + } + + if ( 0 == (jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE)) + { + /* extended progress checking can be enabled */ + + jobgp->have_extended_progress_checking = 1; + + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, + MALIGP2_REG_VAL_PERF_CNT1_SRC_NUMBER_OF_VERTICES_PROCESSED + ); + + mali_core_renderunit_register_write( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + + subsystem_flush_mapped_mem_cache(); + + MALI_DEBUG_PRINT(4, ("Mali GP: STARTING GP WITH CMD: 0x%x\n", startcmd)); + + /* This is the command that starts the Core */ + mali_core_renderunit_register_write(core, + MALIGP2_REG_ADDR_MGMT_CMD, + startcmd); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), jobgp->pid, jobgp->tid, 0, 0, 0); +#endif + + MALI_SUCCESS; +} + +/* Check if given core has an interrupt pending. Return MALI_TRUE and set mask to 0 if pending */ + +static u32 subsystem_maligp_irq_handler_upper_half(mali_core_renderunit * core) +{ + u32 irq_readout; + + if (mali_benchmark) { + return (core->current_job ? 1 : 0); /* simulate irq is pending when a job is pending */ + } + + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_STAT); + + MALI_DEBUG_PRINT(5, ("Mali GP: IRQ: %04x\n", irq_readout)) ; + + if ( MALIGP2_REG_VAL_IRQ_MASK_NONE != irq_readout ) + { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK , MALIGP2_REG_VAL_IRQ_MASK_NONE); + /* We do need to handle this in a bottom half, return 1 */ + return 1; + } + return 0; +} + +/* This function should check if the interrupt indicates that job was finished. +If so it should update the job-struct, reset the core registers, and return MALI_TRUE, . +If the job is still working after this function it should return MALI_FALSE. +The function must also enable the bits in the interrupt mask for the core. +Called by the bottom half interrupt function. */ +static int subsystem_maligp_irq_handler_bottom_half(mali_core_renderunit* core) +{ + mali_core_job * job; + maligp_job * jobgp; + u32 irq_readout; + u32 core_status; + u32 vscl; + u32 plbcl; + + job = core->current_job; + + if (mali_benchmark) { + MALI_DEBUG_PRINT(3, ("MaliGP: Job: Benchmark\n") ); + irq_readout = MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST; + core_status = 0; + } else { + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & MALIGP2_REG_VAL_IRQ_MASK_USED; + core_status = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS); + } + + if (NULL == job) + { + MALI_DEBUG_ASSERT(CORE_IDLE==core->state); + if ( 0 != irq_readout ) + { + MALI_PRINT_ERROR(("Interrupt from a core not running a job. IRQ: 0x%04x Status: 0x%04x", irq_readout, core_status)); + } + return JOB_STATUS_END_UNKNOWN_ERR; + } + MALI_DEBUG_ASSERT(CORE_IDLE!=core->state); + + jobgp = GET_JOBGP2_PTR(job); + + jobgp->heap_current_addr = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR); + + vscl = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR); + plbcl = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR); + + MALI_DEBUG_PRINT(3, ("Mali GP: Job: 0x%08x IRQ RECEIVED Rawstat: 0x%x Status: 0x%x\n", + (u32)jobgp->user_input.user_job_ptr, irq_readout , core_status )) ; + + jobgp->irq_status |= irq_readout; + jobgp->status_reg_on_stop = core_status; + + if ( 0 != jobgp->is_stalled_waiting_for_more_memory ) + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + /* Readback the performance counters */ + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { + jobgp->perf_counter0 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + jobgp->perf_counter1 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + } + +#if defined(USING_MALI400_L2_CACHE) + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (jobgp->perf_counter_l2_src0 == src0) + { + jobgp->perf_counter_l2_val0 = val0 - jobgp->perf_counter_l2_val0; + } + else + { + jobgp->perf_counter_l2_val0 = 0; + } + + if (jobgp->perf_counter_l2_src1 == src1) + { + jobgp->perf_counter_l2_val1 = val1 - jobgp->perf_counter_l2_val1; + } + else + { + jobgp->perf_counter_l2_val1 = 0; + } + } +#endif + + MALI_DEBUG_PRINT(2, ("Mali GP: Job aborted - userspace would not provide more heap memory.\n")); + return JOB_STATUS_END_OOM; /* Core is ready for more jobs.*/ + } + /* finished ? */ + else if (0 == (core_status & MALIGP2_REG_VAL_STATUS_MASK_ACTIVE)) + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Registers On job end:\n")); + maligp_print_regs(4, core); +#endif + MALI_DEBUG_PRINT_IF(5, irq_readout & 0x04, ("OOM when done, ignoring (reg.current = 0x%x, reg.end = 0x%x)\n", + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + + + if (0 != jobgp->user_input.perf_counter_flag ) + { + /* Readback the performance counters */ + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { + jobgp->perf_counter0 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + jobgp->perf_counter1 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + } + +#if defined(USING_MALI400_L2_CACHE) + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (jobgp->perf_counter_l2_src0 == src0) + { + jobgp->perf_counter_l2_val0 = val0 - jobgp->perf_counter_l2_val0; + } + else + { + jobgp->perf_counter_l2_val0 = 0; + } + + if (jobgp->perf_counter_l2_src1 == src1) + { + jobgp->perf_counter_l2_val1 = val1 - jobgp->perf_counter_l2_val1; + } + else + { + jobgp->perf_counter_l2_val1 = 0; + } + } +#endif + } + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + + return JOB_STATUS_END_SUCCESS; /* core idle */ + } + /* sw watchdog timeout handling or time to do hang checking ? */ + else if ( + (CORE_WATCHDOG_TIMEOUT == core->state) || + ( + (CORE_HANG_CHECK_TIMEOUT == core->state) && + ( + (jobgp->have_extended_progress_checking ? (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE) == jobgp->vertices) : 1/*TRUE*/) && + ((core_status & MALIGP2_REG_VAL_STATUS_VS_ACTIVE) ? (vscl == jobgp->last_vscl) : 1/*TRUE*/) && + ((core_status & MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) ? (plbcl == jobgp->last_plbcl) : 1/*TRUE*/) + ) + ) + ) + { + /* no progress detected, killed by the watchdog */ + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali GP: SW-Timeout. Regs:\n")); + if (core_status & MALIGP2_REG_VAL_STATUS_VS_ACTIVE) MALI_DEBUG_PRINT(1, ("vscl current = 0x%x last = 0x%x\n", (void*)vscl, (void*)jobgp->last_vscl)); + if (core_status & MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) MALI_DEBUG_PRINT(1, ("plbcl current = 0x%x last = 0x%x\n", (void*)plbcl, (void*)jobgp->last_plbcl)); + if (jobgp->have_extended_progress_checking) MALI_DEBUG_PRINT(1, ("vertices processed = %d, last = %d\n", mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE), + jobgp->vertices)); +#ifdef DEBUG + maligp_print_regs(2, core); +#endif + return JOB_STATUS_END_HANG; + } + /* if hang timeout checking was enabled and we detected progress, will be fall down to this check */ + /* check for PLBU OOM before the hang check to avoid the race condition of the hw wd trigging while waiting for us to handle the OOM interrupt */ + else if ( 0 != (irq_readout & MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM)) + { + mali_core_session *session; + _mali_osk_notification_t *notific; + _mali_uk_gp_job_suspended_s * suspended_job; + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SUSPEND|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + session = job->session; + + MALI_DEBUG_PRINT(4, ("OOM, new heap requested by GP\n")); + MALI_DEBUG_PRINT(4, ("Status when OOM: current = 0x%x, end = 0x%x\n", + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + + notific = _mali_osk_notification_create( + + _MALI_NOTIFICATION_GP_STALLED, + sizeof( _mali_uk_gp_job_suspended_s ) + ); + if ( NULL == notific) + { + MALI_PRINT_ERROR( ("Mali GP: Could not get notification object\n")) ; + return JOB_STATUS_END_OOM; /* Core is ready for more jobs.*/ + } + + core->state = CORE_WORKING; + jobgp->is_stalled_waiting_for_more_memory = 1; + suspended_job = (_mali_uk_gp_job_suspended_s *)notific->result_buffer; /* this is ok - result_buffer was malloc'd */ + + suspended_job->user_job_ptr = jobgp->user_input.user_job_ptr; + suspended_job->reason = _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY ; + suspended_job->cookie = (u32) core; + last_gp_core_cookie = core; + + _mali_osk_notification_queue_send( session->notification_queue, notific); + +#ifdef DEBUG + maligp_print_regs(4, core); +#endif + + /* stop all active timers */ + _mali_osk_timer_del( core->timer); + _mali_osk_timer_del( core->timer_hang_detection); + MALI_DEBUG_PRINT(4, ("Mali GP: PLBU heap empty, sending memory request to userspace\n")); + /* save to watchdog_jiffies what was remaining WD timeout value when OOM was triggered */ + job->watchdog_jiffies = (long)job->watchdog_jiffies - (long)_mali_osk_time_tickcount(); + /* reuse core->timer as the userspace response timeout handler */ + _mali_osk_timer_add( core->timer, _mali_osk_time_mstoticks(1000) ); /* wait max 1 sec for userspace to respond */ + return JOB_STATUS_CONTINUE_RUN; /* The core is NOT available for new jobs. */ + } + /* hw watchdog is reporting a new hang or an existing progress-during-hang check passed? */ + else if ((CORE_HANG_CHECK_TIMEOUT == core->state) || (irq_readout & jobgp->active_mask & MALIGP2_REG_VAL_IRQ_HANG)) + { + /* check interval in ms */ + u32 timeout = mali_core_hang_check_timeout_get(); + MALI_DEBUG_PRINT(3, ("Mali GP: HW/SW Watchdog triggered, checking for progress in %d ms\n", timeout)); + core->state = CORE_WORKING; + + /* save state for the progress checking */ + jobgp->last_vscl = vscl; + jobgp->last_plbcl = plbcl; + if (jobgp->have_extended_progress_checking) + { + jobgp->vertices = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + } + /* hw watchdog triggered, set up a progress checker every HANGCHECK ms */ + _mali_osk_timer_add( core->timer_hang_detection, _mali_osk_time_mstoticks(timeout)); + jobgp->active_mask &= ~MALIGP2_REG_VAL_IRQ_HANG; /* ignore the hw watchdog from now on */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* not finihsed */ } + /* no errors, but still working */ + else if ( ( 0 == (core_status & MALIGP2_REG_VAL_STATUS_MASK_ERROR)) && + ( 0 != (core_status & MALIGP2_REG_VAL_STATUS_MASK_ACTIVE )) + ) + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + return JOB_STATUS_CONTINUE_RUN; + } + /* Else there must be some error */ + else + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali GP: Core crashed? *IRQ: 0x%x Status: 0x%x\n", irq_readout, core_status )); + #ifdef DEBUG + MALI_DEBUG_PRINT(1, ("Mali GP: Registers Before reset:\n")); + maligp_print_regs(1, core); + #endif + return JOB_STATUS_END_UNKNOWN_ERR; + } +} + + +/* This function is called from the ioctl function and should return a mali_core_job pointer +to a created mali_core_job object with the data given from userspace */ +static _mali_osk_errcode_t subsystem_maligp_get_new_job_from_user(struct mali_core_session * session, void * argument) +{ + maligp_job *jobgp; + mali_core_job *job; + mali_core_job *previous_replaced_job; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_uk_gp_start_job_s * user_ptr_job_input; + + user_ptr_job_input = (_mali_uk_gp_start_job_s *)argument; + + MALI_CHECK_NON_NULL(jobgp = (maligp_job *) _mali_osk_calloc(1, sizeof(maligp_job)), _MALI_OSK_ERR_FAULT); + + /* Copy the job data from the U/K interface */ + if ( NULL == _mali_osk_memcpy(&jobgp->user_input, user_ptr_job_input, sizeof(_mali_uk_gp_start_job_s) ) ) + { + MALI_PRINT_ERROR( ("Mali GP: Could not copy data from U/K interface.\n")) ; + err = _MALI_OSK_ERR_FAULT; + goto function_exit; + } + + MALI_DEBUG_PRINT(3, ("Mali GP: subsystem_maligp_get_new_job_from_user 0x%x\n", (void*)jobgp->user_input.user_job_ptr)); + + MALI_DEBUG_PRINT(3, ("Mali GP: Job Regs: 0x%08X 0x%08X, 0x%08X 0x%08X 0x%08X 0x%08X\n", + jobgp->user_input.frame_registers[0], + jobgp->user_input.frame_registers[1], + jobgp->user_input.frame_registers[2], + jobgp->user_input.frame_registers[3], + jobgp->user_input.frame_registers[4], + jobgp->user_input.frame_registers[5]) ); + + + job = GET_JOB_EMBEDDED_PTR(jobgp); + + job->session = session; + job_priority_set(job, jobgp->user_input.priority); + job_watchdog_set(job, jobgp->user_input.watchdog_msecs ); + jobgp->heap_current_addr = jobgp->user_input.frame_registers[4]; + + job->abort_id = jobgp->user_input.abort_id; + + jobgp->is_stalled_waiting_for_more_memory = 0; + +#if MALI_TIMELINE_PROFILING_ENABLED + jobgp->pid = _mali_osk_get_pid(); + jobgp->tid = _mali_osk_get_tid(); +#endif + + if (NULL != session->job_waiting_to_run) + { + /* IF NOT( newjow HAS HIGHER PRIORITY THAN waitingjob) EXIT_NOT_START new job */ + if(!job_has_higher_priority(job, session->job_waiting_to_run)) + { + /* The job we try to add does NOT have higher pri than current */ + /* Cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + goto function_exit; + } + } + + /* We now know that we have a job, and a slot to put it in */ + + jobgp->active_mask = MALIGP2_REG_VAL_IRQ_MASK_USED; + + /* Allocating User Return Data */ + jobgp->notification_obj = _mali_osk_notification_create( + _MALI_NOTIFICATION_GP_FINISHED, + sizeof(_mali_uk_gp_job_finished_s) ); + + if ( NULL == jobgp->notification_obj ) + { + MALI_PRINT_ERROR( ("Mali GP: Could not get notification_obj.\n")) ; + err = _MALI_OSK_ERR_NOMEM; + goto function_exit; + } + + _MALI_OSK_INIT_LIST_HEAD( &(job->list) ) ; + + MALI_DEBUG_PRINT(4, ("Mali GP: Job: 0x%08x INPUT from user.\n", (u32)jobgp->user_input.user_job_ptr)) ; + + /* This should not happen since we have the checking of priority above */ + err = mali_core_session_add_job(session, job, &previous_replaced_job); + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_PRINT_ERROR( ("Mali GP: Internal error\n")) ; + /* Cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + _mali_osk_notification_delete( jobgp->notification_obj ); + goto function_exit; + } + + /* If MALI_TRUE: This session had a job with lower priority which were removed. + This replaced job is given back to userspace. */ + if ( NULL != previous_replaced_job ) + { + maligp_job *previous_replaced_jobgp; + + previous_replaced_jobgp = GET_JOBGP2_PTR(previous_replaced_job); + + MALI_DEBUG_PRINT(4, ("Mali GP: Replacing job: 0x%08x\n", (u32)previous_replaced_jobgp->user_input.user_job_ptr)) ; + + /* Copy to the input data (which also is output data) the + pointer to the job that were replaced, so that the userspace + driver can put this job in the front of its job-queue */ + user_ptr_job_input->returned_user_job_ptr = previous_replaced_jobgp->user_input.user_job_ptr; + + /** @note failure to 'copy to user' at this point must not free jobgp, + * and so no transaction rollback required in the U/K interface */ + + /* This does not cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED; + MALI_DEBUG_PRINT(5, ("subsystem_maligp_get_new_job_from_user: Job added, prev returned\n")) ; + } + else + { + /* This does not cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED; + MALI_DEBUG_PRINT(5, ("subsystem_maligp_get_new_job_from_user: Job added\n")) ; + } + +function_exit: + if ( _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE == user_ptr_job_input->status + || _MALI_OSK_ERR_OK != err ) + { + _mali_osk_free(jobgp); + } + MALI_ERROR(err); +} + + +static _mali_osk_errcode_t subsystem_maligp_suspend_response(struct mali_core_session * session, void * argument) +{ + mali_core_renderunit *core; + maligp_job *jobgp; + mali_core_job *job; + + _mali_uk_gp_suspend_response_s * suspend_response; + + MALI_DEBUG_PRINT(5, ("subsystem_maligp_suspend_response\n")); + + suspend_response = (_mali_uk_gp_suspend_response_s *)argument; + + /* We read job data from User */ + /* On a single mali_gp system we can only have one Stalled GP, + and therefore one stalled request with a cookie. This checks + that we get the correct cookie */ + if ( last_gp_core_cookie != (mali_core_renderunit *)suspend_response->cookie ) + { + MALI_DEBUG_PRINT(2, ("Mali GP: Got an illegal cookie from Userspace.\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + core = (mali_core_renderunit *)suspend_response->cookie; + last_gp_core_cookie = NULL; + job = core->current_job; + jobgp = GET_JOBGP2_PTR(job); + + switch( suspend_response->code ) + { + case _MALIGP_JOB_RESUME_WITH_NEW_HEAP : + MALI_DEBUG_PRINT(5, ("MALIGP_JOB_RESUME_WITH_NEW_HEAP jiffies: %li\n", _mali_osk_time_tickcount())); + MALI_DEBUG_PRINT(4, ("New Heap addr 0x%08x - 0x%08x\n", suspend_response->arguments[0], suspend_response->arguments[1])); + + jobgp->is_stalled_waiting_for_more_memory = 0; + job->watchdog_jiffies += _mali_osk_time_tickcount(); /* convert to absolute time again */ + _mali_osk_timer_mod( core->timer, job->watchdog_jiffies); /* update the timer */ + + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, (MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | MALIGP2_REG_VAL_IRQ_HANG)); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR, suspend_response->arguments[0]); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR, suspend_response->arguments[1]); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_RESUME|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); +#endif + + MALI_DEBUG_PRINT(4, ("GP resumed with new heap\n")); + + break; + + case _MALIGP_JOB_ABORT: + MALI_DEBUG_PRINT(3, ("MALIGP_JOB_ABORT on heap extend request\n")); + _mali_osk_irq_schedulework( core->irq ); + break; + + default: + MALI_PRINT_ERROR(("Wrong Suspend response from userspace\n")); + } + MALI_SUCCESS; +} + +/* This function is called from the ioctl function and should write the necessary data +to userspace telling which job was finished and the status and debuginfo for this job. +The function must also free and cleanup the input job object. */ +static void subsystem_maligp_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status ) +{ + maligp_job *jobgp; + _mali_uk_gp_job_finished_s * job_out; + _mali_uk_gp_start_job_s* job_input; + mali_core_session *session; + + + jobgp = _MALI_OSK_CONTAINER_OF(job, maligp_job, embedded_core_job); + job_out = (_mali_uk_gp_job_finished_s *)jobgp->notification_obj->result_buffer; /* OK - this should've been malloc'd */ + job_input= &(jobgp->user_input); + session = job->session; + + MALI_DEBUG_PRINT(5, ("Mali GP: Job: 0x%08x OUTPUT to user. Runtime: %d ms, irq readout %x\n", + (u32)jobgp->user_input.user_job_ptr, + job->render_time_msecs, + jobgp->irq_status)) ; + + _mali_osk_memset(job_out, 0 , sizeof(_mali_uk_gp_job_finished_s)); + + job_out->user_job_ptr = job_input->user_job_ptr; + + switch( end_status ) + { + case JOB_STATUS_CONTINUE_RUN: + case JOB_STATUS_END_SUCCESS: + case JOB_STATUS_END_OOM: + case JOB_STATUS_END_ABORT: + case JOB_STATUS_END_TIMEOUT_SW: + case JOB_STATUS_END_HANG: + case JOB_STATUS_END_SEG_FAULT: + case JOB_STATUS_END_ILLEGAL_JOB: + case JOB_STATUS_END_UNKNOWN_ERR: + case JOB_STATUS_END_SHUTDOWN: + case JOB_STATUS_END_SYSTEM_UNUSABLE: + job_out->status = (mali_subsystem_job_end_code) end_status; + break; + default: + job_out->status = JOB_STATUS_END_UNKNOWN_ERR ; + } + + job_out->irq_status = jobgp->irq_status; + job_out->status_reg_on_stop = jobgp->status_reg_on_stop; + job_out->vscl_stop_addr = 0; + job_out->plbcl_stop_addr = 0; + job_out->heap_current_addr = jobgp->heap_current_addr; + job_out->perf_counter0 = jobgp->perf_counter0; + job_out->perf_counter1 = jobgp->perf_counter1; + job_out->perf_counter_src0 = jobgp->user_input.perf_counter_src0 ; + job_out->perf_counter_src1 = jobgp->user_input.perf_counter_src1 ; + job_out->render_time = job->render_time_msecs; +#if defined(USING_MALI400_L2_CACHE) + job_out->perf_counter_l2_src0 = jobgp->perf_counter_l2_src0; + job_out->perf_counter_l2_src1 = jobgp->perf_counter_l2_src1; + job_out->perf_counter_l2_val0 = jobgp->perf_counter_l2_val0; + job_out->perf_counter_l2_val1 = jobgp->perf_counter_l2_val1; +#endif + + _mali_osk_notification_queue_send( session->notification_queue, jobgp->notification_obj); + jobgp->notification_obj = NULL; + + _mali_osk_free(jobgp); + + last_gp_core_cookie = NULL; +} + +static void subsystem_maligp_renderunit_delete(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Mali GP: maligp_renderunit_delete\n")); + _mali_osk_free(core); +} + +static void subsystem_maligp_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style) +{ + MALI_DEBUG_PRINT(5, ("Mali GP: renderunit_reset_core\n")); + + switch (style) + { + case MALI_CORE_RESET_STYLE_RUNABLE: + maligp_reset(core); + break; + case MALI_CORE_RESET_STYLE_DISABLE: + maligp_raw_reset(core); /* do the raw reset */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* then disable the IRQs */ + break; + case MALI_CORE_RESET_STYLE_HARD: + maligp_reset_hard(core); + maligp_initialize_registers_mgmt(core); + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown reset type %d\n", style)); + break; + } +} + +static void subsystem_maligp_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core , MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); + mali_core_renderunit_register_write(core , MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT, MALIGP2_REG_VAL_CMD_FORCE_HANG ); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t subsystem_maligp_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core) +{ + u32 irq_readout; + + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_STAT); + + if ( MALIGP2_REG_VAL_IRQ_FORCE_HANG & irq_readout ) + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +_mali_osk_errcode_t _mali_ukk_gp_start_job( _mali_uk_gp_start_job_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_start_job(session, args); +} + +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_number_of_cores_get(session, &args->number_of_cores); +} + +_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_core_version_get(session, &args->version); +} + +_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_suspend_response(session, args); +} + +void _mali_ukk_gp_abort_job( _mali_uk_gp_abort_job_s * args) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + if (NULL == args->ctx) return; + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + if (NULL == session) return; + mali_core_subsystem_ioctl_abort_job(session, args->abort_id); + +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t maligp_signal_power_up( mali_bool queue_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali GP: signal power up core - queue_only: %d\n", queue_only )); + + return( mali_core_subsystem_signal_power_up( &subsystem_maligp, 0, queue_only ) ); +} + +_mali_osk_errcode_t maligp_signal_power_down( mali_bool immediate_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali GP: signal power down core - immediate_only: %d\n", immediate_only )); + + return( mali_core_subsystem_signal_power_down( &subsystem_maligp, 0, immediate_only ) ); +} + +#endif + +#if MALI_STATE_TRACKING +void maligp_subsystem_dump_state(void) +{ + mali_core_renderunit_dump_state(&subsystem_maligp); +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_MALI200.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_MALI200.c new file mode 100644 index 00000000000..0ac49379bea --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_MALI200.c @@ -0,0 +1,1187 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_core.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_rendercore.h" +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_kernel_profiling.h" +#endif +#ifdef USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" /* Needed for mali_kernel_mmu_force_bus_reset() */ +#endif + +#include "mali_osk_list.h" + +#if defined(USING_MALI200) +#define MALI_PP_SUBSYSTEM_NAME "Mali200" +#define MALI_PP_CORE_TYPE _MALI_200 +#elif defined(USING_MALI400) +#define MALI_PP_SUBSYSTEM_NAME "Mali-400 PP" +#define MALI_PP_CORE_TYPE _MALI_400_PP +#else +#error "No supported mali core defined" +#endif + +#define GET_JOB_EMBEDDED_PTR(job) (&((job)->embedded_core_job)) +#define GET_JOB200_PTR(job_extern) _MALI_OSK_CONTAINER_OF(job_extern, mali200_job, embedded_core_job) + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_mali200_id = -1; + +/* Describing a mali200 job settings */ +typedef struct mali200_job +{ + /* The general job struct common for all mali cores */ + mali_core_job embedded_core_job; + _mali_uk_pp_start_job_s user_input; + + u32 irq_status; + u32 perf_counter0; + u32 perf_counter1; + u32 last_tile_list_addr; /* Neccessary to continue a stopped job */ + + u32 active_mask; + + /* The data we will return back to the user */ + _mali_osk_notification_t *notification_obj; + +#if defined(USING_MALI400_L2_CACHE) + u32 perf_counter_l2_src0; + u32 perf_counter_l2_src1; + u32 perf_counter_l2_val0; + u32 perf_counter_l2_val1; + u32 perf_counter_l2_val0_raw; + u32 perf_counter_l2_val1_raw; +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + u32 pid; + u32 tid; +#endif +} mali200_job; + + +/*Functions Exposed to the General External System through + funciont pointers.*/ + +static _mali_osk_errcode_t mali200_subsystem_startup(mali_kernel_subsystem_identifier id); +#if USING_MMU +static _mali_osk_errcode_t mali200_subsystem_mmu_connect(mali_kernel_subsystem_identifier id); +#endif +static void mali200_subsystem_terminate(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t mali200_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); +static void mali200_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); +static _mali_osk_errcode_t mali200_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t mali200_renderunit_create(_mali_osk_resource_t * resource); +#if USING_MMU +static void mali200_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif +#if MALI_STATE_TRACKING +void mali200_subsystem_dump_state(void); +#endif + +/* Internal support functions */ +static _mali_osk_errcode_t mali200_core_version_legal( mali_core_renderunit *core ); +static void mali200_reset(mali_core_renderunit *core); +static void mali200_reset_hard(struct mali_core_renderunit * core); +static void mali200_raw_reset(mali_core_renderunit * core); +static void mali200_initialize_registers_mgmt(mali_core_renderunit *core ); + +/* Functions exposed to mali_core system through functionpointers + in the subsystem struct. */ +static _mali_osk_errcode_t subsystem_mali200_start_job(mali_core_job * job, mali_core_renderunit * core); +static _mali_osk_errcode_t subsystem_mali200_get_new_job_from_user(struct mali_core_session * session, void * argument); +static void subsystem_mali200_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status); +static void subsystem_mali200_renderunit_delete(mali_core_renderunit * core); +static void subsystem_mali200_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style); +static void subsystem_mali200_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_mali200_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core); + +static void subsystem_mali200_renderunit_stop_bus(struct mali_core_renderunit* core); +static u32 subsystem_mali200_irq_handler_upper_half(struct mali_core_renderunit * core); +static int subsystem_mali200_irq_handler_bottom_half(struct mali_core_renderunit* core); + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ + +struct mali_kernel_subsystem mali_subsystem_mali200= +{ + mali200_subsystem_startup, /* startup */ + mali200_subsystem_terminate, /* shutdown */ +#if USING_MMU + mali200_subsystem_mmu_connect, /* load_complete */ +#else + NULL, +#endif + mali200_subsystem_core_system_info_fill, /* system_info_fill */ + mali200_subsystem_session_begin, /* session_begin */ + mali200_subsystem_session_end, /* session_end */ +#if USING_MMU + mali200_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + mali200_subsystem_dump_state, /* dump_state */ +#endif +} ; + +static mali_core_subsystem subsystem_mali200 ; + +static _mali_osk_errcode_t mali200_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem * subsystem; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_startup\n") ) ; + + mali_subsystem_mali200_id = id; + + /* All values get 0 as default */ + _mali_osk_memset(&subsystem_mali200, 0, sizeof(subsystem_mali200)); + + subsystem = &subsystem_mali200; + subsystem->start_job = &subsystem_mali200_start_job; + subsystem->irq_handler_upper_half = &subsystem_mali200_irq_handler_upper_half; + subsystem->irq_handler_bottom_half = &subsystem_mali200_irq_handler_bottom_half; + subsystem->get_new_job_from_user = &subsystem_mali200_get_new_job_from_user; + subsystem->return_job_to_user = &subsystem_mali200_return_job_to_user; + subsystem->renderunit_delete = &subsystem_mali200_renderunit_delete; + subsystem->reset_core = &subsystem_mali200_renderunit_reset_core; + subsystem->stop_bus = &subsystem_mali200_renderunit_stop_bus; + subsystem->probe_core_irq_trigger = &subsystem_mali200_renderunit_probe_core_irq_trigger; + subsystem->probe_core_irq_acknowledge = &subsystem_mali200_renderunit_probe_core_irq_finished; + + /* Setting variables in the general core part of the subsystem.*/ + subsystem->name = MALI_PP_SUBSYSTEM_NAME; + subsystem->core_type = MALI_PP_CORE_TYPE; + subsystem->id = id; + + /* Initiates the rest of the general core part of the subsystem */ + MALI_CHECK_NO_ERROR(mali_core_subsystem_init( subsystem )); + + /* This will register the function for adding MALI200 cores to the subsystem */ +#if defined(USING_MALI200) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI200, mali200_renderunit_create)); +#endif +#if defined(USING_MALI400) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI400PP, mali200_renderunit_create)); +#endif + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_startup\n") ) ; + + MALI_SUCCESS; +} + +#if USING_MMU +static _mali_osk_errcode_t mali200_subsystem_mmu_connect(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem_attach_mmu(&subsystem_mali200); + MALI_SUCCESS; /* OK */ +} +#endif + +static void mali200_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_terminate\n") ) ; + mali_core_subsystem_cleanup(&subsystem_mali200); +} + +static _mali_osk_errcode_t mali200_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_session_begin\n") ) ; + MALI_CHECK_NON_NULL(session = _mali_osk_malloc( sizeof(mali_core_session) ), _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(session, 0, sizeof(*session) ); + *slot = (mali_kernel_subsystem_session_slot)session; + + session->subsystem = &subsystem_mali200; + + session->notification_queue = queue; + +#if USING_MMU + session->mmu_session = mali_session_data; +#endif + + mali_core_session_begin(session); + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_session_begin\n") ) ; + + MALI_SUCCESS; +} + +static void mali200_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_session_end\n") ) ; + if ( NULL==slot || NULL==*slot) + { + MALI_PRINT_ERROR(("Input slot==NULL")); + return; + } + session = (mali_core_session*) *slot; + mali_core_session_close(session); + + _mali_osk_free(session); + *slot = NULL; + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_session_end\n") ) ; +} + +/** + * We fill in info about all the cores we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali200_subsystem_core_system_info_fill(_mali_system_info* info) +{ + return mali_core_subsystem_system_info_fill(&subsystem_mali200, info); +} + + +static _mali_osk_errcode_t mali200_renderunit_create(_mali_osk_resource_t * resource) +{ + mali_core_renderunit *core; + _mali_osk_errcode_t err; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_renderunit_create\n") ) ; + /* Checking that the resource settings are correct */ +#if defined(USING_MALI200) + if(MALI200 != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_PP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#elif defined(USING_MALI400) + if(MALI400PP != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_PP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + if ( 0 != resource->size ) + { + MALI_PRINT_ERROR(("Memory size set to " MALI_PP_SUBSYSTEM_NAME " core should be zero.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if ( NULL == resource->description ) + { + MALI_PRINT_ERROR(("A " MALI_PP_SUBSYSTEM_NAME " core needs a unique description field")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Create a new core object */ + core = (mali_core_renderunit*) _mali_osk_malloc(sizeof(*core)); + if ( NULL == core ) + { + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* Variables set to be able to open and register the core */ + core->subsystem = &subsystem_mali200 ; + core->registers_base_addr = resource->base ; + core->size = MALI200_REG_SIZEOF_REGISTER_BANK ; + core->irq_nr = resource->irq ; + core->description = resource->description; +#if USING_MMU + core->mmu_id = resource->mmu_id; + core->mmu = NULL; +#endif +#if USING_MALI_PMM + /* Set up core's PMM id */ + switch( subsystem_mali200.number_of_cores ) + { + case 0: + core->pmm_id = MALI_PMM_CORE_PP0; + break; + case 1: + core->pmm_id = MALI_PMM_CORE_PP1; + break; + case 2: + core->pmm_id = MALI_PMM_CORE_PP2; + break; + case 3: + core->pmm_id = MALI_PMM_CORE_PP3; + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown supported core for PMM\n")); + err = _MALI_OSK_ERR_FAULT; + goto exit_on_error0; + } +#endif + + err = mali_core_renderunit_init( core ); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to initialize renderunit\n")); + goto exit_on_error0; + } + + /* Map the new core object, setting: core->registers_mapped */ + err = mali_core_renderunit_map_registers(core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to map register\n")); + goto exit_on_error1; + } + + /* Check that the register mapping of the core works. + Return 0 if Mali PP core is present and accessible. */ + if (mali_benchmark) { +#if defined(USING_MALI200) + core->core_version = (((u32)MALI_PP_PRODUCT_ID) << 16) | 5 /* Fake Mali200-r0p5 */; +#elif defined(USING_MALI400) + core->core_version = (((u32)MALI_PP_PRODUCT_ID) << 16) | 0x0101 /* Fake Mali400-r1p1 */; +#else +#error "No supported mali core defined" +#endif + } else { + core->core_version = mali_core_renderunit_register_read( + core, + MALI200_REG_ADDR_MGMT_VERSION); + } + + err = mali200_core_version_legal(core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Invalid core\n")); + goto exit_on_error2; + } + + /* Reset the core. Put the core into a state where it can start to render. */ + mali200_reset(core); + + /* Registering IRQ, init the work_queue_irq_handle */ + /* Adding this core as an available renderunit in the subsystem. */ + err = mali_core_subsystem_register_renderunit(&subsystem_mali200, core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to register with core\n")); + goto exit_on_error2; + } + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_renderunit_create\n") ) ; + + MALI_SUCCESS; + +exit_on_error2: + mali_core_renderunit_unmap_registers(core); +exit_on_error1: + mali_core_renderunit_term(core); +exit_on_error0: + _mali_osk_free( core ) ; + MALI_PRINT_ERROR(("Renderunit NOT created.")); + MALI_ERROR(err); +} + +#if USING_MMU +/* Used currently only for signalling when MMU has a pagefault */ +static void mali200_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + mali_core_subsystem_broadcast_notification(&subsystem_mali200, message, data); +} +#endif + +static _mali_osk_errcode_t mali200_core_version_legal( mali_core_renderunit *core ) +{ + u32 mali_type; + + mali_type = core->core_version >> 16; +#if defined(USING_MALI400) + /* Mali300 and Mali400 is compatible, accept either core. */ + if (MALI400_PP_PRODUCT_ID != mali_type && MALI300_PP_PRODUCT_ID != mali_type) +#else + if (MALI_PP_PRODUCT_ID != mali_type) +#endif + { + MALI_PRINT_ERROR(("Error: reading this from " MALI_PP_SUBSYSTEM_NAME " version register: 0x%x\n", core->core_version)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + MALI_DEBUG_PRINT(3, ("Mali PP: core_version_legal: Reads correct mali version: %d\n", mali_type) ) ; + MALI_SUCCESS; +} + +static void subsystem_mali200_renderunit_stop_bus(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS); +} + +static void mali200_raw_reset( mali_core_renderunit *core ) +{ + int i; + const int request_loop_count = 20; + + MALI_DEBUG_PRINT(4, ("Mali PP: mali200_raw_reset: %s\n", core->description)); + if (mali_benchmark) return; + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* disable IRQs */ + +#if defined(USING_MALI200) + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_STATUS) & MALI200_REG_VAL_STATUS_BUS_STOPPED) break; + _mali_osk_time_ubusydelay(10); + } + + MALI_DEBUG_PRINT_IF(1, request_loop_count == i, ("Mali PP: Bus was never stopped during core reset\n")); + + + if (request_loop_count==i) + { +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + MALI_DEBUG_PRINT(1, ("Mali PP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow PP to stop its bus, system failure, unable to recover\n")); + return; + } + + /* use the hard reset routine to do the actual reset */ + mali200_reset_hard(core); + +#elif defined(USING_MALI400) + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI400PP_REG_VAL_IRQ_RESET_COMPLETED); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI400PP_REG_VAL_IRQ_RESET_COMPLETED) break; + _mali_osk_time_ubusydelay(10); + } + + if (request_loop_count==i) + { +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + MALI_DEBUG_PRINT(1, ("Mali PP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow PP to stop its bus, system failure, unable to recover\n")); + return; + } + else + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); + +#else +#error "no supported mali core defined" +#endif +} + +static void mali200_reset( mali_core_renderunit *core ) +{ + if (!mali_benchmark) { + mali200_raw_reset(core); + mali200_initialize_registers_mgmt(core); + } +} + +/* Sets the registers on mali200 according to the const default_mgmt_regs array. */ +static void mali200_initialize_registers_mgmt(mali_core_renderunit *core ) +{ + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_initialize_registers_mgmt: %s\n", core->description)) ; + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); +} + +/* Start this job on this core. Return MALI_TRUE if the job was started. */ +static _mali_osk_errcode_t subsystem_mali200_start_job(mali_core_job * job, mali_core_renderunit * core) +{ + mali200_job *job200; + + /* The local extended version of the general structs */ + job200 = _MALI_OSK_CONTAINER_OF(job, mali200_job, embedded_core_job); + + if ( (0 == job200->user_input.frame_registers[0]) || + (0 == job200->user_input.frame_registers[1]) ) + { + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x WILL NOT START SINCE JOB HAS ILLEGAL ADDRESSES\n", + (u32)job200->user_input.user_job_ptr)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x START_RENDER Tile_list: 0x%08x\n", + (u32)job200->user_input.user_job_ptr, + job200->user_input.frame_registers[0])); + MALI_DEBUG_PRINT(6, ("Mali PP: RSW base addr: 0x%08x Vertex base addr: 0x%08x\n", + job200->user_input.frame_registers[1], job200->user_input.frame_registers[2])); + + /* Frame registers. Copy from mem to physical registers */ + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_FRAME, + &(job200->user_input.frame_registers[0]), + MALI200_NUM_REGS_FRAME); + + /* Write Back unit 0. Copy from mem to physical registers*/ + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB0, + &(job200->user_input.wb0_registers[0]), + MALI200_NUM_REGS_WBx); + + /* Write Back unit 1. Copy from mem to physical registers */ + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB1, + &(job200->user_input.wb1_registers[0]), + MALI200_NUM_REGS_WBx); + + /* Write Back unit 2. Copy from mem to physical registers */ + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB2, + &(job200->user_input.wb2_registers[0]), + MALI200_NUM_REGS_WBx); + + + /* This selects which performance counters we are reading */ + if ( 0 != job200->user_input.perf_counter_flag ) + { + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) + { + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, + MALI200_REG_VAL_PERF_CNT_ENABLE); + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, + job200->user_input.perf_counter_src0); + + } + + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) + { + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALI200_REG_VAL_PERF_CNT_ENABLE); + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, + job200->user_input.perf_counter_src1); + + } + +#if defined(USING_MALI400_L2_CACHE) + if ( job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + int force_reset = ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET ) ? 1 : 0; + u32 src0 = 0; + u32 src1 = 0; + + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE ) + { + src0 = job200->user_input.perf_counter_l2_src0; + } + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE ) + { + src1 = job200->user_input.perf_counter_l2_src1; + } + + mali_kernel_l2_cache_set_perf_counters(src0, src1, force_reset); /* will activate and possibly reset counters */ + + /* Now, retrieve the current values, so we can substract them when the job has completed */ + mali_kernel_l2_cache_get_perf_counters(&job200->perf_counter_l2_src0, + &job200->perf_counter_l2_val0, + &job200->perf_counter_l2_src1, + &job200->perf_counter_l2_val1); + } +#endif + } + + subsystem_flush_mapped_mem_cache(); + _mali_osk_mem_barrier(); + + /* This is the command that starts the Core */ + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_CTRL_MGMT, + MALI200_REG_VAL_CTRL_MGMT_START_RENDERING); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), job200->pid, job200->tid, 0, 0, 0); +#endif + + MALI_SUCCESS; +} + +static u32 subsystem_mali200_irq_handler_upper_half(mali_core_renderunit * core) +{ + u32 irq_readout; + + if (mali_benchmark) { + return (core->current_job ? 1 : 0); /* simulate irq is pending when a job is pending */ + } + + MALI_DEBUG_PRINT(5, ("Mali PP: subsystem_mali200_irq_handler_upper_half: %s\n", core->description)) ; + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_STATUS); + + if ( MALI200_REG_VAL_IRQ_MASK_NONE != irq_readout ) + { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE); + return 1; + } + return 0; +} + +static int subsystem_mali200_irq_handler_bottom_half(struct mali_core_renderunit* core) +{ + u32 irq_readout; + u32 current_tile_addr; + u32 core_status; + mali_core_job * job; + mali200_job * job200; + + job = core->current_job; + job200 = GET_JOB200_PTR(job); + + + if (mali_benchmark) { + irq_readout = MALI200_REG_VAL_IRQ_END_OF_FRAME; + current_tile_addr = 0; + core_status = 0; + } else { + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI200_REG_VAL_IRQ_MASK_USED; + current_tile_addr = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR); + core_status = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_STATUS); + } + + if (NULL == job) + { + MALI_DEBUG_ASSERT(CORE_IDLE==core->state); + if ( 0 != irq_readout ) + { + MALI_PRINT_ERROR(("Interrupt from a core not running a job. IRQ: 0x%04x Status: 0x%04x", irq_readout, core_status)); + } + return JOB_STATUS_END_UNKNOWN_ERR; + } + MALI_DEBUG_ASSERT(CORE_IDLE!=core->state); + + job200->irq_status |= irq_readout; + + MALI_DEBUG_PRINT_IF( 3, ( 0 != irq_readout ), + ("Mali PP: Job: 0x%08x IRQ RECEIVED Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x\n", + (u32)job200->user_input.user_job_ptr, irq_readout ,current_tile_addr ,core_status)); + + if ( MALI200_REG_VAL_IRQ_END_OF_FRAME & irq_readout) + { +#if defined(USING_MALI200) + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_FLUSH_CACHES); +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status */ +#endif + + if (0 != job200->user_input.perf_counter_flag ) + { + if (job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { + job200->perf_counter0 = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + job200->perf_counter1 = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + } + +#if defined(USING_MALI400_L2_CACHE) + if (job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (job200->perf_counter_l2_src0 == src0) + { + job200->perf_counter_l2_val0_raw = val0; + job200->perf_counter_l2_val0 = val0 - job200->perf_counter_l2_val0; + } + else + { + job200->perf_counter_l2_val0_raw = 0; + job200->perf_counter_l2_val0 = 0; + } + + if (job200->perf_counter_l2_src1 == src1) + { + job200->perf_counter_l2_val1_raw = val1; + job200->perf_counter_l2_val1 = val1 - job200->perf_counter_l2_val1; + } + else + { + job200->perf_counter_l2_val1_raw = 0; + job200->perf_counter_l2_val1 = 0; + } + } +#endif + + } + + return JOB_STATUS_END_SUCCESS; /* reschedule */ + } + /* Overall SW watchdog timeout or (time to do hang checking and progress detected)? */ + else if ( + (CORE_WATCHDOG_TIMEOUT == core->state) || + ((CORE_HANG_CHECK_TIMEOUT == core->state) && (current_tile_addr == job200->last_tile_list_addr)) + ) + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status */ +#endif + /* no progress detected, killed by the watchdog */ + MALI_DEBUG_PRINT(2, ("M200: SW-Timeout Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x.\n", irq_readout ,current_tile_addr ,core_status) ); + /* In this case will the system outside cleanup and reset the core */ + return JOB_STATUS_END_HANG; + } + /* HW watchdog triggered or an existing hang check passed? */ + else if ((CORE_HANG_CHECK_TIMEOUT == core->state) || (irq_readout & job200->active_mask & MALI200_REG_VAL_IRQ_HANG)) + { + /* check interval in ms */ + u32 timeout = mali_core_hang_check_timeout_get(); + MALI_DEBUG_PRINT(3, ("M200: HW/SW Watchdog triggered, checking for progress in %d ms\n", timeout)); + job200->last_tile_list_addr = current_tile_addr; + /* hw watchdog triggered, set up a progress checker every HANGCHECK ms */ + _mali_osk_timer_add(core->timer_hang_detection, _mali_osk_time_mstoticks(timeout)); + job200->active_mask &= ~MALI200_REG_VAL_IRQ_HANG; /* ignore the hw watchdoig from now on */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, irq_readout & ~MALI200_REG_VAL_IRQ_HANG); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, job200->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* not finished */ + } + /* No irq pending, core still busy */ + else if ((0 == (irq_readout & MALI200_REG_VAL_IRQ_MASK_USED)) && ( 0 != (core_status & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE))) + { + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, job200->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* Not finished */ + } + else + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali PP: Job: 0x%08x CRASH? Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x\n", + (u32)job200->user_input.user_job_ptr, irq_readout ,current_tile_addr ,core_status) ) ; + + if (irq_readout & MALI200_REG_VAL_IRQ_BUS_ERROR) + { + u32 bus_error = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS); + + MALI_DEBUG_PRINT(1, ("Bus error status: 0x%08X\n", bus_error)); + MALI_DEBUG_PRINT_IF(1, (bus_error & 0x01), ("Bus write error from id 0x%02x\n", (bus_error>>2) & 0x0F)); + MALI_DEBUG_PRINT_IF(1, (bus_error & 0x02), ("Bus read error from id 0x%02x\n", (bus_error>>6) & 0x0F)); + MALI_DEBUG_PRINT_IF(1, (0 == (bus_error & 0x03)), ("Bus error but neither read or write was set as the error reason\n")); + (void)bus_error; + } + + return JOB_STATUS_END_UNKNOWN_ERR; /* reschedule */ + } +} + + +/* This function is called from the ioctl function and should return a mali_core_job pointer +to a created mali_core_job object with the data given from userspace */ +static _mali_osk_errcode_t subsystem_mali200_get_new_job_from_user(struct mali_core_session * session, void * argument) +{ + mali200_job *job200; + mali_core_job *job; + mali_core_job *previous_replaced_job; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_uk_pp_start_job_s * user_ptr_job_input; + + user_ptr_job_input = (_mali_uk_pp_start_job_s *)argument; + + MALI_CHECK_NON_NULL(job200 = (mali200_job *) _mali_osk_malloc(sizeof(mali200_job)), _MALI_OSK_ERR_NOMEM); + _mali_osk_memset(job200, 0 , sizeof(mali200_job) ); + + /* We read job data from Userspace pointer */ + if ( NULL == _mali_osk_memcpy((void*)&job200->user_input, user_ptr_job_input, sizeof(job200->user_input)) ) + { + MALI_PRINT_ERROR( ("Mali PP: Could not copy data from U/K interface.\n")) ; + err = _MALI_OSK_ERR_FAULT; + goto function_exit; + } + + MALI_DEBUG_PRINT(5, ("Mali PP: subsystem_mali200_get_new_job_from_user 0x%x\n", (void*)job200->user_input.user_job_ptr)); + + MALI_DEBUG_PRINT(5, ("Mali PP: Frameregs: 0x%x 0x%x 0x%x Writeback[1] 0x%x, Pri:%d; Watchd:%d\n", + job200->user_input.frame_registers[0], job200->user_input.frame_registers[1], job200->user_input.frame_registers[2], + job200->user_input.wb0_registers[1], job200->user_input.priority, + job200->user_input.watchdog_msecs)); + + if ( job200->user_input.perf_counter_flag) + { +#if defined(USING_MALI400_L2_CACHE) + MALI_DEBUG_PRINT(5, ("Mali PP: Performance counters: flag:0x%x src0:0x%x src1:0x%x l2_src0:0x%x l2_src1:0x%x\n", + job200->user_input.perf_counter_flag, + job200->user_input.perf_counter_src0, + job200->user_input.perf_counter_src1, + job200->user_input.perf_counter_l2_src0, + job200->user_input.perf_counter_l2_src1)); +#else + MALI_DEBUG_PRINT(5, ("Mali PP: Performance counters: flag:0x%x src0:0x%x src1:0x%x\n", + job200->user_input.perf_counter_flag, + job200->user_input.perf_counter_src0, + job200->user_input.perf_counter_src1)); +#endif + } + + job = GET_JOB_EMBEDDED_PTR(job200); + + job->session = session; + job_priority_set(job, job200->user_input.priority); + job_watchdog_set(job, job200->user_input.watchdog_msecs ); + +#if MALI_TIMELINE_PROFILING_ENABLED + job200->pid = _mali_osk_get_pid(); + job200->tid = _mali_osk_get_tid(); +#endif + + job->abort_id = job200->user_input.abort_id; + if (NULL != session->job_waiting_to_run) + { + /* IF NOT( newjow HAS HIGHER PRIORITY THAN waitingjob) EXIT_NOT_START newjob */ + if(!job_has_higher_priority(job, session->job_waiting_to_run)) + { + /* The job we try to add does NOT have higher pri than current */ + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + goto function_exit; + } + } + + /* We now know that we has a job, and a empty session slot to put it in */ + + job200->active_mask = MALI200_REG_VAL_IRQ_MASK_USED; + + /* Allocating User Return Data */ + job200->notification_obj = _mali_osk_notification_create( + _MALI_NOTIFICATION_PP_FINISHED, + sizeof(_mali_uk_pp_job_finished_s) ); + + if ( NULL == job200->notification_obj ) + { + MALI_PRINT_ERROR( ("Mali PP: Could not get notification_obj.\n")) ; + err = _MALI_OSK_ERR_NOMEM; + goto function_exit; + } + + _MALI_OSK_INIT_LIST_HEAD( &(job->list) ) ; + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x INPUT from user.\n", (u32)job200->user_input.user_job_ptr)) ; + + /* This should not happen since we have the checking of priority above */ + if ( _MALI_OSK_ERR_OK != mali_core_session_add_job(session, job, &previous_replaced_job)) + { + MALI_PRINT_ERROR( ("Mali PP: Internal error\n")) ; + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + _mali_osk_notification_delete( job200->notification_obj ); + goto function_exit; + } + + /* If MALI_TRUE: This session had a job with lower priority which were removed. + This replaced job is given back to userspace. */ + if ( NULL != previous_replaced_job ) + { + mali200_job *previous_replaced_job200; + + previous_replaced_job200 = GET_JOB200_PTR(previous_replaced_job); + + MALI_DEBUG_PRINT(4, ("Mali PP: Replacing job: 0x%08x\n", (u32)previous_replaced_job200->user_input.user_job_ptr)) ; + + /* Copy to the input data (which also is output data) the + pointer to the job that were replaced, so that the userspace + driver can put this job in the front of its job-queue */ + + user_ptr_job_input->returned_user_job_ptr = previous_replaced_job200->user_input.user_job_ptr; + + /** @note failure to 'copy to user' at this point must not free job200, + * and so no transaction rollback required in the U/K interface */ + + /* This does not cause job200 to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED; + MALI_DEBUG_PRINT(5, ("subsystem_mali200_get_new_job_from_user: Job added, prev returned\n")) ; + } + else + { + /* This does not cause job200 to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED; + MALI_DEBUG_PRINT(5, ("subsystem_mali200_get_new_job_from_user: Job added\n")) ; + } + +function_exit: + if (_MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE == user_ptr_job_input->status + || _MALI_OSK_ERR_OK != err ) + { + _mali_osk_free(job200); + } + MALI_ERROR(err); +} + +/* This function is called from the ioctl function and should write the necessary data +to userspace telling which job was finished and the status and debuginfo for this job. +The function must also free and cleanup the input job object. */ +static void subsystem_mali200_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status) +{ + mali200_job *job200; + _mali_uk_pp_job_finished_s * job_out; + _mali_uk_pp_start_job_s * job_input; + mali_core_session *session; + + if (NULL == job) + { + MALI_DEBUG_PRINT(1, ("subsystem_mali200_return_job_to_user received a NULL ptr\n")); + return; + } + + job200 = _MALI_OSK_CONTAINER_OF(job, mali200_job, embedded_core_job); + + if (NULL == job200->notification_obj) + { + MALI_DEBUG_PRINT(1, ("Found job200 with NULL notification object, abandoning userspace sending\n")); + return; + } + + job_out = job200->notification_obj->result_buffer; + job_input= &(job200->user_input); + session = job->session; + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x OUTPUT to user. Runtime: %dms\n", + (u32)job200->user_input.user_job_ptr, + job->render_time_msecs)) ; + + _mali_osk_memset(job_out, 0 , sizeof(_mali_uk_pp_job_finished_s)); + + job_out->user_job_ptr = job_input->user_job_ptr; + + switch( end_status ) + { + case JOB_STATUS_CONTINUE_RUN: + case JOB_STATUS_END_SUCCESS: + case JOB_STATUS_END_OOM: + case JOB_STATUS_END_ABORT: + case JOB_STATUS_END_TIMEOUT_SW: + case JOB_STATUS_END_HANG: + case JOB_STATUS_END_SEG_FAULT: + case JOB_STATUS_END_ILLEGAL_JOB: + case JOB_STATUS_END_UNKNOWN_ERR: + case JOB_STATUS_END_SHUTDOWN: + case JOB_STATUS_END_SYSTEM_UNUSABLE: + job_out->status = (mali_subsystem_job_end_code) end_status; + break; + + default: + job_out->status = JOB_STATUS_END_UNKNOWN_ERR ; + } + job_out->irq_status = job200->irq_status; + job_out->perf_counter0 = job200->perf_counter0; + job_out->perf_counter1 = job200->perf_counter1; + job_out->render_time = job->render_time_msecs; + +#if defined(USING_MALI400_L2_CACHE) + job_out->perf_counter_l2_src0 = job200->perf_counter_l2_src0; + job_out->perf_counter_l2_src1 = job200->perf_counter_l2_src1; + job_out->perf_counter_l2_val0 = job200->perf_counter_l2_val0; + job_out->perf_counter_l2_val1 = job200->perf_counter_l2_val1; + job_out->perf_counter_l2_val0_raw = job200->perf_counter_l2_val0_raw; + job_out->perf_counter_l2_val1_raw = job200->perf_counter_l2_val1_raw; +#endif + + _mali_osk_notification_queue_send( session->notification_queue, job200->notification_obj); + job200->notification_obj = NULL; + + _mali_osk_free(job200); +} + +static void subsystem_mali200_renderunit_delete(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Mali PP: mali200_renderunit_delete\n")); + _mali_osk_free(core); +} + +static void mali200_reset_hard(struct mali_core_renderunit * core) +{ + const int reset_finished_loop_count = 15; + const u32 reset_wait_target_register = MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW; + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + const u32 reset_default_value = 0; + int i; + + MALI_DEBUG_PRINT(5, ("subsystem_mali200_renderunit_reset_core_hard called for core %s\n", core->description)); + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_invalid_value); + + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_CTRL_MGMT, + MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET); + + for (i = 0; i < reset_finished_loop_count; i++) + { + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_check_value); + if (reset_check_value == mali_core_renderunit_register_read(core, reset_wait_target_register)) + { + MALI_DEBUG_PRINT(5, ("Reset loop exiting after %d iterations\n", i)); + break; + } + _mali_osk_time_ubusydelay(10); + } + + if (i == reset_finished_loop_count) + { + MALI_DEBUG_PRINT(1, ("The reset loop didn't work\n")); + } + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_default_value); /* set it back to the default */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); +} + +static void subsystem_mali200_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style) +{ + MALI_DEBUG_PRINT(5, ("Mali PP: renderunit_reset_core\n")); + + switch (style) + { + case MALI_CORE_RESET_STYLE_RUNABLE: + mali200_reset(core); + break; + case MALI_CORE_RESET_STYLE_DISABLE: + mali200_raw_reset(core); /* do the raw reset */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* then disable the IRQs */ + break; + case MALI_CORE_RESET_STYLE_HARD: + mali200_reset_hard(core); + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown reset type %d\n", style)); + } +} + +static void subsystem_mali200_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t subsystem_mali200_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core) +{ + u32 irq_readout; + + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_STATUS); + + if ( MALI200_REG_VAL_IRQ_FORCE_HANG & irq_readout ) + { + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +_mali_osk_errcode_t _mali_ukk_pp_start_job( _mali_uk_pp_start_job_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_start_job(session, args); +} + +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_number_of_cores_get(session, &args->number_of_cores); +} + +_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_core_version_get(session, &args->version); +} + +void _mali_ukk_pp_abort_job( _mali_uk_pp_abort_job_s * args) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + if (NULL == args->ctx) return; + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + if (NULL == session) return; + mali_core_subsystem_ioctl_abort_job(session, args->abort_id); + +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t malipp_signal_power_up( u32 core_num, mali_bool queue_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali PP: signal power up core: %d - queue_only: %d\n", core_num, queue_only )); + + return( mali_core_subsystem_signal_power_up( &subsystem_mali200, core_num, queue_only ) ); +} + +_mali_osk_errcode_t malipp_signal_power_down( u32 core_num, mali_bool immediate_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali PP: signal power down core: %d - immediate_only: %d\n", core_num, immediate_only )); + + return( mali_core_subsystem_signal_power_down( &subsystem_mali200, core_num, immediate_only ) ); +} + +#endif + +#if MALI_STATE_TRACKING +void mali200_subsystem_dump_state(void) +{ + mali_core_renderunit_dump_state(&subsystem_mali200); +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_common.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_common.h new file mode 100644 index 00000000000..08516c56a9f --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_common.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_COMMON_H__ +#define __MALI_KERNEL_COMMON_H__ + +/* Make sure debug is defined when it should be */ +#ifndef DEBUG + #if defined(_DEBUG) + #define DEBUG + #endif +#endif + +/* The file include several useful macros for error checking, debugging and printing. + * - MALI_PRINTF(...) Do not use this function: Will be included in Release builds. + * - MALI_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=MALI_DEBUG_LEVEL. + * - MALI_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message. + * - MALI_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit. + * - MALI_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer. + * - MALI_DEBUG_CODE( X ) The code inside the macro is only compiled in Debug builds. + * + * The (X) means that you must add an extra parenthesis around the argumentlist. + * + * The printf function: MALI_PRINTF(...) is routed to _mali_osk_debugmsg + * + * Suggested range for the DEBUG-LEVEL is [1:6] where + * [1:2] Is messages with highest priority, indicate possible errors. + * [3:4] Is messages with medium priority, output important variables. + * [5:6] Is messages with low priority, used during extensive debugging. + */ + + /** + * Fundamental error macro. Reports an error code. This is abstracted to allow us to + * easily switch to a different error reporting method if we want, and also to allow + * us to search for error returns easily. + * + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY); + */ +#define MALI_ERROR(error_code) return (error_code) + +/** + * Basic error macro, to indicate success. + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_SUCCESS; + */ +#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK) + +/** + * Basic error macro. This checks whether the given condition is true, and if not returns + * from this function with the supplied error code. This is a macro so that we can override it + * for stress testing. + * + * Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling + * else clauses. Note also no closing semicolon - this is supplied in typical usage: + * + * MALI_CHECK((p!=NULL), ERROR_NO_OBJECT); + */ +#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0) + +/** + * Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR, + * then the value is returned from the enclosing function as an error code. This effectively + * acts as a guard clause, and propagates error values up the call stack. This uses a + * temporary value to ensure that the error expression is not evaluated twice. + * If the counter for forcing a failure has been set using _mali_force_error, this error will be + * returned without evaluating the expression in MALI_CHECK_NO_ERROR + */ +#define MALI_CHECK_NO_ERROR(expression) \ + do { _mali_osk_errcode_t _check_no_error_result=(expression); \ + if(_check_no_error_result != _MALI_OSK_ERR_OK) \ + MALI_ERROR(_check_no_error_result); \ + } while(0) + +/** + * Pointer check macro. Checks non-null pointer. + */ +#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) ) + +/** + * Error macro with goto. This checks whether the given condition is true, and if not jumps + * to the specified label using a goto. The label must therefore be local to the function in + * which this macro appears. This is most usually used to execute some clean-up code before + * exiting with a call to ERROR. + * + * Like the other macros, this is a macro to allow us to override the condition if we wish, + * e.g. to force an error during stress testing. + */ +#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0) + +/** + * Explicitly ignore a parameter passed into a function, to suppress compiler warnings. + * Should only be used with parameter names. + */ +#define MALI_IGNORE(x) x=x + +#define MALI_PRINTF(args) _mali_osk_dbgmsg args; + +#define MALI_PRINT_ERROR(args) do{ \ + MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \ + MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \ + MALI_PRINTF(args); \ + MALI_PRINTF(("\n")); \ + } while(0) + +#define MALI_PRINT(args) do{ \ + MALI_PRINTF(("Mali: ")); \ + MALI_PRINTF(args); \ + } while (0) + +#ifdef DEBUG +extern int mali_debug_level; + +#define MALI_DEBUG_CODE(code) code +#define MALI_DEBUG_PRINT(level, args) do { \ + if((level) <= mali_debug_level)\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } \ + } while (0) + +#define MALI_DEBUG_PRINT_ERROR(args) MALI_PRINT_ERROR(args) + +#define MALI_DEBUG_PRINT_IF(level,condition,args) \ + if((condition)&&((level) <= mali_debug_level))\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +#define MALI_DEBUG_PRINT_ELSE(level, args)\ + else if((level) <= mali_debug_level)\ + { MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +/** + * @note these variants of DEBUG ASSERTS will cause a debugger breakpoint + * to be entered (see _mali_osk_break() ). An alternative would be to call + * _mali_osk_abort(), on OSs that support it. + */ +#define MALI_DEBUG_PRINT_ASSERT(condition, args) do {if( !(condition)) { MALI_PRINT_ERROR(args); _mali_osk_break(); } } while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) {MALI_PRINT_ERROR(("NULL pointer " #pointer)); _mali_osk_break();} } while(0) +#define MALI_DEBUG_ASSERT(condition) do {if( !(condition)) {MALI_PRINT_ERROR(("ASSERT failed: " #condition )); _mali_osk_break();} } while(0) + +#else /* DEBUG */ + +#define MALI_DEBUG_CODE(code) +#define MALI_DEBUG_PRINT(string,args) do {} while(0) +#define MALI_DEBUG_PRINT_ERROR(args) do {} while(0) +#define MALI_DEBUG_PRINT_IF(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ELSE(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ASSERT(condition,args) do {} while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#define MALI_DEBUG_ASSERT(condition) do {} while(0) + +#endif /* DEBUG */ + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __MALI_KERNEL_COMMON_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.c new file mode 100644 index 00000000000..a40808bd2d8 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.c @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_gp.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_kernel_core.h" +#include "mali_kernel_rendercore.h" +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MALI_PMM +#include "mali_pmm.h" +#endif /* USING_MALI_PMM */ + +/* platform specific set up */ +#include "mali_platform.h" + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_core_id = -1; + +/** Pointer to table of resource definitions available to the Mali driver. + * _mali_osk_resources_init() sets up the pointer to this table. + */ +static _mali_osk_resource_t *arch_configuration = NULL; + +/** Number of resources initialized by _mali_osk_resources_init() */ +static u32 num_resources; + +static _mali_osk_errcode_t register_resources( _mali_osk_resource_t **arch_configuration, u32 num_resources ); + +static _mali_osk_errcode_t initialize_subsystems(void); +static void terminate_subsystems(void); + +static _mali_osk_errcode_t mali_kernel_subsystem_core_setup(mali_kernel_subsystem_identifier id); +static void mali_kernel_subsystem_core_cleanup(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t mali_kernel_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t mali_kernel_subsystem_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +static _mali_osk_errcode_t build_system_info(void); + +/** + * @brief handler for MEM_VALIDATION resources + * + * This resource handler is common to all memory systems. It provides a default + * means for validating requests to map in external memory via + * _mali_ukk_map_external_mem. In addition, if _mali_ukk_va_to_pa is + * implemented, then _mali_ukk_va_to_pa can make use of this MEM_VALIDATION + * resource. + * + * MEM_VALIDATION also provide a CPU physical to Mali physical address + * translation, for use by _mali_ukk_map_external_mem. + * + * @note MEM_VALIDATION resources are only to handle simple cases where a + * certain physical address range is allowed to be mapped in by any process, + * e.g. a framebuffer at a fixed location. If the implementor has more complex + * mapping requirements, then they must either: + * - implement their own memory validation function + * - or, integrate with UMP. + * + * @param resource The resource to handle (type MEM_VALIDATION) + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +static _mali_osk_errcode_t mali_kernel_core_resource_mem_validation(_mali_osk_resource_t * resource); + +/* MEM_VALIDATION handler state */ +typedef struct +{ + u32 phys_base; /**< Mali physical base of the memory, page aligned */ + u32 size; /**< size in bytes of the memory, multiple of page size */ + s32 cpu_usage_adjust; /**< Offset to add to Mali Physical address to obtain CPU physical address */ +} _mali_mem_validation_t; + +#define INVALID_MEM 0xffffffff + +static _mali_mem_validation_t mem_validator = { INVALID_MEM, INVALID_MEM, -1 }; + +static struct mali_kernel_subsystem mali_subsystem_core = +{ + mali_kernel_subsystem_core_setup, /* startup */ + mali_kernel_subsystem_core_cleanup, /* shutdown */ + NULL, /* load_complete */ + mali_kernel_subsystem_core_system_info_fill, /* system_info_fill */ + mali_kernel_subsystem_core_session_begin, /* session_begin */ + NULL, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +static struct mali_kernel_subsystem * subsystems[] = +{ + /* always initialize the hw subsystems first */ + /* always included */ + &mali_subsystem_memory, + +#if USING_MALI_PMM + /* The PMM must be initialized before any cores - including L2 cache */ + &mali_subsystem_pmm, +#endif + + /* The rendercore subsystem must be initialized before any subsystem based on the + * rendercores is started e.g. mali_subsystem_mali200 and mali_subsystem_gp2 */ + &mali_subsystem_rendercore, + + /* add reference to the subsystem */ + &mali_subsystem_mali200, + + /* add reference to the subsystem */ + &mali_subsystem_gp2, + +#if defined USING_MALI400_L2_CACHE + &mali_subsystem_l2_cache, +#endif + + /* always included */ + /* NOTE Keep the core entry at the tail of the list */ + &mali_subsystem_core +}; + +#define SUBSYSTEMS_COUNT ( sizeof(subsystems) / sizeof(subsystems[0]) ) + +/* Pointers to this type available as incomplete struct in mali_kernel_session_manager.h */ +struct mali_session_data +{ + void * subsystem_data[SUBSYSTEMS_COUNT]; + _mali_osk_notification_queue_t * ioctl_queue; +}; + +static mali_kernel_resource_registrator resource_handler[RESOURCE_TYPE_COUNT] = { NULL, }; + +/* system info variables */ +static _mali_osk_lock_t *system_info_lock = NULL; +static _mali_system_info * system_info = NULL; +static u32 system_info_size = 0; + +/* is called from OS specific driver entry point */ +_mali_osk_errcode_t mali_kernel_constructor( void ) +{ + _mali_osk_errcode_t err; + + err = mali_platform_init(NULL); + if (_MALI_OSK_ERR_OK != err) goto error1; + + err = _mali_osk_init(); + if (_MALI_OSK_ERR_OK != err) goto error2; + + MALI_DEBUG_PRINT(2, ("\n")); + MALI_DEBUG_PRINT(2, ("Inserting Mali v%d device driver. \n",_MALI_API_VERSION)); + MALI_DEBUG_PRINT(2, ("Compiled: %s, time: %s.\n", __DATE__, __TIME__)); + MALI_DEBUG_PRINT(2, ("Svn revision: %s\n", SVN_REV_STRING)); + + err = initialize_subsystems(); + if (_MALI_OSK_ERR_OK != err) goto error3; + + MALI_PRINT(("Mali device driver %s loaded\n", SVN_REV_STRING)); + + MALI_SUCCESS; + +error3: + MALI_PRINT(("Mali subsystems failed\n")); + _mali_osk_term(); +error2: + MALI_PRINT(("Mali device driver init failed\n")); + if (_MALI_OSK_ERR_OK != mali_platform_deinit(NULL)) + { + MALI_PRINT(("Failed to deinit platform\n")); + } +error1: + MALI_PRINT(("Failed to init platform\n")); + MALI_ERROR(err); +} + +/* is called from OS specific driver exit point */ +void mali_kernel_destructor( void ) +{ + MALI_DEBUG_PRINT(2, ("\n")); + MALI_DEBUG_PRINT(2, ("Unloading Mali v%d device driver.\n",_MALI_API_VERSION)); + terminate_subsystems(); /* subsystems are responsible for their registered resources */ + _mali_osk_term(); + + if (_MALI_OSK_ERR_OK != mali_platform_deinit(NULL)) + { + MALI_PRINT(("Failed to deinit platform\n")); + } + MALI_DEBUG_PRINT(2, ("Module unloaded.\n")); +} + +_mali_osk_errcode_t register_resources( _mali_osk_resource_t **arch_configuration, u32 num_resources ) +{ + _mali_osk_resource_t *arch_resource = *arch_configuration; + u32 i; +#if USING_MALI_PMM + u32 is_pmu_first_resource = 1; +#endif /* USING_MALI_PMM */ + + /* loop over arch configuration */ + for (i = 0; i < num_resources; ++i, arch_resource++) + { + if ( (arch_resource->type >= RESOURCE_TYPE_FIRST) && + (arch_resource->type < RESOURCE_TYPE_COUNT) && + (NULL != resource_handler[arch_resource->type]) + ) + { +#if USING_MALI_PMM + if((arch_resource->type != PMU) && (is_pmu_first_resource == 1)) + { + _mali_osk_resource_t mali_pmu_virtual_resource; + mali_pmu_virtual_resource.type = PMU; + mali_pmu_virtual_resource.description = "Virtual PMU"; + mali_pmu_virtual_resource.base = 0x00000000; + mali_pmu_virtual_resource.cpu_usage_adjust = 0; + mali_pmu_virtual_resource.size = 0; + mali_pmu_virtual_resource.irq = 0; + mali_pmu_virtual_resource.flags = 0; + mali_pmu_virtual_resource.mmu_id = 0; + mali_pmu_virtual_resource.alloc_order = 0; + MALI_CHECK_NO_ERROR(resource_handler[mali_pmu_virtual_resource.type](&mali_pmu_virtual_resource)); + } + is_pmu_first_resource = 0; +#endif /* USING_MALI_PMM */ + + MALI_CHECK_NO_ERROR(resource_handler[arch_resource->type](arch_resource)); + /* the subsystem shutdown process will release all the resources already registered */ + } + else + { + MALI_DEBUG_PRINT(1, ("No handler installed for resource %s, type %d\n", arch_resource->description, arch_resource->type)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t initialize_subsystems(void) +{ + int i, j; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; /* default error code */ + + MALI_CHECK_NON_NULL(system_info_lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0 ), _MALI_OSK_ERR_FAULT); + + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->startup) + { + /* the subsystem has a startup function defined */ + err = subsystems[i]->startup(i); /* the subsystem identifier is the offset in our subsystems array */ + if (_MALI_OSK_ERR_OK != err) goto cleanup; + } + } + + for (j = 0; j < (int)SUBSYSTEMS_COUNT; ++j) + { + if (NULL != subsystems[j]->load_complete) + { + /* the subsystem has a load_complete function defined */ + err = subsystems[j]->load_complete(j); + if (_MALI_OSK_ERR_OK != err) goto cleanup; + } + } + + /* All systems loaded and resources registered */ + /* Build system info */ + if (_MALI_OSK_ERR_OK != build_system_info()) goto cleanup; + + MALI_SUCCESS; /* all ok */ + +cleanup: + /* i is index of subsystem which failed to start, all indices before that has to be shut down */ + for (i = i - 1; i >= 0; --i) + { + /* the subsystem identifier is the offset in our subsystems array */ + /* Call possible shutdown notficiation functions */ + if (NULL != subsystems[i]->shutdown) subsystems[i]->shutdown(i); + } + + _mali_osk_lock_term( system_info_lock ); + MALI_ERROR(err); /* err is what the module which failed its startup returned, or the default */ +} + +static void terminate_subsystems(void) +{ + int i; + /* shut down subsystems in reverse order from startup */ + for (i = SUBSYSTEMS_COUNT - 1; i >= 0; --i) + { + /* the subsystem identifier is the offset in our subsystems array */ + if (NULL != subsystems[i]->shutdown) subsystems[i]->shutdown(i); + } + if (system_info_lock) _mali_osk_lock_term( system_info_lock ); +} + +void _mali_kernel_core_broadcast_subsystem_message(mali_core_notification_message message, u32 data) +{ + int i; + + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->broadcast_notification) + { + subsystems[i]->broadcast_notification(message, data); + } + } +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_setup(mali_kernel_subsystem_identifier id) +{ + mali_subsystem_core_id = id; + + /* Register our own resources */ + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MEM_VALIDATION, mali_kernel_core_resource_mem_validation)); + + /* parse the arch resource definition and tell all the subsystems */ + /* this is why the core subsystem has to be specified last in the subsystem array */ + MALI_CHECK_NO_ERROR(_mali_osk_resources_init(&arch_configuration, &num_resources)); + + MALI_CHECK_NO_ERROR(register_resources(&arch_configuration, num_resources)); + + /* resource parsing succeeded and the subsystem have corretly accepted their resources */ + MALI_SUCCESS; +} + +static void mali_kernel_subsystem_core_cleanup(mali_kernel_subsystem_identifier id) +{ + _mali_osk_resources_term(&arch_configuration, num_resources); +} + + +static _mali_osk_errcode_t build_system_info(void) +{ + unsigned int i; + int err = _MALI_OSK_ERR_FAULT; + _mali_system_info * new_info, * cleanup; + _mali_core_info * current_core; + _mali_mem_info * current_mem; + u32 new_size = 0; + + /* create a new system info struct */ + MALI_CHECK_NON_NULL(new_info = (_mali_system_info *)_mali_osk_malloc(sizeof(_mali_system_info)), _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(new_info, 0, sizeof(_mali_system_info)); + + /* if an error happens during any of the system_info_fill calls cleanup the new info structs */ + cleanup = new_info; + + /* ask each subsystems to fill in their info */ + for (i = 0; i < SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->system_info_fill) + { + err = subsystems[i]->system_info_fill(new_info); + if (_MALI_OSK_ERR_OK != err) goto error_exit; + } + } + + /* building succeeded, calculate the size */ + + /* size needed of the system info struct itself */ + new_size = sizeof(_mali_system_info); + + /* size needed for the cores */ + for (current_core = new_info->core_info; NULL != current_core; current_core = current_core->next) + { + new_size += sizeof(_mali_core_info); + } + + /* size needed for the memory banks */ + for (current_mem = new_info->mem_info; NULL != current_mem; current_mem = current_mem->next) + { + new_size += sizeof(_mali_mem_info); + } + + /* lock system info access so a user wont't get a corrupted version */ + _mali_osk_lock_wait( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* cleanup the old one */ + cleanup = system_info; + /* set new info */ + system_info = new_info; + system_info_size = new_size; + + /* we're safe */ + _mali_osk_lock_signal( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* ok result */ + err = _MALI_OSK_ERR_OK; + + /* we share the cleanup routine with the error case */ +error_exit: + if (NULL == cleanup) MALI_ERROR((_mali_osk_errcode_t)err); /* no cleanup needed, return what err contains */ + + /* cleanup */ + + /* delete all the core info structs */ + while (NULL != cleanup->core_info) + { + current_core = cleanup->core_info; + cleanup->core_info = cleanup->core_info->next; + _mali_osk_free(current_core); + } + + /* delete all the mem info struct */ + while (NULL != cleanup->mem_info) + { + current_mem = cleanup->mem_info; + cleanup->mem_info = cleanup->mem_info->next; + _mali_osk_free(current_mem); + } + + /* delete the system info struct itself */ + _mali_osk_free(cleanup); + + /* return whatever err is, we could end up here in both the error and success cases */ + MALI_ERROR((_mali_osk_errcode_t)err); +} + +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + /* check compatability */ + if ( args->version == _MALI_UK_API_VERSION ) + { + args->compatible = 1; + } + else + { + args->compatible = 0; + } + + args->version = _MALI_UK_API_VERSION; /* report our version */ + + /* success regardless of being compatible or not */ + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_get_system_info_size(_mali_uk_get_system_info_size_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + args->size = system_info_size; + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_get_system_info( _mali_uk_get_system_info_s *args ) +{ + _mali_core_info * current_core; + _mali_mem_info * current_mem; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + void * current_write_pos, ** current_patch_pos; + u32 adjust_ptr_base; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_CHECK_NON_NULL(args->system_info, _MALI_OSK_ERR_INVALID_ARGS); + + /* lock the system info */ + _mali_osk_lock_wait( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* first check size */ + if (args->size < system_info_size) goto exit_when_locked; + + /* we build a copy of system_info in the user space buffer specified by the user and + * patch up the pointers. The ukk_private members of _mali_uk_get_system_info_s may + * indicate a different base address for patching the pointers (normally the + * address of the provided system_info buffer would be used). This is helpful when + * the system_info buffer needs to get copied to user space and the pointers need + * to be in user space. + */ + if (0 == args->ukk_private) + { + adjust_ptr_base = (u32)args->system_info; + } + else + { + adjust_ptr_base = args->ukk_private; + } + + /* copy each struct into the buffer, and update its pointers */ + current_write_pos = (void *)args->system_info; + + /* first, the master struct */ + _mali_osk_memcpy(current_write_pos, system_info, sizeof(_mali_system_info)); + + /* advance write pointer */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_system_info)); + + /* first we write the core info structs, patch starts at master's core_info pointer */ + current_patch_pos = (void **)((u32)args->system_info + offsetof(_mali_system_info, core_info)); + + for (current_core = system_info->core_info; NULL != current_core; current_core = current_core->next) + { + + /* patch the pointer pointing to this core */ + *current_patch_pos = (void*)(adjust_ptr_base + ((u32)current_write_pos - (u32)args->system_info)); + + /* copy the core info */ + _mali_osk_memcpy(current_write_pos, current_core, sizeof(_mali_core_info)); + + /* update patch pos */ + current_patch_pos = (void **)((u32)current_write_pos + offsetof(_mali_core_info, next)); + + /* advance write pos in memory */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_core_info)); + } + /* patching of last patch pos is not needed, since we wrote NULL there in the first place */ + + /* then we write the mem info structs, patch starts at master's mem_info pointer */ + current_patch_pos = (void **)((u32)args->system_info + offsetof(_mali_system_info, mem_info)); + + for (current_mem = system_info->mem_info; NULL != current_mem; current_mem = current_mem->next) + { + /* patch the pointer pointing to this core */ + *current_patch_pos = (void*)(adjust_ptr_base + ((u32)current_write_pos - (u32)args->system_info)); + + /* copy the core info */ + _mali_osk_memcpy(current_write_pos, current_mem, sizeof(_mali_mem_info)); + + /* update patch pos */ + current_patch_pos = (void **)((u32)current_write_pos + offsetof(_mali_mem_info, next)); + + /* advance write pos in memory */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_mem_info)); + } + /* patching of last patch pos is not needed, since we wrote NULL there in the first place */ + + err = _MALI_OSK_ERR_OK; +exit_when_locked: + _mali_osk_lock_signal( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + MALI_ERROR(err); +} + +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ) +{ + _mali_osk_errcode_t err; + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = (_mali_osk_notification_queue_t *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_core_id); + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) + { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + args->type = _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS; + MALI_SUCCESS; + } + + /* receive a notification, might sleep */ + err = _mali_osk_notification_queue_receive(queue, ¬ification); + if (_MALI_OSK_ERR_OK != err) + { + MALI_ERROR(err); /* errcode returned, pass on to caller */ + } + + /* copy the buffer to the user */ + args->type = (_mali_uk_notification_type)notification->notification_type; + _mali_osk_memcpy(&args->data, notification->result_buffer, notification->result_buffer_size); + + /* finished with the notification */ + _mali_osk_notification_delete( notification ); + + MALI_SUCCESS; /* all ok */ +} + +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ) +{ + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = (_mali_osk_notification_queue_t *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_core_id); + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) + { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + MALI_SUCCESS; + } + + notification = _mali_osk_notification_create(args->type, 0); + if ( NULL == notification) + { + MALI_PRINT_ERROR( ("Failed to create notification object\n")) ; + return _MALI_OSK_ERR_NOMEM; + } + + _mali_osk_notification_queue_send(queue, notification); + + MALI_SUCCESS; /* all ok */ +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_system_info_fill(_mali_system_info* info) +{ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + info->drivermode = _MALI_DRIVER_MODE_NORMAL; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + MALI_CHECK_NON_NULL(slot, _MALI_OSK_ERR_INVALID_ARGS); + *slot = queue; + MALI_SUCCESS; +} + +/* MEM_VALIDATION resource handler */ +static _mali_osk_errcode_t mali_kernel_core_resource_mem_validation(_mali_osk_resource_t * resource) +{ + /* Check that no other MEM_VALIDATION resources exist */ + MALI_CHECK( ((u32)-1) == mem_validator.phys_base, _MALI_OSK_ERR_FAULT ); + + /* Check restrictions on page alignment */ + MALI_CHECK( 0 == (resource->base & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == (resource->size & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == (resource->cpu_usage_adjust & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + + mem_validator.phys_base = resource->base; + mem_validator.size = resource->size; + mem_validator.cpu_usage_adjust = resource->cpu_usage_adjust; + MALI_DEBUG_PRINT( 2, ("Memory Validator '%s' installed for Mali physical address base==0x%08X, size==0x%08X, cpu_adjust==0x%08X\n", + resource->description, mem_validator.phys_base, mem_validator.size, mem_validator.cpu_usage_adjust )); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_kernel_core_translate_cpu_to_mali_phys_range( u32 *phys_base, u32 size ) +{ + u32 mali_phys_base; + + mali_phys_base = *phys_base - mem_validator.cpu_usage_adjust; + + MALI_CHECK( 0 == ( mali_phys_base & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + + MALI_CHECK_NO_ERROR( mali_kernel_core_validate_mali_phys_range( mali_phys_base, size ) ); + + *phys_base = mali_phys_base; + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_kernel_core_validate_mali_phys_range( u32 phys_base, u32 size ) +{ + MALI_CHECK_GOTO( 0 == ( phys_base & (~_MALI_OSK_CPU_PAGE_MASK)), failure ); + MALI_CHECK_GOTO( 0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)), failure ); + + if ( phys_base >= mem_validator.phys_base + && (phys_base + size) >= mem_validator.phys_base + && phys_base <= (mem_validator.phys_base + mem_validator.size) + && (phys_base + size) <= (mem_validator.phys_base + mem_validator.size) ) + { + MALI_SUCCESS; + } + + failure: + MALI_PRINTF( ("*******************************************************************************\n") ); + MALI_PRINTF( ("MALI PHYSICAL RANGE VALIDATION ERROR!\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("We failed to validate a Mali-Physical range that the user-side wished to map in\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("It is likely that the user-side wished to do Direct Rendering, but a suitable\n") ); + MALI_PRINTF( ("address range validation mechanism has not been correctly setup\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("The range supplied was: phys_base=0x%08X, size=0x%08X\n", phys_base, size) ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("Please refer to the ARM Mali Software Integration Guide for more information.\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("*******************************************************************************\n") ); + + MALI_ERROR( _MALI_OSK_ERR_FAULT ); +} + + +_mali_osk_errcode_t _mali_kernel_core_register_resource_handler(_mali_osk_resource_type_t type, mali_kernel_resource_registrator handler) +{ + MALI_CHECK(type < RESOURCE_TYPE_COUNT, _MALI_OSK_ERR_INVALID_ARGS); + MALI_DEBUG_ASSERT(NULL == resource_handler[type]); /* A handler for resource already exists */ + resource_handler[type] = handler; + MALI_SUCCESS; +} + +void * mali_kernel_session_manager_slot_get(struct mali_session_data * session_data, int id) +{ + MALI_DEBUG_ASSERT_POINTER(session_data); + if(id >= SUBSYSTEMS_COUNT) { MALI_DEBUG_PRINT(3, ("mali_kernel_session_manager_slot_get: id %d out of range\n", id)); return NULL; } + + if (NULL == session_data) { MALI_DEBUG_PRINT(3, ("mali_kernel_session_manager_slot_get: got NULL session data\n")); return NULL; } + return session_data->subsystem_data[id]; +} + +_mali_osk_errcode_t _mali_ukk_open(void **context) +{ + int i; + _mali_osk_errcode_t err; + struct mali_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct mali_session_data *)_mali_osk_malloc(sizeof(struct mali_session_data)); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(session_data->subsystem_data, 0, sizeof(session_data->subsystem_data)); + + /* create a response queue for this session */ + session_data->ioctl_queue = _mali_osk_notification_queue_init(); + if (NULL == session_data->ioctl_queue) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + MALI_DEBUG_PRINT(3, ("Session starting\n")); + + /* call session_begin on all subsystems */ + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->session_begin) + { + /* subsystem has a session_begin */ + err = subsystems[i]->session_begin(session_data, &session_data->subsystem_data[i], session_data->ioctl_queue); + MALI_CHECK_GOTO(err == _MALI_OSK_ERR_OK, cleanup); + } + } + + *context = (void*)session_data; + + MALI_DEBUG_PRINT(3, ("Session started\n")); + MALI_SUCCESS; + +cleanup: + MALI_DEBUG_PRINT(2, ("Session startup failed\n")); + /* i is index of subsystem which failed session begin, all indices before that has to be ended */ + /* end subsystem sessions in the reverse order they where started in */ + for (i = i - 1; i >= 0; --i) + { + if (NULL != subsystems[i]->session_end) subsystems[i]->session_end(session_data, &session_data->subsystem_data[i]); + } + + _mali_osk_notification_queue_term(session_data->ioctl_queue); + _mali_osk_free(session_data); + + /* return what the subsystem which failed session start returned */ + MALI_ERROR(err); +} + +_mali_osk_errcode_t _mali_ukk_close(void **context) +{ + int i; + struct mali_session_data * session_data; + + MALI_CHECK_NON_NULL(context, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (struct mali_session_data *)*context; + + MALI_DEBUG_PRINT(2, ("Session ending\n")); + + /* end subsystem sessions in the reverse order they where started in */ + for (i = SUBSYSTEMS_COUNT - 1; i >= 0; --i) + { + if (NULL != subsystems[i]->session_end) subsystems[i]->session_end(session_data, &session_data->subsystem_data[i]); + } + + _mali_osk_notification_queue_term(session_data->ioctl_queue); + _mali_osk_free(session_data); + + *context = NULL; + + MALI_DEBUG_PRINT(2, ("Session has ended\n")); + + MALI_SUCCESS; +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t mali_core_signal_power_up( mali_pmm_core_id core, mali_bool queue_only ) +{ + switch( core ) + { + case MALI_PMM_CORE_GP: + MALI_CHECK_NO_ERROR(maligp_signal_power_up(queue_only)); + break; +#if defined USING_MALI400_L2_CACHE + case MALI_PMM_CORE_L2: + if( !queue_only ) + { + /* Enable L2 cache due to power up */ + mali_kernel_l2_cache_do_enable(); + + /* Invalidate the cache on power up */ + MALI_DEBUG_PRINT(5, ("L2 Cache: Invalidate all\n")); + MALI_CHECK_NO_ERROR(mali_kernel_l2_cache_invalidate_all()); + } + break; +#endif + case MALI_PMM_CORE_PP0: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(0, queue_only)); + break; + case MALI_PMM_CORE_PP1: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(1, queue_only)); + break; + case MALI_PMM_CORE_PP2: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(2, queue_only)); + break; + case MALI_PMM_CORE_PP3: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(3, queue_only)); + break; + default: + /* Unknown core */ + MALI_DEBUG_PRINT_ERROR( ("Unknown core signalled with power up: %d\n", core) ); + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); + } + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_signal_power_down( mali_pmm_core_id core, mali_bool immediate_only ) +{ + switch( core ) + { + case MALI_PMM_CORE_GP: + MALI_CHECK_NO_ERROR(maligp_signal_power_down(immediate_only)); + break; +#if defined USING_MALI400_L2_CACHE + case MALI_PMM_CORE_L2: + /* Nothing to do */ + break; +#endif + case MALI_PMM_CORE_PP0: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(0, immediate_only)); + break; + case MALI_PMM_CORE_PP1: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(1, immediate_only)); + break; + case MALI_PMM_CORE_PP2: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(2, immediate_only)); + break; + case MALI_PMM_CORE_PP3: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(3, immediate_only)); + break; + default: + /* Unknown core */ + MALI_DEBUG_PRINT_ERROR( ("Unknown core signalled with power down: %d\n", core) ); + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); + } + + MALI_SUCCESS; +} + +#endif + + + +#if MALI_STATE_TRACKING +void _mali_kernel_core_dump_state(void) +{ + int i; + for (i = 0; i < SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->dump_state) + { + subsystems[i]->dump_state(); + } + } +#if USING_MALI_PMM + mali_pmm_dump_os_thread_state(); +#endif +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.h new file mode 100644 index 00000000000..3819ecc5e1c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_core.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_CORE_H__ +#define __MALI_KERNEL_CORE_H__ + +#include "mali_osk.h" + +#if USING_MALI_PMM +#include "mali_ukk.h" +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#endif + +_mali_osk_errcode_t mali_kernel_constructor( void ); +void mali_kernel_destructor( void ); + +/** + * @brief Tranlate CPU physical to Mali physical addresses. + * + * This function is used to convert CPU physical addresses to Mali Physical + * addresses, such that _mali_ukk_map_external_mem may be used to map them + * into Mali. This will be used by _mali_ukk_va_to_mali_pa. + * + * This function only supports physically contiguous regions. + * + * A default implementation is provided, which uses a registered MEM_VALIDATION + * resource to do a static translation. Only an address range which will lie + * in the range specified by MEM_VALIDATION will be successfully translated. + * + * If a more complex, or non-static translation is required, then the + * implementor has the following options: + * - Rewrite this function to provide such a translation + * - Integrate the provider of the memory with UMP. + * + * @param[in,out] phys_base pointer to the page-aligned base address of the + * physical range to be translated + * + * @param[in] size size of the address range to be translated, which must be a + * multiple of the physical page size. + * + * @return on success, _MALI_OSK_ERR_OK and *phys_base is translated. If the + * cpu physical address range is not in the valid range, then a suitable + * _mali_osk_errcode_t error. + * + */ +_mali_osk_errcode_t mali_kernel_core_translate_cpu_to_mali_phys_range( u32 *phys_base, u32 size ); + + +/** + * @brief Validate a Mali physical address range. + * + * This function is used to ensure that an address range passed to + * _mali_ukk_map_external_mem is allowed to be mapped into Mali. + * + * This function only supports physically contiguous regions. + * + * A default implementation is provided, which uses a registered MEM_VALIDATION + * resource to do a static translation. Only an address range which will lie + * in the range specified by MEM_VALIDATION will be successfully validated. + * + * If a more complex, or non-static validation is required, then the + * implementor has the following options: + * - Rewrite this function to provide such a validation + * - Integrate the provider of the memory with UMP. + * + * @param phys_base page-aligned base address of the Mali physical range to be + * validated. + * + * @param size size of the address range to be validated, which must be a + * multiple of the physical page size. + * + * @return _MALI_OSK_ERR_OK if the Mali physical range is valid. Otherwise, a + * suitable _mali_osk_errcode_t error. + * + */ +_mali_osk_errcode_t mali_kernel_core_validate_mali_phys_range( u32 phys_base, u32 size ); + +#if USING_MALI_PMM +/** + * @brief Signal a power up on a Mali core. + * + * This function flags a core as powered up. + * For PP and GP cores it calls functions that move the core from a power off + * queue into the idle queue ready to run jobs. It also tries to schedule any + * pending jobs to run on it. + * + * This function will fail if the core is not powered off - either running or + * already idle. + * + * @param core The PMM core id to power up. + * @param queue_only When MALI_TRUE only re-queue the core - do not reset. + * + * @return _MALI_OSK_ERR_OK if the core has been powered up. Otherwise a + * suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_core_signal_power_up( mali_pmm_core_id core, mali_bool queue_only ); + +/** + * @brief Signal a power down on a Mali core. + * + * This function flags a core as powered down. + * For PP and GP cores it calls functions that move the core from an idle + * queue into the power off queue. + * + * This function will fail if the core is not idle - either running or + * already powered down. + * + * @param core The PMM core id to power up. + * @param immediate_only Do not set the core to pending power down if it can't + * power down immediately + * + * @return _MALI_OSK_ERR_OK if the core has been powered up. Otherwise a + * suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_core_signal_power_down( mali_pmm_core_id core, mali_bool immediate_only ); + +#endif + +/** + * Flag to indicate whether or not mali_benchmark is turned on. + */ +extern int mali_benchmark; + + +#endif /* __MALI_KERNEL_CORE_H__ */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.c new file mode 100644 index 00000000000..45c280edd6a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static mali_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(mali_descriptor_table * table); + +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries) +{ + mali_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(mali_descriptor_mapping)); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { +#if !USING_MMU + map->lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 20); +#else + map->lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 116); +#endif + if (NULL != map->lock) + { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_lock_term(map->lock); + _mali_osk_free(map); +} + +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *odescriptor) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + int new_descriptor; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(odescriptor); + + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + new_descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (new_descriptor == map->current_nr_mappings) + { + /* no free descriptor, try to expand the table */ + mali_descriptor_table * new_table, * old_table; + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) goto unlock_and_exit; + + map->current_nr_mappings += BITS_PER_LONG; + new_table = descriptor_table_alloc(map->current_nr_mappings); + if (NULL == new_table) goto unlock_and_exit; + + old_table = map->table; + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(new_descriptor, map->table->usage); + map->table->mappings[new_descriptor] = target; + *odescriptor = new_descriptor; + err = _MALI_OSK_ERR_OK; + +unlock_and_exit: + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(err); +} + +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)) +{ + int i; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(callback); + + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + /* id 0 is skipped as it's an reserved ID not mapping to anything */ + for (i = 1; i < map->current_nr_mappings; ++i) + { + if (_mali_osk_test_bit(i, map->table->usage)) + { + callback(i, map->table->mappings[i]); + } + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); +} + +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + MALI_DEBUG_ASSERT_POINTER(map); + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = _MALI_OSK_ERR_OK; + } + else *target = NULL; + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = target; + result = _MALI_OSK_ERR_OK; + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +void mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor) +{ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static mali_descriptor_table * descriptor_table_alloc(int count) +{ + mali_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count)); + + if (NULL != table) + { + table->usage = (u32*)((u8*)table + sizeof(mali_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(mali_descriptor_table * table) +{ + _mali_osk_free(table); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.h new file mode 100644 index 00000000000..f626aa59455 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_descriptor_mapping.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_descriptor_mapping.h + */ + +#ifndef __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct mali_descriptor_table +{ + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} mali_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct mali_descriptor_mapping +{ + _mali_osk_lock_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + mali_descriptor_table * table; /**< Pointer to the current mapping table */ +} mali_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *descriptor); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target); + +/** + * Call the specified callback function for each descriptor in map. + * Entire function is mutex protected. + * @param map The map to do callbacks for + * @param callback A callback function which will be calle for each entry in map + */ +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor); + +#endif /* __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_gp.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_gp.h new file mode 100644 index 00000000000..c2467edc677 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_gp.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_GP2_H__ +#define __MALI_KERNEL_GP2_H__ + +extern struct mali_kernel_subsystem mali_subsystem_gp2; + +#if USING_MALI_PMM +_mali_osk_errcode_t maligp_signal_power_up( mali_bool queue_only ); +_mali_osk_errcode_t maligp_signal_power_down( mali_bool immediate_only ); +#endif + +#endif /* __MALI_KERNEL_GP2_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.c new file mode 100644 index 00000000000..fded5c93ab9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.c @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_list.h" + +#include "mali_kernel_core.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_subsystem.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_rendercore.h" +#include "mali_kernel_l2_cache.h" + +/** + * Size of the Mali L2 cache registers in bytes + */ +#define MALI400_L2_CACHE_REGISTERS_SIZE 0x30 + +/** + * Mali L2 cache register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_l2_cache_register { + MALI400_L2_CACHE_REGISTER_STATUS = 0x0002, + /*unused = 0x0003 */ + MALI400_L2_CACHE_REGISTER_COMMAND = 0x0004, /**< Misc cache commands, e.g. clear */ + MALI400_L2_CACHE_REGISTER_CLEAR_PAGE = 0x0005, + MALI400_L2_CACHE_REGISTER_MAX_READS = 0x0006, /**< Limit of outstanding read requests */ + MALI400_L2_CACHE_REGISTER_ENABLE = 0x0007, /**< Enable misc cache features */ + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0 = 0x0008, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0 = 0x0009, + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1 = 0x000A, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1 = 0x000B, +} mali_l2_cache_register; + + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_command +{ + MALI400_L2_CACHE_COMMAND_CLEAR_ALL = 0x01, /**< Clear the entire cache */ + /* Read HW TRM carefully before adding/using other commands than the clear above */ +} mali_l2_cache_command; + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_enable +{ + MALI400_L2_CACHE_ENABLE_DEFAULT = 0x0, /**< Default state of enable register */ + MALI400_L2_CACHE_ENABLE_ACCESS = 0x01, /**< Permit cacheable accesses */ + MALI400_L2_CACHE_ENABLE_READ_ALLOCATE = 0x02, /**< Permit cache read allocate */ +} mali_l2_cache_enable; + +/** + * Mali L2 cache status bits + */ +typedef enum mali_l2_cache_status +{ + MALI400_L2_CACHE_STATUS_COMMAND_BUSY = 0x01, /**< Command handler of L2 cache is busy */ + MALI400_L2_CACHE_STATUS_DATA_BUSY = 0x02, /**< L2 cache is busy handling data requests */ +} mali_l2_cache_status; + + +/** + * Definition of the L2 cache core struct + * Used to track a L2 cache unit in the system. + * Contains information about the mapping of the registers + */ +typedef struct mali_kernel_l2_cache_core +{ + unsigned long base; /**< Physical address of the registers */ + mali_io_address mapped_registers; /**< Virtual mapping of the registers */ + u32 mapping_size; /**< Size of registers in bytes */ + _mali_osk_list_t list; /**< Used to link multiple cache cores into a list */ + _mali_osk_lock_t *lock; /**< Serialize all L2 cache commands */ +} mali_kernel_l2_cache_core; + + +#define MALI400_L2_MAX_READS_DEFAULT 0x1C + +int mali_l2_max_reads = MALI400_L2_MAX_READS_DEFAULT; + + +/** + * Mali L2 cache subsystem startup function + * Called by the driver core when the driver is loaded. + * + * @param id Identifier assigned by the core to the L2 cache subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_initialize(mali_kernel_subsystem_identifier id); + +/** + * Mali L2 cache subsystem shutdown function + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the L2 cache subsystem + */ +static void mali_l2_cache_terminate(mali_kernel_subsystem_identifier id); + +/** + * L2 cache subsystem complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * @param id Identifier assigned by the core to the L2 cache subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_load_complete(mali_kernel_subsystem_identifier id); + +/** + * Mali L2 cache subsystem's notification handler for a Mali L2 cache resource instances. + * Registered with the core during startup. + * Called by the core for each Mali L2 cache described in the active architecture's config.h file. + * @param resource The resource to handle (type MALI400L2) + * @return 0 if the Mali L2 cache was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_core_create(_mali_osk_resource_t * resource); + +/** + * Write to a L2 cache register + * Writes the given value to the specified register + * @param unit The L2 cache to write to + * @param reg The register to write to + * @param val The value to write to the register + */ +static void mali_l2_cache_register_write(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg, u32 val); + + + +/** + * Invalidate specified L2 cache + * @param cache The L2 cache to invalidate + * @return 0 if Mali L2 cache was successfully invalidated, otherwise error + */ +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all_cache(mali_kernel_l2_cache_core *cache); + + +/* + The fixed Mali L2 cache system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_l2_cache = +{ + mali_l2_cache_initialize, /**< startup */ + mali_l2_cache_terminate, /**< shutdown */ + mali_l2_cache_load_complete, /**< load_complete */ + NULL, /**< system_info_fill */ + NULL, /**< session_begin */ + NULL, /**< session_end */ + NULL, /**< broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /**< dump_state */ +#endif +}; + + + +static _MALI_OSK_LIST_HEAD(caches_head); + + + + +/* called during module init */ +static _mali_osk_errcode_t mali_l2_cache_initialize(mali_kernel_subsystem_identifier id) +{ + _mali_osk_errcode_t err; + + MALI_IGNORE( id ); + + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system initializing\n")); + + _MALI_OSK_INIT_LIST_HEAD(&caches_head); + + /* This will register the function for adding Mali L2 cache cores to the subsystem */ + err = _mali_kernel_core_register_resource_handler(MALI400L2, mali_l2_cache_core_create); + + MALI_ERROR(err); +} + + + +/* called if/when our module is unloaded */ +static void mali_l2_cache_terminate(mali_kernel_subsystem_identifier id) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system terminating\n")); + + /* loop over all L2 cache units and shut them down */ + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list ) + { + /* reset to defaults */ + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)MALI400_L2_MAX_READS_DEFAULT); + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_DEFAULT); + + /* remove from the list of cacges on the system */ + _mali_osk_list_del( &cache->list ); + + /* release resources */ + _mali_osk_mem_unmapioregion( cache->base, cache->mapping_size, cache->mapped_registers ); + _mali_osk_mem_unreqregion( cache->base, cache->mapping_size ); + _mali_osk_lock_term( cache->lock ); + _mali_osk_free( cache ); + + #if USING_MALI_PMM + /* Unregister the L2 cache with the PMM */ + malipmm_core_unregister( MALI_PMM_CORE_L2 ); + #endif + } +} + +static _mali_osk_errcode_t mali_l2_cache_core_create(_mali_osk_resource_t * resource) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT ; + mali_kernel_l2_cache_core * cache = NULL; + + MALI_DEBUG_PRINT(2, ( "Creating Mali L2 cache: %s\n", resource->description)); + +#if USING_MALI_PMM + /* Register the L2 cache with the PMM */ + err = malipmm_core_register( MALI_PMM_CORE_L2 ); + if( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT(1, ( "Failed to register L2 cache unit with PMM")); + return err; + } +#endif + + err = _mali_osk_mem_reqregion( resource->base, MALI400_L2_CACHE_REGISTERS_SIZE, resource->description); + + MALI_CHECK_GOTO( _MALI_OSK_ERR_OK == err, err_cleanup_requestmem_failed); + + /* Reset error that might be passed out */ + err = _MALI_OSK_ERR_FAULT; + + cache = _mali_osk_malloc(sizeof(mali_kernel_l2_cache_core)); + + MALI_CHECK_GOTO( NULL != cache, err_cleanup); + + cache->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 104 ); + + MALI_CHECK_GOTO( NULL != cache->lock, err_cleanup); + + /* basic setup */ + _MALI_OSK_INIT_LIST_HEAD(&cache->list); + + cache->base = resource->base; + cache->mapping_size = MALI400_L2_CACHE_REGISTERS_SIZE; + + /* map the registers */ + cache->mapped_registers = _mali_osk_mem_mapioregion( cache->base, cache->mapping_size, resource->description ); + + MALI_CHECK_GOTO( NULL != cache->mapped_registers, err_cleanup); + + /* Invalidate cache (just to keep it in a known state at startup) */ + err = mali_kernel_l2_cache_invalidate_all_cache(cache); + + MALI_CHECK_GOTO( _MALI_OSK_ERR_OK == err, err_cleanup); + + /* add to our list of L2 caches */ + _mali_osk_list_add( &cache->list, &caches_head ); + + MALI_SUCCESS; + +err_cleanup: + /* This cleanup used when resources have been requested successfully */ + + if ( NULL != cache ) + { + if (NULL != cache->mapped_registers) + { + _mali_osk_mem_unmapioregion( cache->base, cache->mapping_size, cache->mapped_registers); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to map Mali L2 cache registers at 0x%08lX\n", cache->base)); + } + + if( NULL != cache->lock ) + { + _mali_osk_lock_term( cache->lock ); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to allocate a lock for handling a L2 cache unit")); + } + + _mali_osk_free( cache ); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to allocate memory for handling a L2 cache unit")); + } + + /* A call is to request region, so this must always be reversed */ + _mali_osk_mem_unreqregion( resource->base, MALI400_L2_CACHE_REGISTERS_SIZE); +#if USING_MALI_PMM + malipmm_core_unregister( MALI_PMM_CORE_L2 ); +#endif + return err; + +err_cleanup_requestmem_failed: + MALI_DEBUG_PRINT(1, ("Failed to request Mali L2 cache '%s' register address space at (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI400_L2_CACHE_REGISTERS_SIZE - 1) ); +#if USING_MALI_PMM + malipmm_core_unregister( MALI_PMM_CORE_L2 ); +#endif + return err; + +} + + +static void mali_l2_cache_register_write(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg, u32 val) +{ + _mali_osk_mem_iowrite32(unit->mapped_registers, (u32)reg * sizeof(u32), val); +} + + +static u32 mali_l2_cache_register_read(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg) +{ + return _mali_osk_mem_ioread32(unit->mapped_registers, (u32)reg * sizeof(u32)); +} + +void mali_kernel_l2_cache_do_enable(void) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and enable them*/ + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_ACCESS | (u32)MALI400_L2_CACHE_ENABLE_READ_ALLOCATE); + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)mali_l2_max_reads); + } +} + + +static _mali_osk_errcode_t mali_l2_cache_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_kernel_l2_cache_do_enable(); + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system load complete\n")); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_l2_cache_send_command(mali_kernel_l2_cache_core *cache, u32 reg, u32 val) +{ + int i = 0; + const int loop_count = 100000; + + /* + * Grab lock in order to send commands to the L2 cache in a serialized fashion. + * The L2 cache will ignore commands if it is busy. + */ + _mali_osk_lock_wait(cache->lock, _MALI_OSK_LOCKMODE_RW); + + /* First, wait for L2 cache command handler to go idle */ + + for (i = 0; i < loop_count; i++) + { + if (!(_mali_osk_mem_ioread32(cache->mapped_registers , (u32)MALI400_L2_CACHE_REGISTER_STATUS * sizeof(u32)) & (u32)MALI400_L2_CACHE_STATUS_COMMAND_BUSY)) + { + break; + } + } + + if (i == loop_count) + { + _mali_osk_lock_signal(cache->lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for command interface to go idle\n")); + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + + /* then issue the command */ + mali_l2_cache_register_write(cache, reg, val); + + _mali_osk_lock_signal(cache->lock, _MALI_OSK_LOCKMODE_RW); + MALI_SUCCESS; +} + + +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all_cache(mali_kernel_l2_cache_core *cache) +{ + return mali_kernel_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); +} + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all(void) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and invalidate them */ + + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + MALI_CHECK_NO_ERROR( mali_kernel_l2_cache_invalidate_all_cache(cache) ); + } + + MALI_SUCCESS; +} + + +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page_cache(mali_kernel_l2_cache_core *cache, u32 page) +{ + return mali_kernel_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_CLEAR_PAGE, page); +} + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page(u32 page) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and invalidate them */ + + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + MALI_CHECK_NO_ERROR( mali_kernel_l2_cache_invalidate_page_cache(cache, page) ); + } + + MALI_SUCCESS; +} + + +void mali_kernel_l2_cache_set_perf_counters(u32 src0, u32 src1, int force_reset) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + int reset0 = force_reset; + int reset1 = force_reset; + MALI_DEBUG_CODE( + int changed0 = 0; + int changed1 = 0; + ) + + /* loop over all L2 cache units and activate the counters on them */ + _MALI_OSK_LIST_FOREACHENTRY(cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + u32 cur_src0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0); + u32 cur_src1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1); + + if (src0 != cur_src0) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, src0); + MALI_DEBUG_CODE(changed0 = 1;) + reset0 = 1; + } + + if (src1 != cur_src1) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, src1); + MALI_DEBUG_CODE(changed1 = 1;) + reset1 = 1; + } + + if (reset0) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0, 0); + } + + if (reset1) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1, 0); + } + + MALI_DEBUG_PRINT(5, ("L2 cache counters set: SRC0=%u, CHANGED0=%d, RESET0=%d, SRC1=%u, CHANGED1=%d, RESET1=%d\n", + src0, changed0, reset0, + src1, changed1, reset1)); + } +} + + +void mali_kernel_l2_cache_get_perf_counters(u32 *src0, u32 *val0, u32 *src1, u32 *val1) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + int first_time = 1; + *src0 = 0; + *src1 = 0; + *val0 = 0; + *val1 = 0; + + /* loop over all L2 cache units and read the counters */ + _MALI_OSK_LIST_FOREACHENTRY(cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + u32 cur_src0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0); + u32 cur_src1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1); + u32 cur_val0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0); + u32 cur_val1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1); + + MALI_DEBUG_PRINT(5, ("L2 cache counters get: SRC0=%u, VAL0=%u, SRC1=%u, VAL1=%u\n", cur_src0, cur_val0, cur_src1, cur_val1)); + + if (first_time) + { + *src0 = cur_src0; + *src1 = cur_src1; + first_time = 0; + } + + if (*src0 == cur_src0 && *src1 == cur_src1) + { + *val0 += cur_val0; + *val1 += cur_val1; + } + else + { + MALI_DEBUG_PRINT(1, ("Warning: Mali L2 caches has different performance counters set, not retrieving data\n")); + } + } +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.h new file mode 100644 index 00000000000..de3229a0688 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_l2_cache.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_L2_CACHE_H__ +#define __MALI_KERNEL_L2_CACHE_H__ + +#include "mali_osk.h" +#include "mali_kernel_subsystem.h" +extern struct mali_kernel_subsystem mali_subsystem_l2_cache; + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all(void); +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page(u32 page); + +void mali_kernel_l2_cache_do_enable(void); +void mali_kernel_l2_cache_set_perf_counters(u32 src0, u32 src1, int force_reset); +void mali_kernel_l2_cache_get_perf_counters(u32 *src0, u32 *val0, u32 *src1, u32 *val1); + +#endif /* __MALI_KERNEL_L2_CACHE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem.h new file mode 100644 index 00000000000..681b6dd47c9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_H__ +#define __MALI_KERNEL_MEM_H__ + +#include "mali_kernel_subsystem.h" +extern struct mali_kernel_subsystem mali_subsystem_memory; + +#endif /* __MALI_KERNEL_MEM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_buddy.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_buddy.c new file mode 100644 index 00000000000..8277fd1e1c6 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_buddy.c @@ -0,0 +1,1425 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_core.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_kernel_session_manager.h" + +/* kernel side OS functions and user-kernel interface */ +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_osk_list.h" +#include "mali_ukk.h" + +#ifdef _MALI_OSK_SPECIFIC_INDIRECT_MMAP +#include "mali_osk_indir_mmap.h" +#endif + +/** + * Minimum memory allocation size + */ +#define MIN_BLOCK_SIZE (1024*1024UL) + +/** + * Per-session memory descriptor mapping table sizes + */ +#define MALI_MEM_DESCRIPTORS_INIT 64 +#define MALI_MEM_DESCRIPTORS_MAX 4096 + +/** + * Enum uses to store multiple fields in one u32 to keep the memory block struct small + */ +enum MISC_SHIFT { MISC_SHIFT_FREE = 0, MISC_SHIFT_ORDER = 1, MISC_SHIFT_TOPLEVEL = 6 }; +enum MISC_MASK { MISC_MASK_FREE = 0x01, MISC_MASK_ORDER = 0x1F, MISC_MASK_TOPLEVEL = 0x1F }; + +/* forward declaration of the block struct */ +struct mali_memory_block; + +/** + * Definition of memory bank type. + * Represents a memory bank (separate address space) + * Each bank keeps track of its block usage. + * A buddy system used to track the usage +*/ +typedef struct mali_memory_bank +{ + _mali_osk_list_t list; /* links multiple banks together */ + _mali_osk_lock_t *lock; + u32 base_addr; /* Mali seen address of bank */ + u32 cpu_usage_adjust; /* Adjustmen factor for what the CPU sees */ + u32 size; /* the effective size */ + u32 real_size; /* the real size of the bank, as given by to the subsystem */ + int min_order; + int max_order; + struct mali_memory_block * blocklist; + _mali_osk_list_t *freelist; + _mali_osk_atomic_t num_active_allocations; + u32 used_for_flags; + u32 alloc_order; /**< Order in which the bank will be used for allocations */ + const char *name; /**< Descriptive name of the bank */ +} mali_memory_bank; + +/** + * Definition of the memory block type + * Represents a memory block, which is the smallest memory unit operated on. + * A block keeps info about its mapping, if in use by a user process + */ +typedef struct mali_memory_block +{ + _mali_osk_list_t link; /* used for freelist and process usage list*/ + mali_memory_bank * bank; /* the bank it belongs to */ + void __user * mapping; /* possible user space mapping of this block */ + u32 misc; /* used while a block is free to track the number blocks it represents */ + int descriptor; + u32 mmap_cookie; /**< necessary for interaction with _mali_ukk_mem_mmap/munmap */ +} mali_memory_block; + +/** + * Defintion of the type used to represent memory used by a session. + * Containts the head of the list of memory currently in use by a session. + */ +typedef struct memory_session +{ + _mali_osk_lock_t *lock; + _mali_osk_list_t memory_head; /* List of the memory blocks used by this session. */ + mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */ +} memory_session; + +/* + Subsystem interface implementation +*/ +/** + * Buddy block memory subsystem startup function + * Called by the driver core when the driver is loaded. + * Registers the memory systems ioctl handler, resource handlers and memory map function with the core. + * + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id); + +/** + * Buddy block memory subsystem shutdown function + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the memory subsystem + */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id); + +/** + * Buddy block memory load complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * Reports on the memory resources registered + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id); + + +/** + * Buddy block memory subsystem session begin notification + * Called by the core when a new session to the driver is started. + * Creates a memory session object and sets it as the subsystem slot data for this session + * @param slot Pointer to the slot to use for storing per-session data + * @param queue The user space event sink + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +/** + * Buddy block memory subsystem session end notification + * Called by the core when a session to the driver has ended. + * Cleans up per session data, which includes checking and fixing memory leaks + * + * @param slot Pointer to the slot to use for storing per-session data + */ +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + +/** + * Buddy block memory subsystem system info filler + * Called by the core when a system info update is needed + * We fill in info about all the memory types we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info); + +/* our registered resource handlers */ +/** + * Buddy block memory subsystem's notification handler for MEMORY resource instances. + * Registered with the core during startup. + * Called by the core for each memory bank described in the active architecture's config.h file. + * Requests memory region ownership and calls backend. + * @param resource The resource to handle (type MEMORY) + * @return 0 if the memory was claimed and accepted, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_memory(_mali_osk_resource_t * resource); + +/** + * Buddy block memory subsystem's notification handler for MMU resource instances. + * Registered with the core during startup. + * Called by the core for each mmu described in the active architecture's config.h file. + * @param resource The resource to handle (type MMU) + * @return 0 if the MMU was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource); + +/** + * Buddy block memory subsystem's notification handler for FPGA_FRAMEWORK resource instances. + * Registered with the core during startup. + * Called by the core for each fpga framework described in the active architecture's config.h file. + * @param resource The resource to handle (type FPGA_FRAMEWORK) + * @return 0 if the FPGA framework was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource); + +/* ioctl command implementations */ +/** + * Buddy block memory subsystem's handler for MALI_IOC_MEM_GET_BIG_BLOCK ioctl + * Called by the generic ioctl handler when the MALI_IOC_MEM_GET_BIG_BLOCK command is received. + * Finds an available memory block and maps into the current process' address space. + * @param ukk_private private word for use by the User/Kernel interface + * @param session_data Pointer to the per-session object which will track the memory usage + * @param argument The argument from the user. A pointer to an struct mali_dd_get_big_block in user space + * @return Zero if successful, a standard Linux error value value on error (a negative value) + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ); + +/** + * Buddy block memory subsystem's handler for MALI_IOC_MEM_FREE_BIG_BLOCK ioctl + * Called by the generic ioctl handler when the MALI_IOC_MEM_FREE_BIG_BLOCK command is received. + * Unmaps the memory from the process' address space and marks the block as free. + * @param session_data Pointer to the per-session object which tracks the memory usage + * @param argument The argument from the user. A pointer to an struct mali_dd_get_big_block in user space + * @return Zero if successful, a standard Linux error value value on error (a negative value) + */ + +/* this static version allows us to make use of it while holding the memory_session lock. + * This is required for the session_end code */ +static _mali_osk_errcode_t _mali_ukk_free_big_block_internal( struct mali_session_data * mali_session_data, memory_session * session_data, _mali_uk_free_big_block_s *args); + +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ); + +/** + * Buddy block memory subsystem's memory bank registration routine + * Called when a MEMORY resource has been found. + * The memory region has already been reserved for use by this driver. + * Create a bank object to represent this region and initialize its slots. + * @note Can only be called in an module atomic scope, i.e. during module init since no locking is performed + * @param phys_base Physical base address of this bank + * @param cpu_usage_adjust Adjustment factor for CPU seen address + * @param size Size of the bank in bytes + * @param flags Memory type bits + * @param alloc_order Order in which the bank will be used for allocations + * @param name descriptive name of the bank + * @return Zero on success, negative on error + */ +static int mali_memory_bank_register(u32 phys_base, u32 cpu_usage_adjust, u32 size, u32 flags, u32 alloc_order, const char *name); + +/** + * Get a block of mali memory of at least the given size and of the given type + * This is the backend for get_big_block. + * @param type_id The type id of memory requested. + * @param minimum_size The size requested + * @return Pointer to a block on success, NULL on failure + */ +static mali_memory_block * mali_memory_block_get(u32 type_id, u32 minimum_size); + +/** + * Get the mali seen address of the memory described by the block + * @param block The memory block to return the address of + * @return The mali seen address of the memory block + */ +MALI_STATIC_INLINE u32 block_mali_addr_get(mali_memory_block * block); + +/** + * Get the cpu seen address of the memory described by the block + * The cpu_usage_adjust will be used to change the mali seen phys address + * @param block The memory block to return the address of + * @return The mali seen address of the memory block + */ +MALI_STATIC_INLINE u32 block_cpu_addr_get(mali_memory_block * block); + +/** + * Get the size of the memory described by the given block + * @param block The memory block to return the size of + * @return The size of the memory block described by the object + */ +MALI_STATIC_INLINE u32 block_size_get(mali_memory_block * block); + +/** + * Get the user space accessible mapping the memory described by the given memory block + * Returns a pointer in user space to the memory, if one has been created. + * @param block The memory block to return the mapping of + * @return User space pointer to cpu accessible memory or NULL if not mapped + */ +MALI_STATIC_INLINE void __user * block_mapping_get(mali_memory_block * block); + +/** + * Set the user space accessible mapping the memory described by the given memory block. + * Sets the stored pointer to user space for the memory described by this block. + * @param block The memory block to set mapping info for + * @param ptr User space pointer to cpu accessible memory or NULL if not mapped + */ +MALI_STATIC_INLINE void block_mapping_set(mali_memory_block * block, void __user * ptr); + +/** + * Get the cookie for use with _mali_ukk_mem_munmap(). + * @param block The memory block to get the cookie from + * @return the cookie. A return of 0 is still a valid cookie. + */ +MALI_STATIC_INLINE u32 block_mmap_cookie_get(mali_memory_block * block); + +/** + * Set the cookie returned via _mali_ukk_mem_mmap(). + * @param block The memory block to set the cookie for + * @param cookie the cookie + */ +MALI_STATIC_INLINE void block_mmap_cookie_set(mali_memory_block * block, u32 cookie); + + +/** + * Get a memory block's free status + * @param block The block to get the state of + */ +MALI_STATIC_INLINE u32 get_block_free(mali_memory_block * block); + +/** + * Set a memory block's free status + * @param block The block to set the state for + * @param state The state to set + */ +MALI_STATIC_INLINE void set_block_free(mali_memory_block * block, int state); + +/** + * Set a memory block's order + * @param block The block to set the order for + * @param order The order to set + */ +MALI_STATIC_INLINE void set_block_order(mali_memory_block * block, u32 order); + +/** + * Get a memory block's order + * @param block The block to get the order for + * @return The order this block exists on + */ +MALI_STATIC_INLINE u32 get_block_order(mali_memory_block * block); + +/** + * Tag a block as being a toplevel block. + * A toplevel block has no buddy and no parent + * @param block The block to tag as being toplevel + */ +MALI_STATIC_INLINE void set_block_toplevel(mali_memory_block * block, u32 level); + +/** + * Check if a block is a toplevel block + * @param block The block to check + * @return 1 if toplevel, 0 else + */ +MALI_STATIC_INLINE u32 get_block_toplevel(mali_memory_block * block); + +/** + * Checks if the given block is a buddy at the given order and that it's free + * @param block The block to check + * @param order The order to check against + * @return 0 if not valid, else 1 + */ +MALI_STATIC_INLINE int block_is_valid_buddy(mali_memory_block * block, int order); + +/* + The buddy system uses the following rules to quickly find a blocks buddy + and parent (block representing this block at a higher order level): + - Given a block with index i the blocks buddy is at index i ^ ( 1 << order) + - Given a block with index i the blocks parent is at i & ~(1 << order) +*/ + +/** + * Get a blocks buddy + * @param block The block to find the buddy for + * @param order The order to operate on + * @return Pointer to the buddy block + */ +MALI_STATIC_INLINE mali_memory_block * block_get_buddy(mali_memory_block * block, u32 order); + +/** + * Get a blocks parent + * @param block The block to find the parent for + * @param order The order to operate on + * @return Pointer to the parent block + */ +MALI_STATIC_INLINE mali_memory_block * block_get_parent(mali_memory_block * block, u32 order); + +/** + * Release mali memory + * Backend for free_big_block. + * Will release the mali memory described by the given block struct. + * @param block Memory block to free + */ +static void block_release(mali_memory_block * block); + +/* end interface implementation */ + +/** + * List of all the memory banks registerd with the subsystem. + * Access to this list is NOT synchronized since it's only + * written to during module init and termination. + */ +static _MALI_OSK_LIST_HEAD(memory_banks_list); + +/* + The buddy memory system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_memory = +{ + mali_memory_core_initialize, /* startup */ + mali_memory_core_terminate, /* shutdown */ + mali_memory_core_load_complete, /* load_complete */ + mali_memory_core_system_info_fill, /* system_info_fill */ + mali_memory_core_session_begin, /* session_begin */ + mali_memory_core_session_end, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_memory_id = -1; + +/* called during module init */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id) +{ + _MALI_OSK_INIT_LIST_HEAD(&memory_banks_list); + + mali_subsystem_memory_id = id; + + /* register our handlers */ + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MEMORY, mali_memory_core_resource_memory)); + + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MMU, mali_memory_core_resource_mmu)); + + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(FPGA_FRAMEWORK, mali_memory_core_resource_fpga)); + + MALI_SUCCESS; +} + +/* called if/when our module is unloaded */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id) +{ + mali_memory_bank * bank, *temp; + + /* loop over all memory banks to free them */ + /* we use the safe version since we delete the current bank in the body */ + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + MALI_DEBUG_CODE(int usage_count = _mali_osk_atomic_read(&bank->num_active_allocations)); + /* + Report leaked memory + If this happens we have a bug in our session cleanup code. + */ + MALI_DEBUG_PRINT_IF(1, 0 != usage_count, ("%d allocation(s) from memory bank at 0x%X still in use\n", usage_count, bank->base_addr)); + + _mali_osk_atomic_term(&bank->num_active_allocations); + + _mali_osk_lock_term(bank->lock); + + /* unlink from bank list */ + _mali_osk_list_del(&bank->list); + + /* release kernel resources used by the bank */ + _mali_osk_mem_unreqregion(bank->base_addr, bank->real_size); + + /* remove all resources used to represent this bank*/ + _mali_osk_free(bank->freelist); + _mali_osk_free(bank->blocklist); + + /* destroy the bank object itself */ + _mali_osk_free(bank); + } + + /* No need to de-initialize mali_subsystem_memory_id - it could only be + * re-initialized to the same value */ +} + +/* load_complete handler */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_memory_bank * bank, *temp; + + MALI_DEBUG_PRINT( 1, ("Mali memory allocators will be used in this order of preference (lowest number first) :\n")); + + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + if ( NULL != bank->name ) + { + MALI_DEBUG_PRINT( 1, ("\t%d: %s\n", bank->alloc_order, bank->name) ); + } + else + { + MALI_DEBUG_PRINT( 1, ("\t%d: (UNNAMED ALLOCATOR)\n", bank->alloc_order ) ); + } + } + MALI_SUCCESS; +} + +MALI_STATIC_INLINE u32 order_needed_for_size(u32 size, struct mali_memory_bank * bank) +{ + u32 order = 0; + + if (0 < size) + { + for ( order = sizeof(u32)*8 - 1; ((1UL<min_order)) order = bank->min_order; + /* Not capped to max order, that doesn't make sense */ + + return order; +} + +MALI_STATIC_INLINE u32 maximum_order_which_fits(u32 size) +{ + u32 order = 0; + u32 powsize = 1; + while (powsize < size) + { + powsize <<= 1; + if (powsize > size) break; + order++; + } + + return order; +} + +/* called for new MEMORY resources */ +static _mali_osk_errcode_t mali_memory_bank_register(u32 phys_base, u32 cpu_usage_adjust, u32 size, u32 flags, u32 alloc_order, const char *name) +{ + /* no locking performed due to function contract */ + int i; + u32 left, offset; + mali_memory_bank * bank; + mali_memory_bank * bank_enum, *temp; + + _mali_osk_errcode_t err; + + /* Only a multiple of MIN_BLOCK_SIZE is usable */ + u32 usable_size = size & ~(MIN_BLOCK_SIZE - 1); + + /* handle zero sized banks and bank smaller than the fixed block size */ + if (0 == usable_size) + { + MALI_PRINT(("Usable size == 0\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + /* warn for banks not a muliple of the block size */ + MALI_DEBUG_PRINT_IF(1, usable_size != size, ("Memory bank @ 0x%X not a multiple of minimum block size. %d bytes wasted\n", phys_base, size - usable_size)); + + /* check against previous registrations */ + MALI_DEBUG_CODE( + { + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + /* duplicate ? */ + if (bank->base_addr == phys_base) + { + MALI_PRINT(("Duplicate registration of a memory bank at 0x%X detected\n", phys_base)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + /* overlapping ? */ + else if ( + ( (phys_base > bank->base_addr) && (phys_base < (bank->base_addr + bank->real_size)) ) || + ( (phys_base + size) > bank->base_addr && ((phys_base + size) < (bank->base_addr + bank->real_size)) ) + ) + { + MALI_PRINT(("Overlapping memory blocks found. Memory at 0x%X overlaps with memory at 0x%X size 0x%X\n", bank->base_addr, phys_base, size)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + } + } + ); + + /* create an object to represent this memory bank */ + MALI_CHECK_NON_NULL(bank = (mali_memory_bank*)_mali_osk_malloc(sizeof(mali_memory_bank)), _MALI_OSK_ERR_NOMEM); + + /* init the fields */ + _MALI_OSK_INIT_LIST_HEAD(&bank->list); + bank->base_addr = phys_base; + bank->cpu_usage_adjust = cpu_usage_adjust; + bank->size = usable_size; + bank->real_size = size; + bank->alloc_order = alloc_order; + bank->name = name; + + err = _mali_osk_atomic_init(&bank->num_active_allocations, 0); + if (err != _MALI_OSK_ERR_OK) + { + _mali_osk_free(bank); + MALI_ERROR(err); + } + + bank->used_for_flags = flags; + bank->min_order = order_needed_for_size(MIN_BLOCK_SIZE, NULL); + bank->max_order = maximum_order_which_fits(usable_size); + bank->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0); + if (NULL == bank->lock) + { + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + bank->blocklist = _mali_osk_calloc(1, sizeof(struct mali_memory_block) * (usable_size / MIN_BLOCK_SIZE)); + if (NULL == bank->blocklist) + { + _mali_osk_lock_term(bank->lock); + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + for (i = 0; i < (usable_size / MIN_BLOCK_SIZE); i++) + { + bank->blocklist[i].bank = bank; + } + + bank->freelist = _mali_osk_calloc(1, sizeof(_mali_osk_list_t) * (bank->max_order - bank->min_order + 1)); + if (NULL == bank->freelist) + { + _mali_osk_lock_term(bank->lock); + _mali_osk_free(bank->blocklist); + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + for (i = 0; i < (bank->max_order - bank->min_order + 1); i++) _MALI_OSK_INIT_LIST_HEAD(&bank->freelist[i]); + + /* init slot info */ + for (offset = 0, left = usable_size; offset < (usable_size / MIN_BLOCK_SIZE); /* updated inside the body */) + { + u32 block_order; + mali_memory_block * block; + + /* the maximum order which fits in the remaining area */ + block_order = maximum_order_which_fits(left); + + /* find the block pointer */ + block = &bank->blocklist[offset]; + + /* tag the block as being toplevel */ + set_block_toplevel(block, block_order); + + /* tag it as being free */ + set_block_free(block, 1); + + /* set the order */ + set_block_order(block, block_order); + + _mali_osk_list_addtail(&block->link, bank->freelist + (block_order - bank->min_order)); + + left -= (1 << block_order); + offset += ((1 << block_order) / MIN_BLOCK_SIZE); + } + + /* add bank to list of banks on the system */ + _MALI_OSK_LIST_FOREACHENTRY( bank_enum, temp, &memory_banks_list, mali_memory_bank, list ) + { + if ( bank_enum->alloc_order >= alloc_order ) + { + /* Found insertion point - our item must go before this one */ + break; + } + } + _mali_osk_list_addtail(&bank->list, &bank_enum->list); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_memory_mmu_register(u32 type, u32 phys_base) +{ + /* not supported */ + return _MALI_OSK_ERR_INVALID_FUNC; +} + +void mali_memory_mmu_unregister(u32 phys_base) +{ + /* not supported */ + return; +} + +static mali_memory_block * mali_memory_block_get(u32 type_id, u32 minimum_size) +{ + mali_memory_bank * bank; + mali_memory_block * block = NULL; + u32 requested_order, current_order; + + /* input validation */ + if (0 == minimum_size) + { + /* bad size */ + MALI_DEBUG_PRINT(2, ("Zero size block requested by mali_memory_block_get\n")); + return NULL; + } + + bank = (mali_memory_bank*)type_id; + + requested_order = order_needed_for_size(minimum_size, bank); + + MALI_DEBUG_PRINT(4, ("For size %d we need order %d (%d)\n", minimum_size, requested_order, 1 << requested_order)); + + _mali_osk_lock_wait(bank->lock, _MALI_OSK_LOCKMODE_RW); + /* ! critical section begin */ + + MALI_DEBUG_PRINT(7, ("Bank 0x%x locked\n", bank)); + + for (current_order = requested_order; current_order <= bank->max_order; ++current_order) + { + _mali_osk_list_t * list = bank->freelist + (current_order - bank->min_order); + MALI_DEBUG_PRINT(7, ("Checking freelist 0x%x for order %d\n", list, current_order)); + if (0 != _mali_osk_list_empty(list)) continue; /* empty list */ + + MALI_DEBUG_PRINT(7, ("Found an entry on the freelist for order %d\n", current_order)); + + + block = _MALI_OSK_LIST_ENTRY(list->next, mali_memory_block, link); + _mali_osk_list_delinit(&block->link); + + while (current_order > requested_order) + { + mali_memory_block * buddy_block; + MALI_DEBUG_PRINT(7, ("Splitting block 0x%x\n", block)); + current_order--; + list--; + buddy_block = block_get_buddy(block, current_order - bank->min_order); + set_block_order(buddy_block, current_order); + set_block_free(buddy_block, 1); + _mali_osk_list_add(&buddy_block->link, list); + } + + set_block_order(block, current_order); + set_block_free(block, 0); + + /* update usage count */ + _mali_osk_atomic_inc(&bank->num_active_allocations); + + break; + } + + /* ! critical section end */ + _mali_osk_lock_signal(bank->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT(7, ("Lock released for bank 0x%x\n", bank)); + + MALI_DEBUG_PRINT_IF(7, NULL != block, ("Block 0x%x allocated\n", block)); + + return block; +} + + +static void block_release(mali_memory_block * block) +{ + mali_memory_bank * bank; + u32 current_order; + + if (NULL == block) return; + + bank = block->bank; + + /* we're manipulating the free list, so we need to lock it */ + _mali_osk_lock_wait(bank->lock, _MALI_OSK_LOCKMODE_RW); + /* ! critical section begin */ + + set_block_free(block, 1); + current_order = get_block_order(block); + + while (current_order <= bank->max_order) + { + mali_memory_block * buddy_block; + buddy_block = block_get_buddy(block, current_order - bank->min_order); + if (!block_is_valid_buddy(buddy_block, current_order)) break; + _mali_osk_list_delinit(&buddy_block->link); /* remove from free list */ + /* clear tracked data in both blocks */ + set_block_order(block, 0); + set_block_free(block, 0); + set_block_order(buddy_block, 0); + set_block_free(buddy_block, 0); + /* make the parent control the new state */ + block = block_get_parent(block, current_order - bank->min_order); + set_block_order(block, current_order + 1); /* merged has a higher order */ + set_block_free(block, 1); /* mark it as free */ + current_order++; + if (get_block_toplevel(block) == current_order) break; /* stop the merge if we've arrived at a toplevel block */ + } + + _mali_osk_list_add(&block->link, &bank->freelist[current_order - bank->min_order]); + + /* update bank usage statistics */ + _mali_osk_atomic_dec(&block->bank->num_active_allocations); + + /* !critical section end */ + _mali_osk_lock_signal(bank->lock, _MALI_OSK_LOCKMODE_RW); + + return; +} + +MALI_STATIC_INLINE u32 block_get_offset(mali_memory_block * block) +{ + return block - block->bank->blocklist; +} + +MALI_STATIC_INLINE u32 block_mali_addr_get(mali_memory_block * block) +{ + if (NULL != block) return block->bank->base_addr + MIN_BLOCK_SIZE * block_get_offset(block); + else return 0; +} + +MALI_STATIC_INLINE u32 block_cpu_addr_get(mali_memory_block * block) +{ + if (NULL != block) return (block->bank->base_addr + MIN_BLOCK_SIZE * block_get_offset(block)) + block->bank->cpu_usage_adjust; + else return 0; +} + +MALI_STATIC_INLINE u32 block_size_get(mali_memory_block * block) +{ + if (NULL != block) return 1 << get_block_order(block); + else return 0; +} + +MALI_STATIC_INLINE void __user * block_mapping_get(mali_memory_block * block) +{ + if (NULL != block) return block->mapping; + else return NULL; +} + +MALI_STATIC_INLINE void block_mapping_set(mali_memory_block * block, void __user * ptr) +{ + if (NULL != block) block->mapping = ptr; +} + +MALI_STATIC_INLINE u32 block_mmap_cookie_get(mali_memory_block * block) +{ + if (NULL != block) return block->mmap_cookie; + else return 0; +} + +/** + * Set the cookie returned via _mali_ukk_mem_mmap(). + * @param block The memory block to set the cookie for + * @param cookie the cookie + */ +MALI_STATIC_INLINE void block_mmap_cookie_set(mali_memory_block * block, u32 cookie) +{ + if (NULL != block) block->mmap_cookie = cookie; +} + + +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + memory_session * session_data; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (NULL != *slot) + { + MALI_DEBUG_PRINT(1, ("The slot given to memory session begin already contains data")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + /* create the session data object */ + MALI_CHECK_NON_NULL(session_data = _mali_osk_malloc(sizeof(memory_session)), _MALI_OSK_ERR_NOMEM); + + /* create descriptor mapping table */ + session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX); + + if (NULL == session_data->descriptor_mapping) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->memory_head); /* no memory in use */ + session_data->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0); + if (NULL == session_data->lock) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + *slot = session_data; /* slot will point to our data object */ + + MALI_SUCCESS; +} + +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + memory_session * session_data; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + return; + } + + if (NULL == *slot) + { + MALI_DEBUG_PRINT(1, ("NULL memory_session found in current session object")); + return; + } + + _mali_osk_lock_wait(((memory_session*)*slot)->lock, _MALI_OSK_LOCKMODE_RW); + session_data = (memory_session *)*slot; + /* clear our slot */ + *slot = NULL; + + /* + First free all memory still being used. + This can happen if the caller has leaked memory or + the application has crashed forcing an auto-session end. + */ + if (0 == _mali_osk_list_empty(&session_data->memory_head)) + { + mali_memory_block * block, * temp; + MALI_DEBUG_PRINT(1, ("Memory found on session usage list during session termination\n")); + + /* use the _safe version since fre_big_block removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(block, temp, &session_data->memory_head, mali_memory_block, link) + { + _mali_osk_errcode_t err; + _mali_uk_free_big_block_s uk_args; + + MALI_DEBUG_PRINT(4, ("Freeing block 0x%x with mali address 0x%x size %d mapped in user space at 0x%x\n", + block, + (void*)block_mali_addr_get(block), + block_size_get(block), + block_mapping_get(block)) + ); + + /* free the block */ + /** @note manual type safety check-point */ + uk_args.ctx = mali_session_data; + uk_args.cookie = (u32)block->descriptor; + err = _mali_ukk_free_big_block_internal( mali_session_data, session_data, &uk_args ); + + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT_ERROR(("_mali_ukk_free_big_block_internal() failed during session termination on block with cookie==0x%X\n", + uk_args.cookie) + ); + } + } + } + + if (NULL != session_data->descriptor_mapping) + { + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + session_data->descriptor_mapping = NULL; + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_term(session_data->lock); + + /* free the session data object */ + _mali_osk_free(session_data); + + return; +} + +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info) +{ + mali_memory_bank * bank, *temp; + _mali_mem_info **mem_info_tail; + + /* check input */ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + /* make sure we won't leak any memory. It could also be that it's an uninitialized variable, but that would be a bug in the caller */ + MALI_DEBUG_ASSERT(NULL == info->mem_info); + + mem_info_tail = &info->mem_info; + + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + _mali_mem_info * mem_info; + + mem_info = (_mali_mem_info *)_mali_osk_calloc(1, sizeof(_mali_mem_info)); + if (NULL == mem_info) return _MALI_OSK_ERR_NOMEM; /* memory already allocated will be freed by the caller */ + + /* set info */ + mem_info->size = bank->size; + mem_info->flags = (_mali_bus_usage)bank->used_for_flags; + mem_info->maximum_order_supported = bank->max_order; + mem_info->identifier = (u32)bank; + + /* add to system info linked list */ + (*mem_info_tail) = mem_info; + mem_info_tail = &mem_info->next; + } + + /* all OK */ + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_memory(_mali_osk_resource_t * resource) +{ + _mali_osk_errcode_t err; + + /* Request ownership of the memory */ + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, resource->size, resource->description)) + { + MALI_DEBUG_PRINT(1, ("Failed to request memory region %s (0x%08X - 0x%08X)\n", resource->description, resource->base, resource->base + resource->size - 1)); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* call backend */ + err = mali_memory_bank_register(resource->base, resource->cpu_usage_adjust, resource->size, resource->flags, resource->alloc_order, resource->description); + if (_MALI_OSK_ERR_OK != err) + { + /* if backend refused the memory we have to release the region again */ + MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n")); + _mali_osk_mem_unreqregion(resource->base, resource->size); + MALI_ERROR(err); + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource) +{ + /* Not supported by the fixed block memory system */ + MALI_DEBUG_PRINT(1, ("MMU resource not supported by non-MMU driver!\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_FUNC); +} + +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource) +{ + mali_io_address mapping; + + MALI_DEBUG_PRINT(5, ("FPGA framework '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + sizeof(u32) * 2 - 1 + )); + + mapping = _mali_osk_mem_mapioregion(resource->base + 0x1000, sizeof(u32) * 2, "fpga framework"); + if (mapping) + { + u32 data; + data = _mali_osk_mem_ioread32(mapping, 0); + MALI_DEBUG_PRINT(2, ("FPGA framwork '%s' @ 0x%08X:\n", resource->description, resource->base)); + MALI_DEBUG_PRINT(2, ("\tBitfile date: %d%02d%02d_%02d%02d\n", + (data >> 20), + (data >> 16) & 0xF, + (data >> 11) & 0x1F, + (data >> 6) & 0x1F, + (data >> 0) & 0x3F)); + data = _mali_osk_mem_ioread32(mapping, sizeof(u32)); + MALI_DEBUG_PRINT(2, ("\tBitfile SCCS rev: %d\n", data)); + + _mali_osk_mem_unmapioregion(resource->base + 0x1000, sizeof(u32) *2, mapping); + } + else MALI_DEBUG_PRINT(1, ("Failed to access FPGA framwork '%s' @ 0x%08X\n", resource->description, resource->base)); + + MALI_SUCCESS; +} + +/* static _mali_osk_errcode_t get_big_block(void * ukk_private, struct mali_session_data * mali_session_data, void __user * argument) */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ) +{ + _mali_uk_mem_mmap_s args_mmap = {0, }; + int md; + mali_memory_block * block; + _mali_osk_errcode_t err; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER( args ); + + MALI_DEBUG_ASSERT_POINTER( args->ctx ); + + /** @note manual type safety check-point */ + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (!args->type_id) + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* at least min block size */ + if (MIN_BLOCK_SIZE > args->minimum_size_requested) args->minimum_size_requested = MIN_BLOCK_SIZE; + + /* perform the actual allocation */ + block = mali_memory_block_get(args->type_id, args->minimum_size_requested); + if ( NULL == block ) + { + /* no memory available with requested type_id */ + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, block, &md)) + { + block_release(block); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + block->descriptor = md; + + + /* fill in response */ + args->mali_address = block_mali_addr_get(block); + args->block_size = block_size_get(block); + args->cookie = (u32)md; + args->flags = block->bank->used_for_flags; + + /* map the block into the process' address space */ + + /** @note manual type safety check-point */ + args_mmap.ukk_private = (void *)args->ukk_private; + args_mmap.ctx = args->ctx; + args_mmap.size = args->block_size; + args_mmap.phys_addr = block_cpu_addr_get(block); + +#ifndef _MALI_OSK_SPECIFIC_INDIRECT_MMAP + err = _mali_ukk_mem_mmap( &args_mmap ); +#else + err = _mali_osk_specific_indirect_mmap( &args_mmap ); +#endif + + /* check if the mapping failed */ + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT(1, ("Memory mapping failed 0x%x\n", args->cpuptr)); + /* mapping failed */ + + /* remove descriptor entry */ + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + + /* free the mali memory */ + block_release(block); + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + + args->cpuptr = args_mmap.mapping; + block_mmap_cookie_set(block, args_mmap.cookie); + block_mapping_set(block, args->cpuptr); + + MALI_DEBUG_PRINT(2, ("Mali memory 0x%x (size %d) mapped in process memory space at 0x%x\n", (void*)args->mali_address, args->block_size, args->cpuptr)); + + /* track memory in use for the session */ + _mali_osk_list_addtail(&block->link, &session_data->memory_head); + + /* memory assigned to the session, memory mapped into the process' view */ + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_SUCCESS; +} + +/* Internal code that assumes the memory session lock is held */ +static _mali_osk_errcode_t _mali_ukk_free_big_block_internal( struct mali_session_data * mali_session_data, memory_session * session_data, _mali_uk_free_big_block_s *args) +{ + mali_memory_block * block = NULL; + _mali_osk_errcode_t err; + _mali_uk_mem_munmap_s args_munmap = {0,}; + + MALI_DEBUG_ASSERT_POINTER( mali_session_data ); + MALI_DEBUG_ASSERT_POINTER( session_data ); + MALI_DEBUG_ASSERT_POINTER( args ); + + err = mali_descriptor_mapping_get(session_data->descriptor_mapping, (int)args->cookie, (void**)&block); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release memory pages\n", (int)args->cookie)); + MALI_ERROR(err); + } + + MALI_DEBUG_ASSERT_POINTER(block); + + MALI_DEBUG_PRINT(4, ("Asked to free block 0x%x with mali address 0x%x size %d mapped in user space at 0x%x\n", + block, + (void*)block_mali_addr_get(block), + block_size_get(block), + block_mapping_get(block)) + ); + + /** @note manual type safety check-point */ + args_munmap.ctx = (void*)mali_session_data; + args_munmap.mapping = block_mapping_get( block ); + args_munmap.size = block_size_get( block ); + args_munmap.cookie = block_mmap_cookie_get( block ); + +#ifndef _MALI_OSK_SPECIFIC_INDIRECT_MMAP + _mali_ukk_mem_munmap( &args_munmap ); +#else + _mali_osk_specific_indirect_munmap( &args_munmap ); +#endif + + MALI_DEBUG_PRINT(6, ("Session data 0x%x, lock 0x%x\n", session_data, &session_data->lock)); + + /* unlink from session usage list */ + MALI_DEBUG_PRINT(5, ("unlink from session usage list\n")); + _mali_osk_list_delinit(&block->link); + + /* remove descriptor entry */ + mali_descriptor_mapping_free(session_data->descriptor_mapping, (int)args->cookie); + + /* free the mali memory */ + block_release(block); + MALI_DEBUG_PRINT(5, ("Block freed\n")); + + MALI_SUCCESS; +} + +/* static _mali_osk_errcode_t free_big_block( struct mali_session_data * mali_session_data, void __user * argument) */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ) +{ + _mali_osk_errcode_t err; + struct mali_session_data * mali_session_data; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER( args ); + + MALI_DEBUG_ASSERT_POINTER( args->ctx ); + + /** @note manual type safety check-point */ + mali_session_data = (struct mali_session_data *)args->ctx; + + /* Must always verify this, since these are provided by the user */ + MALI_CHECK_NON_NULL(mali_session_data, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + /** @note this has been separated out so that the session_end handler can call this while it has the memory_session lock held */ + err = _mali_ukk_free_big_block_internal( mali_session_data, session_data, args ); + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + return err; +} + +MALI_STATIC_INLINE u32 get_block_free(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_FREE) & MISC_MASK_FREE; +} + +MALI_STATIC_INLINE void set_block_free(mali_memory_block * block, int state) +{ + if (state) block->misc |= (MISC_MASK_FREE << MISC_SHIFT_FREE); + else block->misc &= ~(MISC_MASK_FREE << MISC_SHIFT_FREE); +} + +MALI_STATIC_INLINE void set_block_order(mali_memory_block * block, u32 order) +{ + block->misc &= ~(MISC_MASK_ORDER << MISC_SHIFT_ORDER); + block->misc |= ((order & MISC_MASK_ORDER) << MISC_SHIFT_ORDER); +} + +MALI_STATIC_INLINE u32 get_block_order(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_ORDER) & MISC_MASK_ORDER; +} + +MALI_STATIC_INLINE void set_block_toplevel(mali_memory_block * block, u32 level) +{ + block->misc |= ((level & MISC_MASK_TOPLEVEL) << MISC_SHIFT_TOPLEVEL); +} + +MALI_STATIC_INLINE u32 get_block_toplevel(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_TOPLEVEL) & MISC_MASK_TOPLEVEL; +} + +MALI_STATIC_INLINE int block_is_valid_buddy(mali_memory_block * block, int order) +{ + if (get_block_free(block) && (get_block_order(block) == order)) return 1; + else return 0; +} + +MALI_STATIC_INLINE mali_memory_block * block_get_buddy(mali_memory_block * block, u32 order) +{ + return block + ( (block_get_offset(block) ^ (1 << order)) - block_get_offset(block)); +} + +MALI_STATIC_INLINE mali_memory_block * block_get_parent(mali_memory_block * block, u32 order) +{ + return block + ((block_get_offset(block) & ~(1 << order)) - block_get_offset(block)); +} + +/* This handler registered to mali_mmap for non-MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ) +{ + _mali_osk_errcode_t ret; + struct mali_session_data * mali_session_data; + mali_memory_allocation * descriptor; + memory_session * session_data; + + /* validate input */ + if (NULL == args) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: args was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + /* Unpack arguments */ + mali_session_data = (struct mali_session_data *)args->ctx; + + if (NULL == mali_session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: mali_session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + MALI_DEBUG_ASSERT( mali_subsystem_memory_id >= 0 ); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + /* validate input */ + if (NULL == session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_FAULT); } + + descriptor = (mali_memory_allocation*) _mali_osk_calloc( 1, sizeof(mali_memory_allocation) ); + if (NULL == descriptor) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_NOMEM); } + + descriptor->size = args->size; + descriptor->mali_address = args->phys_addr; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = args->ukk_private; /* save to be used during physical manager callback */ + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE; + + ret = _mali_osk_mem_mapregion_init( descriptor ); + if ( _MALI_OSK_ERR_OK != ret ) + { + MALI_DEBUG_PRINT(3, ("_mali_osk_mem_mapregion_init() failed\n")); + _mali_osk_free(descriptor); + MALI_ERROR(ret); + } + + ret = _mali_osk_mem_mapregion_map( descriptor, 0, &descriptor->mali_address, descriptor->size ); + if ( _MALI_OSK_ERR_OK != ret ) + { + MALI_DEBUG_PRINT(3, ("_mali_osk_mem_mapregion_map() failed\n")); + _mali_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + MALI_ERROR(ret); + } + + args->mapping = descriptor->mapping; + + /** + * @note we do not require use of mali_descriptor_mapping here: + * the cookie gets stored in the mali_memory_block struct, which itself is + * protected by mali_descriptor_mapping, and so this cookie never leaves + * kernel space (on any OS). + * + * In the MMU case, we must use a mali_descriptor_mapping, since on _some_ + * OSs, the cookie leaves kernel space. + */ + args->cookie = (u32)descriptor; + MALI_SUCCESS; +} + +/* This handler registered to mali_munmap for non-MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ) +{ + mali_memory_allocation * descriptor; + + /** see note in _mali_ukk_mem_mmap() - no need to use descriptor mapping */ + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /* args->mapping and args->size are also discarded. They are only necessary for certain do_munmap implementations. However, they could be used to check the descriptor at this point. */ + _mali_osk_mem_mapregion_unmap( descriptor, 0, descriptor->size, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_mem_mapregion_term( descriptor ); + + _mali_osk_free(descriptor); + + return _MALI_OSK_ERR_OK; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.c new file mode 100644 index 00000000000..7a48e27ede7 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.c @@ -0,0 +1,2924 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_ioctl.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_kernel_mem_mmu.h" +#include "mali_kernel_memory_engine.h" +#include "mali_block_allocator.h" +#include "mali_kernel_mem_os.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_core.h" + +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +#include "ump_kernel_interface.h" +#endif + +/* kernel side OS functions and user-kernel interface */ +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" + +/** + * Size of the MMU registers in bytes + */ +#define MALI_MMU_REGISTERS_SIZE 0x24 + +/** + * Size of an MMU page in bytes + */ +#define MALI_MMU_PAGE_SIZE 0x1000 + +/** + * Page directory index from address + * Calculates the page directory index from the given address + */ +#define MALI_MMU_PDE_ENTRY(address) (((address)>>22) & 0x03FF) + +/** + * Page table index from address + * Calculates the page table index from the given address + */ +#define MALI_MMU_PTE_ENTRY(address) (((address)>>12) & 0x03FF) + +/** + * Extract the memory address from an PDE/PTE entry + */ +#define MALI_MMU_ENTRY_ADDRESS(value) ((value) & 0xFFFFFC00) + +/** + * Linux kernel version has marked SA_SHIRQ as deprecated, IRQF_SHARED should be used. + * This is to handle older kernels which haven't done this swap. + */ +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif /* IRQF_SHARED */ + +/** + * Per-session memory descriptor mapping table sizes + */ +#define MALI_MEM_DESCRIPTORS_INIT 64 +#define MALI_MEM_DESCRIPTORS_MAX 65536 + +/** + * Used to disallow more than one core to run a MMU at the same time + * + * @note This value is hardwired into some systems' configuration files, + * which \em might not be a header file (e.g. some external data configuration + * file). Therefore, if this value is modified, its occurance must be + * \b manually checked for in the entire driver source tree. + */ +#define MALI_MMU_DISALLOW_PARALLELL_WORK_OF_MALI_CORES 1 + +#define MALI_INVALID_PAGE ((u32)(~0)) + +/** + * + */ +typedef enum mali_mmu_entry_flags +{ + MALI_MMU_FLAGS_PRESENT = 0x01, + MALI_MMU_FLAGS_READ_PERMISSION = 0x02, + MALI_MMU_FLAGS_WRITE_PERMISSION = 0x04, + MALI_MMU_FLAGS_MASK = 0x07 +} mali_mmu_entry_flags; + +/** + * MMU register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_mmu_register { + MALI_MMU_REGISTER_DTE_ADDR = 0x0000, /**< Current Page Directory Pointer */ + MALI_MMU_REGISTER_STATUS = 0x0001, /**< Status of the MMU */ + MALI_MMU_REGISTER_COMMAND = 0x0002, /**< Command register, used to control the MMU */ + MALI_MMU_REGISTER_PAGE_FAULT_ADDR = 0x0003, /**< Logical address of the last page fault */ + MALI_MMU_REGISTER_ZAP_ONE_LINE = 0x004, /**< Used to invalidate the mapping of a single page from the MMU */ + MALI_MMU_REGISTER_INT_RAWSTAT = 0x0005, /**< Raw interrupt status, all interrupts visible */ + MALI_MMU_REGISTER_INT_CLEAR = 0x0006, /**< Indicate to the MMU that the interrupt has been received */ + MALI_MMU_REGISTER_INT_MASK = 0x0007, /**< Enable/disable types of interrupts */ + MALI_MMU_REGISTER_INT_STATUS = 0x0008 /**< Interrupt status based on the mask */ +} mali_mmu_register; + +/** + * MMU interrupt register bits + * Each cause of the interrupt is reported + * through the (raw) interrupt status registers. + * Multiple interrupts can be pending, so multiple bits + * can be set at once. + */ +typedef enum mali_mmu_interrupt +{ + MALI_MMU_INTERRUPT_PAGE_FAULT = 0x01, /**< A page fault occured */ + MALI_MMU_INTERRUPT_READ_BUS_ERROR = 0x02 /**< A bus read error occured */ +} mali_mmu_interrupt; + +/** + * MMU commands + * These are the commands that can be sent + * to the MMU unit. + */ +typedef enum mali_mmu_command +{ + MALI_MMU_COMMAND_ENABLE_PAGING = 0x00, /**< Enable paging (memory translation) */ + MALI_MMU_COMMAND_DISABLE_PAGING = 0x01, /**< Disable paging (memory translation) */ + MALI_MMU_COMMAND_ENABLE_STALL = 0x02, /**< Enable stall on page fault */ + MALI_MMU_COMMAND_DISABLE_STALL = 0x03, /**< Disable stall on page fault */ + MALI_MMU_COMMAND_ZAP_CACHE = 0x04, /**< Zap the entire page table cache */ + MALI_MMU_COMMAND_PAGE_FAULT_DONE = 0x05, /**< Page fault processed */ + MALI_MMU_COMMAND_SOFT_RESET = 0x06 /**< Reset the MMU back to power-on settings */ +} mali_mmu_command; + +typedef enum mali_mmu_status_bits +{ + MALI_MMU_STATUS_BIT_PAGING_ENABLED = 1 << 0, + MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE = 1 << 1, + MALI_MMU_STATUS_BIT_STALL_ACTIVE = 1 << 2, + MALI_MMU_STATUS_BIT_IDLE = 1 << 3, + MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY = 1 << 4, + MALI_MMU_STATUS_BIT_PAGE_FAULT_IS_WRITE = 1 << 5, +} mali_mmu_status_bits; + +/** + * Defintion of the type used to represent memory used by a session. + * Containts the pointer to the huge user space virtual memory area + * used to access the Mali memory. + */ +typedef struct memory_session +{ + _mali_osk_lock_t *lock; /**< Lock protecting the vm manipulation */ + + u32 mali_base_address; /**< Mali virtual memory area used by this session */ + mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */ + + u32 page_directory; /**< Physical address of the memory session's page directory */ + + mali_io_address page_directory_mapped; /**< Pointer to the mapped version of the page directory into the kernel's address space */ + mali_io_address page_entries_mapped[1024]; /**< Pointers to the page tables which exists in the page directory mapped into the kernel's address space */ + u32 page_entries_usage_count[1024]; /**< Tracks usage count of the page table pages, so they can be releases on the last reference */ + + _mali_osk_list_t active_mmus; /**< The MMUs in this session, in increasing order of ID (so we can lock them in the correct order when necessary) */ + _mali_osk_list_t memory_head; /**< Track all the memory allocated in this session, for freeing on abnormal termination */ +} memory_session; + +typedef struct mali_kernel_memory_mmu_idle_callback +{ + _mali_osk_list_t link; + void (*callback)(void*); + void * callback_argument; +} mali_kernel_memory_mmu_idle_callback; + +/** + * Definition of the MMU struct + * Used to track a MMU unit in the system. + * Contains information about the mapping of the registers + */ +typedef struct mali_kernel_memory_mmu +{ + int id; /**< ID of the MMU, no duplicate IDs may exist on the system */ + const char * description; /**< Description text received from the resource manager to help identify the resource for people */ + int irq_nr; /**< IRQ number */ + u32 base; /**< Physical address of the registers */ + mali_io_address mapped_registers; /**< Virtual mapping of the registers */ + u32 mapping_size; /**< Size of registers in bytes */ + _mali_osk_list_t list; /**< Used to link multiple MMU's into a list */ + _mali_osk_irq_t *irq; + u32 flags; /**< Used to store if there is something special with this mmu. */ + + _mali_osk_lock_t *lock; /**< Lock protecting access to the usage fields */ + /* usage fields */ + memory_session * active_session; /**< Active session, NULL if no session is active */ + u32 usage_count; /**< Number of nested activations of the active session */ + _mali_osk_list_t callbacks; /**< Callback registered for MMU idle notification */ + + int in_page_fault_handler; + + _mali_osk_list_t session_link; +} mali_kernel_memory_mmu; + +typedef struct dedicated_memory_info +{ + u32 base; + u32 size; + struct dedicated_memory_info * next; +} dedicated_memory_info; + +/* types used for external_memory and ump_memory physical memory allocators, which are using the mali_allocation_engine */ +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +typedef struct ump_mem_allocation +{ + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 initial_offset; + u32 size_allocated; + ump_dd_handle ump_mem; +} ump_mem_allocation ; +#endif + +typedef struct external_mem_allocation +{ + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 initial_offset; + u32 size; +} external_mem_allocation; + +/* + Subsystem interface implementation +*/ +/** + * Fixed block memory subsystem startup function. + * Called by the driver core when the driver is loaded. + * Registers the memory systems ioctl handler, resource handlers and memory map function with the core. + * + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id); + +/** + * Fixed block memory subsystem shutdown function. + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the memory subsystem + */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id); + +/** + * MMU Memory load complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * Builds the memory overall memory list + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id); + +/** + * Fixed block memory subsystem session begin notification + * Called by the core when a new session to the driver is started. + * Creates a memory session object and sets it as the subsystem slot data for this session + * @param slot Pointer to the slot to use for storing per-session data + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +/** + * Fixed block memory subsystem session end notification + * Called by the core when a session to the driver has ended. + * Cleans up per session data, which includes checking and fixing memory leaks + * + * @param slot Pointer to the slot to use for storing per-session data + */ +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + +/** + * Fixed block memory subsystem system info filler + * Called by the core when a system info update is needed + * We fill in info about all the memory types we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info); + +/* our registered resource handlers */ + +/** + * Fixed block memory subsystem's notification handler for MMU resource instances. + * Registered with the core during startup. + * Called by the core for each mmu described in the active architecture's config.h file. + * @param resource The resource to handle (type MMU) + * @return 0 if the MMU was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource); + +/** + * Fixed block memory subsystem's notification handler for FPGA_FRAMEWORK resource instances. + * Registered with the core during startup. + * Called by the core for each fpga framework described in the active architecture's config.h file. + * @param resource The resource to handle (type FPGA_FRAMEWORK) + * @return 0 if the FPGA framework was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource); + + +static _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(_mali_osk_resource_t * resource); +static _mali_osk_errcode_t mali_memory_core_resource_os_memory(_mali_osk_resource_t * resource); + +/** + * @brief Internal function for unmapping memory + * + * Worker function for unmapping memory from a user-process. We assume that the + * session/descriptor's lock was obtained before entry. For example, the + * wrapper _mali_ukk_mem_munmap() will lock the descriptor, then call this + * function to do the actual unmapping. mali_memory_core_session_end() could + * also call this directly (depending on compilation options), having locked + * the descriptor. + * + * @param args see _mali_uk_mem_munmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +static void _mali_ukk_mem_munmap_internal( _mali_uk_mem_munmap_s *args ); + +/** + * The MMU interrupt handler + * Upper half of the MMU interrupt processing. + * Called by the kernel when the MMU has triggered an interrupt. + * The interrupt function supports IRQ sharing. So it'll probe the MMU in question + * @param irq The irq number (not used) + * @param dev_id Points to the MMU object being handled + * @param regs Registers of interrupted process (not used) + * @return Standard Linux interrupt result. + * Subset used by the driver is IRQ_HANDLED processed + * IRQ_NONE Not processed + */ +static _mali_osk_errcode_t mali_kernel_memory_mmu_interrupt_handler_upper_half(void * data); + +/** + * The MMU reset hander + * Bottom half of the MMU interrupt processing for page faults and bus errors + * @param work The item to operate on, NULL in our case + */ +static void mali_kernel_memory_mmu_interrupt_handler_bottom_half ( void *data ); + +/** + * Read MMU register value + * Reads the contents of the specified register. + * @param unit The MMU to read from + * @param reg The register to read + * @return The contents of the register + */ +static u32 mali_mmu_register_read(mali_kernel_memory_mmu * unit, mali_mmu_register reg); + +/** + * Write to a MMU register + * Writes the given value to the specified register + * @param unit The MMU to write to + * @param reg The register to write to + * @param val The value to write to the register + */ +static void mali_mmu_register_write(mali_kernel_memory_mmu * unit, mali_mmu_register reg, u32 val); + + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +static void ump_memory_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result ump_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER != 0*/ + + +static void external_memory_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result external_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); + + + + +/* nop functions */ + +/* mali address manager needs to allocate page tables on allocate, write to page table(s) on map, write to page table(s) and release page tables on release */ +static _mali_osk_errcode_t mali_address_manager_allocate(mali_memory_allocation * descriptor); /* validates the range, allocates memory for the page tables if needed */ +static _mali_osk_errcode_t mali_address_manager_map(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size); +static void mali_address_manager_release(mali_memory_allocation * descriptor); + +static void mali_mmu_activate_address_space(mali_kernel_memory_mmu * mmu, u32 page_directory); + +_mali_osk_errcode_t mali_mmu_page_table_cache_create(void); +void mali_mmu_page_table_cache_destroy(void); + +_mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping); +void mali_mmu_release_table_page(u32 pa); + +static _mali_osk_errcode_t mali_allocate_empty_page_directory(void); + +static void mali_free_empty_page_directory(void); + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data); + +static _mali_osk_errcode_t mali_allocate_fault_flush_pages(void); + +static void mali_free_fault_flush_pages(void); + +static void mali_mmu_probe_irq_trigger(mali_kernel_memory_mmu * mmu); +static _mali_osk_errcode_t mali_mmu_probe_irq_acknowledge(mali_kernel_memory_mmu * mmu); + +/* MMU variables */ + +typedef struct mali_mmu_page_table_allocation +{ + _mali_osk_list_t list; + u32 * usage_map; + u32 usage_count; + u32 num_pages; + mali_page_table_block pages; +} mali_mmu_page_table_allocation; + +typedef struct mali_mmu_page_table_allocations +{ + _mali_osk_lock_t *lock; + _mali_osk_list_t partial; + _mali_osk_list_t full; + /* we never hold on to a empty allocation */ +} mali_mmu_page_table_allocations; + +/* Head of the list of MMUs */ +static _MALI_OSK_LIST_HEAD(mmu_head); + +/* the mmu page table cache */ +static struct mali_mmu_page_table_allocations page_table_cache; + +/* page fault queue flush helper pages + * note that the mapping pointers are currently unused outside of the initialization functions */ +static u32 mali_page_fault_flush_page_directory = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_directory_mapping = NULL; +static u32 mali_page_fault_flush_page_table = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_table_mapping = NULL; +static u32 mali_page_fault_flush_data_page = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_data_page_mapping = NULL; + +/* an empty page directory (no address valid) which is active on any MMU not currently marked as in use */ +static u32 mali_empty_page_directory = MALI_INVALID_PAGE; + +/* + The fixed memory system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_memory = +{ + mali_memory_core_initialize, /* startup */ + mali_memory_core_terminate, /* shutdown */ + mali_memory_core_load_complete, /* load_complete */ + mali_memory_core_system_info_fill, /* system_info_fill */ + mali_memory_core_session_begin, /* session_begin */ + mali_memory_core_session_end, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +static mali_kernel_mem_address_manager mali_address_manager = +{ + mali_address_manager_allocate, /* allocate */ + mali_address_manager_release, /* release */ + mali_address_manager_map, /* map_physical */ + NULL /* unmap_physical not present*/ +}; + +static mali_kernel_mem_address_manager process_address_manager = +{ + _mali_osk_mem_mapregion_init, /* allocate */ + _mali_osk_mem_mapregion_term, /* release */ + _mali_osk_mem_mapregion_map, /* map_physical */ + _mali_osk_mem_mapregion_unmap /* unmap_physical */ +}; + +static mali_allocation_engine memory_engine = NULL; +static mali_physical_memory_allocator * physical_memory_allocators = NULL; + +static dedicated_memory_info * mem_region_registrations = NULL; + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_memory_id = (mali_kernel_subsystem_identifier)-1; + +/* called during module init */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(2, ("MMU memory system initializing\n")); + + /* save our subsystem id for later for use in slot lookup during session activation */ + mali_subsystem_memory_id = id; + + _MALI_OSK_INIT_LIST_HEAD(&mmu_head); + + MALI_CHECK_NO_ERROR( mali_mmu_page_table_cache_create() ); + + /* register our handlers */ + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(MMU, mali_memory_core_resource_mmu) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(FPGA_FRAMEWORK, mali_memory_core_resource_fpga) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(MEMORY, mali_memory_core_resource_dedicated_memory) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(OS_MEMORY, mali_memory_core_resource_os_memory) ); + + memory_engine = mali_allocation_engine_create(&mali_address_manager, &process_address_manager); + MALI_CHECK_NON_NULL( memory_engine, _MALI_OSK_ERR_FAULT); + + MALI_SUCCESS; +} + +/* called if/when our module is unloaded */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id) +{ + mali_kernel_memory_mmu * mmu, *temp_mmu; + + MALI_DEBUG_PRINT(2, ("MMU memory system terminating\n")); + + /* loop over all MMU units and shut them down */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + /* reset to defaults */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + + /* unregister the irq */ + _mali_osk_irq_term(mmu->irq); + + /* remove from the list of MMU's on the system */ + _mali_osk_list_del(&mmu->list); + + /* release resources */ + _mali_osk_mem_unmapioregion(mmu->base, mmu->mapping_size, mmu->mapped_registers); + _mali_osk_mem_unreqregion(mmu->base, mmu->mapping_size); + _mali_osk_free(mmu); + } + + /* free global helper pages */ + mali_free_empty_page_directory(); + mali_free_fault_flush_pages(); + + /* destroy the page table cache before shutting down backends in case we have a page table leak to report */ + mali_mmu_page_table_cache_destroy(); + + while ( NULL != mem_region_registrations) + { + dedicated_memory_info * m; + m = mem_region_registrations; + mem_region_registrations = m->next; + _mali_osk_mem_unreqregion(m->base, m->size); + _mali_osk_free(m); + } + + while ( NULL != physical_memory_allocators) + { + mali_physical_memory_allocator * m; + m = physical_memory_allocators; + physical_memory_allocators = m->next; + m->destroy(m); + } + + if (NULL != memory_engine) + { + mali_allocation_engine_destroy(memory_engine); + memory_engine = NULL; + } + +} + +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + memory_session * session_data; + _mali_osk_errcode_t err; + int i; + mali_io_address pd_mapped; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (NULL != *slot) + { + MALI_DEBUG_PRINT(1, ("The slot given to memory session begin already contains data")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_DEBUG_PRINT(2, ("MMU session begin\n")); + + /* create the session data object */ + session_data = _mali_osk_calloc(1, sizeof(memory_session)); + MALI_CHECK_NON_NULL( session_data, _MALI_OSK_ERR_NOMEM ); + + /* create descriptor mapping table */ + session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX); + + if (NULL == session_data->descriptor_mapping) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + err = mali_mmu_get_table_page(&session_data->page_directory, &pd_mapped); + + session_data->page_directory_mapped = pd_mapped; + if (_MALI_OSK_ERR_OK != err) + { + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + _mali_osk_free(session_data); + MALI_ERROR(err); + } + MALI_DEBUG_ASSERT_POINTER( session_data->page_directory_mapped ); + + MALI_DEBUG_PRINT(2, ("Page directory for session 0x%x placed at physical address 0x%08X\n", mali_session_data, session_data->page_directory)); + + for (i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) + { + /* mark each page table as not present */ + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, sizeof(u32) * i, 0); + } + + /* page_table_mapped[] is already set to NULL by _mali_osk_calloc call */ + + _MALI_OSK_INIT_LIST_HEAD(&session_data->active_mmus); + session_data->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 128); + if (NULL == session_data->lock) + { + mali_mmu_release_table_page(session_data->page_directory); + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Init the session's memory allocation list */ + _MALI_OSK_INIT_LIST_HEAD( &session_data->memory_head ); + + *slot = session_data; /* slot will point to our data object */ + MALI_DEBUG_PRINT(2, ("MMU session begin: success\n")); + MALI_SUCCESS; +} + +static void descriptor_table_cleanup_callback(int descriptor_id, void* map_target) +{ + mali_memory_allocation * descriptor; + + descriptor = (mali_memory_allocation*)map_target; + + MALI_DEBUG_PRINT(1, ("Cleanup of descriptor %d mapping to 0x%x in descriptor table\n", descriptor_id, map_target)); + MALI_DEBUG_ASSERT(descriptor); + + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); +} + +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + memory_session * session_data; + int i; + const int num_page_table_entries = sizeof(session_data->page_entries_mapped) / sizeof(session_data->page_entries_mapped[0]); + + MALI_DEBUG_PRINT(2, ("MMU session end\n")); + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + return; + } + + session_data = (memory_session *)*slot; + + if (NULL == session_data) + { + MALI_DEBUG_PRINT(1, ("No session data found during session end\n")); + return; + } + /* Lock the session so we can modify the memory list */ + _mali_osk_lock_wait( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + /* Noninterruptable spinlock type, so must always have locked. Checking should've been done in OSK function. */ + +#ifndef MALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP +#if _MALI_OSK_SPECIFIC_INDIRECT_MMAP +#error Indirect MMAP specified, but UKK does not have implicit MMAP cleanup. Current implementation does not handle this. +#else + + /* Free all memory engine allocations */ + if (0 == _mali_osk_list_empty(&session_data->memory_head)) + { + mali_memory_allocation *descriptor; + mali_memory_allocation *temp; + _mali_uk_mem_munmap_s unmap_args; + + MALI_DEBUG_PRINT(1, ("Memory found on session usage list during session termination\n")); + + unmap_args.ctx = mali_session_data; + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->memory_head, mali_memory_allocation, list) + { + MALI_DEBUG_PRINT(4, ("Freeing block with mali address 0x%x size %d mapped in user space at 0x%x\n", + descriptor->mali_address, descriptor->size, descriptor->size, descriptor->mapping) + ); + /* ASSERT that the descriptor's lock references the correct thing */ + MALI_DEBUG_ASSERT( descriptor->lock == session_data->lock ); + /* Therefore, we have already locked the descriptor */ + + unmap_args.size = descriptor->size; + unmap_args.mapping = descriptor->mapping; + unmap_args.cookie = (u32)descriptor; + + /* + * This removes the descriptor from the list, and frees the descriptor + * + * Does not handle the _MALI_OSK_SPECIFIC_INDIRECT_MMAP case, since + * the only OS we are aware of that requires indirect MMAP also has + * implicit mmap cleanup. + */ + _mali_ukk_mem_munmap_internal( &unmap_args ); + } + } + + /* Assert that we really did free everything */ + MALI_DEBUG_ASSERT( _mali_osk_list_empty(&session_data->memory_head) ); +#endif /* _MALI_OSK_SPECIFIC_INDIRECT_MMAP */ +#endif /* MALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP */ + + if (NULL != session_data->descriptor_mapping) + { + mali_descriptor_mapping_call_for_each(session_data->descriptor_mapping, descriptor_table_cleanup_callback); + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + session_data->descriptor_mapping = NULL; + } + + for (i = 0; i < num_page_table_entries; i++) + { + /* free PTE memory */ + if (session_data->page_directory_mapped && (_mali_osk_mem_ioread32(session_data->page_directory_mapped, sizeof(u32)*i) & MALI_MMU_FLAGS_PRESENT)) + { + mali_mmu_release_table_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); + } + } + + if (MALI_INVALID_PAGE != session_data->page_directory) + { + mali_mmu_release_table_page(session_data->page_directory); + session_data->page_directory = MALI_INVALID_PAGE; + } + + _mali_osk_lock_signal( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + /** + * @note Could the VMA close handler mean that we use the session data after it was freed? + * In which case, would need to refcount the session data, and free on VMA close + */ + + /* Free the lock */ + _mali_osk_lock_term( session_data->lock ); + /* free the session data object */ + _mali_osk_free(session_data); + + /* clear our slot */ + *slot = NULL; + + return; +} + +static _mali_osk_errcode_t mali_allocate_empty_page_directory(void) +{ + _mali_osk_errcode_t err; + mali_io_address mapping; + + MALI_CHECK_NO_ERROR(mali_mmu_get_table_page(&mali_empty_page_directory, &mapping)); + + MALI_DEBUG_ASSERT_POINTER( mapping ); + + err = fill_page(mapping, 0); + if (_MALI_OSK_ERR_OK != err) + { + mali_mmu_release_table_page(mali_empty_page_directory); + mali_empty_page_directory = MALI_INVALID_PAGE; + } + return err; +} + +static void mali_free_empty_page_directory(void) +{ + if (MALI_INVALID_PAGE != mali_empty_page_directory) + { + mali_mmu_release_table_page(mali_empty_page_directory); + mali_empty_page_directory = MALI_INVALID_PAGE; + } +} + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data) +{ + int i; + MALI_DEBUG_ASSERT_POINTER( mapping ); + + for(i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) + { + _mali_osk_mem_iowrite32( mapping, i * sizeof(u32), data); + } + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_allocate_fault_flush_pages(void) +{ + _mali_osk_errcode_t err; + + err = mali_mmu_get_table_page(&mali_page_fault_flush_data_page, &mali_page_fault_flush_data_page_mapping); + if (_MALI_OSK_ERR_OK == err) + { + err = mali_mmu_get_table_page(&mali_page_fault_flush_page_table, &mali_page_fault_flush_page_table_mapping); + if (_MALI_OSK_ERR_OK == err) + { + err = mali_mmu_get_table_page(&mali_page_fault_flush_page_directory, &mali_page_fault_flush_page_directory_mapping); + if (_MALI_OSK_ERR_OK == err) + { + fill_page(mali_page_fault_flush_data_page_mapping, 0); + fill_page(mali_page_fault_flush_page_table_mapping, mali_page_fault_flush_data_page | MALI_MMU_FLAGS_WRITE_PERMISSION | MALI_MMU_FLAGS_READ_PERMISSION | MALI_MMU_FLAGS_PRESENT); + fill_page(mali_page_fault_flush_page_directory_mapping, mali_page_fault_flush_page_table | MALI_MMU_FLAGS_PRESENT); + MALI_SUCCESS; + } + mali_mmu_release_table_page(mali_page_fault_flush_page_table); + mali_page_fault_flush_page_table = MALI_INVALID_PAGE; + mali_page_fault_flush_page_table_mapping = NULL; + } + mali_mmu_release_table_page(mali_page_fault_flush_data_page); + mali_page_fault_flush_data_page = MALI_INVALID_PAGE; + mali_page_fault_flush_data_page_mapping = NULL; + } + MALI_ERROR(err); +} + +static void mali_free_fault_flush_pages(void) +{ + if (MALI_INVALID_PAGE != mali_page_fault_flush_page_directory) + { + mali_mmu_release_table_page(mali_page_fault_flush_page_directory); + mali_page_fault_flush_page_directory = MALI_INVALID_PAGE; + } + + if (MALI_INVALID_PAGE != mali_page_fault_flush_page_table) + { + mali_mmu_release_table_page(mali_page_fault_flush_page_table); + mali_page_fault_flush_page_table = MALI_INVALID_PAGE; + } + + if (MALI_INVALID_PAGE != mali_page_fault_flush_data_page) + { + mali_mmu_release_table_page(mali_page_fault_flush_data_page); + mali_page_fault_flush_data_page = MALI_INVALID_PAGE; + } +} + +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_kernel_memory_mmu * mmu, * temp_mmu; + + /* Report the allocators */ + mali_allocation_engine_report_allocators( physical_memory_allocators ); + + /* allocate the helper pages */ + MALI_CHECK_NO_ERROR( mali_allocate_empty_page_directory() ); + if (_MALI_OSK_ERR_OK != mali_allocate_fault_flush_pages()) + { + mali_free_empty_page_directory(); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* activate the empty page directory on all MMU's */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING); + } + + MALI_DEBUG_PRINT(4, ("MMUs activated\n")); + /* the MMU system is now active */ + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info) +{ + _mali_mem_info * mem_info; + + /* make sure we won't leak any memory. It could also be that it's an uninitialized variable, but that would be a bug in the caller */ + MALI_DEBUG_ASSERT(NULL == info->mem_info); + + info->has_mmu = 1; + + mem_info = _mali_osk_calloc(1,sizeof(_mali_mem_info)); + MALI_CHECK_NON_NULL( mem_info, _MALI_OSK_ERR_NOMEM ); + + mem_info->size = 2048UL * 1024UL * 1024UL; + mem_info->maximum_order_supported = 30; + mem_info->flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE; + mem_info->identifier = 0; + + info->mem_info = mem_info; + + /* all OK */ + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource) +{ + mali_kernel_memory_mmu * mmu; + + MALI_DEBUG_PRINT(4, ("MMU '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1 + )); + + if (NULL != mali_memory_core_mmu_lookup(resource->mmu_id)) + { + MALI_DEBUG_PRINT(1, ("Duplicate MMU ids found. The id %d is already in use\n", resource->mmu_id)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, MALI_MMU_REGISTERS_SIZE, resource->description)) + { + /* specified addresses are already in used by another driver / the kernel */ + MALI_DEBUG_PRINT( + 1, ("Failed to request MMU '%s' register address space at (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1 + )); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mmu = _mali_osk_calloc(1, sizeof(mali_kernel_memory_mmu)); + + if (NULL == mmu) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate memory for handling a MMU unit")); + _mali_osk_mem_unreqregion(resource->base, MALI_MMU_REGISTERS_SIZE); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* basic setup */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->list); + + mmu->id = resource->mmu_id; + mmu->irq_nr = resource->irq; + mmu->flags = resource->flags; + mmu->base = resource->base; + mmu->mapping_size = MALI_MMU_REGISTERS_SIZE; + mmu->description = resource->description; /* no need to copy */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->callbacks); + _MALI_OSK_INIT_LIST_HEAD(&mmu->session_link); + mmu->in_page_fault_handler = 0; + + mmu->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 127-mmu->id); + if (NULL == mmu->lock) + { + MALI_DEBUG_PRINT(1, ("Failed to create mmu lock\n")); + _mali_osk_mem_unreqregion(mmu->base, mmu->mapping_size); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* map the registers */ + mmu->mapped_registers = _mali_osk_mem_mapioregion( mmu->base, mmu->mapping_size, mmu->description ); + if (NULL == mmu->mapped_registers) + { + /* failed to map the registers */ + MALI_DEBUG_PRINT(1, ("Failed to map MMU registers at 0x%08X\n", mmu->base)); + _mali_osk_lock_term(mmu->lock); + _mali_osk_mem_unreqregion(mmu->base, MALI_MMU_REGISTERS_SIZE); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(4, ("MMU '%s' @ (0x%08X - 0x%08X) mapped to 0x%08X\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1, mmu->mapped_registers + )); + + /* setup MMU interrupt mask */ + /* set all values to known defaults */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + /* setup MMU page directory pointer */ + /* The mali_page_directory pointer is guaranteed to be 4kb aligned because we've used get_zeroed_page to accquire it */ + /* convert the kernel virtual address into a physical address and set */ + + /* add to our list of MMU's */ + _mali_osk_list_addtail(&mmu->list, &mmu_head); + + mmu->irq = _mali_osk_irq_init( + mmu->irq_nr, + mali_kernel_memory_mmu_interrupt_handler_upper_half, + mali_kernel_memory_mmu_interrupt_handler_bottom_half, + (_mali_osk_irq_trigger_t)mali_mmu_probe_irq_trigger, + (_mali_osk_irq_ack_t)mali_mmu_probe_irq_acknowledge, + mmu, + "mali_mmu_irq_handlers" + ); + if (NULL == mmu->irq) + { + _mali_osk_list_del(&mmu->list); + _mali_osk_lock_term(mmu->lock); + _mali_osk_mem_unmapioregion( mmu->base, mmu->mapping_size, mmu->mapped_registers ); + _mali_osk_mem_unreqregion(resource->base, MALI_MMU_REGISTERS_SIZE); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* set to a known state */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + + MALI_DEBUG_PRINT(2, ("MMU registered\n")); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource) +{ + mali_io_address mapping; + + MALI_DEBUG_PRINT(5, ("FPGA framework '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + sizeof(u32) * 2 - 1 + )); + + mapping = _mali_osk_mem_mapioregion(resource->base + 0x1000, sizeof(u32) * 2, "fpga framework"); + if (mapping) + { + MALI_DEBUG_CODE(u32 data = ) + _mali_osk_mem_ioread32(mapping, 0); + MALI_DEBUG_PRINT(2, ("FPGA framwork '%s' @ 0x%08X:\n", resource->description, resource->base)); + MALI_DEBUG_PRINT(2, ("\tBitfile date: %d%02d%02d_%02d%02d\n", + (data >> 20), + (data >> 16) & 0xF, + (data >> 11) & 0x1F, + (data >> 6) & 0x1F, + (data >> 0) & 0x3F)); + MALI_DEBUG_CODE(data = ) + _mali_osk_mem_ioread32(mapping, sizeof(u32)); + MALI_DEBUG_PRINT(2, ("\tBitfile SCCS rev: %d\n", data)); + + _mali_osk_mem_unmapioregion(resource->base + 0x1000, sizeof(u32) *2, mapping); + } + else MALI_DEBUG_PRINT(1, ("Failed to access FPGA framwork '%s' @ 0x%08X\n", resource->description, resource->base)); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_os_memory(_mali_osk_resource_t * resource) +{ + mali_physical_memory_allocator * allocator; + mali_physical_memory_allocator ** next_allocator_list; + + u32 alloc_order = resource->alloc_order; + + allocator = mali_os_allocator_create(resource->size, resource->cpu_usage_adjust, resource->description); + if (NULL == allocator) + { + MALI_DEBUG_PRINT(1, ("Failed to create OS memory allocator\n")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + allocator->alloc_order = alloc_order; + + /* link in the allocator: insertion into ordered list + * resources of the same alloc_order will be Last-in-first */ + next_allocator_list = &physical_memory_allocators; + + while ( NULL != *next_allocator_list && + (*next_allocator_list)->alloc_order < alloc_order ) + { + next_allocator_list = &((*next_allocator_list)->next); + } + + allocator->next = (*next_allocator_list); + (*next_allocator_list) = allocator; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(_mali_osk_resource_t * resource) +{ + mali_physical_memory_allocator * allocator; + mali_physical_memory_allocator ** next_allocator_list; + dedicated_memory_info * cleanup_data; + + u32 alloc_order = resource->alloc_order; + + /* do the lowlevel linux operation first */ + + /* Request ownership of the memory */ + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, resource->size, resource->description)) + { + MALI_DEBUG_PRINT(1, ("Failed to request memory region %s (0x%08X - 0x%08X)\n", resource->description, resource->base, resource->base + resource->size - 1)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* create generic block allocator object to handle it */ + allocator = mali_block_allocator_create(resource->base, resource->cpu_usage_adjust, resource->size, resource->description ); + + if (NULL == allocator) + { + MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n")); + _mali_osk_mem_unreqregion(resource->base, resource->size); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* save lowlevel cleanup info */ + allocator->alloc_order = alloc_order; + + cleanup_data = _mali_osk_malloc(sizeof(dedicated_memory_info)); + + if (NULL == cleanup_data) + { + _mali_osk_mem_unreqregion(resource->base, resource->size); + allocator->destroy(allocator); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + cleanup_data->base = resource->base; + cleanup_data->size = resource->size; + + cleanup_data->next = mem_region_registrations; + mem_region_registrations = cleanup_data; + + /* link in the allocator: insertion into ordered list + * resources of the same alloc_order will be Last-in-first */ + next_allocator_list = &physical_memory_allocators; + + while ( NULL != *next_allocator_list && + (*next_allocator_list)->alloc_order < alloc_order ) + { + next_allocator_list = &((*next_allocator_list)->next); + } + + allocator->next = (*next_allocator_list); + (*next_allocator_list) = allocator; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_memory_mmu_interrupt_handler_upper_half(void * data) +{ + mali_kernel_memory_mmu * mmu; + u32 int_stat; + + if (mali_benchmark) MALI_SUCCESS; + + mmu = (mali_kernel_memory_mmu *)data; + + MALI_DEBUG_ASSERT_POINTER(mmu); + + /* check if it was our device which caused the interrupt (we could be sharing the IRQ line) */ + int_stat = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_STATUS); + if (0 == int_stat) + { + MALI_DEBUG_PRINT(5, ("Ignoring shared interrupt\n")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); /* no bits set, we are sharing the IRQ line and someone else caused the interrupt */ + } + + MALI_DEBUG_PRINT(1, ("mali_kernel_memory_mmu_interrupt_handler_upper_half\n")); + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, 0); + + mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS); + + if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) + { + MALI_PRINT(("Page fault on %s\n", mmu->description)); + + _mali_osk_irq_schedulework(mmu->irq); + } + if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) + { + MALI_PRINT(("Bus read error on %s\n", mmu->description)); + /* clear interrupt flag */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR); + /* reenable it */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_MASK) | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + } + + MALI_SUCCESS; +} + + +static void mali_kernel_mmu_bus_reset(mali_kernel_memory_mmu * mmu) +{ + +#if defined(USING_MALI200) + int i; + const int replay_buffer_check_interval = 10; /* must be below 1000 */ + const int replay_buffer_max_number_of_checks = 100; +#endif + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + /* add an extra reference while handling the page fault */ + mmu->usage_count++; + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT(4, ("Sending stop bus request to cores\n")); + /* request to stop the bus, but don't wait for it to actually stop */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES, (u32)mmu); + +#if defined(USING_MALI200) + /* no new request will come from any of the connected cores from now + * we must now flush the playback buffer for any requests queued already + */ + MALI_DEBUG_PRINT(4, ("Switching to the special page fault flush page directory\n")); + /* don't use the mali_mmu_activate_address_space function here as we can't stall the MMU */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_page_fault_flush_page_directory); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + /* resume the MMU */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_PAGE_FAULT_DONE); + /* the MMU will now play back all the requests, all going to our special page fault flush data page */ + + /* just to be safe, check that the playback buffer is empty before continuing */ + if (!mali_benchmark) { + for (i = 0; i < replay_buffer_max_number_of_checks; i++) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY) break; + _mali_osk_time_ubusydelay(replay_buffer_check_interval); + } + + MALI_DEBUG_PRINT_IF(1, i == replay_buffer_max_number_of_checks, ("MMU: %s: Failed to flush replay buffer on page fault\n", mmu->description)); + MALI_DEBUG_PRINT(1, ("Replay playback took %ld usec\n", i * replay_buffer_check_interval)); + } +#endif + /* notify all subsystems that the core should be reset once the bus is actually stopped */ + MALI_DEBUG_PRINT(4,("Sending job abort command to subsystems\n")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS, (u32)mmu); + + /* reprogram the MMU */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); /* no session is active, so just activate the empty page directory */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING); + + /* release the extra address space reference, will schedule */ + mali_memory_core_mmu_release_address_space_reference(mmu); + + /* resume normal operation */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP3_CONTINUE_JOB_HANDLING, (u32)mmu); + MALI_DEBUG_PRINT(4, ("Page fault handling complete\n")); +} + +void mali_kernel_mmu_reset(void * input_mmu) +{ + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(input_mmu); + mmu = (mali_kernel_memory_mmu *)input_mmu; + + MALI_DEBUG_PRINT(4, ("Mali MMU: mali_kernel_mmu_reset: %s\n", mmu->description)); + + if ( 0 != mmu->in_page_fault_handler) + { + /* This is possible if the bus can never be stopped for some reason */ + MALI_PRINT_ERROR(("Stopping the Memory bus not possible. Mali reset could not be performed.")); + return; + } + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); /* no session is active, so just activate the empty page directory */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING); + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + +} + +void mali_kernel_mmu_force_bus_reset(void * input_mmu) +{ + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(input_mmu); + mmu = (mali_kernel_memory_mmu *)input_mmu; + if ( 0 != mmu->in_page_fault_handler) + { + /* This is possible if the bus can never be stopped for some reason */ + MALI_PRINT_ERROR(("Stopping the Memory bus not possible. Mali reset could not be performed.")); + return; + } + MALI_DEBUG_PRINT(1, ("Mali MMU: Force_bus_reset.\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, 0); + mali_kernel_mmu_bus_reset(mmu); +} + + +static void mali_kernel_memory_mmu_interrupt_handler_bottom_half(void * data) +{ + mali_kernel_memory_mmu * mmu; + u32 raw, fault_address, status; + + if (NULL == data) + { + MALI_PRINT_ERROR(("MMU IRQ work queue: NULL argument")); + return; /* Error */ + } + mmu = (mali_kernel_memory_mmu*)data; + + + MALI_DEBUG_PRINT(4, ("Locking subsystems\n")); + /* lock all subsystems */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP0_LOCK_SUBSYSTEM, (u32)mmu); + + raw = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_RAWSTAT); + status = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS); + + if ( (0==(raw & MALI_MMU_INTERRUPT_PAGE_FAULT)) && (0==(status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE)) ) + { + MALI_DEBUG_PRINT(1, ("MMU: Page fault bottom half: No Irq found.\n")); + MALI_DEBUG_PRINT(4, ("Unlocking subsystems")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP4_UNLOCK_SUBSYSTEM, (u32)mmu); + return; + } + + mmu->in_page_fault_handler = 1; + + fault_address = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_PAGE_FAULT_ADDR); + MALI_PRINT(("Page fault detected at 0x%x from bus id %d of type %s on %s\n", + (void*)fault_address, + (status >> 6) & 0x1F, + (status & 32) ? "write" : "read", + mmu->description) + ); + + if (NULL == mmu->active_session) + { + MALI_PRINT(("Spurious memory access detected from MMU %s\n", mmu->description)); + } + else + { + MALI_PRINT(("Active page directory at 0x%08X\n", mmu->active_session->page_directory)); + MALI_PRINT(("Info from page table for VA 0x%x:\n", (void*)fault_address)); + MALI_PRINT(("DTE entry: PTE at 0x%x marked as %s\n", + (void*)(_mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, + MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK), + _mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, + MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT ? "present" : "not present" + )); + + if (_mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT) + { + mali_io_address pte; + u32 data; + pte = mmu->active_session->page_entries_mapped[MALI_MMU_PDE_ENTRY(fault_address)]; + data = _mali_osk_mem_ioread32(pte, MALI_MMU_PTE_ENTRY(fault_address) * sizeof(u32)); + MALI_PRINT(("PTE entry: Page at 0x%x, %s %s %s\n", + (void*)(data & ~MALI_MMU_FLAGS_MASK), + data & MALI_MMU_FLAGS_PRESENT ? "present" : "not present", + data & MALI_MMU_FLAGS_READ_PERMISSION ? "readable" : "", + data & MALI_MMU_FLAGS_WRITE_PERMISSION ? "writable" : "" + )); + } + else + { + MALI_PRINT(("PTE entry: Not present\n")); + } + } + + + mali_kernel_mmu_bus_reset(mmu); + + mmu->in_page_fault_handler = 0; + + /* unlock all subsystems */ + MALI_DEBUG_PRINT(4, ("Unlocking subsystems")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP4_UNLOCK_SUBSYSTEM, (u32)mmu); + +} + + +static u32 mali_mmu_register_read(mali_kernel_memory_mmu * unit, mali_mmu_register reg) +{ + u32 val; + + if (mali_benchmark) return 0; + + val = _mali_osk_mem_ioread32(unit->mapped_registers, (u32)reg * sizeof(u32)); + + MALI_DEBUG_PRINT(6, ("mali_mmu_register_read addr:0x%04X val:0x%08x\n", (u32)reg * sizeof(u32),val)); + + return val; +} + +static void mali_mmu_register_write(mali_kernel_memory_mmu * unit, mali_mmu_register reg, u32 val) +{ + if (mali_benchmark) return; + + MALI_DEBUG_PRINT(6, ("mali_mmu_register_write addr:0x%04X val:0x%08x\n", (u32)reg * sizeof(u32), val)); + + _mali_osk_mem_iowrite32(unit->mapped_registers, (u32)reg * sizeof(u32), val); +} + + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +static mali_physical_memory_allocation_result ump_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + ump_dd_handle ump_mem; + u32 nr_blocks; + u32 i; + ump_dd_physical_block * ump_blocks; + ump_mem_allocation *ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + ret_allocation = _mali_osk_malloc( sizeof( ump_mem_allocation ) ); + if ( NULL==ret_allocation ) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + ump_mem = (ump_dd_handle)ctx; + + MALI_DEBUG_PRINT(4, ("In ump_memory_commit\n")); + + nr_blocks = ump_dd_phys_block_count_get(ump_mem); + + MALI_DEBUG_PRINT(4, ("Have %d blocks\n", nr_blocks)); + + if (nr_blocks == 0) + { + MALI_DEBUG_PRINT(1, ("No block count\n")); + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + ump_blocks = _mali_osk_malloc(sizeof(*ump_blocks)*nr_blocks ); + if ( NULL==ump_blocks ) + { + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + if (UMP_DD_INVALID == ump_dd_phys_blocks_get(ump_mem, ump_blocks, nr_blocks)) + { + _mali_osk_free(ump_blocks); + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + /* Store away the initial offset for unmapping purposes */ + ret_allocation->initial_offset = *offset; + + for(i=0; iinitial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory failed\n")); + + /* unmap all previous blocks (if any) */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_free(ump_blocks); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += ump_blocks[i].size; + } + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + /* Map in an extra virtual guard page at the end of the VMA */ + MALI_DEBUG_PRINT(4, ("Mapping in extra guard page\n")); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, ump_blocks[0].addr , 0, _MALI_OSK_MALI_PAGE_SIZE )) + { + u32 size_allocated = *offset - ret_allocation->initial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory (guard page) failed\n")); + + /* unmap all previous blocks (if any) */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_free(ump_blocks); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += _MALI_OSK_MALI_PAGE_SIZE; + } + + _mali_osk_free( ump_blocks ); + + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + ret_allocation->ump_mem = ump_mem; + ret_allocation->size_allocated = *offset - ret_allocation->initial_offset; + + alloc_info->ctx = NULL; + alloc_info->handle = ret_allocation; + alloc_info->next = NULL; + alloc_info->release = ump_memory_release; + + return MALI_MEM_ALLOC_FINISHED; +} + +static void ump_memory_release(void * ctx, void * handle) +{ + ump_dd_handle ump_mem; + ump_mem_allocation *allocation; + + allocation = (ump_mem_allocation *)handle; + + MALI_DEBUG_ASSERT_POINTER( allocation ); + + ump_mem = allocation->ump_mem; + + MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID!=ump_mem); + + /* At present, this is a no-op. But, it allows the mali_address_manager to + * do unmapping of a subrange in future. */ + mali_allocation_engine_unmap_physical( allocation->engine, + allocation->descriptor, + allocation->initial_offset, + allocation->size_allocated, + (_mali_osk_mem_mapregion_flags_t)0 + ); + _mali_osk_free( allocation ); + + + ump_dd_reference_release(ump_mem) ; + return; +} + +_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ) +{ + ump_dd_handle ump_mem; + mali_physical_memory_allocator external_memory_allocator; + memory_session * session_data; + mali_memory_allocation * descriptor; + int md; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + /* check arguments */ + /* NULL might be a valid Mali address */ + if ( ! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if ( args->size % _MALI_OSK_MALI_PAGE_SIZE ) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map ump memory with secure id %d into virtual memory 0x%08X, size 0x%08X\n", + args->secure_id, args->mali_address, args->size)); + + ump_mem = ump_dd_handle_create_from_secure_id( (int)args->secure_id ) ; + + if ( UMP_DD_HANDLE_INVALID==ump_mem ) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + descriptor = _mali_osk_calloc(1, sizeof(mali_memory_allocation)); + if (NULL == descriptor) + { + ump_dd_reference_release(ump_mem); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + descriptor->size = args->size; + descriptor->mapping = NULL; + descriptor->mali_address = args->mali_address; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = NULL; /* do not map to process address space */ + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) + { + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE; + } + _mali_osk_list_init( &descriptor->list ); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, descriptor, &md)) + { + ump_dd_reference_release(ump_mem); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + external_memory_allocator.allocate = ump_memory_commit; + external_memory_allocator.allocate_page_table_block = NULL; + external_memory_allocator.ctx = ump_mem; + external_memory_allocator.name = "UMP Memory"; + external_memory_allocator.next = NULL; + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_memory(memory_engine, descriptor, &external_memory_allocator, NULL)) + { + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + ump_dd_reference_release(ump_mem); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + args->cookie = md; + + MALI_DEBUG_PRINT(5,("Returning from UMP attach\n")); + + /* All OK */ + MALI_SUCCESS; +} + + +_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ) +{ + mali_memory_allocation * descriptor; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session_data->descriptor_mapping, args->cookie, (void**)&descriptor)) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release ump memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mali_descriptor_mapping_free(session_data->descriptor_mapping, args->cookie); + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); + + MALI_SUCCESS; + +} +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 */ + + +static mali_physical_memory_allocation_result external_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + u32 * data; + external_mem_allocation * ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + ret_allocation = _mali_osk_malloc( sizeof(external_mem_allocation) ); + + if ( NULL == ret_allocation ) + { + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + data = (u32*)ctx; + + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + ret_allocation->initial_offset = *offset; + + alloc_info->ctx = NULL; + alloc_info->handle = ret_allocation; + alloc_info->next = NULL; + alloc_info->release = external_memory_release; + + MALI_DEBUG_PRINT(3, ("External map: mapping phys 0x%08X at mali virtual address 0x%08X staring at offset 0x%08X length 0x%08X\n", data[0], descriptor->mali_address, *offset, data[1])); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, data[0], 0, data[1])) + { + MALI_DEBUG_PRINT(1, ("Mapping of external memory failed\n")); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += data[1]; + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + /* Map in an extra virtual guard page at the end of the VMA */ + MALI_DEBUG_PRINT(4, ("Mapping in extra guard page\n")); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, data[0], 0, _MALI_OSK_MALI_PAGE_SIZE)) + { + u32 size_allocated = *offset - ret_allocation->initial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory (guard page) failed\n")); + + /* unmap what we previously mapped */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += _MALI_OSK_MALI_PAGE_SIZE; + } + + ret_allocation->size = *offset - ret_allocation->initial_offset; + + return MALI_MEM_ALLOC_FINISHED; +} + +static void external_memory_release(void * ctx, void * handle) +{ + external_mem_allocation * allocation; + + allocation = (external_mem_allocation *) handle; + MALI_DEBUG_ASSERT_POINTER( allocation ); + + /* At present, this is a no-op. But, it allows the mali_address_manager to + * do unmapping of a subrange in future. */ + + mali_allocation_engine_unmap_physical( allocation->engine, + allocation->descriptor, + allocation->initial_offset, + allocation->size, + (_mali_osk_mem_mapregion_flags_t)0 + ); + + _mali_osk_free( allocation ); + + return; +} + +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ) +{ + mali_physical_memory_allocator external_memory_allocator; + memory_session * session_data; + u32 info[2]; + mali_memory_allocation * descriptor; + int md; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + external_memory_allocator.allocate = external_memory_commit; + external_memory_allocator.allocate_page_table_block = NULL; + external_memory_allocator.ctx = &info[0]; + external_memory_allocator.name = "External Memory"; + external_memory_allocator.next = NULL; + + /* check arguments */ + /* NULL might be a valid Mali address */ + if ( ! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if ( args->size % _MALI_OSK_MALI_PAGE_SIZE ) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map physical memory 0x%x-0x%x into virtual memory 0x%x\n", + (void*)args->phys_addr, + (void*)(args->phys_addr + args->size -1), + (void*)args->mali_address) + ); + + /* Validate the mali physical range */ + MALI_CHECK_NO_ERROR( mali_kernel_core_validate_mali_phys_range( args->phys_addr, args->size ) ); + + info[0] = args->phys_addr; + info[1] = args->size; + + descriptor = _mali_osk_calloc(1, sizeof(mali_memory_allocation)); + if (NULL == descriptor) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + descriptor->size = args->size; + descriptor->mapping = NULL; + descriptor->mali_address = args->mali_address; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = NULL; /* do not map to process address space */ + descriptor->lock = NULL; + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) + { + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE; + } + _mali_osk_list_init( &descriptor->list ); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, descriptor, &md)) + { + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_memory(memory_engine, descriptor, &external_memory_allocator, NULL)) + { + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + args->cookie = md; + + MALI_DEBUG_PRINT(5,("Returning from range_map_external_memory\n")); + + /* All OK */ + MALI_SUCCESS; +} + + +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ) +{ + mali_memory_allocation * descriptor; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session_data->descriptor_mapping, args->cookie, (void**)&descriptor)) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to unmap external memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mali_descriptor_mapping_free(session_data->descriptor_mapping, args->cookie); + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + args->memory_size = 2 * 1024 * 1024 * 1024UL; /* 2GB address space */ + args->mali_address_base = 1 * 1024 * 1024 * 1024UL; /* staring at 1GB, causing this layout: (0-1GB unused)(1GB-3G usage by Mali)(3G-4G unused) */ + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_mmu_page_table_cache_create(void) +{ + page_table_cache.lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 110); + MALI_CHECK_NON_NULL( page_table_cache.lock, _MALI_OSK_ERR_FAULT ); + _MALI_OSK_INIT_LIST_HEAD(&page_table_cache.partial); + _MALI_OSK_INIT_LIST_HEAD(&page_table_cache.full); + MALI_SUCCESS; +} + +void mali_mmu_page_table_cache_destroy(void) +{ + mali_mmu_page_table_allocation * alloc, *temp; + + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp, &page_table_cache.partial, mali_mmu_page_table_allocation, list) + { + MALI_DEBUG_PRINT_IF(1, 0 != alloc->usage_count, ("Destroying page table cache while pages are tagged as in use. %d allocations still marked as in use.\n", alloc->usage_count)); + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + + MALI_DEBUG_PRINT_IF(1, 0 == _mali_osk_list_empty(&page_table_cache.full), ("Page table cache full list contains one or more elements \n")); + + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp, &page_table_cache.full, mali_mmu_page_table_allocation, list) + { + MALI_DEBUG_PRINT(1, ("Destroy alloc 0x%08X with usage count %d\n", (u32)alloc, alloc->usage_count)); + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } +} + +_mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping) +{ + _mali_osk_lock_wait(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + if (0 == _mali_osk_list_empty(&page_table_cache.partial)) + { + mali_mmu_page_table_allocation * alloc = _MALI_OSK_LIST_ENTRY(page_table_cache.partial.next, mali_mmu_page_table_allocation, list); + int page_number = _mali_osk_find_first_zero_bit(alloc->usage_map, alloc->num_pages); + MALI_DEBUG_PRINT(6, ("Partial page table allocation found, using page offset %d\n", page_number)); + _mali_osk_set_nonatomic_bit(page_number, alloc->usage_map); + alloc->usage_count++; + if (alloc->num_pages == alloc->usage_count) + { + /* full, move alloc to full list*/ + _mali_osk_list_move(&alloc->list, &page_table_cache.full); + } + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + *table_page = (MALI_MMU_PAGE_SIZE * page_number) + alloc->pages.phys_base; + *mapping = (mali_io_address)((MALI_MMU_PAGE_SIZE * page_number) + (u32)alloc->pages.mapping); + MALI_DEBUG_PRINT(4, ("Page table allocated for VA=0x%08X, MaliPA=0x%08X\n", *mapping, *table_page )); + MALI_SUCCESS; + } + else + { + mali_mmu_page_table_allocation * alloc; + /* no free pages, allocate a new one */ + + alloc = (mali_mmu_page_table_allocation *)_mali_osk_calloc(1, sizeof(mali_mmu_page_table_allocation)); + if (NULL == alloc) + { + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _MALI_OSK_INIT_LIST_HEAD(&alloc->list); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_page_tables(memory_engine, &alloc->pages, physical_memory_allocators)) + { + MALI_DEBUG_PRINT(1, ("No more memory for page tables\n")); + _mali_osk_free(alloc); + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + *mapping = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* create the usage map */ + alloc->num_pages = alloc->pages.size / MALI_MMU_PAGE_SIZE; + alloc->usage_count = 1; + MALI_DEBUG_PRINT(3, ("New page table cache expansion, %d pages in new cache allocation\n", alloc->num_pages)); + alloc->usage_map = _mali_osk_calloc(1, ((alloc->num_pages + BITS_PER_LONG - 1) & ~(BITS_PER_LONG-1) / BITS_PER_LONG) * sizeof(unsigned long)); + if (NULL == alloc->usage_map) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate memory to describe MMU page table cache usage\n")); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc); + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + *mapping = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* clear memory allocation */ + fill_page(alloc->pages.mapping, 0); + + _mali_osk_set_nonatomic_bit(0, alloc->usage_map); + + _mali_osk_list_add(&alloc->list, &page_table_cache.partial); + + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = alloc->pages.phys_base; /* return the first page */ + *mapping = alloc->pages.mapping; /* Mapping for first page */ + MALI_DEBUG_PRINT(4, ("Page table allocated for VA=0x%08X, MaliPA=0x%08X\n", *mapping, *table_page )); + MALI_SUCCESS; + } +} + +void mali_mmu_release_table_page(u32 pa) +{ + mali_mmu_page_table_allocation * alloc, * temp_alloc; + + MALI_DEBUG_PRINT_IF(1, pa & 4095, ("Bad page address 0x%x given to mali_mmu_release_table_page\n", (void*)pa)); + + MALI_DEBUG_PRINT(4, ("Releasing table page 0x%08X to the cache\n", pa)); + + _mali_osk_lock_wait(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + /* find the entry this address belongs to */ + /* first check the partial list */ + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp_alloc, &page_table_cache.partial, mali_mmu_page_table_allocation, list) + { + u32 start = alloc->pages.phys_base; + u32 last = start + (alloc->num_pages - 1) * MALI_MMU_PAGE_SIZE; + if (pa >= start && pa <= last) + { + MALI_DEBUG_ASSERT(0 != _mali_osk_test_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map)); + _mali_osk_clear_nonatomic_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map); + alloc->usage_count--; + + _mali_osk_memset((void*)( ((u32)alloc->pages.mapping) + (pa - start) ), 0, MALI_MMU_PAGE_SIZE); + + if (0 == alloc->usage_count) + { + /* empty, release whole page alloc */ + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("(partial list)Released table page 0x%08X to the cache\n", pa)); + return; + } + } + + /* the check the full list */ + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp_alloc, &page_table_cache.full, mali_mmu_page_table_allocation, list) + { + u32 start = alloc->pages.phys_base; + u32 last = start + (alloc->num_pages - 1) * MALI_MMU_PAGE_SIZE; + if (pa >= start && pa <= last) + { + _mali_osk_clear_nonatomic_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map); + alloc->usage_count--; + + _mali_osk_memset((void*)( ((u32)alloc->pages.mapping) + (pa - start) ), 0, MALI_MMU_PAGE_SIZE); + + /* transfer to partial list */ + _mali_osk_list_move(&alloc->list, &page_table_cache.partial); + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("(full list)Released table page 0x%08X to the cache\n", pa)); + return; + } + } + + MALI_DEBUG_PRINT(1, ("pa 0x%x not found in the page table cache\n", (void*)pa)); + + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); +} + +void* mali_memory_core_mmu_lookup(u32 id) +{ + mali_kernel_memory_mmu * mmu, * temp_mmu; + + /* find an MMU with a matching id */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + if (id == mmu->id) return mmu; + } + + /* not found */ + return NULL; +} + +void mali_mmu_activate_address_space(mali_kernel_memory_mmu * mmu, u32 page_directory) +{ + const int delay_in_usecs = 10; + const int max_loop_count = 10; + int i; + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL); + + if (!mali_benchmark) { + for (i = 0; i < max_loop_count; ++i) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_STALL_ACTIVE) break; + _mali_osk_time_ubusydelay(delay_in_usecs); + } + MALI_DEBUG_PRINT_IF(1, (max_loop_count == i), ("Stall request failed, swapping anyway\n")); + } + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, page_directory); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL); +} + +_mali_osk_errcode_t mali_memory_core_mmu_activate_page_table(void* mmu_ptr, struct mali_session_data * mali_session_data, void(*callback)(void*), void * callback_argument) +{ + memory_session * requested_memory_session; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + mali_kernel_memory_mmu * mmu; + + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + MALI_DEBUG_ASSERT_POINTER(mali_session_data); + + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + MALI_DEBUG_PRINT(4, ("Asked to activate page table for session 0x%x on MMU %s\n", mali_session_data, mmu->description)); + requested_memory_session = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + MALI_DEBUG_PRINT(5, ("Session 0x%x looked up as using memory session 0x%x\n", mali_session_data, requested_memory_session)); + + MALI_DEBUG_ASSERT_POINTER(requested_memory_session); + + MALI_DEBUG_PRINT(7, ("Taking locks\n")); + + _mali_osk_lock_wait(requested_memory_session->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + if (0 == mmu->usage_count) + { + /* no session currently active, activate the requested session */ + MALI_DEBUG_ASSERT(NULL == mmu->active_session); + mmu->active_session = requested_memory_session; + mmu->usage_count = 1; + MALI_DEBUG_PRINT(4, ("MMU idle, activating page directory 0x%08X on MMU %s\n", requested_memory_session->page_directory, mmu->description)); + mali_mmu_activate_address_space(mmu, requested_memory_session->page_directory); + { + /* Insert mmu into the right place in the active_mmus list so that + * it is still sorted. The list must be sorted by ID so we can get + * the mutexes in the right order in + * _mali_ukk_mem_munmap_internal(). + */ + _mali_osk_list_t *entry; + for (entry = requested_memory_session->active_mmus.next; + entry != &requested_memory_session->active_mmus; + entry = entry->next) + { + mali_kernel_memory_mmu *temp = _MALI_OSK_LIST_ENTRY(entry, mali_kernel_memory_mmu, session_link); + if (mmu->id < temp->id) + break; + } + /* If we broke out, then 'entry' points to the list node of the + * first mmu with a greater ID; otherwise, it points to + * active_mmus. We want to add *before* this node. + */ + _mali_osk_list_addtail(&mmu->session_link, entry); + } + err = _MALI_OSK_ERR_OK; + } + + /* Allow two cores to run in parallel if they come from the same session */ + else if ( + (mmu->in_page_fault_handler == 0) && + (requested_memory_session == mmu->active_session ) && + (0==(MALI_MMU_DISALLOW_PARALLELL_WORK_OF_MALI_CORES & mmu->flags)) + ) + { + /* nested activation detected, just update the reference count */ + MALI_DEBUG_PRINT(4, ("Nested activation detected, %d previous activations found\n", mmu->usage_count)); + mmu->usage_count++; + err = _MALI_OSK_ERR_OK; + } + + else if (NULL != callback) + { + /* can't activate right now, notify caller on idle via callback */ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp_callback_object; + int found = 0; + + MALI_DEBUG_PRINT(3, ("The MMU is busy and is using a different address space, callback given\n")); + /* check for existing registration */ + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp_callback_object, &mmu->callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + if (callback_object->callback == callback) + { + found = 1; + break; + } + } + + if (found) + { + MALI_DEBUG_PRINT(5, ("Duplicate callback registration found, ignoring\n")); + /* callback already registered */ + err = _MALI_OSK_ERR_BUSY; + } + else + { + MALI_DEBUG_PRINT(5,("New callback, registering\n")); + /* register the new callback */ + callback_object = _mali_osk_malloc(sizeof(mali_kernel_memory_mmu_idle_callback)); + if (NULL != callback_object) + { + MALI_DEBUG_PRINT(7,("Callback struct setup\n")); + callback_object->callback = callback; + callback_object->callback_argument = callback_argument; + _mali_osk_list_addtail(&callback_object->link, &mmu->callbacks); + err = _MALI_OSK_ERR_BUSY; + } + } + } + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_signal(requested_memory_session->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_ERROR(err); +} + +void mali_memory_core_mmu_release_address_space_reference(void* mmu_ptr) +{ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp; + mali_kernel_memory_mmu * mmu; + memory_session * session; + + _MALI_OSK_LIST_HEAD(callbacks); + + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + session = mmu->active_session; + + /* support that we handle spurious page faults */ + if (NULL != session) + { + _mali_osk_lock_wait(session->lock, _MALI_OSK_LOCKMODE_RW); + } + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("Deactivation of address space on MMU %s, %d references exists\n", mmu->description, mmu->usage_count)); + MALI_DEBUG_ASSERT(0 != mmu->usage_count); + mmu->usage_count--; + if (0 != mmu->usage_count) + { + MALI_DEBUG_PRINT(4, ("MMU still in use by this address space, %d references still exists\n", mmu->usage_count)); + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + /* support that we handle spurious page faults */ + if (NULL != session) + { + _mali_osk_lock_signal(session->lock, _MALI_OSK_LOCKMODE_RW); + } + return; + } + + MALI_DEBUG_PRINT(4, ("Activating the empty page directory on %s\n", mmu->description)); + + /* last reference gone, deactivate current address space */ + mali_mmu_activate_address_space(mmu, mali_empty_page_directory); + + /* unlink from session */ + _mali_osk_list_delinit(&mmu->session_link); + /* remove the active session pointer */ + mmu->active_session = NULL; + + /* Notify all registered callbacks. + * We have to be clever here: + * We must call the callbacks with the spinlock unlocked and + * the callback list emptied to allow them to re-register. + * So we make a copy of the list, clears the list and then later call the callbacks on the local copy + */ + /* copy list */ + _MALI_OSK_INIT_LIST_HEAD(&callbacks); + _mali_osk_list_splice(&mmu->callbacks, &callbacks); + /* clear the original, allowing new registrations during the callback */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->callbacks); + + /* end of mmu manipulation, so safe to unlock */ + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + /* then finally remove the (possible) session lock, supporting that no session was active (spurious page fault handling) */ + if (NULL != session) + { + _mali_osk_lock_signal(session->lock, _MALI_OSK_LOCKMODE_RW); + } + + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp, &callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + MALI_DEBUG_ASSERT_POINTER(callback_object->callback); + (callback_object->callback)(callback_object->callback_argument); + _mali_osk_list_del(&callback_object->link); + _mali_osk_free(callback_object); + } +} + +void mali_memory_core_mmu_unregister_callback(void* mmu_ptr, void(*callback)(void*)) +{ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp_callback_object; + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + + MALI_DEBUG_ASSERT_POINTER(callback); + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp_callback_object, &mmu->callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + MALI_DEBUG_ASSERT_POINTER(callback_object->callback); + if (callback_object->callback == callback) + { + _mali_osk_list_del(&callback_object->link); + _mali_osk_free(callback_object); + break; + } + } + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); +} + +static _mali_osk_errcode_t mali_address_manager_allocate(mali_memory_allocation * descriptor) +{ + /* allocate page tables, if needed */ + int i; + const int first_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address); + int last_pde_idx; + memory_session * session_data; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; + int page_dir_updated = 0; +#endif + + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + last_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address + _MALI_OSK_MALI_PAGE_SIZE + descriptor->size - 1); + } + else + { + last_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address + descriptor->size - 1); + } + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + MALI_DEBUG_PRINT(4, ("allocating page tables for Mali virtual address space 0x%08X to 0x%08X\n", descriptor->mali_address, descriptor->mali_address + descriptor->size - 1)); + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * We have active MMUs, so we are probably in the process of alocating more memory for a suspended GP job (PLBU heap) + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page directory + * from the L2 cache if we add new page directory entries (PDEs) to the page directory. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + if ( 0 == (_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT) ) + { + u32 pte_phys; + mali_io_address pte_mapped; + _mali_osk_errcode_t err; + + /* allocate a new page table */ + MALI_DEBUG_ASSERT(0 == session_data->page_entries_usage_count[i]); + MALI_DEBUG_ASSERT(NULL == session_data->page_entries_mapped[i]); + + err = mali_mmu_get_table_page(&pte_phys, &pte_mapped); + if (_MALI_OSK_ERR_OK == err) + { + session_data->page_entries_mapped[i] = pte_mapped; + MALI_DEBUG_ASSERT_POINTER( session_data->page_entries_mapped[i] ); + + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), pte_phys | MALI_MMU_FLAGS_PRESENT); /* mark page table as present */ + + /* update usage count */ + session_data->page_entries_usage_count[i]++; +#if defined USING_MALI400_L2_CACHE + page_dir_updated = 1; +#endif + continue; /* continue loop */ + } + + MALI_DEBUG_PRINT(1, ("Page table alloc failed\n")); + break; /* abort loop, failed to allocate one or more page tables */ + } + else + { + session_data->page_entries_usage_count[i]++; + } + } + + if (i <= last_pde_idx) + { + /* one or more pages could not be allocated, release reference count for the ones we added one for */ + /* adjust for the one which caused the for loop to be aborted */ + i--; + + while (i >= first_pde_idx) + { + MALI_DEBUG_ASSERT(0 != session_data->page_entries_usage_count[i]); + session_data->page_entries_usage_count[i]--; + if (0 == session_data->page_entries_usage_count[i]) + { + /* last reference removed */ + mali_mmu_release_table_page(MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)))); + session_data->page_entries_mapped[i] = NULL; + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); /* mark as not present in the page directory */ + } + i--; + } + + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus && 1 == page_dir_updated) + { + /* + * We have updated the page directory and have an active MMU using it, so invalidate it in the Mali L2 cache. + */ + mali_kernel_l2_cache_invalidate_page(session_data->page_directory); + } +#endif + + /* all OK */ + MALI_SUCCESS; +} + +static void mali_address_manager_release(mali_memory_allocation * descriptor) +{ + int first_pde_idx; + int last_pde_idx; + memory_session * session_data; + u32 mali_address; + u32 mali_address_end; + u32 left; + int i; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; + int page_dir_updated = 0; +#endif + + MALI_DEBUG_ASSERT_POINTER(descriptor); + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + MALI_DEBUG_ASSERT_POINTER(session_data->page_directory_mapped); + + mali_address = descriptor->mali_address; + mali_address_end = descriptor->mali_address + descriptor->size; + left = descriptor->size; + + first_pde_idx = MALI_MMU_PDE_ENTRY(mali_address); + last_pde_idx = MALI_MMU_PDE_ENTRY(mali_address_end - 1); + + MALI_DEBUG_PRINT(3, ("Zapping Mali MMU table for address 0x%08X size 0x%08X\n", mali_address, left)); + MALI_DEBUG_PRINT(4, ("Zapping PDE %d through %d\n", first_pde_idx, last_pde_idx)); + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page tables + * from the L2 cache to ensure that the memory is unmapped. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + const int size_inside_pte = left < 0x400000 ? left : 0x400000; + + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped[i]); + MALI_DEBUG_ASSERT(0 != session_data->page_entries_usage_count[i]); + MALI_DEBUG_PRINT(4, ("PDE %d\n", i)); + + session_data->page_entries_usage_count[i]--; + + if (0 == session_data->page_entries_usage_count[i]) + { + MALI_DEBUG_PRINT(4, ("Releasing page table as this is the last reference\n")); + /* last reference removed, no need to zero out each PTE */ + mali_mmu_release_table_page(MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)))); + session_data->page_entries_mapped[i] = NULL; + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); /* mark as not present in the page directory */ +#if defined USING_MALI400_L2_CACHE + page_dir_updated = 1; +#endif + } + else + { + int j; + const int first_pte_idx = MALI_MMU_PTE_ENTRY(mali_address); + const int last_pte_idx = MALI_MMU_PTE_ENTRY(mali_address + size_inside_pte - 1); + + MALI_DEBUG_PRINT(4, ("Partial page table fill detected, zapping entries %d through %d (page table at 0x%08X)\n", first_pte_idx, last_pte_idx, MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32))))); + + for (j = first_pte_idx; j <= last_pte_idx; j++) + { + _mali_osk_mem_iowrite32(session_data->page_entries_mapped[i], j * sizeof(u32), 0); + } + + MALI_DEBUG_PRINT(5, ("zap complete\n")); + + mali_address += size_inside_pte; + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus) + { + /* Invalidate the page we've just modified */ + mali_kernel_l2_cache_invalidate_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + } +#endif + } + left -= size_inside_pte; + } + +#if defined USING_MALI400_L2_CACHE + if ((1 == page_dir_updated) && (1== has_active_mmus)) + { + /* The page directory was also updated */ + mali_kernel_l2_cache_invalidate_page(session_data->page_directory); + } +#endif +} + +static _mali_osk_errcode_t mali_address_manager_map(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size) +{ + memory_session * session_data; + u32 mali_address; + u32 mali_address_end; + u32 current_phys_addr; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; +#endif + + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_ASSERT_POINTER( phys_addr ); + + current_phys_addr = *phys_addr; + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + mali_address = descriptor->mali_address + offset; + mali_address_end = descriptor->mali_address + offset + size; + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * We have active MMUs, so we are probably in the process of alocating more memory for a suspended GP job (PLBU heap) + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page tables + * from the L2 cache when we have allocated more heap memory. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + MALI_DEBUG_PRINT(6, ("Mali map: mapping 0x%08X to Mali address 0x%08X length 0x%08X\n", current_phys_addr, mali_address, size)); + + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped); + + for ( ; mali_address < mali_address_end; mali_address += MALI_MMU_PAGE_SIZE, current_phys_addr += MALI_MMU_PAGE_SIZE) + { + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)]); + _mali_osk_mem_iowrite32(session_data->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)], MALI_MMU_PTE_ENTRY(mali_address) * sizeof(u32), current_phys_addr | MALI_MMU_FLAGS_WRITE_PERMISSION | MALI_MMU_FLAGS_READ_PERMISSION | MALI_MMU_FLAGS_PRESENT); + } + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus) + { + int i; + const int first_pde_idx = MALI_MMU_PDE_ENTRY(mali_address); + const int last_pde_idx = MALI_MMU_PDE_ENTRY(mali_address_end - 1); + + /* + * Invalidate the updated page table(s), incase they have been used for something + * else since last job start (invalidation of entire Mali L2 cache) + */ + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + mali_kernel_l2_cache_invalidate_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + } + } +#endif + + MALI_SUCCESS; +} + +/* This handler registered to mali_mmap for MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ) +{ + struct mali_session_data * mali_session_data; + mali_memory_allocation * descriptor; + memory_session * session_data; + + /* validate input */ + if (NULL == args) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: args was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + /* Unpack arguments */ + mali_session_data = (struct mali_session_data *)args->ctx; + + if (NULL == mali_session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: mali_session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + MALI_DEBUG_ASSERT( mali_subsystem_memory_id >= 0 ); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + /* validate input */ + if (NULL == session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_FAULT); } + + descriptor = (mali_memory_allocation*) _mali_osk_calloc( 1, sizeof(mali_memory_allocation) ); + if (NULL == descriptor) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_NOMEM); } + + descriptor->size = args->size; + descriptor->mali_address = args->phys_addr; + descriptor->mali_addr_mapping_info = (void*)session_data; + + descriptor->process_addr_mapping_info = args->ukk_private; /* save to be used during physical manager callback */ + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE; + descriptor->lock = session_data->lock; + _mali_osk_list_init( &descriptor->list ); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (0 == mali_allocation_engine_allocate_memory(memory_engine, descriptor, physical_memory_allocators, &session_data->memory_head)) + { + mali_kernel_memory_mmu * mmu, * temp_mmu; + + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + /* no need to lock the MMU as we own it already */ + MALI_DEBUG_PRINT(5, ("Zapping the cache of mmu %s as it's using the page table we have updated\n", mmu->description)); + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL); + if (!mali_benchmark) { + while ( (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_STALL_ACTIVE) == 0) _mali_osk_time_ubusydelay(1); + } + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL); + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + /* All ok, write out any information generated from this call */ + args->mapping = descriptor->mapping; + args->cookie = (u32)descriptor; + + MALI_DEBUG_PRINT(7, ("MMAP OK\n")); + /* All done */ + MALI_SUCCESS; + } + else + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + /* OOM, but not a fatal error */ + MALI_DEBUG_PRINT(4, ("Memory allocation failure, OOM\n")); + _mali_osk_free(descriptor); + /* Linux will free the CPU address allocation, userspace client the Mali address allocation */ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +} + +static void _mali_ukk_mem_munmap_internal( _mali_uk_mem_munmap_s *args ) +{ + memory_session * session_data; + mali_kernel_memory_mmu * mmu, * temp_mmu; + mali_memory_allocation * descriptor; + + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /** @note args->context unused; we use the memory_session from the cookie */ + /* args->mapping and args->size are also discarded. They are only necessary + for certain do_munmap implementations. However, they could be used to check the + descriptor at this point. */ + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + /* Stall the MMU(s) which is using the address space we're operating on. + * Note that active_mmus must be sorted in order of ID to avoid a mutex + * ordering violation. + */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + const int max_loop_count = 100; + const int sleep_duration = 1; /* must be below 1000 */ + int i; + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL); + + if (!mali_benchmark) + { + for ( i = 0; i < max_loop_count; i++) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_STALL_ACTIVE) break; + _mali_osk_time_ubusydelay(sleep_duration); + } + + MALI_DEBUG_PRINT_IF(3, max_loop_count == i, ("Stall failed, trying zap anyway\n")); + } + } + + /* This function also removes the memory from the session's memory list */ + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); + + /* any L2 maintenance was done during mali_allocation_engine_release_memory */ + /* the session is locked, so the active mmu list should be the same */ + /* zap the TLB and resume operation */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL); + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } +} + +/* Handler for unmapping memory for MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ) +{ + mali_memory_allocation * descriptor; + _mali_osk_lock_t *descriptor_lock; + + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /** @note args->context unused; we use the memory_session from the cookie */ + /* args->mapping and args->size are also discarded. They are only necessary + for certain do_munmap implementations. However, they could be used to check the + descriptor at this point. */ + + MALI_DEBUG_ASSERT_POINTER((memory_session*)descriptor->mali_addr_mapping_info); + + descriptor_lock = descriptor->lock; /* should point to the session data lock... */ + + if (descriptor_lock) + { + _mali_osk_lock_wait( descriptor_lock, _MALI_OSK_LOCKMODE_RW ); + } + /* Noninterruptable spinlock type, so must always have locked. Checking should've been done in OSK function. */ + + _mali_ukk_mem_munmap_internal( args ); + /* descriptor is no longer valid - it may've been freed */ + + if (descriptor_lock) + { + _mali_osk_lock_signal( descriptor_lock, _MALI_OSK_LOCKMODE_RW ); + } + return _MALI_OSK_ERR_OK; +} + +/* Is called when the rendercore wants the mmu to give an interrupt */ +static void mali_mmu_probe_irq_trigger(mali_kernel_memory_mmu * mmu) +{ + MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_trigger\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_RAWSTAT, MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR); +} + +/* Is called when the irq probe wants the mmu to acknowledge an interrupt from the hw */ +static _mali_osk_errcode_t mali_mmu_probe_irq_acknowledge(mali_kernel_memory_mmu * mmu) +{ + u32 int_stat; + + int_stat = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_STATUS); + + MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_acknowledge: intstat 0x%x\n", int_stat)); + if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) + { + MALI_DEBUG_PRINT(2, ("Probe: Page fault detect: PASSED\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT); + } + else MALI_DEBUG_PRINT(1, ("Probe: Page fault detect: FAILED\n")); + + if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) + { + MALI_DEBUG_PRINT(2, ("Probe: Bus read error detect: PASSED\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR); + } + else MALI_DEBUG_PRINT(1, ("Probe: Bus read error detect: FAILED\n")); + + if ( (int_stat & (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) == + (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) + { + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +struct dump_info +{ + u32 buffer_left; + u32 register_writes_size; + u32 page_table_dump_size; + u32 *buffer; +}; + +static _mali_osk_errcode_t writereg(u32 where, u32 what, const char * comment, struct dump_info * info, int dump_to_serial) +{ + if (dump_to_serial) MALI_DEBUG_PRINT(1, ("writereg %08X %08X # %s\n", where, what, comment)); + + if (NULL != info) + { + info->register_writes_size += sizeof(u32)*2; /* two 32-bit words */ + + if (NULL != info->buffer) + { + /* check that we have enough space */ + if (info->buffer_left < sizeof(u32)*2) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = where; + info->buffer++; + + *info->buffer = what; + info->buffer++; + + info->buffer_left -= sizeof(u32)*2; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_page(mali_io_address page, u32 phys_addr, struct dump_info * info, int dump_to_serial) +{ + if (dump_to_serial) + { + int i; + for (i = 0; i < 256; i++) + { + MALI_DEBUG_PRINT(1, ("%08X: %08X %08X %08X %08X\n", phys_addr + 16*i, _mali_osk_mem_ioread32(page, (i*4 + 0) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 1) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 2) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 3) * sizeof(u32)))); + + } + } + + if (NULL != info) + { + /* 4096 for the page and 4 bytes for the address */ + const u32 page_size_in_elements = MALI_MMU_PAGE_SIZE / 4; + const u32 page_size_in_bytes = MALI_MMU_PAGE_SIZE; + const u32 dump_size_in_bytes = MALI_MMU_PAGE_SIZE + 4; + + info->page_table_dump_size += dump_size_in_bytes; + + if (NULL != info->buffer) + { + if (info->buffer_left < dump_size_in_bytes) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = phys_addr; + info->buffer++; + + _mali_osk_memcpy(info->buffer, page, page_size_in_bytes); + info->buffer += page_size_in_elements; + + info->buffer_left -= dump_size_in_bytes; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_page_table(memory_session * session_data, struct dump_info * info) +{ + MALI_DEBUG_ASSERT_POINTER(session_data); + MALI_DEBUG_ASSERT_POINTER(info); + + if (NULL != session_data->page_directory_mapped) + { + int i; + + MALI_CHECK_NO_ERROR( + dump_page(session_data->page_directory_mapped, session_data->page_directory, info, 0) + ); + + for (i = 0; i < 1024; i++) + { + if (NULL != session_data->page_entries_mapped[i]) + { + MALI_CHECK_NO_ERROR( + dump_page(session_data->page_entries_mapped[i], _mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK, info, 0) + ); + } + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_registers(memory_session * session_data, struct dump_info * info) +{ + MALI_CHECK_NO_ERROR(writereg(0x00000000, session_data->page_directory, "set the page directory address", info, 0)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 4, "zap???", info, 0)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 0, "enable paging", info, 0)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data, &info)); + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data, &info)); + args->size = info.register_writes_size + info.page_table_dump_size; + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_CHECK_NON_NULL(args->buffer, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + info.buffer_left = args->size; + info.buffer = args->buffer; + + args->register_writes = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data, &info)); + + args->page_table_dump = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data, &info)); + + args->register_writes_size = info.register_writes_size; + args->page_table_dump_size = info.page_table_dump_size; + + MALI_SUCCESS; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.h new file mode 100644 index 00000000000..a199fd66b5a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_mmu.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_MMU_H__ +#define __MALI_KERNEL_MEM_MMU_H__ + +#include "mali_kernel_session_manager.h" + +/** + * Lookup a MMU core by ID. + * @param id ID of the MMU to find + * @return NULL if ID not found or valid, non-NULL if a core was found. + */ +void* mali_memory_core_mmu_lookup(u32 id); + +/** + * Activate a user session with its address space on the given MMU. + * If the session can't be activated due to that the MMU is busy and + * a callback pointer is given, the callback will be called once the MMU becomes idle. + * If the same callback pointer is registered multiple time it will only be called once. + * Nested activations are supported. + * Each call must be matched by a call to @see mali_memory_core_mmu_release_address_space_reference + * + * @param mmu The MMU to activate the address space on + * @param mali_session_data The user session object which address space to activate + * @param callback Pointer to the function to call when the MMU becomes idle + * @param callback_arg Argument given to the callback + * @return 0 if the address space was activated, -EBUSY if the MMU was busy, -EFAULT in all other cases. + */ +int mali_memory_core_mmu_activate_page_table(void* mmu_ptr, struct mali_session_data * mali_session_data, void(*callback)(void*), void * callback_argument); + +/** + * Release a reference to the current active address space. + * Once the last reference is released any callback(s) registered will be called before the function returns + * + * @note Caution must be shown calling this function with locks held due to that callback can be called + * @param mmu The mmu to release a reference to the active address space of + */ +void mali_memory_core_mmu_release_address_space_reference(void* mmu); + +/** + * Soft reset of MMU - needed after power up + * + * @param mmu_ptr The MMU pointer registered with the relevant core + */ +void mali_kernel_mmu_reset(void * mmu_ptr); + +void mali_kernel_mmu_force_bus_reset(void * mmu_ptr); + +/** + * Unregister a previously registered callback. + * @param mmu The MMU to unregister the callback on + * @param callback The function to unregister + */ +void mali_memory_core_mmu_unregister_callback(void* mmu, void(*callback)(void*)); + + + +#endif /* __MALI_KERNEL_MEM_MMU_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.c new file mode 100644 index 00000000000..845de935ec9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_osk.h" + +typedef struct os_allocation +{ + u32 num_pages; + u32 offset_start; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; +} os_allocation; + +typedef struct os_allocator +{ + _mali_osk_lock_t *mutex; + + /** + * Maximum number of pages to allocate from the OS + */ + u32 num_pages_max; + + /** + * Number of pages allocated from the OS + */ + u32 num_pages_allocated; + + /** CPU Usage adjustment (add to mali physical address to get cpu physical address) */ + u32 cpu_usage_adjust; +} os_allocator; + +static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block); +static void os_allocator_release(void * ctx, void * handle); +static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block ); +static void os_allocator_destroy(mali_physical_memory_allocator * allocator); + +mali_physical_memory_allocator * mali_os_allocator_create(u32 max_allocation, u32 cpu_usage_adjust, const char *name) +{ + mali_physical_memory_allocator * allocator; + os_allocator * info; + + max_allocation = (max_allocation + _MALI_OSK_CPU_PAGE_SIZE-1) & ~(_MALI_OSK_CPU_PAGE_SIZE-1); + + MALI_DEBUG_PRINT(2, ("Mali OS memory allocator created with max allocation size of 0x%X bytes, cpu_usage_adjust 0x%08X\n", max_allocation, cpu_usage_adjust)); + + allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator)); + if (NULL != allocator) + { + info = _mali_osk_malloc(sizeof(os_allocator)); + if (NULL != info) + { + info->num_pages_max = max_allocation / _MALI_OSK_CPU_PAGE_SIZE; + info->num_pages_allocated = 0; + info->cpu_usage_adjust = cpu_usage_adjust; + + info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_ORDERED, 0, 106); + if (NULL != info->mutex) + { + allocator->allocate = os_allocator_allocate; + allocator->allocate_page_table_block = os_allocator_allocate_page_table_block; + allocator->destroy = os_allocator_destroy; + allocator->ctx = info; + allocator->name = name; + + return allocator; + } + _mali_osk_free(info); + } + _mali_osk_free(allocator); + } + + return NULL; +} + +static void os_allocator_destroy(mali_physical_memory_allocator * allocator) +{ + os_allocator * info; + MALI_DEBUG_ASSERT_POINTER(allocator); + MALI_DEBUG_ASSERT_POINTER(allocator->ctx); + info = (os_allocator*)allocator->ctx; + _mali_osk_lock_term(info->mutex); + _mali_osk_free(info); + _mali_osk_free(allocator); +} + +static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE; + u32 left; + os_allocator * info; + os_allocation * allocation; + int pages_allocated = 0; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(offset); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + info = (os_allocator*)ctx; + left = descriptor->size - *offset; + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + /** @note this code may not work on Linux, or may require a more complex Linux implementation */ + allocation = _mali_osk_malloc(sizeof(os_allocation)); + if (NULL != allocation) + { + u32 os_mem_max_usage = info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE; + allocation->offset_start = *offset; + allocation->num_pages = ((left + _MALI_OSK_CPU_PAGE_SIZE - 1) & ~(_MALI_OSK_CPU_PAGE_SIZE - 1)) >> _MALI_OSK_CPU_PAGE_ORDER; + MALI_DEBUG_PRINT(6, ("Allocating page array of size %d bytes\n", allocation->num_pages * sizeof(struct page*))); + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max) && _mali_osk_mem_check_allocated(os_mem_max_usage)) + { + err = mali_allocation_engine_map_physical(engine, descriptor, *offset, MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, info->cpu_usage_adjust, _MALI_OSK_CPU_PAGE_SIZE); + if ( _MALI_OSK_ERR_OK != err) + { + if ( _MALI_OSK_ERR_NOMEM == err) + { + /* 'Partial' allocation (or, out-of-memory on first page) */ + break; + } + + MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n")); + + /* Fatal error, cleanup any previous pages allocated. */ + if ( pages_allocated > 0 ) + { + mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*pages_allocated, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR ); + /* (*offset) doesn't need to be restored; it will not be used by the caller on failure */ + } + + pages_allocated = 0; + + result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + break; + } + + /* Loop iteration */ + if (left < _MALI_OSK_CPU_PAGE_SIZE) left = 0; + else left -= _MALI_OSK_CPU_PAGE_SIZE; + + pages_allocated++; + + *offset += _MALI_OSK_CPU_PAGE_SIZE; + } + + /* Loop termination; decide on result */ + if (pages_allocated) + { + MALI_DEBUG_PRINT(6, ("Allocated %d pages\n", pages_allocated)); + if (left) result = MALI_MEM_ALLOC_PARTIAL; + else result = MALI_MEM_ALLOC_FINISHED; + + /* Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory. + * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches. + * This is required for MALI to have the correct view of the memory. + */ + _mali_osk_cache_ensure_uncached_range_flushed( (void *)descriptor, allocation->offset_start, pages_allocated *_MALI_OSK_CPU_PAGE_SIZE ); + allocation->num_pages = pages_allocated; + allocation->engine = engine; /* Necessary to make the engine's unmap call */ + allocation->descriptor = descriptor; /* Necessary to make the engine's unmap call */ + info->num_pages_allocated += pages_allocated; + + MALI_DEBUG_PRINT(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + alloc_info->ctx = info; + alloc_info->handle = allocation; + alloc_info->release = os_allocator_release; + } + else + { + MALI_DEBUG_PRINT(6, ("Releasing pages array due to no pages allocated\n")); + _mali_osk_free( allocation ); + } + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return result; +} + +static void os_allocator_release(void * ctx, void * handle) +{ + os_allocator * info; + os_allocation * allocation; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(handle); + + info = (os_allocator*)ctx; + allocation = (os_allocation*)handle; + engine = allocation->engine; + descriptor = allocation->descriptor; + + MALI_DEBUG_ASSERT_POINTER( engine ); + MALI_DEBUG_ASSERT_POINTER( descriptor ); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + MALI_DEBUG_PRINT(6, ("Releasing %d os pages\n", allocation->num_pages)); + + MALI_DEBUG_ASSERT( allocation->num_pages <= info->num_pages_allocated); + info->num_pages_allocated -= allocation->num_pages; + + mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*allocation->num_pages, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR ); + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_free(allocation); +} + +static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block) +{ + const int allocation_order = 6; /* _MALI_OSK_CPU_PAGE_SIZE << 6 */ + void *virt; + const u32 pages_to_allocate = 1 << allocation_order; + const u32 size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order; + os_allocator * info; + + u32 cpu_phys_base; + + MALI_DEBUG_ASSERT_POINTER(ctx); + info = (os_allocator*)ctx; + + /* Ensure we don't allocate more than we're supposed to from the ctx */ + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + if ( (info->num_pages_allocated + pages_to_allocate > info->num_pages_max) && _mali_osk_mem_check_allocated(info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE) ) + { + /* return OOM */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return MALI_MEM_ALLOC_NONE; + } + + virt = _mali_osk_mem_allocioregion( &cpu_phys_base, size ); + + if ( NULL == virt ) + { + /* return OOM */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return MALI_MEM_ALLOC_NONE; + } + + block->release = os_allocator_page_table_block_release; + block->ctx = ctx; + block->handle = (void*)allocation_order; + block->size = size; + block->phys_base = cpu_phys_base - info->cpu_usage_adjust; + block->mapping = virt; + + info->num_pages_allocated += pages_to_allocate; + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return MALI_MEM_ALLOC_FINISHED; +} + +static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block ) +{ + os_allocator * info; + u32 allocation_order; + u32 pages_allocated; + + MALI_DEBUG_ASSERT_POINTER( page_table_block ); + + info = (os_allocator*)page_table_block->ctx; + + MALI_DEBUG_ASSERT_POINTER( info ); + + allocation_order = (u32)page_table_block->handle; + + pages_allocated = 1 << allocation_order; + + MALI_DEBUG_ASSERT( pages_allocated * _MALI_OSK_CPU_PAGE_SIZE == page_table_block->size ); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + MALI_DEBUG_ASSERT( pages_allocated <= info->num_pages_allocated); + info->num_pages_allocated -= pages_allocated; + + /* Adjust phys_base from mali physical address to CPU physical address */ + _mali_osk_mem_freeioregion( page_table_block->phys_base + info->cpu_usage_adjust, page_table_block->size, page_table_block->mapping ); + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.h new file mode 100644 index 00000000000..ff49f8a816a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_mem_os.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_OS_H__ +#define __MALI_KERNEL_MEM_OS_H__ + +/** + * @brief Creates an object that manages allocating OS memory + * + * Creates an object that provides an interface to allocate OS memory and + * have it mapped into the Mali virtual memory space. + * + * The object exposes pointers to + * - allocate OS memory + * - allocate Mali page tables in OS memory + * - destroy the object + * + * Allocations from OS memory are of type mali_physical_memory_allocation + * which provides a function to release the allocation. + * + * @param max_allocation max. number of bytes that can be allocated from OS memory + * @param cpu_usage_adjust value to add to mali physical addresses to obtain CPU physical addresses + * @param name description of the allocator + * @return pointer to mali_physical_memory_allocator object. NULL on failure. + **/ +mali_physical_memory_allocator * mali_os_allocator_create(u32 max_allocation, u32 cpu_usage_adjust, const char *name); + +#endif /* __MALI_KERNEL_MEM_OS_H__ */ + + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.c new file mode 100644 index 00000000000..3b4ace05f01 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_osk.h" +#include "mali_osk_list.h" + +typedef struct memory_engine +{ + mali_kernel_mem_address_manager * mali_address; + mali_kernel_mem_address_manager * process_address; +} memory_engine; + +mali_allocation_engine mali_allocation_engine_create(mali_kernel_mem_address_manager * mali_address_manager, mali_kernel_mem_address_manager * process_address_manager) +{ + memory_engine * engine; + + /* Mali Address Manager need not support unmap_physical */ + MALI_DEBUG_ASSERT_POINTER(mali_address_manager); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->allocate); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->release); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->map_physical); + + /* Process Address Manager must support unmap_physical for OS allocation + * error path handling */ + MALI_DEBUG_ASSERT_POINTER(process_address_manager); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->allocate); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->release); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->map_physical); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->unmap_physical); + + + engine = (memory_engine*)_mali_osk_malloc(sizeof(memory_engine)); + if (NULL == engine) return NULL; + + engine->mali_address = mali_address_manager; + engine->process_address = process_address_manager; + + return (mali_allocation_engine)engine; +} + +void mali_allocation_engine_destroy(mali_allocation_engine engine) +{ + MALI_DEBUG_ASSERT_POINTER(engine); + _mali_osk_free(engine); +} + +_mali_osk_errcode_t mali_allocation_engine_allocate_memory(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, mali_physical_memory_allocator * physical_allocators, _mali_osk_list_t *tracking_list ) +{ + memory_engine * engine = (memory_engine*)mem_engine; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(physical_allocators); + /* ASSERT that the list member has been initialized, even if it won't be + * used for tracking. We need it to be initialized to see if we need to + * delete it from a list in the release function. */ + MALI_DEBUG_ASSERT( NULL != descriptor->list.next && NULL != descriptor->list.prev ); + + if (_MALI_OSK_ERR_OK == engine->mali_address->allocate(descriptor)) + { + _mali_osk_errcode_t res = _MALI_OSK_ERR_OK; + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + res = engine->process_address->allocate(descriptor); + } + if ( _MALI_OSK_ERR_OK == res ) + { + /* address space setup OK, commit physical memory to the allocation */ + mali_physical_memory_allocator * active_allocator = physical_allocators; + struct mali_physical_memory_allocation * active_allocation_tracker = &descriptor->physical_allocation; + u32 offset = 0; + + while ( NULL != active_allocator ) + { + switch (active_allocator->allocate(active_allocator->ctx, mem_engine, descriptor, &offset, active_allocation_tracker)) + { + case MALI_MEM_ALLOC_FINISHED: + if ( NULL != tracking_list ) + { + /* Insert into the memory session list */ + /* ASSERT that it is not already part of a list */ + MALI_DEBUG_ASSERT( _mali_osk_list_empty( &descriptor->list ) ); + _mali_osk_list_add( &descriptor->list, tracking_list ); + } + + MALI_SUCCESS; /* all done */ + case MALI_MEM_ALLOC_NONE: + /* reuse current active_allocation_tracker */ + MALI_DEBUG_PRINT( 4, ("Memory Engine Allocate: No allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + case MALI_MEM_ALLOC_PARTIAL: + if (NULL != active_allocator->next) + { + /* need a new allocation tracker */ + active_allocation_tracker->next = _mali_osk_calloc(1, sizeof(mali_physical_memory_allocation)); + if (NULL != active_allocation_tracker->next) + { + active_allocation_tracker = active_allocation_tracker->next; + MALI_DEBUG_PRINT( 2, ("Memory Engine Allocate: Partial allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + } + } + /* FALL THROUGH */ + case MALI_MEM_ALLOC_INTERNAL_FAILURE: + active_allocator = NULL; /* end the while loop */ + break; + } + } + + MALI_DEBUG_PRINT(3, ("Non-fatal OOM, have to cleanup, stopped at offset %d for size %d\n", offset, descriptor->size)); + + /* allocation failure, start cleanup */ + /* loop over any potential partial allocations */ + active_allocation_tracker = &descriptor->physical_allocation; + while (NULL != active_allocation_tracker) + { + /* handle blank trackers which will show up during failure */ + if (NULL != active_allocation_tracker->release) + { + active_allocation_tracker->release(active_allocation_tracker->ctx, active_allocation_tracker->handle); + } + active_allocation_tracker = active_allocation_tracker->next; + } + + /* free the allocation tracker objects themselves, skipping the tracker stored inside the descriptor itself */ + for ( active_allocation_tracker = descriptor->physical_allocation.next; active_allocation_tracker != NULL; ) + { + void * buf = active_allocation_tracker; + active_allocation_tracker = active_allocation_tracker->next; + _mali_osk_free(buf); + } + + /* release the address spaces */ + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + engine->process_address->release(descriptor); + } + } + engine->mali_address->release(descriptor); + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +void mali_allocation_engine_release_memory(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor) +{ + memory_engine * engine = (memory_engine*)mem_engine; + mali_physical_memory_allocation * active_allocation_tracker; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /* Determine whether we need to remove this from a tracking list */ + if ( ! _mali_osk_list_empty( &descriptor->list ) ) + { + _mali_osk_list_del( &descriptor->list ); + /* Clear the list for debug mode, catch use-after-free */ + MALI_DEBUG_CODE( descriptor->list.next = descriptor->list.prev = NULL; ) + } + + engine->mali_address->release(descriptor); + + active_allocation_tracker = &descriptor->physical_allocation; + while (NULL != active_allocation_tracker) + { + MALI_DEBUG_ASSERT_POINTER(active_allocation_tracker->release); + active_allocation_tracker->release(active_allocation_tracker->ctx, active_allocation_tracker->handle); + active_allocation_tracker = active_allocation_tracker->next; + } + + /* free the allocation tracker objects themselves, skipping the tracker stored inside the descriptor itself */ + for ( active_allocation_tracker = descriptor->physical_allocation.next; active_allocation_tracker != NULL; ) + { + void * buf = active_allocation_tracker; + active_allocation_tracker = active_allocation_tracker->next; + _mali_osk_free(buf); + } + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + engine->process_address->release(descriptor); + } +} + + +_mali_osk_errcode_t mali_allocation_engine_map_physical(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, u32 offset, u32 phys, u32 cpu_usage_adjust, u32 size) +{ + _mali_osk_errcode_t err; + memory_engine * engine = (memory_engine*)mem_engine; + _mali_osk_mem_mapregion_flags_t unmap_flags = (_mali_osk_mem_mapregion_flags_t)0; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_PRINT(7, ("Mapping phys 0x%08X length 0x%08X at offset 0x%08X\n", phys, size, offset)); + + MALI_DEBUG_ASSERT_POINTER(engine->mali_address); + MALI_DEBUG_ASSERT_POINTER(engine->mali_address->map_physical); + + /* Handle process address manager first, because we may need them to + * allocate the physical page */ + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + /* Handle OS-allocated specially, since an adjustment may be required */ + if ( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC == phys ) + { + MALI_DEBUG_ASSERT( _MALI_OSK_CPU_PAGE_SIZE == size ); + + /* Set flags to use on error path */ + unmap_flags |= _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR; + + err = engine->process_address->map_physical(descriptor, offset, &phys, size); + /* Adjust for cpu physical address to mali physical address */ + phys -= cpu_usage_adjust; + } + else + { + u32 cpu_phys; + /* Adjust mali physical address to cpu physical address */ + cpu_phys = phys + cpu_usage_adjust; + err = engine->process_address->map_physical(descriptor, offset, &cpu_phys, size); + } + + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_ERROR( err ); + } + } + + MALI_DEBUG_PRINT(4, ("Mapping phys 0x%08X length 0x%08X at offset 0x%08X to CPUVA 0x%08X\n", phys, size, offset, (u32)(descriptor->mapping) + offset)); + + /* Mali address manager must use the physical address - no point in asking + * it to allocate another one for us */ + MALI_DEBUG_ASSERT( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC != phys ); + + err = engine->mali_address->map_physical(descriptor, offset, &phys, size); + + if ( _MALI_OSK_ERR_OK != err ) + { + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + MALI_DEBUG_PRINT( 2, ("Process address manager succeeded, but Mali Address manager failed for phys=0x%08X size=0x%08X, offset=0x%08X. Will unmap.\n", phys, size, offset)); + engine->process_address->unmap_physical(descriptor, offset, size, unmap_flags); + } + + MALI_ERROR( err ); + } + + MALI_SUCCESS; +} + +void mali_allocation_engine_unmap_physical(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t unmap_flags ) +{ + memory_engine * engine = (memory_engine*)mem_engine; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_PRINT(7, ("UnMapping length 0x%08X at offset 0x%08X\n", size, offset)); + + MALI_DEBUG_ASSERT_POINTER(engine->mali_address); + MALI_DEBUG_ASSERT_POINTER(engine->process_address); + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + /* Mandetory for process_address manager to have an unmap function*/ + engine->process_address->unmap_physical( descriptor, offset, size, unmap_flags ); + } + + /* Optional for mali_address manager to have an unmap function*/ + if ( NULL != engine->mali_address->unmap_physical ) + { + engine->mali_address->unmap_physical( descriptor, offset, size, unmap_flags ); + } +} + + +_mali_osk_errcode_t mali_allocation_engine_allocate_page_tables(mali_allocation_engine engine, mali_page_table_block * descriptor, mali_physical_memory_allocator * physical_provider) +{ + mali_physical_memory_allocator * active_allocator = physical_provider; + + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(physical_provider); + + while ( NULL != active_allocator ) + { + switch (active_allocator->allocate_page_table_block(active_allocator->ctx, descriptor)) + { + case MALI_MEM_ALLOC_FINISHED: + MALI_SUCCESS; /* all done */ + case MALI_MEM_ALLOC_NONE: + /* try next */ + MALI_DEBUG_PRINT( 2, ("Memory Engine Allocate PageTables: No allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + case MALI_MEM_ALLOC_PARTIAL: + MALI_DEBUG_PRINT(1, ("Invalid return value from allocate_page_table_block call: MALI_MEM_ALLOC_PARTIAL\n")); + /* FALL THROUGH */ + case MALI_MEM_ALLOC_INTERNAL_FAILURE: + MALI_DEBUG_PRINT(1, ("Aborting due to allocation failure\n")); + active_allocator = NULL; /* end the while loop */ + break; + } + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + + +void mali_allocation_engine_report_allocators( mali_physical_memory_allocator * physical_provider ) +{ + mali_physical_memory_allocator * active_allocator = physical_provider; + MALI_DEBUG_ASSERT_POINTER(physical_provider); + + MALI_DEBUG_PRINT( 1, ("Mali memory allocators will be used in this order of preference (lowest numbered first) :\n")); + while ( NULL != active_allocator ) + { + if ( NULL != active_allocator->name ) + { + MALI_DEBUG_PRINT( 1, ("\t%d: %s\n", active_allocator->alloc_order, active_allocator->name) ); + } + else + { + MALI_DEBUG_PRINT( 1, ("\t%d: (UNNAMED ALLOCATOR)\n", active_allocator->alloc_order) ); + } + active_allocator = active_allocator->next; + } + +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.h new file mode 100644 index 00000000000..80a2b4bb3a7 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_memory_engine.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEMORY_ENGINE_H__ +#define __MALI_KERNEL_MEMORY_ENGINE_H__ + +typedef void * mali_allocation_engine; + +typedef enum { MALI_MEM_ALLOC_FINISHED, MALI_MEM_ALLOC_PARTIAL, MALI_MEM_ALLOC_NONE, MALI_MEM_ALLOC_INTERNAL_FAILURE } mali_physical_memory_allocation_result; + +typedef struct mali_physical_memory_allocation +{ + void (*release)(void * ctx, void * handle); /**< Function to call on to release the physical memory */ + void * ctx; + void * handle; + struct mali_physical_memory_allocation * next; +} mali_physical_memory_allocation; + +struct mali_page_table_block; + +typedef struct mali_page_table_block +{ + void (*release)(struct mali_page_table_block *page_table_block); + void * ctx; + void * handle; + u32 size; /**< In bytes, should be a multiple of MALI_MMU_PAGE_SIZE to avoid internal fragementation */ + u32 phys_base; /**< Mali physical address */ + mali_io_address mapping; +} mali_page_table_block; + + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +typedef enum +{ + MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE = 0x1, + MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE = 0x2, +} mali_memory_allocation_flag; + +/** + * Supplying this 'magic' physical address requests that the OS allocate the + * physical address at page commit time, rather than commiting a specific page + */ +#define MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC ((u32)(-1)) + +typedef struct mali_memory_allocation +{ + /* Information about the allocation */ + void * mapping; /**< CPU virtual address where the memory is mapped at */ + u32 mali_address; /**< The Mali seen address of the memory allocation */ + u32 size; /**< Size of the allocation */ + u32 permission; /**< Permission settings */ + mali_memory_allocation_flag flags; + + _mali_osk_lock_t * lock; + + /* Manager specific information pointers */ + void * mali_addr_mapping_info; /**< Mali address allocation specific info */ + void * process_addr_mapping_info; /**< Mapping manager specific info */ + + mali_physical_memory_allocation physical_allocation; + + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ +} mali_memory_allocation; +/** @} */ /* end group _mali_osk_low_level_memory */ + + +typedef struct mali_physical_memory_allocator +{ + mali_physical_memory_allocation_result (*allocate)(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); + mali_physical_memory_allocation_result (*allocate_page_table_block)(void * ctx, mali_page_table_block * block); /* MALI_MEM_ALLOC_PARTIAL not allowed */ + void (*destroy)(struct mali_physical_memory_allocator * allocator); + void * ctx; + const char * name; /**< Descriptive name for use in mali_allocation_engine_report_allocators, or NULL */ + u32 alloc_order; /**< Order in which the allocations should happen */ + struct mali_physical_memory_allocator * next; +} mali_physical_memory_allocator; + +typedef struct mali_kernel_mem_address_manager +{ + _mali_osk_errcode_t (*allocate)(mali_memory_allocation *); /**< Function to call to reserve an address */ + void (*release)(mali_memory_allocation *); /**< Function to call to free the address allocated */ + + /** + * Function called for each physical sub allocation. + * Called for each physical block allocated by the physical memory manager. + * @param[in] descriptor The memory descriptor in question + * @param[in] off Offset from the start of range + * @param[in,out] phys_addr A pointer to the physical address of the start of the + * physical block. When *phys_addr == MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC + * is used, this requests the function must allocate the physical page + * itself, and return it through the pointer provided. + * @param[in] size Length in bytes of the physical block + * @return _MALI_OSK_ERR_OK on success. + * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure. + * Specifically, _MALI_OSK_ERR_UNSUPPORTED indicates that the function + * does not support allocating physical pages itself. + */ + _mali_osk_errcode_t (*map_physical)(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size); + + /** + * Function called to remove a physical sub allocation. + * Called on error paths where one of the address managers fails. + * + * @note this is optional. For address managers where this is not + * implemented, the value of this member is NULL. The memory engine + * currently does not require the mali address manager to be able to + * unmap individual pages, but the process address manager must have this + * capability. + * + * @param[in] descriptor The memory descriptor in question + * @param[in] off Offset from the start of range + * @param[in] size Length in bytes of the physical block + * @param[in] flags flags to use on a per-page basis. For OS-allocated + * physical pages, this must include _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR. + * @return _MALI_OSK_ERR_OK on success. + * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure. + */ + void (*unmap_physical)(mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags); + +} mali_kernel_mem_address_manager; + +mali_allocation_engine mali_allocation_engine_create(mali_kernel_mem_address_manager * mali_address_manager, mali_kernel_mem_address_manager * process_address_manager); + +void mali_allocation_engine_destroy(mali_allocation_engine engine); + +int mali_allocation_engine_allocate_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor, mali_physical_memory_allocator * physical_provider, _mali_osk_list_t *tracking_list ); +void mali_allocation_engine_release_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor); + +int mali_allocation_engine_map_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 phys, u32 cpu_usage_adjust, u32 size); +void mali_allocation_engine_unmap_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t unmap_flags); + +int mali_allocation_engine_allocate_page_tables(mali_allocation_engine, mali_page_table_block * descriptor, mali_physical_memory_allocator * physical_provider); + +void mali_allocation_engine_report_allocators(mali_physical_memory_allocator * physical_provider); + +#endif /* __MALI_KERNEL_MEMORY_ENGINE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_pp.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_pp.h new file mode 100644 index 00000000000..ff1e153678c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_pp.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PP_H__ +#define __MALI_KERNEL_PP_H__ + +extern struct mali_kernel_subsystem mali_subsystem_mali200; + +#if USING_MALI_PMM +_mali_osk_errcode_t malipp_signal_power_up( u32 core_num, mali_bool queue_only ); +_mali_osk_errcode_t malipp_signal_power_down( u32 core_num, mali_bool immediate_only ); +#endif + +#endif /* __MALI_KERNEL_PP_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.c new file mode 100644 index 00000000000..07bb894b1b4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_timestamp.h" + +#define MALI_PROFILING_MAX_BUFFER_ENTRIES 1048576 + +typedef struct mali_profiling_entry +{ + u64 timestamp; + u32 event_id; + u32 data[5]; +} mali_profiling_entry; + + +typedef enum mali_profiling_state +{ + MALI_PROFILING_STATE_UNINITIALIZED, + MALI_PROFILING_STATE_IDLE, + MALI_PROFILING_STATE_RUNNING, + MALI_PROFILING_STATE_RETURN, +} mali_profiling_state; + + +static _mali_osk_lock_t *lock = NULL; +static mali_profiling_state prof_state = MALI_PROFILING_STATE_UNINITIALIZED; +static mali_profiling_entry* profile_entries = NULL; +static u32 profile_entry_count = 0; +static _mali_osk_atomic_t profile_insert_index; +static _mali_osk_atomic_t profile_entries_written; + + +_mali_osk_errcode_t _mali_profiling_init(void) +{ + profile_entries = NULL; + profile_entry_count = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + _mali_osk_atomic_init(&profile_entries_written, 0); + + lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0 ); + if (NULL == lock) + { + return _MALI_OSK_ERR_FAULT; + } + + prof_state = MALI_PROFILING_STATE_IDLE; + + return _MALI_OSK_ERR_OK; +} + +void _mali_profiling_term(void) +{ + prof_state = MALI_PROFILING_STATE_UNINITIALIZED; + + /* wait for all elements to be completely inserted into array */ + while (_mali_osk_atomic_read(&profile_insert_index) != _mali_osk_atomic_read(&profile_entries_written)) + { + /* do nothing */; + } + + if (NULL != profile_entries) + { + _mali_osk_free(profile_entries); + profile_entries = NULL; + } + + if (NULL != lock) + { + _mali_osk_lock_term(lock); + lock = NULL; + } +} + +inline _mali_osk_errcode_t _mali_profiling_add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4) +{ + u32 cur_index = _mali_osk_atomic_inc_return(&profile_insert_index) - 1; + + if (prof_state != MALI_PROFILING_STATE_RUNNING || cur_index >= profile_entry_count) + { + /* + * Not in recording mode, or buffer is full + * Decrement index again, and early out + */ + _mali_osk_atomic_dec(&profile_insert_index); + return _MALI_OSK_ERR_FAULT; + } + + profile_entries[cur_index].timestamp = _mali_timestamp_get(); + profile_entries[cur_index].event_id = event_id; + profile_entries[cur_index].data[0] = data0; + profile_entries[cur_index].data[1] = data1; + profile_entries[cur_index].data[2] = data2; + profile_entries[cur_index].data[3] = data3; + profile_entries[cur_index].data[4] = data4; + + _mali_osk_atomic_inc(&profile_entries_written); + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args) +{ + _mali_osk_errcode_t ret; + + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_IDLE) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + if (args->limit > MALI_PROFILING_MAX_BUFFER_ENTRIES) + { + args->limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; + } + + profile_entries = _mali_osk_malloc(args->limit * sizeof(mali_profiling_entry)); + profile_entry_count = args->limit; + if (NULL == profile_entries) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_NOMEM; + } + + ret = _mali_timestamp_reset(); + + if (ret == _MALI_OSK_ERR_OK) + { + prof_state = MALI_PROFILING_STATE_RUNNING; + } + else + { + _mali_osk_free(profile_entries); + profile_entries = NULL; + } + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return ret; +} + +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args) +{ + /* Always add process and thread identificator in the first two data elements for events from user space */ + return _mali_profiling_add_event(args->event_id, _mali_osk_get_pid(), _mali_osk_get_tid(), args->data[2], args->data[3], args->data[4]); +} + +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RUNNING) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + /* go into return state (user to retreive events), no more events will be added after this */ + prof_state = MALI_PROFILING_STATE_RETURN; + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + + /* wait for all elements to be completely inserted into array */ + while (_mali_osk_atomic_read(&profile_insert_index) != _mali_osk_atomic_read(&profile_entries_written)) + { + /* do nothing */; + } + + args->count = _mali_osk_atomic_read(&profile_insert_index); + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args) +{ + u32 index = args->index; + + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RETURN) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + if (index >= _mali_osk_atomic_read(&profile_entries_written)) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_FAULT; + } + + args->timestamp = profile_entries[index].timestamp; + args->event_id = profile_entries[index].event_id; + args->data[0] = profile_entries[index].data[0]; + args->data[1] = profile_entries[index].data[1]; + args->data[2] = profile_entries[index].data[2]; + args->data[3] = profile_entries[index].data[3]; + args->data[4] = profile_entries[index].data[4]; + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RETURN) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + prof_state = MALI_PROFILING_STATE_IDLE; + profile_entry_count = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + _mali_osk_atomic_init(&profile_entries_written, 0); + if (NULL != profile_entries) + { + _mali_osk_free(profile_entries); + profile_entries = NULL; + } + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_OK; +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.h new file mode 100644 index 00000000000..5d3aa6eb841 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_profiling.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PROFILING_H__ +#define __MALI_KERNEL_PROFILING_H__ + +#if MALI_TIMELINE_PROFILING_ENABLED + +#include <../../../include/cinstr/mali_cinstr_profiling_events_m200.h> + +/** + * Initialize the profiling module. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_init(void); + +/* + * Terminate the profiling module. + */ +void _mali_profiling_term(void); + +/** + * Add an profiling event + * + * @param event_id The event identificator. + * @param data0 - First data parameter, depending on event_id specified. + * @param data1 - Second data parameter, depending on event_id specified. + * @param data2 - Third data parameter, depending on event_id specified. + * @param data3 - Fourth data parameter, depending on event_id specified. + * @param data4 - Fifth data parameter, depending on event_id specified. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4); + +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + +#endif /* __MALI_KERNEL_PROFILING_H__ */ + + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.c new file mode 100644 index 00000000000..1db8f3ad5ab --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.c @@ -0,0 +1,2032 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_osk.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_rendercore.h" +#include "mali_osk_list.h" +#if MALI_GPU_UTILIZATION +#include "mali_kernel_utilization.h" +#endif +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_kernel_profiling.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" +#endif /* USING_MMU */ +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif /* USING_MALI400_L2_CACHE */ + +#define HANG_CHECK_MSECS_MIN 100 +#define HANG_CHECK_MSECS_MAX 2000 /* 2 secs */ +#define HANG_CHECK_MSECS_DEFAULT 500 /* 500 ms */ + +#define WATCHDOG_MSECS_MIN (2*HANG_CHECK_MSECS_MIN) +#define WATCHDOG_MSECS_MAX 3600000 /* 1 hour */ +#define WATCHDOG_MSECS_DEFAULT 900000 /* 15 mins */ + +/* max value that will be converted from jiffies to msecs and written to job->render_time_msecs */ +#define JOB_MAX_JIFFIES 100000 + +int mali_hang_check_interval = HANG_CHECK_MSECS_DEFAULT; +int mali_max_job_runtime = WATCHDOG_MSECS_DEFAULT; + +/* Subsystem entrypoints: */ +static _mali_osk_errcode_t rendercore_subsystem_startup(mali_kernel_subsystem_identifier id); +static void rendercore_subsystem_terminate(mali_kernel_subsystem_identifier id); +#if USING_MMU +static void rendercore_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif + + +static void mali_core_subsystem_cleanup_all_renderunits(struct mali_core_subsystem* subsys); +static void mali_core_subsystem_move_core_set_idle(struct mali_core_renderunit *core); + +static mali_core_session * mali_core_subsystem_get_waiting_session(mali_core_subsystem *subsystem); +static mali_core_job * mali_core_subsystem_release_session_get_job(mali_core_subsystem *subsystem, mali_core_session * session); + +static void find_and_abort(mali_core_session* session, u32 abort_id); + +static void mali_core_job_start_on_core(mali_core_job *job, mali_core_renderunit *core); +#if USING_MMU +static void mali_core_subsystem_callback_schedule_wrapper(void* sub); +#endif +static void mali_core_subsystem_schedule(mali_core_subsystem*subsystem); +static void mali_core_renderunit_detach_job_from_core(mali_core_renderunit* core, mali_subsystem_reschedule_option reschedule, mali_subsystem_job_end_code end_status); + +static void mali_core_renderunit_irq_handler_remove(struct mali_core_renderunit *core); + +static _mali_osk_errcode_t mali_core_irq_handler_upper_half (void * data); +static void mali_core_irq_handler_bottom_half ( void *data ); + +#if USING_MMU +static void lock_subsystem(struct mali_core_subsystem * subsys); +static void unlock_subsystem(struct mali_core_subsystem * subsys); +#endif + + +/** + * This will be one of the subsystems in the array of subsystems: + * static struct mali_kernel_subsystem * subsystems[]; + * found in file: mali_kernel_core.c + * + * This subsystem is necessary for operations common to all rendercore + * subsystems. For example, mali_subsystem_mali200 and mali_subsystem_gp2 may + * share a mutex when RENDERCORES_USE_GLOBAL_MUTEX is non-zero. + */ +struct mali_kernel_subsystem mali_subsystem_rendercore= +{ + rendercore_subsystem_startup, /* startup */ + rendercore_subsystem_terminate, /* shutdown */ + NULL, /* load_complete */ + NULL, /* system_info_fill */ + NULL, /* session_begin */ + NULL, /* session_end */ +#if USING_MMU + rendercore_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +} ; + +static _mali_osk_lock_t *rendercores_global_mutex = NULL; +static u32 rendercores_global_mutex_is_held = 0; +static u32 rendercores_global_mutex_owner = 0; + +/** The 'dummy' rendercore subsystem to allow global subsystem mutex to be + * locked for all subsystems that extend the ''rendercore'' */ +static mali_core_subsystem rendercore_dummy_subsystem = {0,}; + +/* + * Rendercore Subsystem functions. + * + * These are exposed by mali_subsystem_rendercore + */ + +/** + * @brief Initialize the Rendercore subsystem. + * + * This must be called before any other subsystem that extends the + * ''rendercore'' may be initialized. For example, this must be called before + * the following functions: + * - mali200_subsystem_startup(), from mali_subsystem_mali200 + * - maligp_subsystem_startup(), from mali_subsystem_gp2 + * + * @note This function is separate from mali_core_subsystem_init(). They + * are related, in that mali_core_subsystem_init() may use the structures + * initialized by rendercore_subsystem_startup() + */ +static _mali_osk_errcode_t rendercore_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + rendercores_global_mutex_is_held = 0; + rendercores_global_mutex = _mali_osk_lock_init( + (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_ORDERED), + 0, 129); + + if (NULL == rendercores_global_mutex) + { + MALI_PRINT_ERROR(("Failed: _mali_osk_lock_init\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + rendercore_dummy_subsystem.name = "Rendercore Global Subsystem"; /* On the constant pool, do not free */ + rendercore_dummy_subsystem.magic_nr = SUBSYSTEM_MAGIC_NR; /* To please the Subsystem Mutex code */ + +#if MALI_GPU_UTILIZATION + if (mali_utilization_init() != _MALI_OSK_ERR_OK) + { + _mali_osk_lock_term(rendercores_global_mutex); + rendercores_global_mutex = NULL; + MALI_PRINT_ERROR(("Failed: mali_utilization_init\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + if (_mali_profiling_init() != _MALI_OSK_ERR_OK) + { + /* No biggie if we wheren't able to initialize the profiling */ + MALI_PRINT_ERROR(("Rendercore: Failed to initialize profiling, feature will be unavailable\n")) ; + } +#endif + + MALI_DEBUG_PRINT(2, ("Rendercore: subsystem global mutex initialized\n")) ; + MALI_SUCCESS; +} + +/** + * @brief Terminate the Rendercore subsystem. + * + * This must only be called \b after any other subsystem that extends the + * ''rendercore'' has been terminated. For example, this must be called \b after + * the following functions: + * - mali200_subsystem_terminate(), from mali_subsystem_mali200 + * - maligp_subsystem_terminate(), from mali_subsystem_gp2 + * + * @note This function is separate from mali_core_subsystem_cleanup(), though, + * the subsystems that extend ''rendercore'' must still call + * mali_core_subsystem_cleanup() when they terminate. + */ +static void rendercore_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + /* Catch double-terminate */ + MALI_DEBUG_ASSERT_POINTER( rendercores_global_mutex ); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_term(); +#endif + +#if MALI_GPU_UTILIZATION + mali_utilization_term(); +#endif + + rendercore_dummy_subsystem.name = NULL; /* The original string was on the constant pool, do not free */ + rendercore_dummy_subsystem.magic_nr = 0; + + /* ASSERT that no-one's holding this */ + MALI_DEBUG_PRINT_ASSERT( 0 == rendercores_global_mutex_is_held, + ("Rendercores' Global Mutex was held at termination time. Have the subsystems that extend ''rendercore'' been terminated?\n") ); + + _mali_osk_lock_term( rendercores_global_mutex ); + rendercores_global_mutex = NULL; + + MALI_DEBUG_PRINT(2, ("Rendercore: subsystem global mutex terminated\n")) ; +} + + +#if USING_MMU +/** + * @brief Handle certain Rendercore subsystem broadcast notifications + * + * When RENDERCORES_USE_GLOBAL_MUTEX is non-zero, this handles the following messages: + * - MMU_KILL_STEP0_LOCK_SUBSYSTEM + * - MMU_KILL_STEP4_UNLOCK_SUBSYSTEM + * + * The purpose is to manage the Rendercode Global Mutex, which cannot be + * managed by any system that extends the ''rendercore''. + * + * All other messages must be handled by mali_core_subsystem_broadcast_notification() + * + * + * When RENDERCORES_USE_GLOBAL_MUTEX is 0, this function does nothing. + * Instead, the subsystem that extends the ''rendercore' \b must handle its + * own mutexes - refer to mali_core_subsystem_broadcast_notification(). + * + * Used currently only for signalling when MMU has a pagefault + */ +static void rendercore_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + switch(message) + { + case MMU_KILL_STEP0_LOCK_SUBSYSTEM: + lock_subsystem( &rendercore_dummy_subsystem ); + break; + case MMU_KILL_STEP4_UNLOCK_SUBSYSTEM: + unlock_subsystem( &rendercore_dummy_subsystem ); + break; + + case MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES: + /** FALLTHROUGH */ + case MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS: + /** FALLTHROUGH */ + case MMU_KILL_STEP3_CONTINUE_JOB_HANDLING: + break; + + default: + MALI_PRINT_ERROR(("Illegal message: 0x%x, data: 0x%x\n", (u32)message, data)); + break; + } + +} +#endif + +/* + * Functions inherited by the subsystems that extend the ''rendercore''. + */ + +u32 mali_core_renderunit_register_read(mali_core_renderunit *core, u32 relative_address) +{ + u32 read_val; + + #if USING_MALI_PMM + if( core->state == CORE_OFF ) + { + MALI_PRINT_ERROR(("Core is OFF during read: Core:%s Addr:0x%04X\n", + core->description,relative_address)); + return 0xDEADBEEF; + } + #endif + + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + + if (mali_benchmark) return 0; + + if (relative_address >= core->size) + { + MALI_PRINT_ERROR(("Trying to read from illegal register: 0x%04x in core: %s\n", + relative_address, core->description)); + return 0xDEADBEEF; + } + + read_val = _mali_osk_mem_ioread32(core->registers_mapped, relative_address); + + MALI_DEBUG_PRINT(6, ("Core: renderunit_register_read: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, read_val)); + + return read_val; +} + +void mali_core_renderunit_register_read_array(mali_core_renderunit *core, + u32 relative_address, + u32 * result_array, + u32 nr_of_regs + ) +{ + /* NOTE Do not use burst reads against the registers */ + + u32 i; + + for(i=0; idescription,relative_address, nr_of_regs)); +} + +void mali_core_renderunit_register_write(mali_core_renderunit *core, u32 relative_address, u32 new_val) +{ + #if USING_MALI_PMM + if( core->state == CORE_OFF ) + { + MALI_PRINT_ERROR(("Core is OFF during write: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, new_val)); + return; + } + #endif + + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + + if (mali_benchmark) return; + + if (relative_address >= core->size) + { + MALI_PRINT_ERROR(("Trying to write to illegal register: 0x%04x in core: %s", + relative_address, core->description)); + return; + } + + MALI_DEBUG_PRINT(6, ("mali_core_renderunit_register_write: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, new_val)); + + _mali_osk_mem_iowrite32(core->registers_mapped, relative_address, new_val); +} + + +void mali_core_renderunit_register_write_array(mali_core_renderunit *core, + u32 relative_address, + u32 * source_array, + u32 nr_of_regs) +{ + + u32 i; + MALI_DEBUG_PRINT(6, ("Core: renderunit_register_write_array: Core:%s Addr:0x%04X Nr_regs: %u\n", + core->description,relative_address, nr_of_regs)); + + /* Do not use burst writes against the registers */ + + for( i = 0; i< nr_of_regs; i++) + { + mali_core_renderunit_register_write(core, relative_address + i*4, source_array[i]); + } +} + +void mali_core_renderunit_timeout_function_hang_detection(void *arg) +{ + mali_bool action = MALI_FALSE; + mali_core_renderunit * core; + + core = (mali_core_renderunit *) arg; + if( !core ) return; + + /* if NOT idle OR has TIMED_OUT */ + if ( !((CORE_WATCHDOG_TIMEOUT == core->state ) || (CORE_IDLE== core->state)) ) + { + core->state = CORE_HANG_CHECK_TIMEOUT; + action = MALI_TRUE; + } + + if(action) _mali_osk_irq_schedulework(core->irq); +} + + +void mali_core_renderunit_timeout_function(void *arg) +{ + mali_core_renderunit * core; + mali_bool is_watchdog; + + core = (mali_core_renderunit *)arg; + if( !core ) return; + + is_watchdog = MALI_TRUE; + if (mali_benchmark) + { + /* poll based core */ + mali_core_job *job; + job = core->current_job; + if ( (NULL != job) && + (0 != _mali_osk_time_after(job->watchdog_jiffies,_mali_osk_time_tickcount())) + ) + { + core->state = CORE_POLL; + is_watchdog = MALI_FALSE; + } + } + + if (is_watchdog) + { + MALI_DEBUG_PRINT(3, ("SW-Watchdog timeout: Core:%s\n", core->description)); + core->state = CORE_WATCHDOG_TIMEOUT; + } + + _mali_osk_irq_schedulework(core->irq); +} + +/* Used by external renderunit_create<> function */ +_mali_osk_errcode_t mali_core_renderunit_init(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Core: renderunit_init: Core:%s\n", core->description)); + + _MALI_OSK_INIT_LIST_HEAD(&core->list) ; + core->timer = _mali_osk_timer_init(); + if (NULL == core->timer) + { + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot init timer\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + _mali_osk_timer_setcallback(core->timer, mali_core_renderunit_timeout_function, (void *)core); + + core->timer_hang_detection = _mali_osk_timer_init(); + if (NULL == core->timer_hang_detection) + { + _mali_osk_timer_term(core->timer); + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot init hang detection timer\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + _mali_osk_timer_setcallback(core->timer_hang_detection, mali_core_renderunit_timeout_function_hang_detection, (void *)core); + +#if USING_MALI_PMM + /* Init no pending power downs */ + core->pend_power_down = MALI_FALSE; + + /* Register the core with the PMM - which powers it up */ + if (_MALI_OSK_ERR_OK != malipmm_core_register( core->pmm_id )) + { + _mali_osk_timer_term(core->timer); + _mali_osk_timer_term(core->timer_hang_detection); + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot register with PMM\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif /* USING_MALI_PMM */ + + core->error_recovery = MALI_FALSE; + core->in_detach_function = MALI_FALSE; + core->state = CORE_IDLE; + core->current_job = NULL; + core->magic_nr = CORE_MAGIC_NR; +#if USING_MMU + core->mmu = NULL; +#endif /* USING_MMU */ + + MALI_SUCCESS; +} + +void mali_core_renderunit_term(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Core: renderunit_term: Core:%s\n", core->description)); + + if (NULL != core->timer) + { + _mali_osk_timer_term(core->timer); + core->timer = NULL; + } + if (NULL != core->timer_hang_detection) + { + _mali_osk_timer_term(core->timer_hang_detection); + core->timer_hang_detection = NULL; + } + +#if USING_MALI_PMM + /* Unregister the core with the PMM */ + malipmm_core_unregister( core->pmm_id ); +#endif +} + +/* Used by external renderunit_create<> function */ +_mali_osk_errcode_t mali_core_renderunit_map_registers(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_map_registers: Core:%s\n", core->description)) ; + if( (0 == core->registers_base_addr) || + (0 == core->size) || + (NULL == core->description) + ) + { + MALI_PRINT_ERROR(("Missing fields in the core structure %u %u 0x%x;\n", core->registers_base_addr, core->size, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(core->registers_base_addr, core->size, core->description)) + { + MALI_PRINT_ERROR(("Could not request register region (0x%08X - 0x%08X) to core: %s\n", + core->registers_base_addr, core->registers_base_addr + core->size - 1, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + else + { + MALI_DEBUG_PRINT(6, ("Success: request_mem_region: (0x%08X - 0x%08X) Core:%s\n", + core->registers_base_addr, core->registers_base_addr + core->size - 1, core->description)); + } + + core->registers_mapped = _mali_osk_mem_mapioregion( core->registers_base_addr, core->size, core->description ); + + if ( 0 == core->registers_mapped ) + { + MALI_PRINT_ERROR(("Could not ioremap registers for %s .\n", core->description)); + _mali_osk_mem_unreqregion(core->registers_base_addr, core->size); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + else + { + MALI_DEBUG_PRINT(6, ("Success: ioremap_nocache: Internal ptr: (0x%08X - 0x%08X) Core:%s\n", + (u32) core->registers_mapped, + ((u32)core->registers_mapped)+ core->size - 1, + core->description)); + } + + MALI_DEBUG_PRINT(4, ("Success: Mapping registers to core: %s\n",core->description)); + + MALI_SUCCESS; +} + +/* Used by external renderunit_create<> function + other places */ +void mali_core_renderunit_unmap_registers(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_unmap_registers: Core:%s\n", core->description)); + if (0 == core->registers_mapped) + { + MALI_PRINT_ERROR(("Trying to unmap register-mapping with NULL from core: %s\n", core->description)); + return; + } + _mali_osk_mem_unmapioregion(core->registers_base_addr, core->size, core->registers_mapped); + core->registers_mapped = 0; + _mali_osk_mem_unreqregion(core->registers_base_addr, core->size); +} + +static void mali_core_renderunit_irq_handler_remove(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_irq_handler_remove: Core:%s\n", core->description)); + _mali_osk_irq_term(core->irq); +} + +mali_core_renderunit * mali_core_renderunit_get_mali_core_nr(mali_core_subsystem *subsys, u32 mali_core_nr) +{ + mali_core_renderunit * core; + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + if (subsys->number_of_cores <= mali_core_nr) + { + MALI_PRINT_ERROR(("Trying to get illegal mali_core_nr: 0x%x for %s", mali_core_nr, subsys->name)); + return NULL; + } + core = (subsys->mali_core_array)[mali_core_nr]; + MALI_DEBUG_PRINT(6, ("Core: renderunit_get_mali_core_nr: Core:%s\n", core->description)); + MALI_CHECK_CORE(core); + return core; +} + +/* Is used by external function: + subsystem_startup<> */ +_mali_osk_errcode_t mali_core_subsystem_init(mali_core_subsystem* new_subsys) +{ + int i; + + /* These function pointers must have been set on before calling this function */ + if ( + ( NULL == new_subsys->name ) || + ( NULL == new_subsys->start_job ) || + ( NULL == new_subsys->irq_handler_upper_half ) || + ( NULL == new_subsys->irq_handler_bottom_half ) || + ( NULL == new_subsys->get_new_job_from_user ) || + ( NULL == new_subsys->return_job_to_user ) + ) + { + MALI_PRINT_ERROR(("Missing functions in subsystem.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(2, ("Core: subsystem_init: %s\n", new_subsys->name)) ; + + /* Catch use-before-initialize/use-after-terminate */ + MALI_DEBUG_ASSERT_POINTER( rendercores_global_mutex ); + + new_subsys->magic_nr = SUBSYSTEM_MAGIC_NR; + + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->renderunit_idle_head); /* Idle cores of this type */ + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->renderunit_off_head); /* Powered off cores of this type */ + + /* Linked list for each priority of sessions with a job ready for scheduleing */ + for(i=0; iawaiting_sessions_head[i]); + } + + /* Linked list of all sessions connected to this coretype */ + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->all_sessions_head); + + MALI_SUCCESS; +} + +#if USING_MMU +void mali_core_subsystem_attach_mmu(mali_core_subsystem* subsys) +{ + u32 i; + mali_core_renderunit * core; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + if ( NULL==core ) break; + core->mmu = mali_memory_core_mmu_lookup(core->mmu_id); + MALI_DEBUG_PRINT(2, ("Attach mmu: 0x%x to core: %s in subsystem: %s\n", core->mmu, core->description, subsys->name)); + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); +} +#endif + +/* This will register an IRQ handler, and add the core to the list of available cores for this subsystem. */ +_mali_osk_errcode_t mali_core_subsystem_register_renderunit(mali_core_subsystem* subsys, mali_core_renderunit * core) +{ + mali_core_renderunit ** mali_core_array; + u32 previous_nr; + u32 previous_size; + u32 new_nr; + u32 new_size; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + + /* If any of these are 0 there is an error */ + if(0 == core->subsystem || + 0 == core->registers_base_addr || + 0 == core->size || + 0 == core->description) + { + MALI_PRINT_ERROR(("Missing fields in the core structure 0x%x 0x%x 0x%x;\n", + core->registers_base_addr, core->size, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_DEBUG_PRINT(3, ("Core: subsystem_register_renderunit: %s\n", core->description)); + + MALI_CHECK_NON_NULL( + core->irq = _mali_osk_irq_init( + core->irq_nr, + mali_core_irq_handler_upper_half, + mali_core_irq_handler_bottom_half, + (_mali_osk_irq_trigger_t)subsys->probe_core_irq_trigger, + (_mali_osk_irq_ack_t)subsys->probe_core_irq_acknowledge, + core, + "mali_core_irq_handlers" + ), + _MALI_OSK_ERR_FAULT + ); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + /* Update which core number this is */ + core->core_number = subsys->number_of_cores; + + /* Update the array of cores in the subsystem. */ + previous_nr = subsys->number_of_cores; + previous_size = sizeof(mali_core_renderunit*)*previous_nr; + new_nr = previous_nr + 1; + new_size = sizeof(mali_core_renderunit*)*new_nr; + + if (0 != previous_nr) + { + if (NULL == subsys->mali_core_array) + { + MALI_PRINT_ERROR(("Internal error")); + goto exit_function; + } + + mali_core_array = (mali_core_renderunit **) _mali_osk_malloc( new_size ); + if (NULL == mali_core_array ) + { + MALI_PRINT_ERROR(("Out of mem")); + err = _MALI_OSK_ERR_NOMEM; + goto exit_function; + } + _mali_osk_memcpy(mali_core_array, subsys->mali_core_array, previous_size); + _mali_osk_free( subsys->mali_core_array); + MALI_DEBUG_PRINT(5, ("Success: adding a new core to subsystem array %s\n", core->description) ) ; + } + else + { + mali_core_array = (mali_core_renderunit **) _mali_osk_malloc( new_size ); + if (NULL == mali_core_array ) + { + MALI_PRINT_ERROR(("Out of mem")); + err = _MALI_OSK_ERR_NOMEM; + goto exit_function; + } + MALI_DEBUG_PRINT(6, ("Success: adding first core to subsystem array %s\n", core->description) ) ; + } + subsys->mali_core_array = mali_core_array; + mali_core_array[previous_nr] = core; + + /* Add the core to the list of available cores on the system */ + _mali_osk_list_add(&(core->list), &(subsys->renderunit_idle_head)); + + /* Update total number of cores */ + subsys->number_of_cores = new_nr; + MALI_DEBUG_PRINT(6, ("Success: mali_core_subsystem_register_renderunit %s\n", core->description)); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_SUCCESS; + +exit_function: + mali_core_renderunit_irq_handler_remove(core); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_ERROR(err); +} + + +/** + * Called by the core when a system info update is needed + * We fill in info about all the core types available + * @param subsys Pointer to the core's @a mali_core_subsystem data structure + * @param info Pointer to system info struct to update + * @return _MALI_OSK_ERR_OK on success, or another _mali_osk_errcode_t error code on failure + */ +_mali_osk_errcode_t mali_core_subsystem_system_info_fill(mali_core_subsystem* subsys, _mali_system_info* info) +{ + u32 i; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; /* OK if no cores to update info for */ + mali_core_renderunit * core; + _mali_core_info **core_info_nextp; + _mali_core_info * cinfo; + + MALI_DEBUG_PRINT(4, ("mali_core_subsystem_system_info_fill: %s\n", subsys->name) ) ; + + /* check input */ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + core_info_nextp = &(info->core_info); + cinfo = info->core_info; + + while(NULL!=cinfo) + { + core_info_nextp = &(cinfo->next); + cinfo = cinfo->next; + } + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + if ( NULL==core ) + { + err = _MALI_OSK_ERR_FAULT; + goto early_exit; + } + cinfo = (_mali_core_info *)_mali_osk_calloc(1, sizeof(_mali_core_info)); + if ( NULL==cinfo ) + { + err = _MALI_OSK_ERR_NOMEM; + goto early_exit; + } + cinfo->version = core->core_version; + cinfo->type =subsys->core_type; + cinfo->reg_address = core->registers_base_addr; + cinfo->core_nr = i; + cinfo->next = NULL; + /* Writing this address to the previous' *(&next) ptr */ + *core_info_nextp = cinfo; + /* Setting the next_ptr to point to &this->next_ptr */ + core_info_nextp = &(cinfo->next); + } +early_exit: + if ( _MALI_OSK_ERR_OK != err) MALI_PRINT_ERROR(("Error: In mali_core_subsystem_system_info_fill %d\n", err)); + MALI_DEBUG_CODE( + cinfo = info->core_info; + + MALI_DEBUG_PRINT(3, ("Current list of cores\n")); + while( NULL != cinfo ) + { + MALI_DEBUG_PRINT(3, ("Type: 0x%x\n", cinfo->type)); + MALI_DEBUG_PRINT(3, ("Version: 0x%x\n", cinfo->version)); + MALI_DEBUG_PRINT(3, ("Reg_addr: 0x%x\n", cinfo->reg_address)); + MALI_DEBUG_PRINT(3, ("Core_nr: 0x%x\n", cinfo->core_nr)); + MALI_DEBUG_PRINT(3, ("Flags: 0x%x\n", cinfo->flags)); + MALI_DEBUG_PRINT(3, ("*****\n")); + cinfo = cinfo->next; + } + ); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_ERROR(err); +} + + +/* Is used by external function: + subsystem_terminate<> */ +void mali_core_subsystem_cleanup(mali_core_subsystem* subsys) +{ + u32 i; + mali_core_renderunit * core; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + MALI_DEBUG_PRINT(2, ("Core: subsystem_cleanup: %s\n", subsys->name )) ; + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + +#if USING_MMU + if (NULL != core->mmu) + { + /* the MMU is attached in the load_complete callback, which will never be called if the module fails to load, handle that case */ + mali_memory_core_mmu_unregister_callback(core->mmu, mali_core_subsystem_callback_schedule_wrapper); + } +#endif + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + mali_core_renderunit_irq_handler_remove(core); + + /* When a process terminates, all cores running jobs from that process is reset and put to idle. + That means that when the module is unloading (this code) we are guaranteed that all cores are idle. + However: if something (we can't think of) is really wrong, a core may give an interrupt during this + unloading, and we may now in the code have a bottom-half-processing pending from the interrupts + we deregistered above. To be sure that the bottom halves do not access the structures after they + are deallocated we flush the bottom-halves processing here, before the deallocation. */ + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + +#if USING_MALI_PMM + /* Only reset when we are using PMM and the core is not off */ +#if MALI_PMM_NO_PMU + /* We need to reset when there is no PMU - but this will + * cause the register read/write functions to report an + * error (hence the if to check for CORE_OFF below) we + * change state to allow the reset to happen. + */ + core->state = CORE_IDLE; +#endif + if( core->state != CORE_OFF ) + { + subsys->reset_core( core, MALI_CORE_RESET_STYLE_DISABLE ); + } +#else + /* Always reset the core */ + subsys->reset_core( core, MALI_CORE_RESET_STYLE_DISABLE ); +#endif + + mali_core_renderunit_unmap_registers(core); + + _mali_osk_list_delinit(&core->list); + + mali_core_renderunit_term(core); + + subsys->renderunit_delete(core); + } + + mali_core_subsystem_cleanup_all_renderunits(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT(6, ("SUCCESS: mali_core_subsystem_cleanup: %s\n", subsys->name )) ; +} + +_mali_osk_errcode_t mali_core_subsystem_ioctl_number_of_cores_get(mali_core_session * session, u32 *number_of_cores) +{ + mali_core_subsystem * subsystem; + + subsystem = session->subsystem; + if ( NULL != number_of_cores ) + { + *number_of_cores = subsystem->number_of_cores; + } + + MALI_DEBUG_PRINT(4, ("Core: ioctl_number_of_cores_get: %s: %u\n", subsystem->name, *number_of_cores) ) ; + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_subsystem_ioctl_start_job(mali_core_session * session, void *job_data) +{ + mali_core_subsystem * subsystem; + _mali_osk_errcode_t err; + + /* need the subsystem to run callback function */ + subsystem = session->subsystem; + MALI_CHECK_NON_NULL(subsystem, _MALI_OSK_ERR_FAULT); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + err = subsystem->get_new_job_from_user(session, job_data); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_ERROR(err); +} + + +/* We return the version number to the first core in this subsystem */ +_mali_osk_errcode_t mali_core_subsystem_ioctl_core_version_get(mali_core_session * session, _mali_core_version *version) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit * core0; + u32 nr_return; + + subsystem = session->subsystem; + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + + core0 = mali_core_renderunit_get_mali_core_nr(subsystem, 0); + + if( NULL == core0 ) + { + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + nr_return = core0->core_version; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_DEBUG_PRINT(4, ("Core: ioctl_core_version_get: %s: %u\n", subsystem->name, nr_return )) ; + + *version = nr_return; + + MALI_SUCCESS; +} + +void mali_core_subsystem_ioctl_abort_job(mali_core_session * session, u32 id) +{ + find_and_abort(session, id); +} + +static mali_bool job_should_be_aborted(mali_core_job *job, u32 abort_id) +{ + if ( job->abort_id == abort_id ) return MALI_TRUE; + else return MALI_FALSE; +} + +static void find_and_abort(mali_core_session* session, u32 abort_id) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit *core; + mali_core_renderunit *tmp; + mali_core_job *job; + + subsystem = session->subsystem; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + job = session->job_waiting_to_run; + if ( (job!=NULL) && job_should_be_aborted (job, abort_id) ) + { + MALI_DEBUG_PRINT(3, ("Core: Aborting %s job, with id nr: %u, from the waiting_to_run slot.\n", subsystem->name, abort_id )); + session->job_waiting_to_run = NULL; + _mali_osk_list_delinit(&(session->awaiting_sessions_list)); + subsystem->awaiting_sessions_sum_all_priorities--; + subsystem->return_job_to_user( job , JOB_STATUS_END_ABORT); + } + + _MALI_OSK_LIST_FOREACHENTRY( core, tmp, &session->renderunits_working_head, mali_core_renderunit, list ) + { + job = core->current_job; + if ( (job!=NULL) && (job_should_be_aborted (job, abort_id) ) ) + { + MALI_DEBUG_PRINT(3, ("Core: Aborting %s job, with id nr: %u, which is currently running on mali.\n", subsystem->name, abort_id )); + if ( core->state==CORE_IDLE ) + { + MALI_PRINT_ERROR(("Aborting core with running job which is idle. Must be something very wrong.")); + goto end_bug; + } + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_ABORT); + } + } +end_bug: + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); +} + + +_mali_osk_errcode_t mali_core_subsystem_ioctl_suspend_response(mali_core_session * session, void *argument) +{ + mali_core_subsystem * subsystem; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + + /* need the subsystem to run callback function */ + subsystem = session->subsystem; + MALI_CHECK_NON_NULL(subsystem, _MALI_OSK_ERR_FAULT); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + if ( NULL != subsystem->suspend_response) + { + MALI_DEBUG_PRINT(4, ("MALI_IOC_CORE_CMD_SUSPEND_RESPONSE start\n")); + err = subsystem->suspend_response(session, argument); + MALI_DEBUG_PRINT(4, ("MALI_IOC_CORE_CMD_SUSPEND_RESPONSE end\n")); + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + return err; +} + + +/* Is used by internal function: + mali_core_subsystem_cleanup<>s */ +/* All cores should be removed before calling this function +Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_cleanup_all_renderunits(mali_core_subsystem* subsys) +{ + int i; + _mali_osk_free(subsys->mali_core_array); + subsys->number_of_cores = 0; + + MALI_DEBUG_PRINT(5, ("Core: subsystem_cleanup_all_renderunits: %s\n", subsys->name) ) ; + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + + if ( ! _mali_osk_list_empty(&(subsys->renderunit_idle_head))) + { + MALI_PRINT_ERROR(("List renderunit_list_idle should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->renderunit_idle_head)) ; + } + + if ( ! _mali_osk_list_empty(&(subsys->renderunit_off_head))) + { + MALI_PRINT_ERROR(("List renderunit_list_off should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->renderunit_off_head)) ; + } + + for(i=0; iawaiting_sessions_head[i]))) + { + MALI_PRINT_ERROR(("List awaiting_sessions_linkedlist should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->awaiting_sessions_head[i])) ; + subsys->awaiting_sessions_sum_all_priorities = 0; + } + } + + if ( ! _mali_osk_list_empty(&(subsys->all_sessions_head))) + { + MALI_PRINT_ERROR(("List all_sessions_linkedlist should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->all_sessions_head)) ; + } +} + +/* Is used by internal functions: + mali_core_irq_handler_bottom_half<>; + mali_core_subsystem_schedule<>; */ +/* Will release the core.*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_core_set_idle(mali_core_renderunit *core) +{ + mali_core_subsystem *subsystem; +#if USING_MALI_PMM + mali_core_status oldstatus; +#endif + subsystem = core->subsystem; + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(subsystem); + + _mali_osk_timer_del(core->timer); + _mali_osk_timer_del(core->timer_hang_detection); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_core_set_idle: %s\n", core->description) ) ; + + core->current_job = NULL ; + +#if USING_MALI_PMM + + oldstatus = core->state; + + if( core->pend_power_down ) + { + core->state = CORE_OFF ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_off_head ); + /* Done the move from the active queues, so the pending power down can be done */ + core->pend_power_down = MALI_FALSE; + malipmm_core_power_down_okay( core->pmm_id ); + } + else + { + core->state = CORE_IDLE ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_idle_head ); + } + + if( CORE_OFF != oldstatus ) + { + /* Message that this core is now idle or in fact off */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_FINISHED, + 0 }; + event.data = core->pmm_id; + _mali_ukk_pmm_event_message( &event ); +#if USING_MMU + /* Only free the reference when entering idle state from + * anything other than power off + */ + mali_memory_core_mmu_release_address_space_reference(core->mmu); +#endif /* USING_MMU */ + } + + +#else /* !USING_MALI_PMM */ + + core->state = CORE_IDLE ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_idle_head ); + +#if USING_MMU + mali_memory_core_mmu_release_address_space_reference(core->mmu); +#endif + +#endif /* USING_MALI_PMM */ +} + +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_set_working(mali_core_renderunit *core, mali_core_job *job) +{ + mali_core_subsystem *subsystem; + mali_core_session *session; + + session = job->session; + subsystem = core->subsystem; + + MALI_CHECK_CORE(core); + MALI_CHECK_JOB(job); + MALI_CHECK_SUBSYSTEM(subsystem); + + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_set_working: %s\n", core->description) ) ; + + core->current_job = job ; + core->state = CORE_WORKING ; + job->start_time_jiffies = _mali_osk_time_tickcount(); + _mali_osk_list_move( &core->list, &session->renderunits_working_head ); + +} + +#if USING_MALI_PMM + +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_core_set_off(mali_core_renderunit *core) +{ + mali_core_subsystem *subsystem; + subsystem = core->subsystem; + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(subsystem); + + /* Cores must be idle before powering off */ + MALI_DEBUG_ASSERT(core->state == CORE_IDLE); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_core_set_off: %s\n", core->description) ) ; + + core->current_job = NULL ; + core->state = CORE_OFF ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_off_head ); +} + +#endif /* USING_MALI_PMM */ + +/* Is used by internal function: + mali_core_subsystem_schedule<>; */ +/* Returns the job with the highest priority for the subsystem. NULL if none*/ +/* Must hold subsystem_mutex before entering this function */ +static mali_core_session * mali_core_subsystem_get_waiting_session(mali_core_subsystem *subsystem) +{ + int i; + + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + if ( 0 == subsystem->awaiting_sessions_sum_all_priorities ) + { + MALI_DEBUG_PRINT(5, ("Core: subsystem_get_waiting_job: No awaiting session found\n")); + return NULL; + } + + for( i=0; iawaiting_sessions_head[i])) + { + return _MALI_OSK_LIST_ENTRY(subsystem->awaiting_sessions_head[i].next, mali_core_session, awaiting_sessions_list); + } + } + + return NULL; +} + +static mali_core_job * mali_core_subsystem_release_session_get_job(mali_core_subsystem *subsystem, mali_core_session * session) +{ + mali_core_job *job; + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + _mali_osk_list_delinit(&session->awaiting_sessions_list); + subsystem->awaiting_sessions_sum_all_priorities--; + job = session->job_waiting_to_run; + session->job_waiting_to_run = NULL; + MALI_CHECK_JOB(job); + return job; +} + +/* Is used by internal functions: + mali_core_subsystem_schedule<> */ +/* This will start the job on the core. It will also release the core if it did not start.*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_job_start_on_core(mali_core_job *job, mali_core_renderunit *core) +{ + mali_core_session *session; + mali_core_subsystem *subsystem; + _mali_osk_errcode_t err; + session = job->session; + subsystem = core->subsystem; + + MALI_CHECK_CORE(core); + MALI_CHECK_JOB(job); + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_CHECK_SESSION(session); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(4, ("Core: job_start_on_core: job=0x%x, session=0x%x, core=%s\n", job, session, core->description)); + + MALI_DEBUG_ASSERT(NULL == core->current_job) ; + MALI_DEBUG_ASSERT(CORE_IDLE == core->state ); + + mali_core_subsystem_move_set_working(core, job); + +#if defined USING_MALI400_L2_CACHE + /* Invalidate the L2 cache */ + if (_MALI_OSK_ERR_OK != mali_kernel_l2_cache_invalidate_all() ) + { + MALI_DEBUG_PRINT(4, ("Core: Clear of L2 failed, return job. System may not be usable for some reason.\n")); + mali_core_subsystem_move_core_set_idle(core); + subsystem->return_job_to_user(job,JOB_STATUS_END_SYSTEM_UNUSABLE ); + return; + } +#endif + + /* Tries to start job on the core. Returns MALI_FALSE if the job could not be started */ + err = subsystem->start_job(job, core); + +#if MALI_GPU_UTILIZATION + mali_utilization_core_start(); +#endif + + if ( _MALI_OSK_ERR_OK != err ) + { + /* This will happen only if there is something in the job object + which make it inpossible to start. Like if it require illegal memory.*/ + MALI_DEBUG_PRINT(4, ("Core: start_job failed, return job and putting core back into idle list\n")); + mali_core_subsystem_move_core_set_idle(core); + subsystem->return_job_to_user(job,JOB_STATUS_END_ILLEGAL_JOB ); + } + else + { + u32 delay = _mali_osk_time_mstoticks(job->watchdog_msecs)+1; + job->watchdog_jiffies = _mali_osk_time_tickcount() + delay; + if (mali_benchmark) + { + _mali_osk_timer_add(core->timer, 1); + } + else + { + _mali_osk_timer_add(core->timer, delay); + } + } +} + +#if USING_MMU +static void mali_core_subsystem_callback_schedule_wrapper(void* sub) +{ + mali_core_subsystem * subsystem; + subsystem = (mali_core_subsystem *)sub; + MALI_DEBUG_PRINT(3, ("MMU: Is schedulling subsystem: %s\n", subsystem->name)); + mali_core_subsystem_schedule(subsystem); +} +#endif + +/* Is used by internal function: + mali_core_irq_handler_bottom_half + mali_core_session_add_job +*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_schedule(mali_core_subsystem * subsystem) +{ + mali_core_renderunit *core, *tmp; + mali_core_session *session; + mali_core_job *job; + + MALI_DEBUG_PRINT(5, ("Core: subsystem_schedule: %s\n", subsystem->name )) ; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + /* First check that there are sessions with jobs waiting to run */ + if ( 0 == subsystem->awaiting_sessions_sum_all_priorities) + { + MALI_DEBUG_PRINT(6, ("Core: No jobs available for %s\n", subsystem->name) ) ; + return; + } + + /* Returns the session with the highest priority job for the subsystem. NULL if none*/ + session = mali_core_subsystem_get_waiting_session(subsystem); + + if (NULL == session) + { + MALI_DEBUG_PRINT(6, ("Core: Schedule: No runnable job found\n")); + return; + } + + _MALI_OSK_LIST_FOREACHENTRY(core, tmp, &subsystem->renderunit_idle_head, mali_core_renderunit, list) + { +#if USING_MMU + int err = mali_memory_core_mmu_activate_page_table(core->mmu, session->mmu_session, mali_core_subsystem_callback_schedule_wrapper, subsystem); + if (0 == err) + { + /* core points to a core where the MMU page table activation succeeded */ +#endif + /* This will remove the job from queue system */ + job = mali_core_subsystem_release_session_get_job(subsystem, session); + MALI_DEBUG_ASSERT_POINTER(job); + + MALI_DEBUG_PRINT(6, ("Core: Schedule: Got a job 0x%x\n", job)); + +#if USING_MALI_PMM + { + /* Message that there is a job scheduled to run + * NOTE: mali_core_job_start_on_core() can fail to start + * the job for several reasons, but it will move the core + * back to idle which will create the FINISHED message + * so we can still say that the job is SCHEDULED + */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_SCHEDULED, + 0 }; + event.data = core->pmm_id; + _mali_ukk_pmm_event_message( &event ); + } +#endif + /* This will {remove core from freelist AND start the job on the core}*/ + mali_core_job_start_on_core(job, core); + + MALI_DEBUG_PRINT(6, ("Core: Schedule: Job started, done\n")); + return; +#if USING_MMU + } +#endif + } + MALI_DEBUG_PRINT(6, ("Core: Schedule: Could not activate MMU. Scheduelling postponed to MMU, checking next.\n")); + +#if USING_MALI_PMM + { + /* Message that there are jobs to run */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_QUEUED, + 0 }; + if( subsystem->core_type == _MALI_GP2 || subsystem->core_type == _MALI_400_GP ) + { + event.data = MALI_PMM_CORE_GP; + } + else + { + /* Check the PP is supported by the PMM */ + MALI_DEBUG_ASSERT( subsystem->core_type == _MALI_200 || subsystem->core_type == _MALI_400_PP ); + /* We state that all PP cores are scheduled to inform the PMM + * that it may need to power something up! + */ + event.data = MALI_PMM_CORE_PP_ALL; + } + _mali_ukk_pmm_event_message( &event ); + } +#endif /* USING_MALI_PMM */ + +} + +/* Is used by external function: + session_begin<> */ +void mali_core_session_begin(mali_core_session * session) +{ + mali_core_subsystem * subsystem; + + subsystem = session->subsystem; + if ( NULL == subsystem ) + { + MALI_PRINT_ERROR(("Missing data in struct\n")); + return; + } + MALI_DEBUG_PRINT(2, ("Core: session_begin: for %s\n", session->subsystem->name )) ; + + session->magic_nr = SESSION_MAGIC_NR; + + _MALI_OSK_INIT_LIST_HEAD(&session->renderunits_working_head); + + session->job_waiting_to_run = NULL; + _MALI_OSK_INIT_LIST_HEAD(&session->awaiting_sessions_list); + _MALI_OSK_INIT_LIST_HEAD(&session->all_sessions_list); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + _mali_osk_list_add(&session->all_sessions_list, &session->subsystem->all_sessions_head); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: session_begin: for %s DONE\n", session->subsystem->name) ) ; +} + +#if USING_MMU +static void mali_core_renderunit_stop_bus(mali_core_renderunit* core) +{ + core->subsystem->stop_bus(core); +} +#endif + +void mali_core_session_close(mali_core_session * session) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit *core; + + subsystem = session->subsystem; + MALI_DEBUG_ASSERT_POINTER(subsystem); + + MALI_DEBUG_PRINT(2, ("Core: session_close: for %s\n", session->subsystem->name) ) ; + + /* We must grab subsystem mutex since the list this session belongs to + is owned by the subsystem */ + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + /* Return the potensial waiting job to user */ + if ( session->job_waiting_to_run ) + { + subsystem->return_job_to_user( session->job_waiting_to_run, JOB_STATUS_END_SHUTDOWN ); + session->job_waiting_to_run = NULL; + _mali_osk_list_delinit(&(session->awaiting_sessions_list)); + subsystem->awaiting_sessions_sum_all_priorities--; + } + + /* Kill active cores working for this session - freeing their jobs + Since the handling of one core also could stop jobs from another core, there is a while loop */ + while ( ! _mali_osk_list_empty(&session->renderunits_working_head) ) + { + core = _MALI_OSK_LIST_ENTRY(session->renderunits_working_head.next, mali_core_renderunit, list); + MALI_DEBUG_PRINT(3, ("Core: session_close: Core was working: %s\n", core->description )) ; + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_SHUTDOWN ); + break; + } + _MALI_OSK_INIT_LIST_HEAD(&session->renderunits_working_head); /* Not necessary - we will _mali_osk_free session*/ + + /* Remove this session from the global sessionlist */ + _mali_osk_list_delinit(&session->all_sessions_list); + + MALI_DEBUG_PRINT(5, ("Core: session_close: for %s FINISHED\n", session->subsystem->name )) ; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); +} + +/* Must hold subsystem_mutex before entering this function */ +_mali_osk_errcode_t mali_core_session_add_job(mali_core_session * session, mali_core_job *job, mali_core_job **job_return) +{ + mali_core_subsystem * subsystem; + + job->magic_nr = JOB_MAGIC_NR; + MALI_CHECK_SESSION(session); + + subsystem = session->subsystem; + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: session_add_job: for %s\n", subsystem->name )) ; + + /* Setting the default value; No job to return */ + MALI_DEBUG_ASSERT_POINTER(job_return); + *job_return = NULL; + + if ( NULL != session->job_waiting_to_run) + { + MALI_DEBUG_PRINT(5, ("The session already had a job waiting\n")) ; + /* Checing if the new job has a higher priority than the one that was pending.*/ + if ( job_has_higher_priority(job,session->job_waiting_to_run)) + { + /* Remove this session from current priority */ + _mali_osk_list_del( &(session->awaiting_sessions_list)); + subsystem->awaiting_sessions_sum_all_priorities--; + /* Returning the previous waiting job through the input double pointer*/ + *job_return = session->job_waiting_to_run; + } + else + { + MALI_PRINT_ERROR(("Illegal internal state.")); + /* There was a job waiting in this session, and the priority of this job + we try to add was NOT higher. Return -1 indicated new job NOT enqueued.*/ + /* We check prior to calling this function that we are not in this state.*/ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + } + /* Continue to add the new job as the next job from this session */ + MALI_DEBUG_PRINT(6, ("Core: session_add_job job=0x%x\n", job)); + + /* Adding this session to the subsystem list of sessions with pending job, with priority */ + session->job_waiting_to_run = job; + + _mali_osk_list_addtail( &(session->awaiting_sessions_list), &(subsystem->awaiting_sessions_head[job->priority])); + subsystem->awaiting_sessions_sum_all_priorities++; + + mali_core_subsystem_schedule(subsystem); + + MALI_DEBUG_PRINT(6, ("Core: session_add_job: for %s FINISHED\n", session->subsystem->name )) ; + + MALI_SUCCESS; +} + +static void mali_core_job_set_run_time(mali_core_job * job) +{ + u32 jiffies_used; + jiffies_used = _mali_osk_time_tickcount() - job->start_time_jiffies; + if ( jiffies_used > JOB_MAX_JIFFIES ) + { + MALI_PRINT_ERROR(("Job used too many jiffies: %d\n", jiffies_used )); + jiffies_used = 0; + } + job->render_time_msecs = _mali_osk_time_tickstoms(jiffies_used); +} + +static void mali_core_renderunit_detach_job_from_core(mali_core_renderunit* core, mali_subsystem_reschedule_option reschedule, mali_subsystem_job_end_code end_status) +{ + mali_core_job * job; + mali_core_subsystem * subsystem; + mali_bool already_in_detach_function; + + job = core->current_job; + subsystem = core->subsystem; + MALI_DEBUG_ASSERT(CORE_IDLE != core->state); + + /* The reset_core() called some lines below might call this detach + * funtion again. To protect the core object from being modified by + * recursive calls, the in_detach_function would track if it is an recursive call + */ + already_in_detach_function = core->in_detach_function; + + if ( MALI_FALSE == already_in_detach_function ) + { + core->in_detach_function = MALI_TRUE; + if ( NULL != job ) + { + mali_core_job_set_run_time(job); + core->current_job = NULL; + } + } + + if (JOB_STATUS_END_SEG_FAULT == end_status) + { + subsystem->reset_core( core, MALI_CORE_RESET_STYLE_HARD ); + } + else + { + subsystem->reset_core( core, MALI_CORE_RESET_STYLE_RUNABLE ); + } + + if ( MALI_FALSE == already_in_detach_function ) + { + if ( CORE_IDLE != core->state ) + { + #if MALI_GPU_UTILIZATION + mali_utilization_core_end(); + #endif + mali_core_subsystem_move_core_set_idle(core); + } + + core->in_detach_function = MALI_FALSE; + + if ( SUBSYSTEM_RESCHEDULE == reschedule ) + { + mali_core_subsystem_schedule(subsystem); + } + if ( NULL != job ) + { + core->subsystem->return_job_to_user(job, end_status); + } + } +} + +#if USING_MMU +/* This function intentionally does not release the semaphore. You must run + stop_bus_for_all_cores(), reset_all_cores_on_mmu() and continue_job_handling() + after calling this function, and then call unlock_subsystem() to release the + semaphore. */ + +static void lock_subsystem(struct mali_core_subsystem * subsys) +{ + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. + + This function only stops cores behind the given MMU, unless "mmu" is NULL, in + which case all cores are stopped. +*/ +static void stop_bus_for_all_cores_on_mmu(struct mali_core_subsystem * subsys, void* mmu) +{ + u32 i; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_DEBUG_PRINT(2,("Handling: bus stop %s\n", subsys->name )); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + + /* We stop only cores behind the given MMU, unless MMU is NULL */ + if ( (NULL!=mmu) && (core->mmu != mmu) ) continue; + + if ( CORE_IDLE != core->state ) + { + MALI_DEBUG_PRINT(4, ("Stopping bus on core %s\n", core->description)); + mali_core_renderunit_stop_bus(core); + core->error_recovery = MALI_TRUE; + } + else + { + MALI_DEBUG_PRINT(4,("Core: not active %s\n", core->description )); + } + } + /* Mutex is still being held, to prevent things to happen while we do cleanup */ + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. + + This function only resets cores behind the given MMU, unless "mmu" is NULL, in + which case all cores are reset. +*/ +static void reset_all_cores_on_mmu(struct mali_core_subsystem * subsys, void* mmu) +{ + u32 i; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_DEBUG_PRINT(3, ("Handling: reset cores from mmu: 0x%x on %s\n", mmu, subsys->name )); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + + /* We reset only cores behind the given MMU, unless MMU is NULL */ + if ( (NULL!=mmu) && (core->mmu != mmu) ) continue; + + if ( CORE_IDLE != core->state ) + { + MALI_DEBUG_PRINT(4, ("Abort and reset core: %s\n", core->description )); + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_WAIT, JOB_STATUS_END_SEG_FAULT); + } + else + { + MALI_DEBUG_PRINT(4, ("Core: not active %s\n", core->description )); + } + } + MALI_DEBUG_PRINT(4, ("Handling: done %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. */ +static void continue_job_handling(struct mali_core_subsystem * subsys) +{ + u32 i, j; + + MALI_DEBUG_PRINT(3, ("Handling: Continue: %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + core->error_recovery = MALI_FALSE; + } + + i = subsys->number_of_cores; + j = subsys->awaiting_sessions_sum_all_priorities; + + /* Schedule MIN(nr_waiting_jobs , number of cores) times */ + while( i-- && j--) + { + mali_core_subsystem_schedule(subsys); + } + MALI_DEBUG_PRINT(4, ("Handling: done %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* Unlock the subsystem. */ +static void unlock_subsystem(struct mali_core_subsystem * subsys) +{ + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); +} + +void mali_core_subsystem_broadcast_notification(struct mali_core_subsystem * subsys, mali_core_notification_message message, u32 data) +{ + void * mmu; + mmu = (void*) data; + + switch(message) + { + case MMU_KILL_STEP0_LOCK_SUBSYSTEM: + break; + case MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES: + stop_bus_for_all_cores_on_mmu(subsys, mmu); + break; + case MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS: + reset_all_cores_on_mmu(subsys, mmu ); + break; + case MMU_KILL_STEP3_CONTINUE_JOB_HANDLING: + continue_job_handling(subsys); + break; + case MMU_KILL_STEP4_UNLOCK_SUBSYSTEM: + break; + + default: + MALI_PRINT_ERROR(("Illegal message: 0x%x, data: 0x%x\n", (u32)message, data)); + break; + } +} +#endif /* USING_MMU */ + +void job_watchdog_set(mali_core_job * job, u32 watchdog_msecs) +{ + if (watchdog_msecs == 0) job->watchdog_msecs = mali_max_job_runtime; /* use the default */ + else if (watchdog_msecs > WATCHDOG_MSECS_MAX) job->watchdog_msecs = WATCHDOG_MSECS_MAX; /* no larger than max */ + else if (watchdog_msecs < WATCHDOG_MSECS_MIN) job->watchdog_msecs = WATCHDOG_MSECS_MIN; /* not below min */ + else job->watchdog_msecs = watchdog_msecs; +} + +u32 mali_core_hang_check_timeout_get(void) +{ + /* check the value. The user might have set the value outside the allowed range */ + if (mali_hang_check_interval > HANG_CHECK_MSECS_MAX) mali_hang_check_interval = HANG_CHECK_MSECS_MAX; /* cap to max */ + else if (mali_hang_check_interval < HANG_CHECK_MSECS_MIN) mali_hang_check_interval = HANG_CHECK_MSECS_MIN; /* cap to min */ + + /* return the active value */ + return mali_hang_check_interval; +} + +static _mali_osk_errcode_t mali_core_irq_handler_upper_half (void * data) +{ + mali_core_renderunit *core; + u32 has_pending_irq; + + core = (mali_core_renderunit * )data; + + if ( (NULL == core) || + (NULL == core->subsystem) || + (NULL == core->subsystem->irq_handler_upper_half) ) + { + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(core->subsystem); + + has_pending_irq = core->subsystem->irq_handler_upper_half(core); + + if ( has_pending_irq ) + { + _mali_osk_irq_schedulework( core->irq ) ; + MALI_SUCCESS; + } + + if (mali_benchmark) MALI_SUCCESS; + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +static void mali_core_irq_handler_bottom_half ( void *data ) +{ + mali_core_renderunit *core; + mali_core_subsystem* subsystem; + + mali_subsystem_job_end_code job_status; + + core = (mali_core_renderunit * )data; + + MALI_CHECK_CORE(core); + subsystem = core->subsystem; + MALI_CHECK_SUBSYSTEM(subsystem); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + if ( CORE_IDLE == core->state ) goto end_function; + + MALI_DEBUG_PRINT(5, ("IRQ: handling irq from core %s\n", core->description )) ; + + _mali_osk_cache_flushall(); + + /* This function must also update the job status flag */ + job_status = subsystem->irq_handler_bottom_half( core ); + + /* Retval is nonzero if the job is finished. */ + if ( JOB_STATUS_CONTINUE_RUN != job_status ) + { + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, job_status); + } + else + { + switch ( core->state ) + { + case CORE_WATCHDOG_TIMEOUT: + MALI_DEBUG_PRINT(2, ("Watchdog SW Timeout of job from core: %s\n", core->description )); + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_TIMEOUT_SW ); + break; + + case CORE_POLL: + MALI_DEBUG_PRINT(5, ("Poll core: %s\n", core->description )) ; + core->state = CORE_WORKING; + _mali_osk_timer_add( core->timer, 1); + break; + + default: + MALI_DEBUG_PRINT(4, ("IRQ: The job on the core continue to run: %s\n", core->description )) ; + break; + } + } +end_function: + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); +} + +void subsystem_flush_mapped_mem_cache(void) +{ + _mali_osk_cache_flushall(); + _mali_osk_mem_barrier(); +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t mali_core_subsystem_signal_power_down(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool immediate_only) +{ + mali_core_renderunit * core = NULL; + + MALI_CHECK_SUBSYSTEM(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + /* It is possible that this signal funciton can be called during a driver exit, + * and so the requested core may now be destroyed. (This is due to us not having + * the subsys lock before signalling power down). + * mali_core_renderunit_get_mali_core_nr() will report a Mali ERR because + * the core number is out of range (which is a valid error in other cases). + * So instead we check here (now that we have the subsys lock) and let the + * caller cope with the core get failure and check that the core has + * been unregistered in the PMM as part of its destruction. + */ + if ( subsys->number_of_cores > mali_core_nr ) + { + core = mali_core_renderunit_get_mali_core_nr(subsys, mali_core_nr); + } + + if ( NULL == core ) + { + /* Couldn't find the core */ + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 5, ("Core: Failed to find core to power down\n") ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + else if ( core->state != CORE_IDLE ) + { + /* When powering down we either set a pending power down flag here so we + * can power down cleanly after the job completes or we don't set the + * flag if we have been asked to only do a power down right now + * In either case, return that the core is busy + */ + if ( !immediate_only ) core->pend_power_down = MALI_TRUE; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 5, ("Core: No idle core to power down\n") ); + MALI_ERROR(_MALI_OSK_ERR_BUSY); + } + + /* Shouldn't have a pending power down flag set */ + MALI_DEBUG_ASSERT( !core->pend_power_down ); + + /* Move core to off queue */ + mali_core_subsystem_move_core_set_off(core); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_subsystem_signal_power_up(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool queue_only) +{ + mali_core_renderunit * core; + + MALI_CHECK_SUBSYSTEM(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + core = mali_core_renderunit_get_mali_core_nr(subsys, mali_core_nr); + + if( core == NULL ) + { + /* Couldn't find the core */ + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 5, ("Core: Failed to find core to power up\n") ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + else if( core->state != CORE_OFF ) + { + /* This will usually happen because we are trying to cancel a pending power down */ + core->pend_power_down = MALI_FALSE; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 5, ("Core: No powered off core to power up (cancelled power down?)\n") ); + MALI_ERROR(_MALI_OSK_ERR_BUSY); + } + + /* Shouldn't have a pending power down set */ + MALI_DEBUG_ASSERT( !core->pend_power_down ); + + /* Move core to idle queue */ + mali_core_subsystem_move_core_set_idle(core); + + if( !queue_only ) + { + /* Reset MMU & core - core must be idle to allow this */ +#if USING_MMU + if ( NULL!=core->mmu ) + { +#if defined(USING_MALI200) + if (core->pmm_id != MALI_PMM_CORE_PP0) + { +#endif + mali_kernel_mmu_reset(core->mmu); +#if defined(USING_MALI200) + } +#endif + + } +#endif /* USING_MMU */ + subsys->reset_core( core, MALI_CORE_RESET_STYLE_RUNABLE ); + } + + /* Need to schedule work to start on this core */ + mali_core_subsystem_schedule(subsys); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + MALI_SUCCESS; +} + +#endif /* USING_MALI_PMM */ + +#if MALI_STATE_TRACKING +void mali_core_renderunit_dump_state(mali_core_subsystem* subsystem) +{ + u32 i; + mali_core_renderunit *core; + mali_core_renderunit *tmp_core; + + mali_core_session* session; + mali_core_session* tmp_session; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + MALI_PRINT(("Subsystem;\n")); + MALI_PRINT((" Name: %s\n", subsystem->name)); + + for (i = 0; i < subsystem->number_of_cores; i++) + { + MALI_PRINT((" Core: #%u\n", subsystem->mali_core_array[i]->core_number)); + MALI_PRINT((" Description: %s\n", subsystem->mali_core_array[i]->description)); + switch(subsystem->mali_core_array[i]->state) + { + case CORE_IDLE: + MALI_PRINT((" State: CORE_IDLE\n")); + break; + case CORE_WORKING: + MALI_PRINT((" State: CORE_WORKING\n")); + break; + case CORE_WATCHDOG_TIMEOUT: + MALI_PRINT((" State: CORE_WATCHDOG_TIMEOUT\n")); + break; + case CORE_POLL: + MALI_PRINT((" State: CORE_POLL\n")); + break; + case CORE_HANG_CHECK_TIMEOUT: + MALI_PRINT((" State: CORE_HANG_CHECK_TIMEOUT\n")); + break; + case CORE_OFF: + MALI_PRINT((" State: CORE_OFF\n")); + break; + default: + MALI_PRINT((" State: Unknown (0x%X)\n", subsystem->mali_core_array[i]->state)); + break; + } + MALI_PRINT((" Current job: 0x%x\n", (u32)(subsystem->mali_core_array[i]->current_job))); + MALI_PRINT((" Core version: 0x%x\n", subsystem->mali_core_array[i]->core_version)); +#if USING_MALI_PMM + MALI_PRINT((" PMM id: 0x%x\n", subsystem->mali_core_array[i]->pmm_id)); + MALI_PRINT((" Power down requested: %s\n", subsystem->mali_core_array[i]->pend_power_down ? "TRUE" : "FALSE")); +#endif + } + + MALI_PRINT((" Cores on idle list:\n")); + _MALI_OSK_LIST_FOREACHENTRY(core, tmp_core, &subsystem->renderunit_idle_head, mali_core_renderunit, list) + { + MALI_PRINT((" Core #%u\n", core->core_number)); + } + + MALI_PRINT((" Cores on off list:\n")); + _MALI_OSK_LIST_FOREACHENTRY(core, tmp_core, &subsystem->renderunit_off_head, mali_core_renderunit, list) + { + MALI_PRINT((" Core #%u\n", core->core_number)); + } + + MALI_PRINT((" Connected sessions:\n")); + _MALI_OSK_LIST_FOREACHENTRY(session, tmp_session, &subsystem->all_sessions_head, mali_core_session, all_sessions_list) + { + MALI_PRINT((" Session 0x%X:\n", (u32)session)); + MALI_PRINT((" Waiting job: 0x%X\n", (u32)session->job_waiting_to_run)); + MALI_PRINT((" Notification queue: %s\n", _mali_osk_notification_queue_is_empty(session->notification_queue) ? "EMPTY" : "NON-EMPTY")); + } + + MALI_PRINT((" Waiting sessions sum all priorities: %u\n", subsystem->awaiting_sessions_sum_all_priorities)); + for (i = 0; i < PRIORITY_LEVELS; i++) + { + MALI_PRINT((" Waiting sessions with priority %u:\n", i)); + _MALI_OSK_LIST_FOREACHENTRY(session, tmp_session, &subsystem->awaiting_sessions_head[i], mali_core_session, awaiting_sessions_list) + { + MALI_PRINT((" Session 0x%X:\n", (u32)session)); + MALI_PRINT((" Waiting job: 0x%X\n", (u32)session->job_waiting_to_run)); + MALI_PRINT((" Notification queue: %s\n", _mali_osk_notification_queue_is_empty(session->notification_queue) ? "EMPTY" : "NON-EMPTY")); + } + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.h new file mode 100644 index 00000000000..94bce94d4e1 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_rendercore.h @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_RENDERCORE_H__ +#define __MALI_RENDERCORE_H__ + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#define PRIORITY_LEVELS 3 +#define PRIORITY_MAX 0 +#define PRIORITY_MIN (PRIORITY_MAX+PRIORITY_LEVELS-1) + +/* This file contains what we need in kernel for all core types. */ + +typedef enum +{ + CORE_IDLE, /**< Core is ready for a new job */ + CORE_WORKING, /**< Core is working on a job */ + CORE_WATCHDOG_TIMEOUT, /**< Core is working but it has timed out */ + CORE_POLL, /**< Poll timer triggered, pending handling */ + CORE_HANG_CHECK_TIMEOUT,/**< Timeout for hang detection */ + CORE_OFF /**< Core is powered off */ +} mali_core_status; + +typedef enum +{ + SUBSYSTEM_RESCHEDULE, + SUBSYSTEM_WAIT +} mali_subsystem_reschedule_option; + +typedef enum +{ + MALI_CORE_RESET_STYLE_RUNABLE, + MALI_CORE_RESET_STYLE_DISABLE, + MALI_CORE_RESET_STYLE_HARD +} mali_core_reset_style; + +typedef enum +{ + JOB_STATUS_CONTINUE_RUN = 0x01, + JOB_STATUS_END_SUCCESS = 1<<(16+0), + JOB_STATUS_END_OOM = 1<<(16+1), + JOB_STATUS_END_ABORT = 1<<(16+2), + JOB_STATUS_END_TIMEOUT_SW = 1<<(16+3), + JOB_STATUS_END_HANG = 1<<(16+4), + JOB_STATUS_END_SEG_FAULT = 1<<(16+5), + JOB_STATUS_END_ILLEGAL_JOB = 1<<(16+6), + JOB_STATUS_END_UNKNOWN_ERR = 1<<(16+7), + JOB_STATUS_END_SHUTDOWN = 1<<(16+8), + JOB_STATUS_END_SYSTEM_UNUSABLE = 1<<(16+9) +} mali_subsystem_job_end_code; + + +struct mali_core_job; +struct mali_core_subsystem; +struct mali_core_renderunit; +struct mali_core_session; + +/* We have one of these subsystems for each core type */ +typedef struct mali_core_subsystem +{ + struct mali_core_renderunit ** mali_core_array; /* An array of all cores of this type */ + u32 number_of_cores; /* Number of cores in this list */ + + _mali_core_type core_type; + + u32 magic_nr; + + _mali_osk_list_t renderunit_idle_head; /* Idle cores of this type */ + _mali_osk_list_t renderunit_off_head; /* Powered off cores of this type */ + + /* Linked list for each priority of sessions with a job ready for scheduelling */ + _mali_osk_list_t awaiting_sessions_head[PRIORITY_LEVELS]; + u32 awaiting_sessions_sum_all_priorities; + + /* Linked list of all sessions connected to this coretype */ + _mali_osk_list_t all_sessions_head; + + /* Linked list of all sessions connected to this coretype */ + struct _mali_osk_notification_queue_t * notification_queue; + + const char * name; + mali_kernel_subsystem_identifier id; + + /**** Functions registered for this core type. Set during mali_core_init ******/ + /* Start this job on this core. Return MALI_TRUE if the job was started. */ + _mali_osk_errcode_t (*start_job)(struct mali_core_job * job, struct mali_core_renderunit * core); + + /* Check if given core has an interrupt pending. Return MALI_TRUE and set mask to 0 if pending */ + u32 (*irq_handler_upper_half)(struct mali_core_renderunit * core); + + /* This function should check if the interrupt indicates that job was finished. + If so it should update the job-struct, reset the core registers, and return MALI_TRUE, . + If the job is still working after this function it should return MALI_FALSE. + The function must also enable the bits in the interrupt mask for the core. + Called by the bottom half interrupt function. */ + int (*irq_handler_bottom_half)(struct mali_core_renderunit* core); + + /* This function is called from the ioctl function and should return a mali_core_job pointer + to a created mali_core_job object with the data given from userspace */ + _mali_osk_errcode_t (*get_new_job_from_user)(struct mali_core_session * session, void * argument); + + _mali_osk_errcode_t (*suspend_response)(struct mali_core_session * session, void * argument); + + /* This function is called from the ioctl function and should write the necessary data + to userspace telling which job was finished and the status and debuginfo for this job. + The function must also free and cleanup the input job object. */ + void (*return_job_to_user)(struct mali_core_job * job, mali_subsystem_job_end_code end_status); + + /* Is called when a subsystem shuts down. This function needs to + release internal pointers in the core struct, and free the + core struct before returning. + It is not allowed to write to any registers, since this + unmapping is already done. */ + void (*renderunit_delete)(struct mali_core_renderunit * core); + + /* Is called when we want to abort a job that is running on the core. + This is done if program exits while core is running */ + void (*reset_core)(struct mali_core_renderunit * core, mali_core_reset_style style); + + /* Is called when the rendercore wants the core to give an interrupt */ + void (*probe_core_irq_trigger)(struct mali_core_renderunit* core); + + /* Is called when the irq probe wants the core to acknowledge an interrupt from the hw */ + _mali_osk_errcode_t (*probe_core_irq_acknowledge)(struct mali_core_renderunit* core); + + /* Called when the rendercore want to issue a bus stop request to a core */ + void (*stop_bus)(struct mali_core_renderunit* core); +} mali_core_subsystem; + + +/* Per core data. This must be embedded into each core type internal core info. */ +typedef struct mali_core_renderunit +{ + struct mali_core_subsystem * subsystem; /* The core belongs to this subsystem */ + _mali_osk_list_t list; /* Is always in subsystem->idle_list OR session->renderunits_working */ + mali_core_status state; + mali_bool error_recovery; /* Indicates if the core is waiting for external help to recover (typically the MMU) */ + mali_bool in_detach_function; + struct mali_core_job * current_job; /* Current job being processed on this core ||NULL */ + u32 magic_nr; + _mali_osk_timer_t * timer; + _mali_osk_timer_t * timer_hang_detection; + + mali_io_address registers_mapped; /* IO-mapped pointer to registers */ + u32 registers_base_addr; /* Base addres of the registers */ + u32 size; /* The size of registers_mapped */ + const char * description; /* Description of this core. */ + u32 irq_nr; /* The IRQ nr for this core */ + u32 core_version; +#if USING_MMU + u32 mmu_id; + void * mmu; /* The MMU this rendercore is behind.*/ +#endif +#if USING_MALI_PMM + mali_pmm_core_id pmm_id; /* The PMM core id */ + mali_bool pend_power_down; /* Power down is requested */ +#endif + + u32 core_number; /* 0 for first detected core of this type, 1 for second and so on */ + + _mali_osk_irq_t *irq; +} mali_core_renderunit; + + +/* Per open FILE data. */ +/* You must held subsystem->mutex before any transactions to this datatype. */ +typedef struct mali_core_session +{ + struct mali_core_subsystem * subsystem; /* The session belongs to this subsystem */ + _mali_osk_list_t renderunits_working_head; /* List of renderunits working for this session */ + struct mali_core_job *job_waiting_to_run; /* The next job from this session to run */ + + _mali_osk_list_t awaiting_sessions_list; /* Linked list of sessions with jobs, for each priority */ + _mali_osk_list_t all_sessions_list; /* Linked list of all sessions on the system. */ + + _mali_osk_notification_queue_t * notification_queue; /* Messages back to Base in userspace*/ +#if USING_MMU + struct mali_session_data * mmu_session; /* The session associated with the MMU page tables for this core */ +#endif + u32 magic_nr; +} mali_core_session; + + +/* This must be embedded into a specific mali_core_job struct */ +/* use this macro to get spesific mali_core_job: container_of(ptr, type, member)*/ +typedef struct mali_core_job +{ + _mali_osk_list_t list; /* Linked list of jobs. Used by struct mali_core_session */ + struct mali_core_session *session; + u32 magic_nr; + u32 priority; + u32 watchdog_msecs; + u32 render_time_msecs ; + u32 start_time_jiffies; + unsigned long watchdog_jiffies; + u32 abort_id; +} mali_core_job; + +/* + * The rendercode subsystem is included in the subsystems[] array. + */ +extern struct mali_kernel_subsystem mali_subsystem_rendercore; + +void subsystem_flush_mapped_mem_cache(void); + + +#define SUBSYSTEM_MAGIC_NR 0xdeadbeef +#define CORE_MAGIC_NR 0xcafebabe +#define SESSION_MAGIC_NR 0xbabe1234 +#define JOB_MAGIC_NR 0x0123abcd + + +#define MALI_CHECK_SUBSYSTEM(subsystem)\ + do { \ + if ( SUBSYSTEM_MAGIC_NR != subsystem->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ + } while (0) + +#define MALI_CHECK_CORE(CORE)\ + do { \ + if ( CORE_MAGIC_NR != CORE->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + +#define MALI_CHECK_SESSION(SESSION)\ + do { \ + if ( SESSION_MAGIC_NR != SESSION->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + +#define MALI_CHECK_JOB(JOB)\ + do { \ + if ( JOB_MAGIC_NR != JOB->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + + +/* Check if job_a has higher priority than job_b */ +MALI_STATIC_INLINE int job_has_higher_priority(mali_core_job * job_a, mali_core_job * job_b) +{ + /* The lowest number has the highest priority */ + return (int) (job_a->priority < job_b->priority); +} + +MALI_STATIC_INLINE void job_priority_set(mali_core_job * job, u32 priority) +{ + if (priority > PRIORITY_MIN) job->priority = PRIORITY_MIN; + else job->priority = priority; +} + +void job_watchdog_set(mali_core_job * job, u32 watchdog_msecs); + +/* For use by const default register settings (e.g. set these after reset) */ +typedef struct register_address_and_value +{ + u32 address; + u32 value; +} register_address_and_value ; + + +/* For use by dynamic default register settings (e.g. set these after reset) */ +typedef struct register_address_and_value_list +{ + _mali_osk_list_t list; + register_address_and_value item; +} register_address_and_value_list ; + +/* Used if the user wants to set a continious block of registers */ +typedef struct register_array_user +{ + u32 entries_in_array; + u32 start_address; + void __user * reg_array; +}register_array_user; + + +#define MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys) \ + do { \ + MALI_DEBUG_PRINT(5, ("MUTEX: GRAB %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + _mali_osk_lock_wait( rendercores_global_mutex, _MALI_OSK_LOCKMODE_RW); \ + MALI_DEBUG_PRINT(5, ("MUTEX: GRABBED %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + rendercores_global_mutex_is_held = 1; \ + rendercores_global_mutex_owner = _mali_osk_get_tid(); \ + } while (0) ; + +#define MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys) \ + do { \ + MALI_DEBUG_PRINT(5, ("MUTEX: RELEASE %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + rendercores_global_mutex_is_held = 0; \ + rendercores_global_mutex_owner = 0; \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + _mali_osk_lock_signal( rendercores_global_mutex, _MALI_OSK_LOCKMODE_RW); \ + MALI_DEBUG_PRINT(5, ("MUTEX: RELEASED %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + } while (0) ; + + +#define MALI_ASSERT_MUTEX_IS_GRABBED(input_pointer)\ + do { \ + if ( 0 == rendercores_global_mutex_is_held ) MALI_PRINT_ERROR(("ASSERT MUTEX SHOULD BE GRABBED"));\ + if ( SUBSYSTEM_MAGIC_NR != input_pointer->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + if ( rendercores_global_mutex_owner != _mali_osk_get_tid() ) MALI_PRINT_ERROR(("Owner mismatch"));\ + } while (0) + + +u32 mali_core_renderunit_register_read(struct mali_core_renderunit *core, u32 relative_address); +void mali_core_renderunit_register_read_array(struct mali_core_renderunit *core, u32 relative_address, u32 * result_array, u32 nr_of_regs); +void mali_core_renderunit_register_write(struct mali_core_renderunit *core, u32 relative_address, u32 new_val); +void mali_core_renderunit_register_write_array(struct mali_core_renderunit *core, u32 relative_address, u32 * write_array, u32 nr_of_regs); + +_mali_osk_errcode_t mali_core_renderunit_init(struct mali_core_renderunit * core); +void mali_core_renderunit_term(struct mali_core_renderunit * core); +int mali_core_renderunit_map_registers(struct mali_core_renderunit *core); +void mali_core_renderunit_unmap_registers(struct mali_core_renderunit *core); +int mali_core_renderunit_irq_handler_add(struct mali_core_renderunit *core); +mali_core_renderunit * mali_core_renderunit_get_mali_core_nr(mali_core_subsystem *subsys, u32 mali_core_nr); + +int mali_core_subsystem_init(struct mali_core_subsystem * new_subsys); +#if USING_MMU +void mali_core_subsystem_attach_mmu(mali_core_subsystem* subsys); +#endif +int mali_core_subsystem_register_renderunit(struct mali_core_subsystem * subsys, struct mali_core_renderunit * core); +int mali_core_subsystem_system_info_fill(mali_core_subsystem* subsys, _mali_system_info* info); +void mali_core_subsystem_cleanup(struct mali_core_subsystem * subsys); +#if USING_MMU +void mali_core_subsystem_broadcast_notification(struct mali_core_subsystem * subsys, mali_core_notification_message message, u32 data); +#endif +void mali_core_session_begin(mali_core_session *session); +void mali_core_session_close(mali_core_session * session); +int mali_core_session_add_job(mali_core_session * session, mali_core_job *job, mali_core_job **job_return); +u32 mali_core_hang_check_timeout_get(void); + +_mali_osk_errcode_t mali_core_subsystem_ioctl_start_job(mali_core_session * session, void *job_data); +_mali_osk_errcode_t mali_core_subsystem_ioctl_number_of_cores_get(mali_core_session * session, u32 *number_of_cores); +_mali_osk_errcode_t mali_core_subsystem_ioctl_core_version_get(mali_core_session * session, _mali_core_version *version); +_mali_osk_errcode_t mali_core_subsystem_ioctl_suspend_response(mali_core_session * session, void* argument); +void mali_core_subsystem_ioctl_abort_job(mali_core_session * session, u32 id); + +#if USING_MALI_PMM +_mali_osk_errcode_t mali_core_subsystem_signal_power_down(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool immediate_only); +_mali_osk_errcode_t mali_core_subsystem_signal_power_up(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool queue_only); +#endif + +#if MALI_STATE_TRACKING +void mali_core_renderunit_dump_state(mali_core_subsystem* subsystem); +#endif + +#endif /* __MALI_RENDERCORE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_session_manager.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_session_manager.h new file mode 100644 index 00000000000..ce290d0c1c9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_session_manager.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_SESSION_MANAGER_H__ +#define __MALI_KERNEL_SESSION_MANAGER_H__ + +/* Incomplete struct to pass around pointers to it */ +struct mali_session_data; + +void * mali_kernel_session_manager_slot_get(struct mali_session_data * session, int id); + +#endif /* __MALI_KERNEL_SESSION_MANAGER_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_subsystem.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_subsystem.h new file mode 100644 index 00000000000..e8c6530668c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_subsystem.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_subsystem.h + */ + +#ifndef __MALI_KERNEL_SUBSYSTEM_H__ +#define __MALI_KERNEL_SUBSYSTEM_H__ + +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" + +/* typedefs of the datatypes used in the hook functions */ +typedef void * mali_kernel_subsystem_session_slot; +typedef int mali_kernel_subsystem_identifier; +typedef _mali_osk_errcode_t (*mali_kernel_resource_registrator)(_mali_osk_resource_t *); + +/** + * Broadcast notification messages + */ +typedef enum mali_core_notification_message +{ + MMU_KILL_STEP0_LOCK_SUBSYSTEM, /**< Request to lock subsystem */ + MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES, /**< Request to stop all buses */ + MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS, /**< Request kill all jobs, and not start more jobs */ + MMU_KILL_STEP3_CONTINUE_JOB_HANDLING, /**< Request to continue with new jobs on all cores */ + MMU_KILL_STEP4_UNLOCK_SUBSYSTEM /**< Request to unlock subsystem */ +} mali_core_notification_message; + +/** + * A function pointer can be NULL if the subsystem isn't interested in the event. + */ +typedef struct mali_kernel_subsystem +{ + /* subsystem control */ + _mali_osk_errcode_t (*startup)(mali_kernel_subsystem_identifier id); /**< Called during module load or system startup*/ + void (*shutdown)(mali_kernel_subsystem_identifier id); /**< Called during module unload or system shutdown */ + + /** + * Called during module load or system startup. + * Called when all subsystems have reported startup OK and all resources where successfully initialized + */ + _mali_osk_errcode_t (*load_complete)(mali_kernel_subsystem_identifier id); + + /* per subsystem handlers */ + _mali_osk_errcode_t (*system_info_fill)(_mali_system_info* info); /**< Fill info into info struct. MUST allocate memory with kmalloc, since it's kfree'd */ + + /* per session handlers */ + /** + * Informs about a new session. + * slot can be used to track per-session per-subsystem data. + * queue can be used to send events to user space. + * _mali_osk_errcode_t error return value. + */ + _mali_osk_errcode_t (*session_begin)(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + /** + * Informs that a session is ending + * slot was the same as given during session_begin + */ + void (*session_end)(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + + /* Used by subsystems to send messages to each other. This is the receiving end */ + void (*broadcast_notification)(mali_core_notification_message message, u32 data); + +#if MALI_STATE_TRACKING + /** Dump the current state of the subsystem */ + void (*dump_state)(void); +#endif +} mali_kernel_subsystem; + +/* functions used by the subsystems to interact with the core */ +/** + * Register a resouce handler + * @param type The resoruce type to register a handler for + * @param handler Pointer to the function handling this resource + * @return _MALI_OSK_ERR_OK on success. Otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t _mali_kernel_core_register_resource_handler(_mali_osk_resource_type_t type, mali_kernel_resource_registrator handler); + +/* function used to interact with other subsystems */ +/** + * Broadcast a message + * Sends a message to all subsystems which have registered a broadcast notification handler + * @param message The message to send + * @param data Message specific extra data + */ +void _mali_kernel_core_broadcast_subsystem_message(mali_core_notification_message message, u32 data); + +#if MALI_STATE_TRACKING +/** + * Tell all subsystems to dump their current state + */ +void _mali_kernel_core_dump_state(void); +#endif + + +#endif /* __MALI_KERNEL_SUBSYSTEM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.c new file mode 100644 index 00000000000..04653b06ad5 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_utilization.h" +#include "mali_osk.h" +#include "mali_platform.h" + +/* Define how often to calculate and report GPU utilization, in milliseconds */ +#define MALI_GPU_UTILIZATION_TIMEOUT 500 + +static _mali_osk_lock_t *time_data_lock; + +static _mali_osk_atomic_t num_running_cores; + +static u64 period_start_time = 0; +static u64 work_start_time = 0; +static u64 accumulated_work_time = 0; + +static _mali_osk_timer_t *utilization_timer = NULL; +static mali_bool timer_running = MALI_FALSE; + + +static void calculate_gpu_utilization(void* arg) +{ + u64 time_now; + u64 time_period; + u32 leading_zeroes; + u32 shift_val; + u32 work_normalized; + u32 period_normalized; + u32 utilization; + + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + if (accumulated_work_time == 0 && work_start_time == 0) + { + /* Don't reschedule timer, this will be started if new work arrives */ + timer_running = MALI_FALSE; + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + /* No work done for this period, report zero usage */ + mali_gpu_utilization_handler(0); + + return; + } + + time_now = _mali_osk_time_get_ns(); + time_period = time_now - period_start_time; + + /* If we are currently busy, update working period up to now */ + if (work_start_time != 0) + { + accumulated_work_time += (time_now - work_start_time); + work_start_time = time_now; + } + + /* + * We have two 64-bit values, a dividend and a divisor. + * To avoid dependencies to a 64-bit divider, we shift down the two values + * equally first. + * We shift the dividend up and possibly the divisor down, making the result X in 256. + */ + + /* Shift the 64-bit values down so they fit inside a 32-bit integer */ + leading_zeroes = _mali_osk_clz((u32)(time_period >> 32)); + shift_val = 32 - leading_zeroes; + work_normalized = (u32)(accumulated_work_time >> shift_val); + period_normalized = (u32)(time_period >> shift_val); + + /* + * Now, we should report the usage in parts of 256 + * this means we must shift up the dividend or down the divisor by 8 + * (we could do a combination, but we just use one for simplicity, + * but the end result should be good enough anyway) + */ + if (period_normalized > 0x00FFFFFF) + { + /* The divisor is so big that it is safe to shift it down */ + period_normalized >>= 8; + } + else + { + /* + * The divisor is so small that we can shift up the dividend, without loosing any data. + * (dividend is always smaller than the divisor) + */ + work_normalized <<= 8; + } + + utilization = work_normalized / period_normalized; + + accumulated_work_time = 0; + period_start_time = time_now; /* starting a new period */ + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(MALI_GPU_UTILIZATION_TIMEOUT)); + + mali_gpu_utilization_handler(utilization); +} + + + +_mali_osk_errcode_t mali_utilization_init(void) +{ + time_data_lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ|_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0 ); + if (NULL == time_data_lock) + { + return _MALI_OSK_ERR_FAULT; + } + + _mali_osk_atomic_init(&num_running_cores, 0); + + utilization_timer = _mali_osk_timer_init(); + if (NULL == utilization_timer) + { + _mali_osk_lock_term(time_data_lock); + return _MALI_OSK_ERR_FAULT; + } + _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL); + + return _MALI_OSK_ERR_OK; +} + +void mali_utilization_suspend(void) +{ + if (NULL != utilization_timer) + { + _mali_osk_timer_del(utilization_timer); + timer_running = MALI_FALSE; + } +} + +void mali_utilization_term(void) +{ + if (NULL != utilization_timer) + { + _mali_osk_timer_del(utilization_timer); + timer_running = MALI_FALSE; + _mali_osk_timer_term(utilization_timer); + utilization_timer = NULL; + } + + _mali_osk_atomic_term(&num_running_cores); + + _mali_osk_lock_term(time_data_lock); +} + + + +void mali_utilization_core_start(void) +{ + if (_mali_osk_atomic_inc_return(&num_running_cores) == 1) + { + /* + * We went from zero cores working, to one core working, + * we now consider the entire GPU for being busy + */ + + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + work_start_time = _mali_osk_time_get_ns(); + + if (timer_running != MALI_TRUE) + { + timer_running = MALI_TRUE; + period_start_time = work_start_time; /* starting a new period */ + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(MALI_GPU_UTILIZATION_TIMEOUT)); + } + else + { + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + } + } +} + + + +void mali_utilization_core_end(void) +{ + if (_mali_osk_atomic_dec_return(&num_running_cores) == 0) + { + /* + * No more cores are working, so accumulate the time we was busy. + */ + u64 time_now; + + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + time_now = _mali_osk_time_get_ns(); + accumulated_work_time += (time_now - work_start_time); + work_start_time = 0; + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + } +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.h new file mode 100644 index 00000000000..f6485006729 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_utilization.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_UTILIZATION_H__ +#define __MALI_KERNEL_UTILIZATION_H__ + +#include "mali_osk.h" + +/** + * Initialize/start the Mali GPU utilization metrics reporting. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_utilization_init(void); + +/** + * Terminate the Mali GPU utilization metrics reporting + */ +void mali_utilization_term(void); + +/** + * Should be called when a job is about to execute a job + */ +void mali_utilization_core_start(void); + +/** + * Should be called to stop the utilization timer during system suspend + */ +void mali_utilization_suspend(void); + +/** + * Should be called when a job has completed executing a job + */ +void mali_utilization_core_end(void); + + +#endif /* __MALI_KERNEL_UTILIZATION_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_vsync.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_vsync.c new file mode 100644 index 00000000000..8dfa3a393ba --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_kernel_vsync.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +/*#include "mali_timestamp.h" +*/ + +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args) +{ + _mali_uk_vsync_event event = (_mali_uk_vsync_event)args->event; + MALI_IGNORE(event); /* event is not used for release code, and that is OK */ +/* u64 ts = _mali_timestamp_get(); + */ + + MALI_DEBUG_PRINT(4, ("Received VSYNC event: %d\n", event)); + + MALI_SUCCESS; +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk.h new file mode 100644 index 00000000000..c35d90577a3 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk.h @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk.h + * Defines the OS abstraction layer for the kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_H__ +#define __MALI_OSK_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup oskapi UDD OS Abstraction for Kernel-side (OSK) APIs + * + * @{ + */ + +/** @defgroup _mali_osk_miscellaneous OSK Miscellaneous functions, constants and types + * @{ */ + +/* Define integer types used by OSK. Note: these currently clash with Linux so we only define them if not defined already */ +#ifndef __KERNEL__ + typedef unsigned char u8; + typedef signed char s8; + typedef unsigned short u16; + typedef signed short s16; + typedef unsigned int u32; + typedef signed int s32; + typedef unsigned long long u64; + #define BITS_PER_LONG (sizeof(long)*8) +#else + /* Ensure Linux types u32, etc. are defined */ + #include +#endif + +/** @brief Mali Boolean type which uses MALI_TRUE and MALI_FALSE + */ + typedef unsigned long mali_bool; + +#ifndef MALI_TRUE + #define MALI_TRUE ((mali_bool)1) +#endif + +#ifndef MALI_FALSE + #define MALI_FALSE ((mali_bool)0) +#endif + +/** + * @brief OSK Error codes + * + * Each OS may use its own set of error codes, and may require that the + * User/Kernel interface take certain error code. This means that the common + * error codes need to be sufficiently rich to pass the correct error code + * thorugh from the OSK to U/K layer, across all OSs. + * + * The result is that some error codes will appear redundant on some OSs. + * Under all OSs, the OSK layer must translate native OS error codes to + * _mali_osk_errcode_t codes. Similarly, the U/K layer must translate from + * _mali_osk_errcode_t codes to native OS error codes. + */ +typedef enum +{ + _MALI_OSK_ERR_OK = 0, /**< Success. */ + _MALI_OSK_ERR_FAULT = -1, /**< General non-success */ + _MALI_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */ + _MALI_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */ + _MALI_OSK_ERR_NOMEM = -4, /**< Insufficient memory */ + _MALI_OSK_ERR_TIMEOUT = -5, /**< Timeout occurred */ + _MALI_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */ + _MALI_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */ + _MALI_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */ + _MALI_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */ +} _mali_osk_errcode_t; + +/** @} */ /* end group _mali_osk_miscellaneous */ + + +/** @defgroup _mali_osk_irq OSK IRQ handling + * @{ */ + +/** @brief Private type for IRQ handling objects */ +typedef struct _mali_osk_irq_t_struct _mali_osk_irq_t; + +/** @brief Optional function to trigger an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data */ +typedef void (*_mali_osk_irq_trigger_t)( void * arg ); + +/** @brief Optional function to acknowledge an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was successful, or a suitable _mali_osk_errcode_t on failure. */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_ack_t)( void * arg ); + +/** @brief IRQ 'upper-half' handler callback. + * + * This function is implemented by the common layer to do the initial handling of a + * resource's IRQ. This maps on to the concept of an ISR that does the minimum + * work necessary before handing off to an IST. + * + * The communication of the resource-specific data from the ISR to the IST is + * handled by the OSK implementation. + * + * On most systems, the IRQ upper-half handler executes in IRQ context. + * Therefore, the system may have restrictions about what can be done in this + * context + * + * If an IRQ upper-half handler requires more work to be done than can be + * acheived in an IRQ context, then it may defer the work with + * _mali_osk_irq_schedulework(). Refer to \ref _mali_osk_irq_schedulework() for + * more information. + * + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was correctly handled, or a suitable + * _mali_osk_errcode_t otherwise. + */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_uhandler_t)( void * arg ); + +/** @brief IRQ 'bottom-half' handler callback. + * + * This function is implemented by the common layer to do the deferred handling + * of a resource's IRQ. Usually, this work cannot be carried out in IRQ context + * by the IRQ upper-half handler. + * + * The IRQ bottom-half handler maps on to the concept of an IST that may + * execute some time after the actual IRQ has fired. + * + * All OSK-registered IRQ bottom-half handlers will be serialized, across all + * CPU-cores in the system. + * + * Refer to \ref _mali_osk_irq_schedulework() for more information on the + * IRQ work-queue, and the calling of the IRQ bottom-half handler. + * + * @param arg resource-specific data + */ +typedef void (*_mali_osk_irq_bhandler_t)( void * arg ); +/** @} */ /* end group _mali_osk_irq */ + + +/** @defgroup _mali_osk_atomic OSK Atomic counters + * @{ */ + +/** @brief Public type of atomic counters + * + * This is public for allocation on stack. On systems that support it, this is just a single 32-bit value. + * On others, it could be encapsulating an object stored elsewhere. + * + * Even though the structure has space for a u32, the counters will only + * represent signed 24-bit integers. + * + * Regardless of implementation, the \ref _mali_osk_atomic functions \b must be used + * for all accesses to the variable's value, even if atomicity is not required. + * Do not access u.val or u.obj directly. + */ +typedef struct +{ + union + { + u32 val; + void *obj; + } u; +} _mali_osk_atomic_t; +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_lock OSK Mutual Exclusion Locks + * @{ */ + +/** @brief OSK Mutual Exclusion Lock flags type + * + * Flags are supplied at the point where the Lock is initialized. Each flag can + * be combined with others using bitwise OR, '|'. + * + * The flags must be sufficiently rich to cope with all our OSs. This means + * that on some OSs, certain flags can be completely ignored. We define a + * number of terms that are significant across all OSs: + * + * - Sleeping/non-sleeping mutexs. Sleeping mutexs can block on waiting, and so + * schedule out the current thread. This is significant on OSs where there are + * situations in which the current thread must not be put to sleep. On OSs + * without this restriction, sleeping and non-sleeping mutexes can be treated + * as the same (if that is required). + * - Interruptable/non-interruptable mutexes. For sleeping mutexes, it may be + * possible for the sleep to be interrupted for a reason other than the thread + * being able to obtain the lock. OSs behaving in this way may provide a + * mechanism to control whether sleeping mutexes can be interrupted. On OSs + * that do not support the concept of interruption, \b or they do not support + * control of mutex interruption, then interruptable mutexes may be treated + * as non-interruptable. + * + * Some constrains apply to the lock type flags: + * + * - Spinlocks are by nature, non-interruptable. Hence, they must always be + * combined with the NONINTERRUPTABLE flag, because it is meaningless to ask + * for a spinlock that is interruptable (and this highlights its + * non-interruptable-ness). For example, on certain OSs they should be used when + * you must not sleep. + * - Reader/writer is an optimization hint, and any type of lock can be + * reader/writer. Since this is an optimization hint, the implementation need + * not respect this for any/all types of lock. For example, on certain OSs, + * there's no interruptable reader/writer mutex. If such a thing were requested + * on that OS, the fact that interruptable was requested takes priority over the + * reader/writer-ness, because reader/writer-ness is not necessary for correct + * operation. + * - Any lock can use the order parameter. + * - A onelock is an optimization hint specific to certain OSs. It can be + * specified when it is known that only one lock will be held by the thread, + * and so can provide faster mutual exclusion. This can be safely ignored if + * such optimization is not required/present. + * + * The absence of any flags (the value 0) results in a sleeping-mutex, which is interruptable. + */ +typedef enum +{ + _MALI_OSK_LOCKFLAG_SPINLOCK = 0x1, /**< Specifically, don't sleep on those architectures that require it */ + _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE = 0x2, /**< The mutex cannot be interrupted, e.g. delivery of signals on those architectures where this is required */ + _MALI_OSK_LOCKFLAG_READERWRITER = 0x4, /**< Optimise for readers/writers */ + _MALI_OSK_LOCKFLAG_ORDERED = 0x8, /**< Use the order parameter; otherwise use automatic ordering */ + _MALI_OSK_LOCKFLAG_ONELOCK = 0x10, /**< Each thread can only hold one lock at a time */ + _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ = 0x20, /**< IRQ version of spinlock */ + /** @enum _mali_osk_lock_flags_t + * + * Flags from 0x10000--0x80000000 are RESERVED for User-mode */ + +} _mali_osk_lock_flags_t; + +/** @brief Mutual Exclusion Lock Mode Optimization hint + * + * The lock mode is used to implement the read/write locking of locks specified + * as _MALI_OSK_LOCKFLAG_READERWRITER. In this case, the RO mode can be used + * to allow multiple concurrent readers, but no writers. The RW mode is used for + * writers, and so will wait for all readers to release the lock (if any present). + * Further readers and writers will wait until the writer releases the lock. + * + * The mode is purely an optimization hint: for example, it is permissible for + * all locks to behave in RW mode, regardless of that supplied. + * + * It is an error to attempt to use locks in anything other that RW mode when + * _MALI_OSK_LOCKFLAG_READERWRITER is not supplied. + * + */ +typedef enum +{ + _MALI_OSK_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */ + _MALI_OSK_LOCKMODE_RW = 0x0, /**< Read-write mode, default. All readers and writers are mutually-exclusive */ + _MALI_OSK_LOCKMODE_RO, /**< Read-only mode, to support multiple concurrent readers, but mutual exclusion in the presence of writers. */ + /** @enum _mali_osk_lock_mode_t + * + * Lock modes 0x40--0x7F are RESERVED for User-mode */ +} _mali_osk_lock_mode_t; + +/** @brief Private type for Mutual Exclusion lock objects */ +typedef struct _mali_osk_lock_t_struct _mali_osk_lock_t; +/** @} */ /* end group _mali_osk_lock */ + +/** @defgroup _mali_osk_low_level_memory OSK Low-level Memory Operations + * @{ */ + +/** + * @brief Private data type for use in IO accesses to/from devices. + * + * This represents some range that is accessible from the device. Examples + * include: + * - Device Registers, which could be readable and/or writeable. + * - Memory that the device has access to, for storing configuration structures. + * + * Access to this range must be made through the _mali_osk_mem_ioread32() and + * _mali_osk_mem_iowrite32() functions. + */ +typedef struct _mali_io_address * mali_io_address; + +/** @defgroup _MALI_OSK_CPU_PAGE CPU Physical page size macros. + * + * The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The CPU Physical Page Size has been assumed to be the same as the Mali + * Physical Page Size. + * + * @{ + */ + +/** CPU Page Order, as log to base 2 of the Page size. @see _MALI_OSK_CPU_PAGE_SIZE */ +#define _MALI_OSK_CPU_PAGE_ORDER ((u32)12) +/** CPU Page Size, in bytes. */ +#define _MALI_OSK_CPU_PAGE_SIZE (((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) +/** CPU Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_CPU_PAGE_MASK (~((((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_CPU_PAGE */ + +/** @defgroup _MALI_OSK_MALI_PAGE Mali Physical Page size macros + * + * Mali Physical page size macros. The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The Mali Physical Page Size has been assumed to be the same as the CPU + * Physical Page Size. + * + * @{ + */ + +/** Mali Page Order, as log to base 2 of the Page size. @see _MALI_OSK_MALI_PAGE_SIZE */ +#define _MALI_OSK_MALI_PAGE_ORDER ((u32)12) +/** Mali Page Size, in bytes. */ +#define _MALI_OSK_MALI_PAGE_SIZE (((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) +/** Mali Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_MALI_PAGE_MASK (~((((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_MALI_PAGE*/ + +/** @brief flags for mapping a user-accessible memory range + * + * Where a function with prefix '_mali_osk_mem_mapregion' accepts flags as one + * of the function parameters, it will use one of these. These allow per-page + * control over mappings. Compare with the mali_memory_allocation_flag type, + * which acts over an entire range + * + * These may be OR'd together with bitwise OR (|), but must be cast back into + * the type after OR'ing. + */ +typedef enum +{ + _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR = 0x1, /**< Physical address is OS Allocated */ +} _mali_osk_mem_mapregion_flags_t; +/** @} */ /* end group _mali_osk_low_level_memory */ + +/** @defgroup _mali_osk_notification OSK Notification Queues + * @{ */ + +/** @brief Private type for notification queue objects */ +typedef struct _mali_osk_notification_queue_t_struct _mali_osk_notification_queue_t; + +/** @brief Public notification data object type */ +typedef struct _mali_osk_notification_t_struct +{ + u32 notification_type; /**< The notification type */ + u32 result_buffer_size; /**< Size of the result buffer to copy to user space */ + void * result_buffer; /**< Buffer containing any type specific data */ +} _mali_osk_notification_t; + +/** @} */ /* end group _mali_osk_notification */ + + +/** @defgroup _mali_osk_timer OSK Timer Callbacks + * @{ */ + +/** @brief Function to call when a timer expires + * + * When a timer expires, this function is called. Note that on many systems, + * a timer callback will be executed in IRQ context. Therefore, restrictions + * may apply on what can be done inside the timer callback. + * + * If a timer requires more work to be done than can be acheived in an IRQ + * context, then it may defer the work with a work-queue. For example, it may + * use \ref _mali_osk_irq_schedulework() to make use of the IRQ bottom-half handler + * to carry out the remaining work. + * + * Stopping the timer with \ref _mali_osk_timer_del() blocks on compeletion of + * the callback. Therefore, the callback may not obtain any mutexes also held + * by any callers of _mali_osk_timer_del(). Otherwise, a deadlock may occur. + * + * @param arg Function-specific data */ +typedef void (*_mali_osk_timer_callback_t)(void * arg ); + +/** @brief Private type for Timer Callback Objects */ +typedef struct _mali_osk_timer_t_struct _mali_osk_timer_t; +/** @} */ /* end group _mali_osk_timer */ + + +/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists + * @{ */ + +/** @brief Public List objects. + * + * To use, add a _mali_osk_list_t member to the structure that may become part + * of a list. When traversing the _mali_osk_list_t objects, use the + * _MALI_OSK_CONTAINER_OF() macro to recover the structure from its + *_mali_osk_list_t member + * + * Each structure may have multiple _mali_osk_list_t members, so that the + * structure is part of multiple lists. When traversing lists, ensure that the + * correct _mali_osk_list_t member is used, because type-checking will be + * lost by the compiler. + */ +typedef struct _mali_osk_list_s +{ + struct _mali_osk_list_s *next; + struct _mali_osk_list_s *prev; +} _mali_osk_list_t; + +/** @brief Initialize a list to be a head of an empty list + * @param exp the list to initialize. */ +#define _MALI_OSK_INIT_LIST_HEAD(exp) _mali_osk_list_init(exp) + +/** @brief Define a list variable, which is uninitialized. + * @param exp the name of the variable that the list will be defined as. */ +#define _MALI_OSK_LIST_HEAD(exp) _mali_osk_list_t exp + +/** @brief Find the containing structure of another structure + * + * This is the reverse of the operation 'offsetof'. This means that the + * following condition is satisfied: + * + * ptr == _MALI_OSK_CONTAINER_OF( &ptr->member, type, member ) + * + * When ptr is of type 'type'. + * + * Its purpose it to recover a larger structure that has wrapped a smaller one. + * + * @note no type or memory checking occurs to ensure that a wrapper structure + * does in fact exist, and that it is being recovered with respect to the + * correct member. + * + * @param ptr the pointer to the member that is contained within the larger + * structure + * @param type the type of the structure that contains the member + * @param member the name of the member in the structure that ptr points to. + * @return a pointer to a \a type object which contains \a member, as pointed + * to by \a ptr. + */ +#define _MALI_OSK_CONTAINER_OF(ptr, type, member) \ + ((type *)( ((char *)ptr) - offsetof(type,member) )) + +/** @brief Find the containing structure of a list + * + * When traversing a list, this is used to recover the containing structure, + * given that is contains a _mali_osk_list_t member. + * + * Each list must be of structures of one type, and must link the same members + * together, otherwise it will not be possible to correctly recover the + * sturctures that the lists link. + * + * @note no type or memory checking occurs to ensure that a structure does in + * fact exist for the list entry, and that it is being recovered with respect + * to the correct list member. + * + * @param ptr the pointer to the _mali_osk_list_t member in this structure + * @param type the type of the structure that contains the member + * @param member the member of the structure that ptr points to. + * @return a pointer to a \a type object which contains the _mali_osk_list_t + * \a member, as pointed to by the _mali_osk_list_t \a *ptr. + */ +#define _MALI_OSK_LIST_ENTRY(ptr, type, member) \ + _MALI_OSK_CONTAINER_OF(ptr, type, member) + +/** @brief Enumerate a list safely + * + * With this macro, lists can be enumerated in a 'safe' manner. That is, + * entries can be deleted from the list without causing an error during + * enumeration. To achieve this, a 'temporary' pointer is required, which must + * be provided to the macro. + * + * Use it like a 'for()', 'while()' or 'do()' construct, and so it must be + * followed by a statement or compound-statement which will be executed for + * each list entry. + * + * Upon loop completion, providing that an early out was not taken in the + * loop body, then it is guaranteed that ptr->member == list, even if the loop + * body never executed. + * + * @param ptr a pointer to an object of type 'type', which points to the + * structure that contains the currently enumerated list entry. + * @param tmp a pointer to an object of type 'type', which must not be used + * inside the list-execution statement. + * @param list a pointer to a _mali_osk_list_t, from which enumeration will + * begin + * @param type the type of the structure that contains the _mali_osk_list_t + * member that is part of the list to be enumerated. + * @param member the _mali_osk_list_t member of the structure that is part of + * the list to be enumerated. + */ +#define _MALI_OSK_LIST_FOREACHENTRY(ptr, tmp, list, type, member) \ + for (ptr = _MALI_OSK_LIST_ENTRY((list)->next, type, member), \ + tmp = _MALI_OSK_LIST_ENTRY(ptr->member.next, type, member); \ + &ptr->member != (list); \ + ptr = tmp, tmp = _MALI_OSK_LIST_ENTRY(tmp->member.next, type, member)) +/** @} */ /* end group _mali_osk_list */ + + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief The known resource types + * + * @note \b IMPORTANT: these must remain fixed, and only be extended. This is + * because not all systems use a header file for reading in their resources. + * The resources may instead come from a data file where these resources are + * 'hard-coded' in, because there's no easy way of transferring the enum values + * into such data files. E.g. the C-Pre-processor does \em not process enums. + */ +typedef enum _mali_osk_resource_type +{ + RESOURCE_TYPE_FIRST =0, /**< Duplicate resource marker for the first resource*/ + MEMORY =0, /**< Physically contiguous memory block, not managed by the OS */ + OS_MEMORY =1, /**< Memory managed by and shared with the OS */ + MALI200 =3, /**< Mali200 Programmable Fragment Shader */ + MALIGP2 =4, /**< MaliGP2 Programmable Vertex Shader */ + MMU =5, /**< Mali MMU (Memory Management Unit) */ + FPGA_FRAMEWORK =6, /**< Mali registers specific to FPGA implementations */ + MALI400L2 =7, /**< Mali400 L2 Cache */ + MALI300L2 =7, /**< Mali300 L2 Cache */ + MALI400GP =8, /**< Mali400 Programmable Vertex Shader Core */ + MALI300GP =8, /**< Mali300 Programmable Vertex Shader Core */ + MALI400PP =9, /**< Mali400 Programmable Fragment Shader Core */ + MALI300PP =9, /**< Mali300 Programmable Fragment Shader Core */ + MEM_VALIDATION =10, /**< External Memory Validator */ + PMU =11, /**< Power Manangement Unit */ + RESOURCE_TYPE_COUNT /**< The total number of known resources */ +} _mali_osk_resource_type_t; + +/** @brief resource description struct + * + * _mali_osk_resources_init() will enumerate objects of this type. Not all + * members have a valid meaning across all types. + * + * The mmu_id is used to group resources to a certain MMU, since there may be + * more than one MMU in the system, and each resource may be using a different + * MMU: + * - For MMU resources, the setting of mmu_id is a uniquely identifying number. + * - For Other resources, the setting of mmu_id determines which MMU the + * resource uses. + */ +typedef struct _mali_osk_resource +{ + _mali_osk_resource_type_t type; /**< type of the resource */ + const char * description; /**< short description of the resource */ + u32 base; /**< Physical base address of the resource, as seen by Mali resources. */ + s32 cpu_usage_adjust; /**< Offset added to the base address of the resource to arrive at the CPU physical address of the resource (if different from the Mali physical address) */ + u32 size; /**< Size in bytes of the resource - either the size of its register range, or the size of the memory block. */ + u32 irq; /**< IRQ number delivered to the CPU, or -1 to tell the driver to probe for it (if possible) */ + u32 flags; /**< Resources-specific flags. */ + u32 mmu_id; /**< Identifier for Mali MMU resources. */ + u32 alloc_order; /**< Order in which MEMORY/OS_MEMORY resources are used */ +} _mali_osk_resource_t; +/** @} */ /* end group _mali_osk_miscellaneous */ + + +#include "mali_kernel_memory_engine.h" /* include for mali_memory_allocation and mali_physical_memory_allocation type */ + +/** @addtogroup _mali_osk_irq + * @{ */ + +/** @brief Fake IRQ number for testing purposes + */ +#define _MALI_OSK_IRQ_NUMBER_FAKE ((u32)0xFFFFFFF1) + +/** @addtogroup _mali_osk_irq + * @{ */ + +/** @brief PMM Virtual IRQ number + */ +#define _MALI_OSK_IRQ_NUMBER_PMM ((u32)0xFFFFFFF2) + + +/** @brief Initialize IRQ handling for a resource + * + * The _mali_osk_irq_t returned must be written into the resource-specific data + * pointed to by data. This is so that the upper and lower handlers can call + * _mali_osk_irq_schedulework(). + * + * @note The caller must ensure that the resource does not generate an + * interrupt after _mali_osk_irq_init() finishes, and before the + * _mali_osk_irq_t is written into the resource-specific data. Otherwise, + * the upper-half handler will fail to call _mali_osk_irq_schedulework(). + * + * @param irqnum The IRQ number that the resource uses, as seen by the CPU. + * The value -1 has a special meaning which indicates the use of probing, and trigger_func and ack_func must be + * non-NULL. + * @param uhandler The upper-half handler, corresponding to a ISR handler for + * the resource + * @param bhandler The lower-half handler, corresponding to an IST handler for + * the resource + * @param trigger_func Optional: a function to trigger the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param ack_func Optional: a function to acknowledge the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param data resource-specific data, which will be passed to uhandler, + * bhandler and (if present) trigger_func and ack_funnc + * @param description textual description of the IRQ resource. + * @return on success, a pointer to a _mali_osk_irq_t object, which represents + * the IRQ handling on this resource. NULL on failure. + */ +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, _mali_osk_irq_bhandler_t bhandler, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *data, const char *description ); + +/** @brief Cause a queued, deferred call of the IRQ bottom-half. + * + * _mali_osk_irq_schedulework provides a mechanism for enqueuing deferred calls + * to the IRQ bottom-half handler. The queue is known as the IRQ work-queue. + * After calling _mali_osk_irq_schedulework(), the IRQ bottom-half handler will + * be scheduled to run at some point in the future. + * + * This is called by the IRQ upper-half to defer further processing of + * IRQ-related work to the IRQ bottom-half handler. This is necessary for work + * that cannot be done in an IRQ context by the IRQ upper-half handler. Timer + * callbacks also use this mechanism, because they are treated as though they + * operate in an IRQ context. Refer to \ref _mali_osk_timer_t for more + * information. + * + * Code that operates in a kernel-process context (with no IRQ context + * restrictions) may also enqueue deferred calls to the IRQ bottom-half. The + * advantage over direct calling is that deferred calling allows the caller and + * IRQ bottom half to hold the same mutex, with a guarantee that they will not + * deadlock just by using this mechanism. + * + * _mali_osk_irq_schedulework() places deferred call requests on a queue, to + * allow for more than one thread to make a deferred call. Therfore, if it is + * called 'K' times, then the IRQ bottom-half will be scheduled 'K' times too. + * 'K' is a number that is implementation-specific. + * + * _mali_osk_irq_schedulework() is guaranteed to not block on: + * - enqueuing a deferred call request. + * - the completion of the IRQ bottom-half handler. + * + * This is to prevent deadlock. For example, if _mali_osk_irq_schedulework() + * blocked, then it would cause a deadlock when the following two conditions + * hold: + * - The IRQ bottom-half callback (of type _mali_osk_irq_bhandler_t) locks + * a mutex + * - And, at the same time, the caller of _mali_osk_irq_schedulework() also + * holds the same mutex + * + * @note care must be taken to not overflow the queue that + * _mali_osk_irq_schedulework() operates on. Code must be structured to + * ensure that the number of requests made to the queue is bounded. Otherwise, + * IRQs will be lost. + * + * The queue that _mali_osk_irq_schedulework implements is a FIFO of N-writer, + * 1-reader type. The writers are the callers of _mali_osk_irq_schedulework + * (all OSK-registered IRQ upper-half handlers in the system, watchdog timers, + * callers from a Kernel-process context). The reader is a single thread that + * handles all OSK-registered IRQs. + * + * The consequence of the queue being a 1-reader type is that calling + * _mali_osk_irq_schedulework() on different _mali_osk_irq_t objects causes + * their IRQ bottom-halves to be serialized, across all CPU-cores in the + * system. + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ bottom-half must begin processing. + */ +void _mali_osk_irq_schedulework( _mali_osk_irq_t *irq ); + +/** @brief Terminate IRQ handling on a resource. + * + * This will disable the interrupt from the device, and then waits for the + * IRQ work-queue to finish the work that is currently in the queue. That is, + * for every deferred call currently in the IRQ work-queue, it waits for each + * of those to be processed by their respective IRQ bottom-half handler. + * + * This function is used to ensure that the bottom-half handler of the supplied + * IRQ object will not be running at the completion of this function call. + * However, the caller must ensure that no other sources could call the + * _mali_osk_irq_schedulework() on the same IRQ object. For example, the + * relevant timers must be stopped. + * + * @note While this function is being called, other OSK-registered IRQs in the + * system may enqueue work for their respective bottom-half handlers. This + * function will not wait for those entries in the work-queue to be flushed. + * + * Since this blocks on the completion of work in the IRQ work-queue, the + * caller of this function \b must \b not hold any mutexes that are taken by + * any OSK-registered IRQ bottom-half handler. To do so may cause a deadlock. + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ handling is to be terminated. + */ +void _mali_osk_irq_term( _mali_osk_irq_t *irq ); +/** @} */ /* end group _mali_osk_irq */ + + +/** @addtogroup _mali_osk_atomic + * @{ */ + +/** @brief Decrement an atomic counter + * + * @note It is an error to decrement the counter beyond -(1<<23) + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ); + +/** @brief Decrement an atomic counter, return new value + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * @note It is an error to decrement the counter beyond -(1<<23) + * + * @param atom pointer to an atomic counter + * @return The new value, after decrement */ +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter + * + * @note It is an error to increment the counter beyond (1<<23)-1 + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter, return new value + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * @note It is an error to increment the counter beyond (1<<23)-1 + * + * @param atom pointer to an atomic counter */ +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ); + +/** @brief Initialize an atomic counter + * + * The counters have storage for signed 24-bit integers. Initializing to signed + * values requiring more than 24-bits storage will fail. + * + * @note the parameter required is a u32, and so signed integers should be + * cast to u32. + * + * @param atom pointer to an atomic counter + * @param val the value to initialize the atomic counter. + * @return _MALI_OSK_ERR_OK on success, otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ); + +/** @brief Read a value from an atomic counter + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * This can only be safely used to determine the value of the counter when it + * is guaranteed that other threads will not be modifying the counter. This + * makes its usefulness limited. + * + * @param atom pointer to an atomic counter + */ +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ); + +/** @brief Terminate an atomic counter + * + * @param atom pointer to an atomic counter + */ +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ); +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_memory OSK Memory Allocation + * @{ */ + +/** @brief Allocate zero-initialized memory. + * + * Returns a buffer capable of containing at least \a n elements of \a size + * bytes each. The buffer is initialized to zero. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * @param n Number of elements to allocate + * @param size Size of each element + * @return On success, the zero-initialized buffer allocated. NULL on failure + */ +void *_mali_osk_calloc( u32 n, u32 size ); + +/** @brief Allocate memory. + * + * Returns a buffer capable of containing at least \a size bytes. The + * contents of the buffer are undefined. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * Remember to free memory using _mali_osk_free(). + * @param size Number of bytes to allocate + * @return On success, the buffer allocated. NULL on failure. + */ +void *_mali_osk_malloc( u32 size ); + +/** @brief Free memory. + * + * Reclaims the buffer pointed to by the parameter \a ptr for the system. + * All memory returned from _mali_osk_malloc() and _mali_osk_calloc() + * must be freed before the application exits. Otherwise, + * a memory leak will occur. + * + * Memory must be freed once. It is an error to free the same non-NULL pointer + * more than once. + * + * It is legal to free the NULL pointer. + * + * @param ptr Pointer to buffer to free + */ +void _mali_osk_free( void *ptr ); + +/** @brief Copies memory. + * + * Copies the \a len bytes from the buffer pointed by the parameter \a src + * directly to the buffer pointed by \a dst. + * + * It is an error for \a src to overlap \a dst anywhere in \a len bytes. + * + * @param dst Pointer to the destination array where the content is to be + * copied. + * @param src Pointer to the source of data to be copied. + * @param len Number of bytes to copy. + * @return \a dst is always passed through unmodified. + */ +void *_mali_osk_memcpy( void *dst, const void *src, u32 len ); + +/** @brief Fills memory. + * + * Sets the first \a n bytes of the block of memory pointed to by \a s to + * the specified value + * @param s Pointer to the block of memory to fill. + * @param c Value to be set, passed as u32. Only the 8 Least Significant Bits (LSB) + * are used. + * @param n Number of bytes to be set to the value. + * @return \a s is always passed through unmodified + */ +void *_mali_osk_memset( void *s, u32 c, u32 n ); +/** @} */ /* end group _mali_osk_memory */ + + +/** @brief Checks the amount of memory allocated + * + * Checks that not more than \a max_allocated bytes are allocated. + * + * Some OS bring up an interactive out of memory dialogue when the + * system runs out of memory. This can stall non-interactive + * apps (e.g. automated test runs). This function can be used to + * not trigger the OOM dialogue by keeping allocations + * within a certain limit. + * + * @return MALI_TRUE when \a max_allocated bytes are not in use yet. MALI_FALSE + * when at least \a max_allocated bytes are in use. + */ +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ); + +/** @addtogroup _mali_osk_lock + * @{ */ + +/** @brief Initialize a Mutual Exclusion Lock + * + * Locks are created in the signalled (unlocked) state. + * + * initial must be zero, since there is currently no means of expressing + * whether a reader/writer lock should be initially locked as a reader or + * writer. This would require some encoding to be used. + * + * 'Automatic' ordering means that locks must be obtained in the order that + * they were created. For all locks that can be held at the same time, they must + * either all provide the order parameter, or they all must use 'automatic' + * ordering - because there is no way of mixing 'automatic' and 'manual' + * ordering. + * + * @param flags flags combined with bitwise OR ('|'), or zero. There are + * restrictions on which flags can be combined, @see _mali_osk_lock_flags_t. + * @param initial For future expansion into semaphores. SBZ. + * @param order The locking order of the mutex. That is, locks obtained by the + * same thread must have been created with an increasing order parameter, for + * deadlock prevention. Setting to zero causes 'automatic' ordering to be used. + * @return On success, a pointer to a _mali_osk_lock_t object. NULL on failure. + */ +_mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order ); + +/** @brief Wait for a lock to be signalled (obtained) + + * After a thread has successfully waited on the lock, the lock is obtained by + * the thread, and is marked as unsignalled. The thread releases the lock by + * signalling it. + * + * In the case of Reader/Writer locks, multiple readers can obtain a lock in + * the absence of writers, which is a performance optimization (providing that + * the readers never write to the protected resource). + * + * To prevent deadlock, locks must always be obtained in the same order. + * + * For locks marked as _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, it is a + * programming error for the function to exit without obtaining the lock. This + * means that the error code must only be checked for interruptible locks. + * + * @param lock the lock to wait upon (obtain). + * @param mode the mode in which the lock should be obtained. Unless the lock + * was created with _MALI_OSK_LOCKFLAG_READERWRITER, this must be + * _MALI_OSK_LOCKMODE_RW. + * @return On success, _MALI_OSK_ERR_OK. For interruptible locks, a suitable + * _mali_osk_errcode_t will be returned on failure, and the lock will not be + * obtained. In this case, the error code must be propagated up to the U/K + * interface. + */ +_mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode); + + +/** @brief Signal (release) a lock + * + * Locks may only be signalled by the thread that originally waited upon the + * lock. + * + * @note In the OSU, a flag exists to allow any thread to signal a + * lock. Such functionality is not present in the OSK. + * + * @param lock the lock to signal (release). + * @param mode the mode in which the lock should be obtained. This must match + * the mode in which the lock was waited upon. + */ +void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode ); + +/** @brief Terminate a lock + * + * This terminates a lock and frees all associated resources. + * + * It is a programming error to terminate the lock when it is held (unsignalled) + * by a thread. + * + * @param lock the lock to terminate. + */ +void _mali_osk_lock_term( _mali_osk_lock_t *lock ); +/** @} */ /* end group _mali_osk_lock */ + + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Issue a memory barrier + * + * This defines an arbitrary memory barrier operation, which affects memory + * mapped by _mali_osk_mem_mapregion. It will not be needed for memory + * mapped through _mali_osk_mem_mapioregion. + */ +void _mali_osk_mem_barrier( void ); + +/** @brief Map a physically contiguous region into kernel space + * + * This is primarily used for mapping in registers from resources, and Mali-MMU + * page tables. The mapping is only visable from kernel-space. + * + * Access has to go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @param phys CPU-physical base address of the memory to map in. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * map in + * @param description A textual description of the memory being mapped in. + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure. + */ +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ); + +/** @brief Unmap a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_mapioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt an unmap twice + * - unmap only part of a range obtained through _mali_osk_mem_mapioregion + * - unmap more than the range obtained through _mali_osk_mem_mapioregion + * - unmap an address range that was not successfully mapped using + * _mali_osk_mem_mapioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in. This must be aligned to the system's page size, which is assumed + * to be 4K + * @param size The number of bytes that were originally mapped in. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Allocate and Map a physically contiguous region into kernel space + * + * This is used for allocating physically contiguous regions (such as Mali-MMU + * page tables) and mapping them into kernel space. The mapping is only + * visible from kernel-space. + * + * The alignment of the returned memory is guaranteed to be at least + * _MALI_OSK_CPU_PAGE_SIZE. + * + * Access must go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @note This function is primarily to provide support for OSs that are + * incapable of separating the tasks 'allocate physically contiguous memory' + * and 'map it into kernel space' + * + * @param[out] phys CPU-physical base address of memory that was allocated. + * (*phys) will be guaranteed to be aligned to at least + * _MALI_OSK_CPU_PAGE_SIZE on success. + * + * @param[in] size the number of bytes of physically contiguous memory to + * allocate. This must be a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure, and (*phys) is unmodified. + */ +mali_io_address _mali_osk_mem_allocioregion( u32 *phys, u32 size ); + +/** @brief Free a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_allocioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt a free twice on the same ioregion + * - free only part of a range obtained through _mali_osk_mem_allocioregion + * - free more than the range obtained through _mali_osk_mem_allocioregion + * - free an address range that was not successfully mapped using + * _mali_osk_mem_allocioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in, which was aligned to _MALI_OSK_CPU_PAGE_SIZE. + * @param size The number of bytes that were originally mapped in, which was + * a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_freeioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Request a region of physically contiguous memory + * + * This is used to ensure exclusive access to a region of physically contigous + * memory. + * + * It is acceptable to implement this as a stub. However, it is then the job + * of the System Integrator to ensure that no other device driver will be using + * the physical address ranges used by Mali, while the Mali device driver is + * loaded. + * + * @param phys CPU-physical base address of the memory to request. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * request. + * @param description A textual description of the memory being requested. + * @return _MALI_OSK_ERR_OK on success. Otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ); + +/** @brief Un-request a region of physically contiguous memory + * + * This is used to release a regious of physically contiguous memory previously + * requested through _mali_osk_mem_reqregion, so that other device drivers may + * use it. This will be called at time of Mali device driver termination. + * + * It is a programming error to attempt to: + * - unrequest a region twice + * - unrequest only part of a range obtained through _mali_osk_mem_reqregion + * - unrequest more than the range obtained through _mali_osk_mem_reqregion + * - unrequest an address range that was not successfully requested using + * _mali_osk_mem_reqregion + * + * @param phys CPU-physical base address of the memory to un-request. This must + * be aligned to the system's page size, which is assumed to be 4K + * @param size the number of bytes of physically contiguous address space to + * un-request. + */ +void _mali_osk_mem_unreqregion( u32 phys, u32 size ); + +/** @brief Read from a location currently mapped in through + * _mali_osk_mem_mapioregion + * + * This reads a 32-bit word from a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to read from memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to read from + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @return the 32-bit word from the specified location. + */ +u32 _mali_osk_mem_ioread32( volatile mali_io_address mapping, u32 offset ); + +/** @brief Write to a location currently mapped in through + * _mali_osk_mem_mapioregion + * + * This write a 32-bit word to a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to write to memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to write to + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @param val the 32-bit word to write. + */ +void _mali_osk_mem_iowrite32( volatile mali_io_address mapping, u32 offset, u32 val ); + +/** @brief Flush all CPU caches + * + * This should only be implemented if flushing of the cache is required for + * memory mapped in through _mali_osk_mem_mapregion. + */ +void _mali_osk_cache_flushall( void ); + +/** @brief Flush any caches necessary for the CPU and MALI to have the same view of a range of uncached mapped memory + * + * This should only be implemented if your OS doesn't do a full cache flush (inner & outer) + * after allocating uncached mapped memory. + * + * Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory. + * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches. + * This is required for MALI to have the correct view of the memory. + */ +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ); + +/** @} */ /* end group _mali_osk_low_level_memory */ + + +/** @addtogroup _mali_osk_notification + * + * User space notification framework + * + * Communication with user space of asynchronous events is performed through a + * synchronous call to the \ref u_k_api. + * + * Since the events are asynchronous, the events have to be queued until a + * synchronous U/K API call can be made by user-space. A U/K API call might also + * be received before any event has happened. Therefore the notifications the + * different subsystems wants to send to user space has to be queued for later + * reception, or a U/K API call has to be blocked until an event has occured. + * + * Typical uses of notifications are after running of jobs on the hardware or + * when changes to the system is detected that needs to be relayed to user + * space. + * + * After an event has occured user space has to be notified using some kind of + * message. The notification framework supports sending messages to waiting + * threads or queueing of messages until a U/K API call is made. + * + * The notification queue is a FIFO. There are no restrictions on the numbers + * of readers or writers in the queue. + * + * A message contains what user space needs to identifiy how to handle an + * event. This includes a type field and a possible type specific payload. + * + * A notification to user space is represented by a + * \ref _mali_osk_notification_t object. A sender gets hold of such an object + * using _mali_osk_notification_create(). The buffer given by the + * _mali_osk_notification_t::result_buffer field in the object is used to store + * any type specific data. The other fields are internal to the queue system + * and should not be touched. + * + * @{ */ + +/** @brief Create a notification object + * + * Returns a notification object which can be added to the queue of + * notifications pending for user space transfer. + * + * The implementation will initialize all members of the + * \ref _mali_osk_notification_t object. In particular, the + * _mali_osk_notification_t::result_buffer member will be initialized to point + * to \a size bytes of storage, and that storage will be suitably aligned for + * storage of any structure. That is, the created buffer meets the same + * requirements as _mali_osk_malloc(). + * + * The notification object must be deleted when not in use. Use + * _mali_osk_notification_delete() for deleting it. + * + * @note You \b must \b not call _mali_osk_free() on a \ref _mali_osk_notification_t, + * object, or on a _mali_osk_notification_t::result_buffer. You must only use + * _mali_osk_notification_delete() to free the resources assocaited with a + * \ref _mali_osk_notification_t object. + * + * @param type The notification type + * @param size The size of the type specific buffer to send + * @return Pointer to a notification object with a suitable buffer, or NULL on error. + */ +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ); + +/** @brief Delete a notification object + * + * This must be called to reclaim the resources of a notification object. This + * includes: + * - The _mali_osk_notification_t::result_buffer + * - The \ref _mali_osk_notification_t itself. + * + * A notification object \b must \b not be used after it has been deleted by + * _mali_osk_notification_delete(). + * + * In addition, the notification object may not be deleted while it is in a + * queue. That is, if it has been placed on a queue with + * _mali_osk_notification_queue_send(), then it must not be deleted until + * it has been received by a call to _mali_osk_notification_queue_receive(). + * Otherwise, the queue may be corrupted. + * + * @param object the notification object to delete. + */ +void _mali_osk_notification_delete( _mali_osk_notification_t *object ); + +/** @brief Create a notification queue + * + * Creates a notification queue which can be used to queue messages for user + * delivery and get queued messages from + * + * The queue is a FIFO, and has no restrictions on the numbers of readers or + * writers. + * + * When the queue is no longer in use, it must be terminated with + * \ref _mali_osk_notification_queue_term(). Failure to do so will result in a + * memory leak. + * + * @return Pointer to a new notification queue or NULL on error. + */ +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ); + +/** @brief Destroy a notification queue + * + * Destroys a notification queue and frees associated resources from the queue. + * + * A notification queue \b must \b not be destroyed in the following cases: + * - while there are \ref _mali_osk_notification_t objects in the queue. + * - while there are writers currently acting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_send() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_send() on the queue in the future. + * - while there are readers currently waiting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_receive() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_receive() on the queue in the future. + * + * Therefore, all \ref _mali_osk_notification_t objects must be flushed and + * deleted by the code that makes use of the notification queues, since only + * they know the structure of the _mali_osk_notification_t::result_buffer + * (even if it may only be a flat sturcture). + * + * @note Since the queue is a FIFO, the code using notification queues may + * create its own 'flush' type of notification, to assist in flushing the + * queue. + * + * Once the queue has been destroyed, it must not be used again. + * + * @param queue The queue to destroy + */ +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ); + +/** @brief Schedule notification for delivery + * + * When a \ref _mali_osk_notification_t object has been created successfully + * and set up, it may be added to the queue of objects waiting for user space + * transfer. + * + * The sending will not block if the queue is full. + * + * A \ref _mali_osk_notification_t object \b must \b not be put on two different + * queues at the same time, or enqueued twice onto a single queue before + * reception. However, it is acceptable for it to be requeued \em after reception + * from a call to _mali_osk_notification_queue_receive(), even onto the same queue. + * + * Again, requeuing must also not enqueue onto two different queues at the same + * time, or enqueue onto the same queue twice before reception. + * + * @param queue The notification queue to add this notification to + * @param object The entry to add + */ +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ); + +#if MALI_STATE_TRACKING +/** @brief Receive a notification from a queue + * + * Check if a notification queue is empty. + * + * @param queue The queue to check. + * @return MALI_TRUE if queue is empty, otherwise MALI_FALSE. + */ +mali_bool _mali_osk_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ); +#endif + +/** @brief Receive a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the thread will sleep until one becomes ready. + * Therefore, notifications may not be received into an + * IRQ or 'atomic' context (that is, a context where sleeping is disallowed). + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success. _MALI_OSK_ERR_RESTARTSYSCALL if the sleep was interrupted. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @brief Dequeues a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the function call will return an error code. + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if queue was empty. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @} */ /* end group _mali_osk_notification */ + + +/** @addtogroup _mali_osk_timer + * + * Timers use the OS's representation of time, which are 'ticks'. This is to + * prevent aliasing problems between the internal timer time, and the time + * asked for. + * + * @{ */ + +/** @brief Initialize a timer + * + * Allocates resources for a new timer, and initializes them. This does not + * start the timer. + * + * @return a pointer to the allocated timer object, or NULL on failure. + */ +_mali_osk_timer_t *_mali_osk_timer_init(void); + +/** @brief Start a timer + * + * It is an error to start a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * It is an error to use this to start an already started timer. + * + * The timer will expire in \a ticks_to_expire ticks, at which point, the + * callback function will be invoked with the callback-specific data, + * as registered by _mali_osk_timer_setcallback(). + * + * @param tim the timer to start + * @param ticks_to_expire the amount of time in ticks for the timer to run + * before triggering. + */ +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ); + +/** @brief Modify a timer + * + * Set the absolute time at which a timer will expire, and start it if it is + * stopped. If \a expiry_tick is in the past (determined by + * _mali_osk_time_after() ), the timer fires immediately. + * + * It is an error to modify a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * The timer will expire at absolute time \a expiry_tick, at which point, the + * callback function will be invoked with the callback-specific data, as set + * by _mali_osk_timer_setcallback(). + * + * @param tim the timer to modify, and start if necessary + * @param expiry_tick the \em absolute time in ticks at which this timer should + * trigger. + * + */ +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 expiry_tick); + +/** @brief Stop a timer, and block on its completion. + * + * Stop the timer. When the function returns, it is guaranteed that the timer's + * callback will not be running on any CPU core. + * + * Since stoping the timer blocks on compeletion of the callback, the callback + * may not obtain any mutexes that the caller holds. Otherwise, a deadlock will + * occur. + * + * @note While the callback itself is guaranteed to not be running, work + * enqueued on the IRQ work-queue by the timer (with + * \ref _mali_osk_irq_schedulework()) may still run. The timer callback and IRQ + * bottom-half handler must take this into account. + * + * It is legal to stop an already stopped timer. + * + * @param tim the timer to stop. + * + */ +void _mali_osk_timer_del( _mali_osk_timer_t *tim ); + +/** @brief Set a timer's callback parameters. + * + * This must be called at least once before a timer is started/modified. + * + * After a timer has been stopped or expires, the callback remains set. This + * means that restarting the timer will call the same function with the same + * parameters on expiry. + * + * @param tim the timer to set callback on. + * @param callback Function to call when timer expires + * @param data Function-specific data to supply to the function on expiry. + */ +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ); + +/** @brief Terminate a timer, and deallocate resources. + * + * The timer must first be stopped by calling _mali_osk_timer_del(). + * + * It is a programming error for _mali_osk_timer_term() to be called on: + * - timer that is currently running + * - a timer that is currently executing its callback. + * + * @param tim the timer to deallocate. + */ +void _mali_osk_timer_term( _mali_osk_timer_t *tim ); +/** @} */ /* end group _mali_osk_timer */ + + +/** @defgroup _mali_osk_time OSK Time functions + * + * \ref _mali_osk_time use the OS's representation of time, which are + * 'ticks'. This is to prevent aliasing problems between the internal timer + * time, and the time asked for. + * + * OS tick time is measured as a u32. The time stored in a u32 may either be + * an absolute time, or a time delta between two events. Whilst it is valid to + * use math opeartors to \em change the tick value represented as a u32, it + * is often only meaningful to do such operations on time deltas, rather than + * on absolute time. However, it is meaningful to add/subtract time deltas to + * absolute times. + * + * Conversion between tick time and milliseconds (ms) may not be loss-less, + * and are \em implementation \em depenedant. + * + * Code use OS time must take this into account, since: + * - a small OS time may (or may not) be rounded + * - a large time may (or may not) overflow + * + * @{ */ + +/** @brief Return whether ticka occurs after tickb + * + * Some OSs handle tick 'rollover' specially, and so can be more robust against + * tick counters rolling-over. This function must therefore be called to + * determine if a time (in ticks) really occurs after another time (in ticks). + * + * @param ticka ticka + * @param tickb tickb + * @return non-zero if ticka represents a time that occurs after tickb. + * Zero otherwise. + */ +int _mali_osk_time_after( u32 ticka, u32 tickb ); + +/** @brief Convert milliseconds to OS 'ticks' + * + * @param ms time interval in milliseconds + * @return the corresponding time interval in OS ticks. + */ +u32 _mali_osk_time_mstoticks( u32 ms ); + +/** @brief Convert OS 'ticks' to milliseconds + * + * @param ticks time interval in OS ticks. + * @return the corresponding time interval in milliseconds + */ +u32 _mali_osk_time_tickstoms( u32 ticks ); + + +/** @brief Get the current time in OS 'ticks'. + * @return the current time in OS 'ticks'. + */ +u32 _mali_osk_time_tickcount( void ); + +/** @brief Cause a microsecond delay + * + * The delay will have microsecond resolution, and is necessary for correct + * operation of the driver. At worst, the delay will be \b at least \a usecs + * microseconds, and so may be (significantly) more. + * + * This function may be implemented as a busy-wait, which is the most sensible + * implementation. On OSs where there are situations in which a thread must not + * sleep, this is definitely implemented as a busy-wait. + * + * @param usecs the number of microseconds to wait for. + */ +void _mali_osk_time_ubusydelay( u32 usecs ); + +/** @brief Return time in nano seconds, since any given reference. + * + * @return Time in nano seconds + */ +u64 _mali_osk_time_get_ns( void ); + + +/** @} */ /* end group _mali_osk_time */ + +/** @defgroup _mali_osk_math OSK Math + * @{ */ + +/** @brief Count Leading Zeros (Little-endian) + * + * @note This function must be implemented to support the reference + * implementation of _mali_osk_find_first_zero_bit, as defined in + * mali_osk_bitops.h. + * + * @param val 32-bit words to count leading zeros on + * @return the number of leading zeros. + */ +u32 _mali_osk_clz( u32 val ); +/** @} */ /* end group _mali_osk_math */ + + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Output a device driver debug message. + * + * The interpretation of \a fmt is the same as the \c format parameter in + * _mali_osu_vsnprintf(). + * + * @param fmt a _mali_osu_vsnprintf() style format string + * @param ... a variable-number of parameters suitable for \a fmt + */ +void _mali_osk_dbgmsg( const char *fmt, ... ); + +/** @brief Abnormal process abort. + * + * Terminates the caller-process if this function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h. + * + * This function will never return - because to continue from a Debug assert + * could cause even more problems, and hinder debugging of the initial problem. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_abort(void); + +/** @brief Sets breakpoint at point where function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h, + * to assist in debugging. If debugging at this level is not required, then this + * function may be implemented as a stub. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_break(void); + +/** @brief Return an identificator for calling process. + * + * @return Identificator for calling process. + */ +u32 _mali_osk_get_pid(void); + +/** @brief Return an identificator for calling thread. + * + * @return Identificator for calling thread. + */ +u32 _mali_osk_get_tid(void); + +/** @} */ /* end group _mali_osk_miscellaneous */ + + +/** @} */ /* end group osuapi */ + +/** @} */ /* end group uddapi */ + + +#ifdef __cplusplus +} +#endif + +#include "mali_osk_specific.h" /* include any per-os specifics */ + +/* Check standard inlines */ +#ifndef MALI_STATIC_INLINE + #error MALI_STATIC_INLINE not defined on your OS +#endif + +#ifndef MALI_NON_STATIC_INLINE + #error MALI_NON_STATIC_INLINE not defined on your OS +#endif + +#endif /* __MALI_OSK_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_bitops.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_bitops.h new file mode 100644 index 00000000000..28026ba1e6d --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_bitops.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_bitops.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_BITOPS_H__ +#define __MALI_OSK_BITOPS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +MALI_STATIC_INLINE void _mali_internal_clear_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) &= ~(1 << bit); +} + +MALI_STATIC_INLINE void _mali_internal_set_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) |= (1 << bit); +} + +MALI_STATIC_INLINE u32 _mali_internal_test_bit( u32 bit, u32 value ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + return value & (1 << bit); +} + +MALI_STATIC_INLINE int _mali_internal_find_first_zero_bit( u32 value ) +{ + u32 inverted; + u32 negated; + u32 isolated; + u32 leading_zeros; + + /* Begin with xxx...x0yyy...y, where ys are 1, number of ys is in range 0..31 */ + inverted = ~value; /* zzz...z1000...0 */ + /* Using count_trailing_zeros on inverted value - + * See ARM System Developers Guide for details of count_trailing_zeros */ + + /* Isolate the zero: it is preceeded by a run of 1s, so add 1 to it */ + negated = (u32)-inverted ; /* -a == ~a + 1 (mod 2^n) for n-bit numbers */ + /* negated = xxx...x1000...0 */ + + isolated = negated & inverted ; /* xxx...x1000...0 & zzz...z1000...0, zs are ~xs */ + /* And so the first zero bit is in the same position as the 1 == number of 1s that preceeded it + * Note that the output is zero if value was all 1s */ + + leading_zeros = _mali_osk_clz( isolated ); + + return 31 - leading_zeros; +} + + +/** @defgroup _mali_osk_bitops OSK Non-atomic Bit-operations + * @{ */ + +/** + * These bit-operations do not work atomically, and so locks must be used if + * atomicity is required. + * + * Reference implementations for Little Endian are provided, and so it should + * not normally be necessary to re-implement these. Efficient bit-twiddling + * techniques are used where possible, implemented in portable C. + * + * Note that these reference implementations rely on _mali_osk_clz() being + * implemented. + */ + +/** @brief Clear a bit in a sequence of 32-bit words + * @param nr bit number to clear, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_clear_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_clear_bit( nr, addr ); +} + +/** @brief Set a bit in a sequence of 32-bit words + * @param nr bit number to set, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_set_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_set_bit( nr, addr ); +} + +/** @brief Test a bit in a sequence of 32-bit words + * @param nr bit number to test, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + * @return zero if bit was clear, non-zero if set. Do not rely on the return + * value being related to the actual word under test. + */ +MALI_STATIC_INLINE u32 _mali_osk_test_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + return _mali_internal_test_bit( nr, *addr ); +} + +/* Return maxbit if not found */ +/** @brief Find the first zero bit in a sequence of 32-bit words + * @param addr starting point for search. + * @param maxbit the maximum number of bits to search + * @return the number of the first zero bit found, or maxbit if none were found + * in the specified range. + */ +MALI_STATIC_INLINE u32 _mali_osk_find_first_zero_bit( const u32 *addr, u32 maxbit ) +{ + u32 total; + + for ( total = 0; total < maxbit; total += 32, ++addr ) + { + int result; + result = _mali_internal_find_first_zero_bit( *addr ); + + /* non-negative signifies the bit was found */ + if ( result >= 0 ) + { + total += (u32)result; + break; + } + } + + /* Now check if we reached maxbit or above */ + if ( total >= maxbit ) + { + total = maxbit; + } + + return total; /* either the found bit nr, or maxbit if not found */ +} +/** @} */ /* end group _mali_osk_bitops */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_BITOPS_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_list.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_list.h new file mode 100644 index 00000000000..a8d15f2a107 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_list.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_list.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_LIST_H__ +#define __MALI_OSK_LIST_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +MALI_STATIC_INLINE void __mali_osk_list_add(_mali_osk_list_t *new_entry, _mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = new_entry; + new_entry->next = next; + new_entry->prev = prev; + prev->next = new_entry; +} + +MALI_STATIC_INLINE void __mali_osk_list_del(_mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +/** @addtogroup _mali_osk_list + * @{ */ + +/** Reference implementations of Doubly-linked Circular Lists are provided. + * There is often no need to re-implement these. + * + * @note The implementation may differ subtly from any lists the OS provides. + * For this reason, these lists should not be mixed with OS-specific lists + * inside the OSK/UKK implementation. */ + +/** @brief Initialize a list element. + * + * All list elements must be initialized before use. + * + * Do not use on any list element that is present in a list without using + * _mali_osk_list_del first, otherwise this will break the list. + * + * @param list the list element to initialize + */ +MALI_STATIC_INLINE void _mali_osk_list_init( _mali_osk_list_t *list ) +{ + list->next = list; + list->prev = list; +} + +/** @brief Insert a single list element after an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the first element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the next + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_add( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list, list->next); +} + +/** @brief Insert a single list element before an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the last element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the previous + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_addtail( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list->prev, list); +} + +/** @brief Remove a single element from a list + * + * The element will no longer be present in the list. The removed list element + * will be uninitialized, and so should not be traversed. It must be + * initialized before further use. + * + * @param list the list element to remove. + */ +MALI_STATIC_INLINE void _mali_osk_list_del( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); +} + +/** @brief Remove a single element from a list, and re-initialize it + * + * The element will no longer be present in the list. The removed list element + * will initialized, and so can be used as normal. + * + * @param list the list element to remove and initialize. + */ +MALI_STATIC_INLINE void _mali_osk_list_delinit( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); + _mali_osk_list_init(list); +} + +/** @brief Determine whether a list is empty. + * + * An empty list is one that contains a single element that points to itself. + * + * @param list the list to check. + * @return non-zero if the list is empty, and zero otherwise. + */ +MALI_STATIC_INLINE int _mali_osk_list_empty( _mali_osk_list_t *list ) +{ + return list->next == list; +} + +/** @brief Move a list element from one list to another. + * + * The list element must be initialized. + * + * As an example, moving a list item to the head of a new list causes this item + * to be the first element in the new list. + * + * @param move the list element to move + * @param list the new list into which the element will be inserted, as the next + * element in the list. + */ +MALI_STATIC_INLINE void _mali_osk_list_move( _mali_osk_list_t *move_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_del(move_entry->prev, move_entry->next); + _mali_osk_list_add(move_entry, list); +} + +/** @brief Join two lists + * + * The list element must be initialized. + * + * Allows you to join a list into another list at a specific location + * + * @param list the new list to add + * @param at the location in a list to add the new list into + */ +MALI_STATIC_INLINE void _mali_osk_list_splice( _mali_osk_list_t *list, _mali_osk_list_t *at ) +{ + if (!_mali_osk_list_empty(list)) + { + /* insert all items from 'list' after 'at' */ + _mali_osk_list_t *first = list->next; + _mali_osk_list_t *last = list->prev; + _mali_osk_list_t *split = at->next; + + first->prev = at; + at->next = first; + + last->next = split; + split->prev = last; + } +} +/** @} */ /* end group _mali_osk_list */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_LIST_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_mali.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_mali.h new file mode 100644 index 00000000000..9a046fb91eb --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_osk_mali.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_mali.h + * Defines the OS abstraction layer which is specific for the Mali kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_MALI_H__ +#define __MALI_OSK_MALI_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Initialize the OSK layer + * + * This function is used to setup any initialization of OSK functionality, if + * required. + * + * This must be the first function called from the common code, specifically, + * from the common code entry-point, mali_kernel_constructor. + * + * The OS-integration into the OS's kernel must handle calling of + * mali_kernel_constructor when the device driver is loaded. + * + * @return _MALI_OSK_ERR_OK on success, or a suitable _mali_osk_errcode_t on + * failure. + */ +_mali_osk_errcode_t _mali_osk_init( void ); + +/** @brief Terminate the OSK layer + * + * This function is used to terminate any resources initialized by + * _mali_osk_init. + * + * This must be the last function called from the common code, specifically, + * from the common code closedown function, mali_kernel_destructor, and the + * error path in mali_kernel_constructor. + * + * The OS-integration into the OS's kernel must handle calling of + * mali_kernel_destructor when the device driver is terminated. + */ +void _mali_osk_term( void ); + +/** @brief Read the Mali Resource configuration + * + * Populates a _mali_arch_resource_t array from configuration settings, which + * are stored in an OS-specific way. + * + * For example, these may be compiled in to a static structure, or read from + * the filesystem at startup. + * + * On failure, do not call _mali_osk_resources_term. + * + * @param arch_config a pointer to the store the pointer to the resources + * @param num_resources the number of resources read + * @return _MALI_OSK_ERR_OK on success. _MALI_OSK_ERR_NOMEM on allocation + * error. For other failures, a suitable _mali_osk_errcode_t is returned. + */ +_mali_osk_errcode_t _mali_osk_resources_init( _mali_osk_resource_t **arch_config, u32 *num_resources ); + +/** @brief Free resources allocated by _mali_osk_resources_init. + * + * Frees the _mali_arch_resource_t array allocated by _mali_osk_resources_init + * + * @param arch_config a pointer to the stored the pointer to the resources + * @param num_resources the number of resources in the array + */ +void _mali_osk_resources_term( _mali_osk_resource_t **arch_config, u32 num_resources); +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Initialize a user-space accessible memory range + * + * This initializes a virtual address range such that it is reserved for the + * current process, but does not map any physical pages into this range. + * + * This function may initialize or adjust any members of the + * mali_memory_allocation \a descriptor supplied, before the physical pages are + * mapped in with _mali_osk_mem_mapregion_map(). + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * The \a descriptor's process_addr_mapping_info member can be modified to + * allocate OS-specific information. Note that on input, this will be a + * ukk_private word from the U/K inteface, as inserted by _mali_ukk_mem_mmap(). + * This is used to pass information from the U/K interface to the OSK interface, + * if necessary. The precise usage of the process_addr_mapping_info member + * depends on the U/K implementation of _mali_ukk_mem_mmap(). + * + * Therefore, the U/K implementation of _mali_ukk_mem_mmap() and the OSK + * implementation of _mali_osk_mem_mapregion_init() must agree on the meaning and + * usage of the ukk_private word and process_addr_mapping_info member. + * + * Refer to \ref u_k_api for more information on the U/K interface. + * + * On successful return, \a descriptor's mapping member will be correct for + * use with _mali_osk_mem_mapregion_term() and _mali_osk_mem_mapregion_map(). + * + * @param descriptor the mali_memory_allocation to initialize. + */ +_mali_osk_errcode_t _mali_osk_mem_mapregion_init( mali_memory_allocation * descriptor ); + +/** @brief Terminate a user-space accessible memory range + * + * This terminates a virtual address range reserved in the current user process, + * where none, some or all of the virtual address ranges have mappings to + * physical pages. + * + * It will unmap any physical pages that had been mapped into a reserved + * virtual address range for the current process, and then releases the virtual + * address range. Any extra book-keeping information or resources allocated + * during _mali_osk_mem_mapregion_init() will also be released. + * + * The \a descriptor itself is not freed - this must be handled by the caller of + * _mali_osk_mem_mapregion_term(). + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, descriptor->flags bits are reserved for + * future expansion + * + * @param descriptor the mali_memory_allocation to terminate. + */ +void _mali_osk_mem_mapregion_term( mali_memory_allocation * descriptor ); + +/** @brief Map physical pages into a user process's virtual address range + * + * This is used to map a number of physically contigous pages into a + * user-process's virtual address range, which was previously reserved by a + * call to _mali_osk_mem_mapregion_init(). + * + * This need not provide a mapping for the entire virtual address range + * reserved for \a descriptor - it may be used to map single pages per call. + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * The function may supply \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC. + * In this case, \a size must be set to \ref _MALI_OSK_CPU_PAGE_SIZE, and the function + * will allocate the physical page itself. The physical address of the + * allocated page will be returned through \a phys_addr. + * + * It is an error to set \a size != \ref _MALI_OSK_CPU_PAGE_SIZE while + * \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, + * since it is not always possible for OSs to support such a setting through this + * interface. + * + * @note \b IMPORTANT: This code must validate the input parameters. If the + * range defined by \a offset and \a size is outside the range allocated in + * \a descriptor, then this function \b MUST not attempt any mapping, and must + * instead return a suitable \ref _mali_osk_errcode_t \b failure code. + * + * @param[in,out] descriptor the mali_memory_allocation representing the + * user-process's virtual address range to map into. + * + * @param[in] offset the offset into the virtual address range. This is only added + * to the mapping member of the \a descriptor, and not the \a phys_addr parameter. + * It must be a multiple of \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in,out] phys_addr a pointer to the physical base address to begin the + * mapping from. If \a size == \ref _MALI_OSK_CPU_PAGE_SIZE and + * \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, then this + * function will allocate the physical page itself, and return the + * physical address of the page through \a phys_addr, which will be aligned to + * \ref _MALI_OSK_CPU_PAGE_SIZE. Otherwise, \a *phys_addr must be aligned to + * \ref _MALI_OSK_CPU_PAGE_SIZE, and is unmodified after the call. + * \a phys_addr is unaffected by the \a offset parameter. + * + * @param[in] size the number of bytes to map in. This must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @return _MALI_OSK_ERR_OK on sucess, otherwise a _mali_osk_errcode_t value + * on failure + * + * @note could expand to use _mali_osk_mem_mapregion_flags_t instead of + * \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, but note that we must + * also modify the mali process address manager in the mmu/memory engine code. + */ +_mali_osk_errcode_t _mali_osk_mem_mapregion_map( mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size ); + + +/** @brief Unmap physical pages from a user process's virtual address range + * + * This is used to unmap a number of physically contigous pages from a + * user-process's virtual address range, which were previously mapped by a + * call to _mali_osk_mem_mapregion_map(). If the range specified was allocated + * from OS memory, then that memory will be returned to the OS. Whilst pages + * will be mapped out, the Virtual address range remains reserved, and at the + * same base address. + * + * When this function is used to unmap pages from OS memory + * (_mali_osk_mem_mapregion_map() was called with *phys_addr == + * \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC), then the \a flags must + * include \ref _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR. This is because + * it is not always easy for an OS implementation to discover whether the + * memory was OS allocated or not (and so, how it should release the memory). + * + * For this reason, only a range of pages of the same allocation type (all OS + * allocated, or none OS allocacted) may be unmapped in one call. Multiple + * calls must be made if allocations of these different types exist across the + * entire region described by the \a descriptor. + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * @param[in,out] descriptor the mali_memory_allocation representing the + * user-process's virtual address range to map into. + * + * @param[in] offset the offset into the virtual address range. This is only added + * to the mapping member of the \a descriptor. \a offset must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in] size the number of bytes to unmap. This must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in] flags specifies how the memory should be unmapped. For a range + * of pages that were originally OS allocated, this must have + * \ref _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR set. + */ +void _mali_osk_mem_mapregion_unmap( mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags ); +/** @} */ /* end group _mali_osk_low_level_memory */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_MALI_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_uk_types.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_uk_types.h new file mode 100644 index 00000000000..9a1583658c9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_uk_types.h @@ -0,0 +1,1148 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __MALI_UK_TYPES_H__ +#define __MALI_UK_TYPES_H__ + +/* + * NOTE: Because this file can be included from user-side and kernel-side, + * it is up to the includee to ensure certain typedefs (e.g. u32) are already + * defined when #including this. + */ +#include "regs/mali_200_regs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * @{ + */ + +/** @defgroup _mali_uk_core U/K Core + * @{ */ + +/** Definition of subsystem numbers, to assist in creating a unique identifier + * for each U/K call. + * + * @see _mali_uk_functions */ +typedef enum +{ + _MALI_UK_CORE_SUBSYSTEM, /**< Core Group of U/K calls */ + _MALI_UK_MEMORY_SUBSYSTEM, /**< Memory Group of U/K calls */ + _MALI_UK_PP_SUBSYSTEM, /**< Fragment Processor Group of U/K calls */ + _MALI_UK_GP_SUBSYSTEM, /**< Vertex Processor Group of U/K calls */ + _MALI_UK_PROFILING_SUBSYSTEM, /**< Profiling Group of U/K calls */ + _MALI_UK_PMM_SUBSYSTEM, /**< Power Management Module Group of U/K calls */ + _MALI_UK_VSYNC_SUBSYSTEM, /**< VSYNC Group of U/K calls */ +} _mali_uk_subsystem_t; + +/** Within a function group each function has its unique sequence number + * to assist in creating a unique identifier for each U/K call. + * + * An ordered pair of numbers selected from + * ( \ref _mali_uk_subsystem_t,\ref _mali_uk_functions) will uniquely identify the + * U/K call across all groups of functions, and all functions. */ +typedef enum +{ + /** Core functions */ + + _MALI_UK_OPEN = 0, /**< _mali_ukk_open() */ + _MALI_UK_CLOSE, /**< _mali_ukk_close() */ + _MALI_UK_GET_SYSTEM_INFO_SIZE, /**< _mali_ukk_get_system_info_size() */ + _MALI_UK_GET_SYSTEM_INFO, /**< _mali_ukk_get_system_info() */ + _MALI_UK_WAIT_FOR_NOTIFICATION, /**< _mali_ukk_wait_for_notification() */ + _MALI_UK_GET_API_VERSION, /**< _mali_ukk_get_api_version() */ + _MALI_UK_POST_NOTIFICATION, /**< _mali_ukk_post_notification() */ + + /** Memory functions */ + + _MALI_UK_INIT_MEM = 0, /**< _mali_ukk_init_mem() */ + _MALI_UK_TERM_MEM, /**< _mali_ukk_term_mem() */ + _MALI_UK_GET_BIG_BLOCK, /**< _mali_ukk_get_big_block() */ + _MALI_UK_FREE_BIG_BLOCK, /**< _mali_ukk_free_big_block() */ + _MALI_UK_MAP_MEM, /**< _mali_ukk_mem_mmap() */ + _MALI_UK_UNMAP_MEM, /**< _mali_ukk_mem_munmap() */ + _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, /**< _mali_ukk_mem_get_mmu_page_table_dump_size() */ + _MALI_UK_DUMP_MMU_PAGE_TABLE, /**< _mali_ukk_mem_dump_mmu_page_table() */ + _MALI_UK_ATTACH_UMP_MEM, /**< _mali_ukk_attach_ump_mem() */ + _MALI_UK_RELEASE_UMP_MEM, /**< _mali_ukk_release_ump_mem() */ + _MALI_UK_MAP_EXT_MEM, /**< _mali_uku_map_external_mem() */ + _MALI_UK_UNMAP_EXT_MEM, /**< _mali_uku_unmap_external_mem() */ + _MALI_UK_VA_TO_MALI_PA, /**< _mali_uku_va_to_mali_pa() */ + + /** Common functions for each core */ + + _MALI_UK_START_JOB = 0, /**< Start a Fragment/Vertex Processor Job on a core */ + _MALI_UK_ABORT_JOB, /**< Abort a job */ + _MALI_UK_GET_NUMBER_OF_CORES, /**< Get the number of Fragment/Vertex Processor cores */ + _MALI_UK_GET_CORE_VERSION, /**< Get the Fragment/Vertex Processor version compatible with all cores */ + + /** Fragment Processor Functions */ + + _MALI_UK_PP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_pp_start_job() */ + _MALI_UK_PP_ABORT_JOB = _MALI_UK_ABORT_JOB, /**< _mali_ukk_pp_abort_job() */ + _MALI_UK_GET_PP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_pp_number_of_cores() */ + _MALI_UK_GET_PP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_pp_core_version() */ + + /** Vertex Processor Functions */ + + _MALI_UK_GP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_gp_start_job() */ + _MALI_UK_GP_ABORT_JOB = _MALI_UK_ABORT_JOB, /**< _mali_ukk_gp_abort_job() */ + _MALI_UK_GET_GP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_gp_number_of_cores() */ + _MALI_UK_GET_GP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_gp_core_version() */ + _MALI_UK_GP_SUSPEND_RESPONSE, /**< _mali_ukk_gp_suspend_response() */ + + /** Profiling functions */ + + _MALI_UK_PROFILING_START = 0, /**< __mali_uku_profiling_start() */ + _MALI_UK_PROFILING_ADD_EVENT, /**< __mali_uku_profiling_add_event() */ + _MALI_UK_PROFILING_STOP, /**< __mali_uku_profiling_stop() */ + _MALI_UK_PROFILING_GET_EVENT, /**< __mali_uku_profiling_get_event() */ + _MALI_UK_PROFILING_CLEAR, /**< __mali_uku_profiling_clear() */ + +#if USING_MALI_PMM + /** Power Management Module Functions */ + _MALI_UK_PMM_EVENT_MESSAGE = 0, /**< Raise an event message */ +#endif + + /** VSYNC reporting fuctions */ + _MALI_UK_VSYNC_EVENT_REPORT = 0, /**< _mali_ukk_vsync_event_report() */ + +} _mali_uk_functions; + +/** @brief Get the size necessary for system info + * + * @see _mali_ukk_get_system_info_size() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of buffer necessary to hold system information data, in bytes */ +} _mali_uk_get_system_info_size_s; + + +/** @defgroup _mali_uk_getsysteminfo U/K Get System Info + * @{ */ + +/** + * Type definition for the core version number. + * Used when returning the version number read from a core + * + * Its format is that of the 32-bit Version register for a particular core. + * Refer to the "Mali200 and MaliGP2 3D Graphics Processor Technical Reference + * Manual", ARM DDI 0415C, for more information. + */ +typedef u32 _mali_core_version; + +/** + * Enum values for the different modes the driver can be put in. + * Normal is the default mode. The driver then uses a job queue and takes job objects from the clients. + * Job completion is reported using the _mali_ukk_wait_for_notification call. + * The driver blocks this io command until a job has completed or failed or a timeout occurs. + * + * The 'raw' mode is reserved for future expansion. + */ +typedef enum _mali_driver_mode +{ + _MALI_DRIVER_MODE_RAW = 1, /**< Reserved for future expansion */ + _MALI_DRIVER_MODE_NORMAL = 2 /**< Normal mode of operation */ +} _mali_driver_mode; + +/** @brief List of possible cores + * + * add new entries to the end of this enum */ +typedef enum _mali_core_type +{ + _MALI_GP2 = 2, /**< MaliGP2 Programmable Vertex Processor */ + _MALI_200 = 5, /**< Mali200 Programmable Fragment Processor */ + _MALI_400_GP = 6, /**< Mali400 Programmable Vertex Processor */ + _MALI_400_PP = 7, /**< Mali400 Programmable Fragment Processor */ + /* insert new core here, do NOT alter the existing values */ +} _mali_core_type; + +/** @brief Information about each Mali Core + * + * Information is stored in a linked list, which is stored entirely in the + * buffer pointed to by the system_info member of the + * _mali_uk_get_system_info_s arguments provided to _mali_ukk_get_system_info() + * + * Both Fragment Processor (PP) and Vertex Processor (GP) cores are represented + * by this struct. + * + * The type is reported by the type field, _mali_core_info::_mali_core_type. + * + * Each core is given a unique Sequence number identifying it, the core_nr + * member. + * + * Flags are taken directly from the resource's flags, and are currently unused. + * + * Multiple mali_core_info structs are linked in a single linked list using the next field + */ +typedef struct _mali_core_info +{ + _mali_core_type type; /**< Type of core */ + _mali_core_version version; /**< Core Version, as reported by the Core's Version Register */ + u32 reg_address; /**< Address of Registers */ + u32 core_nr; /**< Sequence number */ + u32 flags; /**< Flags. Currently Unused. */ + struct _mali_core_info * next; /**< Next core in Linked List */ +} _mali_core_info; + +/** @brief Capabilities of Memory Banks + * + * These may be used to restrict memory banks for certain uses. They may be + * used when access is not possible (e.g. Bus does not support access to it) + * or when access is possible but not desired (e.g. Access is slow). + * + * In the case of 'possible but not desired', there is no way of specifying + * the flags as an optimization hint, so that the memory could be used as a + * last resort. + * + * @see _mali_mem_info + */ +typedef enum _mali_bus_usage +{ + + _MALI_PP_READABLE = (1<<0), /** Readable by the Fragment Processor */ + _MALI_PP_WRITEABLE = (1<<1), /** Writeable by the Fragment Processor */ + _MALI_GP_READABLE = (1<<2), /** Readable by the Vertex Processor */ + _MALI_GP_WRITEABLE = (1<<3), /** Writeable by the Vertex Processor */ + _MALI_CPU_READABLE = (1<<4), /** Readable by the CPU */ + _MALI_CPU_WRITEABLE = (1<<5), /** Writeable by the CPU */ + _MALI_MMU_READABLE = _MALI_PP_READABLE | _MALI_GP_READABLE, /** Readable by the MMU (including all cores behind it) */ + _MALI_MMU_WRITEABLE = _MALI_PP_WRITEABLE | _MALI_GP_WRITEABLE, /** Writeable by the MMU (including all cores behind it) */ +} _mali_bus_usage; + +/** @brief Information about the Mali Memory system + * + * Information is stored in a linked list, which is stored entirely in the + * buffer pointed to by the system_info member of the + * _mali_uk_get_system_info_s arguments provided to _mali_ukk_get_system_info() + * + * Each element of the linked list describes a single Mali Memory bank. + * Each allocation can only come from one bank, and will not cross multiple + * banks. + * + * Each bank is uniquely identified by its identifier member. On Mali-nonMMU + * systems, to allocate from this bank, the value of identifier must be passed + * as the type_id member of the _mali_uk_get_big_block_s arguments to + * _mali_ukk_get_big_block. + * + * On Mali-MMU systems, there is only one bank, which describes the maximum + * possible address range that could be allocated (which may be much less than + * the available physical memory) + * + * The flags member describes the capabilities of the memory. It is an error + * to attempt to build a job for a particular core (PP or GP) when the memory + * regions used do not have the capabilities for supporting that core. This + * would result in a job abort from the Device Driver. + * + * For example, it is correct to build a PP job where read-only data structures + * are taken from a memory with _MALI_PP_READABLE set and + * _MALI_PP_WRITEABLE clear, and a framebuffer with _MALI_PP_WRITEABLE set and + * _MALI_PP_READABLE clear. However, it would be incorrect to use a framebuffer + * where _MALI_PP_WRITEABLE is clear. + */ +typedef struct _mali_mem_info +{ + u32 size; /**< Size of the memory bank in bytes */ + _mali_bus_usage flags; /**< Capabilitiy flags of the memory */ + u32 maximum_order_supported; /**< log2 supported size */ + u32 identifier; /**< Unique identifier, to be used in allocate calls */ + struct _mali_mem_info * next; /**< Next List Link */ +} _mali_mem_info; + +/** @brief Info about the whole Mali system. + * + * This Contains a linked list of the cores and memory banks available. Each + * list pointer will remain inside the system_info buffer supplied in the + * _mali_uk_get_system_info_s arguments to a _mali_ukk_get_system_info call. + * + * The has_mmu member must be inspected to ensure the correct group of + * Memory function calls is obtained - that is, those for either Mali-MMU + * or Mali-nonMMU. @see _mali_uk_memory + */ +typedef struct _mali_system_info +{ + _mali_core_info * core_info; /**< List of _mali_core_info structures */ + _mali_mem_info * mem_info; /**< List of _mali_mem_info structures */ + u32 has_mmu; /**< Non-zero if Mali-MMU present. Zero otherwise. */ + _mali_driver_mode drivermode; /**< Reserved. Must always be _MALI_DRIVER_MODE_NORMAL */ +} _mali_system_info; + +/** @brief Arguments to _mali_ukk_get_system_info() + * + * A buffer of the size returned by _mali_ukk_get_system_info_size() must be + * allocated, and the pointer to this buffer must be written into the + * system_info member. The buffer must be suitably aligned for storage of + * the _mali_system_info structure - for example, one returned by + * _mali_osk_malloc(), which will be suitably aligned for any structure. + * + * The ukk_private member must be set to zero by the user-side. Under an OS + * implementation, the U/K interface must write in the user-side base address + * into the ukk_private member, so that the common code in + * _mali_ukk_get_system_info() can determine how to adjust the pointers such + * that they are sensible from user space. Leaving ukk_private as NULL implies + * that no pointer adjustment is necessary - which will be the case on a + * bare-metal/RTOS system. + * + * @see _mali_system_info + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [in] size of buffer provided to store system information data */ + _mali_system_info * system_info; /**< [in,out] pointer to buffer to store system information data. No initialisation of buffer required on input. */ + u32 ukk_private; /**< [in] Kernel-side private word inserted by certain U/K interface implementations. Caller must set to Zero. */ +} _mali_uk_get_system_info_s; +/** @} */ /* end group _mali_uk_getsysteminfo */ + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @defgroup _mali_uk_gp_suspend_response_s Vertex Processor Suspend Response + * @{ */ + +/** @brief Arguments for _mali_ukk_gp_suspend_response() + * + * When _mali_wait_for_notification() receives notification that a + * Vertex Processor job was suspended, you need to send a response to indicate + * what needs to happen with this job. You can either abort or resume the job. + * + * - set @c code to indicate response code. This is either @c _MALIGP_JOB_ABORT or + * @c _MALIGP_JOB_RESUME_WITH_NEW_HEAP to indicate you will provide a new heap + * for the job that will resolve the out of memory condition for the job. + * - copy the @c cookie value from the @c _mali_uk_gp_job_suspended_s notification; + * this is an identifier for the suspended job + * - set @c arguments[0] and @c arguments[1] to zero if you abort the job. If + * you resume it, @c argument[0] should specify the Mali start address for the new + * heap and @c argument[1] the Mali end address of the heap. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + */ +typedef enum _maligp_job_suspended_response_code +{ + _MALIGP_JOB_ABORT, /**< Abort the Vertex Processor job */ + _MALIGP_JOB_RESUME_WITH_NEW_HEAP /**< Resume the Vertex Processor job with a new heap */ +} _maligp_job_suspended_response_code; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] cookie from the _mali_uk_gp_job_suspended_s notification */ + _maligp_job_suspended_response_code code; /**< [in] abort or resume response code, see \ref _maligp_job_suspended_response_code */ + u32 arguments[2]; /**< [in] 0 when aborting a job. When resuming a job, the Mali start and end address for a new heap to resume the job with */ +} _mali_uk_gp_suspend_response_s; + +/** @} */ /* end group _mali_uk_gp_suspend_response_s */ + +/** @defgroup _mali_uk_gpstartjob_s Vertex Processor Start Job + * @{ */ + +/** @brief Status indicating the result of starting a Vertex or Fragment processor job */ +typedef enum +{ + _MALI_UK_START_JOB_STARTED, /**< Job started */ + _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED, /**< Job started and bumped a lower priority job that was pending execution */ + _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE /**< Job could not be started at this time. Try starting the job again */ +} _mali_uk_start_job_status; + +/** @brief Status indicating the result of the execution of a Vertex or Fragment processor job */ + +typedef enum +{ + _MALI_UK_JOB_STATUS_END_SUCCESS = 1<<(16+0), + _MALI_UK_JOB_STATUS_END_OOM = 1<<(16+1), + _MALI_UK_JOB_STATUS_END_ABORT = 1<<(16+2), + _MALI_UK_JOB_STATUS_END_TIMEOUT_SW = 1<<(16+3), + _MALI_UK_JOB_STATUS_END_HANG = 1<<(16+4), + _MALI_UK_JOB_STATUS_END_SEG_FAULT = 1<<(16+5), + _MALI_UK_JOB_STATUS_END_ILLEGAL_JOB = 1<<(16+6), + _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR = 1<<(16+7), + _MALI_UK_JOB_STATUS_END_SHUTDOWN = 1<<(16+8), + _MALI_UK_JOB_STATUS_END_SYSTEM_UNUSABLE = 1<<(16+9) +} _mali_uk_job_status; + +#define MALIGP2_NUM_REGS_FRAME (6) + +/** @brief Arguments for _mali_ukk_gp_start_job() + * + * To start a Vertex Processor job + * - associate the request with a reference to a @c mali_gp_job_info by setting + * user_job_ptr to the address of the @c mali_gp_job_info of the job. + * - set @c priority to the priority of the @c mali_gp_job_info + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_gp_job_info into @c frame_registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + * When @c _mali_ukk_gp_start_job() returns @c _MALI_OSK_ERR_OK, status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. If the job had a higher priority than the one currently pending + * execution (@c _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED), it will bump + * the lower priority job and returns the address of the @c mali_gp_job_info + * for that job in @c returned_user_job_ptr. That job should get requeued. + * + * After the job has started, @c _mali_wait_for_notification() will be notified + * that the job finished or got suspended. It may get suspended due to + * resource shortage. If it finished (see _mali_ukk_wait_for_notification()) + * the notification will contain a @c _mali_uk_gp_job_finished_s result. If + * it got suspended the notification will contain a @c _mali_uk_gp_job_suspended_s + * result. + * + * The @c _mali_uk_gp_job_finished_s contains the job status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + * In case the job got suspended, @c _mali_uk_gp_job_suspended_s contains + * the @c user_job_ptr identifier used to start the job with, the @c reason + * why the job stalled (see \ref _maligp_job_suspended_reason) and a @c cookie + * to identify the core on which the job stalled. This @c cookie will be needed + * when responding to this nofication by means of _mali_ukk_gp_suspend_response(). + * (see _mali_ukk_gp_suspend_response()). The response is either to abort or + * resume the job. If the job got suspended due to an out of memory condition + * you may be able to resolve this by providing more memory and resuming the job. + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space, a @c mali_gp_job_info* */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 watchdog_msecs; /**< [in] maximum allowed runtime in milliseconds. The job gets killed if it runs longer than this. A value of 0 selects the default used by the device driver. */ + u32 frame_registers[MALIGP2_NUM_REGS_FRAME]; /**< [in] core specific registers associated with this job */ + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 returned_user_job_ptr; /**< [out] identifier for the returned job in user space, a @c mali_gp_job_info* */ + _mali_uk_start_job_status status; /**< [out] indicates job start status (success, previous job returned, requeue) */ + u32 abort_id; /**< [in] abort id of this job, used to identify this job for later abort requests */ + u32 perf_counter_l2_src0; /**< [in] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [in] source id for Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_gp_start_job_s; + +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE (1<<0) /**< Enable performance counter SRC0 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE (1<<1) /**< Enable performance counter SRC1 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE (1<<2) /**< Enable performance counter L2_SRC0 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE (1<<3) /**< Enable performance counter L2_SRC1 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET (1<<4) /**< Enable performance counter L2_RESET for a job */ + +/** @} */ /* end group _mali_uk_gpstartjob_s */ + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 irq_status; /**< [out] value of the GP interrupt rawstat register (see ARM DDI0415A) */ + u32 status_reg_on_stop; /**< [out] value of the GP control register */ + u32 vscl_stop_addr; /**< [out] value of the GP VLSCL start register */ + u32 plbcl_stop_addr; /**< [out] value of the GP PLBCL start register */ + u32 heap_current_addr; /**< [out] value of the GP PLB PL heap start address register */ + u32 perf_counter_src0; /**< [out] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [out] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter0; /**< [out] value of perfomance counter 0 (see ARM DDI0415A) */ + u32 perf_counter1; /**< [out] value of perfomance counter 1 (see ARM DDI0415A) */ + u32 render_time; /**< [out] number of milliseconds it took for the job to render */ + u32 perf_counter_l2_src0; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0; /**< [out] Value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1; /**< [out] Value of the Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_gp_job_finished_s; + +typedef enum _maligp_job_suspended_reason +{ + _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY /**< Polygon list builder unit (PLBU) has run out of memory */ +} _maligp_job_suspended_reason; + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _maligp_job_suspended_reason reason; /**< [out] reason why the job stalled */ + u32 cookie; /**< [out] identifier for the core in kernel space on which the job stalled */ +} _mali_uk_gp_job_suspended_s; + +/** @} */ /* end group _mali_uk_gp */ + + +/** @defgroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +/** @defgroup _mali_uk_ppstartjob_s Fragment Processor Start Job + * @{ */ + +/** @brief Arguments for _mali_ukk_pp_start_job() + * + * To start a Fragment Processor job + * - associate the request with a reference to a mali_pp_job by setting + * @c user_job_ptr to the address of the @c mali_pp_job of the job. + * - set @c priority to the priority of the mali_pp_job + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_pp_job into @c frame_registers. + * For MALI200 you also need to copy the write back 0,1 and 2 registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context in @c ctx that was returned from _mali_ukk_open() + * + * When _mali_ukk_pp_start_job() returns @c _MALI_OSK_ERR_OK, @c status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. If the job had a higher priority than the one currently pending + * execution (@c _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED), it will bump + * the lower priority job and returns the address of the @c mali_pp_job + * for that job in @c returned_user_job_ptr. That job should get requeued. + * + * After the job has started, _mali_wait_for_notification() will be notified + * when the job finished. The notification will contain a + * @c _mali_uk_pp_job_finished_s result. It contains the @c user_job_ptr + * identifier used to start the job with, the job @c status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than @c watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 watchdog_msecs; /**< [in] maximum allowed runtime in milliseconds. The job gets killed if it runs longer than this. A value of 0 selects the default used by the device driver. */ + u32 frame_registers[MALI200_NUM_REGS_FRAME]; /**< [in] core specific registers associated with this job, see ARM DDI0415A */ + u32 wb0_registers[MALI200_NUM_REGS_WBx]; + u32 wb1_registers[MALI200_NUM_REGS_WBx]; + u32 wb2_registers[MALI200_NUM_REGS_WBx]; + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 returned_user_job_ptr; /**< [out] identifier for the returned job in user space */ + _mali_uk_start_job_status status; /**< [out] indicates job start status (success, previous job returned, requeue) */ + u32 abort_id; /**< [in] abort id of this job, used to identify this job for later abort requests */ + u32 perf_counter_l2_src0; /**< [in] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [in] source id for Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_pp_start_job_s; +/** @} */ /* end group _mali_uk_ppstartjob_s */ + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 irq_status; /**< [out] value of interrupt rawstat register (see ARM DDI0415A) */ + u32 last_tile_list_addr; /**< [out] value of renderer list register (see ARM DDI0415A); necessary to restart a stopped job */ + u32 perf_counter_src0; /**< [out] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [out] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter0; /**< [out] value of perfomance counter 0 (see ARM DDI0415A) */ + u32 perf_counter1; /**< [out] value of perfomance counter 1 (see ARM DDI0415A) */ + u32 render_time; /**< [out] number of milliseconds it took for the job to render */ + u32 perf_counter_l2_src0; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0; /**< [out] Value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1; /**< [out] Value of the Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0_raw; /**< [out] Raw value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1_raw; /**< [out] Raw value of the Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_pp_job_finished_s; +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_core U/K Core + * @{ */ + +/** @defgroup _mali_uk_waitfornotification_s Wait For Notification + * @{ */ + +/** @brief Notification type encodings + * + * Each Notification type is an ordered pair of (subsystem,id), and is unique. + * + * The encoding of subsystem,id into a 32-bit word is: + * encoding = (( subsystem << _MALI_NOTIFICATION_SUBSYSTEM_SHIFT ) & _MALI_NOTIFICATION_SUBSYSTEM_MASK) + * | (( id << _MALI_NOTIFICATION_ID_SHIFT ) & _MALI_NOTIFICATION_ID_MASK) + * + * @see _mali_uk_wait_for_notification_s + */ +typedef enum +{ + /** core notifications */ + + _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x20, + _MALI_NOTIFICATION_APPLICATION_QUIT = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x40, + + /** Fragment Processor notifications */ + + _MALI_NOTIFICATION_PP_FINISHED = (_MALI_UK_PP_SUBSYSTEM << 16) | 0x10, + + /** Vertex Processor notifications */ + + _MALI_NOTIFICATION_GP_FINISHED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x10, + _MALI_NOTIFICATION_GP_STALLED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x20, +} _mali_uk_notification_type; + +/** to assist in splitting up 32-bit notification value in subsystem and id value */ +#define _MALI_NOTIFICATION_SUBSYSTEM_MASK 0xFFFF0000 +#define _MALI_NOTIFICATION_SUBSYSTEM_SHIFT 16 +#define _MALI_NOTIFICATION_ID_MASK 0x0000FFFF +#define _MALI_NOTIFICATION_ID_SHIFT 0 + + +/** @brief Arguments for _mali_ukk_wait_for_notification() + * + * On successful return from _mali_ukk_wait_for_notification(), the members of + * this structure will indicate the reason for notification. + * + * Specifically, the source of the notification can be identified by the + * subsystem and id fields of the mali_uk_notification_type in the code.type + * member. The type member is encoded in a way to divide up the types into a + * subsystem field, and a per-subsystem ID field. See + * _mali_uk_notification_type for more information. + * + * Interpreting the data union member depends on the notification type: + * + * - type == _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS + * - The kernel side is shutting down. No further + * _mali_uk_wait_for_notification() calls should be made. + * - In this case, the value of the data union member is undefined. + * - This is used to indicate to the user space client that it should close + * the connection to the Mali Device Driver. + * - type == _MALI_NOTIFICATION_PP_FINISHED + * - The notification data is of type _mali_uk_pp_job_finished_s. It contains the user_job_ptr + * identifier used to start the job with, the job status, the number of milliseconds the job took to render, + * and values of core registers when the job finished (irq status, performance counters, renderer list + * address). + * - A job has finished succesfully when its status member is _MALI_UK_JOB_STATUS_FINISHED. + * - If the hardware detected a timeout while rendering the job, or software detected the job is + * taking more than watchdog_msecs (see _mali_ukk_pp_start_job()) to complete, the status member will + * indicate _MALI_UK_JOB_STATUS_HANG. + * - If the hardware detected a bus error while accessing memory associated with the job, status will + * indicate _MALI_UK_JOB_STATUS_SEG_FAULT. + * - Status will indicate MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to stop the job but the job + * didn't start the hardware yet, e.g. when the driver closes. + * - type == _MALI_NOTIFICATION_GP_FINISHED + * - The notification data is of type _mali_uk_gp_job_finished_s. The notification is similar to that of + * type == _MALI_NOTIFICATION_PP_FINISHED, except that several other GP core register values are returned. + * The status values have the same meaning for type == _MALI_NOTIFICATION_PP_FINISHED. + * - type == _MALI_NOTIFICATION_GP_STALLED + * - The nofication data is of type _mali_uk_gp_job_suspended_s. It contains the user_job_ptr + * identifier used to start the job with, the reason why the job stalled and a cookie to identify the core on + * which the job stalled. + * - The reason member of gp_job_suspended is set to _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY + * when the polygon list builder unit has run out of memory. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [out] Type of notification available */ + union + { + _mali_uk_gp_job_suspended_s gp_job_suspended;/**< [out] Notification data for _MALI_NOTIFICATION_GP_STALLED notification type */ + _mali_uk_gp_job_finished_s gp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_GP_FINISHED notification type */ + _mali_uk_pp_job_finished_s pp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_PP_FINISHED notification type */ + } data; +} _mali_uk_wait_for_notification_s; + +/** @brief Arguments for _mali_ukk_post_notification() + * + * Posts the specified notification to the notification queue for this application. + * This is used to send a quit message to the callback thread. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [in] Type of notification to post */ +} _mali_uk_post_notification_s; +/** @} */ /* end group _mali_uk_waitfornotification_s */ + +/** @defgroup _mali_uk_getapiversion_s Get API Version + * @{ */ + +/** helpers for Device Driver API version handling */ + +/** @brief Encode a version ID from a 16-bit input + * + * @note the input is assumed to be 16 bits. It must not exceed 16 bits. */ +#define _MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) + +/** @brief Check whether a 32-bit value is likely to be Device Driver API + * version ID. */ +#define _IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) + +/** @brief Decode a 16-bit version number from a 32-bit Device Driver API version + * ID */ +#define _GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) + +/** @brief Determine whether two 32-bit encoded version IDs match */ +#define _IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * For example, for version 1 the value would be 0x00010001 + */ +#define _MALI_API_VERSION 8 +#define _MALI_UK_API_VERSION _MAKE_VERSION_ID(_MALI_API_VERSION) + +/** + * The API version is a 16-bit integer stored in both the lower and upper 16-bits + * of a 32-bit value. The 16-bit API version value is incremented on each API + * change. Version 1 would be 0x00010001. Used in _mali_uk_get_api_version_s. + */ +typedef u32 _mali_uk_api_version; + +/** @brief Arguments for _mali_uk_get_api_version() + * + * The user-side interface version must be written into the version member, + * encoded using _MAKE_VERSION_ID(). It will be compared to the API version of + * the kernel-side interface. + * + * On successful return, the version member will be the API version of the + * kernel-side interface. _MALI_UK_API_VERSION macro defines the current version + * of the API. + * + * The compatible member must be checked to see if the version of the user-side + * interface is compatible with the kernel-side interface, since future versions + * of the interface may be backwards compatible. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_api_version version; /**< [in,out] API version of user-side interface. */ + int compatible; /**< [out] @c 1 when @version is compatible, @c 0 otherwise */ +} _mali_uk_get_api_version_s; +/** @} */ /* end group _mali_uk_getapiversion_s */ + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_memory U/K Memory + * @{ */ + +/** @brief Arguments for _mali_ukk_init_mem(). */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 mali_address_base; /**< [out] start of MALI address space */ + u32 memory_size; /**< [out] total MALI address space available */ +} _mali_uk_init_mem_s; + +/** @brief Arguments for _mali_ukk_term_mem(). */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_term_mem_s; + +/** @brief Arguments for _mali_ukk_get_big_block() + * + * - type_id should be set to the value of the identifier member of one of the + * _mali_mem_info structures returned through _mali_ukk_get_system_info() + * - ukk_private must be zero when calling from user-side. On Kernel-side, the + * OS implementation of the U/K interface can use it to communicate data to the + * OS implementation of the OSK layer. Specifically, ukk_private will be placed + * into the ukk_private member of the _mali_uk_mem_mmap_s structure. See + * _mali_ukk_mem_mmap() for more details. + * - minimum_size_requested will be updated if it is too small + * - block_size will always be >= minimum_size_requested, because the underlying + * allocation mechanism may only be able to divide up memory regions in certain + * ways. To avoid wasting memory, block_size should always be taken into account + * rather than assuming minimum_size_requested was really allocated. + * - to free the memory, the returned cookie member must be stored, and used to + * refer to it. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 type_id; /**< [in] the type id of the memory bank to allocate memory from */ + u32 minimum_size_requested; /**< [in,out] minimum size of the allocation */ + u32 ukk_private; /**< [in] Kernel-side private word inserted by certain U/K interface implementations. Caller must set to Zero. */ + u32 mali_address; /**< [out] address of the allocation in mali address space */ + void *cpuptr; /**< [out] address of the allocation in the current process address space */ + u32 block_size; /**< [out] size of the block that got allocated */ + u32 flags; /**< [out] flags associated with the allocated block, of type _mali_bus_usage */ + u32 cookie; /**< [out] identifier for the allocated block in kernel space */ +} _mali_uk_get_big_block_s; + +/** @brief Arguments for _mali_ukk_free_big_block() + * + * All that is required is that the cookie member must be set to the value of + * the cookie member returned through _mali_ukk_get_big_block() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_free_big_block_s; + +/** @note Mali-MMU only */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 phys_addr; /**< [in] physical address */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_map_external_mem_s; + +/** Flag for _mali_uk_map_external_mem_s and _mali_uk_attach_ump_mem_s */ +#define _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE (1<<0) + +/** @note Mali-MMU only */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_unmap_external_mem_s; + +/** @note This is identical to _mali_uk_map_external_mem_s above, however phys_addr is replaced by secure_id */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure id */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_attach_ump_mem_s; + +/** @note Mali-MMU only; will be supported in future version */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_release_ump_mem_s; + +/** @brief Arguments for _mali_ukk_va_to_mali_pa() + * + * if size is zero or not a multiple of the system's page size, it will be + * rounded up to the next multiple of the page size. This will occur before + * any other use of the size parameter. + * + * if va is not PAGE_SIZE aligned, it will be rounded down to the next page + * boundary. + * + * The range (va) to ((u32)va)+(size-1) inclusive will be checked for physical + * contiguity. + * + * The implementor will check that the entire physical range is allowed to be mapped + * into user-space. + * + * Failure will occur if either of the above are not satisfied. + * + * Otherwise, the physical base address of the range is returned through pa, + * va is updated to be page aligned, and size is updated to be a non-zero + * multiple of the system's pagesize. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *va; /**< [in,out] Virtual address of the start of the range */ + u32 pa; /**< [out] Physical base address of the range */ + u32 size; /**< [in,out] Size of the range, in bytes. */ +} _mali_uk_va_to_mali_pa_s; + + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of MMU page table information (registers + page tables) */ +} _mali_uk_query_mmu_page_table_dump_size_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [in] size of buffer to receive mmu page table information */ + void *buffer; /**< [in,out] buffer to receive mmu page table information */ + u32 register_writes_size; /**< [out] size of MMU register dump */ + u32 *register_writes; /**< [out] pointer within buffer where MMU register dump is stored */ + u32 page_table_dump_size; /**< [out] size of MMU page table dump */ + u32 *page_table_dump; /**< [out] pointer within buffer where MMU page table dump is stored */ +} _mali_uk_dump_mmu_page_table_s; + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_pp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_number_of_cores(), @c number_of_cores + * will contain the number of Fragment Processor cores in the system. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_cores; /**< [out] number of Fragment Processor cores in the system */ +} _mali_uk_get_pp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_pp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_core_version(), @c version contains + * the version that all Fragment Processor cores are compatible with. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_pp_core_version_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 abort_id; /**< [in] ID of job(s) to abort */ +} _mali_uk_pp_abort_job_s; + +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_gp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_number_of_cores(), @c number_of_cores + * will contain the number of Vertex Processor cores in the system. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_cores; /**< [out] number of Vertex Processor cores in the system */ +} _mali_uk_get_gp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_gp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_core_version(), @c version contains + * the version that all Vertex Processor cores are compatible with. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_gp_core_version_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 abort_id; /**< [in] ID of job(s) to abort */ +} _mali_uk_gp_abort_job_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 limit; /**< [in,out] The desired limit for number of events to record on input, actual limit on output */ +} _mali_uk_profiling_start_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 event_id; /**< [in] event id to register (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [in] event specific data */ +} _mali_uk_profiling_add_event_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 count; /**< [out] The number of events sampled */ +} _mali_uk_profiling_stop_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 index; /**< [in] which index to get (starting at zero) */ + u64 timestamp; /**< [out] timestamp of event */ + u32 event_id; /**< [out] event id of event (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [out] event specific data */ +} _mali_uk_profiling_get_event_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_profiling_clear_s; + + + +/** @} */ /* end group _mali_uk_gp */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * @{ */ + +/** @brief Arguments to _mali_ukk_mem_mmap() + * + * Use of the phys_addr member depends on whether the driver is compiled for + * Mali-MMU or nonMMU: + * - in the nonMMU case, this is the physical address of the memory as seen by + * the CPU (which may be a constant offset from that used by Mali) + * - in the MMU case, this is the Mali Virtual base address of the memory to + * allocate, and the particular physical pages used to back the memory are + * entirely determined by _mali_ukk_mem_mmap(). The details of the physical pages + * are not reported to user-space for security reasons. + * + * The cookie member must be stored for use later when freeing the memory by + * calling _mali_ukk_mem_munmap(). In the Mali-MMU case, the cookie is secure. + * + * The ukk_private word must be set to zero when calling from user-space. On + * Kernel-side, the OS implementation of the U/K interface can use it to + * communicate data to the OS implementation of the OSK layer. In particular, + * _mali_ukk_get_big_block() directly calls _mali_ukk_mem_mmap directly, and + * will communicate its own ukk_private word through the ukk_private member + * here. The common code itself will not inspect or modify the ukk_private + * word, and so it may be safely used for whatever purposes necessary to + * integrate Mali Memory handling into the OS. + * + * The uku_private member is currently reserved for use by the user-side + * implementation of the U/K interface. Its value must be zero. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + u32 size; /**< [in] Size of the requested mapping */ + u32 phys_addr; /**< [in] Physical address - could be offset, depending on caller+callee convention */ + u32 cookie; /**< [out] Returns a cookie for use in munmap calls */ + void *uku_private; /**< [in] User-side Private word used by U/K interface */ + void *ukk_private; /**< [in] Kernel-side Private word used by U/K interface */ +} _mali_uk_mem_mmap_s; + +/** @brief Arguments to _mali_ukk_mem_munmap() + * + * The cookie and mapping members must be that returned from the same previous + * call to _mali_ukk_mem_mmap(). The size member must correspond to cookie + * and mapping - that is, it must be the value originally supplied to a call to + * _mali_ukk_mem_mmap that returned the values of mapping and cookie. + * + * An error will be returned if an attempt is made to unmap only part of the + * originally obtained range, or to unmap more than was originally obtained. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] The mapping returned from mmap call */ + u32 size; /**< [in] The size passed to mmap call */ + u32 cookie; /**< [in] Cookie from mmap call */ +} _mali_uk_mem_munmap_s; +/** @} */ /* end group _mali_uk_memory */ + +#if USING_MALI_PMM + +/** @defgroup _mali_uk_pmm U/K Power Management Module + * @{ */ + +/** @brief Power management event message identifiers. + * + * U/K events start after id 200, and can range up to 999 + * Adding new events will require updates to the PMM mali_pmm_event_id type + */ +#define _MALI_PMM_EVENT_UK_EXAMPLE 201 + +/** @brief Generic PMM message data type, that will be dependent on the event msg + */ +typedef u32 mali_pmm_message_data; + + +/** @brief Arguments to _mali_ukk_pmm_event_message() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 id; /**< [in] event id */ + mali_pmm_message_data data; /**< [in] specific data associated with the event */ +} _mali_uk_pmm_message_s; + +/** @} */ /* end group _mali_uk_pmm */ +#endif /* USING_MALI_PMM */ + +/** @defgroup _mali_uk_vsync U/K VSYNC Wait Reporting Module + * @{ */ + +/** @brief VSYNC events + * + * These events are reported when DDK starts to wait for vsync and when the + * vsync has occured and the DDK can continue on the next frame. + */ +typedef enum _mali_uk_vsync_event +{ + _MALI_UK_VSYNC_EVENT_BEGIN_WAIT = 0, + _MALI_UK_VSYNC_EVENT_END_WAIT +} _mali_uk_vsync_event; + +/** @brief Arguments to _mali_ukk_vsync_event() + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_vsync_event event; /**< [in] VSYNCH event type */ +} _mali_uk_vsync_event_report_s; + +/** @} */ /* end group _mali_uk_vsync */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UK_TYPES_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_ukk.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_ukk.h new file mode 100644 index 00000000000..d066046901d --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/mali_ukk.h @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __MALI_UKK_H__ +#define __MALI_UKK_H__ + +#include "mali_osk.h" +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * - The _mali_uk functions are an abstraction of the interface to the device + * driver. On certain OSs, this would be implemented via the IOCTL interface. + * On other OSs, it could be via extension of some Device Driver Class, or + * direct function call for Bare metal/RTOSs. + * - It is important to note that: + * - The Device Driver has implemented the _mali_ukk set of functions + * - The Base Driver calls the corresponding set of _mali_uku functions. + * - What requires porting is solely the calling mechanism from User-side to + * Kernel-side, and propagating back the results. + * - Each U/K function is associated with a (group, number) pair from + * \ref _mali_uk_functions to make it possible for a common function in the + * Base Driver and Device Driver to route User/Kernel calls from/to the + * correct _mali_uk function. For example, in an IOCTL system, the IOCTL number + * would be formed based on the group and number assigned to the _mali_uk + * function, as listed in \ref _mali_uk_functions. On the user-side, each + * _mali_uku function would just make an IOCTL with the IOCTL-code being an + * encoded form of the (group, number) pair. On the kernel-side, the Device + * Driver's IOCTL handler decodes the IOCTL-code back into a (group, number) + * pair, and uses this to determine which corresponding _mali_ukk should be + * called. + * - Refer to \ref _mali_uk_functions for more information about this + * (group, number) pairing. + * - In a system where there is no distinction between user and kernel-side, + * the U/K interface may be implemented as:@code + * MALI_STATIC_INLINE _mali_osk_errcode_t _mali_uku_examplefunction( _mali_uk_examplefunction_s *args ) + * { + * return mali_ukk_examplefunction( args ); + * } + * @endcode + * - Therefore, all U/K calls behave \em as \em though they were direct + * function calls (but the \b implementation \em need \em not be a direct + * function calls) + * + * @note Naming the _mali_uk functions the same on both User and Kernel sides + * on non-RTOS systems causes debugging issues when setting breakpoints. In + * this case, it is not clear which function the breakpoint is put on. + * Therefore the _mali_uk functions in user space are prefixed with \c _mali_uku + * and in kernel space with \c _mali_ukk. The naming for the argument + * structures is unaffected. + * + * - The _mali_uk functions are synchronous. + * - Arguments to the _mali_uk functions are passed in a structure. The only + * parameter passed to the _mali_uk functions is a pointer to this structure. + * This first member of this structure, ctx, is a pointer to a context returned + * by _mali_uku_open(). For example:@code + * typedef struct + * { + * void *ctx; + * u32 number_of_cores; + * } _mali_uk_get_gp_number_of_cores_s; + * @endcode + * + * - Each _mali_uk function has its own argument structure named after the + * function. The argument is distinguished by the _s suffix. + * - The argument types are defined by the base driver and user-kernel + * interface. + * - All _mali_uk functions return a standard \ref _mali_osk_errcode_t. + * - Only arguments of type input or input/output need be initialized before + * calling a _mali_uk function. + * - Arguments of type output and input/output are only valid when the + * _mali_uk function returns \ref _MALI_OSK_ERR_OK. + * - The \c ctx member is always invalid after it has been used by a + * _mali_uk function, except for the context management functions + * + * + * \b Interface \b restrictions + * + * The requirements of the interface mean that an implementation of the + * User-kernel interface may do no 'real' work. For example, the following are + * illegal in the User-kernel implementation: + * - Calling functions necessary for operation on all systems, which would + * not otherwise get called on RTOS systems. + * - For example, a U/K interface that calls multiple _mali_ukk functions + * during one particular U/K call. This could not be achieved by the same code + * which uses direct function calls for the U/K interface. + * - Writing in values to the args members, when otherwise these members would + * not hold a useful value for a direct function call U/K interface. + * - For example, U/K interface implementation that take NULL members in + * their arguments structure from the user side, but those members are + * replaced with non-NULL values in the kernel-side of the U/K interface + * implementation. A scratch area for writing data is one such example. In this + * case, a direct function call U/K interface would segfault, because no code + * would be present to replace the NULL pointer with a meaningful pointer. + * - Note that we discourage the case where the U/K implementation changes + * a NULL argument member to non-NULL, and then the Device Driver code (outside + * of the U/K layer) re-checks this member for NULL, and corrects it when + * necessary. Whilst such code works even on direct function call U/K + * intefaces, it reduces the testing coverage of the Device Driver code. This + * is because we have no way of testing the NULL == value path on an OS + * implementation. + * + * A number of allowable examples exist where U/K interfaces do 'real' work: + * - The 'pointer switching' technique for \ref _mali_ukk_get_system_info + * - In this case, without the pointer switching on direct function call + * U/K interface, the Device Driver code still sees the same thing: a pointer + * to which it can write memory. This is because such a system has no + * distinction between a user and kernel pointer. + * - Writing an OS-specific value into the ukk_private member for + * _mali_ukk_mem_mmap(). + * - In this case, this value is passed around by Device Driver code, but + * its actual value is never checked. Device Driver code simply passes it from + * the U/K layer to the OSK layer, where it can be acted upon. In this case, + * \em some OS implementations of the U/K (_mali_ukk_mem_mmap()) and OSK + * (_mali_osk_mem_mapregion_init()) functions will collaborate on the + * meaning of ukk_private member. On other OSs, it may be unused by both + * U/K and OSK layers + * - On OS systems (not including direct function call U/K interface + * implementations), _mali_ukk_get_big_block() may succeed, but the subsequent + * copying to user space may fail. + * - A problem scenario exists: some memory has been reserved by + * _mali_ukk_get_big_block(), but the user-mode will be unaware of it (it will + * never receive any information about this memory). In this case, the U/K + * implementation must do everything necessary to 'rollback' the \em atomic + * _mali_ukk_get_big_block() transaction. + * - Therefore, on error inside the U/K interface implementation itself, + * it will be as though the _mali_ukk function itself had failed, and cleaned + * up after itself. + * - Compare this to a direct function call U/K implementation, where all + * error cleanup is handled by the _mali_ukk function itself. The direct + * function call U/K interface implementation is automatically atomic. + * + * The last example highlights a consequence of all U/K interface + * implementations: they must be atomic with respect to the Device Driver code. + * And therefore, should Device Driver code succeed but the U/K implementation + * fail afterwards (but before return to user-space), then the U/K + * implementation must cause appropriate cleanup actions to preserve the + * atomicity of the interface. + * + * @{ + */ + + +/** @defgroup _mali_uk_context U/K Context management + * + * These functions allow for initialisation of the user-kernel interface once per process. + * + * Generally the context will store the OS specific object to communicate with the kernel device driver and further + * state information required by the specific implementation. The context is shareable among all threads in the caller process. + * + * On IOCTL systems, this is likely to be a file descriptor as a result of opening the kernel device driver. + * + * On a bare-metal/RTOS system with no distinction between kernel and + * user-space, the U/K interface simply calls the _mali_ukk variant of the + * function by direct function call. In this case, the context returned is the + * mali_session_data from _mali_ukk_open(). + * + * The kernel side implementations of the U/K interface expect the first member of the argument structure to + * be the context created by _mali_uku_open(). On some OS implementations, the meaning of this context + * will be different between user-side and kernel-side. In which case, the kernel-side will need to replace this context + * with the kernel-side equivalent, because user-side will not have access to kernel-side data. The context parameter + * in the argument structure therefore has to be of type input/output. + * + * It should be noted that the caller cannot reuse the \c ctx member of U/K + * argument structure after a U/K call, because it may be overwritten. Instead, + * the context handle must always be stored elsewhere, and copied into + * the appropriate U/K argument structure for each user-side call to + * the U/K interface. This is not usually a problem, since U/K argument + * structures are usually placed on the stack. + * + * @{ */ + +/** @brief Begin a new Mali Device Driver session + * + * This is used to obtain a per-process context handle for all future U/K calls. + * + * @param context pointer to storage to return a (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_open( void **context ); + +/** @brief End a Mali Device Driver session + * + * This should be called when the process no longer requires use of the Mali Device Driver. + * + * The context handle must not be used after it has been closed. + * + * @param context pointer to a stored (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_close( void **context ); + +/** @} */ /* end group _mali_uk_context */ + + +/** @addtogroup _mali_uk_core U/K Core + * + * The core functions provide the following functionality: + * - verify that the user and kernel API are compatible + * - retrieve information about the cores and memory banks in the system + * - wait for the result of jobs started on a core + * + * @{ */ + +/** @brief Returns the size of the buffer needed for a _mali_ukk_get_system_info call + * + * This function must be called before a call is made to + * _mali_ukk_get_system_info, so that memory of the correct size can be + * allocated, and a pointer to this memory written into the system_info member + * of _mali_uk_get_system_info_s. + * + * @param args see _mali_uk_get_system_info_size_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_system_info_size( _mali_uk_get_system_info_size_s *args ); + +/** @brief Returns information about the system (cores and memory banks) + * + * A buffer for this needs to be allocated by the caller. The size of the buffer required is returned by + * _mali_ukk_get_system_info_size(). The user is responsible for freeing the buffer. + * + * The _mali_system_info structure will be written to the start of this buffer, + * and the core_info and mem_info lists will be written to locations inside + * the buffer, and will be suitably aligned. + * + * Under OS implementations of the U/K interface we need to pack/unpack + * pointers across the user/kernel boundary. This has required that we malloc() + * an intermediate buffer inside the kernel-side U/K interface, and free it + * before returning to user-side. To avoid modifying common code, we do the + * following pseudo-code, which we shall call 'pointer switching': + * + * @code + * { + * Copy_From_User(kargs, args, ... ); + * void __user * local_ptr = kargs->system_info; + * kargs->system_info = _mali_osk_malloc( ... ); + * _mali_ukk_get_system_info( kargs ); + * Copy_To_User( local_ptr, kargs->system_info, ... ); + * _mali_osk_free( kargs->system_info ); + * } + * @endcode + * @note The user-side's args->system_info members was unmodified here. + * + * However, the current implementation requires an extra ukk_private word so that the common code can work out + * how to patch pointers to user-mode for an OS's U/K implementation, this should be set to the user-space + * destination address for pointer-patching to occur. When NULL, it is unused, an no pointer-patching occurs in the + * common code. + * + * @param args see _mali_uk_get_system_info_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_system_info( _mali_uk_get_system_info_s *args ); + +/** @brief Waits for a job notification. + * + * Sleeps until notified or a timeout occurs. Returns information about the notification. + * + * @param args see _mali_uk_wait_for_notification_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ); + +/** @brief Post a notification to the notification queue of this application. + * + * @param args see _mali_uk_post_notification_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ); + +/** @brief Verifies if the user and kernel side of this API are compatible. + * + * @param args see _mali_uk_get_api_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ); +/** @} */ /* end group _mali_uk_core */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * + * The memory functions provide functionality with and without a Mali-MMU present. + * + * For Mali-MMU based systems, the following functionality is provided: + * - Initialize and terminate MALI virtual address space + * - Allocate/deallocate physical memory to a MALI virtual address range and map into/unmap from the + * current process address space + * - Map/unmap external physical memory into the MALI virtual address range + * + * For Mali-nonMMU based systems: + * - Allocate/deallocate MALI memory + * + * @{ */ + +/** + * @brief Initialize the Mali-MMU Memory system + * + * For Mali-MMU builds of the drivers, this function must be called before any + * other functions in the \ref _mali_uk_memory group are called. + * + * @note This function is for Mali-MMU builds \b only. It should not be called + * when the drivers are built without Mali-MMU support. + * + * @param args see \ref _mali_uk_init_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ); + +/** + * @brief Terminate the MMU Memory system + * + * For Mali-MMU builds of the drivers, this function must be called when + * functions in the \ref _mali_uk_memory group will no longer be called. This + * function must be called before the application terminates. + * + * @note This function is for Mali-MMU builds \b only. It should not be called + * when the drivers are built without Mali-MMU support. + * + * @param args see \ref _mali_uk_term_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ); + +/** @brief Map a block of memory into the current user process + * + * Allocates a minimum of minimum_size_requested bytes of MALI memory and maps it into the current + * process space. The number of bytes allocated is returned in args->block_size. + * + * This is only used for Mali-nonMMU mode. + * + * @param args see _mali_uk_get_big_block_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ); + +/** @brief Unmap a block of memory from the current user process + * + * Frees allocated MALI memory and unmaps it from the current process space. The previously allocated memory + * is indicated by the cookie as returned by _mali_ukk_get_big_block(). + * + * This is only used for Mali-nonMMU mode. + * + * @param args see _mali_uk_free_big_block_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ); + +/** @brief Map Mali Memory into the current user process + * + * Maps Mali memory into the current user process in a generic way. + * + * This function is to be used for Mali-MMU mode. The function is available in both Mali-MMU and Mali-nonMMU modes, + * but should not be called by a user process in Mali-nonMMU mode. In Mali-nonMMU mode, the function is callable + * from the kernel side, and is used to implement _mali_ukk_get_big_block() in this case. + * + * The implementation and operation of _mali_ukk_mem_mmap() is dependant on whether the driver is built for Mali-MMU + * or Mali-nonMMU: + * - In the nonMMU case, _mali_ukk_mem_mmap() requires a physical address to be specified. For this reason, an OS U/K + * implementation should not allow this to be called from user-space. In any case, nonMMU implementations are + * inherently insecure, and so the overall impact is minimal. Mali-MMU mode should be used if security is desired. + * - In the MMU case, _mali_ukk_mem_mmap() the _mali_uk_mem_mmap_s::phys_addr + * member is used for the \em Mali-virtual address desired for the mapping. The + * implementation of _mali_ukk_mem_mmap() will allocate both the CPU-virtual + * and CPU-physical addresses, and can cope with mapping a contiguous virtual + * address range to a sequence of non-contiguous physical pages. In this case, + * the CPU-physical addresses are not communicated back to the user-side, as + * they are unnecsessary; the \em Mali-virtual address range must be used for + * programming Mali structures. + * + * This means that in the first (nonMMU) case, the caller must manage the physical address allocations. The caller + * in this case is _mali_ukk_get_big_block(), which does indeed manage the Mali physical address ranges. + * + * In the second (MMU) case, _mali_ukk_mem_mmap() handles management of + * CPU-virtual and CPU-physical ranges, but the \em caller must manage the + * \em Mali-virtual address range from the user-side. + * + * @note Mali-virtual address ranges are entirely separate between processes. + * It is not possible for a process to accidentally corrupt another process' + * \em Mali-virtual address space. + * + * @param args see _mali_uk_mem_mmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ); + +/** @brief Unmap Mali Memory from the current user process + * + * Unmaps Mali memory from the current user process in a generic way. This only operates on Mali memory supplied + * from _mali_ukk_mem_mmap(). + * + * @param args see _mali_uk_mem_munmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ); + +/** @brief Determine the buffer size necessary for an MMU page table dump. + * @param args see _mali_uk_query_mmu_page_table_dump_size_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ); +/** @brief Dump MMU Page tables. + * @param args see _mali_uk_dump_mmu_page_table_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ); + +/** @brief Map a physically contiguous range of memory into Mali + * @param args see _mali_uk_map_external_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ); + +/** @brief Unmap a physically contiguous range of memory from Mali + * @param args see _mali_uk_unmap_external_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ); + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +/** @brief Map UMP memory into Mali + * @param args see _mali_uk_attach_ump_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ); +/** @brief Unmap UMP memory from Mali + * @param args see _mali_uk_release_ump_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +/** @brief Determine virtual-to-physical mapping of a contiguous memory range + * (optional) + * + * This allows the user-side to do a virtual-to-physical address translation. + * In conjunction with _mali_uku_map_external_mem, this can be used to do + * direct rendering. + * + * This function will only succeed on a virtual range that is mapped into the + * current process, and that is contigious. + * + * If va is not page-aligned, then it is rounded down to the next page + * boundary. The remainer is added to size, such that ((u32)va)+size before + * rounding is equal to ((u32)va)+size after rounding. The rounded modified + * va and size will be written out into args on success. + * + * If the supplied size is zero, or not a multiple of the system's PAGE_SIZE, + * then size will be rounded up to the next multiple of PAGE_SIZE before + * translation occurs. The rounded up size will be written out into args on + * success. + * + * On most OSs, virtual-to-physical address translation is a priveledged + * function. Therefore, the implementer must validate the range supplied, to + * ensure they are not providing arbitrary virtual-to-physical address + * translations. While it is unlikely such a mechanism could be used to + * compromise the security of a system on its own, it is possible it could be + * combined with another small security risk to cause a much larger security + * risk. + * + * @note This is an optional part of the interface, and is only used by certain + * implementations of libEGL. If the platform layer in your libEGL + * implementation does not require Virtual-to-Physical address translation, + * then this function need not be implemented. A stub implementation should not + * be required either, as it would only be removed by the compiler's dead code + * elimination. + * + * @note if implemented, this function is entirely platform-dependant, and does + * not exist in common code. + * + * @param args see _mali_uk_va_to_mali_pa_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_va_to_mali_pa( _mali_uk_va_to_mali_pa_s * args ); + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * + * The Fragment Processor (aka PP (Pixel Processor)) functions provide the following functionality: + * - retrieving version of the fragment processors + * - determine number of fragment processors + * - starting a job on a fragment processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Fragment Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started instead and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * If an existing lower priority job is returned, args->returned_user_job_ptr contains a + * pointer to the returned job and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param args see _mali_uk_pp_start_job_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_pp_start_job( _mali_uk_pp_start_job_s *args ); + +/** @brief Returns the number of Fragment Processors in the system + * + * @param args see _mali_uk_get_pp_number_of_cores_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args ); + +/** @brief Returns the version that all Fragment Processor cores are compatible with. + * + * This function may only be called when _mali_ukk_get_pp_number_of_cores() indicated at least one Fragment + * Processor core is available. + * + * @param args see _mali_uk_get_pp_core_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args ); + +/** @brief Abort any PP jobs with the given ID. + * + * Jobs internally queued or currently running on the hardware is to be stopped/aborted. + * Jobs aborted are reported via the normal job completion system. + * Any jobs, running or internally queued should be aborted imediately. + * Normal notifiction procedures to report on the status of these jobs. + * + * + * @param args see _malu_uk_pp_abort_job_s in "mali_uk_types.h" + */ +void _mali_ukk_pp_abort_job( _mali_uk_pp_abort_job_s *args ); +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * + * The Vertex Processor (aka GP (Geometry Processor)) functions provide the following functionality: + * - retrieving version of the Vertex Processors + * - determine number of Vertex Processors available + * - starting a job on a Vertex Processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Vertex Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * If an existing lower priority job is returned, args->returned_user_job_ptr contains a pointer to + * the returned job and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param args see _mali_uk_gp_start_job_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_start_job( _mali_uk_gp_start_job_s *args ); + +/** @brief Returns the number of Vertex Processors in the system. + * + * @param args see _mali_uk_get_gp_number_of_cores_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args ); + +/** @brief Returns the version that all Vertex Processor cores are compatible with. + * + * This function may only be called when _mali_uk_get_gp_number_of_cores() indicated at least one Vertex + * Processor core is available. + * + * @param args see _mali_uk_get_gp_core_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args ); + +/** @brief Resume or abort suspended Vertex Processor jobs. + * + * After receiving notification that a Vertex Processor job was suspended from + * _mali_ukk_wait_for_notification() you can use this function to resume or abort the job. + * + * @param args see _mali_uk_gp_suspend_response_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args ); + +/** @brief Abort any GP jobs with the given ID. + * + * Jobs internally queued or currently running on the hardware is to be stopped/aborted. + * Jobs aborted are reported via the normal job completion system. + * + * Any jobs, running or internally queued should be aborted imediately. + * Normal notifiction procedures to report on the status of these jobs. + * + * @param args see _mali_uk_gp_abort_job_s in "mali_uk_types.h" + */ +void _mali_ukk_gp_abort_job( _mali_uk_gp_abort_job_s *args ); +/** @} */ /* end group _mali_uk_gp */ + +#if USING_MALI_PMM +/** @addtogroup _mali_uk_pmm U/K Power Management Module + * @{ */ + +/* @brief Power Management Module event message + * + * @note The event message can fail to be sent due to OOM but this is + * stored in the PMM state machine to be handled later + * + * @param args see _mali_uk_pmm_event_message_s in "mali_uk_types.h" + */ +void _mali_ukk_pmm_event_message( _mali_uk_pmm_message_s *args ); +/** @} */ /* end group _mali_uk_pmm */ +#endif /* USING_MALI_PMM */ + +#if MALI_TIMELINE_PROFILING_ENABLED +/** @addtogroup _mali_uk_profiling U/K Timeline profiling module + * @{ */ + +/** @brief Start recording profiling events. + * + * @param args see _mali_uk_profiling_start_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args); + +/** @brief Add event to profiling buffer. + * + * @param args see _mali_uk_profiling_add_event_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args); + +/** @brief Stop recording profiling events. + * + * @param args see _mali_uk_profiling_stop_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args); + +/** @brief Retrieve a recorded profiling event. + * + * @param args see _mali_uk_profiling_get_event_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args); + +/** @brief Clear recorded profiling events. + * + * @param args see _mali_uk_profiling_clear_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args); + +/** @} */ /* end group _mali_uk_profiling */ +#endif + +/** @addtogroup _mali_uk_vsync U/K VSYNC reporting module + * @{ */ + +/** @brief Report events related to vsync. + * + * @note Events should be reported when starting to wait for vsync and when the + * waiting is finished. This information can then be used in kernel space to + * complement the GPU utilization metric. + * + * @param args see _mali_uk_vsync_event_report_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args); + +/** @} */ /* end group _mali_uk_vsync */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.c new file mode 100644 index 00000000000..eeb29589ddc --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.c @@ -0,0 +1,921 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm.c + * Implementation of the power management module for the kernel device driver + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_platform.h" + +/* Internal PMM subsystem state */ +static _mali_pmm_internal_state_t *pmm_state = NULL; +/* Mali kernel subsystem id */ +static mali_kernel_subsystem_identifier mali_subsystem_pmm_id = -1; + +#define GET_PMM_STATE_PTR (pmm_state) + +/* Internal functions */ +static _mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource); +static void pmm_event_process( void ); +_mali_osk_errcode_t malipmm_irq_uhandler(void *data); +void malipmm_irq_bhandler(void *data); + +/** @brief Start the PMM subsystem + * + * @param id Subsystem id to uniquely identify this subsystem + * @return _MALI_OSK_ERR_OK if the system started successfully, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ); + +/** @brief Perform post start up of the PMM subsystem + * + * Post start up includes initializing the current policy, now that the system is + * completely started - to stop policies turning off hardware during the start up + * + * @param id the unique subsystem id + * @return _MALI_OSK_ERR_OK if the post startup was successful, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ); + +/** @brief Terminate the PMM subsystem + * + * @param id the unique subsystem id + */ +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ); + +#if MALI_STATE_TRACKING + void malipmm_subsystem_dump_state( void ); +#endif + + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ +struct mali_kernel_subsystem mali_subsystem_pmm= +{ + malipmm_kernel_subsystem_start, /* startup */ + malipmm_kernel_subsystem_terminate, /* shutdown */ + malipmm_kernel_load_complete, /* loaded all subsystems */ + NULL, + NULL, + NULL, + NULL, +#if MALI_STATE_TRACKING + malipmm_subsystem_dump_state, /* dump_state */ +#endif +}; + +#if PMM_OS_TEST + +u32 power_test_event = 0; +mali_bool power_test_flag = MALI_FALSE; +_mali_osk_timer_t *power_test_timer = NULL; + +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS UP DONE\n")); +} + +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS DOWN DONE\n")); +} + +/** + * Symbian OS Power Up call to the driver + */ +void power_test_callback( void *arg ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + power_test_flag = MALI_TRUE; + _mali_osk_irq_schedulework( pmm->irq ); +} + +void power_test_start() +{ + power_test_timer = _mali_osk_timer_init(); + _mali_osk_timer_setcallback( power_test_timer, power_test_callback, NULL ); + + /* First event is power down */ + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + _mali_osk_timer_add( power_test_timer, 10000 ); +} + +mali_bool power_test_check() +{ + if( power_test_flag ) + { + _mali_uk_pmm_message_s event = { + NULL, + 0, + 1 }; + event.id = power_test_event; + + power_test_flag = MALI_FALSE; + + /* Send event */ + _mali_ukk_pmm_event_message( &event ); + + /* Switch to next event to test */ + if( power_test_event == MALI_PMM_EVENT_OS_POWER_DOWN ) + { + power_test_event = MALI_PMM_EVENT_OS_POWER_UP; + } + else + { + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + } + _mali_osk_timer_add( power_test_timer, 5000 ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +void power_test_end() +{ + _mali_osk_timer_del( power_test_timer ); + _mali_osk_timer_term( power_test_timer ); + power_test_timer = NULL; +} + +#endif + +void _mali_ukk_pmm_event_message( _mali_uk_pmm_message_s *args ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + _mali_osk_notification_t *msg; + mali_pmm_message_t *event; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(args); + + MALIPMM_DEBUG_PRINT( ("PMM: sending message\n") ); + +#if MALI_PMM_TRACE && MALI_PMM_TRACE_SENT_EVENTS + _mali_pmm_trace_event_message( args, MALI_FALSE ); +#endif + + msg = _mali_osk_notification_create( MALI_PMM_NOTIFICATION_TYPE, sizeof( mali_pmm_message_t ) ); + + if( msg ) + { + event = (mali_pmm_message_t *)msg->result_buffer; + event->id = args->id; + event->ts = _mali_osk_time_tickcount(); + event->data = args->data; + + _mali_osk_atomic_inc( &(pmm->messages_queued) ); + + if( args->id > MALI_PMM_EVENT_INTERNALS ) + { + /* Internal PMM message */ + _mali_osk_notification_queue_send( pmm->iqueue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_sent++; + #endif + } + else + { + /* Real event */ + _mali_osk_notification_queue_send( pmm->queue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_sent++; + #endif + } + } + else + { + MALI_PRINT_ERROR( ("PMM: Could not send message %d", args->id) ); + /* Make note of this OOM - which has caused a missed event */ + pmm->missed++; + } + + /* Schedule time to look at the event or the fact we couldn't create an event */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +mali_pmm_state _mali_pmm_state( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm && (mali_subsystem_pmm_id != -1) ) + { + return pmm->state; + } + + /* No working subsystem yet */ + return MALI_PMM_STATE_UNAVAILABLE; +} + + +mali_pmm_core_mask _mali_pmm_cores_list( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_registered; +} + +mali_pmm_core_mask _mali_pmm_cores_powered( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_powered; +} + + +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ) +{ + /* TBD - This is currently a stub function for basic power management */ + + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ) +{ + /* TBD - This is currently a stub function for basic power management */ + +/* TBD - When this is not a stub... include tracing... +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( old, newpolicy ); +#endif +*/ + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ) +{ + if( policy ) + { + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm ) + { + *policy = pmm->policy; + MALI_SUCCESS; + } + else + { + *policy = MALI_PMM_POLICY_NONE; + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + } + + /* No return argument */ + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); +} + +#if MALI_PMM_TRACE + +/* Event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events[] = { + "OS_POWER_UP", + "OS_POWER_DOWN", + "JOB_SCHEDULED", + "JOB_QUEUED", + "JOB_FINISHED", + "TIMEOUT", +}; + +/* UK event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_uk[] = { + "UKS", + "UK_EXAMPLE", +}; + +/* Internal event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_internal[] = { + "INTERNALS", + "INTERNAL_POWER_UP_ACK", + "INTERNAL_POWER_DOWN_ACK", +}; + +/* State names - order must match mali_pmm_state enum */ +static char *pmm_trace_state[] = { + "UNAVAILABLE", + "SYSTEM ON", + "SYSTEM OFF", + "SYSTEM TRANSITION", +}; + +/* Policy names - order must match mali_pmm_policy enum */ +static char *pmm_trace_policy[] = { + "NONE", + "ALWAYS ON", + "JOB CONTROL", +}; + +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ) +{ + const char *dname; + const char *cname; + const char *ename; + + if( old != newstate ) + { + if( newstate == 0 ) + { + dname = "NO cores"; + } + else + { + dname = pmm_trace_get_core_name( newstate ); + } + + /* These state checks only work if the assumption that only cores can be + * turned on or turned off in seperate actions is true. If core power states can + * be toggled (some one, some off) at the same time, this check does not work + */ + if( old > newstate ) + { + /* Cores have turned off */ + cname = pmm_trace_get_core_name( old - newstate ); + ename = "OFF"; + } + else + { + /* Cores have turned on */ + cname = pmm_trace_get_core_name( newstate - old ); + ename = "ON"; + } + MALI_PRINT( ("PMM Trace: Hardware %s ON, %s just turned %s. { 0x%08x -> 0x%08x }", dname, cname, ename, old, newstate) ); + } +} + +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ) +{ + if( old != newstate ) + { + MALI_PRINT( ("PMM Trace: State changed from %s to %s", pmm_trace_state[old], pmm_trace_state[newstate]) ); + } +} + +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ) +{ + if( old != newpolicy ) + { + MALI_PRINT( ("PMM Trace: Policy changed from %s to %s", pmm_trace_policy[old], pmm_trace_policy[newpolicy]) ); + } +} + +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ) +{ + const char *ename; + const char *dname; + const char *tname; + const char *format = "PMM Trace: Event %s { (%d) %s, %d ticks, (0x%x) %s }"; + + MALI_DEBUG_ASSERT_POINTER(event); + + tname = (received) ? "received" : "sent"; + + if( event->id >= MALI_PMM_EVENT_INTERNALS ) + { + ename = pmm_trace_events_internal[((int)event->id) - MALI_PMM_EVENT_INTERNALS]; + } + else if( event->id >= MALI_PMM_EVENT_UKS ) + { + ename = pmm_trace_events_uk[((int)event->id) - MALI_PMM_EVENT_UKS]; + } + else + { + ename = pmm_trace_events[event->id]; + } + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + dname = "os event"; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + dname = pmm_trace_get_core_name( (mali_pmm_core_mask)event->data ); + break; + + case MALI_PMM_EVENT_TIMEOUT: + dname = "timeout start"; + /* Print data with a different format */ + format = "PMM Trace: Event %s { (%d) %s, %d ticks, %d ticks %s }"; + break; + default: + dname = "unknown data"; + } + + MALI_PRINT( (format, tname, (u32)event->id, ename, event->ts, (u32)event->data, dname) ); +} + +#endif /* MALI_PMM_TRACE */ + + +/****************** Mali Kernel API *****************/ + +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ) +{ + mali_subsystem_pmm_id = id; + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(PMU, malipmm_create)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource) +{ + /* Create PMM state memory */ + MALI_DEBUG_ASSERT( pmm_state == NULL ); + pmm_state = (_mali_pmm_internal_state_t *) _mali_osk_malloc(sizeof(*pmm_state)); + MALI_CHECK_NON_NULL( pmm_state, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmm_state, 0, sizeof(*pmm_state)); + + /* Set up the initial PMM state */ + pmm_state->waiting = 0; + pmm_state->status = MALI_PMM_STATUS_IDLE; + pmm_state->state = MALI_PMM_STATE_UNAVAILABLE; /* Until a core registers */ + + /* Set up policy via compile time option for the moment */ +#if MALI_PMM_ALWAYS_ON + pmm_state->policy = MALI_PMM_POLICY_ALWAYS_ON; +#else + pmm_state->policy = MALI_PMM_POLICY_JOB_CONTROL; +#endif + +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( MALI_PMM_POLICY_NONE, pmm_state->policy ); +#endif + + /* Set up assumes all values are initialized to NULL or MALI_FALSE, so + * we can exit halfway through set up and perform clean up + */ +#if !MALI_PMM_NO_PMU + if( mali_platform_init(resource) != _MALI_OSK_ERR_OK ) goto pmm_fail_cleanup; + pmm_state->pmu_initialized = MALI_TRUE; +#endif + + pmm_state->queue = _mali_osk_notification_queue_init(); + if( !pmm_state->queue ) goto pmm_fail_cleanup; + + pmm_state->iqueue = _mali_osk_notification_queue_init(); + if( !pmm_state->iqueue ) goto pmm_fail_cleanup; + + /* We are creating an IRQ handler just for the worker thread it gives us */ + pmm_state->irq = _mali_osk_irq_init( _MALI_OSK_IRQ_NUMBER_PMM, + malipmm_irq_uhandler, + malipmm_irq_bhandler, + NULL, + NULL, + (void *)pmm_state, /* PMM state is passed to IRQ */ + "PMM handler" ); + + if( !pmm_state->irq ) goto pmm_fail_cleanup; + + pmm_state->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 75); + if( !pmm_state->lock ) goto pmm_fail_cleanup; + + if( _mali_osk_atomic_init( &(pmm_state->messages_queued), 0 ) != _MALI_OSK_ERR_OK ) + { + goto pmm_fail_cleanup; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem created, policy=%d\n", pmm_state->policy) ); + + MALI_SUCCESS; + +pmm_fail_cleanup: + MALI_PRINT_ERROR( ("PMM: subsystem failed to be created\n") ); + if( pmm_state ) + { + _mali_osk_resource_type_t t = PMU; + if( pmm_state->lock ) _mali_osk_lock_term( pmm_state->lock ); + if( pmm_state->irq ) _mali_osk_irq_term( pmm_state->irq ); + if( pmm_state->queue ) _mali_osk_notification_queue_term( pmm_state->queue ); + if( pmm_state->iqueue ) _mali_osk_notification_queue_term( pmm_state->iqueue ); + if( pmm_state->pmu_initialized ) ( mali_platform_deinit(&t) ); + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + MALI_ERROR( _MALI_OSK_ERR_FAULT ); +} + +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem loaded, policy initializing\n") ); + +#if PMM_OS_TEST + power_test_start(); +#endif + + /* Initialize the profile now the system has loaded - so that cores are + * not turned off during start up + */ + return pmm_policy_init( pmm ); +} + +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ) +{ + /* Check this is the right system */ + MALI_DEBUG_ASSERT( id == mali_subsystem_pmm_id ); + MALI_DEBUG_ASSERT_POINTER(pmm_state); + + if( pmm_state ) + { + _mali_osk_resource_type_t t = PMU; +#if PMM_OS_TEST + power_test_end(); +#endif + /* Get the lock so we can shutdown */ + MALI_PMM_LOCK(pmm_state); +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + pmm_state->status = MALI_PMM_STATUS_OFF; +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + MALI_PMM_UNLOCK(pmm_state); + pmm_policy_term(pmm_state); + _mali_osk_irq_term( pmm_state->irq ); + _mali_osk_notification_queue_term( pmm_state->queue ); + _mali_osk_notification_queue_term( pmm_state->iqueue ); + if( pmm_state->pmu_initialized ) mali_platform_deinit(&t); + _mali_osk_atomic_term( &(pmm_state->messages_queued) ); + MALI_PMM_LOCK_TERM(pmm_state); + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem terminated\n") ); +} + +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ) +{ + _mali_osk_errcode_t err; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( pmm == NULL ) + { + /* PMM state has not been created, this is because the PMU resource has not been + * created yet. + * This probably means that the PMU resource has not been specfied as the first + * resource in the config file + */ + MALI_PRINT_ERROR( ("PMM: Cannot register core %s because the PMU resource has not been\n initialized. Please make sure the PMU resource is the first resource in the\n resource configuration.\n", + pmm_trace_get_core_name(core)) ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered more than once in PMM */ + MALI_DEBUG_ASSERT( (pmm->cores_registered & core) == 0 ); + + MALIPMM_DEBUG_PRINT( ("PMM: core registered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + +#if !MALI_PMM_NO_PMU + /* Make sure the core is powered up */ + err = mali_platform_powerup( core ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( _MALI_OSK_ERR_OK == err ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Assume a registered core is now powered up and idle */ + pmm->cores_registered |= core; + pmm->cores_idle |= core; + pmm->cores_powered |= core; + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + MALI_PRINT_ERROR( ("PMM: Error(%d) powering up registered core: (0x%x) %s\n", + err, core, pmm_trace_get_core_name(core)) ); + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + + return err; +} + +void malipmm_core_unregister( mali_pmm_core_id core ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered in PMM */ + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, core ); + + MALIPMM_DEBUG_PRINT( ("PMM: core unregistered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + +#if !MALI_PMM_NO_PMU + /* Turn off the core */ + if( mali_platform_powerdown( core ) != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Error powering down unregistered core: (0x%x) %s\n", + core, pmm_trace_get_core_name(core)) ); + } +#endif + + /* Remove the core from the system */ + pmm->cores_registered &= (~core); + pmm->cores_idle &= (~core); + pmm->cores_powered &= (~core); + pmm->cores_pend_down &= (~core); + pmm->cores_pend_up &= (~core); + pmm->cores_ack_down &= (~core); + pmm->cores_ack_up &= (~core); + + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} +void malipmm_core_power_down_okay( mali_pmm_core_id core ) +{ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK, + 0 }; + + event.data = core; + + _mali_ukk_pmm_event_message( &event ); +} + +void malipmm_set_policy_check() +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + pmm->check_policy = MALI_TRUE; + + /* To check the policy we need to schedule some work */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +_mali_osk_errcode_t malipmm_irq_uhandler(void *data) +{ + MALIPMM_DEBUG_PRINT( ("PMM: uhandler - not expected to be used\n") ); + + MALI_SUCCESS; +} + +void malipmm_irq_bhandler(void *data) +{ + _mali_pmm_internal_state_t *pmm; + pmm = (_mali_pmm_internal_state_t *)data; + MALI_DEBUG_ASSERT_POINTER(pmm); + +#if PMM_OS_TEST + if( power_test_check() ) return; +#endif + + MALI_PMM_LOCK(pmm); +#ifdef MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Quick out when we are shutting down */ + if( pmm->status == MALI_PMM_STATUS_OFF ) + { + + #if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; + #endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + return; + } + + MALIPMM_DEBUG_PRINT( ("PMM: bhandler - Processing event\n") ); + + if( pmm->missed > 0 ) + { + MALI_PRINT_ERROR( ("PMM: Failed to send %d events", pmm->missed) ); + pmm_fatal_reset( pmm ); + } + + if( pmm->check_policy ) + { + pmm->check_policy = MALI_FALSE; + pmm_policy_check_policy(pmm); + } + else + { + /* Perform event processing */ + pmm_event_process(); + if( pmm->fatal_power_err ) + { + /* Try a reset */ + pmm_fatal_reset( pmm ); + } + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} + +static void pmm_event_process( void ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + mali_pmm_message_t *event; + u32 process_messages; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + + /* Max number of messages to process before exiting - as we shouldn't stay + * processing the messages for a long time + */ + process_messages = _mali_osk_atomic_read( &(pmm->messages_queued) ); + + while( process_messages > 0 ) + { + /* Check internal message queue first */ + err = _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + if( pmm->status == MALI_PMM_STATUS_IDLE || pmm->status == MALI_PMM_STATUS_OS_WAITING || pmm->status == MALI_PMM_STATUS_DVFS_PAUSE) + { + if( pmm->waiting > 0 ) pmm->waiting--; + + /* We aren't busy changing state, so look at real events */ + err = _mali_osk_notification_queue_dequeue( pmm->queue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + pmm->no_events++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - No message to process\n") ); + /* Nothing to do - so return */ + return; + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_received++; + #endif + } + } + else + { + /* Waiting for an internal message */ + pmm->waiting++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - Waiting for internal message, messages queued=%d\n", pmm->waiting) ); + return; + } + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_received++; + #endif + } + + MALI_DEBUG_ASSERT_POINTER( msg ); + /* Check the message type matches */ + MALI_DEBUG_ASSERT( msg->notification_type == MALI_PMM_NOTIFICATION_TYPE ); + + event = msg->result_buffer; + + _mali_osk_atomic_dec( &(pmm->messages_queued) ); + process_messages--; + + #if MALI_PMM_TRACE + /* Trace before we process the event in case we have an error */ + _mali_pmm_trace_event_message( event, MALI_TRUE ); + #endif + err = pmm_policy_process( pmm, event ); + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Error(%d) in policy %d when processing event message with id: %d", + err, pmm->policy, event->id) ); + } + + /* Delete notification */ + _mali_osk_notification_delete ( msg ); + + if( pmm->fatal_power_err ) + { + /* Nothing good has happened - exit */ + return; + } + + + #if MALI_PMM_TRACE + MALI_PRINT( ("PMM Trace: Event processed, msgs (sent/read) = %d/%d, int msgs (sent/read) = %d/%d, no events = %d, waiting = %d\n", + pmm->messages_sent, pmm->messages_received, pmm->imessages_sent, pmm->imessages_received, pmm->no_events, pmm->waiting) ); + #endif + } + + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->waiting > 0 ) + { + /* For events we ignored whilst we were busy, add a new + * scheduled time to look at them */ + _mali_osk_irq_schedulework( pmm->irq ); + } +} + +#if MALI_STATE_TRACKING +void malipmm_subsystem_dump_state(void) +{ + malipmm_state_dump(); +} +#endif + +#if (defined(DEBUG) || MALI_STATE_TRACKING) +void malipmm_state_dump() +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( !pmm ) + { + MALI_PRINT(("PMM: Null state\n")); + } + else + { + MALI_PRINT(("Locks::\nPMM_LOCK_STATUS=%ld",pmm->mali_pmm_lock_acquired)); + MALI_PRINT(("PMM state:\nPrevious_status=%d\nstatus=%d\nCurrent_event=%d\npolicy=%d\ncheck_policy=%d\nstate=%d\n", pmm->mali_last_pmm_status,pmm->status, pmm->mali_new_event_status, pmm->policy, pmm->check_policy, pmm->state)); + MALI_PRINT(("PMM cores:\ncores_registered=%d\ncores_powered=%d\ncores_idle=%d\ncores_pend_down=%d\ncores_pend_up=%d\ncores_ack_down=%d\ncores_ack_up=%d\n", pmm->cores_registered, pmm->cores_powered, pmm->cores_idle, pmm->cores_pend_down, pmm->cores_pend_up, pmm->cores_ack_down, pmm->cores_ack_up)); + MALI_PRINT(("PMM misc:\npmu_init=%d\nmessages_queued=%d\nwaiting=%d\nno_events=%d\nmissed=%d\nfatal_power_err=%d\n", pmm->pmu_initialized, _mali_osk_atomic_read( &(pmm->messages_queued) ), pmm->waiting, pmm->no_events, pmm->missed, pmm->fatal_power_err)); + } +} +#endif + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.h new file mode 100644 index 00000000000..fe7a046cb47 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm.h + * Defines the power management module for the kernel device driver + */ + +#ifndef __MALI_PMM_H__ +#define __MALI_PMM_H__ + +/* For mali_pmm_message_data and MALI_PMM_EVENT_UK_* defines */ +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @defgroup pmmapi Power Management Module APIs + * + * @{ + */ + +/** OS event tester */ +#define PMM_OS_TEST 0 + +/** @brief Compile option to turn on/off tracing */ +#define MALI_PMM_TRACE 0 +#define MALI_PMM_TRACE_SENT_EVENTS 0 + +/** @brief Compile option to switch between always on or job control PMM policy */ +#define MALI_PMM_ALWAYS_ON 0 + +/** @brief Overrides hardware PMU and uses software simulation instead + * @note This even stops intialization of PMU and cores being powered on at start up + */ +#define MALI_PMM_NO_PMU 0 + +/** @brief PMM debug print to control debug message level */ +#define MALIPMM_DEBUG_PRINT(args) \ + MALI_DEBUG_PRINT(3, args) + + +/** @brief power management event message identifiers. + */ +/* These must match up with the pmm_trace_events & pmm_trace_events_internal + * arrays + */ +typedef enum mali_pmm_event_id +{ + MALI_PMM_EVENT_OS_POWER_UP = 0, /**< OS power up event */ + MALI_PMM_EVENT_OS_POWER_DOWN = 1, /**< OS power down event */ + MALI_PMM_EVENT_JOB_SCHEDULED = 2, /**< Job scheduled to run event */ + MALI_PMM_EVENT_JOB_QUEUED = 3, /**< Job queued (but not run) event */ + MALI_PMM_EVENT_JOB_FINISHED = 4, /**< Job finished event */ + MALI_PMM_EVENT_TIMEOUT = 5, /**< Time out timer has expired */ + MALI_PMM_EVENT_DVFS_PAUSE = 6, /**< Mali device pause event */ + MALI_PMM_EVENT_DVFS_RESUME = 7, /**< Mali device resume event */ + + MALI_PMM_EVENT_UKS = 200, /**< Events from the user-side start here */ + MALI_PMM_EVENT_UK_EXAMPLE = _MALI_PMM_EVENT_UK_EXAMPLE, + + MALI_PMM_EVENT_INTERNALS = 1000, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK = 1001, /**< Internal power up acknowledgement */ + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK = 1002, /**< Internal power down acknowledgment */ +} mali_pmm_event_id; + + +/** @brief Use this when the power up/down callbacks do not need any OS data. */ +#define MALI_PMM_NO_OS_DATA 1 + + +/* @brief Geometry and pixel processor identifiers for the PMM + * + * @note these match the ARM Mali 400 PMU hardware definitions, apart from the "SYSTEM" + */ +typedef enum mali_pmm_core_id_tag +{ + MALI_PMM_CORE_SYSTEM = 0x00000000, /**< All of the Mali hardware */ + MALI_PMM_CORE_GP = 0x00000001, /**< Mali GP2 */ + MALI_PMM_CORE_L2 = 0x00000002, /**< Level 2 cache */ + MALI_PMM_CORE_PP0 = 0x00000004, /**< Mali 200 pixel processor 0 */ + MALI_PMM_CORE_PP1 = 0x00000008, /**< Mali 200 pixel processor 1 */ + MALI_PMM_CORE_PP2 = 0x00000010, /**< Mali 200 pixel processor 2 */ + MALI_PMM_CORE_PP3 = 0x00000020, /**< Mali 200 pixel processor 3 */ + MALI_PMM_CORE_PP_ALL = 0x0000003C /**< Mali 200 pixel processors 0-3 */ +} mali_pmm_core_id; + +/* @brief PMM bitmask of mali_pmm_core_ids + */ +typedef u32 mali_pmm_core_mask; + +/* @brief PMM event timestamp type + */ +typedef u32 mali_pmm_timestamp; + +/** @brief power management event message struct + */ +typedef struct _mali_pmm_message +{ + mali_pmm_event_id id; /**< event id */ + mali_pmm_message_data data; /**< specific data associated with the event */ + mali_pmm_timestamp ts; /**< timestamp the event was placed in the event queue */ +} mali_pmm_message_t; + + + +/** @brief the state of the power management module. + */ +/* These must match up with the pmm_trace_state array */ +typedef enum mali_pmm_state_tag +{ + MALI_PMM_STATE_UNAVAILABLE = 0, /**< PMM is not available */ + MALI_PMM_STATE_SYSTEM_ON = 1, /**< All of the Mali hardware is on */ + MALI_PMM_STATE_SYSTEM_OFF = 2, /**< All of the Mali hardware is off */ + MALI_PMM_STATE_SYSTEM_TRANSITION = 3 /**< System is changing state */ +} mali_pmm_state; + + +/** @brief a power management policy. + */ +/* These must match up with the pmm_trace_policy array */ +typedef enum mali_pmm_policy_tag +{ + MALI_PMM_POLICY_NONE = 0, /**< No policy */ + MALI_PMM_POLICY_ALWAYS_ON = 1, /**< Always on policy */ + MALI_PMM_POLICY_JOB_CONTROL = 2, /**< Job control policy */ + MALI_PMM_POLICY_RUNTIME_JOB_CONTROL = 3 /**< Run time power management control policy */ +} mali_pmm_policy; + +/** @brief Function to report to the OS when the power down has finished + * + * @param data The event message data that initiated the power down + */ +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data); + +/** @brief Function to report to the OS when the power up has finished + * + * @param data The event message data that initiated the power up + */ +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data); + +/** @brief Function to report that DVFS operation done + * + * @param data The event message data + */ +void _mali_osk_pmm_dvfs_operation_done(mali_pmm_message_data data); + +#if MALI_POWER_MGMT_TEST_SUITE +/** @brief Function to notify power management events + * + * @param data The event message data + */ +void _mali_osk_pmm_policy_events_notifications(mali_pmm_event_id event_id); + +#endif + +/** @brief Function to report the OS that device is idle + * + * @note inform the OS that device is idle + */ +_mali_osk_errcode_t _mali_osk_pmm_dev_idle( void ); + +/** @brief Function to report the OS to activate device + * + * @note inform the os that device needs to be activated + */ +void _mali_osk_pmm_dev_activate( void ); + +/** @brief Queries the current state of the PMM software + * + * @note the state of the PMM can change after this call has returned + * + * @return the current PMM state value + */ +mali_pmm_state _mali_pmm_state( void ); + +/** @brief List of cores that are registered with the PMM + * + * This will return the cores that have been currently registered with the PMM, + * which is a bitwise OR of the mali_pmm_core_id_tags. A value of 0x0 means that + * there are no cores registered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that have been registered with the PMM + */ +mali_pmm_core_mask _mali_pmm_cores_list( void ); + +/** @brief List of cores that are powered up in the PMM + * + * This will return the subset of the cores that can be listed using mali_pmm_cores_ + * list, that have power. It is a bitwise OR of the mali_pmm_core_id_tags. A value of + * 0x0 means that none of the cores registered are powered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that are powered up + */ +mali_pmm_core_mask _mali_pmm_cores_powered( void ); + + +/** @brief List of power management policies that are supported by the PMM + * + * Given an empty array of policies - policy_list - which contains the number + * of entries as specified by - policy_list_size, this function will populate + * the list with the available policies. If the policy_list is too small for + * all the policies then only policy_list_size entries will be returned. If the + * policy_list is bigger than the number of available policies then, the extra + * entries will be set to MALI_PMM_POLICY_NONE. + * The function will also update available_policies with the number of policies + * that are available, even if it exceeds the policy_list_size. + * The function will succeed if all policies could be returned, else it will + * fail if none or only a subset of policies could be returned. + * The function will also fail if no policy_list is supplied, though + * available_policies is optional. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy_list_size is the number of policies that can be returned in + * the policy_list argument + * @param policy_list is an array of policies that should be populated with + * the list of policies that are supported by the PMM + * @param policies_available optional argument, if non-NULL will be set to the + * number of policies available + * @return _MALI_OSK_ERR_OK if the policies could be listed, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ); + +/** @brief Set the power management policy in the PMM + * + * Given a valid supported policy, this function will change the PMM to use + * this new policy + * The function will fail if the policy given is invalid or unsupported. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy the new policy to be set + * @return _MALI_OSK_ERR_OK if the policy could be set, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ); + +/** @brief Get the current power management policy in the PMM + * + * Given a pointer to a policy data type, this function will return the current + * policy that is in effect for the PMM. This maybe out of date if there is a + * pending set policy call that has not been serviced. + * The function will fail if the policy given is NULL. + * + * @note the policy of the PMM can change after this call has returned + * + * @param policy a pointer to a policy that can be updated to the current + * policy + * @return _MALI_OSK_ERR_OK if the policy could be returned, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ); + +#if MALI_PMM_TRACE + +/** @brief Indicates when a hardware state change occurs in the PMM + * + * @param old a mask of the cores indicating the previous state of the cores + * @param newstate a mask of the cores indicating the new current state of the cores + */ +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ); + +/** @brief Indicates when a state change occurs in the PMM + * + * @param old the previous state for the PMM + * @param newstate the new current state of the PMM + */ +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ); + +/** @brief Indicates when a policy change occurs in the PMM + * + * @param old the previous policy for the PMM + * @param newpolicy the new current policy of the PMM + */ +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ); + +/** @brief Records when an event message is read by the event system + * + * @param event the message details + * @param received MALI_TRUE when the message is received by the PMM, else it is being sent + */ +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ); + +#endif /* MALI_PMM_TRACE */ + +/** @brief Dumps the current state of OS PMM thread + */ +#if MALI_STATE_TRACKING +void mali_pmm_dump_os_thread_state( void ); +#endif /* MALI_STATE_TRACKING */ + +#if (defined(DEBUG) || MALI_STATE_TRACKING) +/** @brief Dumps the current state of the PMM + */ +void malipmm_state_dump( void ); +#endif + +/** @} */ /* end group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.c new file mode 100644 index 00000000000..327e8b4c853 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.c + * Implementation of the common routines for power management module + * policies + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" + +#include "mali_pmm_policy_alwayson.h" +#include "mali_pmm_policy_jobcontrol.h" + +/* Call back function for timer expiration */ +static void pmm_policy_timer_callback( void *arg ); + +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* All values get 0 as default */ + _mali_osk_memset(pptimer, 0, sizeof(*pptimer)); + + pptimer->timer = _mali_osk_timer_init(); + if( pptimer->timer ) + { + _mali_osk_timer_setcallback( pptimer->timer, pmm_policy_timer_callback, (void *)pptimer ); + pptimer->timeout = timeout; + pptimer->event_id = id; + MALI_SUCCESS; + } + + return _MALI_OSK_ERR_FAULT; +} + +static void pmm_policy_timer_callback( void *arg ) +{ + _pmm_policy_timer_t *pptimer = (_pmm_policy_timer_t *)arg; + + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT( pptimer->set ); + + /* Set timer expired and flag there is a policy to check */ + pptimer->expired = MALI_TRUE; + malipmm_set_policy_check(); +} + + +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + _mali_osk_timer_del( pptimer->timer ); + _mali_osk_timer_term( pptimer->timer ); + pptimer->timer = NULL; +} + +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( !(pptimer->set) ) + { + pptimer->set = MALI_TRUE; + pptimer->expired = MALI_FALSE; + pptimer->start = _mali_osk_time_tickcount(); + _mali_osk_timer_add( pptimer->timer, pptimer->timeout ); + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( pptimer->set ) + { + _mali_osk_timer_del( pptimer->timer ); + pptimer->set = MALI_FALSE; + pptimer->expired = MALI_FALSE; + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + if( pptimer->expired ) + { + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_TIMEOUT, /* Assume timeout id, but set it below */ + 0 }; + + event.id = pptimer->event_id; + event.data = (mali_pmm_message_data)pptimer->start; + + /* Don't need to do any other notification with this timer */ + pptimer->expired = MALI_FALSE; + /* Unset timer so it is free to be set again */ + pptimer->set = MALI_FALSE; + + _mali_ukk_pmm_event_message( &event ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ) +{ + return (_mali_osk_time_after( other_start, timer_start ) == 0); +} + + +_mali_osk_errcode_t pmm_policy_init(_mali_pmm_internal_state_t *pmm) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_init_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_init_job_control(pmm); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + +void pmm_policy_term(_mali_pmm_internal_state_t *pmm) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + pmm_policy_term_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_term_job_control(); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + MALI_PRINT_ERROR( ("PMM: Invalid policy terminated %d\n", pmm->policy) ); + } +} + + +_mali_osk_errcode_t pmm_policy_process(_mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_process_always_on( pmm, event ); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_process_job_control( pmm, event ); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + + +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_check_job_control(); + } + break; + + default: + /* Nothing needs to be done */ + break; + } +} + + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.h new file mode 100644 index 00000000000..83cb7f29a92 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_H__ +#define __MALI_PMM_POLICY_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Generic timer for use with policies + */ +typedef struct _pmm_policy_timer +{ + u32 timeout; /**< Timeout for this timer in ticks */ + mali_pmm_event_id event_id; /**< Event id that will be raised when timer expires */ + _mali_osk_timer_t *timer; /**< Timer */ + mali_bool set; /**< Timer set */ + mali_bool expired; /**< Timer expired - event needs to be raised */ + u32 start; /**< Timer start ticks */ +} _pmm_policy_timer_t; + +/** @brief Policy timer initialization + * + * This will create a timer for use in policies, but won't start it + * + * @param pptimer An empty timer structure to be initialized + * @param timeout Timeout in ticks for the timer + * @param id Event id that will be raised on timeout + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ); + +/** @brief Policy timer termination + * + * This will clean up a timer that was previously used in policies, it + * will also stop it if started + * + * @param pptimer An initialized timer structure to be terminated + */ +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer start + * + * This will start a previously created timer for use in policies + * When the timer expires after the initialized timeout it will raise + * a PMM event of the event id given on initialization + * As data for the event it will pass the start time of the timer + * + * @param pptimer A previously initialized policy timer + * @return MALI_TRUE if the timer was started, MALI_FALSE if it is already started + */ +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This will stop a previously created timer for use in policies + * + * @param pptimer A previously started policy timer + * @return MALI_TRUE if the timer was stopped, MALI_FALSE if it is already stopped + */ +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This raise an event for an expired timer + * + * @param pptimer An expired policy timer + * @return MALI_TRUE if an event was raised, else MALI_FALSE + */ +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer valid checker + * + * This will check that a timer was started after a given time + * + * @param timer_start Time the timer was started + * @param other_start Time when another event or action occurred + * @return MALI_TRUE if the timer was started after the other time, else MALI_FALSE + */ +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ); + + +/** @brief Common policy initialization + * + * This will initialize the current policy + * + * @note Any previously initialized policy should be terminated first + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy termination + * + * This will terminate the current policy. + * @note This can be called when a policy has not been initialized + */ +void pmm_policy_term( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy state changer + * + * Given the next available event message, this routine passes it to + * the current policy for processing + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + + +/** @brief Common policy checker + * + * If a policy timer fires then this function will be called to + * allow the policy to take the correct action + * + * @param pmm internal PMM state + */ +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ); + +/** @} */ /* End group pmmapi_policy */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.c new file mode 100644 index 00000000000..643bb04553b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_alwayson.c + * Implementation of the power management module policy - always on + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_policy_alwayson.h" + +_mali_osk_errcode_t pmm_policy_init_always_on(void) +{ + /* Nothing to set up */ + MALI_SUCCESS; +} + +void pmm_policy_term_always_on(void) +{ + /* Nothing to tear down */ +} + +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* We aren't going to do anything, but signal so we don't block the OS + * NOTE: This may adversely affect any jobs Mali is currently running + */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + case MALI_PMM_EVENT_OS_POWER_UP: + /* Nothing to do */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + /* Nothing to do - we are always on */ + break; + + case MALI_PMM_EVENT_TIMEOUT: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + default: + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + + MALI_SUCCESS; +} + +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.h new file mode 100644 index 00000000000..a158b09f610 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_alwayson.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_alwayson.h + * Defines the power management module policy for always on + */ + +#ifndef __MALI_PMM_POLICY_ALWAYSON_H__ +#define __MALI_PMM_POLICY_ALWAYSON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Always on policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_always_on(void); + +/** @brief Always on policy termination + */ +void pmm_policy_term_always_on(void); + +/** @brief Always on policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Always on policy will ignore all events and keep the Mali cores on + * all the time + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @} */ /* End group pmmapi_policies */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_ALWAYSON_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.c new file mode 100644 index 00000000000..8450bd722f4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_jobcontrol.c + * Implementation of the power management module policy - job control + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_policy_jobcontrol.h" + +typedef struct _pmm_policy_data_job_control +{ + _pmm_policy_timer_t latency; /**< Latency timeout timer for all cores */ + u32 core_active_start; /**< Last time a core was set to active */ + u32 timeout; /**< Timeout in ticks for latency timer */ +} _pmm_policy_data_job_control_t; + + +/* @ brief Local data for this policy + */ +static _pmm_policy_data_job_control_t *data_job_control = NULL; + +/* @brief Set up the timeout if it hasn't already been set and if there are active cores */ +static void job_control_timeout_setup( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Do we have an inactivity time out and some powered cores? */ + if( pptimer->timeout > 0 && pmm->cores_powered != 0 ) + { + /* Is the system idle and all the powered cores are idle? */ + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->cores_idle == pmm->cores_powered ) + { + if( pmm_policy_timer_start(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Setting in-activity latency timer\n") ); + } + } + else + { + /* We are not idle so there is no need for an inactivity timer + */ + if( pmm_policy_timer_stop(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Removing in-activity latency timer\n") ); + } + } + } +} + +/* @brief Check the validity of the timeout - and if there is one set */ +static mali_bool job_control_timeout_valid( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer, u32 timer_start ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Not a valid timer! */ + if( pptimer->timeout == 0 ) return MALI_FALSE; + + /* Are some cores powered and are they all idle? */ + if( (pmm->cores_powered != 0) && (pmm->cores_idle == pmm->cores_powered) ) + { + /* Has latency timeout started after the last core was active? */ + if( pmm_policy_timer_valid( timer_start, data_job_control->core_active_start ) ) + { + return MALI_TRUE; + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - out of date\n") ); + } + } + else + { + if( pmm->cores_powered == 0 ) + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores already off\n") ); + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores active\n") ); + } + } + + return MALI_FALSE; +} + +_mali_osk_errcode_t pmm_policy_init_job_control( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER( pmm ); + MALI_DEBUG_ASSERT( data_job_control == NULL ); + + data_job_control = (_pmm_policy_data_job_control_t *) _mali_osk_malloc(sizeof(*data_job_control)); + MALI_CHECK_NON_NULL( data_job_control, _MALI_OSK_ERR_NOMEM ); + + data_job_control->core_active_start = _mali_osk_time_tickcount(); + data_job_control->timeout = MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT; + + err = pmm_policy_timer_init( &data_job_control->latency, data_job_control->timeout, MALI_PMM_EVENT_TIMEOUT ); + if( err != _MALI_OSK_ERR_OK ) + { + _mali_osk_free( data_job_control ); + data_job_control = NULL; + return err; + } + + /* Start the latency timeout */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + MALI_SUCCESS; +} + +void pmm_policy_term_job_control(void) +{ + if( data_job_control != NULL ) + { + pmm_policy_timer_term( &data_job_control->latency ); + _mali_osk_free( data_job_control ); + data_job_control = NULL; + } +} + +static void pmm_policy_job_control_job_queued( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + + /* Make sure that all cores are powered in this + * simple policy + */ + cores = pmm->cores_registered; + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until finished */ + pmm->status = MALI_PMM_STATUS_POLICY_POWER_UP; + } + } +} + +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process start - status=%d\n", pmm->status) ); + + /* Mainly the data is the cores */ + cores = pmm_cores_from_event_data( pmm, event ); + +#if MALI_STATE_TRACKING + pmm->mali_last_pmm_status = pmm->status; +#endif /* MALI_STATE_TRACKING */ + + switch( pmm->status ) + { + /**************** IDLE ****************/ + case MALI_PMM_STATUS_IDLE: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + /* Not expected in this state */ + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + + /* Update idle cores to indicate active - remove these! */ + pmm_cores_set_active( pmm, cores ); + /* Remember when this happened */ + data_job_control->core_active_start = event->ts; +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_SCHEDULED); +#endif + + /*** FALL THROUGH to QUEUED to check POWER UP ***/ + + case MALI_PMM_EVENT_JOB_QUEUED: + + pmm_policy_job_control_job_queued( pmm ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_QUEUED); +#endif + break; + + case MALI_PMM_EVENT_DVFS_PAUSE: + + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if ( cores_subset != 0 ) + { + if ( !pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 1; + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + pmm_save_os_event_data( pmm, event->data ); + break; + } + } + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done(0); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + + /* Need to power down all cores even if we need to wait for them */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering down */ + if( !pmm_invoke_power_down( pmm ) ) + { + /* We need to wait until they are idle */ + + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + /* All cores now down - respond to OS power event */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_FINISHED: + + /* Update idle cores - add these! */ + pmm_cores_set_idle( pmm, cores ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_FINISHED); +#endif + if( data_job_control->timeout > 0 ) + { + /* Wait for time out to fire */ + break; + } + /* For job control policy - turn off all cores */ + cores = pmm->cores_powered; + + /*** FALL THROUGH to TIMEOUT TEST as NO TIMEOUT ***/ + + case MALI_PMM_EVENT_TIMEOUT: + + /* Main job control policy - turn off cores after inactivity */ + if( job_control_timeout_valid( pmm, &data_job_control->latency, (u32)event->data ) ) + { + /* Valid timeout of inactivity - so find out if we can power down + * immedately - if we can't then this means the cores are still in fact + * active + */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_TRUE ); + if( cores_subset != 0 ) + { + /* Check if we can really power down, if not then we are not + * really in-active + */ + if( !pmm_invoke_power_down( pmm ) ) + { + pmm_power_down_cancel( pmm ); + } + } + /* else there are no cores powered up! */ + } +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_TIMEOUT); +#endif + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /******************DVFS PAUSE**************/ + case MALI_PMM_STATUS_DVFS_PAUSE: + switch ( event->id ) + { + case MALI_PMM_EVENT_DVFS_RESUME: + + if ( pmm->cores_powered != 0 ) + { + pmm->cores_ack_down =0; + pmm_power_down_cancel( pmm ); + pmm->status = MALI_PMM_STATUS_IDLE; + } + else + { + pmm_policy_job_control_job_queued( pmm ); + } + _mali_osk_pmm_dvfs_operation_done( 0 ); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + if ( pmm->cores_powered != 0 ) + { + if ( pmm_invoke_power_down( pmm ) ) + { + _mali_osk_pmm_power_down_done( 0 ); + break; + } + } + _mali_osk_pmm_power_down_done( 0 ); + break; + default: + break; + } + break; + + /**************** POWER UP ****************/ + case MALI_PMM_STATUS_OS_POWER_UP: + case MALI_PMM_STATUS_POLICY_POWER_UP: + switch( event->id ) + { + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + /* Make sure cores powered off equal what we expect */ + MALI_DEBUG_ASSERT( cores == pmm->cores_pend_up ); + pmm_cores_set_up_ack( pmm, cores ); + + if( pmm_invoke_power_up( pmm ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_IDLE; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /**************** POWER DOWN ****************/ + case MALI_PMM_STATUS_OS_POWER_DOWN: + case MALI_PMM_STATUS_POLICY_POWER_DOWN: + switch( event->id ) + { + + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + + pmm_cores_set_down_ack( pmm, cores ); + + if ( pmm->is_dvfs_active == 1 ) + { + if( pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 0; + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done( pmm_retrieve_os_event_data( pmm ) ); + } + break; + } + + /* Now check if we can power down */ + if( pmm_invoke_power_down( pmm ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_OS_WAITING; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + case MALI_PMM_STATUS_OS_WAITING: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until power up complete */ + pmm->status = MALI_PMM_STATUS_OS_POWER_UP; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + pmm->status = MALI_PMM_STATUS_IDLE; + /* All cores now up - respond to OS power up event */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + default: + /* All other messages are ignored in this state */ + break; + } + break; + + default: + /* Unexpected state */ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Set in-activity latency timer - if required */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + /* Update the PMM state */ + pmm_update_system_state( pmm ); +#if MALI_STATE_TRACKING + pmm->mali_new_event_status = event->id; +#endif /* MALI_STATE_TRACKING */ + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process end - status=%d and event=%d\n", pmm->status,event->id) ); + + MALI_SUCCESS; +} + +void pmm_policy_check_job_control() +{ + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + /* Latency timer must have expired raise the event */ + pmm_policy_timer_raise_event(&data_job_control->latency); +} + + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.h new file mode 100644 index 00000000000..455234b80bc --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_policy_jobcontrol.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_JOBCONTROL_H__ +#define __MALI_PMM_POLICY_JOBCONTROL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief The jobcontrol policy inactivity latency timeout (in ticks) + * before the hardware is switched off + * + * @note Setting this low whilst tracing or producing debug output can + * cause alot of timeouts to fire which can affect the PMM behaviour + */ +#define MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT 50 + +/** @brief Job control policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_job_control(_mali_pmm_internal_state_t *pmm); + +/** @brief Job control policy termination + */ +void pmm_policy_term_job_control(void); + +/** @brief Job control policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Job control policy depends on events from the Mali cores, and will + * power down all cores after an inactivity latency timeout. It will + * power the cores back on again when a job is scheduled to run. + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Job control policy checker + * + * The latency timer has fired and we need to raise the correct event to + * handle it + * + * @param pmm internal PMM state + */ +void pmm_policy_check_job_control(void); + +/** @} */ /* End group pmmapi_policy */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_JOBCONTROL_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.c new file mode 100644 index 00000000000..a0ac1c7790c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.c @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_state.c + * Implementation of the power management module internal state + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_state.h" +#include "mali_pmm_system.h" + +#include "mali_kernel_core.h" +#include "mali_platform.h" + +#define SIZEOF_CORES_LIST 6 + +/* NOTE: L2 *MUST* be first on the list so that it + * is correctly powered on first and powered off last + */ +static mali_pmm_core_id cores_list[] = { MALI_PMM_CORE_L2, + MALI_PMM_CORE_GP, + MALI_PMM_CORE_PP0, + MALI_PMM_CORE_PP1, + MALI_PMM_CORE_PP2, + MALI_PMM_CORE_PP3 }; + + + +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_state state; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm->cores_registered == 0 ) + { + state = MALI_PMM_STATE_UNAVAILABLE; + } + else if( pmm->cores_powered == 0 ) + { + state = MALI_PMM_STATE_SYSTEM_OFF; + } + else if( pmm->cores_powered == pmm->cores_registered ) + { + state = MALI_PMM_STATE_SYSTEM_ON; + } + else + { + /* Some other state where not everything is on or off */ + state = MALI_PMM_STATE_SYSTEM_TRANSITION; + } + +#if MALI_PMM_TRACE + _mali_pmm_trace_state_change( pmm->state, state ); +#endif + pmm->state = state; +} + +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* All cores - the system */ + cores = pmm->cores_registered; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Currently the main event data is only the cores + * for these messages + */ + cores = (mali_pmm_core_mask)event->data; + if( cores == MALI_PMM_CORE_SYSTEM ) + { + cores = pmm->cores_registered; + } + else if( cores == MALI_PMM_CORE_PP_ALL ) + { + /* Get the subset of registered PP cores */ + cores = (pmm->cores_registered & MALI_PMM_CORE_PP_ALL); + } + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + break; + + default: + /* Assume timeout messages - report cores still powered */ + cores = pmm->cores_powered; + break; + } + + return cores; +} + +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power down when asked for power up */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + cores_subset = (~(pmm->cores_powered) & cores); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + pmm->cores_pend_up = cores_subset; + } + + return cores_subset; +} + +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ) +{ + mali_pmm_core_mask cores_subset; + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power up when asked for power down */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + cores_subset = (pmm->cores_powered & cores); + if( cores_subset != 0 ) + { + int n; + volatile mali_pmm_core_mask *ppowered = &(pmm->cores_powered); + + /* There are some cores that need powering up, but we may + * need to wait until they are idle + */ + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & cores_subset) != 0 ) + { + /* Core is to be powered down */ + pmm->cores_pend_down |= cores_list[n]; + + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * down, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Signal the core to power down + * If it is busy (not idle) it will set a pending power down flag + * (as long as we don't want to only immediately power down). + * If it isn't busy it will move out of the idle queue right + * away + */ + err = mali_core_signal_power_down( cores_list[n], immediate_only ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Re-read cores_subset in case it has changed */ + cores_subset = (*ppowered & cores); + + if( err == _MALI_OSK_ERR_OK ) + { + /* We moved an idle core to the power down queue + * which means it is now acknowledged (if it is still + * registered) + */ + pmm->cores_ack_down |= (cores_list[n] & cores_subset); + } + else + { + MALI_DEBUG_ASSERT( err == _MALI_OSK_ERR_BUSY || + (err == _MALI_OSK_ERR_FAULT && + (*ppowered & cores_list[n]) == 0) ); + /* If we didn't move a core - it must be active, so + * leave it pending, so we get an acknowledgement (when + * not in immediate only mode) + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + } + + return cores_subset; +} + +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ) +{ + int n; + mali_pmm_core_mask pd, ad; + _mali_osk_errcode_t err; + volatile mali_pmm_core_mask *pregistered; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: Cancelling power down\n") ); + + pd = pmm->cores_pend_down; + ad = pmm->cores_ack_down; + /* Clear the pending cores so that they don't move to the off + * queue if they haven't already + */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; + pregistered = &(pmm->cores_registered); + + /* Power up all the pending power down cores - just so + * we make sure the system is in a known state, as a + * pending core might have sent an acknowledged message + * which hasn't been read yet. + */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & pd) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following power up function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* As we are cancelling - only move the cores back to the queue - + * no reset needed + */ + err = mali_core_signal_power_up( cores_list[n], MALI_TRUE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Update pending list with the current registered cores */ + pd &= (*pregistered); + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_BUSY && + ((cores_list[n] & ad) == 0)) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + /* If we didn't power up a core - it must be active and + * hasn't actually tried to power down - this is expected + * for cores that haven't acknowledged + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + /* Only used in debug builds */ + MALI_IGNORE(ad); +} + + +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_down == pmm->cores_ack_down ? MALI_TRUE : MALI_FALSE ); +} + +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power down during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down != 0 ); + /* Check that cores are not pending power up during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + if( !pmm_power_down_okay( pmm ) ) + { + MALIPMM_DEBUG_PRINT( ("PMM: Waiting for cores to go idle for power off - 0x%08x / 0x%08x\n", + pmm->cores_pend_down, pmm->cores_ack_down) ); + return MALI_FALSE; + } + else + { +#if !MALI_PMM_NO_PMU + err = mali_platform_powerdown( pmm->cores_pend_down ); +#else + err = _MALI_OSK_ERR_OK; +#endif + + if( err == _MALI_OSK_ERR_OK ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Remove powered down cores from idle and powered list */ + pmm->cores_powered &= ~(pmm->cores_pend_down); + pmm->cores_idle &= ~(pmm->cores_pend_down); + /* Reset pending/acknowledged status */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power down cores - (0x%x) %s", + pmm->cores_pend_down, pmm_trace_get_core_name(pmm->cores_pend_down)) ); + pmm->fatal_power_err = MALI_TRUE; + } + } + + return MALI_TRUE; +} + + +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_up == pmm->cores_ack_up ? MALI_TRUE : MALI_FALSE ); +} + + +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power up during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up != 0 ); + /* Check that cores are not pending power down during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + if( pmm_power_up_okay( pmm ) ) + { + /* Power up has completed - sort out subsystem core status */ + + int n; + /* Use volatile to access, so that it is updated if any cores are unregistered */ + volatile mali_pmm_core_mask *ppendup = &(pmm->cores_pend_up); +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Move cores into idle queues */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & (*ppendup)) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_FAULT && + (*ppendup & cores_list[n]) == 0) ); + /* We only expect this to fail when we are shutting down + * and the core has been unregistered + */ + } + } + } + /* Finished power up - add cores to idle and powered list */ + pmm->cores_powered |= (*ppendup); + pmm->cores_idle |= (*ppendup); + /* Reset pending/acknowledge status */ + pmm->cores_pend_up = 0; + pmm->cores_ack_up = 0; + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + return MALI_TRUE; + } + else + { +#if !MALI_PMM_NO_PMU + /* Power up must now be done */ + err = mali_platform_powerup( pmm->cores_pend_up ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power up cores - (0x%x) %s", + pmm->cores_pend_up, pmm_trace_get_core_name(pmm->cores_pend_up)) ); + pmm->fatal_power_err = MALI_TRUE; + } + else + { + /* TBD - Update core status immediately rather than use event message */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK, + 0 }; + /* All the cores that were pending power up, have now completed power up */ + event.data = pmm->cores_pend_up; + _mali_ukk_pmm_event_message( &event ); + MALIPMM_DEBUG_PRINT( ("PMM: Sending ACK to power up") ); + } + } + + /* Always return false, as we need an interrupt to acknowledge + * when power up is complete + */ + return MALI_FALSE; +} + +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle &= (~cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle |= (cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power down */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_down & cores) != 0 ); + /* Check core has not acknowledged power down more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_down & cores) == 0 ); + + pmm->cores_ack_down |= (cores); + + return pmm->cores_ack_down; +} + +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + mali_pmm_status status; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALIPMM_DEBUG_PRINT( ("PMM: Fatal Reset called") ); + + MALI_DEBUG_ASSERT( pmm->status != MALI_PMM_STATUS_OFF ); + + /* Reset the common status */ + pmm->waiting = 0; + pmm->missed = 0; + pmm->fatal_power_err = MALI_FALSE; + pmm->no_events = 0; + pmm->check_policy = MALI_FALSE; + pmm->cores_pend_down = 0; + pmm->cores_pend_up = 0; + pmm->cores_ack_down = 0; + pmm->cores_ack_up = 0; + pmm->is_dvfs_active = 0; +#if MALI_PMM_TRACE + pmm->messages_sent = 0; + pmm->messages_received = 0; + pmm->imessages_sent = 0; + pmm->imessages_received = 0; + MALI_PRINT( ("PMM Trace: *** Fatal reset occurred ***") ); +#endif + + /* Set that we are unavailable whilst resetting */ + pmm->state = MALI_PMM_STATE_UNAVAILABLE; + status = pmm->status; + pmm->status = MALI_PMM_STATUS_OFF; + + /* We want all cores powered */ + pmm->cores_powered = pmm->cores_registered; + /* The cores may not be idle, but this state will be rectified later */ + pmm->cores_idle = pmm->cores_registered; + + /* So power on any cores that are registered */ + if( pmm->cores_registered != 0 ) + { + int n; + volatile mali_pmm_core_mask *pregistered = &(pmm->cores_registered); +#if !MALI_PMM_NO_PMU + err = mali_platform_powerup( pmm->cores_registered ); +#endif + if( err != _MALI_OSK_ERR_OK ) + { + /* This is very bad as we can't even be certain the cores are now + * powered up + */ + MALI_PRINT_ERROR( ("PMM: Failed to perform PMM reset!\n") ); + /* TBD driver exit? */ + } + + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & (*pregistered)) != 0 ) + { +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Core is now active - so try putting it in the idle queue */ + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* We either succeeded, or we were not off anyway, or we have + * just be deregistered + */ + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_OK) || + (err == _MALI_OSK_ERR_BUSY) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + } + } + } + + /* Unblock any pending OS event */ + if( status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + if( status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down + * NOTE: We are not powered down at this point due to power problems, + * so we are lying to the system, but something bad has already + * happened and we are trying unstick things + * TBD - Add busy loop to power down cores? + */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + + /* Purge the event queues */ + do + { + if( _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + do + { + if( _mali_osk_notification_queue_dequeue( pmm->queue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + /* Return status/state to normal */ + pmm->status = MALI_PMM_STATUS_IDLE; + pmm_update_system_state(pmm); +} + +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power up */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_up & cores) != 0 ); + /* Check core has not acknowledged power up more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_up & cores) == 0 ); + + pmm->cores_ack_up |= (cores); + + return pmm->cores_ack_up; +} + +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is no saved data */ + MALI_DEBUG_ASSERT( pmm->os_data == 0 ); + /* Can't store zero data - as retrieve check will fail */ + MALI_DEBUG_ASSERT( data != 0 ); + + pmm->os_data = data; +} + +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm) +{ + mali_pmm_message_data data; + + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is saved data */ + MALI_DEBUG_ASSERT( pmm->os_data != 0 ); + + /* Get data, and clear the saved version */ + data = pmm->os_data; + pmm->os_data = 0; + + return data; +} + +/* Create list of core names to look up + * We are doing it this way to overcome the need for + * either string allocation, or stack space, so we + * use constant strings instead + */ +typedef struct pmm_trace_corelist +{ + mali_pmm_core_mask id; + const char *name; +} pmm_trace_corelist_t; + +static pmm_trace_corelist_t pmm_trace_cores[] = { + { MALI_PMM_CORE_SYSTEM, "SYSTEM" }, + { MALI_PMM_CORE_GP, "GP" }, + { MALI_PMM_CORE_L2, "L2" }, + { MALI_PMM_CORE_PP0, "PP0" }, + { MALI_PMM_CORE_PP1, "PP1" }, + { MALI_PMM_CORE_PP2, "PP2" }, + { MALI_PMM_CORE_PP3, "PP3" }, + { MALI_PMM_CORE_PP_ALL, "PP (all)" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0), + "GP+L2+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0), + "GP+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+L2+PP0+PP1" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+PP0+PP1" }, + { 0, NULL } /* Terminator of list */ +}; + +const char *pmm_trace_get_core_name( mali_pmm_core_mask cores ) +{ + const char *dname = NULL; + int cl; + + /* Look up name in corelist */ + cl = 0; + while( pmm_trace_cores[cl].name != NULL ) + { + if( pmm_trace_cores[cl].id == cores ) + { + dname = pmm_trace_cores[cl].name; + break; + } + cl++; + } + + if( dname == NULL ) + { + /* We don't know a good short-hand for the configuration */ + dname = "[multi-core]"; + } + + return dname; +} + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.h new file mode 100644 index 00000000000..3c8c31f066d --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_state.h @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_state.h + * Defines the internal power management module state + */ + +#ifndef __MALI_PMM_STATE_H__ +#define __MALI_PMM_STATE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_state Power Management Module State + * + * @{ + */ + +/* Check that the subset is really a subset of cores */ +#define MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( cores, subset ) \ + MALI_DEBUG_ASSERT( ((~(cores)) & (subset)) == 0 ) + + +/* Locking macros */ +#define MALI_PMM_LOCK(pmm) \ + _mali_osk_lock_wait( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_UNLOCK(pmm) \ + _mali_osk_lock_signal( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_LOCK_TERM(pmm) \ + _mali_osk_lock_term( pmm->lock ) + +/* Notification type for messages */ +#define MALI_PMM_NOTIFICATION_TYPE 0 + +/** @brief Status of the PMM state machine + */ +typedef enum mali_pmm_status_tag +{ + MALI_PMM_STATUS_IDLE, /**< PMM is waiting next event */ + MALI_PMM_STATUS_POLICY_POWER_DOWN, /**< Policy initiated power down */ + MALI_PMM_STATUS_POLICY_POWER_UP, /**< Policy initiated power down */ + MALI_PMM_STATUS_OS_WAITING, /**< PMM is waiting for OS power up */ + MALI_PMM_STATUS_OS_POWER_DOWN, /**< OS initiated power down */ + MALI_PMM_STATUS_RUNTIME_IDLE_IN_PROGRESS, + MALI_PMM_STATUS_DVFS_PAUSE, /**< PMM DVFS Status Pause */ + MALI_PMM_STATUS_OS_POWER_UP, /**< OS initiated power up */ + MALI_PMM_STATUS_OFF, /**< PMM is not active */ +} mali_pmm_status; + + +/** @brief Internal state of the PMM + */ +typedef struct _mali_pmm_internal_state +{ + mali_pmm_status status; /**< PMM state machine */ + mali_pmm_policy policy; /**< PMM policy */ + mali_bool check_policy; /**< PMM policy needs checking */ + mali_pmm_state state; /**< PMM state */ + mali_pmm_core_mask cores_registered; /**< Bitmask of cores registered */ + mali_pmm_core_mask cores_powered; /**< Bitmask of cores powered up */ + mali_pmm_core_mask cores_idle; /**< Bitmask of cores idle */ + mali_pmm_core_mask cores_pend_down; /**< Bitmask of cores pending power down */ + mali_pmm_core_mask cores_pend_up; /**< Bitmask of cores pending power up */ + mali_pmm_core_mask cores_ack_down; /**< Bitmask of cores acknowledged power down */ + mali_pmm_core_mask cores_ack_up; /**< Bitmask of cores acknowledged power up */ + + _mali_osk_notification_queue_t *queue; /**< PMM event queue */ + _mali_osk_notification_queue_t *iqueue; /**< PMM internal event queue */ + _mali_osk_irq_t *irq; /**< PMM irq handler */ + _mali_osk_lock_t *lock; /**< PMM lock */ + + mali_pmm_message_data os_data; /**< OS data sent via the OS events */ + + mali_bool pmu_initialized; /**< PMU initialized */ + + _mali_osk_atomic_t messages_queued; /**< PMM event messages queued */ + u32 waiting; /**< PMM waiting events - due to busy */ + u32 no_events; /**< PMM called to process when no events */ + + u32 missed; /**< PMM missed events due to OOM */ + mali_bool fatal_power_err; /**< PMM has had a fatal power error? */ + u32 is_dvfs_active; /**< PMM DVFS activity */ + +#if (defined(DEBUG) || MALI_STATE_TRACKING) + u32 mali_last_pmm_status; + u32 mali_new_event_status; + u32 mali_pmm_lock_acquired; +#endif + +#if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + u32 messages_sent; /**< Total event messages sent */ + u32 messages_received; /**< Total event messages received */ + u32 imessages_sent; /**< Total event internal messages sent */ + u32 imessages_received; /**< Total event internal messages received */ +#endif +} _mali_pmm_internal_state_t; + +/** @brief Sets that a policy needs a check before processing events + * + * A timer or something has expired that needs dealing with + */ +void malipmm_set_policy_check(void); + +/** @brief Update the PMM externally viewable state depending on the current PMM internal state + * + * @param pmm internal PMM state + * @return MALI_TRUE if the timeout is valid, else MALI_FALSE + */ +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ); + +/** @brief Returns the core mask from the event data - if applicable + * + * @param pmm internal PMM state + * @param event event message to get the core mask from + * @return mask of cores that is relevant to this event message + */ +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Sort out which cores need to be powered up from the given core mask + * + * All cores that can be powered up will be put into a pending state + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered up + * @return mask of cores that need to be powered up, this can be 0 if all cores + * are powered up already + */ +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Sort out which cores need to be powered down from the given core mask + * + * All cores that can be powered down will be put into a pending state. If they + * can be powered down immediately they will also be acknowledged that they can be + * powered down. If the immediate_only flag is set, then only those cores that + * can be acknowledged for power down will be put into a pending state. + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered down + * @param immediate_only MALI_TRUE means that only cores that can power down now will + * be put into a pending state + * @return mask of cores that need to be powered down, this can be 0 if all cores + * are powered down already + */ +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ); + +/** @brief Cancel an invokation to power down (pmm_invoke_power_down) + * + * @param pmm internal PMM state + */ +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ); + +/** @brief Check if a call to invoke power down should succeed, or fail + * + * This will report MALI_FALSE if some of the cores are still active and need + * to acknowledge that they are ready to power down + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power down have acknowledged they + * can power down, else MALI_FALSE + */ +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power down + * + * If all the pending cores have acknowledged they can power down, this will call the + * PMU power down function to turn them off + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered down, else MALI_FALSE + */ +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm ); + +/** @brief Check if all the pending cores to power up have done so + * + * This will report MALI_FALSE if some of the cores are still powered off + * and have not acknowledged that they have powered up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power up have acknowledged they + * are now powered up, else MALI_FALSE + */ +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power up + * + * If all the pending cores have acknowledged they have powered up, this will + * make the cores start processing jobs again, else this will call the PMU + * power up function to turn them on, and the PMM is then expected to wait for an + * interrupt to acknowledge the power up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered up, else MALI_FALSE + */ +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ); + +/** @brief Set the cores that are now active in the system + * + * Updates which cores are active and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to active + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that are now idle in the system + * + * Updates which cores are idle and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to idle + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power down + * + * Updates which cores have acknowledged the pending power down and are now ready + * to be turned off + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power down + * @return mask of all the cores that have acknowledged the power down + */ +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power up + * + * Updates which cores have acknowledged the pending power up and are now + * fully powered and ready to run jobs + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power up + * @return mask of all the cores that have acknowledged the power up + */ +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + + +/** @brief Tries to reset the PMM and PMU hardware to a known state after any fatal issues + * + * This will try and make all the cores powered up and reset the PMM state + * to its initial state after core registration - all cores powered but not + * pending or active. + * All events in the event queues will be thrown away. + * + * @note: Any pending power down will be cancelled including the OS calling for power down + */ +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ); + +/** @brief Save the OS specific data for an OS power up/down event + * + * @param pmm internal PMM state + * @param data OS specific event data + */ +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data); + +/** @brief Retrieve the OS specific data for an OS power up/down event + * + * This will clear the stored OS data, as well as return it. + * + * @param pmm internal PMM state + * @return OS specific event data that was saved previously + */ +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm); + + +/** @brief Get a human readable name for the cores in a core mask + * + * @param core the core mask + * @return string containing a name relating to the given core mask + */ +const char *pmm_trace_get_core_name( mali_pmm_core_mask core ); + +/** @} */ /* End group pmmapi_state */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_STATE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_system.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_system.h new file mode 100644 index 00000000000..f5106479e33 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/common/pmm/mali_pmm_system.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_system.h + * Defines the power management module system functions + */ + +#ifndef __MALI_PMM_SYSTEM_H__ +#define __MALI_PMM_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_system Power Management Module System Functions + * + * @{ + */ + +extern struct mali_kernel_subsystem mali_subsystem_pmm; + +/** @brief Register a core with the PMM, which will power up + * the core + * + * @param core the core to register with the PMM + * @return error if the core cannot be powered up + */ +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ); + +/** @brief Unregister a core with the PMM + * + * @param core the core to unregister with the PMM + */ +void malipmm_core_unregister( mali_pmm_core_id core ); + +/** @brief Acknowledge that a power down is okay to happen + * + * A core should not be running a job, or be in the idle queue when this + * is called. + * + * @param core the core that can now be powered down + */ +void malipmm_core_power_down_okay( mali_pmm_core_id core ); + +/** @} */ /* End group pmmapi_system */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/license/gpl/mali_kernel_license.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/license/gpl/mali_kernel_license.h new file mode 100644 index 00000000000..e9e5e55a082 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/license/gpl/mali_kernel_license.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __MALI_KERNEL_LICENSE_H__ +#define __MALI_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_KERNEL_LINUX_LICENSE "GPL" +#define MALI_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.c new file mode 100644 index 00000000000..1c1bf306688 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.c @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_device_pause_resume.c + * Implementation of the Mali pause/resume functionality + */ +#if USING_MALI_PMM +#include +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" +#include "mali_linux_pm.h" +#include "mali_device_pause_resume.h" +#include "mali_pmm.h" +#include "mali_kernel_license.h" +#ifdef CONFIG_PM +#if MALI_LICENSE_IS_GPL + +/* Mali Pause Resume APIs */ +int mali_dev_pause() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_SUSPEND) || (mali_device_state == _MALI_DEVICE_SUSPEND_IN_PROGRESS) + || (mali_device_state == _MALI_DEVICE_SUSPEND) +#ifdef CONFIG_HAS_EARLYSUSPEND + || (mali_device_state == _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB)) +#else + ) +#endif + { + err = -EPERM; + } + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) && (!err)) + { + mali_device_suspend(MALI_PMM_EVENT_DVFS_PAUSE, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_SUSPEND; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_pause); + +int mali_dev_resume() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) || (mali_device_state == _MALI_DEVICE_SUSPEND_IN_PROGRESS) + || (mali_device_state == _MALI_DEVICE_SUSPEND) +#ifdef CONFIG_HAS_EARLYSUSPEND + || (mali_device_state == _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB)) +#else + ) +#endif + { + err = -EPERM; + } + if (!err) + { + mali_device_resume(MALI_PMM_EVENT_DVFS_RESUME, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_resume); + +#endif /* MALI_LICENSE_IS_GPL */ +#endif /* CONFIG_PM */ +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.h new file mode 100644 index 00000000000..5362f88cdbd --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_device_pause_resume.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_DEVICE_PAUSE_RESUME_H__ +#define __MALI_DEVICE_PAUSE_RESUME_H__ + +#if USING_MALI_PMM +int mali_dev_pause(void); +int mali_dev_resume(void); +#endif /* USING_MALI_PMM */ + +#endif /* __MALI_DEVICE_PAUSE_RESUME_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_ioctl.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_ioctl.h new file mode 100644 index 00000000000..30a6fa041db --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_ioctl.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_IOCTL_H__ +#define __MALI_KERNEL_IOCTL_H__ + +#include +#include +#include /* file system operations */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file mali_kernel_ioctl.h + * Interface to the Linux device driver. + * This file describes the interface needed to use the Linux device driver. + * Its interface is designed to used by the HAL implementation through a thin arch layer. + */ + +/** + * ioctl commands + */ + +#define MALI_IOC_BASE 0x82 +#define MALI_IOC_CORE_BASE (_MALI_UK_CORE_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_MEMORY_BASE (_MALI_UK_MEMORY_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PP_BASE (_MALI_UK_PP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_GP_BASE (_MALI_UK_GP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PROFILING_BASE (_MALI_UK_PROFILING_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_VSYNC_BASE (_MALI_UK_VSYNC_SUBSYSTEM + MALI_IOC_BASE) + +#define MALI_IOC_GET_SYSTEM_INFO_SIZE _IOR (MALI_IOC_CORE_BASE, _MALI_UK_GET_SYSTEM_INFO_SIZE, _mali_uk_get_system_info_s *) +#define MALI_IOC_GET_SYSTEM_INFO _IOR (MALI_IOC_CORE_BASE, _MALI_UK_GET_SYSTEM_INFO, _mali_uk_get_system_info_s *) +#define MALI_IOC_WAIT_FOR_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_WAIT_FOR_NOTIFICATION, _mali_uk_wait_for_notification_s *) +#define MALI_IOC_GET_API_VERSION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_API_VERSION, _mali_uk_get_api_version_s *) +#define MALI_IOC_POST_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_POST_NOTIFICATION, _mali_uk_post_notification_s *) +#define MALI_IOC_MEM_GET_BIG_BLOCK _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_GET_BIG_BLOCK, _mali_uk_get_big_block_s *) +#define MALI_IOC_MEM_FREE_BIG_BLOCK _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_FREE_BIG_BLOCK, _mali_uk_free_big_block_s *) +#define MALI_IOC_MEM_INIT _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_INIT_MEM, _mali_uk_init_mem_s *) +#define MALI_IOC_MEM_TERM _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_TERM_MEM, _mali_uk_term_mem_s *) +#define MALI_IOC_MEM_MAP_EXT _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MAP_EXT_MEM, _mali_uk_map_external_mem_s *) +#define MALI_IOC_MEM_UNMAP_EXT _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_UNMAP_EXT_MEM, _mali_uk_unmap_external_mem_s *) +#define MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, _mali_uk_query_mmu_page_table_dump_size_s *) +#define MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_DUMP_MMU_PAGE_TABLE, _mali_uk_dump_mmu_page_table_s *) +#define MALI_IOC_MEM_ATTACH_UMP _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_UMP_MEM, _mali_uk_attach_ump_mem_s *) +#define MALI_IOC_MEM_RELEASE_UMP _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_UMP_MEM, _mali_uk_release_ump_mem_s *) +#define MALI_IOC_PP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_START_JOB, _mali_uk_pp_start_job_s *) +#define MALI_IOC_PP_NUMBER_OF_CORES_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_NUMBER_OF_CORES, _mali_uk_get_pp_number_of_cores_s *) +#define MALI_IOC_PP_CORE_VERSION_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_CORE_VERSION, _mali_uk_get_pp_core_version_s * ) +#define MALI_IOC_PP_ABORT_JOB _IOW (MALI_IOC_PP_BASE, _MALI_UK_PP_ABORT_JOB, _mali_uk_pp_abort_job_s * ) +#define MALI_IOC_GP2_START_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_START_JOB, _mali_uk_gp_start_job_s *) +#define MALI_IOC_GP2_ABORT_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_ABORT_JOB, _mali_uk_gp_abort_job_s *) +#define MALI_IOC_GP2_NUMBER_OF_CORES_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_NUMBER_OF_CORES, _mali_uk_get_gp_number_of_cores_s *) +#define MALI_IOC_GP2_CORE_VERSION_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_CORE_VERSION, _mali_uk_get_gp_core_version_s *) +#define MALI_IOC_GP2_SUSPEND_RESPONSE _IOW (MALI_IOC_GP_BASE, _MALI_UK_GP_SUSPEND_RESPONSE,_mali_uk_gp_suspend_response_s *) +#define MALI_IOC_PROFILING_START _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_START, _mali_uk_profiling_start_s *) +#define MALI_IOC_PROFILING_ADD_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_ADD_EVENT, _mali_uk_profiling_add_event_s*) +#define MALI_IOC_PROFILING_STOP _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_STOP, _mali_uk_profiling_stop_s *) +#define MALI_IOC_PROFILING_GET_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_EVENT, _mali_uk_profiling_get_event_s *) +#define MALI_IOC_PROFILING_CLEAR _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_CLEAR, _mali_uk_profiling_clear_s *) +#define MALI_IOC_VSYNC_EVENT_REPORT _IOW (MALI_IOC_VSYNC_BASE, _MALI_UK_VSYNC_EVENT_REPORT, _mali_uk_vsync_event_report_s *) + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_IOCTL_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.c new file mode 100644 index 00000000000..a5144faae2b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.c @@ -0,0 +1,565 @@ +/** + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_linux.c + * Implementation of the Linux device driver entrypoints + */ +#include /* kernel module definitions */ +#include /* file system operations */ +#include /* character device definitions */ +#include /* memory mananger definitions */ +#include /* user space access */ +#include +#include + +/* the mali kernel subsystem types */ +#include "mali_kernel_subsystem.h" + +/* A memory subsystem always exists, so no need to conditionally include it */ +#include "mali_kernel_common.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_core.h" + +#include "mali_osk.h" +#include "mali_kernel_linux.h" +#include "mali_ukk.h" +#include "mali_kernel_ioctl.h" +#include "mali_ukk_wrappers.h" +#include "mali_kernel_pm.h" + +/* */ +#include "mali_kernel_license.h" + +/* Setting this parameter will override memory settings in arch/config.h */ +char *mali_mem = ""; +module_param(mali_mem, charp, S_IRUSR | S_IWUSR | S_IWGRP | S_IROTH); /* rw-r--r-- */ +MODULE_PARM_DESC(mali_mem, "Override mali memory configuration. e.g. 32M@224M or 64M@OS_MEMORY"); + +/* Module parameter to control log level */ +int mali_debug_level = 2; +module_param(mali_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(mali_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int mali_major = 0; +module_param(mali_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(mali_major, "Device major number"); + +int mali_benchmark = 0; +module_param(mali_benchmark, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(mali_benchmark, "Bypass Mali hardware when non-zero"); + +extern int mali_hang_check_interval; +module_param(mali_hang_check_interval, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_hang_check_interval, "Interval at which to check for progress after the hw watchdog has been triggered"); + +extern int mali_max_job_runtime; +module_param(mali_max_job_runtime, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_job_runtime, "Maximum allowed job runtime in msecs.\nJobs will be killed after this no matter what"); + +#if defined(USING_MALI400_L2_CACHE) +extern int mali_l2_max_reads; +module_param(mali_l2_max_reads, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_l2_max_reads, "Maximum reads for Mali L2 cache"); +#endif + +struct mali_dev +{ + struct cdev cdev; +#if MALI_LICENSE_IS_GPL + struct class * mali_class; +#endif +}; + +static char mali_dev_name[] = "mali"; /* should be const, but the functions we call requires non-cost */ + +/* the mali device */ +static struct mali_dev device; + +#if MALI_STATE_TRACKING +static struct proc_dir_entry *proc_entry; +static int mali_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data); +#endif + + +static int mali_open(struct inode *inode, struct file *filp); +static int mali_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif + +static int mali_mmap(struct file * filp, struct vm_area_struct * vma); + +/* Linux char file operations provided by the Mali module */ +struct file_operations mali_fops = +{ + .owner = THIS_MODULE, + .open = mali_open, + .release = mali_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = mali_ioctl, +#else + .ioctl = mali_ioctl, +#endif + .mmap = mali_mmap +}; + + +int mali_driver_init(void) +{ + int err; +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + err = _mali_dev_platform_register(); + if (err) + { + return err; + } +#endif +#endif +#endif + err = mali_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) + { +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + _mali_dev_platform_unregister(); +#endif +#endif +#endif + MALI_PRINT(("Failed to initialize driver (error %d)\n", err)); + return -EFAULT; + } + + return 0; +} + +void mali_driver_exit(void) +{ + +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + + _mali_osk_pmm_dev_activate(); +#endif +#endif +#endif +#endif + mali_kernel_destructor(); + +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + _mali_osk_pmm_dev_idle(); +#endif +#endif +#endif +#endif + +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + _mali_dev_platform_unregister(); +#endif +#endif +#endif +} + +/* called from _mali_osk_init */ +int initialize_kernel_device(void) +{ + int err; + dev_t dev = 0; + if (0 == mali_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0/*first minor*/, 1/*count*/, mali_dev_name); + mali_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(mali_major, 0); + err = register_chrdev_region(dev, 1/*count*/, mali_dev_name); + } + + if (0 == err) + { + memset(&device, 0, sizeof(device)); + + /* initialize our char dev data */ + cdev_init(&device.cdev, &mali_fops); + device.cdev.owner = THIS_MODULE; + device.cdev.ops = &mali_fops; + + /* register char dev with the kernel */ + err = cdev_add(&device.cdev, dev, 1/*count*/); + + if (0 == err) + { +#if MALI_STATE_TRACKING + proc_entry = create_proc_entry(mali_dev_name, 0444, NULL); + if (proc_entry != NULL) + { + proc_entry->read_proc = mali_proc_read; +#endif +#if MALI_LICENSE_IS_GPL + device.mali_class = class_create(THIS_MODULE, mali_dev_name); + if (IS_ERR(device.mali_class)) + { + err = PTR_ERR(device.mali_class); + } + else + { + struct device * mdev; + mdev = device_create(device.mali_class, NULL, dev, NULL, mali_dev_name); + if (!IS_ERR(mdev)) + { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&device.cdev); +#else + return 0; +#endif +#if MALI_STATE_TRACKING + remove_proc_entry(mali_dev_name, NULL); + } + else + { + err = EFAULT; + } +#endif + } + unregister_chrdev_region(dev, 1/*count*/); + } + + + return err; +} + +/* called from _mali_osk_term */ +void terminate_kernel_device(void) +{ + dev_t dev = MKDEV(mali_major, 0); + +#if MALI_LICENSE_IS_GPL + device_destroy(device.mali_class, dev); + class_destroy(device.mali_class); +#endif + +#if MALI_STATE_TRACKING + remove_proc_entry(mali_dev_name, NULL); +#endif + + /* unregister char device */ + cdev_del(&device.cdev); + /* free major */ + unregister_chrdev_region(dev, 1/*count*/); + return; +} + +/** @note munmap handler is done by vma close handler */ +static int mali_mmap(struct file * filp, struct vm_area_struct * vma) +{ + struct mali_session_data * session_data; + _mali_uk_mem_mmap_s args = {0, }; + + session_data = (struct mali_session_data *)filp->private_data; + if (NULL == session_data) + { + MALI_PRINT_ERROR(("mmap called without any session data available\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("MMap() handler: start=0x%08X, phys=0x%08X, size=0x%08X\n", (unsigned int)vma->vm_start, (unsigned int)(vma->vm_pgoff << PAGE_SHIFT), (unsigned int)(vma->vm_end - vma->vm_start)) ); + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = vma->vm_pgoff << PAGE_SHIFT; + args.size = vma->vm_end - vma->vm_start; + args.ukk_private = vma; + + /* Call the common mmap handler */ + MALI_CHECK(_MALI_OSK_ERR_OK ==_mali_ukk_mem_mmap( &args ), -EFAULT); + + return 0; +} + +static int mali_open(struct inode *inode, struct file *filp) +{ + struct mali_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) return -ENODEV; + + /* allocated struct to track this session */ + err = _mali_ukk_open((void **)&session_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* initialize file pointer */ + filp->f_pos = 0; + + /* link in our session data */ + filp->private_data = (void*)session_data; + + return 0; +} + +static int mali_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) return -ENODEV; + + err = _mali_ukk_close((void **)&filp->private_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) + { + case _MALI_OSK_ERR_OK : return 0; + case _MALI_OSK_ERR_FAULT: return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: return -EINVAL; + case _MALI_OSK_ERR_NOMEM: return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: return -ENOENT; + default: return -EFAULT; + } +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err; + struct mali_session_data *session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + /* inode not used */ + (void)inode; +#endif + + MALI_DEBUG_PRINT(7, ("Ioctl received 0x%08X 0x%08lX\n", cmd, arg)); + + session_data = (struct mali_session_data *)filp->private_data; + if (NULL == session_data) + { + MALI_DEBUG_PRINT(7, ("filp->private_data was NULL\n")); + return -ENOTTY; + } + if (NULL == (void *)arg) + { + MALI_DEBUG_PRINT(7, ("arg was NULL\n")); + return -ENOTTY; + } + + switch(cmd) + { + case MALI_IOC_GET_SYSTEM_INFO_SIZE: + err = get_system_info_size_wrapper(session_data, (_mali_uk_get_system_info_size_s __user *)arg); + break; + + case MALI_IOC_GET_SYSTEM_INFO: + err = get_system_info_wrapper(session_data, (_mali_uk_get_system_info_s __user *)arg); + break; + + case MALI_IOC_WAIT_FOR_NOTIFICATION: + err = wait_for_notification_wrapper(session_data, (_mali_uk_wait_for_notification_s __user *)arg); + break; + + case MALI_IOC_GET_API_VERSION: + err = get_api_version_wrapper(session_data, (_mali_uk_get_api_version_s __user *)arg); + break; + + case MALI_IOC_POST_NOTIFICATION: + err = post_notification_wrapper(session_data, (_mali_uk_post_notification_s __user *)arg); + break; + +#if MALI_TIMELINE_PROFILING_ENABLED + case MALI_IOC_PROFILING_START: + err = profiling_start_wrapper(session_data, (_mali_uk_profiling_start_s __user *)arg); + break; + + case MALI_IOC_PROFILING_ADD_EVENT: + err = profiling_add_event_wrapper(session_data, (_mali_uk_profiling_add_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_STOP: + err = profiling_stop_wrapper(session_data, (_mali_uk_profiling_stop_s __user *)arg); + break; + + case MALI_IOC_PROFILING_GET_EVENT: + err = profiling_get_event_wrapper(session_data, (_mali_uk_profiling_get_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_CLEAR: + err = profiling_clear_wrapper(session_data, (_mali_uk_profiling_clear_s __user *)arg); + break; +#endif + + case MALI_IOC_MEM_INIT: + err = mem_init_wrapper(session_data, (_mali_uk_init_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_TERM: + err = mem_term_wrapper(session_data, (_mali_uk_term_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_MAP_EXT: + err = mem_map_ext_wrapper(session_data, (_mali_uk_map_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_UNMAP_EXT: + err = mem_unmap_ext_wrapper(session_data, (_mali_uk_unmap_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE: + err = mem_query_mmu_page_table_dump_size_wrapper(session_data, (_mali_uk_query_mmu_page_table_dump_size_s __user *)arg); + break; + + case MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE: + err = mem_dump_mmu_page_table_wrapper(session_data, (_mali_uk_dump_mmu_page_table_s __user *)arg); + break; + + case MALI_IOC_MEM_GET_BIG_BLOCK: + err = mem_get_big_block_wrapper(filp, (_mali_uk_get_big_block_s __user *)arg); + break; + + case MALI_IOC_MEM_FREE_BIG_BLOCK: + err = mem_free_big_block_wrapper(session_data, (_mali_uk_free_big_block_s __user *)arg); + break; + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 + + case MALI_IOC_MEM_ATTACH_UMP: + err = mem_attach_ump_wrapper(session_data, (_mali_uk_attach_ump_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_RELEASE_UMP: + err = mem_release_ump_wrapper(session_data, (_mali_uk_release_ump_mem_s __user *)arg); + break; + +#else + + case MALI_IOC_MEM_ATTACH_UMP: + case MALI_IOC_MEM_RELEASE_UMP: /* FALL-THROUGH */ + MALI_DEBUG_PRINT(2, ("UMP not supported\n")); + err = -ENOTTY; + break; +#endif + + case MALI_IOC_PP_START_JOB: + err = pp_start_job_wrapper(session_data, (_mali_uk_pp_start_job_s __user *)arg); + break; + + case MALI_IOC_PP_ABORT_JOB: + err = pp_abort_job_wrapper(session_data, (_mali_uk_pp_abort_job_s __user *)arg); + break; + + case MALI_IOC_PP_NUMBER_OF_CORES_GET: + err = pp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_pp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_PP_CORE_VERSION_GET: + err = pp_get_core_version_wrapper(session_data, (_mali_uk_get_pp_core_version_s __user *)arg); + break; + + case MALI_IOC_GP2_START_JOB: + err = gp_start_job_wrapper(session_data, (_mali_uk_gp_start_job_s __user *)arg); + break; + + case MALI_IOC_GP2_ABORT_JOB: + err = gp_abort_job_wrapper(session_data, (_mali_uk_gp_abort_job_s __user *)arg); + break; + + case MALI_IOC_GP2_NUMBER_OF_CORES_GET: + err = gp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_gp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_GP2_CORE_VERSION_GET: + err = gp_get_core_version_wrapper(session_data, (_mali_uk_get_gp_core_version_s __user *)arg); + break; + + case MALI_IOC_GP2_SUSPEND_RESPONSE: + err = gp_suspend_response_wrapper(session_data, (_mali_uk_gp_suspend_response_s __user *)arg); + break; + + case MALI_IOC_VSYNC_EVENT_REPORT: + err = vsync_event_report_wrapper(session_data, (_mali_uk_vsync_event_report_s __user *)arg); + break; + + default: + MALI_DEBUG_PRINT(2, ("No handler for ioctl 0x%08X 0x%08lX\n", cmd, arg)); + err = -ENOTTY; + }; + + return err; +} + +#if MALI_STATE_TRACKING +static int mali_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + MALI_DEBUG_PRINT(1, ("mali_proc_read(page=%p, start=%p, off=%u, count=%d, eof=%p, data=%p\n", page, start, off, count, eof, data)); + + if (off > 0) + { + return 0; + } + + if (count < 1024) + { + return 0; + } + + len = sprintf(page + len, "Mali device driver %s\n", SVN_REV_STRING); + len += sprintf(page + len, "License: %s\n", MALI_KERNEL_LINUX_LICENSE); + + /* + * A more elegant solution would be to gather information from all subsystems and + * then report it all in the /proc/mali file, but this would require a bit more work. + * Use MALI_PRINT for now so we get the information in the dmesg log at least. + */ + _mali_kernel_core_dump_state(); + + return len; +} +#endif + + +module_init(mali_driver_init); +module_exit(mali_driver_exit); + +MODULE_LICENSE(MALI_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.h new file mode 100644 index 00000000000..969449d8ef4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_linux.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_LINUX_H__ +#define __MALI_KERNEL_LINUX_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +_mali_osk_errcode_t initialize_kernel_device(void); +void terminate_kernel_device(void); + +void mali_osk_low_level_mem_init(void); +void mali_osk_low_level_mem_term(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LINUX_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c new file mode 100644 index 00000000000..d868582c75a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c @@ -0,0 +1,785 @@ +/** + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_pm.c + * Implementation of the Linux Power Management for Mali GPU kernel driver + */ +#undef CONFIG_HAS_EARLYSUSPEND /*ARM will remove .early_suspend support for r2p2_rel*/ +#if USING_MALI_PMM +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef CONFIG_PM_RUNTIME +#include +#endif /* CONFIG_PM_RUNTIME */ + +#include +#include +#include +#include +#include + +#include "mali_platform.h" +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_pmm.h" +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_kernel_pm.h" +#include "mali_device_pause_resume.h" +#include "mali_linux_pm.h" + +#if MALI_GPU_UTILIZATION +#include "mali_kernel_utilization.h" +#endif /* MALI_GPU_UTILIZATION */ + +#if MALI_POWER_MGMT_TEST_SUITE +#ifdef CONFIG_PM +#include "mali_linux_pm_testsuite.h" +unsigned int pwr_mgmt_status_reg = 0; +#endif /* CONFIG_PM */ +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +#if MALI_STATE_TRACKING + int is_os_pmm_thread_waiting = -1; +#endif /* MALI_STATE_TRACKING */ + +/* kernel should be configured with power management support */ +#ifdef CONFIG_PM + +/* License should be GPL */ +#if MALI_LICENSE_IS_GPL + +/* Linux kernel major version */ +#define LINUX_KERNEL_MAJOR_VERSION 2 + +/* Linux kernel minor version */ +#define LINUX_KERNEL_MINOR_VERSION 6 + +/* Linux kernel development version */ +#define LINUX_KERNEL_DEVELOPMENT_VERSION 29 + +#ifdef CONFIG_PM_DEBUG +static const char* const mali_states[_MALI_MAX_DEBUG_OPERATIONS] = { + [_MALI_DEVICE_SUSPEND] = "suspend", + [_MALI_DEVICE_RESUME] = "resume", +#ifdef CONFIG_HAS_EARLYSUSPEND + [_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB] = "early_suspend_level_disable_framebuffer", + [_MALI_DEVICE_LATERESUME] = "late_resume", +#endif /* CONFIG_HAS_EARLYSUSPEND */ + [_MALI_DVFS_PAUSE_EVENT] = "dvfs_pause", + [_MALI_DVFS_RESUME_EVENT] = "dvfs_resume", +}; + +#endif /* CONFIG_PM_DEBUG */ + +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +extern void set_mali_parent_power_domain(struct platform_device* dev); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifndef CONFIG_HAS_EARLYSUSPEND +static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy); + +static struct notifier_block mali_pwr_notif_block = { + .notifier_call = mali_pwr_suspend_notifier +}; +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/* Power management thread pointer */ +struct task_struct *pm_thread; + +/* dvfs power management thread */ +struct task_struct *dvfs_pm_thread; + +/* is wake up needed */ +short is_wake_up_needed = 0; +int timeout_fired = 2; +unsigned int is_mali_pmm_testsuite_enabled = 0; + +_mali_device_power_states mali_device_state = _MALI_DEVICE_RESUME; +_mali_device_power_states mali_dvfs_device_state = _MALI_DEVICE_RESUME; +_mali_osk_lock_t *lock; + +#if MALI_POWER_MGMT_TEST_SUITE + +const char* const mali_pmm_recording_events[_MALI_DEVICE_MAX_PMM_EVENTS] = { + [_MALI_DEVICE_PMM_TIMEOUT_EVENT] = "timeout", + [_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS] = "job_scheduling", + [_MALI_DEVICE_PMM_REGISTERED_CORES] = "cores", + +}; + +unsigned int mali_timeout_event_recording_on = 0; +unsigned int mali_job_scheduling_events_recording_on = 0; +unsigned int is_mali_pmu_present = 0; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/* Function prototypes */ +static int mali_pm_probe(struct platform_device *pdev); +static int mali_pm_remove(struct platform_device *pdev); + +/* Mali device suspend function */ +static int mali_pm_suspend(struct device *dev); + +/* Mali device resume function */ +static int mali_pm_resume(struct device *dev); + +/* Run time suspend and resume functions */ +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +static int mali_device_runtime_suspend(struct device *dev); +static int mali_device_runtime_resume(struct device *dev); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/* Early suspend functions */ +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mali_pm_early_suspend(struct early_suspend *mali_dev); +static void mali_pm_late_resume(struct early_suspend *mali_dev); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* OS suspend and resume callbacks */ +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifndef CONFIG_PM_RUNTIME +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state); +#else +static int mali_pm_os_suspend(struct device *dev); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_resume(struct platform_device *pdev); +#else +static int mali_pm_os_resume(struct device *dev); +#endif +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + +/* OS Hibernation suspend callback */ +static int mali_pm_os_suspend_on_hibernation(struct device *dev); + +/* OS Hibernation resume callback */ +static int mali_pm_os_resume_on_hibernation(struct device *dev); + +static void _mali_release_pm(struct device* device); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static const struct dev_pm_ops mali_dev_pm_ops = { + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + .runtime_suspend = mali_device_runtime_suspend, + .runtime_resume = mali_device_runtime_resume, +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON + .suspend = mali_pm_os_suspend, + .resume = mali_pm_os_resume, +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + .freeze = mali_pm_os_suspend_on_hibernation, + .poweroff = mali_pm_os_suspend_on_hibernation, + .thaw = mali_pm_os_resume_on_hibernation, + .restore = mali_pm_os_resume_on_hibernation, +}; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +struct pm_ext_ops mali_pm_operations = { + .base = { + .freeze = mali_pm_os_suspend_on_hibernation, + .thaw = mali_pm_os_resume_on_hibernation, + .poweroff = mali_pm_os_resume_on_hibernation, + .restore = mali_pm_os_resume_on_hibernation, + }, +}; +#endif + +static struct platform_driver mali_plat_driver = { + .probe = mali_pm_probe, + .remove = mali_pm_remove, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON + .suspend = mali_pm_os_suspend, + .resume = mali_pm_os_resume, +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + .pm = &mali_pm_operations, +#endif + + .driver = { + .name = "mali_dev", + .owner = THIS_MODULE, + .bus = &platform_bus_type, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) + .pm = &mali_dev_pm_ops, +#endif + }, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +/* Early suspend hooks */ +static struct early_suspend mali_dev_early_suspend = { + .suspend = mali_pm_early_suspend, + .resume = mali_pm_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB, +}; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* Mali GPU platform device */ +struct platform_device mali_gpu_device = { + .name = "mali_dev", + .id = 0, + .dev.release = _mali_release_pm +}; + +/** This function is called when platform device is unregistered. This function + * is necessary when the platform device is unregistered. + */ +static void _mali_release_pm(struct device *device) +{ + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Platform device removed\n" )); +} + +#if MALI_POWER_MGMT_TEST_SUITE +void mali_is_pmu_present(void) +{ + int temp = 0; + temp = pmu_get_power_up_down_info(); + if (4095 == temp) + { + is_mali_pmu_present = 0; + } + else + { + is_mali_pmu_present = 1; + } +} +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* MALI_LICENSE_IS_GPL */ + +#if MALI_LICENSE_IS_GPL + +static int mali_wait_for_power_management_policy_event(void) +{ + int err = 0; + for (; ;) + { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) + { + err = -EINTR; + break; + } + if (is_wake_up_needed == 1) + { + break; + } + schedule(); + } + __set_current_state(TASK_RUNNING); + is_wake_up_needed =0; + return err; +} + +/** This function is invoked when mali device is suspended + */ +int mali_device_suspend(unsigned int event_id, struct task_struct **pwr_mgmt_thread) +{ + int err = 0; + _mali_uk_pmm_message_s event = { + NULL, + event_id, + timeout_fired}; + *pwr_mgmt_thread = current; + MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being suspended\n" )); + _mali_ukk_pmm_event_message(&event); +#if MALI_STATE_TRACKING + is_os_pmm_thread_waiting = 1; +#endif /* MALI_STATE_TRACKING */ + err = mali_wait_for_power_management_policy_event(); +#if MALI_STATE_TRACKING + is_os_pmm_thread_waiting = 0; +#endif /* MALI_STATE_TRACKING */ + return err; +} + +/** This function is called when Operating system wants to power down + * the mali GPU device. + */ +static int mali_pm_suspend(struct device *dev) +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); +#if MALI_GPU_UTILIZATION + mali_utilization_suspend(); +#endif /* MALI_GPU_UTILIZATION */ + if ((mali_device_state == _MALI_DEVICE_SUSPEND) +#ifdef CONFIG_HAS_EARLYSUSPEND + || mali_device_state == (_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB)) +#else + ) +#endif /* CONFIG_HAS_EARLYSUSPEND */ + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + mali_device_state = _MALI_DEVICE_SUSPEND_IN_PROGRESS; + err = mali_device_suspend(MALI_PMM_EVENT_OS_POWER_DOWN, &pm_thread); + mali_device_state = _MALI_DEVICE_SUSPEND; + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state) +#else +static int mali_pm_os_suspend(struct device *dev) +#endif +{ + int err = 0; + err = mali_pm_suspend(NULL); + return err; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifndef CONFIG_HAS_EARLYSUSPEND +static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy) +{ + int err = 0; + switch (event) + { + case PM_SUSPEND_PREPARE: + err = mali_pm_suspend(NULL); + break; + + case PM_POST_SUSPEND: + err = mali_pm_resume(NULL); + break; + default: + break; + } + return 0; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/** This function is called when mali GPU device is to be resumed. + */ +int mali_device_resume(unsigned int event_id, struct task_struct **pwr_mgmt_thread) +{ + int err = 0; + _mali_uk_pmm_message_s event = { + NULL, + event_id, + timeout_fired}; + *pwr_mgmt_thread = current; + MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being resumed\n" )); + _mali_ukk_pmm_event_message(&event); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Power up event is scheduled\n" )); + +#if MALI_STATE_TRACKING + is_os_pmm_thread_waiting = 1; +#endif /* MALI_STATE_TRACKING */ + + err = mali_wait_for_power_management_policy_event(); + +#if MALI_STATE_TRACKING + is_os_pmm_thread_waiting = 0; +#endif /* MALI_STATE_TRACKING */ + + return err; +} + +/** This function is called when mali GPU device is to be resumed + */ + +static int mali_pm_resume(struct device *dev) +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if (mali_device_state == _MALI_DEVICE_RESUME) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + err = mali_device_resume(MALI_PMM_EVENT_OS_POWER_UP, &pm_thread); + mali_device_state = _MALI_DEVICE_RESUME; + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_resume(struct platform_device *pdev) +#else +static int mali_pm_os_resume(struct device *dev) +#endif +{ + int err = 0; + err = mali_pm_resume(NULL); + return err; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +static int mali_pm_os_suspend_on_hibernation(struct device *dev) +{ + int err = 0; + err = mali_pm_suspend(NULL); + return err; +} + +static int mali_pm_os_resume_on_hibernation(struct device *dev) +{ + int err = 0; + err = mali_pm_resume(NULL); + return err; +} + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +/** This function is called when runtime suspend of mali device is required. + */ +static int mali_device_runtime_suspend(struct device *dev) +{ + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time suspended \n" )); + return 0; +} + +/** This function is called when runtime resume of mali device is required. + */ +static int mali_device_runtime_resume(struct device *dev) +{ + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time Resumed \n" )); + return 0; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_HAS_EARLYSUSPEND + +/* This function is called from android framework. + */ +static void mali_pm_early_suspend(struct early_suspend *mali_dev) +{ + switch(mali_dev->level) + { + /* Screen should be turned off but framebuffer will be accessible */ + case EARLY_SUSPEND_LEVEL_BLANK_SCREEN: + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Screen is off\n" )); + break; + + case EARLY_SUSPEND_LEVEL_STOP_DRAWING: + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Suspend level stop drawing\n" )); + break; + + /* Turn off the framebuffer. In our case No Mali GPU operation */ + case EARLY_SUSPEND_LEVEL_DISABLE_FB: + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Suspend level Disable framebuffer\n" )); + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); +#if MALI_GPU_UTILIZATION + mali_utilization_suspend(); +#endif /* MALI_GPU_UTILIZATION */ + if ((mali_device_state == _MALI_DEVICE_SUSPEND) || (mali_device_state == _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB)) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return; + } + mali_device_suspend(MALI_PMM_EVENT_OS_POWER_DOWN, &pm_thread); + mali_device_state = _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB; + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + break; + + default: + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Invalid Suspend Mode\n" )); + break; + } +} + +/* This function is invoked from android framework when mali device needs to be + * resumed. + */ +static void mali_pm_late_resume(struct early_suspend *mali_dev) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if (mali_device_state == _MALI_DEVICE_RESUME) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return; + } + if (mali_device_state == _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB) + { + mali_device_resume(MALI_PMM_EVENT_OS_POWER_UP, &pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + mali_device_state = _MALI_DEVICE_RESUME; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + +} + +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef CONFIG_PM_DEBUG + +/** This function is used for debugging purposes when the user want to see + * which power management operations are supported for + * mali device. + */ +static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *str = buf; +#if !MALI_POWER_MGMT_TEST_SUITE + int pm_counter = 0; + for (pm_counter = 0; pm_counter<_MALI_MAX_DEBUG_OPERATIONS; pm_counter++) + { + str += sprintf(str, "%s ", mali_states[pm_counter]); + } +#else + str += sprintf(str, "%d ",pwr_mgmt_status_reg); +#endif + if (str != buf) + { + *(str-1) = '\n'; + } + return (str-buf); +} + +/** This function is called when user wants to suspend the mali GPU device in order + * to simulate the power up and power down events. + */ +static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int err = 0; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend mali_dev; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#if MALI_POWER_MGMT_TEST_SUITE + int test_flag_dvfs = 0; + pwr_mgmt_status_reg = 0; + mali_is_pmu_present(); + +#endif + if (!strncmp(buf,mali_states[_MALI_DEVICE_SUSPEND],strlen(mali_states[_MALI_DEVICE_SUSPEND]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI suspend Power operation is scheduled\n" )); + err = mali_pm_suspend(NULL); + } + +#if MALI_POWER_MGMT_TEST_SUITE + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Device get number of registerd cores\n" )); + pwr_mgmt_status_reg = _mali_pmm_cores_list(); + return count; + } + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI timeout event recording is enabled\n" )); + mali_timeout_event_recording_on = 1; + } + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Job scheduling events recording is enabled\n" )); + mali_job_scheduling_events_recording_on = 1; + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + + else if (!strncmp(buf,mali_states[_MALI_DEVICE_RESUME],strlen(mali_states[_MALI_DEVICE_RESUME]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Resume Power operation is scheduled\n" )); + err = mali_pm_resume(NULL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + else if (!strncmp(buf,mali_states[_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB],strlen(mali_states[_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB]))) + { + mali_dev.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Android early suspend operation is scheduled\n" )); + mali_pm_early_suspend(&mali_dev); + } + else if (!strncmp(buf,mali_states[_MALI_DEVICE_LATERESUME],strlen(mali_states[_MALI_DEVICE_LATERESUME]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Resume Power operation is scheduled\n" )); + mali_pm_late_resume(NULL); + } +#endif /* CONFIG_HAS_EARLYSUSPEND */ + else if (!strncmp(buf,mali_states[_MALI_DVFS_PAUSE_EVENT],strlen(mali_states[_MALI_DVFS_PAUSE_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Pause Power operation is scheduled\n" )); + err = mali_dev_pause(); +#if MALI_POWER_MGMT_TEST_SUITE + test_flag_dvfs = 1; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + } + else if (!strncmp(buf,mali_states[_MALI_DVFS_RESUME_EVENT],strlen(mali_states[_MALI_DVFS_RESUME_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Resume Power operation is scheduled\n" )); + err = mali_dev_resume(); +#if MALI_POWER_MGMT_TEST_SUITE + test_flag_dvfs = 1; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + } + else + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Invalid Power Mode Operation selected\n" )); + } +#if MALI_POWER_MGMT_TEST_SUITE + if (test_flag_dvfs == 1) + { + if (err) + { + pwr_mgmt_status_reg = 2; + } + else + { + pwr_mgmt_status_reg = 1; + } + } + else + { + if (1 == is_mali_pmu_present) + { + pwr_mgmt_status_reg = pmu_get_power_up_down_info(); + } + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + return count; +} + +/* Device attribute file */ +static DEVICE_ATTR(file, 0644, show_file, store_file); +#endif /* CONFIG_PM_DEBUG */ + +static int mali_pm_remove(struct platform_device *pdev) +{ +#ifdef CONFIG_PM_DEBUG + device_remove_file(&mali_gpu_device.dev, &dev_attr_file); +#endif /* CONFIG_PM_DEBUG */ +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + pm_runtime_disable(&pdev->dev); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + return 0; +} + +/** This function is called when the device is probed */ +static int mali_pm_probe(struct platform_device *pdev) +{ +#ifdef CONFIG_PM_DEBUG + int err; + err = device_create_file(&mali_gpu_device.dev, &dev_attr_file); + if (err) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Error in creating device file\n" )); + } +#endif /* CONFIG_PM_DEBUG */ + return 0; +} + +/** This function is called when Mali GPU device is initialized + */ +int _mali_dev_platform_register(void) +{ + int err; +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + set_mali_parent_power_domain(&mali_gpu_device); +#endif + +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_HAS_EARLYSUSPEND +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + err = register_pm_notifier(&mali_pwr_notif_block); + if (err) + { + return err; + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ + err = platform_device_register(&mali_gpu_device); + lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)( _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 0); + if (!err) + { + err = platform_driver_register(&mali_plat_driver); + if (!err) + { +#ifdef CONFIG_HAS_EARLYSUSPEND + register_early_suspend(&mali_dev_early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + } + else + { + _mali_osk_lock_term(lock); +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_HAS_EARLYSUSPEND +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + unregister_pm_notifier(&mali_pwr_notif_block); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ + platform_device_unregister(&mali_gpu_device); + } + } + return err; +} + +/** This function is called when Mali GPU device is unloaded + */ +void _mali_dev_platform_unregister(void) +{ + _mali_osk_lock_term(lock); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&mali_dev_early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_HAS_EARLYSUSPEND +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + unregister_pm_notifier(&mali_pwr_notif_block); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ + + platform_driver_unregister(&mali_plat_driver); + platform_device_unregister(&mali_gpu_device); +} + +#endif /* MALI_LICENSE_IS_GPL */ +#endif /* CONFIG_PM */ + +#if MALI_STATE_TRACKING +void mali_pmm_dump_os_thread_state( void ) +{ + MALI_PRINTF(("\nOSPMM::The OS PMM thread state is...%d",is_os_pmm_thread_waiting)); +} +#endif /* MALI_STATE_TRACKING */ +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.h new file mode 100644 index 00000000000..6e879fea447 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PM_H__ +#define __MALI_KERNEL_PM_H__ + +#ifdef USING_MALI_PMM +int _mali_dev_platform_register(void); +void _mali_dev_platform_unregister(void); +#endif /* USING_MALI_PMM */ + +#endif /* __MALI_KERNEL_PM_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_dvfs_pause_resume.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_dvfs_pause_resume.c new file mode 100644 index 00000000000..3d60d18e005 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_dvfs_pause_resume.c @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_linux_dvfs_pause_resume.c + * Implementation of the Mali pause/resume functionality + */ +#if USING_MALI_PMM +#include +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" +#include "mali_linux_pm.h" +#include "mali_linux_dvfs_pause_resume.h" +#include "mali_pmm.h" +#include "mali_kernel_license.h" +#ifdef CONFIG_PM +#if MALI_LICENSE_IS_GPL + +/* Mali Pause Resume APIs */ +int mali_dev_dvfs_pause() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_SUSPEND) || (mali_device_state == _MALI_DEVICE_SUSPEND_IN_PROGRESS) + || (mali_device_state == _MALI_DEVICE_SUSPEND)) + { + err = -EPERM; + } + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) && (!err)) + { + mali_device_suspend(MALI_PMM_EVENT_DVFS_PAUSE, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_SUSPEND; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_dvfs_pause); + +int mali_dev_dvfs_resume() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) || (mali_device_state == _MALI_DEVICE_SUSPEND_IN_PROGRESS) + || (mali_device_state == _MALI_DEVICE_SUSPEND)) + { + err = -EPERM; + } + if (!err) + { + mali_device_resume(MALI_PMM_EVENT_DVFS_RESUME, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_dvfs_resume); + +#endif /* MALI_LICENSE_IS_GPL */ +#endif /* CONFIG_PM */ +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm.h new file mode 100644 index 00000000000..614d5e1deee --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm.h @@ -0,0 +1,57 @@ + +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_LINUX_PM_H__ +#define __MALI_LINUX_PM_H__ + +#if USING_MALI_PMM + +#ifdef CONFIG_PM +/* Number of power states supported for making power up and down */ +typedef enum +{ + _MALI_DEVICE_SUSPEND, /* Suspend */ + _MALI_DEVICE_RESUME, /* Resume */ +#ifdef CONFIG_HAS_EARLYSUSPEND + _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB, /* Early suspend */ + _MALI_DEVICE_LATERESUME, /* Late resume */ +#endif /* CONFIG_HAS_EARLYSUSPEND */ + _MALI_DEVICE_SUSPEND_IN_PROGRESS, /* Suspend in progress */ + _MALI_DEVICE_MAX_POWER_STATES, /* Maximum power states */ +} _mali_device_power_states; + +/* Number of DVFS events */ +typedef enum +{ + _MALI_DVFS_PAUSE_EVENT = _MALI_DEVICE_MAX_POWER_STATES-1, /* DVFS Pause event */ + _MALI_DVFS_RESUME_EVENT, /* DVFS Resume event */ + _MALI_MAX_DEBUG_OPERATIONS, +} _mali_device_dvfs_events; + +extern _mali_device_power_states mali_device_state; +extern _mali_device_power_states mali_dvfs_device_state; +extern _mali_osk_lock_t *lock; +extern short is_wake_up_needed; +extern int timeout_fired; +extern struct platform_device mali_gpu_device; + +/* dvfs pm thread */ +extern struct task_struct *dvfs_pm_thread; + +/* Power management thread */ +extern struct task_struct *pm_thread; + +int mali_device_suspend(u32 event_id, struct task_struct **pwr_mgmt_thread); +int mali_device_resume(u32 event_id, struct task_struct **pwr_mgmt_thread); + +#endif /* CONFIG_PM */ +#endif /* USING_MALI_PMM */ +#endif /* __MALI_LINUX_PM_H___ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm_testsuite.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm_testsuite.h new file mode 100644 index 00000000000..c80b0b0a5e3 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_linux_pm_testsuite.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef __MALI_LINUX_PM_TESTSUITE_H__ +#define __MALI_LINUX_PM_TESTSUITE_H__ + +#if USING_MALI_PMM +#if MALI_POWER_MGMT_TEST_SUITE +#ifdef CONFIG_PM + +typedef enum +{ + _MALI_DEVICE_PMM_TIMEOUT_EVENT, + _MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS, + _MALI_DEVICE_PMM_REGISTERED_CORES, + _MALI_DEVICE_MAX_PMM_EVENTS + +} _mali_device_pmm_recording_events; + +extern unsigned int mali_timeout_event_recording_on; +extern unsigned int mali_job_scheduling_events_recording_on; +extern unsigned int pwr_mgmt_status_reg; +extern unsigned int is_mali_pmm_testsuite_enabled; +extern unsigned int is_mali_pmu_present; + +#endif /* CONFIG_PM */ +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* USING_MALI_PMM */ +#endif /* __MALI_LINUX_PM_TESTSUITE_H__ */ + + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_atomics.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_atomics.c new file mode 100644 index 00000000000..cc029c4a8f8 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_atomics.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_atomics.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include +#include "mali_kernel_common.h" + +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ) +{ + atomic_dec((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ) +{ + atomic_inc((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} + +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ) +{ + MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS); + atomic_set((atomic_t *)&atom->u.val, val); + return _MALI_OSK_ERR_OK; +} + +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ) +{ + return atomic_read((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ) +{ + MALI_IGNORE(atom); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.c new file mode 100644 index 00000000000..b60bf825897 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mali_osk.h" +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +/** + * @file mali_osk_specific.c + * Implementation of per-OS Kernel level specifics + */ + +_mali_osk_errcode_t _mali_osk_specific_indirect_mmap( _mali_uk_mem_mmap_s *args ) +{ + /* args->ctx ignored here; args->ukk_private required instead */ + /* we need to lock the mmap semaphore before calling the do_mmap function */ + down_write(¤t->mm->mmap_sem); + + args->mapping = (void __user *)do_mmap( + (struct file *)args->ukk_private, + 0, /* start mapping from any address after NULL */ + args->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + args->phys_addr + ); + + /* and unlock it after the call */ + up_write(¤t->mm->mmap_sem); + + /* No cookie required here */ + args->cookie = 0; + /* uku_private meaningless, so zero */ + args->uku_private = NULL; + + if ( (NULL == args->mapping) || IS_ERR((void *)args->mapping) ) + { + return _MALI_OSK_ERR_FAULT; + } + + /* Success */ + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _mali_osk_specific_indirect_munmap( _mali_uk_mem_munmap_s *args ) +{ + /* args->ctx and args->cookie ignored here */ + + if ((NULL != current) && (NULL != current->mm)) + { + /* remove mapping of mali memory from the process' view */ + /* lock mmap semaphore before call */ + /* lock mmap_sem before calling do_munmap */ + down_write(¤t->mm->mmap_sem); + do_munmap( + current->mm, + (unsigned long)args->mapping, + args->size + ); + /* and unlock after call */ + up_write(¤t->mm->mmap_sem); + MALI_DEBUG_PRINT(5, ("unmapped\n")); + } + else + { + MALI_DEBUG_PRINT(2, ("Freeing of a big block while no user process attached, assuming crash cleanup in progress\n")); + } + + return _MALI_OSK_ERR_OK; /* always succeeds */ +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.h new file mode 100644 index 00000000000..f87739bb8f9 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_indir_mmap.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_specific.h + * Defines per-OS Kernel level specifics, such as unusual workarounds for + * certain OSs. + */ + +#ifndef __MALI_OSK_INDIR_MMAP_H__ +#define __MALI_OSK_INDIR_MMAP_H__ + +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Linux specific means for calling _mali_ukk_mem_mmap/munmap + * + * The presence of _MALI_OSK_SPECIFIC_INDIRECT_MMAP indicates that + * _mali_osk_specific_indirect_mmap and _mali_osk_specific_indirect_munmap + * should be used instead of _mali_ukk_mem_mmap/_mali_ukk_mem_munmap. + * + * The arguments are the same as _mali_ukk_mem_mmap/_mali_ukk_mem_munmap. + * + * In ALL operating system other than Linux, it is expected that common code + * should be able to call _mali_ukk_mem_mmap/_mali_ukk_mem_munmap directly. + * Such systems should NOT define _MALI_OSK_SPECIFIC_INDIRECT_MMAP. + */ +_mali_osk_errcode_t _mali_osk_specific_indirect_mmap( _mali_uk_mem_mmap_s *args ); +_mali_osk_errcode_t _mali_osk_specific_indirect_munmap( _mali_uk_mem_munmap_s *args ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_INDIR_MMAP_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_irq.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_irq.c new file mode 100644 index 00000000000..1a4bbcecdce --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_irq.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_irq.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include /* For memory allocation */ +#include + +#include "mali_osk.h" +#include "mali_kernel_core.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "linux/interrupt.h" + +typedef struct _mali_osk_irq_t_struct +{ + u32 irqnum; + void *data; + _mali_osk_irq_uhandler_t uhandler; + _mali_osk_irq_bhandler_t bhandler; + struct work_struct work_queue_irq_handle; /* Workqueue for the bottom half of the IRQ-handling. This job is activated when this core gets an IRQ.*/ +} mali_osk_irq_object_t; + +#if MALI_LICENSE_IS_GPL +static struct workqueue_struct *pmm_wq=NULL; +#endif + +typedef void (*workqueue_func_t)(void *); +typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *); +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ); /* , struct pt_regs *regs*/ + +#if defined(INIT_DELAYED_WORK) +static void irq_handler_bottom_half ( struct work_struct *work ); +#else +static void irq_handler_bottom_half ( void * input ); +#endif + +/** + * Linux kernel version has marked SA_SHIRQ as deprecated, IRQF_SHARED should be used. + * This is to handle older kernels which haven't done this swap. + */ +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif /* IRQF_SHARED */ + +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, _mali_osk_irq_bhandler_t bhandler, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *data, const char *description ) +{ + mali_osk_irq_object_t *irq_object; + + irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL); + if (NULL == irq_object) return NULL; + + /* workqueue API changed in 2.6.20, support both versions: */ +#if defined(INIT_DELAYED_WORK) + /* New syntax: INIT_WORK( struct work_struct *work, void (*function)(struct work_struct *)) */ + INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half); +#else + /* Old syntax: INIT_WORK( struct work_struct *work, void (*function)(void *), void *data) */ + INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half, irq_object); +#endif /* defined(INIT_DELAYED_WORK) */ + + if (-1 == irqnum) + { + /* Probe for IRQ */ + if ( (NULL != trigger_func) && (NULL != ack_func) ) + { + unsigned long probe_count = 3; + _mali_osk_errcode_t err; + int irq; + + MALI_DEBUG_PRINT(2, ("Probing for irq\n")); + + do + { + unsigned long mask; + + mask = probe_irq_on(); + trigger_func(data); + + _mali_osk_time_ubusydelay(5); + + irq = probe_irq_off(mask); + err = ack_func(data); + } + while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--); + + if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1; + else irqnum = irq; + } + else irqnum = -1; /* no probe functions, fault */ + + if (-1 != irqnum) + { + /* found an irq */ + MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum)); + } + else + { + MALI_DEBUG_PRINT(2, ("Probe for irq failed\n")); + } + } + + irq_object->irqnum = irqnum; + irq_object->uhandler = uhandler; + irq_object->bhandler = bhandler; + irq_object->data = data; + + /* Is this a real IRQ handler we need? */ + if (!mali_benchmark && irqnum != _MALI_OSK_IRQ_NUMBER_FAKE && irqnum != _MALI_OSK_IRQ_NUMBER_PMM) + { + if (-1 == irqnum) + { + MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description)); + kfree(irq_object); + return NULL; + } + + if (0 != request_irq(irqnum, irq_handler_upper_half, IRQF_SHARED, description, irq_object)) + { + MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description)); + kfree(irq_object); + return NULL; + } + } + +#if MALI_LICENSE_IS_GPL + if ( _MALI_OSK_IRQ_NUMBER_PMM == irqnum ) + { + pmm_wq = create_singlethread_workqueue("mali-pmm-wq"); + } +#endif + + return irq_object; +} + +void _mali_osk_irq_schedulework( _mali_osk_irq_t *irq ) +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; +#if MALI_LICENSE_IS_GPL + if ( irq_object->irqnum == _MALI_OSK_IRQ_NUMBER_PMM ) + { + queue_work(pmm_wq,&irq_object->work_queue_irq_handle); + } + else + { +#endif + schedule_work(&irq_object->work_queue_irq_handle); +#if MALI_LICENSE_IS_GPL + } +#endif +} + +void _mali_osk_irq_term( _mali_osk_irq_t *irq ) +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; + +#if MALI_LICENSE_IS_GPL + if(irq_object->irqnum == _MALI_OSK_IRQ_NUMBER_PMM ) + { + flush_workqueue(pmm_wq); + destroy_workqueue(pmm_wq); + } +#endif + if (!mali_benchmark) + { + free_irq(irq_object->irqnum, irq_object); + } + kfree(irq_object); + flush_scheduled_work(); +} + + +/** This function is called directly in interrupt context from the OS just after + * the CPU get the hw-irq from mali, or other devices on the same IRQ-channel. + * It is registered one of these function for each mali core. When an interrupt + * arrives this function will be called equal times as registered mali cores. + * That means that we only check one mali core in one function call, and the + * core we check for each turn is given by the \a dev_id variable. + * If we detect an pending interrupt on the given core, we mask the interrupt + * out by settging the core's IRQ_MASK register to zero. + * Then we schedule the mali_core_irq_handler_bottom_half to run as high priority + * work queue job. + */ +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ) /* , struct pt_regs *regs*/ +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id; + + if (irq_object->uhandler(irq_object->data) == _MALI_OSK_ERR_OK) + { + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* Is executed when an interrupt occur on one core */ +/* workqueue API changed in 2.6.20, support both versions: */ +#if defined(INIT_DELAYED_WORK) +static void irq_handler_bottom_half ( struct work_struct *work ) +#else +static void irq_handler_bottom_half ( void * input ) +#endif +{ + mali_osk_irq_object_t *irq_object; + +#if defined(INIT_DELAYED_WORK) + irq_object = _MALI_OSK_CONTAINER_OF(work, mali_osk_irq_object_t, work_queue_irq_handle); +#else + if ( NULL == input ) + { + MALI_PRINT_ERROR(("IRQ: Null pointer! Illegal!")); + return; /* Error */ + } + irq_object = (mali_osk_irq_object_t *) input; +#endif + + irq_object->bhandler(irq_object->data); +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_locks.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_locks.c new file mode 100644 index 00000000000..11285efc682 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_locks.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_locks.c + * Implemenation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include + +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/* These are all the locks we implement: */ +typedef enum +{ + _MALI_OSK_INTERNAL_LOCKTYPE_SPIN, /* Mutex, implicitly non-interruptable, use spin_lock/spin_unlock */ + _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ, /* Mutex, IRQ version of spinlock, use spin_lock_irqsave/spin_unlock_irqrestore */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX, /* Interruptable, use up()/down_interruptable() */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT, /* Non-Interruptable, use up()/down() */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW, /* Non-interruptable, Reader/Writer, use {up,down}{read,write}() */ + + /* Linux supports, but we do not support: + * Non-Interruptable Reader/Writer spinlock mutexes - RW optimization will be switched off + */ + + /* Linux does not support: + * One-locks, of any sort - no optimization for this fact will be made. + */ + +} _mali_osk_internal_locktype; + +struct _mali_osk_lock_t_struct +{ + _mali_osk_internal_locktype type; + unsigned long flags; + union + { + spinlock_t spinlock; + struct semaphore sema; + struct rw_semaphore rw_sema; + } obj; + MALI_DEBUG_CODE( + /** original flags for debug checking */ + _mali_osk_lock_flags_t orig_flags; + _mali_osk_lock_mode_t locked_as; + ); /* MALI_DEBUG_CODE */ +}; + +_mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order ) +{ + _mali_osk_lock_t *lock = NULL; + + /* Validate parameters: */ + /* Flags acceptable */ + MALI_DEBUG_ASSERT( 0 == ( flags & ~(_MALI_OSK_LOCKFLAG_SPINLOCK + | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ + | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE + | _MALI_OSK_LOCKFLAG_READERWRITER + | _MALI_OSK_LOCKFLAG_ORDERED + | _MALI_OSK_LOCKFLAG_ONELOCK )) ); + /* Spinlocks are always non-interruptable */ + MALI_DEBUG_ASSERT( (((flags & _MALI_OSK_LOCKFLAG_SPINLOCK) || (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ)) && (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE)) + || !(flags & _MALI_OSK_LOCKFLAG_SPINLOCK)); + /* Parameter initial SBZ - for future expansion */ + MALI_DEBUG_ASSERT( 0 == initial ); + + lock = kmalloc(sizeof(_mali_osk_lock_t), GFP_KERNEL); + + if ( NULL == lock ) + { + return lock; + } + + /* Determine type of mutex: */ + /* defaults to interruptable mutex if no flags are specified */ + + if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK) ) + { + /* Non-interruptable Spinlocks override all others */ + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN; + spin_lock_init( &lock->obj.spinlock ); + } + else if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ ) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ; + lock->flags = 0; + spin_lock_init( &lock->obj.spinlock ); + } + else if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) + && (flags & _MALI_OSK_LOCKFLAG_READERWRITER) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW; + init_rwsem( &lock->obj.rw_sema ); + } + else + { + /* Usual mutex types */ + if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT; + } + else + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX; + } + + /* Initially unlocked */ + sema_init( &lock->obj.sema, 1 ); + } + + MALI_DEBUG_CODE( + /* Debug tracking of flags */ + lock->orig_flags = flags; + lock->locked_as = _MALI_OSK_LOCKMODE_UNDEF; + ); /* MALI_DEBUG_CODE */ + + return lock; +} + +_mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || _MALI_OSK_LOCKMODE_RO == mode ); + + /* Only allow RO locks when the initial object was a Reader/Writer lock + * Since information is lost on the internal locktype, we use the original + * information, which is only stored when built for DEBUG */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); + + switch ( lock->type ) + { + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: + spin_lock(&lock->obj.spinlock); + break; + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: + spin_lock_irqsave(&lock->obj.spinlock, lock->flags); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: + if ( down_interruptible(&lock->obj.sema) ) + { + err = _MALI_OSK_ERR_RESTARTSYSCALL; + } + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: + down(&lock->obj.sema); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: + if (mode == _MALI_OSK_LOCKMODE_RO) + { + down_read(&lock->obj.rw_sema); + } + else + { + down_write(&lock->obj.rw_sema); + } + break; + + default: + /* Reaching here indicates a programming error, so you will not get here + * on non-DEBUG builds */ + MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); + break; + } + + /* DEBUG tracking of previously locked state - occurs after lock obtained */ + MALI_DEBUG_CODE( + if ( _MALI_OSK_ERR_OK == err ) + { + /* Assert that this is not currently locked */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_UNDEF == lock->locked_as ); + + lock->locked_as = mode; + } + ); /* MALI_DEBUG_CODE */ + + return err; +} + +void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode ) +{ + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || _MALI_OSK_LOCKMODE_RO == mode ); + + /* Only allow RO locks when the initial object was a Reader/Writer lock + * Since information is lost on the internal locktype, we use the original + * information, which is only stored when built for DEBUG */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); + + /* For DEBUG only, assert that we previously locked this, and in the same way (RW/RO) */ + MALI_DEBUG_ASSERT( mode == lock->locked_as ); + + /* DEBUG tracking of previously locked state - occurs before lock released */ + MALI_DEBUG_CODE( lock->locked_as = _MALI_OSK_LOCKMODE_UNDEF ); + + switch ( lock->type ) + { + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: + spin_unlock(&lock->obj.spinlock); + break; + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: + spin_unlock_irqrestore(&lock->obj.spinlock, lock->flags); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: + /* FALLTHROUGH */ + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: + up(&lock->obj.sema); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: + if (mode == _MALI_OSK_LOCKMODE_RO) + { + up_read(&lock->obj.rw_sema); + } + else + { + up_write(&lock->obj.rw_sema); + } + break; + + default: + /* Reaching here indicates a programming error, so you will not get here + * on non-DEBUG builds */ + MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); + break; + } +} + +void _mali_osk_lock_term( _mali_osk_lock_t *lock ) +{ + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + /* For DEBUG only, assert that this is not currently locked */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_UNDEF == lock->locked_as ); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_low_level_mem.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_low_level_mem.c new file mode 100644 index 00000000000..6454709caaf --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_low_level_mem.c @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_low_level_mem.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include + +#include +#include +#include +#include +#include +#include + +#include "mali_osk.h" +#include "mali_ukk.h" /* required to hook in _mali_ukk_mem_mmap handling */ +#include "mali_kernel_common.h" +#include "mali_kernel_linux.h" + +static void mali_kernel_memory_vma_open(struct vm_area_struct * vma); +static void mali_kernel_memory_vma_close(struct vm_area_struct * vma); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + + +typedef struct mali_vma_usage_tracker +{ + int references; + u32 cookie; +} mali_vma_usage_tracker; + + +/* Linked list structure to hold details of all OS allocations in a particular + * mapping + */ +struct AllocationList +{ + struct AllocationList *next; + u32 offset; + u32 physaddr; +}; + +typedef struct AllocationList AllocationList; + +/* Private structure to store details of a mapping region returned + * from _mali_osk_mem_mapregion_init + */ +struct MappingInfo +{ + struct vm_area_struct *vma; + struct AllocationList *list; +}; + +typedef struct MappingInfo MappingInfo; + + +static u32 _kernel_page_allocate(void); +static void _kernel_page_release(u32 physical_address); +static AllocationList * _allocation_list_item_get(void); +static void _allocation_list_item_release(AllocationList * item); + + +/* Variable declarations */ +spinlock_t allocation_list_spinlock; +static AllocationList * pre_allocated_memory = (AllocationList*) NULL ; +static int pre_allocated_memory_size_current = 0; +#ifdef MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB + static int pre_allocated_memory_size_max = MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB * 1024 * 1024; +#else + static int pre_allocated_memory_size_max = 6 * 1024 * 1024; /* 6 MiB */ +#endif + +static struct vm_operations_struct mali_kernel_vm_ops = +{ + .open = mali_kernel_memory_vma_open, + .close = mali_kernel_memory_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = mali_kernel_memory_cpu_page_fault_handler +#else + .nopfn = mali_kernel_memory_cpu_page_fault_handler +#endif +}; + + +void mali_osk_low_level_mem_init(void) +{ + spin_lock_init( &allocation_list_spinlock ); + pre_allocated_memory = (AllocationList*) NULL ; +} + +void mali_osk_low_level_mem_term(void) +{ + while ( NULL != pre_allocated_memory ) + { + AllocationList *item; + item = pre_allocated_memory; + pre_allocated_memory = item->next; + _kernel_page_release(item->physaddr); + _mali_osk_free( item ); + } + pre_allocated_memory_size_current = 0; +} + +static u32 _kernel_page_allocate(void) +{ + struct page *new_page; + u32 linux_phys_addr; + + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + + if ( NULL == new_page ) + { + return 0; + } + + /* Ensure page is flushed from CPU caches. */ + linux_phys_addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + + return linux_phys_addr; +} + +static void _kernel_page_release(u32 physical_address) +{ + struct page *unmap_page; + + #if 1 + dma_unmap_page(NULL, physical_address, PAGE_SIZE, DMA_BIDIRECTIONAL); + #endif + + unmap_page = pfn_to_page( physical_address >> PAGE_SHIFT ); + MALI_DEBUG_ASSERT_POINTER( unmap_page ); + __free_page( unmap_page ); +} + +static AllocationList * _allocation_list_item_get(void) +{ + AllocationList *item = NULL; + unsigned long flags; + + spin_lock_irqsave(&allocation_list_spinlock,flags); + if ( pre_allocated_memory ) + { + item = pre_allocated_memory; + pre_allocated_memory = pre_allocated_memory->next; + pre_allocated_memory_size_current -= PAGE_SIZE; + + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + return item; + } + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + + item = _mali_osk_malloc( sizeof(AllocationList) ); + if ( NULL == item) + { + return NULL; + } + + item->physaddr = _kernel_page_allocate(); + if ( 0 == item->physaddr ) + { + /* Non-fatal error condition, out of memory. Upper levels will handle this. */ + _mali_osk_free( item ); + return NULL; + } + return item; +} + +static void _allocation_list_item_release(AllocationList * item) +{ + unsigned long flags; + spin_lock_irqsave(&allocation_list_spinlock,flags); + if ( pre_allocated_memory_size_current < pre_allocated_memory_size_max) + { + item->next = pre_allocated_memory; + pre_allocated_memory = item; + pre_allocated_memory_size_current += PAGE_SIZE; + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + return; + } + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + + _kernel_page_release(item->physaddr); + _mali_osk_free( item ); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + /* + * We always fail the call since all memory is pre-faulted when assigned to the process. + * Only the Mali cores can use page faults to extend buffers. + */ + + MALI_DEBUG_PRINT(1, ("Page-fault in Mali memory region caused by the CPU.\n")); + MALI_DEBUG_PRINT(1, ("Tried to access %p (process local virtual address) which is not currently mapped to any Mali memory.\n", (void*)address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void mali_kernel_memory_vma_open(struct vm_area_struct * vma) +{ + mali_vma_usage_tracker * vma_usage_tracker; + MALI_DEBUG_PRINT(4, ("Open called on vma %p\n", vma)); + + vma_usage_tracker = (mali_vma_usage_tracker*)vma->vm_private_data; + vma_usage_tracker->references++; + + return; +} + +static void mali_kernel_memory_vma_close(struct vm_area_struct * vma) +{ + _mali_uk_mem_munmap_s args = {0, }; + mali_memory_allocation * descriptor; + mali_vma_usage_tracker * vma_usage_tracker; + MALI_DEBUG_PRINT(3, ("Close called on vma %p\n", vma)); + + vma_usage_tracker = (mali_vma_usage_tracker*)vma->vm_private_data; + + BUG_ON(!vma_usage_tracker); + BUG_ON(0 == vma_usage_tracker->references); + + vma_usage_tracker->references--; + + if (0 != vma_usage_tracker->references) + { + MALI_DEBUG_PRINT(3, ("Ignoring this close, %d references still exists\n", vma_usage_tracker->references)); + return; + } + + /** @note args->context unused, initialized to 0. + * Instead, we use the memory_session from the cookie */ + + descriptor = (mali_memory_allocation *)vma_usage_tracker->cookie; + + args.cookie = (u32)descriptor; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + _mali_ukk_mem_munmap( &args ); + + /* vma_usage_tracker is free()d by _mali_osk_mem_mapregion_term(). + * In the case of the memory engine, it is called as the release function that has been registered with the engine*/ +} + + +void _mali_osk_mem_barrier( void ) +{ + mb(); +} + +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ) +{ + return (mali_io_address)ioremap_nocache(phys, size); +} + +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address virt ) +{ + iounmap((void*)virt); +} + +mali_io_address _mali_osk_mem_allocioregion( u32 *phys, u32 size ) +{ + void * virt; + MALI_DEBUG_ASSERT_POINTER( phys ); + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + MALI_DEBUG_ASSERT( 0 != size ); + + /* dma_alloc_* uses a limited region of address space. On most arch/marchs + * 2 to 14 MiB is available. This should be enough for the page tables, which + * currently is the only user of this function. */ + virt = dma_alloc_coherent(NULL, size, phys, GFP_KERNEL | GFP_DMA ); + + MALI_DEBUG_PRINT(3, ("Page table virt: 0x%x = dma_alloc_coherent(size:%d, phys:0x%x, )\n", virt, size, phys)); + + if ( NULL == virt ) + { + MALI_DEBUG_PRINT(1, ("allocioregion: Failed to allocate Pagetable memory, size=0x%.8X\n", size )); + MALI_DEBUG_PRINT(1, ("Solution: When configuring and building linux kernel, set CONSISTENT_DMA_SIZE to be 14 MB.\n")); + return 0; + } + + MALI_DEBUG_ASSERT( 0 == (*phys & ~_MALI_OSK_CPU_PAGE_MASK) ); + + return (mali_io_address)virt; +} + +void _mali_osk_mem_freeioregion( u32 phys, u32 size, mali_io_address virt ) +{ + MALI_DEBUG_ASSERT_POINTER( (void*)virt ); + MALI_DEBUG_ASSERT( 0 != size ); + MALI_DEBUG_ASSERT( 0 == (phys & ( (1 << PAGE_SHIFT) - 1 )) ); + + dma_free_coherent(NULL, size, virt, phys); +} + +_mali_osk_errcode_t inline _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ) +{ + return ((NULL == request_mem_region(phys, size, description)) ? _MALI_OSK_ERR_NOMEM : _MALI_OSK_ERR_OK); +} + +void inline _mali_osk_mem_unreqregion( u32 phys, u32 size ) +{ + release_mem_region(phys, size); +} + +u32 inline _mali_osk_mem_ioread32( volatile mali_io_address addr, u32 offset ) +{ + return ioread32(((u8*)addr) + offset); +} + +void inline _mali_osk_mem_iowrite32( volatile mali_io_address addr, u32 offset, u32 val ) +{ + iowrite32(val, ((u8*)addr) + offset); +} + +void _mali_osk_cache_flushall( void ) +{ + /** @note Cached memory is not currently supported in this implementation */ +} + +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ) +{ + wmb(); +} + +_mali_osk_errcode_t _mali_osk_mem_mapregion_init( mali_memory_allocation * descriptor ) +{ + struct vm_area_struct *vma; + mali_vma_usage_tracker * vma_usage_tracker; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + vma = (struct vm_area_struct*)descriptor->process_addr_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + /* Re-write the process_addr_mapping_info */ + mappingInfo = _mali_osk_calloc( 1, sizeof(MappingInfo) ); + + if ( NULL == mappingInfo ) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = _mali_osk_calloc( 1, sizeof(mali_vma_usage_tracker) ); + + if (NULL == vma_usage_tracker) + { + MALI_DEBUG_PRINT(2, ("Failed to allocate memory to track memory usage\n")); + _mali_osk_free( mappingInfo ); + return _MALI_OSK_ERR_FAULT; + } + + mappingInfo->vma = vma; + descriptor->process_addr_mapping_info = mappingInfo; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + /* list member is already NULL */ + + /* + set some bits which indicate that: + The memory is IO memory, meaning that no paging is to be performed and the memory should not be included in crash dumps + The memory is reserved, meaning that it's present and can never be paged out (see also previous entry) + */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_DONTCOPY; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mali_kernel_vm_ops; /* Operations used on any memory system */ + + vma_usage_tracker->references = 1; /* set initial reference count to be 1 as vma_open won't be called for the first mmap call */ + vma_usage_tracker->cookie = (u32)descriptor; /* cookie for munmap */ + + vma->vm_private_data = vma_usage_tracker; + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_mem_mapregion_term( mali_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + mali_vma_usage_tracker * vma_usage_tracker; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = mappingInfo->vma; + + MALI_DEBUG_ASSERT_POINTER( vma ); + + /* ASSERT that there are no allocations on the list. Unmap should've been + * called on all OS allocations. */ + MALI_DEBUG_ASSERT( NULL == mappingInfo->list ); + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + _mali_osk_free(vma_usage_tracker); + + _mali_osk_free( mappingInfo ); + return; +} + +_mali_osk_errcode_t _mali_osk_mem_mapregion_map( mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size ) +{ + struct vm_area_struct *vma; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_ASSERT_POINTER( phys_addr ); + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + + MALI_DEBUG_ASSERT( 0 == (offset & ~_MALI_OSK_CPU_PAGE_MASK)); + + if (NULL == descriptor->mapping) return _MALI_OSK_ERR_INVALID_ARGS; + + if (size > (descriptor->size - offset)) + { + MALI_DEBUG_PRINT(1,("_mali_osk_mem_mapregion_map: virtual memory area not large enough to map physical 0x%x size %x into area 0x%x at offset 0x%xr\n", + *phys_addr, size, descriptor->mapping, offset)); + return _MALI_OSK_ERR_FAULT; + } + + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + vma = mappingInfo->vma; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_PRINT(7, ("Process map: mapping 0x%08X to process address 0x%08lX length 0x%08X\n", *phys_addr, (long unsigned int)(descriptor->mapping + offset), size)); + + if ( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC == *phys_addr ) + { + _mali_osk_errcode_t ret; + AllocationList *alloc_item; + u32 linux_phys_frame_num; + + alloc_item = _allocation_list_item_get(); + + linux_phys_frame_num = alloc_item->physaddr >> PAGE_SHIFT; + + ret = ( remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, linux_phys_frame_num, size, vma->vm_page_prot) ) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK; + + if ( ret != _MALI_OSK_ERR_OK) + { + _allocation_list_item_release(alloc_item); + return ret; + } + + /* Put our alloc_item into the list of allocations on success */ + alloc_item->next = mappingInfo->list; + alloc_item->offset = offset; + + /*alloc_item->physaddr = linux_phys_addr;*/ + mappingInfo->list = alloc_item; + + /* Write out new physical address on success */ + *phys_addr = alloc_item->physaddr; + + return ret; + } + + /* Otherwise, Use the supplied physical address */ + + /* ASSERT that supplied phys_addr is page aligned */ + MALI_DEBUG_ASSERT( 0 == ((*phys_addr) & ~_MALI_OSK_CPU_PAGE_MASK) ); + + return ( remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, *phys_addr >> PAGE_SHIFT, size, vma->vm_page_prot) ) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK; + +} + +void _mali_osk_mem_mapregion_unmap( mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags ) +{ + MappingInfo *mappingInfo; + + if (NULL == descriptor) return; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + + MALI_DEBUG_ASSERT( 0 == (offset & ~_MALI_OSK_CPU_PAGE_MASK) ); + + if (NULL == descriptor->mapping) return; + + if (size > (descriptor->size - offset)) + { + MALI_DEBUG_PRINT(1,("_mali_osk_mem_mapregion_unmap: virtual memory area not large enough to unmap size %x from area 0x%x at offset 0x%x\n", + size, descriptor->mapping, offset)); + return; + } + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + if ( 0 != (flags & _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR) ) + { + /* This physical RAM was allocated in _mali_osk_mem_mapregion_map and + * so needs to be unmapped + */ + while (size) + { + /* First find the allocation in the list of allocations */ + AllocationList *alloc = mappingInfo->list; + AllocationList **prev = &(mappingInfo->list); + while (NULL != alloc && alloc->offset != offset) + { + prev = &(alloc->next); + alloc = alloc->next; + } + if (alloc == NULL) { + MALI_DEBUG_PRINT(1, ("Unmapping memory that isn't mapped\n")); + size -= _MALI_OSK_CPU_PAGE_SIZE; + offset += _MALI_OSK_CPU_PAGE_SIZE; + continue; + } + + _kernel_page_release(alloc->physaddr); + + /* Remove the allocation from the list */ + *prev = alloc->next; + _mali_osk_free( alloc ); + + /* Move onto the next allocation */ + size -= _MALI_OSK_CPU_PAGE_SIZE; + offset += _MALI_OSK_CPU_PAGE_SIZE; + } + } + + /* Linux does the right thing as part of munmap to remove the mapping */ + + return; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_mali.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_mali.c new file mode 100644 index 00000000000..694d8eeaabc --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_mali.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Added support for overriding memory settings in arch_configuration using + * mali_mem module parameter. + * + * Author: Magnus Wendt for + * ST-Ericsson. + */ + +/** + * @file mali_osk_mali.c + * Implementation of the OS abstraction layer which is specific for the Mali kernel device driver + */ +#include +#include + +#include "mali_kernel_common.h" /* MALI_xxx macros */ +#include "mali_osk.h" /* kernel side OS functions */ +#include "mali_uk_types.h" +#include "mali_kernel_linux.h" /* exports initialize/terminate_kernel_device() definition of mali_osk_low_level_mem_init() and term */ +#include "arch/config.h" /* contains the configuration of the arch we are compiling for */ + +extern char* mali_mem; + +/* is called from mali_kernel_constructor in common code */ +_mali_osk_errcode_t _mali_osk_init( void ) +{ + if (0 != initialize_kernel_device()) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + mali_osk_low_level_mem_init(); + + MALI_SUCCESS; +} + +/* is called from mali_kernel_deconstructor in common code */ +void _mali_osk_term( void ) +{ + mali_osk_low_level_mem_term(); + terminate_kernel_device(); +} + +_mali_osk_errcode_t _mali_osk_resources_init( _mali_osk_resource_t **arch_config, u32 *num_resources ) +{ + *num_resources = sizeof(arch_configuration) / sizeof(arch_configuration[0]); + *arch_config = arch_configuration; + + /* override the MEMORY resource if a value has been supplied from the command line */ + if ('\0' != mali_mem[0]) { + char *p = mali_mem; + _mali_osk_resource_type_t mem_type = MEMORY; + unsigned long mem_base = 0; + unsigned long mem_size = memparse(p, &p); + if (*p == '@') { + if ((*(p + 1) == 'O') || (*(p + 1) == 'o')) { /* as in OS. e.g. mali_mem=64M@OS_MEMORY */ + mem_type = OS_MEMORY; + mem_base = 0; + } else { /* parse the base address */ + mem_type = MEMORY; + mem_base = memparse(p + 1, &p); + } + } + + /* change the first memory entry in the architecture config. */ + if (0 < mem_size) { + int i; + for (i = 0; i < *num_resources; ++i) { + if ((MEMORY == arch_configuration[i].type) || + (OS_MEMORY == arch_configuration[i].type)) { + MALI_DEBUG_PRINT( 1, ("Overriding arch resource[%d] :\n",i)); + MALI_DEBUG_PRINT( 1, ("Type: %s, base: %x, size %x\n", + (OS_MEMORY==mem_type?"OS_MEMORY":"MEMORY"),mem_base,mem_size)); + arch_configuration[i].type = mem_type; + arch_configuration[i].base = mem_base; + arch_configuration[i].size = mem_size; + break; + } + } + } + } + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_resources_term( _mali_osk_resource_t **arch_config, u32 num_resources ) +{ + /* Nothing to do */ +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_math.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_math.c new file mode 100644 index 00000000000..2f7fc15847b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_math.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_math.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include + +u32 inline _mali_osk_clz( u32 input ) +{ + return 32-fls(input); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_memory.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_memory.c new file mode 100644 index 00000000000..bf6679f7bac --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_memory.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include + +void inline *_mali_osk_calloc( u32 n, u32 size ) +{ + return kcalloc(n, size, GFP_KERNEL); +} + +void inline *_mali_osk_malloc( u32 size ) +{ + return kmalloc(size, GFP_KERNEL); +} + +void inline _mali_osk_free( void *ptr ) +{ + kfree(ptr); +} + +void inline *_mali_osk_memcpy( void *dst, const void *src, u32 len ) +{ + return memcpy(dst, src, len); +} + +void inline *_mali_osk_memset( void *s, u32 c, u32 n ) +{ + return memset(s, c, n); +} + +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ) +{ + /* No need to prevent an out-of-memory dialogue appearing on Linux, + * so we always return MALI_TRUE. + */ + return MALI_TRUE; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_misc.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_misc.c new file mode 100644 index 00000000000..3afd2da736c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_misc.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_misc.c + * Implementation of the OS abstraction layer for the kernel device driver + */ +#include +#include +#include +#include +#include +#include "mali_osk.h" + +void _mali_osk_dbgmsg( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + vprintk(fmt, args); + va_end(args); +} + +void _mali_osk_abort(void) +{ + /* make a simple fault by dereferencing a NULL pointer */ + *(int *)0 = 0; +} + +void _mali_osk_break(void) +{ + _mali_osk_abort(); +} + +u32 _mali_osk_get_pid(void) +{ + /* Thread group ID is the process ID on Linux */ + return (u32)current->tgid; +} + +u32 _mali_osk_get_tid(void) +{ + /* pid is actually identifying the thread on Linux */ + return (u32)current->pid; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_notification.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_notification.c new file mode 100644 index 00000000000..2efc140f60a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_notification.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_notification.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/* needed to detect kernel version specific code */ +#include + +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +/** + * Declaration of the notification queue object type + * Contains a linked list of notification pending delivery to user space. + * It also contains a wait queue of exclusive waiters blocked in the ioctl + * When a new notification is posted a single thread is resumed. + */ +struct _mali_osk_notification_queue_t_struct +{ + struct semaphore mutex; /**< Mutex protecting the list */ + wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */ + struct list_head head; /**< List of notifications waiting to be picked up */ +}; + +typedef struct _mali_osk_notification_wrapper_t_struct +{ + struct list_head list; /**< Internal linked list variable */ + _mali_osk_notification_t data; /**< Notification data */ +} _mali_osk_notification_wrapper_t; + +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ) +{ + _mali_osk_notification_queue_t * result; + + result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL); + if (NULL == result) return NULL; + + sema_init(&result->mutex, 1); + init_waitqueue_head(&result->receive_queue); + INIT_LIST_HEAD(&result->head); + + return result; +} + +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ) +{ + /* OPT Recycling of notification objects */ + _mali_osk_notification_wrapper_t *notification; + + notification = (_mali_osk_notification_wrapper_t *)kmalloc( sizeof(_mali_osk_notification_wrapper_t) + size, GFP_KERNEL ); + if (NULL == notification) + { + MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n")); + return NULL; + } + + /* Init the list */ + INIT_LIST_HEAD(¬ification->list); + + if (0 != size) + { + notification->data.result_buffer = ((u8*)notification) + sizeof(_mali_osk_notification_wrapper_t); + } + else + { + notification->data.result_buffer = NULL; + } + + /* set up the non-allocating fields */ + notification->data.notification_type = type; + notification->data.result_buffer_size = size; + + /* all ok */ + return &(notification->data); +} + +void _mali_osk_notification_delete( _mali_osk_notification_t *object ) +{ + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + + /* Remove from the list */ + list_del(¬ification->list); + /* Free the container */ + kfree(notification); +} + +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ) +{ + MALI_DEBUG_ASSERT_POINTER( queue ); + + /* not much to do, just free the memory */ + kfree(queue); +} + +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ) +{ + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + + /* lock queue access */ + down(&queue->mutex); + /* add to list */ + list_add_tail(¬ification->list, &queue->head); + /* unlock the queue */ + up(&queue->mutex); + + /* and wake up one possible exclusive waiter */ + wake_up(&queue->receive_queue); +} + +static int _mali_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ) +{ + int ret; + + down(&queue->mutex); + ret = list_empty(&queue->head); + up(&queue->mutex); + return ret; +} + +#if MALI_STATE_TRACKING +mali_bool _mali_osk_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ) +{ + return _mali_notification_queue_is_empty(queue) ? MALI_TRUE : MALI_FALSE; +} +#endif + +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ + _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND; + _mali_osk_notification_wrapper_t *wrapper_object; + + down(&queue->mutex); + + if (!list_empty(&queue->head)) + { + wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list); + *result = &(wrapper_object->data); + list_del_init(&wrapper_object->list); + ret = _MALI_OSK_ERR_OK; + } + + up(&queue->mutex); + + return ret; +} + +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ + /* check input */ + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( result ); + + /* default result */ + *result = NULL; + + while (_MALI_OSK_ERR_OK != _mali_osk_notification_queue_dequeue(queue, result)) + { + if (wait_event_interruptible(queue->receive_queue, !_mali_notification_queue_is_empty(queue))) + { + return _MALI_OSK_ERR_RESTARTSYSCALL; + } + } + + return _MALI_OSK_ERR_OK; /* all ok */ +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_pm.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_pm.c new file mode 100644 index 00000000000..04fcd4572df --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_pm.c @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_pm.c + * Implementation of the callback functions from common power management + */ + +#include + +#ifdef CONFIG_PM_RUNTIME +#include +#endif /* CONFIG_PM_RUNTIME */ + +#include + +#include "mali_platform.h" +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_pmm.h" +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_kernel_pm.h" +#include "mali_device_pause_resume.h" +#include "mali_linux_pm.h" +#include "mali_linux_pm_testsuite.h" + +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifdef CONFIG_PM_RUNTIME +static int is_runtime =0; +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + +#if MALI_POWER_MGMT_TEST_SUITE + +#ifdef CONFIG_PM +unsigned int mali_pmm_events_triggered_mask = 0; +#endif /* CONFIG_PM */ + +void _mali_osk_pmm_policy_events_notifications(mali_pmm_event_id mali_pmm_event) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + + switch (mali_pmm_event) + { + case MALI_PMM_EVENT_JOB_QUEUED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<0); + } + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<1); + } + break; + + case MALI_PMM_EVENT_JOB_FINISHED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<2); + mali_job_scheduling_events_recording_on = 0; + pwr_mgmt_status_reg = mali_pmm_events_triggered_mask; + } + break; + + case MALI_PMM_EVENT_TIMEOUT: + if (mali_timeout_event_recording_on == 1) + { + pwr_mgmt_status_reg = (1<<3); + mali_timeout_event_recording_on = 0; + } + break; + + default: + + break; + + } +#endif /* CONFIG_PM */ + +#endif /* MALI_LICENSE_IS_GPL */ +} +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/** This function is called when the Mali device has completed power up + * operation. + */ +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; + wake_up_process(pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI OSK Power up Done\n" )); + return; +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +/** This function is called when the Mali device has completed power down + * operation. + */ +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; +#if MALI_POWER_MGMT_TEST_SUITE + if (is_mali_pmu_present == 0) + { + pwr_mgmt_status_reg = _mali_pmm_cores_list(); + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + wake_up_process(pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Power down Done\n" )); + return; + +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +/** This function is invoked when mali device is idle. +*/ +_mali_osk_errcode_t _mali_osk_pmm_dev_idle(void) +{ + _mali_osk_errcode_t err = 0; +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + + err = pm_runtime_put_sync(&(mali_gpu_device.dev)); + if(err) + { + MALI_DEBUG_PRINT(4, ("OSPMM: Error in _mali_osk_pmm_dev_idle\n" )); + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_LICENSE_IS_GPL */ + return err; +} + +/** This funtion is invoked when mali device needs to be activated. +*/ +void _mali_osk_pmm_dev_activate(void) +{ + +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + int err = 0; + if(is_runtime == 0) + { + pm_suspend_ignore_children(&(mali_gpu_device.dev), true); + pm_runtime_enable(&(mali_gpu_device.dev)); + pm_runtime_get_sync(&(mali_gpu_device.dev)); + is_runtime = 1; + } + else + { + err = pm_runtime_get_sync(&(mali_gpu_device.dev)); + } + if(err) + { + MALI_DEBUG_PRINT(4, ("OSPMM: Error in _mali_osk_pmm_dev_activate\n" )); + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +void _mali_osk_pmm_dvfs_operation_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; + wake_up_process(dvfs_pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI OSK DVFS Operation done\n" )); + return; +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_specific.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_specific.h new file mode 100644 index 00000000000..54acfdd138c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_specific.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_specific.h + * Defines per-OS Kernel level specifics, such as unusual workarounds for + * certain OSs. + */ + +#ifndef __MALI_OSK_SPECIFIC_H__ +#define __MALI_OSK_SPECIFIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_STATIC_INLINE static inline +#define MALI_NON_STATIC_INLINE inline + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_SPECIFIC_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_time.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_time.c new file mode 100644 index 00000000000..aa7962380e6 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_time.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_time.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include +#include +#include + +int _mali_osk_time_after( u32 ticka, u32 tickb ) +{ + return time_after((unsigned long)ticka, (unsigned long)tickb); +} + +u32 _mali_osk_time_mstoticks( u32 ms ) +{ + return msecs_to_jiffies(ms); +} + +u32 _mali_osk_time_tickstoms( u32 ticks ) +{ + return jiffies_to_msecs(ticks); +} + +u32 _mali_osk_time_tickcount( void ) +{ + return jiffies; +} + +void _mali_osk_time_ubusydelay( u32 usecs ) +{ + udelay(usecs); +} + +u64 _mali_osk_time_get_ns( void ) +{ + struct timespec tsval; + getnstimeofday(&tsval); + return (u64)timespec_to_ns(&tsval); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_timers.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_timers.c new file mode 100644 index 00000000000..3dfba76bcb3 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_osk_timers.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_timers.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" + +struct _mali_osk_timer_t_struct +{ + struct timer_list timer; +}; + +typedef void (*timer_timeout_function_t)(unsigned long); + +_mali_osk_timer_t *_mali_osk_timer_init(void) +{ + _mali_osk_timer_t *t = (_mali_osk_timer_t*)kmalloc(sizeof(_mali_osk_timer_t), GFP_KERNEL); + if (NULL != t) init_timer(&t->timer); + return t; +} + +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.expires = _mali_osk_time_tickcount() + ticks_to_expire; + add_timer(&(tim->timer)); +} + +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 expiry_tick) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + mod_timer(&(tim->timer), expiry_tick); +} + +void _mali_osk_timer_del( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + del_timer_sync(&(tim->timer)); +} + +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.data = (unsigned long)data; + tim->timer.function = (timer_timeout_function_t)callback; +} + +void _mali_osk_timer_term( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + kfree(tim); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_core.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_core.c new file mode 100644 index 00000000000..0f745492613 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_core.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* memort allocation functions */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs) +{ + _mali_uk_get_api_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.version, &uargs->version)) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_get_api_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + if (0 != put_user(kargs.compatible, &uargs->compatible)) return -EFAULT; + + return 0; +} + +int get_system_info_size_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_size_s __user *uargs) +{ + _mali_uk_get_system_info_size_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_system_info_size(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.size, &uargs->size)) return -EFAULT; + + return 0; +} + +int get_system_info_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_s __user *uargs) +{ + _mali_uk_get_system_info_s kargs; + _mali_osk_errcode_t err; + _mali_system_info *system_info_user; + _mali_system_info *system_info_kernel; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.system_info, &uargs->system_info)) return -EFAULT; + if (0 != get_user(kargs.size, &uargs->size)) return -EFAULT; + + /* A temporary kernel buffer for the system_info datastructure is passed through the system_info + * member. The ukk_private member will point to the user space destination of this buffer so + * that _mali_ukk_get_system_info() can correct the pointers in the system_info correctly + * for user space. + */ + system_info_kernel = kmalloc(kargs.size, GFP_KERNEL); + if (NULL == system_info_kernel) return -EFAULT; + + system_info_user = kargs.system_info; + kargs.system_info = system_info_kernel; + kargs.ukk_private = (u32)system_info_user; + kargs.ctx = session_data; + + err = _mali_ukk_get_system_info(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + kfree(system_info_kernel); + return map_errcode(err); + } + + if (0 != copy_to_user(system_info_user, system_info_kernel, kargs.size)) + { + kfree(system_info_kernel); + return -EFAULT; + } + + kfree(system_info_kernel); + return 0; +} + +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs) +{ + _mali_uk_wait_for_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_wait_for_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if(_MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS != kargs.type) + { + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_wait_for_notification_s))) return -EFAULT; + } + else + { + if (0 != put_user(kargs.type, &uargs->type)) return -EFAULT; + } + + return 0; +} + +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs) +{ + _mali_uk_post_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + + if (0 != get_user(kargs.type, &uargs->type)) + { + return -EFAULT; + } + + err = _mali_ukk_post_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_gp.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_gp.c new file mode 100644 index 00000000000..a6f355fd459 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_gp.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs) +{ + _mali_uk_gp_start_job_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (!access_ok(VERIFY_WRITE, uargs, sizeof(_mali_uk_gp_start_job_s))) + { + return -EFAULT; + } + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_start_job_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_gp_start_job(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_gp_start_job_s))) + { + /* + * If this happens, then user space will not know that the job was actually started, + * and if we return a queued job, then user space will still think that one is still queued. + * This will typically lead to a deadlock in user space. + * This could however only happen if user space deliberately passes a user buffer which + * passes the access_ok(VERIFY_WRITE) check, but isn't fully writable at the time of copy_to_user(). + * The official Mali driver will never attempt to do that, and kernel space should not be affected. + * That is why we do not bother to do a complex rollback in this very very very rare case. + */ + return -EFAULT; + } + + return 0; +} + +int gp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_abort_job_s __user *uargs) +{ + _mali_uk_gp_abort_job_s kargs; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_abort_job_s))) return -EFAULT; + + kargs.ctx = session_data; + _mali_ukk_gp_abort_job(&kargs); + + return 0; +} + + +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs) +{ + _mali_uk_get_gp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} + +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs) +{ + _mali_uk_gp_suspend_response_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_suspend_response_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_gp_suspend_response(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.cookie, &uargs->cookie)) return -EFAULT; + + /* no known transactions to roll-back */ + return 0; +} + +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_gp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.number_of_cores, &uargs->number_of_cores)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_mem.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_mem.c new file mode 100644 index 00000000000..3f67a1e3328 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_mem.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int mem_init_wrapper(struct mali_session_data *session_data, _mali_uk_init_mem_s __user *uargs) +{ + _mali_uk_init_mem_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_init_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.mali_address_base, &uargs->mali_address_base)) goto mem_init_rollback; + if (0 != put_user(kargs.memory_size, &uargs->memory_size)) goto mem_init_rollback; + + return 0; + +mem_init_rollback: + { + _mali_uk_term_mem_s kargs; + kargs.ctx = session_data; + err = _mali_ukk_term_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_init_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; +} + +int mem_term_wrapper(struct mali_session_data *session_data, _mali_uk_term_mem_s __user *uargs) +{ + _mali_uk_term_mem_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_term_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument) +{ + _mali_uk_map_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_map_external_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_map_external_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) + { + if (_MALI_OSK_ERR_OK == err_code) + { + /* Rollback */ + _mali_uk_unmap_external_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_unmap_external_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_unmap_external_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument) +{ + _mali_uk_unmap_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_unmap_external_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_unmap_external_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument) +{ + _mali_uk_release_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_release_ump_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_release_ump_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument) +{ + _mali_uk_attach_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_attach_ump_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_attach_ump_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) + { + if (_MALI_OSK_ERR_OK == err_code) + { + /* Rollback */ + _mali_uk_release_ump_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_release_ump_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_attach_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_map_external_ump_mem produced */ + return map_errcode(err_code); +} +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs) +{ + _mali_uk_query_mmu_page_table_dump_size_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + + err = _mali_ukk_query_mmu_page_table_dump_size(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.size, &uargs->size)) return -EFAULT; + + return 0; +} + +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs) +{ + _mali_uk_dump_mmu_page_table_s kargs; + _mali_osk_errcode_t err; + void *buffer; + int rc = -EFAULT; + + /* validate input */ + MALI_CHECK_NON_NULL(uargs, -EINVAL); + /* the session_data pointer was validated by caller */ + + kargs.buffer = NULL; + + /* get location of user buffer */ + if (0 != get_user(buffer, &uargs->buffer)) goto err_exit; + /* get size of mmu page table info buffer from user space */ + if ( 0 != get_user(kargs.size, &uargs->size) ) goto err_exit; + /* verify we can access the whole of the user buffer */ + if (!access_ok(VERIFY_WRITE, buffer, kargs.size)) goto err_exit; + + /* allocate temporary buffer (kernel side) to store mmu page table info */ + kargs.buffer = _mali_osk_malloc(kargs.size); + if (NULL == kargs.buffer) + { + rc = -ENOMEM; + goto err_exit; + } + + kargs.ctx = session_data; + err = _mali_ukk_dump_mmu_page_table(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + rc = map_errcode(err); + goto err_exit; + } + + /* copy mmu page table info back to user space and update pointers */ + if (0 != copy_to_user(uargs->buffer, kargs.buffer, kargs.size) ) goto err_exit; + if (0 != put_user((kargs.register_writes - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->register_writes)) goto err_exit; + if (0 != put_user((kargs.page_table_dump - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->page_table_dump)) goto err_exit; + if (0 != put_user(kargs.register_writes_size, &uargs->register_writes_size)) goto err_exit; + if (0 != put_user(kargs.page_table_dump_size, &uargs->page_table_dump_size)) goto err_exit; + rc = 0; + +err_exit: + if (kargs.buffer) _mali_osk_free(kargs.buffer); + return rc; +} + + + +int mem_get_big_block_wrapper( struct file * filp, _mali_uk_get_big_block_s __user * argument ) +{ + _mali_uk_get_big_block_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_get_big_block_s)) ) + { + return -EFAULT; + } + + /* This interface inserts something into the ukk_private word */ + uk_args.ukk_private = (u32)filp; + uk_args.ctx = filp->private_data; + err_code = _mali_ukk_get_big_block( &uk_args ); + + /* Do not leak the private word back into user space */ + uk_args.ukk_private = 0; + + if ( _MALI_OSK_ERR_OK != err_code ) + { + return map_errcode(err_code); + } + + /* From this point on, we must roll-back any failing action to preserve the + * meaning of the U/K interface (e.g. when excluded) */ + + /* transfer response back to user space */ + if ( 0 != copy_to_user(argument, &uk_args, sizeof(_mali_uk_get_big_block_s)) ) + { + /* Roll-back - the _mali_uk_get_big_block call succeeded, so all + * values in uk_args will be correct */ + _mali_uk_free_big_block_s uk_args_rollback = {0, }; + + uk_args_rollback.ctx = uk_args.ctx; + uk_args_rollback.cookie = uk_args.cookie; + err_code = _mali_ukk_free_big_block( &uk_args_rollback ); + + if ( _MALI_OSK_ERR_OK != err_code ) + { + /* error in DEBUG and RELEASE */ + MALI_PRINT_ERROR( ("Failed to rollback get_big_block: %.8X\n", (u32)err_code) ); + } + return -EFAULT; + } + + return 0; +} + +int mem_free_big_block_wrapper(struct mali_session_data *session_data, _mali_uk_free_big_block_s __user * argument) +{ + _mali_uk_free_big_block_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL ); + + /* get call arguments from user space. get_user returns 0 on success */ + if ( 0 != get_user(uk_args.cookie, &argument->cookie) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_free_big_block( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_pp.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_pp.c new file mode 100644 index 00000000000..f77a177d865 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_pp.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs) +{ + _mali_uk_pp_start_job_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (!access_ok(VERIFY_WRITE, uargs, sizeof(_mali_uk_pp_start_job_s))) + { + return -EFAULT; + } + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_start_job_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_pp_start_job(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.returned_user_job_ptr, &uargs->returned_user_job_ptr) || + 0 != put_user(kargs.status, &uargs->status)) + { + /* + * If this happens, then user space will not know that the job was actually started, + * and if we return a queued job, then user space will still think that one is still queued. + * This will typically lead to a deadlock in user space. + * This could however only happen if user space deliberately passes a user buffer which + * passes the access_ok(VERIFY_WRITE) check, but isn't fully writable at the time of copy_to_user(). + * The official Mali driver will never attempt to do that, and kernel space should not be affected. + * That is why we do not bother to do a complex rollback in this very very very rare case. + */ + return -EFAULT; + } + + return 0; +} + +int pp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_abort_job_s __user *uargs) +{ + _mali_uk_pp_abort_job_s kargs; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_abort_job_s))) return -EFAULT; + + kargs.ctx = session_data; + _mali_ukk_pp_abort_job(&kargs); + + return 0; +} + +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_pp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_pp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.number_of_cores, &uargs->number_of_cores)) return -EFAULT; + + return 0; +} + +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs) +{ + _mali_uk_get_pp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_pp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_profiling.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_profiling.c new file mode 100644 index 00000000000..636bd03580e --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_profiling.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs) +{ + _mali_uk_profiling_start_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_start_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_start(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.limit, &uargs->limit)) + { + return -EFAULT; + } + + return 0; +} + +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs) +{ + _mali_uk_profiling_add_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_add_event_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_add_event(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs) +{ + _mali_uk_profiling_stop_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_stop(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.count, &uargs->count)) + { + return -EFAULT; + } + + return 0; +} + +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs) +{ + _mali_uk_profiling_get_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.index, &uargs->index)) + { + return -EFAULT; + } + + kargs.ctx = session_data; + + err = _mali_ukk_profiling_get_event(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_profiling_get_event_s))) + { + return -EFAULT; + } + + return 0; +} + +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs) +{ + _mali_uk_profiling_clear_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_clear(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_vsync.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_vsync.c new file mode 100644 index 00000000000..965ee411535 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_vsync.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs) +{ + _mali_uk_vsync_event_report_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_vsync_event_report_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_vsync_event_report(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_wrappers.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_wrappers.h new file mode 100644 index 00000000000..54e3f656b37 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_ukk_wrappers.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk_wrappers.h + * Defines the wrapper functions for each user-kernel function + */ + +#ifndef __MALI_UKK_WRAPPERS_H__ +#define __MALI_UKK_WRAPPERS_H__ + +#include "mali_uk_types.h" +#include "mali_osk.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +int get_system_info_size_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_size_s __user *uargs); +int get_system_info_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_s __user *uargs); +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs); +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs); +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs); +int mem_init_wrapper(struct mali_session_data *session_data, _mali_uk_init_mem_s __user *uargs); +int mem_term_wrapper(struct mali_session_data *session_data, _mali_uk_term_mem_s __user *uargs); +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument); +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument); +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs); +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs); + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument); +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +int mem_get_big_block_wrapper( struct file * filp, _mali_uk_get_big_block_s __user * argument ); +int mem_free_big_block_wrapper( struct mali_session_data *session_data, _mali_uk_free_big_block_s __user * argument); +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs); +int pp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_abort_job_s __user *uargs); +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs); +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs); +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs); +int gp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_abort_job_s __user *uargs); +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs); +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs); +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs); + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs); +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs); +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs); +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs); +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs); + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs); + +int map_errcode( _mali_osk_errcode_t err ); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/default/mali_platform.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/default/mali_platform.c new file mode 100644 index 00000000000..36301a93a2c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/default/mali_platform.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for a default platform + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + + +_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerdown(u32 cores) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerup(u32 cores) +{ + MALI_SUCCESS; +} + +void mali_gpu_utilization_handler(u32 utilization) +{ +} + +#if MALI_POWER_MGMT_TEST_SUITE +u32 pmu_get_power_up_down_info(void) +{ + return 4095; + +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali-runtimepm/mali_platform.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali-runtimepm/mali_platform.c new file mode 100644 index 00000000000..b8c47094177 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali-runtimepm/mali_platform.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for a default platform + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" +#include "mali_pmm.h" + +static int is_run_time = 0; +_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerdown(u32 cores) +{ + if(is_run_time == 1) + { + _mali_osk_pmm_dev_idle(); + is_run_time =0; + } + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerup(u32 cores) +{ + if(is_run_time == 0) + { + _mali_osk_pmm_dev_activate(); + is_run_time = 1; + } + MALI_SUCCESS; +} + +void mali_gpu_utilization_handler(u32 utilization) +{ +} + +#if MALI_POWER_MGMT_TEST_SUITE +u32 pmu_get_power_up_down_info(void) +{ + return 4095; + +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali400-pmu/mali_platform.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali400-pmu/mali_platform.c new file mode 100644 index 00000000000..cf95b8ae09e --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali400-pmu/mali_platform.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for Mali 400 PMU hardware + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#if USING_MALI_PMM + +#include "mali_pmm.h" + +/* Internal test on/off */ +#define PMU_TEST 0 + +/** @brief PMU hardware info + */ +typedef struct platform_pmu +{ + u32 reg_base_addr; /**< PMU registers base address */ + u32 reg_size; /**< PMU registers size */ + const char *name; /**< PMU name */ + u32 irq_num; /**< PMU irq number */ + + mali_io_address reg_mapped; /**< IO-mapped pointer to registers */ +} platform_pmu_t; + +static platform_pmu_t *pmu_info = NULL; + +/** @brief Register layout for hardware PMU + */ +typedef enum { + PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */ + PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */ + PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */ + PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */ + PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */ + PMU_REG_ADDR_MGMT_INT_STAT = 0x14, /*< Interrupt status register */ + PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */ + PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Software delay register */ + PMU_REG_ADDR_MGMT_MASTER_PWR_UP = 0x24, /*< Master power up register */ + PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */ +} pmu_reg_addr_mgmt_addr; + +/* Internal functions */ +u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address); +void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val); +mali_pmm_core_mask pmu_translate_cores_to_pmu(mali_pmm_core_mask cores); +#if PMU_TEST +void pmm_pmu_dump_regs( platform_pmu_t *pmu ); +void pmm_pmu_test( platform_pmu_t *pmu, u32 cores ); +#endif + +#endif /* USING_MALI_PMM */ + + +_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource) +{ +#if USING_MALI_PMM + if( resource == NULL ) + { + /* Nothing to set up for the system */ + } + else if( resource->type == PMU ) + { + if( (resource->base == 0) || + (resource->description == NULL) ) + { + /* NOTE: We currently don't care about any other resource settings */ + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Missing PMU set up information\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_DEBUG_ASSERT( pmu_info == NULL ); + pmu_info = (platform_pmu_t *)_mali_osk_malloc(sizeof(*pmu_info)); + MALI_CHECK_NON_NULL( pmu_info, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmu_info, 0, sizeof(*pmu_info)); + + pmu_info->reg_base_addr = resource->base; + pmu_info->reg_size = (u32)PMU_REGISTER_ADDRESS_SPACE_SIZE; + pmu_info->name = resource->description; + pmu_info->irq_num = resource->irq; + + if( _MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name) ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not request register region (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: request_mem_region: (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + } + + pmu_info->reg_mapped = _mali_osk_mem_mapioregion( pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name ); + + if( 0 == pmu_info->reg_mapped ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not ioremap registers for %s .\n", pmu_info->name)); + _mali_osk_mem_unreqregion( pmu_info->reg_base_addr, pmu_info->reg_size ); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: ioremap_nocache: Internal ptr: (0x%08X - 0x%08X) for %s\n", + (u32) pmu_info->reg_mapped, + ((u32)pmu_info->reg_mapped)+ pmu_info->reg_size - 1, + pmu_info->name)); + } + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: Mapping registers to %s\n", pmu_info->name)); + +#if PMU_TEST + pmu_test(pmu_info, (MALI_PMM_CORE_GP)); + pmu_test(pmu_info, (MALI_PMM_CORE_GP|MALI_PMM_CORE_L2|MALI_PMM_CORE_PP0)); +#endif + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Initialized - %s\n", pmu_info->name) ); + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +cleanup: + _mali_osk_free(pmu_info); + pmu_info = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + +#else + /* Nothing to do when not using PMM - as mali already on */ + MALI_SUCCESS; +#endif + +} + +_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type) +{ +#if USING_MALI_PMM + if( type == NULL ) + { + /* Nothing to tear down for the system */ + } + else if (*type == PMU) + { + if( pmu_info ) + { + _mali_osk_mem_unmapioregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->reg_mapped); + _mali_osk_mem_unreqregion(pmu_info->reg_base_addr, pmu_info->reg_size); + _mali_osk_free(pmu_info); + pmu_info = NULL; + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Terminated PMU\n") ); + } + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +#else + /* Nothing to do when not using PMM */ + MALI_SUCCESS; +#endif +} + +_mali_osk_errcode_t mali_platform_powerdown(u32 cores) +{ +#if USING_MALI_PMM + u32 stat; + u32 timeout; + u32 cores_pmu; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power down (0x%x)\n", cores) ); + + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_DOWN, cores_pmu ); + + /* Wait for cores to be powered down */ + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == cores_pmu ) break; /* All cores we wanted are now asleep */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; + +#else + /* Nothing to do when not using PMM */ + MALI_SUCCESS; +#endif +} + +_mali_osk_errcode_t mali_platform_powerup(u32 cores) +{ +#if USING_MALI_PMM + u32 cores_pmu; + u32 stat; + u32 timeout; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power up (0x%x)\n", cores) ); + + /* Don't use interrupts - just poll status */ + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_INT_MASK, 0 ); + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_UP, cores_pmu ); + + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == 0 ) break; /* All cores we wanted are now awake */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; + +#else + /* Nothing to do when not using PMM */ + MALI_SUCCESS; +#endif +} + +void mali_gpu_utilization_handler(u32 utilization) +{ +} + +#if USING_MALI_PMM + +/***** INTERNAL *****/ + +/** @brief Internal PMU function to translate the cores bit mask + * into something the hardware PMU understands + * + * @param cores PMM cores bitmask + * @return PMU hardware cores bitmask + */ +u32 pmu_translate_cores_to_pmu(mali_pmm_core_mask cores) +{ + /* For Mali 400 PMU the cores mask is already the same as what + * the hardware PMU expects. + * For other hardware, some translation can be done here, by + * translating the MALI_PMM_CORE_* bits into specific hardware + * bits + */ + return cores; +} + +/** @brief Internal PMU function to read a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to read from + * @return 32-bit value that was read from the address + */ +u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address) +{ + u32 read_val; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + read_val = _mali_osk_mem_ioread32(pmu->reg_mapped, relative_address); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_read: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, read_val)); + + return read_val; +} + +/** @brief Internal PMU function to write to a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to write to + * @param new_val new 32-bit value to write into the address + */ +void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val) +{ + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_write: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, new_val)); + + _mali_osk_mem_iowrite32(pmu->reg_mapped, relative_address, new_val); +} + +#if MALI_POWER_MGMT_TEST_SUITE + +u32 pmu_get_power_up_down_info(void) +{ + return pmu_reg_read(pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS); +} + +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +#endif /* USING_MALI_PMM */ + + +#if USING_MALI_PMM && PMU_TEST + +/***** TEST *****/ + +void pmu_dump_regs( platform_pmu_t *pmu ) +{ + u32 addr; + for( addr = 0x0; addr < PMU_REGISTER_ADDRESS_SPACE_SIZE; addr += 0x4 ) + { + MALI_PRINT( ("PMU_REG: 0x%08x: 0x%04x\n", (addr + pmu->reg_base_addr), pmu_reg_read( pmu, addr ) ) ); + } +} + +/* This function is an internal test for the PMU without any Mali h/w interaction */ +void pmu_test( platform_pmu_t *pmu, u32 cores ) +{ + u32 stat; + u32 timeout; + + MALI_PRINT( ("PMU_TEST: Start\n") ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power down cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_down( pmu, cores, MALI_TRUE ); + + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == cores ? "SUCCESS" : "FAIL" ) ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power up cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_up( pmu, cores, MALI_FALSE ); + + MALI_PRINT( ("PMU_TEST: Waiting for power up...\n") ); + timeout = 1000; /* 1 sec */ + while( !_mali_pmm_pmu_irq_power_up(pmu) && timeout > 0 ) + { + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } + + MALI_PRINT( ("PMU_TEST: Waited %dms for interrupt\n", (1000-timeout)) ); + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == 0 ? "SUCCESS" : "FAIL" ) ); + + _mali_pmm_pmu_irq_power_up_clear(pmu); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Finish\n") ); +} +#endif /* USING_MALI_PMM && PMU_TEST */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali_platform.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali_platform.h new file mode 100644 index 00000000000..575c1fb6113 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/mali_platform.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.h + * Platform specific Mali driver functions + */ + +#include "mali_osk.h" + +#if USING_MALI_PMM +#include "mali_pmm.h" +#endif + +#if !USING_MALI_PMM +/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +#define MALI_PLATFORM_SYSTEM 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * When using PMM, it is also called from the PMM start up to initialise the + * system PMU + * + * @param resource This is NULL when called on first driver start up, else it will + * be a pointer to a PMU resource + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * When using PMM, it is also called from the PMM termination code to clean up the + * system PMU + * + * @param type This is NULL when called on driver exit, else it will + * be a pointer to a PMU resource type (not the full resource) + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type); + +/** @brief Platform specific powerdown sequence of MALI + * + * Called as part of platform init if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_powerdown(u32 cores); + +/** @brief Platform specific powerup sequence of MALI + * + * Called as part of platform deinit if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_powerup(u32 cores); + +/** @brief Platform specific handling of GPU utilization data + * + * When GPU utilization data is enabled, this function will be + * periodically called. + * + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +void mali_gpu_utilization_handler(u32 utilization); + +#if USING_MALI_PMM +#if MALI_POWER_MGMT_TEST_SUITE +/** @brief function to get status of individual cores + * + * This function is used by power management test suite to get the status of powered up/down the number + * of cores + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +u32 pmu_get_power_up_down_info(void); +#endif +#endif + +#ifdef __cplusplus +} +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c new file mode 100644 index 00000000000..481e6e76c54 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Mali related Ux500 platform initialization + * + * Author: Marta Lofstedt for + * ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for ST-Ericsson's Ux500 platforms + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#include +#include +#include +#include +#include + +#include + +#define MALI_HIGH_TO_LOW_LEVEL_UTILIZATION_LIMIT 64 +#define MALI_LOW_TO_HIGH_LEVEL_UTILIZATION_LIMIT 192 + +static int is_running; +static struct regulator *regulator; +static struct clk *clk_sga; +static u32 last_utilization; +static struct work_struct mali_utilization_work; +static struct workqueue_struct *mali_utilization_workqueue; + +/* Rationale behind the values for: +* MALI_HIGH_LEVEL_UTILIZATION_LIMIT and MALI_LOW_LEVEL_UTILIZATION_LIMIT +* When operating at half clock frequency a faster clock is requested when +* reaching 75% utilization. When operating at full clock frequency a slower +* clock is requested when reaching 25% utilization. There is a margin of 25% +* at the high range of the slow clock to avoid complete saturation of the +* hardware and there is some overlap to avoid an oscillating situation where +* the clock goes back and forth from high to low. +* +* Utilization on full speed clock +* 0 64 128 192 255 +* |---------------|---------------|---------------|---------------| +* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +* | ^ +* V | +* XXXXXXXXXXXXXXXXXXXXXXXXX +* 0 64 128 192 255 +* |-------|-------|-------|-------| +* Utilization on half speed clock +*/ +void mali_utilization_function(struct work_struct *ptr) +{ + /*By default, platform start with 50% APE OPP and 25% DDR OPP*/ + static u32 has_requested_low = 1; + + if (last_utilization > MALI_LOW_TO_HIGH_LEVEL_UTILIZATION_LIMIT) { + if (has_requested_low) { + MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u SIGNAL_HIGH\n", last_utilization)); + /*Request 100% APE_OPP.*/ + if (prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "mali", 100)) { + MALI_DEBUG_PRINT(2, ("MALI 100% APE_OPP failed\n")); + return; + } + /* + * Since the utilization values will be reported higher + * if DDR_OPP is lowered, we also request 100% DDR_OPP. + */ + if (prcmu_qos_add_requirement(PRCMU_QOS_DDR_OPP, "mali", 100)) { + MALI_DEBUG_PRINT(2, ("MALI 100% DDR_OPP failed\n")); + return; + } + has_requested_low = 0; + } + } else { + if (last_utilization < MALI_HIGH_TO_LOW_LEVEL_UTILIZATION_LIMIT) { + if (!has_requested_low) { + /*Remove APE_OPP and DDR_OPP requests*/ + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "mali"); + prcmu_qos_remove_requirement(PRCMU_QOS_DDR_OPP, "mali"); + MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u SIGNAL_LOW\n", last_utilization)); + has_requested_low = 1; + } + } + } + MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u\n", last_utilization)); +} + +_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource) +{ + is_running = 0; + last_utilization = 0; + + mali_utilization_workqueue = create_singlethread_workqueue("mali_utilization_workqueue"); + if (NULL == mali_utilization_workqueue) { + MALI_DEBUG_PRINT(2, ("%s: Failed to setup workqueue %s\n", __func__, "v-mali")); + goto error; + } + INIT_WORK(&mali_utilization_work, mali_utilization_function); + + regulator = regulator_get(NULL, "v-mali"); + if (IS_ERR(regulator)) { + MALI_DEBUG_PRINT(2, ("%s: Failed to get regulator %s\n", __func__, "v-mali")); + goto error; + } + + clk_sga = clk_get_sys("mali", NULL); + if (IS_ERR(clk_sga)) { + regulator_put(regulator); + MALI_DEBUG_PRINT(2, ("%s: Failed to get clock %s\n", __func__, "mali")); + goto error; + } + + MALI_SUCCESS; +error: + MALI_DEBUG_PRINT(1, ("SGA initialization failed.\n")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type) +{ + destroy_workqueue(mali_utilization_workqueue); + regulator_put(regulator); + clk_put(clk_sga); + MALI_DEBUG_PRINT(2, ("SGA terminated.\n")); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerdown(u32 cores) +{ + if (is_running) { + clk_disable(clk_sga); + if (regulator) { + int ret = regulator_disable(regulator); + if (ret < 0) { + MALI_DEBUG_PRINT(2, ("%s: Failed to disable regulator %s\n", __func__, "v-mali")); + is_running = 0; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + } + is_running = 0; + } + MALI_DEBUG_PRINT(4, ("mali_platform_powerdown is_running: %u cores: %u\n", is_running, cores)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerup(u32 cores) +{ + if (!is_running) { + int ret = regulator_enable(regulator); + if (ret < 0) { + MALI_DEBUG_PRINT(2, ("%s: Failed to enable regulator %s\n", __func__, "v-mali")); + goto error; + } + + ret = clk_enable(clk_sga); + if (ret < 0) { + regulator_disable(regulator); + MALI_DEBUG_PRINT(2, ("%s: Failed to enable clock %s\n", __func__, "mali")); + goto error; + } + is_running = 1; + } + MALI_DEBUG_PRINT(4, ("mali_platform_powerup is_running:%u cores: %u\n", is_running, cores)); + MALI_SUCCESS; +error: + MALI_DEBUG_PRINT(1, ("Failed to power up.\n")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +void mali_gpu_utilization_handler(u32 utilization) +{ + last_utilization = utilization; + /* + * We should not cancel the potentially not yet run old work + * in favor of a new work. + * Since the utilization value will change, + * the mali_utilization_function will evaluate based on + * what is the utilization now and not on what it was + * when it was scheduled. + */ + queue_work(mali_utilization_workqueue, &mali_utilization_work); +} + + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c new file mode 100644 index 00000000000..b321b504839 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Magnus Wendt for + * ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include "ump_kernel_types.h" +#include "mali_kernel_common.h" + +#include +#include + + +/* The UMP kernel API for hwmem has been mapped so that + * ump_dd_handle == hwmem_alloc + * ump_secure_id == hwmem global name + * + * The current implementation is limited to contiguous memory + */ + +ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) +{ + int hwmem_name = hwmem_get_name((struct hwmem_alloc *) memh); + + if (unlikely(hwmem_name < 0)) { + MALI_DEBUG_PRINT(1, ("%s: Invalid Alloc 0x%x\n",__func__, memh)); + return UMP_INVALID_SECURE_ID; + } + + return (ump_secure_id)hwmem_name; +} + + + +ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + struct hwmem_alloc *alloc; + enum hwmem_mem_type mem_type; + enum hwmem_access access; + + alloc = hwmem_resolve_by_name((int) secure_id); + + if (IS_ERR(alloc)) { + MALI_DEBUG_PRINT(1, ("%s: Invalid UMP id %d\n",__func__, secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + hwmem_get_info(alloc, NULL, &mem_type, &access); + + if (unlikely((access & (HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE | HWMEM_ACCESS_IMPORT)) != + (HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE | HWMEM_ACCESS_IMPORT))) { + MALI_DEBUG_PRINT(1, ("%s: Access denied on UMP id %d, (access==%d)\n", + __func__, secure_id, access)); + hwmem_release(alloc); + return UMP_DD_HANDLE_INVALID; + } + + if (unlikely(HWMEM_MEM_CONTIGUOUS_SYS != mem_type)) { + MALI_DEBUG_PRINT(1, ("%s: UMP id %d is non-contiguous! (not supported)\n", + __func__, secure_id)); + hwmem_release(alloc); + return UMP_DD_HANDLE_INVALID; + } + + return (ump_dd_handle)alloc; +} + + + +unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) +{ + return 1; +} + + + +ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, + ump_dd_physical_block * blocks, + unsigned long num_blocks) +{ + struct hwmem_mem_chunk hwmem_mem_chunk; + size_t hwmem_mem_chunk_length = 1; + + int hwmem_result; + struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh; + + if (unlikely(blocks == NULL)) { + MALI_DEBUG_PRINT(1, ("%s: blocks == NULL\n",__func__)); + return UMP_DD_INVALID; + } + + if (unlikely(1 != num_blocks)) { + MALI_DEBUG_PRINT(1, ("%s: num_blocks == %d (!= 1)\n",__func__, num_blocks)); + return UMP_DD_INVALID; + } + + MALI_DEBUG_PRINT(5, ("Returning physical block information. Alloc: 0x%x\n", memh)); + + /* It might not look natural to pin here, but it matches the usage by the mali kernel module */ + hwmem_result = hwmem_pin(alloc, &hwmem_mem_chunk, &hwmem_mem_chunk_length); + + if (unlikely(hwmem_result < 0)) { + MALI_DEBUG_PRINT(1, ("%s: Pin failed. Alloc: 0x%x\n",__func__, memh)); + return UMP_DD_INVALID; + } + + blocks[0].addr = hwmem_mem_chunk.paddr; + blocks[0].size = hwmem_mem_chunk.size; + + hwmem_set_domain(alloc, HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE, + HWMEM_DOMAIN_SYNC, NULL); + + return UMP_DD_SUCCESS; +} + + + +ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, + unsigned long index, + ump_dd_physical_block * block) +{ + if (unlikely(0 != index)) { + MALI_DEBUG_PRINT(1, ("%s: index == %d (!= 0)\n",__func__, index)); + return UMP_DD_INVALID; + } + return ump_dd_phys_blocks_get(memh, block, 1); +} + + + +unsigned long ump_dd_size_get(ump_dd_handle memh) +{ + struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh; + int size; + + hwmem_get_info(alloc, &size, NULL, NULL); + + return size; +} + + + +void ump_dd_reference_add(ump_dd_handle memh) +{ + /* This is never called from tha mali kernel driver */ +} + + + +void ump_dd_reference_release(ump_dd_handle memh) +{ + struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh; + + hwmem_unpin(alloc); + hwmem_release(alloc); + + return; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/readme.txt b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/readme.txt new file mode 100644 index 00000000000..af40fddd20f --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/readme.txt @@ -0,0 +1,28 @@ +Building the Mali Device Driver for Linux +----------------------------------------- + +Build the Mali Device Driver for Linux by running the following make command: + +KDIR= USING_MMU= USING_UMP= \ +BUILD= CONFIG= make + +where + kdir_path: Path to your Linux Kernel directory + mmu_option: 1 = Mali MMU(s) on + 0 = Mali MMU(s) off + ump_option: 1 = Enable UMP support(*) + 0 = disable UMP support + build_option: debug = debug build of driver + release = release build of driver + your_config: Name of the sub-folder to find the required config.h(**) file + ("arch-" will be prepended) + +(*) For newer Linux Kernels, the Module.symvers file for the UMP device driver + must be available. The UMP_SYMVERS_FILE variable in the Makefile should + point to this file. This file is generated when the UMP driver is built. + +(**) The config.h file contains the configuration parameters needed, like where the + Mali GPU is located, interrupts it uses, memory and so on. + +The result will be a mali.ko file, which can be loaded into the Linux kernel +by using the insmod command. diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_200_regs.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_200_regs.h new file mode 100644 index 00000000000..6b30802bb2d --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_200_regs.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI200_REGS_H_ +#define _MALI200_REGS_H_ + +/** + * Enum for management register addresses. + */ +enum mali200_mgmt_reg +{ + MALI200_REG_ADDR_MGMT_VERSION = 0x1000, + MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR = 0x1004, + MALI200_REG_ADDR_MGMT_STATUS = 0x1008, + MALI200_REG_ADDR_MGMT_CTRL_MGMT = 0x100c, + + MALI200_REG_ADDR_MGMT_INT_RAWSTAT = 0x1020, + MALI200_REG_ADDR_MGMT_INT_CLEAR = 0x1024, + MALI200_REG_ADDR_MGMT_INT_MASK = 0x1028, + MALI200_REG_ADDR_MGMT_INT_STATUS = 0x102c, + + MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW = 0x1044, + + MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS = 0x1050, + + MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x1080, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x1084, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x108c, + + MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x10a0, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x10a4, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x10ac, + + MALI200_REG_SIZEOF_REGISTER_BANK = 0x10f0 + +}; + +#define MALI200_REG_VAL_PERF_CNT_ENABLE 1 + +enum mali200_mgmt_ctrl_mgmt { + MALI200_REG_VAL_CTRL_MGMT_STOP_BUS = (1<<0), +#if defined(USING_MALI200) + MALI200_REG_VAL_CTRL_MGMT_FLUSH_CACHES = (1<<3), +#endif + MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET = (1<<5), + MALI200_REG_VAL_CTRL_MGMT_START_RENDERING = (1<<6), +#if defined(USING_MALI400) + MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET = (1<<7), +#endif +}; + +enum mali200_mgmt_irq { + MALI200_REG_VAL_IRQ_END_OF_FRAME = (1<<0), + MALI200_REG_VAL_IRQ_END_OF_TILE = (1<<1), + MALI200_REG_VAL_IRQ_HANG = (1<<2), + MALI200_REG_VAL_IRQ_FORCE_HANG = (1<<3), + MALI200_REG_VAL_IRQ_BUS_ERROR = (1<<4), + MALI200_REG_VAL_IRQ_BUS_STOP = (1<<5), + MALI200_REG_VAL_IRQ_CNT_0_LIMIT = (1<<6), + MALI200_REG_VAL_IRQ_CNT_1_LIMIT = (1<<7), + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR = (1<<8), + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND = (1<<9), + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW = (1<<10), + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW = (1<<11), + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED = (1<<12), +}; + +#if defined USING_MALI200 +#define MALI200_REG_VAL_IRQ_MASK_ALL ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_END_OF_TILE |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_CNT_0_LIMIT |\ + MALI200_REG_VAL_IRQ_CNT_1_LIMIT |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR)) +#elif defined USING_MALI400 +#define MALI200_REG_VAL_IRQ_MASK_ALL ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_END_OF_TILE |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_CNT_0_LIMIT |\ + MALI200_REG_VAL_IRQ_CNT_1_LIMIT |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW |\ + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED)) +#else +#error "No supported mali core defined" +#endif + +#if defined USING_MALI200 +#define MALI200_REG_VAL_IRQ_MASK_USED ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR)) +#elif defined USING_MALI400 +#define MALI200_REG_VAL_IRQ_MASK_USED ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW)) +#else +#error "No supported mali core defined" +#endif + +#define MALI200_REG_VAL_IRQ_MASK_NONE ((enum mali200_mgmt_irq)(0)) + +enum mali200_mgmt_status { + MALI200_REG_VAL_STATUS_RENDERING_ACTIVE = (1<<0), + MALI200_REG_VAL_STATUS_BUS_STOPPED = (1<<4), +}; + +enum mali200_render_unit +{ + MALI200_REG_ADDR_FRAME = 0x0000, +}; + +#if defined USING_MALI200 +#define MALI200_NUM_REGS_FRAME ((0x04C/4)+1) +#elif defined USING_MALI400 +#define MALI200_NUM_REGS_FRAME ((0x058/4)+1) +#else +#error "No supported mali core defined" +#endif + +enum mali200_wb_unit { + MALI200_REG_ADDR_WB0 = 0x0100, + MALI200_REG_ADDR_WB1 = 0x0200, + MALI200_REG_ADDR_WB2 = 0x0300 +}; + +/** The number of registers in one single writeback unit */ +#ifndef MALI200_NUM_REGS_WBx +#define MALI200_NUM_REGS_WBx ((0x02C/4)+1) +#endif + +/* This should be in the top 16 bit of the version register of Mali PP */ +#if defined USING_MALI200 +#define MALI_PP_PRODUCT_ID 0xC807 +#elif defined USING_MALI400 +#define MALI300_PP_PRODUCT_ID 0xCE07 +#define MALI400_PP_PRODUCT_ID 0xCD07 +#define MALI_PP_PRODUCT_ID MALI400_PP_PRODUCT_ID +#else +#error "No supported mali core defined" +#endif + + +#endif /* _MALI200_REGS_H_ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_gp_regs.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_gp_regs.h new file mode 100644 index 00000000000..11eb55c424e --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/regs/mali_gp_regs.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALIGP2_CONROL_REGS_H_ +#define _MALIGP2_CONROL_REGS_H_ + +/** + * These are the different geometry processor controll registers. + * Their usage is to control and monitor the operation of the + * Vertex Shader and the Polygon List Builer in the geometry processor. + * Addresses are in 32-bit word relative sizes. + * @see [P0081] "Geometry Processor Data Structures" for details + */ + +typedef enum { + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR = 0x00, + MALIGP2_REG_ADDR_MGMT_VSCL_END_ADDR = 0x04, + MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR = 0x08, + MALIGP2_REG_ADDR_MGMT_PLBUCL_END_ADDR = 0x0c, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR = 0x10, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR = 0x14, + MALIGP2_REG_ADDR_MGMT_CMD = 0x20, + MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT = 0x24, + MALIGP2_REG_ADDR_MGMT_INT_CLEAR = 0x28, + MALIGP2_REG_ADDR_MGMT_INT_MASK = 0x2C, + MALIGP2_REG_ADDR_MGMT_INT_STAT = 0x30, + MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW = 0x34, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x3C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x40, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x44, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x48, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x4C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x50, + MALIGP2_REG_ADDR_MGMT_STATUS = 0x68, + MALIGP2_REG_ADDR_MGMT_VERSION = 0x6C, + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR_READ = 0x80, + MALIGP2_REG_ADDR_MGMT_PLBCL_START_ADDR_READ = 0x84, + MALIGP2_CONTR_AXI_BUS_ERROR_STAT = 0x94, + MALIGP2_REGISTER_ADDRESS_SPACE_SIZE = 0x98, +} maligp_reg_addr_mgmt_addr; + +#define MALIGP2_REG_VAL_PERF_CNT_ENABLE 1 + +/** + * Commands to geometry processor. + * @see MALIGP2_CTRL_REG_CMD + */ +typedef enum +{ + MALIGP2_REG_VAL_CMD_START_VS = (1<< 0), + MALIGP2_REG_VAL_CMD_START_PLBU = (1<< 1), + MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC = (1<< 4), + MALIGP2_REG_VAL_CMD_RESET = (1<< 5), + MALIGP2_REG_VAL_CMD_FORCE_HANG = (1<< 6), + MALIGP2_REG_VAL_CMD_STOP_BUS = (1<< 9), +#if defined(USING_MALI400) + MALI400GP_REG_VAL_CMD_SOFT_RESET = (1<<10), +#endif +} mgp_contr_reg_val_cmd; + + +/** @defgroup MALIGP2_IRQ + * Interrupt status of geometry processor. + * @see MALIGP2_CTRL_REG_INT_RAWSTAT, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, + * MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_ADDR_MGMT_INT_STAT + * @{ + */ +#define MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST (1 << 0) +#define MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST (1 << 1) +#define MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM (1 << 2) +#define MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ (1 << 3) +#define MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ (1 << 4) +#define MALIGP2_REG_VAL_IRQ_HANG (1 << 5) +#define MALIGP2_REG_VAL_IRQ_FORCE_HANG (1 << 6) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT (1 << 7) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT (1 << 8) +#define MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR (1 << 9) +#define MALIGP2_REG_VAL_IRQ_SYNC_ERROR (1 << 10) +#define MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR (1 << 11) +#if defined USING_MALI400 +#define MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED (1 << 12) +#define MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD (1 << 13) +#define MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD (1 << 14) +#define MALI400GP_REG_VAL_IRQ_RESET_COMPLETED (1 << 19) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW (1 << 20) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW (1 << 21) +#define MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS (1 << 22) +#elif !defined USING_MALI200 +#error "No supported mali core defined" +#endif + +/* Mask defining all IRQs in MaliGP2 */ +#if defined USING_MALI200 +#define MALIGP2_REG_VAL_IRQ_MASK_ALL \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR) +#elif defined USING_MALI400 +#define MALIGP2_REG_VAL_IRQ_MASK_ALL \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_RESET_COMPLETED | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) +#else +#error "No supported mali core defined" +#endif + +/* Mask defining the IRQs in MaliGP2 which we use*/ +#if defined USING_MALI200 +#define MALIGP2_REG_VAL_IRQ_MASK_USED \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR) +#elif defined USING_MALI400 +#define MALIGP2_REG_VAL_IRQ_MASK_USED \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) +#else +#error "No supported mali core defined" +#endif + +/* Mask defining non IRQs on MaliGP2*/ +#define MALIGP2_REG_VAL_IRQ_MASK_NONE 0 + +/** }@ defgroup MALIGP2_IRQ*/ + +/** @defgroup MALIGP2_STATUS + * The different Status values to the geometry processor. + * @see MALIGP2_CTRL_REG_STATUS + * @{ + */ +#define MALIGP2_REG_VAL_STATUS_VS_ACTIVE 0x0002 +#define MALIGP2_REG_VAL_STATUS_BUS_STOPPED 0x0004 +#define MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE 0x0008 +#define MALIGP2_REG_VAL_STATUS_BUS_ERROR 0x0040 +#define MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR 0x0100 +/** }@ defgroup MALIGP2_STATUS*/ + +#define MALIGP2_REG_VAL_STATUS_MASK_ACTIVE (\ + MALIGP2_REG_VAL_STATUS_VS_ACTIVE|\ + MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) + + +#define MALIGP2_REG_VAL_STATUS_MASK_ERROR (\ + MALIGP2_REG_VAL_STATUS_BUS_ERROR |\ + MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR ) + +/* This should be in the top 16 bit of the version register of gp.*/ +#if defined(USING_MALI200) +#define MALI_GP_PRODUCT_ID 0xA07 +#elif defined(USING_MALI400) +#define MALI300_GP_PRODUCT_ID 0xC07 +#define MALI400_GP_PRODUCT_ID 0xB07 +#define MALI_GP_PRODUCT_ID MALI400_GP_PRODUCT_ID +#else +#error "No supported mali core defined" +#endif + +/** + * The different sources for instrumented on the geometry processor. + * @see MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC + */ + +enum MALIGP2_cont_reg_perf_cnt_src { + MALIGP2_REG_VAL_PERF_CNT1_SRC_NUMBER_OF_VERTICES_PROCESSED = 0x0a, +}; + +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.c new file mode 100644 index 00000000000..a6b1d76d567 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.h new file mode 100644 index 00000000000..96b709bc166 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-arm11-cc/mali_timestamp.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + /* + * reset counters and overflow flags + */ + + u32 mask = (1 << 0) | /* enable all three counters */ + (0 << 1) | /* reset both Count Registers to 0x0 */ + (1 << 2) | /* reset the Cycle Counter Register to 0x0 */ + (0 << 3) | /* 1 = Cycle Counter Register counts every 64th processor clock cycle */ + (0 << 4) | /* Count Register 0 interrupt enable */ + (0 << 5) | /* Count Register 1 interrupt enable */ + (0 << 6) | /* Cycle Counter interrupt enable */ + (0 << 8) | /* Count Register 0 overflow flag (clear or write, flag on read) */ + (0 << 9) | /* Count Register 1 overflow flag (clear or write, flag on read) */ + (1 << 10); /* Cycle Counter Register overflow flag (clear or write, flag on read) */ + + __asm__ __volatile__ ("MCR p15, 0, %0, c15, c12, 0" : : "r" (mask) ); + + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + u32 result; + + /* this is for the clock cycles */ + __asm__ __volatile__ ("MRC p15, 0, %0, c15, c12, 1" : "=r" (result)); + + return (u64)result; +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.c new file mode 100644 index 00000000000..a6b1d76d567 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.h new file mode 100644 index 00000000000..11031d675ca --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/timestamp-default/mali_timestamp.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + return _mali_osk_time_get_ns(); +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile new file mode 100644 index 00000000000..97225bbbf0b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile @@ -0,0 +1,115 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +UMP_FILE_PREFIX = +UDD_FILE_PREFIX = ../mali/ + +ifneq ($(KBUILD_EXTMOD),) +include $(KBUILD_EXTMOD)/Makefile.common +else +include ./Makefile.common +endif + +# For each arch check: CROSS_COMPILE , KDIR , CFLAGS += -DARCH + +ARCH ?= arm +## @note Should allow overriding of building UMP for non-debug: +EXTRA_CFLAGS += -DDEBUG -DMALI_STATE_TRACKING=0 + +# linux build system integration + +ifneq ($(KERNELRELEASE),) +# Inside the kernel build system + +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) -I$(KBUILD_EXTMOD)/common -I$(KBUILD_EXTMOD)/linux -I$(KBUILD_EXTMOD)/../mali/common -I$(KBUILD_EXTMOD)/../mali/linux -I$(KBUILD_EXTMOD)/../../ump/include/ump + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +ifeq ($(wildcard $(KBUILD_EXTMOD)/linux/license/gpl/*),) +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD)/linux/license/proprietary +else +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD)/linux/license/gpl +endif + +SRC += $(UMP_FILE_PREFIX)linux/ump_kernel_linux.c \ + $(UMP_FILE_PREFIX)linux/ump_kernel_memory_backend_os.c \ + $(UMP_FILE_PREFIX)linux/ump_kernel_memory_backend_dedicated.c \ + $(UMP_FILE_PREFIX)linux/ump_memory_backend.c \ + $(UMP_FILE_PREFIX)linux/ump_ukk_wrappers.c \ + $(UMP_FILE_PREFIX)linux/ump_ukk_ref_wrappers.c \ + $(UMP_FILE_PREFIX)linux/ump_osk_atomics.c \ + $(UMP_FILE_PREFIX)linux/ump_osk_low_level_mem.c \ + $(UMP_FILE_PREFIX)linux/ump_osk_misc.c \ + $(UDD_FILE_PREFIX)linux/mali_osk_atomics.c \ + $(UDD_FILE_PREFIX)linux/mali_osk_locks.c \ + $(UDD_FILE_PREFIX)linux/mali_osk_memory.c \ + $(UDD_FILE_PREFIX)linux/mali_osk_math.c \ + $(UDD_FILE_PREFIX)linux/mali_osk_misc.c + +# Selecting files to compile by parsing the config file + +MODULE:=ump.ko + +obj-m := $(MODULE:.ko=.o) +$(MODULE:.ko=-y) := $(SRC:.c=.o) + +else +# Outside the kernel build system + +# Get any user defined KDIR- or maybe even a hardcoded KDIR +-include KDIR_CONFIGURATION + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +ifeq ($(ARCH), arm) + # when compiling for ARM we're cross compiling + export CROSS_COMPILE ?= arm-none-linux-gnueabi- + # default to Virtex5 + CONFIG ?= pb-virtex5 +else + # Compiling for the host + CONFIG ?= $(shell uname -m) +endif + +# default cpu to select +CPU ?= ct11mp + +# look up KDIR based om CPU selection +KDIR ?= $(KDIR-$(CPU)) + +ifeq ($(KDIR),) +$(error No KDIR found for platform $(CPU)) +endif + +# Validate selected config +ifneq ($(shell [ -d arch-$(CONFIG) ] && [ -f arch-$(CONFIG)/config.h ] && echo "OK"), OK) +$(warning Current directory is $(shell pwd)) +$(error No configuration found for config $(CONFIG). Check that arch-$(CONFIG)/config.h exists) +else +# Link arch to the selected arch-config directory +$(shell [ -L arch ] && rm arch) +$(shell ln -sf arch-$(CONFIG) arch) +$(shell touch arch/config.h) +endif + +all: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) modules + +kernelrelease: + $(MAKE) -C $(KDIR) kernelrelease + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR)/../mali clean + +endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common new file mode 100644 index 00000000000..c6aa6337f80 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common @@ -0,0 +1,20 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +SRC = $(UMP_FILE_PREFIX)common/ump_kernel_common.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_descriptor_mapping.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_api.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.c + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_REV:=$(shell ((svnversion | grep -qv exported && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') + +EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) +EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/arch-pb-virtex5/config.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/arch-pb-virtex5/config.h new file mode 100644 index 00000000000..38ae1eeb3c2 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/arch-pb-virtex5/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT 0 +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xCE000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT 32UL * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_api.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_api.c new file mode 100644 index 00000000000..719370c3de7 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_api.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_common.h" + + + +/* ---------------- UMP kernel space API functions follows ---------------- */ + + + +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id)); + + return mem->secure_id; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + ump_dd_mem * mem; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); + if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + ump_dd_reference_add(mem); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return (ump_dd_handle)mem; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*) memh; + + DEBUG_ASSERT_POINTER(mem); + + return mem->nr_blocks; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (blocks == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n")); + return UMP_DD_INVALID; + } + + if (mem->nr_blocks != num_blocks) + { + DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id)); + + _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks); + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block * block) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (block == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + if (index >= mem->nr_blocks) + { + DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index)); + + *block = mem->block_array[index]; + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return mem->size_bytes; +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + int new_ref; + + DEBUG_ASSERT_POINTER(mem); + + new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count); + + DBG_MSG(4, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh) +{ + int new_ref; + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + /* We must hold this mutex while doing the atomic_dec_and_read, to protect + that elements in the ump_descriptor_mapping table is always valid. If they + are not, userspace may accidently map in this secure_ids right before its freed + giving a mapped backdoor into unallocated memory.*/ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); + + DBG_MSG(4, ("Memory reference decremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); + + if (0 == new_ref) + { + DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); + + ump_descriptor_mapping_free(device.secure_id_map, (int)mem->secure_id); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + mem->release_func(mem->ctx, mem); + _mali_osk_free(mem); + } + else + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + } +} + + + +/* --------------- Handling of user space requests follows --------------- */ + + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ) +{ + ump_session_data * session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + /* check compatability */ + if (args->version == UMP_IOCTL_API_VERSION) + { + DBG_MSG(3, ("API version set to newest %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else if (args->version == MAKE_VERSION_ID(1)) + { + DBG_MSG(2, ("API version set to depricated: %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else + { + DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n", GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version))); + args->compatible = 0; + args->version = UMP_IOCTL_API_VERSION; /* report our version */ + } + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ) +{ + ump_session_memory_list_element * session_memory_element; + ump_session_memory_list_element * tmp; + ump_session_data * session_data; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC; + int secure_id; + + DEBUG_ASSERT_POINTER( release_info ); + DEBUG_ASSERT_POINTER( release_info->ctx ); + + /* Retreive the session data */ + session_data = (ump_session_data*)release_info->ctx; + + /* If there are many items in the memory session list we + * could be de-referencing this pointer a lot so keep a local copy + */ + secure_id = release_info->secure_id; + + DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id)); + + /* Iterate through the memory list looking for the requested secure ID */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + if ( session_memory_element->mem->secure_id == secure_id) + { + ump_dd_mem *release_mem; + + release_mem = session_memory_element->mem; + _mali_osk_list_del(&session_memory_element->list); + ump_dd_reference_release(release_mem); + _mali_osk_free(session_memory_element); + + ret = _MALI_OSK_ERR_OK; + break; + } + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id)); + + DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret)); + return ret; +} + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ) +{ + ump_dd_mem * mem; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT; + + DEBUG_ASSERT_POINTER( user_interaction ); + + /* We lock the mappings so things don't get removed while we are looking for the memory */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)user_interaction->secure_id, (void**)&mem)) + { + user_interaction->size = mem->size_bytes; + DBG_MSG(4, ("Returning size. ID: %u, size: %lu ", (ump_secure_id)user_interaction->secure_id, (unsigned long)user_interaction->size)); + ret = _MALI_OSK_ERR_OK; + } + else + { + user_interaction->size = 0; + DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n", (ump_secure_id)user_interaction->secure_id)); + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + return ret; +} + + + +void _ump_ukk_msync( _ump_uk_msync_s *args ) +{ + ump_dd_mem * mem = NULL; + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + if (NULL==mem) + { + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + + /* Returns the cache settings back to Userspace */ + args->is_cached=mem->is_cached; + + /* If this flag is the only one set, we should not do the actual flush, only the readout */ + if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED==args->op ) + { + DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached)); + return; + } + + /* Nothing to do if the memory is not caches */ + if ( 0==mem->is_cached ) + { + DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); + return ; + } + DBG_MSG(3, ("_ump_ukk_msync FLUSHING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); + + /* The actual cache flush - Implemented for each OS*/ + _ump_osk_msync( mem , args->op); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.c new file mode 100644 index 00000000000..b99c3e7c7d2 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + + +/** + * Define the initial and maximum size of number of secure_ids on the system + */ +#define UMP_SECURE_ID_TABLE_ENTRIES_INITIAL (128 ) +#define UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM (4096 ) + + +/** + * Define the initial and maximum size of the ump_session_data::cookies_map, + * which is a \ref ump_descriptor_mapping. This limits how many secure_ids + * may be mapped into a particular process using _ump_ukk_map_mem(). + */ + +#define UMP_COOKIES_PER_SESSION_INITIAL (UMP_SECURE_ID_TABLE_ENTRIES_INITIAL ) +#define UMP_COOKIES_PER_SESSION_MAXIMUM (UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM) + +struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void) +{ + _mali_osk_errcode_t err; + + /* Perform OS Specific initialization */ + err = _ump_osk_init(); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Failed to initiaze the UMP Device Driver")); + return err; + } + + /* Init the global device */ + _mali_osk_memset(&device, 0, sizeof(device) ); + + /* Create the descriptor map, which will be used for mapping secure ID to ump_dd_mem structs */ + device.secure_id_map_lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0 , 0); + if (NULL == device.secure_id_map_lock) + { + MSG_ERR(("Failed to create OSK lock for secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + device.secure_id_map = ump_descriptor_mapping_create(UMP_SECURE_ID_TABLE_ENTRIES_INITIAL, UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM); + if (NULL == device.secure_id_map) + { + _mali_osk_lock_term(device.secure_id_map_lock); + MSG_ERR(("Failed to create secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Init memory backend */ + device.backend = ump_memory_backend_create(); + if (NULL == device.backend) + { + MSG_ERR(("Failed to create memory backend\n")); + _mali_osk_lock_term(device.secure_id_map_lock); + ump_descriptor_mapping_destroy(device.secure_id_map); + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void ump_kernel_destructor(void) +{ + DEBUG_ASSERT_POINTER(device.secure_id_map); + DEBUG_ASSERT_POINTER(device.secure_id_map_lock); + + _mali_osk_lock_term(device.secure_id_map_lock); + device.secure_id_map_lock = NULL; + + ump_descriptor_mapping_destroy(device.secure_id_map); + device.secure_id_map = NULL; + + device.backend->shutdown(device.backend); + device.backend = NULL; + + ump_memory_backend_destroy(); + + _ump_osk_term(); +} + +/** Creates a new UMP session + */ +_mali_osk_errcode_t _ump_ukk_open( void** context ) +{ + struct ump_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct ump_session_data *)_mali_osk_malloc(sizeof(struct ump_session_data)); + if (NULL == session_data) + { + MSG_ERR(("Failed to allocate ump_session_data in ump_file_open()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0); + if( NULL == session_data->lock ) + { + MSG_ERR(("Failed to initialize lock for ump_session_data in ump_file_open()\n")); + _mali_osk_free(session_data); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->cookies_map = ump_descriptor_mapping_create( UMP_COOKIES_PER_SESSION_INITIAL, UMP_COOKIES_PER_SESSION_MAXIMUM ); + + if ( NULL == session_data->cookies_map ) + { + MSG_ERR(("Failed to create descriptor mapping for _ump_ukk_map_mem cookies\n")); + + _mali_osk_lock_term( session_data->lock ); + _mali_osk_free( session_data ); + return _MALI_OSK_ERR_NOMEM; + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_list); + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_mappings_list); + + /* Since initial version of the UMP interface did not use the API_VERSION ioctl we have to assume + that it is this version, and not the "latest" one: UMP_IOCTL_API_VERSION + Current and later API versions would do an additional call to this IOCTL and update this variable + to the correct one.*/ + session_data->api_version = MAKE_VERSION_ID(1); + + *context = (void*)session_data; + + DBG_MSG(2, ("New session opened\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_close( void** context ) +{ + struct ump_session_data * session_data; + ump_session_memory_list_element * item; + ump_session_memory_list_element * tmp; + + session_data = (struct ump_session_data *)*context; + if (NULL == session_data) + { + MSG_ERR(("Session data is NULL in _ump_ukk_close()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + /* Unmap any descriptors mapped in. */ + if (0 == _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list)) + { + ump_memory_allocation *descriptor; + ump_memory_allocation *temp; + + DBG_MSG(1, ("Memory mappings found on session usage list during session termination\n")); + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->list_head_session_memory_mappings_list, ump_memory_allocation, list) + { + _ump_uk_unmap_mem_s unmap_args; + DBG_MSG(4, ("Freeing block with phys address 0x%x size 0x%x mapped in user space at 0x%x\n", + descriptor->phys_addr, descriptor->size, descriptor->mapping)); + unmap_args.ctx = (void*)session_data; + unmap_args.mapping = descriptor->mapping; + unmap_args.size = descriptor->size; + unmap_args._ukk_private = NULL; /* NOTE: unused */ + unmap_args.cookie = descriptor->cookie; + + /* NOTE: This modifies the list_head_session_memory_mappings_list */ + _ump_ukk_unmap_mem( &unmap_args ); + } + } + + /* ASSERT that we really did free everything, because _ump_ukk_unmap_mem() + * can fail silently. */ + DEBUG_ASSERT( _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list) ); + + _MALI_OSK_LIST_FOREACHENTRY(item, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + _mali_osk_list_del(&item->list); + DBG_MSG(2, ("Releasing UMP memory %u as part of file close\n", item->mem->secure_id)); + ump_dd_reference_release(item->mem); + _mali_osk_free(item); + } + + ump_descriptor_mapping_destroy( session_data->cookies_map ); + + _mali_osk_lock_term(session_data->lock); + _mali_osk_free(session_data); + + DBG_MSG(2, ("Session closed\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; /* Describes current mapping of memory */ + _mali_osk_errcode_t err; + unsigned long offset = 0; + unsigned long left; + ump_dd_handle handle; /* The real UMP handle for this memory. Its real datatype is ump_dd_mem* */ + ump_dd_mem * mem; /* The real UMP memory. It is equal to the handle, but with exposed struct */ + u32 block; + int map_id; + + session_data = (ump_session_data *)args->ctx; + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + descriptor = (ump_memory_allocation*) _mali_osk_calloc( 1, sizeof(ump_memory_allocation)); + if (NULL == descriptor) + { + MSG_ERR(("ump_ukk_map_mem: descriptor allocation failed\n")); + return _MALI_OSK_ERR_NOMEM; + } + + handle = ump_dd_handle_create_from_secure_id(args->secure_id); + if ( UMP_DD_HANDLE_INVALID == handle) + { + _mali_osk_free(descriptor); + DBG_MSG(1, ("Trying to map unknown secure ID %u\n", args->secure_id)); + return _MALI_OSK_ERR_FAULT; + } + + mem = (ump_dd_mem*)handle; + DEBUG_ASSERT(mem); + if (mem->size_bytes != args->size) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("Trying to map too much or little. ID: %u, virtual size=%lu, UMP size: %lu\n", args->secure_id, args->size, mem->size_bytes)); + return _MALI_OSK_ERR_FAULT; + } + + map_id = ump_descriptor_mapping_allocate_mapping( session_data->cookies_map, (void*) descriptor ); + + if (map_id < 0) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("ump_ukk_map_mem: unable to allocate a descriptor_mapping for return cookie\n")); + + return _MALI_OSK_ERR_NOMEM; + } + + descriptor->size = args->size; + descriptor->handle = handle; + descriptor->phys_addr = args->phys_addr; + descriptor->process_mapping_info = args->_ukk_private; + descriptor->ump_session = session_data; + descriptor->cookie = (u32)map_id; + + if ( mem->is_cached ) + { + descriptor->is_cached = 1; + args->is_cached = 1; + DBG_MSG(3, ("Mapping UMP secure_id: %d as cached.\n", args->secure_id)); + } + else + { + descriptor->is_cached = 0; + args->is_cached = 0; + DBG_MSG(3, ("Mapping UMP secure_id: %d as Uncached.\n", args->secure_id)); + } + + _mali_osk_list_init( &descriptor->list ); + + err = _ump_osk_mem_mapregion_init( descriptor ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("Failed to initialize memory mapping in _ump_ukk_map_mem(). ID: %u\n", args->secure_id)); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + _mali_osk_free(descriptor); + ump_dd_reference_release(mem); + return err; + } + + DBG_MSG(4, ("Mapping virtual to physical memory: ID: %u, size:%lu, first physical addr: 0x%08lx, number of regions: %lu\n", + mem->secure_id, + mem->size_bytes, + ((NULL != mem->block_array) ? mem->block_array->addr : 0), + mem->nr_blocks)); + + left = descriptor->size; + /* loop over all blocks and map them in */ + for (block = 0; block < mem->nr_blocks; block++) + { + unsigned long size_to_map; + + if (left > mem->block_array[block].size) + { + size_to_map = mem->block_array[block].size; + } + else + { + size_to_map = left; + } + + if (_MALI_OSK_ERR_OK != _ump_osk_mem_mapregion_map(descriptor, offset, (u32 *)&(mem->block_array[block].addr), size_to_map ) ) + { + DBG_MSG(1, ("WARNING: _ump_ukk_map_mem failed to map memory into userspace\n")); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + ump_dd_reference_release(mem); + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + return _MALI_OSK_ERR_FAULT; + } + left -= size_to_map; + offset += size_to_map; + } + + /* Add to the ump_memory_allocation tracking list */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add( &descriptor->list, &session_data->list_head_session_memory_mappings_list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + args->mapping = descriptor->mapping; + args->cookie = descriptor->cookie; + + return _MALI_OSK_ERR_OK; +} + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; + ump_dd_handle handle; + + session_data = (ump_session_data *)args->ctx; + + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return; + } + + if (0 != ump_descriptor_mapping_get( session_data->cookies_map, (int)args->cookie, (void**)&descriptor) ) + { + MSG_ERR(("_ump_ukk_map_mem: cookie 0x%X not found for this session\n", args->cookie )); + return; + } + + DEBUG_ASSERT_POINTER(descriptor); + + handle = descriptor->handle; + if ( UMP_DD_HANDLE_INVALID == handle) + { + DBG_MSG(1, ("WARNING: Trying to unmap unknown handle: UNKNOWN\n")); + return; + } + + /* Remove the ump_memory_allocation from the list of tracked mappings */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_del( &descriptor->list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + ump_descriptor_mapping_free( session_data->cookies_map, (int)args->cookie ); + + ump_dd_reference_release(handle); + + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.h new file mode 100644 index 00000000000..0c55b14bc94 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_common.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_H__ +#define __UMP_KERNEL_H__ + +#include "ump_kernel_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + +#ifdef DEBUG + extern int ump_debug_level; + #define UMP_DEBUG_PRINT(args) _mali_osk_dbgmsg args + #define UMP_DEBUG_CODE(args) args + #define DBG_MSG(level,args) do { /* args should be in brackets */ \ + ((level) <= ump_debug_level)?\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")), \ + UMP_DEBUG_PRINT(args):0; \ + } while (0) + + #define DBG_MSG_IF(level,condition,args) /* args should be in brackets */ \ + if((condition)&&((level) <= ump_debug_level)) {\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DBG_MSG_ELSE(level,args) /* args should be in brackets */ \ + else if((level) <= ump_debug_level) { \ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) MSG_ERR(("NULL pointer " #pointer)); } while(0) + #define DEBUG_ASSERT(condition) do {if(!(condition)) MSG_ERR(("ASSERT failed: " #condition)); } while(0) +#else /* DEBUG */ + #define UMP_DEBUG_PRINT(args) do {} while(0) + #define UMP_DEBUG_CODE(args) + #define DBG_MSG(level,args) do {} while(0) + #define DBG_MSG_IF(level,condition,args) do {} while(0) + #define DBG_MSG_ELSE(level,args) do {} while(0) + #define DEBUG_ASSERT(condition) do {} while(0) + #define DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#endif /* DEBUG */ + +#define MSG_ERR(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: ERR: %s\n" ,__FILE__); \ + _mali_osk_dbgmsg( " %s()%4d\n", __FUNCTION__, __LINE__) ; \ + _mali_osk_dbgmsg args ; \ + _mali_osk_dbgmsg("\n"); \ + } while(0) + +#define MSG(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: "); \ + _mali_osk_dbgmsg args; \ + } while (0) + + + +/* + * This struct is used to store per session data. + * A session is created when someone open() the device, and + * closed when someone close() it or the user space application terminates. + */ +typedef struct ump_session_data +{ + _mali_osk_list_t list_head_session_memory_list; /**< List of ump allocations made by the process (elements are ump_session_memory_list_element) */ + _mali_osk_list_t list_head_session_memory_mappings_list; /**< List of ump_memory_allocations mapped in */ + int api_version; + _mali_osk_lock_t * lock; + ump_descriptor_mapping * cookies_map; /**< Secure mapping of cookies from _ump_ukk_map_mem() */ +} ump_session_data; + + + +/* + * This struct is used to track the UMP memory references a session has. + * We need to track this in order to be able to clean up after user space processes + * which don't do it themself (e.g. due to a crash or premature termination). + */ +typedef struct ump_session_memory_list_element +{ + struct ump_dd_mem * mem; + _mali_osk_list_t list; +} ump_session_memory_list_element; + + + +/* + * Device specific data, created when device driver is loaded, and then kept as the global variable device. + */ +typedef struct ump_dev +{ + _mali_osk_lock_t * secure_id_map_lock; + ump_descriptor_mapping * secure_id_map; + ump_memory_backend * backend; +} ump_dev; + + + +extern int ump_debug_level; +extern struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void); +void ump_kernel_destructor(void); +int map_errcode( _mali_osk_errcode_t err ); + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.c new file mode 100644 index 00000000000..2531f802127 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static ump_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(ump_descriptor_table * table); + +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries) +{ + ump_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(ump_descriptor_mapping) ); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { + map->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_READERWRITER, 0 , 0); + if ( NULL != map->lock ) + { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_lock_term( map->lock ); + _mali_osk_free(map); +} + +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target) +{ + int descriptor = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (descriptor == map->current_nr_mappings) + { + int nr_mappings_new; + /* no free descriptor, try to expand the table */ + ump_descriptor_table * new_table; + ump_descriptor_table * old_table = map->table; + nr_mappings_new= map->current_nr_mappings *2; + + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) + { + descriptor = -1; + goto unlock_and_exit; + } + + new_table = descriptor_table_alloc(nr_mappings_new); + if (NULL == new_table) + { + descriptor = -1; + goto unlock_and_exit; + } + + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + map->current_nr_mappings = nr_mappings_new; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(descriptor, map->table->usage); + map->table->mappings[descriptor] = target; + +unlock_and_exit: + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + return descriptor; +} + +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target) +{ + int result = -1;/*-EFAULT;*/ + DEBUG_ASSERT(map); + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = 0; + } + else *target = NULL; + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target) +{ + int result = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = target; + result = 0; + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor) +{ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static ump_descriptor_table * descriptor_table_alloc(int count) +{ + ump_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count) ); + + if (NULL != table) + { + table->usage = (u32*)((u8*)table + sizeof(ump_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(ump_descriptor_table * table) +{ + _mali_osk_free(table); +} + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.h new file mode 100644 index 00000000000..92bbe54bd35 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_descriptor_mapping.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_descriptor_mapping.h + */ + +#ifndef __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct ump_descriptor_table +{ + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} ump_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct ump_descriptor_mapping +{ + _mali_osk_lock_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + ump_descriptor_table * table; /**< Pointer to the current mapping table */ +} ump_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor); + +#endif /* __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_memory_backend.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_memory_backend.h new file mode 100644 index 00000000000..02a64707036 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_memory_backend.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_mapping.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_H__ + +#include "ump_kernel_interface.h" +#include "ump_kernel_types.h" + + +typedef struct ump_memory_allocation +{ + void * phys_addr; + void * mapping; + unsigned long size; + ump_dd_handle handle; + void * process_mapping_info; + u32 cookie; /**< necessary on some U/K interface implementations */ + struct ump_session_data * ump_session; /**< Session that this allocation belongs to */ + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ + u32 is_cached; +} ump_memory_allocation; + +typedef struct ump_memory_backend +{ + int (*allocate)(void* ctx, ump_dd_mem * descriptor); + void (*release)(void* ctx, ump_dd_mem * descriptor); + void (*shutdown)(struct ump_memory_backend * backend); + int (*pre_allocate_physical_check)(void *ctx, u32 size); + u32 (*adjust_to_mali_phys)(void *ctx, u32 cpu_phys); + void * ctx; +} ump_memory_backend; + +ump_memory_backend * ump_memory_backend_create ( void ); +void ump_memory_backend_destroy( void ); + +#endif /*__UMP_KERNEL_MEMORY_BACKEND_H__ */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_ref_drv.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_ref_drv.c new file mode 100644 index 00000000000..5a997e222a7 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_ref_drv.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" + +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define UMP_MINIMUM_SIZE 4096 +#define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) +#define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) +#define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor); + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem; + unsigned long size_total = 0; + int map_id; + u32 i; + + /* Go through the input blocks and verify that they are sane */ + for (i=0; i < num_blocks; i++) + { + unsigned long addr = blocks[i].addr; + unsigned long size = blocks[i].size; + + DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); + size_total += blocks[i].size; + + if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) + { + MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); + return UMP_DD_HANDLE_INVALID; + } + + if (0 != UMP_ADDR_ALIGN_OFFSET(size)) + { + MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); + return UMP_DD_HANDLE_INVALID; + } + } + + /* Allocate the ump_dd_mem struct for this allocation */ + mem = _mali_osk_malloc(sizeof(*mem)); + if (NULL == mem) + { + DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Find a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Now, make a copy of the block information supplied by the user */ + mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks); + if (NULL == mem->block_array) + { + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); + + /* And setup the rest of the ump_dd_mem struct */ + _mali_osk_atomic_init(&mem->ref_count, 1); + mem->secure_id = (ump_secure_id)map_id; + mem->size_bytes = size_total; + mem->nr_blocks = num_blocks; + mem->backend_info = NULL; + mem->ctx = NULL; + mem->release_func = phys_blocks_release; + /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ + mem->is_cached = 0; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return (ump_dd_handle)mem; +} + +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor) +{ + _mali_osk_free(descriptor->block_array); + descriptor->block_array = NULL; +} + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ) +{ + ump_session_data * session_data = NULL; + ump_dd_mem *new_allocation = NULL; + ump_session_memory_list_element * session_memory_element = NULL; + int map_id; + + DEBUG_ASSERT_POINTER( user_interaction ); + DEBUG_ASSERT_POINTER( user_interaction->ctx ); + + session_data = (ump_session_data *) user_interaction->ctx; + + session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); + if (NULL == session_memory_element) + { + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + + new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem)); + if (NULL==new_allocation) + { + _mali_osk_free(session_memory_element); + DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Create a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(session_memory_element); + _mali_osk_free(new_allocation); + DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); + return - _MALI_OSK_ERR_INVALID_FUNC; + } + + /* Initialize the part of the new_allocation that we know so for */ + new_allocation->secure_id = (ump_secure_id)map_id; + _mali_osk_atomic_init(&new_allocation->ref_count,1); + if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) ) + new_allocation->is_cached = 0; + else new_allocation->is_cached = 1; + + /* special case a size of 0, we should try to emulate what malloc does in this case, which is to return a valid pointer that must be freed, but can't be dereferences */ + if (0 == user_interaction->size) + { + user_interaction->size = 1; /* emulate by actually allocating the minimum block size */ + } + + new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */ + + /* Now, ask the active memory backend to do the actual memory allocation */ + if (!device.backend->allocate( device.backend->ctx, new_allocation ) ) + { + DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", new_allocation->size_bytes, (unsigned long)user_interaction->size)); + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(new_allocation); + _mali_osk_free(session_memory_element); + return _MALI_OSK_ERR_INVALID_FUNC; + } + + new_allocation->ctx = device.backend->ctx; + new_allocation->release_func = device.backend->release; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Initialize the session_memory_element, and add it to the session object */ + session_memory_element->mem = new_allocation; + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + user_interaction->secure_id = new_allocation->secure_id; + user_interaction->size = new_allocation->size_bytes; + DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes)); + + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_types.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_types.h new file mode 100644 index 00000000000..dc79b6f289b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_kernel_types.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_TYPES_H__ +#define __UMP_KERNEL_TYPES_H__ + +#include "ump_kernel_interface.h" +#include "mali_osk.h" + +/* + * This struct is what is "behind" a ump_dd_handle + */ +typedef struct ump_dd_mem +{ + ump_secure_id secure_id; + _mali_osk_atomic_t ref_count; + unsigned long size_bytes; + unsigned long nr_blocks; + ump_dd_physical_block * block_array; + void (*release_func)(void * ctx, struct ump_dd_mem * descriptor); + void * ctx; + void * backend_info; + int is_cached; +} ump_dd_mem; + + + +#endif /* __UMP_KERNEL_TYPES_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_osk.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_osk.h new file mode 100644 index 00000000000..73284f02b47 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_osk.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk.h + * Defines the OS abstraction layer for the UMP kernel device driver (OSK) + */ + +#ifndef __UMP_OSK_H__ +#define __UMP_OSK_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +_mali_osk_errcode_t _ump_osk_init( void ); + +_mali_osk_errcode_t _ump_osk_term( void ); + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ); + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation *descriptor ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ); + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ); + +void _ump_osk_msync( ump_dd_mem * mem, ump_uk_msync_op op ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_uk_types.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_uk_types.h new file mode 100644 index 00000000000..b08335f61f1 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_uk_types.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __UMP_UK_TYPES_H__ +#define __UMP_UK_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Helpers for API version handling */ +#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) +#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) +#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) +#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * So for version 1 the value would be 0x00010001 + */ +#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2) + +typedef enum +{ + _UMP_IOC_QUERY_API_VERSION = 1, + _UMP_IOC_ALLOCATE, + _UMP_IOC_RELEASE, + _UMP_IOC_SIZE_GET, + _UMP_IOC_MAP_MEM, /* not used in Linux */ + _UMP_IOC_UNMAP_MEM, /* not used in Linux */ + _UMP_IOC_MSYNC, +}_ump_uk_functions; + +typedef enum +{ + UMP_REF_DRV_UK_CONSTRAINT_NONE = 0, + UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1, + UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4, +} ump_uk_alloc_constraints; + +typedef enum +{ + _UMP_UK_MSYNC_CLEAN = 0, + _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1, + _UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128, +} ump_uk_msync_op; + +/** + * Get API version ([in,out] u32 api_version, [out] u32 compatible) + */ +typedef struct _ump_uk_api_version_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */ + u32 compatible; /**< Non-null if the device is compatible with the client */ +} _ump_uk_api_version_s; + +/** + * ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints) + */ +typedef struct _ump_uk_allocate_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Return value from DD to Userdriver */ + u32 size; /**< Input and output. Requested size; input. Returned size; output */ + ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ +} _ump_uk_allocate_s; + +/** + * SIZE_GET ([in] u32 secure_id, [out]size ) + */ +typedef struct _ump_uk_size_get_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ + u32 size; /**< Returned size; output */ +} _ump_uk_size_get_s; + +/** + * Release ([in] u32 secure_id) + */ +typedef struct _ump_uk_release_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ +} _ump_uk_release_s; + +typedef struct _ump_uk_map_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + void *phys_addr; /**< [in] physical address */ + unsigned long size; /**< [in] size */ + u32 secure_id; /**< [in] secure_id to assign to mapping */ + void * _ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */ + u32 cookie; + u32 is_cached; /**< [in,out] caching of CPU mappings */ +} _ump_uk_map_mem_s; + +typedef struct _ump_uk_unmap_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; + u32 size; + void * _ukk_private; + u32 cookie; +} _ump_uk_unmap_mem_s; + +typedef struct _ump_uk_msync_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] mapping addr */ + void *address; /**< [in] flush start addr */ + u32 size; /**< [in] size to flush */ + ump_uk_msync_op op; /**< [in] flush operation */ + u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 secure_id; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 is_cached; /**< [out] caching of CPU mappings */ +} _ump_uk_msync_s; + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UK_TYPES_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_ukk.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_ukk.h new file mode 100644 index 00000000000..a3317fc7b21 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/common/ump_ukk.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __UMP_UKK_H__ +#define __UMP_UKK_H__ + +#include "mali_osk.h" +#include "ump_uk_types.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +_mali_osk_errcode_t _ump_ukk_open( void** context ); + +_mali_osk_errcode_t _ump_ukk_close( void** context ); + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ); + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ); + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ); + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ); + +void _ump_ukk_msync( _ump_uk_msync_s *args ); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/license/gpl/ump_kernel_license.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/license/gpl/ump_kernel_license.h new file mode 100644 index 00000000000..17b930d2c57 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/license/gpl/ump_kernel_license.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __UMP_KERNEL_LICENSE_H__ +#define __UMP_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define UMP_KERNEL_LINUX_LICENSE "GPL" +#define UMP_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ioctl.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ioctl.h new file mode 100644 index 00000000000..b11429816ab --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ioctl.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_IOCTL_H__ +#define __UMP_IOCTL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "../common/ump_uk_types.h" + +#ifndef __user +#define __user +#endif + + +/** + * @file UMP_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace UMP driver. + */ + +#define UMP_IOCTL_NR 0x90 + + +#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) +#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) +#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) +#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) +#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_size_get_s) + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.c new file mode 100644 index 00000000000..76d6b2f913a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* file system operations */ +#include /* character device definitions */ +#include /* request_mem_region */ +#include /* memory management functions and types */ +#include /* user space access */ +#include +#include +#include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ +#include "ump_ioctl.h" +#include "ump_kernel_common.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" +#include "ump_kernel_license.h" + +#include "ump_osk.h" +#include "ump_ukk.h" +#include "ump_uk_types.h" +#include "ump_ukk_wrappers.h" +#include "ump_ukk_ref_wrappers.h" + + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 0; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + + + +/* + * The data which we attached to each virtual memory mapping request we get. + * Each memory mapping has a reference to the UMP memory it maps. + * We release this reference when the last memory mapping is unmapped. + */ +typedef struct ump_vma_usage_tracker +{ + int references; + ump_dd_handle handle; +} ump_vma_usage_tracker; + +struct ump_device +{ + struct cdev cdev; +#if UMP_LICENSE_IS_GPL + struct class * ump_class; +#endif +}; + +/* The global variable containing the global device data */ +static struct ump_device ump_device; + + +/* Forward declare static functions */ +static int ump_file_open(struct inode *inode, struct file *filp); +static int ump_file_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma); + + +/* This variable defines the file operations this UMP device driver offer */ +static struct file_operations ump_fops = +{ + .owner = THIS_MODULE, + .open = ump_file_open, + .release = ump_file_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = ump_file_ioctl, +#else + .ioctl = ump_file_ioctl, +#endif + .mmap = ump_file_mmap +}; + + +/* This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int ump_initialize_module(void) +{ + _mali_osk_errcode_t err; + + DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); + + err = ump_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("UMP device driver init failed\n")); + return map_errcode(err); + } + + MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void ump_cleanup_module(void) +{ + DBG_MSG(2, ("Unloading UMP device driver\n")); + ump_kernel_destructor(); + DBG_MSG(2, ("Module unloaded\n")); +} + + + +/* + * Initialize the UMP device driver. + */ +int ump_kernel_device_initialize(void) +{ + int err; + dev_t dev = 0; + + if (0 == ump_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) + { + memset(&ump_device, 0, sizeof(ump_device)); + + /* initialize our char dev data */ + cdev_init(&ump_device.cdev, &ump_fops); + ump_device.cdev.owner = THIS_MODULE; + ump_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_device.cdev, dev, 1/*count*/); + if (0 == err) + { + +#if UMP_LICENSE_IS_GPL + ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_device.ump_class)) + { + err = PTR_ERR(ump_device.ump_class); + } + else + { + struct device * mdev; + mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) + { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&ump_device.cdev); +#else + return 0; +#endif + } + + unregister_chrdev_region(dev, 1); + } + + return err; +} + + + +/* + * Terminate the UMP device driver + */ +void ump_kernel_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + +#if UMP_LICENSE_IS_GPL + device_destroy(ump_device.ump_class, dev); + class_destroy(ump_device.ump_class); +#endif + + /* unregister char device */ + cdev_del(&ump_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); +} + +/* + * Open a new session. User space has called open() on us. + */ +static int ump_file_open(struct inode *inode, struct file *filp) +{ + struct ump_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) + { + MSG_ERR(("Minor not zero in ump_file_open()\n")); + return -ENODEV; + } + + /* Call the OS-Independent UMP Open function */ + err = _ump_ukk_open((void**) &session_data ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Ump failed to open a new session\n")); + return map_errcode( err ); + } + + filp->private_data = (void*)session_data; + filp->f_pos = 0; + + return 0; /* success */ +} + + + +/* + * Close a session. User space has called close() or crashed/terminated. + */ +static int ump_file_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + err = _ump_ukk_close((void**) &filp->private_data ); + if( _MALI_OSK_ERR_OK != err ) + { + return map_errcode( err ); + } + + return 0; /* success */ +} + + + +/* + * Handle IOCTL requests. + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err = -ENOTTY; + void __user * argument; + struct ump_session_data * session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* inode not used */ +#endif + + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) + { + MSG_ERR(("No session data attached to file object\n")); + return -ENOTTY; + } + + /* interpret the argument as a user pointer to something */ + argument = (void __user *)arg; + + switch (cmd) + { + case UMP_IOC_QUERY_API_VERSION: + err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_ALLOCATE : + err = ump_allocate_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_RELEASE: + err = ump_release_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SIZE_GET: + err = ump_size_get_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_MSYNC: + err = ump_msync_wrapper((u32 __user *)argument, session_data); + break; + + default: + DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); + err = -EFAULT; + break; + } + + return err; +} + +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) + { + case _MALI_OSK_ERR_OK : return 0; + case _MALI_OSK_ERR_FAULT: return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: return -EINVAL; + case _MALI_OSK_ERR_NOMEM: return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: return -ENOENT; + default: return -EFAULT; + } +} + +/* + * Handle from OS to map specified virtual memory to specified UMP memory. + */ +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma) +{ + _ump_uk_map_mem_s args; + _mali_osk_errcode_t err; + struct ump_session_data * session_data; + + /* Validate the session data */ + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) + { + MSG_ERR(("mmap() called without any session data available\n")); + return -EFAULT; + } + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = 0; + args.size = vma->vm_end - vma->vm_start; + args._ukk_private = vma; + args.secure_id = vma->vm_pgoff; + args.is_cached = 0; + + if (!(vma->vm_flags & VM_SHARED)) + { + args.is_cached = 1; + vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; + DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); + } + + DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags )); + + /* Call the common mmap handler */ + err = _ump_ukk_map_mem( &args ); + if ( _MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); + return map_errcode( err ); + } + + return 0; /* success */ +} + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); + +/* Export our own extended kernel space allocator */ +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + +/* Setup init and exit functions for this module */ +module_init(ump_initialize_module); +module_exit(ump_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.h new file mode 100644 index 00000000000..464c035974b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_H__ +#define __UMP_KERNEL_H__ + +int ump_kernel_device_initialize(void); +void ump_kernel_device_terminate(void); + + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.c new file mode 100644 index 00000000000..5c8a7f3783f --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ + + + +typedef struct block_info +{ + struct block_info * next; +} block_info; + + + +typedef struct block_allocator +{ + struct semaphore mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 num_blocks; + u32 num_free; +} block_allocator; + + +static void block_allocator_shutdown(ump_memory_backend * backend); +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem); +static void block_allocator_release(void * ctx, ump_dd_mem * handle); +static inline u32 get_phys(block_allocator * allocator, block_info * block); + + + +/* + * Create dedicated memory backend + */ +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size) +{ + ump_memory_backend * backend; + block_allocator * allocator; + u32 usable_size; + u32 num_blocks; + + usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); + num_blocks = usable_size / UMP_BLOCK_SIZE; + + if (0 == usable_size) + { + DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); + return NULL; + } + + DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); + DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); + + backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL != backend) + { + allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); + if (NULL != allocator) + { + allocator->all_blocks = kmalloc(sizeof(block_allocator) * num_blocks, GFP_KERNEL); + if (NULL != allocator->all_blocks) + { + int i; + + allocator->first_free = NULL; + allocator->num_blocks = num_blocks; + allocator->num_free = num_blocks; + allocator->base = base_address; + sema_init(&allocator->mutex, 1); + + for (i = 0; i < num_blocks; i++) + { + allocator->all_blocks[i].next = allocator->first_free; + allocator->first_free = &allocator->all_blocks[i]; + } + + backend->ctx = allocator; + backend->allocate = block_allocator_allocate; + backend->release = block_allocator_release; + backend->shutdown = block_allocator_shutdown; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; + } + kfree(allocator); + } + kfree(backend); + } + + return NULL; +} + + + +/* + * Destroy specified dedicated memory backend + */ +static void block_allocator_shutdown(ump_memory_backend * backend) +{ + block_allocator * allocator; + + BUG_ON(!backend); + BUG_ON(!backend->ctx); + + allocator = (block_allocator*)backend->ctx; + + DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); + + kfree(allocator->all_blocks); + kfree(allocator); + kfree(backend); +} + + + +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem) +{ + block_allocator * allocator; + u32 left; + block_info * last_allocated = NULL; + int i = 0; + + BUG_ON(!ctx); + BUG_ON(!mem); + + allocator = (block_allocator*)ctx; + left = mem->size_bytes; + + BUG_ON(!left); + BUG_ON(!&allocator->mutex); + + mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; + mem->block_array = (ump_dd_physical_block*)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); + if (NULL == mem->block_array) + { + MSG_ERR(("Failed to allocate block array\n")); + return 0; + } + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Could not get mutex to do block_allocate\n")); + return 0; + } + + mem->size_bytes = 0; + + while ((left > 0) && (allocator->first_free)) + { + block_info * block; + + block = allocator->first_free; + allocator->first_free = allocator->first_free->next; + block->next = last_allocated; + last_allocated = block; + allocator->num_free--; + + mem->block_array[i].addr = get_phys(allocator, block); + mem->block_array[i].size = UMP_BLOCK_SIZE; + mem->size_bytes += UMP_BLOCK_SIZE; + + i++; + + if (left < UMP_BLOCK_SIZE) left = 0; + else left -= UMP_BLOCK_SIZE; + } + + if (left) + { + block_info * block; + /* release all memory back to the pool */ + while (last_allocated) + { + block = last_allocated->next; + last_allocated->next = allocator->first_free; + allocator->first_free = last_allocated; + last_allocated = block; + allocator->num_free++; + } + + vfree(mem->block_array); + mem->backend_info = NULL; + mem->block_array = NULL; + + DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); + up(&allocator->mutex); + + return 0; + } + + mem->backend_info = last_allocated; + + up(&allocator->mutex); + mem->is_cached=0; + + return 1; +} + + + +static void block_allocator_release(void * ctx, ump_dd_mem * handle) +{ + block_allocator * allocator; + block_info * block, * next; + + BUG_ON(!ctx); + BUG_ON(!handle); + + allocator = (block_allocator*)ctx; + block = (block_info*)handle->backend_info; + BUG_ON(!block); + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); + return; + } + + while (block) + { + next = block->next; + + BUG_ON( (block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); + + block->next = allocator->first_free; + allocator->first_free = block; + allocator->num_free++; + + block = next; + } + DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); + up(&allocator->mutex); + + vfree(handle->block_array); + handle->block_array = NULL; +} + + + +/* + * Helper function for calculating the physical base adderss of a memory block + */ +static inline u32 get_phys(block_allocator * allocator, block_info * block) +{ + return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.h new file mode 100644 index 00000000000..58ebe15e5a2 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_dedicated.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_dedicated.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.c new file mode 100644 index 00000000000..99c9c223036 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +typedef struct os_allocator +{ + struct semaphore mutex; + u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ + u32 num_pages_allocated; /**< Number of pages allocated from the OS */ +} os_allocator; + + + +static void os_free(void* ctx, ump_dd_mem * descriptor); +static int os_allocate(void* ctx, ump_dd_mem * descriptor); +static void os_memory_backend_destroy(ump_memory_backend * backend); + + + +/* + * Create OS memory backend + */ +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation) +{ + ump_memory_backend * backend; + os_allocator * info; + + info = kmalloc(sizeof(os_allocator), GFP_KERNEL); + if (NULL == info) + { + return NULL; + } + + info->num_pages_max = max_allocation >> PAGE_SHIFT; + info->num_pages_allocated = 0; + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) + { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = os_allocate; + backend->release = os_free; + backend->shutdown = os_memory_backend_destroy; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; +} + + + +/* + * Destroy specified OS memory backend + */ +static void os_memory_backend_destroy(ump_memory_backend * backend) +{ + os_allocator * info = (os_allocator*)backend->ctx; + + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); + + kfree(info); + kfree(backend); +} + + + +/* + * Allocate UMP memory + */ +static int os_allocate(void* ctx, ump_dd_mem * descriptor) +{ + u32 left; + os_allocator * info; + int pages_allocated = 0; + int is_cached; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (os_allocator*)ctx; + left = descriptor->size_bytes; + is_cached = descriptor->is_cached; + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return 0; /* failure */ + } + + descriptor->backend_info = NULL; + descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; + + DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); + + descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); + if (NULL == descriptor->block_array) + { + up(&info->mutex); + DBG_MSG(1, ("Block array could not be allocated\n")); + return 0; /* failure */ + } + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max)) + { + struct page * new_page; + + if (is_cached) + { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN); + } else + { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + } + if (NULL == new_page) + { + break; + } + + /* Ensure page caches are flushed. */ + if ( is_cached ) + { + descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } else + { + descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } + + DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); + + if (left < PAGE_SIZE) + { + left = 0; + } + else + { + left -= PAGE_SIZE; + } + + pages_allocated++; + } + + DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); + + if (left) + { + DBG_MSG(1, ("Failed to allocate needed pages\n")); + + while(pages_allocated) + { + pages_allocated--; + if ( !is_cached ) + { + dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT) ); + } + + up(&info->mutex); + + return 0; /* failure */ + } + + info->num_pages_allocated += pages_allocated; + + DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + up(&info->mutex); + + return 1; /* success*/ +} + + +/* + * Free specified UMP memory + */ +static void os_free(void* ctx, ump_dd_mem * descriptor) +{ + os_allocator * info; + int i; + + BUG_ON(!ctx); + BUG_ON(!descriptor); + + info = (os_allocator*)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); + + info->num_pages_allocated -= descriptor->nr_blocks; + + up(&info->mutex); + + for ( i = 0; i < descriptor->nr_blocks; i++) + { + DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); + if ( ! descriptor->is_cached) + { + dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[i].addr>>PAGE_SHIFT) ); + } + + vfree(descriptor->block_array); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.h new file mode 100644 index 00000000000..d6083477d72 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_kernel_memory_backend_os.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_os.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ + diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_memory_backend.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_memory_backend.c new file mode 100644 index 00000000000..1c1190a537f --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_memory_backend.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* request_mem_region */ + +#include "arch/config.h" /* Configuration for current platform. The symlink for arch is set by Makefile */ + +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" + +/* Configure which dynamic memory allocator to use */ +int ump_backend = ARCH_UMP_BACKEND_DEFAULT; +module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); + +/* The base address of the memory block for the dedicated memory backend */ +unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; +module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); + +/* The size of the memory block for the dedicated memory backend */ +unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; +module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); + +ump_memory_backend* ump_memory_backend_create ( void ) +{ + ump_memory_backend * backend = NULL; + + /* Create the dynamic memory allocator backend */ + if (0 == ump_backend) + { + DBG_MSG(2, ("Using dedicated memory backend\n")); + + DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); + /* Ask the OS if we can use the specified physical memory */ + if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) + { + MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); + return NULL; + } + backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); + } + else if (1 == ump_backend) + { + DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_os_memory_backend_create(ump_memory_size); + } + + return backend; +} + +void ump_memory_backend_destroy( void ) +{ + if (0 == ump_backend) + { + DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); + release_mem_region(ump_memory_address, ump_memory_size); + } +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_atomics.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_atomics.c new file mode 100644 index 00000000000..b3300ab691a --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_atomics.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_atomics.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + +#include "ump_osk.h" +#include + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_low_level_mem.c new file mode 100644 index 00000000000..7a1c5e97886 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_low_level_mem.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include /* kernel module definitions */ +#include +#include +#include + +#include +#include +#include + +typedef struct ump_vma_usage_tracker +{ + atomic_t references; + ump_memory_allocation *descriptor; +} ump_vma_usage_tracker; + +static void ump_vma_open(struct vm_area_struct * vma); +static void ump_vma_close(struct vm_area_struct * vma); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + +static struct vm_operations_struct ump_vm_ops = +{ + .open = ump_vma_open, + .close = ump_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = ump_cpu_page_fault_handler +#else + .nopfn = ump_cpu_page_fault_handler +#endif +}; + +/* + * Page fault for VMA region + * This should never happen since we always map in the entire virtual memory range. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); + MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void ump_vma_open(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_inc_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); +} + +static void ump_vma_close(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + _ump_uk_unmap_mem_s args; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_dec_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); + + if (0 == new_val) + { + ump_memory_allocation * descriptor; + + descriptor = vma_usage_tracker->descriptor; + + args.ctx = descriptor->ump_session; + args.cookie = descriptor->cookie; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + args._ukk_private = NULL; /** @note unused */ + + DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); + _ump_ukk_unmap_mem( & args ); + + /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ + } +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descriptor ) +{ + ump_vma_usage_tracker * vma_usage_tracker; + struct vm_area_struct *vma; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); + if (NULL == vma_usage_tracker) + { + DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); + return -_MALI_OSK_ERR_FAULT; + } + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + vma->vm_private_data = vma_usage_tracker; + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + if (0==descriptor->is_cached) + { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot )); + + /* Setup the functions which handle further VMA handling */ + vma->vm_ops = &ump_vm_ops; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + + atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ + vma_usage_tracker->descriptor = descriptor; + + return _MALI_OSK_ERR_OK; +} + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + ump_vma_usage_tracker * vma_usage_tracker; + + if (NULL == descriptor) return; + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + kfree(vma_usage_tracker); + return; +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ) +{ + struct vm_area_struct *vma; + _mali_osk_errcode_t retval; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + retval = remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; + + DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", + ump_dd_secure_id_get(descriptor->handle), + (unsigned long)vma, + (unsigned long)(vma->vm_start + offset), + (unsigned long)*phys_addr, + size, + (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); + + return retval; +} + + +void _ump_osk_msync( ump_dd_mem * mem, ump_uk_msync_op op ) +{ + int i; + DBG_MSG(3, ("Flushing nr of blocks: %u. First: paddr: 0x%08x vaddr: 0x%08x size:%dB\n", mem->nr_blocks, mem->block_array[0].addr, phys_to_virt(mem->block_array[0].addr), mem->block_array[0].size)); + + /* TODO: Use args->size and args->address to select a subrange of this allocation to flush */ + for (i=0 ; inr_blocks; i++) + { + /* TODO: Find out which flush method is best of 1)Dma OR 2)Normal flush functions */ + /* TODO: Use args->op to select the flushing method: CLEAN_AND_INVALIDATE or CLEAN */ + /*#define USING_DMA_FLUSH*/ + #ifdef USING_DMA_FLUSH + DEBUG_ASSERT( (PAGE_SIZE==mem->block_array[i].size)); + dma_map_page(NULL, pfn_to_page(mem->block_array[i].addr >> PAGE_SHIFT), 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); + /*dma_unmap_page(NULL, mem->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL);*/ + #else + /* Normal style flush */ + ump_dd_physical_block *block; + u32 start_p, end_p; + const void *start_v, *end_v; + block = &mem->block_array[i]; + + start_p = (u32)block->addr; + start_v = phys_to_virt( start_p ) ; + + end_p = start_p + block->size-1; + end_v = phys_to_virt( end_p ) ; + + dmac_flush_range(start_v, end_v); + outer_flush_range(start_p, end_p); + #endif + } + + return ; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_misc.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_misc.c new file mode 100644 index 00000000000..78569c4457b --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_osk_misc.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_misc.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + + +#include "ump_osk.h" + +#include +#include "ump_kernel_linux.h" + +/* is called from ump_kernel_constructor in common code */ +_mali_osk_errcode_t _ump_osk_init( void ) +{ + if (0 != ump_kernel_device_initialize()) + { + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_osk_term( void ) +{ + ump_kernel_device_terminate(); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.c new file mode 100644 index 00000000000..155635026aa --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Allocate UMP memory + */ +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_allocate_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + err = _ump_ukk_allocate( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); + return map_errcode(err); + } + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + _ump_uk_release_s release_args; + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + release_args.ctx = (void *) session_data; + release_args.secure_id = user_interaction.secure_id; + + err = _ump_ukk_release( &release_args ); + if(_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); + } + + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.h new file mode 100644 index 00000000000..b2bef1152c3 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_ref_wrappers.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + +#ifndef __UMP_UKK_REF_WRAPPERS_H__ +#define __UMP_UKK_REF_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.c new file mode 100644 index 00000000000..d14b631246c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Negotiate version of IOCTL API + */ +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_api_version_s version_info; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + version_info.ctx = (void*) session_data; + err = _ump_uku_get_api_version( &version_info ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); + return map_errcode(err); + } + + version_info.ctx = NULL; + + /* Copy ouput data back to user space */ + if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + + +/* + * IOCTL operation; Release reference to specified UMP memory. + */ +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_release_s release_args; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + release_args.ctx = (void*) session_data; + err = _ump_ukk_release( &release_args ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); + return map_errcode(err); + } + + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_size_get_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + err = _ump_ukk_size_get( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); + return map_errcode(err); + } + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ + int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_msync_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_msync( &user_interaction ); + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.h b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.h new file mode 100644 index 00000000000..31afe2dca56 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/linux/ump_ukk_wrappers.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#ifndef __UMP_UKK_WRAPPERS_H__ +#define __UMP_UKK_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + +#ifdef __cplusplus +} +#endif + + + +#endif /* __UMP_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/readme.txt b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/readme.txt new file mode 100644 index 00000000000..bf1bf61d60e --- /dev/null +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/readme.txt @@ -0,0 +1,17 @@ +Building the UMP Device Driver for Linux +---------------------------------------- + +Build the UMP Device Driver for Linux by running the following make command: + +KDIR= CONFIG= make + +where + kdir_path: Path to your Linux Kernel directory + your_config: Name of the sub-folder to find the required config.h file + ("arch-" will be prepended) + +The config.h file contains the configuration parameters needed, like the +memory backend to use, and the amount of memory. + +The result will be a ump.ko file, which can be loaded into the Linux kernel +by using the insmod command. diff --git a/drivers/gpu/mali/mali400ko/mali.spec b/drivers/gpu/mali/mali400ko/mali.spec new file mode 100644 index 00000000000..62e8fa0e1b4 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/mali.spec @@ -0,0 +1,57 @@ +%define kernel_target u8500 +%define kernel_version %(find /lib/modules -name "*%{kernel_target}" | cut -c 14-) + +Name: mali400ko +License: GPL +Summary: Mali400 kernel module +Version: 1.0 +Release: 1 +URL: http://stericsson.com +BuildRoot: %{_tmppath}/%{name} +Requires: kernel +BuildRequires: kernel-u8500-devel +Requires(post): ldconfig +Requires(postun): ldconfig + +Source0: mali400ko.tar.gz + +%description +Mali400 kernel module. + +%files +%defattr(-,root,root) +/lib/modules/%{kernel_version}/extra/mali.ko +/lib/modules/%{kernel_version}/extra/mali_drm.ko + +%prep +%setup -q + +%build +export CROSS_COMPILE="" +export KERNELDIR=/usr/src/kernels/%{kernel_version} +export KERNEL_BUILD_DIR=/usr/src/kernels/%{kernel_version} +export USING_UMP=1 +export USING_HWMEM=1 +#make V=0 mali-devicedrv + +%install +export CROSS_COMPILE="" +export KERNELDIR=/usr/src/kernels/%{kernel_version} +export KERNEL_BUILD_DIR=/usr/src/kernels/%{kernel_version} +export USING_UMP=1 +export USING_HWMEM=1 +export INSTALL_MOD_DIR=extra +export INSTALL_MOD_PATH=$RPM_BUILD_ROOT +make V=0 install-mali install-mali_drm + +# Remove kernel modules.* files +rm -f $RPM_BUILD_ROOT/lib/modules/%{kernel_version}/modules.* + +%clean +rm -rf $RPM_BUILD_ROOT + +%post + +%postun + + diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/README.txt b/drivers/gpu/mali/mali400ko/x11/mali_drm/README.txt new file mode 100644 index 00000000000..47009fe7686 --- /dev/null +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/README.txt @@ -0,0 +1,24 @@ +Notes on integrating the Mali DRM module: + +The Mali DRM is a platform device, meaning that you have to add an entry for it in your kernel architecture specification. + +Example: (arch/arm/mach-/mach-.c) + +#ifdef CONFIG_DRM_MALI +static struct platform_device _device_mali_drm = { + .name = "mali_drm", + .id = -1, +}; +#endif + +static struct platform_device *_devices[] __initdata = { +... +#ifdef CONFIG_DRM_MALI + &_device_mali_drm, +#endif +... +}; + +Where is substituted with the selected platform. + +The "mali" folder should be placed under drivers/gpu/drm/ diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/Makefile b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/Makefile new file mode 100644 index 00000000000..0f3ace966df --- /dev/null +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/Makefile @@ -0,0 +1,17 @@ +# +# * Copyright (C) 2010 ARM Limited. All rights reserved. +# * +# * This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# * +# * A copy of the licence is included with the program, and can also be obtained from Free Software +# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# +# Makefile for the Mali drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +obj-y += mali_drm.o +mali_drm-objs := mali_drv.o +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) -I$(KBUILD_EXTMOD)/include -I$(KBUILD_EXTMOD)/../drm/include/ diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c new file mode 100644 index 00000000000..aa0d0c5059c --- /dev/null +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_drv.c + * Implementation of the Linux device driver entrypoints for Mali DRM + */ +#include +#include +#include "mali_drv.h" + +static struct platform_device *dev0; + +void mali_drm_preclose(struct drm_device *dev, struct drm_file *file_priv) +{ +} + +void mali_drm_lastclose(struct drm_device *dev) +{ +} + +static int mali_drm_suspend(struct drm_device *dev, pm_message_t state) +{ + return 0; +} + +static int mali_drm_resume(struct drm_device *dev) +{ + return 0; +} + +static int mali_drm_load(struct drm_device *dev, unsigned long chipset) +{ + return 0; +} + +static int mali_drm_unload(struct drm_device *dev) +{ + return 0; +} + +static struct drm_driver driver = +{ + .load = mali_drm_load, + .unload = mali_drm_unload, + .context_dtor = NULL, + .reclaim_buffers = NULL, + .reclaim_buffers_idlelocked = NULL, + .preclose = mali_drm_preclose, + .lastclose = mali_drm_lastclose, + .suspend = mali_drm_suspend, + .resume = mali_drm_resume, + .ioctls = NULL, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + }, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static struct platform_driver platform_drm_driver; + +int mali_drm_init(struct platform_device *dev) +{ + printk(KERN_INFO "Mali DRM initialize, driver name: %s, version %d.%d\n", DRIVER_NAME, DRIVER_MAJOR, DRIVER_MINOR); + if (dev == dev0) { + driver.num_ioctls = 0; + return drm_platform_init(&driver, dev0); + } + return 0; + +} + +void mali_drm_exit(struct platform_device *dev) +{ + if (dev0 == dev) + drm_platform_exit(&driver, dev); +} + +static int __devinit mali_platform_drm_probe(struct platform_device *dev) +{ + return mali_drm_init(dev); +} + +static int mali_platform_drm_remove(struct platform_device *dev) +{ + mali_drm_exit(dev); + + return 0; +} + +static int mali_platform_drm_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int mali_platform_drm_resume(struct platform_device *dev) +{ + return 0; +} + +static struct platform_driver platform_drm_driver = { + .probe = mali_platform_drm_probe, + .remove = __devexit_p(mali_platform_drm_remove), + .suspend = mali_platform_drm_suspend, + .resume = mali_platform_drm_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init mali_platform_drm_init(void) +{ + dev0 = platform_device_register_simple("mali_drm", 0, NULL, 0); + return platform_driver_register(&platform_drm_driver); +} + +static void __exit mali_platform_drm_exit(void) +{ + platform_driver_unregister(&platform_drm_driver); + platform_device_unregister(dev0); +} + +#ifdef MODULE +module_init(mali_platform_drm_init); +#else +late_initcall(mali_platform_drm_init); +#endif +module_exit(mali_platform_drm_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE(DRIVER_LICENSE); +MODULE_ALIAS(DRIVER_ALIAS); +MODULE_INFO(vermagic, VERMAGIC_STRING); diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.h b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.h new file mode 100644 index 00000000000..188f427ee6d --- /dev/null +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.h @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI_DRV_H_ +#define _MALI_DRV_H_ + +#define DRIVER_AUTHOR "ARM Ltd." +#define DRIVER_NAME "mali_drm" +#define DRIVER_DESC "DRM module for Mali-200, Mali-400" +#define DRIVER_LICENSE "GPL v2" +#define DRIVER_ALIAS "platform:mali_drm" +#define DRIVER_DATE "20101111" +#define DRIVER_VERSION "0.2" +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 1 + +#endif /* _MALI_DRV_H_ */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a290be51a1f..750c5182ca8 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -23,6 +23,8 @@ source "drivers/gpu/drm/Kconfig" source "drivers/gpu/stub/Kconfig" +source "drivers/gpu/mali/Kconfig" + config VGASTATE tristate default n -- cgit v1.2.3 From e9b1ede595b1fb80bc1218c964610469df5e11be Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 2 Dec 2011 14:26:43 +0100 Subject: mali: Include module.h after 3.2 update Signed-off-by: Philippe Langlais --- .../gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c | 1 + drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c index d868582c75a..aaafc151077 100644 --- a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/linux/mali_kernel_pm.c @@ -23,6 +23,7 @@ #include #endif /* CONFIG_PM_RUNTIME */ +#include #include #include #include diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c index aa0d0c5059c..583fb55c50a 100644 --- a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c @@ -12,6 +12,7 @@ * @file mali_drv.c * Implementation of the Linux device driver entrypoints for Mali DRM */ +#include #include #include #include "mali_drv.h" -- cgit v1.2.3 From 6020a86def5b4ce377a843315bcf66c9ce9ca37c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 30 Jan 2012 10:47:27 +0100 Subject: mali: Changes for 3.3 Signed-off-by: Philippe Langlais --- .../gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c index 583fb55c50a..571ab04e1e2 100644 --- a/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c +++ b/drivers/gpu/mali/mali400ko/x11/mali_drm/mali/mali_drv.c @@ -47,6 +47,17 @@ static int mali_drm_unload(struct drm_device *dev) return 0; } +static const struct file_operations mali_driver_fops = +{ + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +}; + static struct drm_driver driver = { .load = mali_drm_load, @@ -59,15 +70,7 @@ static struct drm_driver driver = .suspend = mali_drm_suspend, .resume = mali_drm_resume, .ioctls = NULL, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - }, + .fops = &mali_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, -- cgit v1.2.3 From 5bd3314357fc6ec991071e0bb2f959fb98bc881c Mon Sep 17 00:00:00 2001 From: Bernhard Rosenkranzer Date: Wed, 23 May 2012 15:50:02 +0200 Subject: android: compilation failure with new version of svn The line setting SVN_REV assumes "svnversion" will report either the svn revision of the directory or "exported". This assumption is no longer true for subversion 1.7.4, which reports "Unversioned directory" instead of "exported", causing the build to fail (gcc -DSVN_REV=Unversioned directory) Signed-off-by: Bernhard Rosenkranzer Submitted-by: Mathieu Poirier --- drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common | 2 +- drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common index ebe3b9e3399..a8f4189d0cf 100644 --- a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/Makefile.common @@ -37,7 +37,7 @@ MALI_PLATFORM_FILE=platform/default/mali_platform.c endif # Get subversion revision number, fall back to only ${MALI_RELEASE_NAME} if no svn info is available -SVN_REV := $(shell (cd $(DRIVER_DIR); (svnversion | grep -qv exported && svnversion) || git svn info | grep '^Revision: '| sed -e 's/^Revision: //' ) 2>/dev/null ) +SVN_REV := $(shell (cd $(DRIVER_DIR); (svnversion | grep -qvE '(exported|Unversioned)' && svnversion) || git svn info | grep '^Revision: '| sed -e 's/^Revision: //' ) 2>/dev/null ) ifeq ($(SVN_REV),) SVN_REV := $(MALI_RELEASE_NAME) else diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common index c6aa6337f80..f797c85a6f2 100644 --- a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common +++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/ump/Makefile.common @@ -14,7 +14,6 @@ SRC = $(UMP_FILE_PREFIX)common/ump_kernel_common.c \ $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.c # Get subversion revision number, fall back to 0000 if no svn info is available -SVN_REV:=$(shell ((svnversion | grep -qv exported && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') - +SVN_REV:=$(shell ((svnversion | grep -qvE '(exported|Unversioned)' && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" -- cgit v1.2.3