mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
MALI: rockchip: upgrade bifrost DDK to g6p0-01eac0, from g2p0-01eac0
Include a new directory include/uapi/gpu/arm/bifrost/, which includes some header files of bifrost device driver. In the original part of g6, the path is include/uapi/gpu/arm/midgard/. I changed the "midgard" to "bifrost", and modified the paths of the header files in .c files. I resolved some conflicts between modifications form ARM and RK, manually. In addition, introduce source files of protected_memory_allocator that might be needed by bifrost_device_driver into build system. Further more, to avoid errors when building in GKI mode, add "WITH Linux-syscall-note" to SPDX tag of uapi headers. Change-Id: I09d500a0fdbc5da352c81dc4fcfbffb5b7f907f5 Signed-off-by: Zhen Chen <chenzhen@rock-chips.com>
This commit is contained in:
284
Documentation/ABI/testing/sysfs-device-mali
Normal file
284
Documentation/ABI/testing/sysfs-device-mali
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
What: /sys/class/misc/mali%u/device/core_mask
|
||||
Description:
|
||||
This attribute is used to restrict number of shader core
|
||||
available, is useful for debugging purposes. Reading
|
||||
this attribute provides us mask of cores available.
|
||||
Writing to it will set the current core mask.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/debug_command
|
||||
Description:
|
||||
This attribute is used to issue debug commands that supported
|
||||
by the driver. On reading it provides the list of debug commands
|
||||
that are supported, and writing back one of those commands will
|
||||
enable that debug option.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/dvfs_period
|
||||
Description:
|
||||
This is used to set the DVFS sampling period to be used by the
|
||||
driver, On reading it provides the current DVFS sampling period,
|
||||
on writing a value we set the DVFS sampling period.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/dummy_job_wa_info
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU that requires a GPU workaround
|
||||
to execute the dummy fragment job on all shader cores to
|
||||
workaround a hang issue.
|
||||
|
||||
Its a readonly attribute and on reading gives details on the
|
||||
options used with the dummy workaround.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/gpuinfo
|
||||
Description:
|
||||
This attribute provides description of the present Mali GPU.
|
||||
Its a read only attribute provides details like GPU family, the
|
||||
number of cores, the hardware version and the raw product id.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/idle_hysteresis_time
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. This attribute is
|
||||
used to set the duration value in milliseconds for the
|
||||
configuring hysteresis field for determining GPU idle detection.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/js_ctx_scheduling_mode
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU. This attribute is used to set
|
||||
context scheduling priority for a job slot.
|
||||
|
||||
On Reading it provides the currently set job slot context
|
||||
priority.
|
||||
|
||||
Writing 0 to this attribute sets it to the mode were
|
||||
higher priority atoms will be scheduled first, regardless of
|
||||
the context they belong to. Newly-runnable higher priority atoms
|
||||
can preempt lower priority atoms currently running on the GPU,
|
||||
even if they belong to a different context.
|
||||
|
||||
Writing 1 to this attribute set it to the mode were the
|
||||
highest-priority atom will be chosen from each context in turn
|
||||
using a round-robin algorithm, so priority only has an effect
|
||||
within the context an atom belongs to. Newly-runnable higher
|
||||
priority atoms can preempt the lower priority atoms currently
|
||||
running on the GPU, but only if they belong to the same context.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/js_scheduling_period
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU. Used to set the job scheduler
|
||||
tick period in nano-seconds. The Job Scheduler determines the
|
||||
jobs that are run on the GPU, and for how long, Job Scheduler
|
||||
makes decisions at a regular time interval determined by value
|
||||
in js_scheduling_period.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/js_softstop_always
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU. Soft-stops are disabled when
|
||||
only a single context is present, this attribute is used to
|
||||
enable soft-stop when only a single context is present can be
|
||||
used for debug and unit-testing purposes.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/js_timeouts
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU. It used to set the soft stop
|
||||
and hard stop times for the job scheduler.
|
||||
|
||||
Writing value 0 causes no change, or -1 to restore the
|
||||
default timeout.
|
||||
|
||||
The format used to set js_timeouts is
|
||||
"<soft_stop_ms> <soft_stop_ms_cl> <hard_stop_ms_ss>
|
||||
<hard_stop_ms_cl> <hard_stop_ms_dumping> <reset_ms_ss>
|
||||
<reset_ms_cl> <reset_ms_dumping>"
|
||||
|
||||
|
||||
What: /sys/class/misc/mali%u/device/lp_mem_pool_max_size
|
||||
Description:
|
||||
This attribute is used to set the maximum number of large pages
|
||||
memory pools that the driver can contain. Large pages are of
|
||||
size 2MB. On read it displays all the max size of all memory
|
||||
pools and can be used to modify each individual pools as well.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/lp_mem_pool_size
|
||||
Description:
|
||||
This attribute is used to set the number of large memory pages
|
||||
which should be populated, changing this value may cause
|
||||
existing pages to be removed from the pool, or new pages to be
|
||||
created and then added to the pool. On read it will provide
|
||||
pool size for all available pools and we can modify individual
|
||||
pool.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/mem_pool_max_size
|
||||
Description:
|
||||
This attribute is used to set the maximum number of small pages
|
||||
for memory pools that the driver can contain. Here small pages
|
||||
are of size 4KB. On read it will display the max size for all
|
||||
available pools and allows us to set max size of
|
||||
individual pools.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/mem_pool_size
|
||||
Description:
|
||||
This attribute is used to set the number of small memory pages
|
||||
which should be populated, changing this value may cause
|
||||
existing pages to be removed from the pool, or new pages to
|
||||
be created and then added to the pool. On read it will provide
|
||||
pool size for all available pools and we can modify individual
|
||||
pool.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/device/mempool/ctx_default_max_size
|
||||
Description:
|
||||
This attribute is used to set maximum memory pool size for
|
||||
all the memory pool so that the maximum amount of free memory
|
||||
that each pool can hold is identical.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/device/mempool/lp_max_size
|
||||
Description:
|
||||
This attribute is used to set the maximum number of large pages
|
||||
for all memory pools that the driver can contain.
|
||||
Large pages are of size 2MB.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/device/mempool/max_size
|
||||
Description:
|
||||
This attribute is used to set the maximum number of small pages
|
||||
for all the memory pools that the driver can contain.
|
||||
Here small pages are of size 4KB.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/pm_poweroff
|
||||
Description:
|
||||
This attribute contains the current values, represented as the
|
||||
following space-separated integers:
|
||||
• PM_GPU_POWEROFF_TICK_NS.
|
||||
• PM_POWEROFF_TICK_SHADER.
|
||||
• PM_POWEROFF_TICK_GPU.
|
||||
|
||||
Example:
|
||||
echo 100000 4 4 > /sys/class/misc/mali0/device/pm_poweroff
|
||||
|
||||
Sets the following new values: 100,000ns tick, four ticks
|
||||
for shader power down, and four ticks for GPU power down.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/power_policy
|
||||
Description:
|
||||
This attribute is used to find the current power policy been
|
||||
used, reading will list the power policies available and
|
||||
enclosed in square bracket is the current one been selected.
|
||||
|
||||
Example:
|
||||
cat /sys/class/misc/mali0/device/power_policy
|
||||
[demand] coarse_demand always_on
|
||||
|
||||
To switch to a different policy at runtime write the valid entry
|
||||
name back to the attribute.
|
||||
|
||||
Example:
|
||||
echo "coarse_demand" > /sys/class/misc/mali0/device/power_policy
|
||||
|
||||
What: /sys/class/misc/mali%u/device/progress_timeout
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. This attribute
|
||||
is used to set the progress timeout value and read the current
|
||||
progress timeout value.
|
||||
|
||||
Progress timeout value is the maximum number of GPU cycles
|
||||
without forward progress to allow to elapse before terminating a
|
||||
GPU command queue group.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/reset_timeout
|
||||
Description:
|
||||
This attribute is used to set the number of milliseconds to
|
||||
wait for the soft stop to complete for the GPU jobs before
|
||||
proceeding with the GPU reset.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/soft_job_timeout
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU. It used to set the timeout
|
||||
value for waiting for any soft event to complete.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/scheduling/serialize_jobs
|
||||
Description:
|
||||
This attribute is available only with platform device that
|
||||
supports a Job Manager based GPU.
|
||||
|
||||
Various options available under this are:
|
||||
• none - for disabling serialization.
|
||||
• intra-slot - Serialize atoms within a slot, only one
|
||||
atom per job slot.
|
||||
• inter-slot - Serialize atoms between slots, only one
|
||||
job slot running at any time.
|
||||
• full - it a combination of both inter and intra slot,
|
||||
so only one atom and one job slot running
|
||||
at any time.
|
||||
• full-reset - full serialization and Reset the GPU after
|
||||
each atom completion
|
||||
|
||||
These options are useful for debugging and investigating
|
||||
failures and gpu hangs to narrow down atoms that could cause
|
||||
troubles.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/Compute iterator count/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. Its a read-only attribute
|
||||
which indicates the maximum number of Compute iterators
|
||||
supported by the GPU.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/CSHWIF count/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. Its a read-only
|
||||
attribute which indicates the maximum number of CSHWIFs
|
||||
supported by the GPU.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/Fragment iterator count/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. Its a read-only
|
||||
attribute which indicates the maximum number of
|
||||
Fragment iterators supported by the GPU.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/Scoreboard set count/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. Its a read-only
|
||||
attribute which indicates the maximum number of
|
||||
Scoreboard set supported by the GPU.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/Tiler iterator count/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU. Its a read-only
|
||||
attribute which indicates the maximum number of Tiler iterators
|
||||
supported by the GPU.
|
||||
|
||||
What: /sys/class/misc/mali%u/device/firmware_config/Log verbosity/*
|
||||
Description:
|
||||
This attribute is available only with mali platform
|
||||
device-driver that supports a CSF GPU.
|
||||
|
||||
Used to enable firmware logs, logging levels valid values
|
||||
are indicated using 'min and 'max' attribute values
|
||||
values that are read-only.
|
||||
|
||||
Log level can be set using the 'cur' read, write attribute,
|
||||
we can use a valid log level value from min and max range values
|
||||
and set a valid desired log level for firmware logs.
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2013-2020 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2013-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
* ARM Mali Midgard / Bifrost devices
|
||||
@@ -46,12 +45,12 @@ Documentation/devicetree/bindings/regulator/regulator.txt for details.
|
||||
This is optional.
|
||||
- operating-points-v2 : Refer to Documentation/devicetree/bindings/power/mali-opp.txt
|
||||
for details.
|
||||
- quirks_jm : Used to write to the JM_CONFIG register or equivalent.
|
||||
- quirks_gpu : Used to write to the JM_CONFIG or CSF_CONFIG register.
|
||||
Should be used with care. Options passed here are used to override
|
||||
certain default behavior. Note: This will override 'idvs-group-size'
|
||||
field in devicetree and module param 'corestack_driver_control',
|
||||
therefore if 'quirks_jm' is used then 'idvs-group-size' and
|
||||
'corestack_driver_control' value should be incorporated into 'quirks_jm'.
|
||||
therefore if 'quirks_gpu' is used then 'idvs-group-size' and
|
||||
'corestack_driver_control' value should be incorporated into 'quirks_gpu'.
|
||||
- quirks_sc : Used to write to the SHADER_CONFIG register.
|
||||
Should be used with care. Options passed here are used to override
|
||||
certain default behavior.
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2019-2020 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
* Arm memory group manager for Mali GPU device drivers
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2020 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
* Arm priority control manager for Mali GPU device drivers
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be "arm,priority-control-manager"
|
||||
|
||||
An example node:
|
||||
|
||||
gpu_priority_control_manager: priority-control-manager {
|
||||
compatible = "arm,priority-control-manager";
|
||||
};
|
||||
|
||||
It must be referenced by the GPU as well, see priority-control-manager:
|
||||
|
||||
gpu: gpu@0x6e000000 {
|
||||
compatible = "arm,mali-midgard";
|
||||
reg = <0x0 0x6e000000 0x0 0x200000>;
|
||||
interrupts = <0 168 4>, <0 168 4>, <0 168 4>;
|
||||
interrupt-names = "JOB", "MMU", "GPU";
|
||||
clocks = <&scpi_dvfs 2>;
|
||||
clock-names = "clk_mali";
|
||||
system-coherency = <31>;
|
||||
priority-control-manager = <&gpu_priority_control_manager>;
|
||||
operating-points = <
|
||||
/* KHz uV */
|
||||
50000 820000
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019-2020 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
* Arm protected memory allocator for Mali GPU device drivers
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be "arm,protected-memory-allocator"
|
||||
|
||||
The protected memory allocator manages allocation of physical pages of a
|
||||
reserved memory region of protected memory, therefore its device node shall
|
||||
reference a reserved memory region.
|
||||
|
||||
In addition to that, the protected memory allocator shall be referenced
|
||||
by the GPU.
|
||||
|
||||
A complete example configuration for the device tree:
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
mali_protected: mali_protected@c0000000 {
|
||||
compatible = "mali-reserved";
|
||||
reg = <0x0 0xc0000000 0x0 0x1000000>;
|
||||
};
|
||||
};
|
||||
|
||||
gpu_protected_memory_allocator: protected-memory-allocator {
|
||||
compatible = "arm,protected-memory-allocator";
|
||||
memory-region = <&mali_protected>;
|
||||
};
|
||||
|
||||
gpu_fpga: gpu@0x6e000000 {
|
||||
compatible = "arm,mali-midgard";
|
||||
reg = <0x0 0x6e000000 0x0 0x200000>;
|
||||
interrupts = <0 168 4>, <0 168 4>, <0 168 4>;
|
||||
interrupt-names = "JOB", "MMU", "GPU";
|
||||
clocks = <&scpi_dvfs 2>;
|
||||
clock-names = "clk_mali";
|
||||
protected-memory-allocator = <&gpu_protected_memory_allocator>;
|
||||
operating-points = <
|
||||
/* KHz uV */
|
||||
50000 820000
|
||||
>;
|
||||
};
|
||||
|
||||
The protected memory allocator is gpu_protected_memory_allocator.
|
||||
It references the mali_protected reserved memory region and, in turn,
|
||||
it is referenced by the GPU as protected-memory-allocator.
|
||||
@@ -1,14 +1,20 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2017, 2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2017, 2019-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# 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.
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
@@ -48,7 +54,7 @@ Optional properties:
|
||||
|
||||
- opp-core-count: Number of cores to use for this OPP. If this is present then
|
||||
the driver will build a core mask using the available core mask provided by
|
||||
the GPU hardware.
|
||||
the GPU hardware. An opp-core-count value of 0 is not permitted.
|
||||
|
||||
If neither this nor opp-core-mask are present then all shader cores will be
|
||||
used for this OPP.
|
||||
|
||||
@@ -183,6 +183,9 @@ config SOC_BUS
|
||||
|
||||
source "drivers/base/regmap/Kconfig"
|
||||
|
||||
source "drivers/base/arm/memory_group_manager/Kconfig"
|
||||
source "drivers/base/arm/protected_memory_allocator/Kconfig"
|
||||
|
||||
config DMA_SHARED_BUFFER
|
||||
bool
|
||||
default n
|
||||
|
||||
@@ -18,6 +18,10 @@ obj-$(CONFIG_MODULES) += module.o
|
||||
endif
|
||||
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
|
||||
obj-$(CONFIG_REGMAP) += regmap/
|
||||
|
||||
obj-$(CONFIG_MALI_MEMORY_GROUP_MANAGER) += arm/memory_group_manager/
|
||||
obj-$(CONFIG_MALI_PROTECTED_MEMORY_ALLOCATOR) += arm/protected_memory_allocator/
|
||||
|
||||
obj-$(CONFIG_SOC_BUS) += soc.o
|
||||
obj-$(CONFIG_PINCTRL) += pinctrl.o
|
||||
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2019-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MALI_MEMORY_GROUP_MANAGER) := memory_group_manager.o
|
||||
obj-$(CONFIG_MALI_MEMORY_GROUP_MANAGER) := memory_group_manager.o
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2019-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2019-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
# linux build system bootstrap for out-of-tree module
|
||||
@@ -24,12 +23,15 @@
|
||||
# default to building for the host
|
||||
ARCH ?= $(shell uname -m)
|
||||
|
||||
ifeq ($(KDIR),)
|
||||
$(error Must specify KDIR to point to the kernel to target))
|
||||
endif
|
||||
# Handle Android Common Kernel source naming
|
||||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
KDIR ?= $(KERNEL_SRC)
|
||||
|
||||
all:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" modules CONFIG_MALI_MEMORY_GROUP_MANAGER=m
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../../include" modules CONFIG_MALI_MEMORY_GROUP_MANAGER=m
|
||||
|
||||
clean:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean
|
||||
|
||||
modules_install:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) modules_install
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,16 +17,14 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_GPU_H_
|
||||
#define _KBASE_GPU_H_
|
||||
|
||||
#include "mali_kbase_gpu_regmap.h"
|
||||
#include "mali_kbase_gpu_fault.h"
|
||||
#include "mali_kbase_gpu_coherency.h"
|
||||
#include "mali_kbase_gpu_id.h"
|
||||
|
||||
#endif /* _KBASE_GPU_H_ */
|
||||
bob_kernel_module {
|
||||
name: "memory_group_manager",
|
||||
srcs: [
|
||||
"Kbuild",
|
||||
"memory_group_manager.c",
|
||||
],
|
||||
kbuild_options: ["CONFIG_MALI_MEMORY_GROUP_MANAGER=m"],
|
||||
defaults: ["kernel_defaults"],
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
@@ -481,7 +480,6 @@ static struct platform_driver memory_group_manager_driver = {
|
||||
.remove = memory_group_manager_remove,
|
||||
.driver = {
|
||||
.name = "physical-memory-group-manager",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(memory_group_manager_dt_ids),
|
||||
/*
|
||||
* Prevent the mgm_dev from being unbound and freed, as other's
|
||||
21
drivers/base/arm/protected_memory_allocator/Kbuild
Normal file
21
drivers/base/arm/protected_memory_allocator/Kbuild
Normal file
@@ -0,0 +1,21 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019-2021 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MALI_PROTECTED_MEMORY_ALLOCATOR) := protected_memory_allocator.o
|
||||
27
drivers/base/arm/protected_memory_allocator/Kconfig
Normal file
27
drivers/base/arm/protected_memory_allocator/Kconfig
Normal file
@@ -0,0 +1,27 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019-2021 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
config MALI_PROTECTED_MEMORY_ALLOCATOR
|
||||
tristate "MALI_PROTECTED_MEMORY_ALLOCATOR"
|
||||
help
|
||||
This option enables an example implementation of a protected memory allocator
|
||||
for allocation and release of pages of secure memory intended to be used
|
||||
by the firmware of Mali GPU device drivers.
|
||||
37
drivers/base/arm/protected_memory_allocator/Makefile
Normal file
37
drivers/base/arm/protected_memory_allocator/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019-2021 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
# linux build system bootstrap for out-of-tree module
|
||||
|
||||
# default to building for the host
|
||||
ARCH ?= $(shell uname -m)
|
||||
|
||||
# Handle Android Common Kernel source naming
|
||||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
KDIR ?= $(KERNEL_SRC)
|
||||
|
||||
all:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../../include" modules CONFIG_MALI_PROTECTED_MEMORY_ALLOCATOR=m
|
||||
|
||||
clean:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean
|
||||
|
||||
modules_install:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) modules_install
|
||||
34
drivers/base/arm/protected_memory_allocator/build.bp
Normal file
34
drivers/base/arm/protected_memory_allocator/build.bp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2021 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
bob_kernel_module {
|
||||
name: "protected_memory_allocator",
|
||||
srcs: [
|
||||
"Kbuild",
|
||||
"protected_memory_allocator.c",
|
||||
],
|
||||
kbuild_options: ["CONFIG_MALI_PROTECTED_MEMORY_ALLOCATOR=m"],
|
||||
defaults: ["kernel_defaults"],
|
||||
enabled: false,
|
||||
build_csf_only_module: {
|
||||
enabled: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2021 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/protected_memory_allocator.h>
|
||||
|
||||
/* Size of a bitfield element in bytes */
|
||||
#define BITFIELD_ELEM_SIZE sizeof(u64)
|
||||
|
||||
/* We can track whether or not 64 pages are currently allocated in a u64 */
|
||||
#define PAGES_PER_BITFIELD_ELEM (BITFIELD_ELEM_SIZE * BITS_PER_BYTE)
|
||||
|
||||
/* Order 6 (ie, 64) corresponds to the number of pages held in a bitfield */
|
||||
#define ORDER_OF_PAGES_PER_BITFIELD_ELEM 6
|
||||
|
||||
/**
|
||||
* struct simple_pma_device - Simple implementation of a protected memory
|
||||
* allocator device
|
||||
*
|
||||
* @pma_dev: Protected memory allocator device pointer
|
||||
* @dev: Device pointer
|
||||
* @alloc_pages_bitfield_arr: Status of all the physical memory pages within the
|
||||
* protected memory region, one bit per page
|
||||
* @rmem_base: Base address of the reserved memory region
|
||||
* @rmem_size: Size of the reserved memory region, in pages
|
||||
* @num_free_pages: Number of free pages in the memory region
|
||||
* @rmem_lock: Lock to serialize the allocation and freeing of
|
||||
* physical pages from the protected memory region
|
||||
*/
|
||||
struct simple_pma_device {
|
||||
struct protected_memory_allocator_device pma_dev;
|
||||
struct device *dev;
|
||||
u64 *allocated_pages_bitfield_arr;
|
||||
phys_addr_t rmem_base;
|
||||
size_t rmem_size;
|
||||
size_t num_free_pages;
|
||||
spinlock_t rmem_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Number of elements in array 'allocated_pages_bitfield_arr'. If the number of
|
||||
* pages required does not divide exactly by PAGES_PER_BITFIELD_ELEM, adds an
|
||||
* extra page for the remainder.
|
||||
*/
|
||||
#define ALLOC_PAGES_BITFIELD_ARR_SIZE(num_pages) \
|
||||
((PAGES_PER_BITFIELD_ELEM * (0 != (num_pages % PAGES_PER_BITFIELD_ELEM)) + \
|
||||
num_pages) / PAGES_PER_BITFIELD_ELEM)
|
||||
|
||||
/**
|
||||
* Allocate a power-of-two number of pages, N, where
|
||||
* 0 <= N <= ORDER_OF_PAGES_PER_BITFIELD_ELEM - 1. ie, Up to 32 pages. The routine
|
||||
* fills-in a pma structure and sets the appropriate bits in the allocated-pages
|
||||
* bitfield array but assumes the caller has already determined that these are
|
||||
* already clear.
|
||||
*
|
||||
* This routine always works within only a single allocated-pages bitfield element.
|
||||
* It can be thought of as the 'small-granularity' allocator.
|
||||
*/
|
||||
static void small_granularity_alloc(struct simple_pma_device *const epma_dev,
|
||||
size_t alloc_bitfield_idx, size_t start_bit,
|
||||
size_t order,
|
||||
struct protected_memory_allocation *pma)
|
||||
{
|
||||
size_t i;
|
||||
size_t page_idx;
|
||||
u64 *bitfield;
|
||||
size_t alloc_pages_bitfield_size;
|
||||
|
||||
if (WARN_ON(!epma_dev) ||
|
||||
WARN_ON(!pma))
|
||||
return;
|
||||
|
||||
WARN(epma_dev->rmem_size == 0, "%s: rmem_size is 0", __func__);
|
||||
alloc_pages_bitfield_size = ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size);
|
||||
|
||||
WARN(alloc_bitfield_idx >= alloc_pages_bitfield_size,
|
||||
"%s: idx>bf_size: %zu %zu", __FUNCTION__,
|
||||
alloc_bitfield_idx, alloc_pages_bitfield_size);
|
||||
|
||||
WARN((start_bit + (1 << order)) > PAGES_PER_BITFIELD_ELEM,
|
||||
"%s: start=%zu order=%zu ppbe=%zu",
|
||||
__FUNCTION__, start_bit, order, PAGES_PER_BITFIELD_ELEM);
|
||||
|
||||
bitfield = &epma_dev->allocated_pages_bitfield_arr[alloc_bitfield_idx];
|
||||
|
||||
for (i = 0; i < (1 << order); i++) {
|
||||
/* Check the pages represented by this bit are actually free */
|
||||
WARN (*bitfield & (1ULL << (start_bit + i)),
|
||||
"in %s: page not free: %zu %zu %.16llx %zu\n",
|
||||
__FUNCTION__, i, order, *bitfield, alloc_pages_bitfield_size);
|
||||
|
||||
/* Mark the pages as now allocated */
|
||||
*bitfield |= (1ULL << (start_bit + i));
|
||||
}
|
||||
|
||||
/* Compute the page index */
|
||||
page_idx = (alloc_bitfield_idx * PAGES_PER_BITFIELD_ELEM) + start_bit;
|
||||
|
||||
/* Fill-in the allocation struct for the caller */
|
||||
pma->pa = epma_dev->rmem_base + (page_idx << PAGE_SHIFT);
|
||||
pma->order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a power-of-two number of pages, N, where
|
||||
* N >= ORDER_OF_PAGES_PER_BITFIELD_ELEM. ie, 64 pages or more. The routine fills-in
|
||||
* a pma structure and sets the appropriate bits in the allocated-pages bitfield array
|
||||
* but assumes the caller has already determined that these are already clear.
|
||||
*
|
||||
* Unlike small_granularity_alloc, this routine can work with multiple 64-page groups,
|
||||
* ie multiple elements from the allocated-pages bitfield array. However, it always
|
||||
* works with complete sets of these 64-page groups. It can therefore be thought of
|
||||
* as the 'large-granularity' allocator.
|
||||
*/
|
||||
static void large_granularity_alloc(struct simple_pma_device *const epma_dev,
|
||||
size_t start_alloc_bitfield_idx,
|
||||
size_t order,
|
||||
struct protected_memory_allocation *pma)
|
||||
{
|
||||
size_t i;
|
||||
size_t num_pages_to_alloc = (size_t)1 << order;
|
||||
size_t num_bitfield_elements_needed = num_pages_to_alloc / PAGES_PER_BITFIELD_ELEM;
|
||||
size_t start_page_idx = start_alloc_bitfield_idx * PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
if (WARN_ON(!epma_dev) ||
|
||||
WARN_ON(!pma))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Are there anough bitfield array elements (groups of 64 pages)
|
||||
* between the start element and the end of the bitfield array
|
||||
* to fulfill the request?
|
||||
*/
|
||||
WARN((start_alloc_bitfield_idx + order) >= ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size),
|
||||
"%s: start=%zu order=%zu ms=%zu",
|
||||
__FUNCTION__, start_alloc_bitfield_idx, order, epma_dev->rmem_size);
|
||||
|
||||
for (i = 0; i < num_bitfield_elements_needed; i++) {
|
||||
u64 *bitfield = &epma_dev->allocated_pages_bitfield_arr[start_alloc_bitfield_idx + i];
|
||||
|
||||
/* We expect all pages that relate to this bitfield element to be free */
|
||||
WARN((*bitfield != 0),
|
||||
"in %s: pages not free: i=%zu o=%zu bf=%.16llx\n",
|
||||
__FUNCTION__, i, order, *bitfield);
|
||||
|
||||
/* Mark all the pages for this element as not free */
|
||||
*bitfield = ~0ULL;
|
||||
}
|
||||
|
||||
/* Fill-in the allocation struct for the caller */
|
||||
pma->pa = epma_dev->rmem_base + (start_page_idx << PAGE_SHIFT);
|
||||
pma->order = order;
|
||||
}
|
||||
|
||||
static struct protected_memory_allocation *simple_pma_alloc_page(
|
||||
struct protected_memory_allocator_device *pma_dev, unsigned int order)
|
||||
{
|
||||
struct simple_pma_device *const epma_dev =
|
||||
container_of(pma_dev, struct simple_pma_device, pma_dev);
|
||||
struct protected_memory_allocation *pma;
|
||||
size_t num_pages_to_alloc;
|
||||
|
||||
u64 *bitfields = epma_dev->allocated_pages_bitfield_arr;
|
||||
size_t i;
|
||||
size_t bit;
|
||||
size_t count;
|
||||
|
||||
dev_dbg(epma_dev->dev, "%s(pma_dev=%px, order=%u\n",
|
||||
__func__, (void *)pma_dev, order);
|
||||
|
||||
/* This is an example function that follows an extremely simple logic
|
||||
* and is very likely to fail to allocate memory if put under stress.
|
||||
*
|
||||
* The simple_pma_device maintains an array of u64s, with one bit used
|
||||
* to track the status of each page.
|
||||
*
|
||||
* In order to create a memory allocation, the allocator looks for an
|
||||
* adjacent group of cleared bits. This does leave the algorithm open
|
||||
* to fragmentation issues, but is deemed sufficient for now.
|
||||
* If successful, the allocator shall mark all the pages as allocated
|
||||
* and increment the offset accordingly.
|
||||
*
|
||||
* Allocations of 64 pages or more (order 6) can be allocated only with
|
||||
* 64-page alignment, in order to keep the algorithm as simple as
|
||||
* possible. ie, starting from bit 0 of any 64-bit page-allocation
|
||||
* bitfield. For this, the large-granularity allocator is utilised.
|
||||
*
|
||||
* Allocations of lower-order can only be allocated entirely within the
|
||||
* same group of 64 pages, with the small-ganularity allocator (ie
|
||||
* always from the same 64-bit page-allocation bitfield) - again, to
|
||||
* keep things as simple as possible, but flexible to meet
|
||||
* current needs.
|
||||
*/
|
||||
|
||||
num_pages_to_alloc = (size_t)1 << order;
|
||||
|
||||
pma = devm_kzalloc(epma_dev->dev, sizeof(*pma), GFP_KERNEL);
|
||||
if (!pma) {
|
||||
dev_err(epma_dev->dev, "Failed to alloc pma struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock(&epma_dev->rmem_lock);
|
||||
|
||||
if (epma_dev->num_free_pages < num_pages_to_alloc) {
|
||||
dev_err(epma_dev->dev, "not enough free pages\n");
|
||||
devm_kfree(epma_dev->dev, pma);
|
||||
spin_unlock(&epma_dev->rmem_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For order 0-5 (ie, 1 to 32 pages) we always allocate within the same set of 64 pages
|
||||
* Currently, most allocations will be very small (1 page), so the more likely path
|
||||
* here is order < ORDER_OF_PAGES_PER_BITFIELD_ELEM.
|
||||
*/
|
||||
if (likely(order < ORDER_OF_PAGES_PER_BITFIELD_ELEM)) {
|
||||
size_t alloc_pages_bitmap_size = ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size);
|
||||
|
||||
for (i = 0; i < alloc_pages_bitmap_size; i++) {
|
||||
count = 0;
|
||||
|
||||
for (bit = 0; bit < PAGES_PER_BITFIELD_ELEM; bit++) {
|
||||
if (0 == (bitfields[i] & (1ULL << bit))) {
|
||||
if ((count + 1) >= num_pages_to_alloc) {
|
||||
/*
|
||||
* We've found enough free, consecutive pages with which to
|
||||
* make an allocation
|
||||
*/
|
||||
small_granularity_alloc(
|
||||
epma_dev, i,
|
||||
bit - count, order,
|
||||
pma);
|
||||
|
||||
epma_dev->num_free_pages -=
|
||||
num_pages_to_alloc;
|
||||
|
||||
spin_unlock(
|
||||
&epma_dev->rmem_lock);
|
||||
return pma;
|
||||
}
|
||||
|
||||
/* So far so good, but we need more set bits yet */
|
||||
count++;
|
||||
} else {
|
||||
/*
|
||||
* We found an allocated page, so nothing we've seen so far can be used.
|
||||
* Keep looking.
|
||||
*/
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* For allocations of order ORDER_OF_PAGES_PER_BITFIELD_ELEM and above (>= 64 pages), we know
|
||||
* we'll only get allocations for whole groups of 64 pages, which hugely simplifies the task.
|
||||
*/
|
||||
size_t alloc_pages_bitmap_size = ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size);
|
||||
|
||||
/* How many 64-bit bitfield elements will be needed for the allocation? */
|
||||
size_t num_bitfield_elements_needed = num_pages_to_alloc / PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < alloc_pages_bitmap_size; i++) {
|
||||
/* Are all the pages free for the i'th u64 bitfield element? */
|
||||
if (bitfields[i] == 0) {
|
||||
count += PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
if (count >= (1 << order)) {
|
||||
size_t start_idx = (i + 1) - num_bitfield_elements_needed;
|
||||
|
||||
large_granularity_alloc(epma_dev,
|
||||
start_idx,
|
||||
order, pma);
|
||||
|
||||
epma_dev->num_free_pages -= 1 << order;
|
||||
spin_unlock(&epma_dev->rmem_lock);
|
||||
return pma;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&epma_dev->rmem_lock);
|
||||
devm_kfree(epma_dev->dev, pma);
|
||||
|
||||
dev_err(epma_dev->dev, "not enough contiguous pages (need %zu), total free pages left %zu\n",
|
||||
num_pages_to_alloc, epma_dev->num_free_pages);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static phys_addr_t simple_pma_get_phys_addr(
|
||||
struct protected_memory_allocator_device *pma_dev,
|
||||
struct protected_memory_allocation *pma)
|
||||
{
|
||||
struct simple_pma_device *const epma_dev =
|
||||
container_of(pma_dev, struct simple_pma_device, pma_dev);
|
||||
|
||||
dev_dbg(epma_dev->dev, "%s(pma_dev=%px, pma=%px, pa=%llx\n",
|
||||
__func__, (void *)pma_dev, (void *)pma,
|
||||
(unsigned long long)pma->pa);
|
||||
|
||||
return pma->pa;
|
||||
}
|
||||
|
||||
static void simple_pma_free_page(
|
||||
struct protected_memory_allocator_device *pma_dev,
|
||||
struct protected_memory_allocation *pma)
|
||||
{
|
||||
struct simple_pma_device *const epma_dev =
|
||||
container_of(pma_dev, struct simple_pma_device, pma_dev);
|
||||
size_t num_pages_in_allocation;
|
||||
size_t offset;
|
||||
size_t i;
|
||||
size_t bitfield_idx;
|
||||
size_t bitfield_start_bit;
|
||||
size_t page_num;
|
||||
u64 *bitfield;
|
||||
size_t alloc_pages_bitmap_size;
|
||||
size_t num_bitfield_elems_used_by_alloc;
|
||||
|
||||
WARN_ON(pma == NULL);
|
||||
|
||||
dev_dbg(epma_dev->dev, "%s(pma_dev=%px, pma=%px, pa=%llx\n",
|
||||
__func__, (void *)pma_dev, (void *)pma,
|
||||
(unsigned long long)pma->pa);
|
||||
|
||||
WARN_ON(pma->pa < epma_dev->rmem_base);
|
||||
|
||||
/* This is an example function that follows an extremely simple logic
|
||||
* and is vulnerable to abuse.
|
||||
*/
|
||||
offset = (pma->pa - epma_dev->rmem_base);
|
||||
num_pages_in_allocation = (size_t)1 << pma->order;
|
||||
|
||||
/* The number of bitfield elements used by the allocation */
|
||||
num_bitfield_elems_used_by_alloc = num_pages_in_allocation / PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
/* The page number of the first page of the allocation, relative to rmem_base */
|
||||
page_num = offset >> PAGE_SHIFT;
|
||||
|
||||
/* Which u64 bitfield refers to this page? */
|
||||
bitfield_idx = page_num / PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
alloc_pages_bitmap_size = ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size);
|
||||
|
||||
/* Is the allocation within expected bounds? */
|
||||
WARN_ON((bitfield_idx + num_bitfield_elems_used_by_alloc) >= alloc_pages_bitmap_size);
|
||||
|
||||
spin_lock(&epma_dev->rmem_lock);
|
||||
|
||||
if (pma->order < ORDER_OF_PAGES_PER_BITFIELD_ELEM) {
|
||||
bitfield = &epma_dev->allocated_pages_bitfield_arr[bitfield_idx];
|
||||
|
||||
/* Which bit within that u64 bitfield is the lsb covering this allocation? */
|
||||
bitfield_start_bit = page_num % PAGES_PER_BITFIELD_ELEM;
|
||||
|
||||
/* Clear the bits for the pages we're now freeing */
|
||||
*bitfield &= ~(((1ULL << num_pages_in_allocation) - 1) << bitfield_start_bit);
|
||||
}
|
||||
else {
|
||||
WARN(page_num % PAGES_PER_BITFIELD_ELEM,
|
||||
"%s: Expecting allocs of order >= %d to be %zu-page aligned\n",
|
||||
__FUNCTION__, ORDER_OF_PAGES_PER_BITFIELD_ELEM, PAGES_PER_BITFIELD_ELEM);
|
||||
|
||||
for (i = 0; i < num_bitfield_elems_used_by_alloc; i++) {
|
||||
bitfield = &epma_dev->allocated_pages_bitfield_arr[bitfield_idx + i];
|
||||
|
||||
/* We expect all bits to be set (all pages allocated) */
|
||||
WARN((*bitfield != ~0),
|
||||
"%s: alloc being freed is not fully allocated: of=%zu np=%zu bf=%.16llx\n",
|
||||
__FUNCTION__, offset, num_pages_in_allocation, *bitfield);
|
||||
|
||||
/*
|
||||
* Now clear all the bits in the bitfield element to mark all the pages
|
||||
* it refers to as free.
|
||||
*/
|
||||
*bitfield = 0ULL;
|
||||
}
|
||||
}
|
||||
|
||||
epma_dev->num_free_pages += num_pages_in_allocation;
|
||||
spin_unlock(&epma_dev->rmem_lock);
|
||||
devm_kfree(epma_dev->dev, pma);
|
||||
}
|
||||
|
||||
static int protected_memory_allocator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct simple_pma_device *epma_dev;
|
||||
struct device_node *np;
|
||||
phys_addr_t rmem_base;
|
||||
size_t rmem_size;
|
||||
size_t alloc_bitmap_pages_arr_size;
|
||||
#if (KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE)
|
||||
struct reserved_mem *rmem;
|
||||
#endif
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "device node pointer not set\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
np = of_parse_phandle(np, "memory-region", 0);
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "memory-region node not set\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if (KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE)
|
||||
rmem = of_reserved_mem_lookup(np);
|
||||
if (rmem) {
|
||||
rmem_base = rmem->base;
|
||||
rmem_size = rmem->size >> PAGE_SHIFT;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
of_node_put(np);
|
||||
dev_err(&pdev->dev, "could not read reserved memory-region\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
epma_dev = devm_kzalloc(&pdev->dev, sizeof(*epma_dev), GFP_KERNEL);
|
||||
if (!epma_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
epma_dev->pma_dev.ops.pma_alloc_page = simple_pma_alloc_page;
|
||||
epma_dev->pma_dev.ops.pma_get_phys_addr = simple_pma_get_phys_addr;
|
||||
epma_dev->pma_dev.ops.pma_free_page = simple_pma_free_page;
|
||||
epma_dev->pma_dev.owner = THIS_MODULE;
|
||||
epma_dev->dev = &pdev->dev;
|
||||
epma_dev->rmem_base = rmem_base;
|
||||
epma_dev->rmem_size = rmem_size;
|
||||
epma_dev->num_free_pages = rmem_size;
|
||||
spin_lock_init(&epma_dev->rmem_lock);
|
||||
|
||||
alloc_bitmap_pages_arr_size = ALLOC_PAGES_BITFIELD_ARR_SIZE(epma_dev->rmem_size);
|
||||
|
||||
epma_dev->allocated_pages_bitfield_arr = devm_kzalloc(&pdev->dev,
|
||||
alloc_bitmap_pages_arr_size * BITFIELD_ELEM_SIZE, GFP_KERNEL);
|
||||
|
||||
if (!epma_dev->allocated_pages_bitfield_arr) {
|
||||
dev_err(&pdev->dev, "failed to allocate resources\n");
|
||||
devm_kfree(&pdev->dev, epma_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (epma_dev->rmem_size % PAGES_PER_BITFIELD_ELEM) {
|
||||
size_t extra_pages =
|
||||
alloc_bitmap_pages_arr_size * PAGES_PER_BITFIELD_ELEM -
|
||||
epma_dev->rmem_size;
|
||||
size_t last_bitfield_index = alloc_bitmap_pages_arr_size - 1;
|
||||
|
||||
/* Mark the extra pages (that lie outside the reserved range) as
|
||||
* always in use.
|
||||
*/
|
||||
epma_dev->allocated_pages_bitfield_arr[last_bitfield_index] =
|
||||
((1ULL << extra_pages) - 1) <<
|
||||
(PAGES_PER_BITFIELD_ELEM - extra_pages);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, &epma_dev->pma_dev);
|
||||
dev_info(&pdev->dev,
|
||||
"Protected memory allocator probed successfully\n");
|
||||
dev_info(&pdev->dev, "Protected memory region: base=%llx num pages=%zu\n",
|
||||
(unsigned long long)rmem_base, rmem_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int protected_memory_allocator_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct protected_memory_allocator_device *pma_dev =
|
||||
platform_get_drvdata(pdev);
|
||||
struct simple_pma_device *epma_dev;
|
||||
struct device *dev;
|
||||
|
||||
if (!pma_dev)
|
||||
return -EINVAL;
|
||||
|
||||
epma_dev = container_of(pma_dev, struct simple_pma_device, pma_dev);
|
||||
dev = epma_dev->dev;
|
||||
|
||||
if (epma_dev->num_free_pages < epma_dev->rmem_size) {
|
||||
dev_warn(&pdev->dev, "Leaking %zu pages of protected memory\n",
|
||||
epma_dev->rmem_size - epma_dev->num_free_pages);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
devm_kfree(dev, epma_dev->allocated_pages_bitfield_arr);
|
||||
devm_kfree(dev, epma_dev);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"Protected memory allocator removed successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id protected_memory_allocator_dt_ids[] = {
|
||||
{ .compatible = "arm,protected-memory-allocator" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, protected_memory_allocator_dt_ids);
|
||||
|
||||
static struct platform_driver protected_memory_allocator_driver = {
|
||||
.probe = protected_memory_allocator_probe,
|
||||
.remove = protected_memory_allocator_remove,
|
||||
.driver = {
|
||||
.name = "simple_protected_memory_allocator",
|
||||
.of_match_table = of_match_ptr(protected_memory_allocator_dt_ids),
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(protected_memory_allocator_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("ARM Ltd.");
|
||||
MODULE_VERSION("1.0");
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* (C) COPYRIGHT 2019 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.
|
||||
*/
|
||||
|
||||
bob_kernel_module {
|
||||
name: "memory_group_manager",
|
||||
srcs: [
|
||||
"Kbuild",
|
||||
"memory_group_manager.c",
|
||||
],
|
||||
kbuild_options: ["CONFIG_MALI_MEMORY_GROUP_MANAGER=m"],
|
||||
defaults: ["kernel_defaults"],
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2012, 2020 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MALI_MIDGARD) += midgard/
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2012 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
#
|
||||
source "drivers/gpu/arm/mali400/mali/Kconfig"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2012-2020 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2012-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,16 +16,14 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
# Driver version string which is returned to userspace via an ioctl
|
||||
MALI_RELEASE_NAME ?= "g2p0-01eac0"
|
||||
MALI_RELEASE_NAME ?= '"g6p0-01eac0"'
|
||||
|
||||
# Paths required for build
|
||||
|
||||
# make $(src) as absolute path if it isn't already, by prefixing $(srctree)
|
||||
# make $(src) as absolute path if it is not already, by prefixing $(srctree)
|
||||
src:=$(if $(patsubst /%,,$(src)),$(srctree)/$(src),$(src))
|
||||
KBASE_PATH = $(src)
|
||||
KBASE_PLATFORM_PATH = $(KBASE_PATH)/platform_dummy
|
||||
@@ -32,11 +31,8 @@ UMP_PATH = $(src)/../../../base
|
||||
|
||||
# Set up defaults if not defined by build system
|
||||
MALI_CUSTOMER_RELEASE ?= 1
|
||||
MALI_USE_CSF ?= 0
|
||||
MALI_UNIT_TEST ?= 0
|
||||
MALI_KERNEL_TEST_API ?= 0
|
||||
MALI_COVERAGE ?= 0
|
||||
MALI_JIT_PRESSURE_LIMIT_BASE ?= 1
|
||||
CONFIG_MALI_PLATFORM_NAME ?= "devicetree"
|
||||
# Experimental features (corresponding -D definition should be appended to
|
||||
# DEFINES below, e.g. for MALI_EXPERIMENTAL_FEATURE,
|
||||
@@ -46,6 +42,20 @@ CONFIG_MALI_PLATFORM_NAME ?= "devicetree"
|
||||
# MALI_EXPERIMENTAL_FEATURE ?= 0
|
||||
MALI_INCREMENTAL_RENDERING ?= 0
|
||||
|
||||
ifeq ($(CONFIG_MALI_CSF_SUPPORT),y)
|
||||
MALI_JIT_PRESSURE_LIMIT_BASE = 0
|
||||
MALI_USE_CSF = 1
|
||||
else
|
||||
MALI_JIT_PRESSURE_LIMIT_BASE ?= 1
|
||||
MALI_USE_CSF ?= 0
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_MALI_KUTF), n)
|
||||
MALI_KERNEL_TEST_API ?= 1
|
||||
else
|
||||
MALI_KERNEL_TEST_API ?= 0
|
||||
endif
|
||||
|
||||
# Set up our defines, which will be passed to gcc
|
||||
DEFINES = \
|
||||
-DMALI_CUSTOMER_RELEASE=$(MALI_CUSTOMER_RELEASE) \
|
||||
@@ -53,7 +63,7 @@ DEFINES = \
|
||||
-DMALI_KERNEL_TEST_API=$(MALI_KERNEL_TEST_API) \
|
||||
-DMALI_UNIT_TEST=$(MALI_UNIT_TEST) \
|
||||
-DMALI_COVERAGE=$(MALI_COVERAGE) \
|
||||
-DMALI_RELEASE_NAME=\"$(MALI_RELEASE_NAME)\" \
|
||||
-DMALI_RELEASE_NAME=$(MALI_RELEASE_NAME) \
|
||||
-DMALI_JIT_PRESSURE_LIMIT_BASE=$(MALI_JIT_PRESSURE_LIMIT_BASE) \
|
||||
-DMALI_INCREMENTAL_RENDERING=$(MALI_INCREMENTAL_RENDERING)
|
||||
|
||||
@@ -90,7 +100,6 @@ SRC := \
|
||||
mali_kbase_config.c \
|
||||
mali_kbase_vinstr.c \
|
||||
mali_kbase_hwcnt.c \
|
||||
mali_kbase_hwcnt_backend_jm.c \
|
||||
mali_kbase_hwcnt_gpu.c \
|
||||
mali_kbase_hwcnt_legacy.c \
|
||||
mali_kbase_hwcnt_types.c \
|
||||
@@ -104,7 +113,6 @@ SRC := \
|
||||
mali_kbase_mem_profile_debugfs.c \
|
||||
mmu/mali_kbase_mmu.c \
|
||||
mmu/mali_kbase_mmu_hw_direct.c \
|
||||
mmu/mali_kbase_mmu_mode_lpae.c \
|
||||
mmu/mali_kbase_mmu_mode_aarch64.c \
|
||||
mali_kbase_disjoint_events.c \
|
||||
mali_kbase_debug_mem_view.c \
|
||||
@@ -115,6 +123,7 @@ SRC := \
|
||||
mali_kbase_strings.c \
|
||||
mali_kbase_as_fault_debugfs.c \
|
||||
mali_kbase_regs_history_debugfs.c \
|
||||
mali_kbase_dvfs_debugfs.c \
|
||||
mali_power_gpu_frequency_trace.c \
|
||||
mali_kbase_trace_gpu_mem.c \
|
||||
thirdparty/mali_kbase_mmap.c \
|
||||
@@ -126,6 +135,8 @@ SRC := \
|
||||
|
||||
ifeq ($(MALI_USE_CSF),1)
|
||||
SRC += \
|
||||
mali_kbase_hwcnt_backend_csf.c \
|
||||
mali_kbase_hwcnt_backend_csf_if_fw.c \
|
||||
debug/backend/mali_kbase_debug_ktrace_csf.c \
|
||||
device/backend/mali_kbase_device_csf.c \
|
||||
device/backend/mali_kbase_device_hw_csf.c \
|
||||
@@ -135,6 +146,7 @@ ifeq ($(MALI_USE_CSF),1)
|
||||
context/backend/mali_kbase_context_csf.c
|
||||
else
|
||||
SRC += \
|
||||
mali_kbase_hwcnt_backend_jm.c \
|
||||
mali_kbase_dummy_job_wa.c \
|
||||
mali_kbase_debug_job_fault.c \
|
||||
mali_kbase_event.c \
|
||||
@@ -156,9 +168,6 @@ ifeq ($(CONFIG_MALI_CINSTR_GWT),y)
|
||||
SRC += mali_kbase_gwt.c
|
||||
endif
|
||||
|
||||
ifeq ($(MALI_UNIT_TEST),1)
|
||||
SRC += tl/mali_kbase_timeline_test.c
|
||||
endif
|
||||
|
||||
ifeq ($(MALI_CUSTOMER_RELEASE),0)
|
||||
SRC += mali_kbase_regs_dump_debugfs.c
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2012-2020 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2012-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,8 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@@ -31,6 +30,13 @@ menuconfig MALI_BIFROST
|
||||
To compile this driver as a module, choose M here:
|
||||
this will generate a single module, called mali_kbase.
|
||||
|
||||
config MALI_CSF_SUPPORT
|
||||
bool "Mali CSF based GPU support"
|
||||
depends on MALI_BIFROST=m
|
||||
default n
|
||||
help
|
||||
Enables support for CSF based GPUs.
|
||||
|
||||
config MALI_BIFROST_GATOR_SUPPORT
|
||||
bool "Enable Streamline tracing support"
|
||||
depends on MALI_BIFROST
|
||||
@@ -277,10 +283,20 @@ config MALI_JOB_DUMP
|
||||
minimal overhead when not in use. Enable only if you know what
|
||||
you are doing.
|
||||
|
||||
config MALI_BIFROST_PRFCNT_SET_SECONDARY
|
||||
bool "Use secondary set of performance counters"
|
||||
choice
|
||||
prompt "Performance counters set"
|
||||
default MALI_PRFCNT_SET_PRIMARY
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT
|
||||
|
||||
config MALI_PRFCNT_SET_PRIMARY
|
||||
bool "Primary"
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT
|
||||
help
|
||||
Select this option to use primary set of performance counters.
|
||||
|
||||
config MALI_BIFROST_PRFCNT_SET_SECONDARY
|
||||
bool "Secondary"
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT
|
||||
default n
|
||||
help
|
||||
Select this option to use secondary set of performance counters. Kernel
|
||||
features that depend on an access to the primary set of counters may
|
||||
@@ -288,21 +304,43 @@ config MALI_BIFROST_PRFCNT_SET_SECONDARY
|
||||
from working optimally and may cause instrumentation tools to return
|
||||
bogus results.
|
||||
|
||||
If unsure, say N.
|
||||
If unsure, use MALI_PRFCNT_SET_PRIMARY.
|
||||
|
||||
config MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS
|
||||
bool "Use secondary set of performance counters"
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT && !MALI_BIFROST_PRFCNT_SET_SECONDARY && DEBUG_FS
|
||||
config MALI_PRFCNT_SET_TERTIARY
|
||||
bool "Tertiary"
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT
|
||||
help
|
||||
Select this option to use tertiary set of performance counters. Kernel
|
||||
features that depend on an access to the primary set of counters may
|
||||
become unavailable. Enabling this option will prevent power management
|
||||
from working optimally and may cause instrumentation tools to return
|
||||
bogus results.
|
||||
|
||||
If unsure, use MALI_PRFCNT_SET_PRIMARY.
|
||||
|
||||
endchoice
|
||||
|
||||
config MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS
|
||||
bool "Allow runtime selection of performance counters set via debugfs"
|
||||
depends on MALI_BIFROST && MALI_BIFROST_EXPERT && DEBUG_FS
|
||||
default n
|
||||
help
|
||||
Select this option to make the secondary set of performance counters
|
||||
available at runtime via debugfs. Kernel features that depend on an
|
||||
access to the primary set of counters may become unavailable.
|
||||
|
||||
If no runtime debugfs option is set, the build time counter set
|
||||
choice will be used.
|
||||
|
||||
This feature is unsupported and unstable, and may break at any time.
|
||||
Enabling this option will prevent power management from working
|
||||
optimally and may cause instrumentation tools to return bogus results.
|
||||
|
||||
No validation is done on the debugfs input. Invalid input could cause
|
||||
performance counter errors. Valid inputs are the values accepted by
|
||||
the SET_SELECT bits of the PRFCNT_CONFIG register as defined in the
|
||||
architecture specification.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
source "drivers/gpu/arm/midgard/platform/Kconfig"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2010-2019 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2010-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,24 +16,49 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
# Handle Android Common Kernel source naming
|
||||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
KDIR ?= $(KERNEL_SRC)
|
||||
|
||||
KDIR ?= /lib/modules/$(shell uname -r)/build
|
||||
# out-of-tree
|
||||
ifeq ($(KBUILD_EXTMOD),)
|
||||
export CONFIG_MALI_MIDGARD?=m
|
||||
|
||||
ifneq ($(CONFIG_MALI_MIDGARD),n)
|
||||
export CONFIG_MALI_CSF_SUPPORT?=n
|
||||
export CONFIG_MALI_KUTF?=m
|
||||
export CONFIG_MALI_REAL_HW?=y
|
||||
|
||||
# Handle default y/m in Kconfig
|
||||
export CONFIG_MALI_BIFROST_GATOR_SUPPORT?=y
|
||||
export CONFIG_MALI_BIFROST_DEVFREQ?=n
|
||||
ifneq ($(CONFIG_PM_DEVFREQ),n)
|
||||
export CONFIG_MALI_BIFROST_DEVFREQ?=y
|
||||
endif
|
||||
|
||||
DEFINES += -DCONFIG_MALI_MIDGARD=$(CONFIG_MALI_MIDGARD) \
|
||||
-DCONFIG_MALI_CSF_SUPPORT=$(CONFIG_MALI_CSF_SUPPORT) \
|
||||
-DCONFIG_MALI_KUTF=$(CONFIG_MALI_KUTF) \
|
||||
-DCONFIG_MALI_REAL_HW=$(CONFIG_MALI_REAL_HW) \
|
||||
-DCONFIG_MALI_GATOR_SUPPORT=$(CONFIG_MALI_BIFROST_GATOR_SUPPORT) \
|
||||
-DCONFIG_MALI_DEVFREQ=$(CONFIG_MALI_BIFROST_DEVFREQ)
|
||||
|
||||
export DEFINES
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
BUSLOG_PATH_RELATIVE = $(CURDIR)/../../../..
|
||||
KBASE_PATH_RELATIVE = $(CURDIR)
|
||||
|
||||
ifeq ($(CONFIG_MALI_BUSLOG),y)
|
||||
#Add bus logger symbols
|
||||
EXTRA_SYMBOLS += $(BUSLOG_PATH_RELATIVE)/drivers/base/bus_logger/Module.symvers
|
||||
endif
|
||||
|
||||
# we get the symbols from modules using KBUILD_EXTRA_SYMBOLS to prevent warnings about unknown functions
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../../include -I$(CURDIR)/../../../../tests/include $(SCONS_CFLAGS)" $(SCONS_CONFIGS) KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" modules
|
||||
|
||||
modules_install:
|
||||
$(MAKE) -C $(KDIR) M=$(CURDIR) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KDIR) M=$(CURDIR) clean
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2010, 2013, 2018 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2010, 2013, 2018-2020 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,9 +16,6 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
EXTRA_CFLAGS += -I$(ROOT) -I$(KBASE_PATH) -I$(KBASE_PATH)/platform_$(PLATFORM)
|
||||
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2012-2020 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2012-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# 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.
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
menuconfig MALI_BIFROST
|
||||
bool "Mali Midgard series support"
|
||||
default y
|
||||
@@ -22,6 +27,13 @@ menuconfig MALI_BIFROST
|
||||
To compile this driver as a module, choose M here:
|
||||
this will generate a single module, called mali_kbase.
|
||||
|
||||
config MALI_CSF_SUPPORT
|
||||
bool "Mali CSF based GPU support"
|
||||
depends on MALI_BIFROST
|
||||
default n
|
||||
help
|
||||
Enables support for CSF based GPUs.
|
||||
|
||||
config MALI_BIFROST_GATOR_SUPPORT
|
||||
bool "Enable Streamline tracing support"
|
||||
depends on MALI_BIFROST && !BACKEND_USER
|
||||
@@ -272,6 +284,9 @@ config MALI_GEM5_BUILD
|
||||
# Instrumentation options.
|
||||
|
||||
# config MALI_JOB_DUMP exists in the Kernel Kconfig but is configured using CINSTR_JOB_DUMP in Mconfig.
|
||||
# config MALI_PRFCNT_SET_PRIMARY exists in the Kernel Kconfig but is configured using CINSTR_PRIMARY_HWC in Mconfig.
|
||||
# config MALI_BIFROST_PRFCNT_SET_SECONDARY exists in the Kernel Kconfig but is configured using CINSTR_SECONDARY_HWC in Mconfig.
|
||||
# config MALI_PRFCNT_SET_TERTIARY exists in the Kernel Kconfig but is configured using CINSTR_TERTIARY_HWC in Mconfig.
|
||||
# config MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS exists in the Kernel Kconfig but is configured using CINSTR_HWC_SET_SELECT_VIA_DEBUG_FS in Mconfig.
|
||||
|
||||
source "kernel/drivers/gpu/arm/midgard/tests/Mconfig"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2019-2020 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,10 +16,8 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
mali_kbase-y += \
|
||||
bifrost_kbase-y += \
|
||||
arbiter/mali_kbase_arbif.o \
|
||||
arbiter/mali_kbase_arbiter_pm.o
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -18,13 +17,10 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mali_kbase_arbif.c
|
||||
* Mali arbiter interface APIs to share GPU between Virtual Machines
|
||||
* DOC: Mali arbiter interface APIs to share GPU between Virtual Machines
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
@@ -34,29 +30,148 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include "mali_kbase_arbiter_interface.h"
|
||||
|
||||
/* Arbiter interface version against which was implemented this module */
|
||||
#define MALI_REQUIRED_KBASE_ARBITER_INTERFACE_VERSION 5
|
||||
#if MALI_REQUIRED_KBASE_ARBITER_INTERFACE_VERSION != \
|
||||
MALI_KBASE_ARBITER_INTERFACE_VERSION
|
||||
#error "Unsupported Mali Arbiter interface version."
|
||||
#endif
|
||||
|
||||
static void on_max_config(struct device *dev, uint32_t max_l2_slices,
|
||||
uint32_t max_core_mask)
|
||||
{
|
||||
struct kbase_device *kbdev;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s(): dev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbdev = dev_get_drvdata(dev);
|
||||
if (!kbdev) {
|
||||
dev_err(dev, "%s(): kbdev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!max_l2_slices || !max_core_mask) {
|
||||
dev_dbg(dev,
|
||||
"%s(): max_config ignored as one of the fields is zero",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set the max config info in the kbase device */
|
||||
kbase_arbiter_set_max_config(kbdev, max_l2_slices, max_core_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* on_update_freq() - Updates GPU clock frequency
|
||||
* @dev: arbiter interface device handle
|
||||
* @freq: GPU clock frequency value reported from arbiter
|
||||
*
|
||||
* call back function to update GPU clock frequency with
|
||||
* new value from arbiter
|
||||
*/
|
||||
static void on_update_freq(struct device *dev, uint32_t freq)
|
||||
{
|
||||
struct kbase_device *kbdev;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s(): dev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbdev = dev_get_drvdata(dev);
|
||||
if (!kbdev) {
|
||||
dev_err(dev, "%s(): kbdev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbase_arbiter_pm_update_gpu_freq(&kbdev->arb.arb_freq, freq);
|
||||
}
|
||||
|
||||
/**
|
||||
* on_gpu_stop() - sends KBASE_VM_GPU_STOP_EVT event on VM stop
|
||||
* @dev: arbiter interface device handle
|
||||
*
|
||||
* call back function to signal a GPU STOP event from arbiter interface
|
||||
*/
|
||||
static void on_gpu_stop(struct device *dev)
|
||||
{
|
||||
struct kbase_device *kbdev = dev_get_drvdata(dev);
|
||||
struct kbase_device *kbdev;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s(): dev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbdev = dev_get_drvdata(dev);
|
||||
if (!kbdev) {
|
||||
dev_err(dev, "%s(): kbdev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
KBASE_TLSTREAM_TL_ARBITER_STOP_REQUESTED(kbdev, kbdev);
|
||||
kbase_arbiter_pm_vm_event(kbdev, KBASE_VM_GPU_STOP_EVT);
|
||||
}
|
||||
|
||||
/**
|
||||
* on_gpu_granted() - sends KBASE_VM_GPU_GRANTED_EVT event on GPU granted
|
||||
* @dev: arbiter interface device handle
|
||||
*
|
||||
* call back function to signal a GPU GRANT event from arbiter interface
|
||||
*/
|
||||
static void on_gpu_granted(struct device *dev)
|
||||
{
|
||||
struct kbase_device *kbdev = dev_get_drvdata(dev);
|
||||
struct kbase_device *kbdev;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s(): dev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbdev = dev_get_drvdata(dev);
|
||||
if (!kbdev) {
|
||||
dev_err(dev, "%s(): kbdev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
KBASE_TLSTREAM_TL_ARBITER_GRANTED(kbdev, kbdev);
|
||||
kbase_arbiter_pm_vm_event(kbdev, KBASE_VM_GPU_GRANTED_EVT);
|
||||
}
|
||||
|
||||
/**
|
||||
* on_gpu_lost() - sends KBASE_VM_GPU_LOST_EVT event on GPU granted
|
||||
* @dev: arbiter interface device handle
|
||||
*
|
||||
* call back function to signal a GPU LOST event from arbiter interface
|
||||
*/
|
||||
static void on_gpu_lost(struct device *dev)
|
||||
{
|
||||
struct kbase_device *kbdev = dev_get_drvdata(dev);
|
||||
struct kbase_device *kbdev;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s(): dev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbdev = dev_get_drvdata(dev);
|
||||
if (!kbdev) {
|
||||
dev_err(dev, "%s(): kbdev is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
kbase_arbiter_pm_vm_event(kbdev, KBASE_VM_GPU_LOST_EVT);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_init() - Kbase Arbiter interface initialisation.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Initialise Kbase Arbiter interface and assign callback functions.
|
||||
*
|
||||
* Return: 0 on success else a Linux error code
|
||||
*/
|
||||
int kbase_arbif_init(struct kbase_device *kbdev)
|
||||
{
|
||||
#ifdef CONFIG_OF
|
||||
@@ -100,6 +215,12 @@ int kbase_arbif_init(struct kbase_device *kbdev)
|
||||
ops.arb_vm_gpu_stop = on_gpu_stop;
|
||||
ops.arb_vm_gpu_granted = on_gpu_granted;
|
||||
ops.arb_vm_gpu_lost = on_gpu_lost;
|
||||
ops.arb_vm_max_config = on_max_config;
|
||||
ops.arb_vm_update_freq = on_update_freq;
|
||||
|
||||
|
||||
kbdev->arb.arb_freq.arb_freq = 0;
|
||||
mutex_init(&kbdev->arb.arb_freq.arb_freq_lock);
|
||||
|
||||
/* register kbase arbiter_if callbacks */
|
||||
if (arb_if->vm_ops.vm_arb_register_dev) {
|
||||
@@ -111,6 +232,7 @@ int kbase_arbif_init(struct kbase_device *kbdev)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
dev_dbg(kbdev->dev, "No arbiter without Device Tree support\n");
|
||||
kbdev->arb.arb_dev = NULL;
|
||||
@@ -119,6 +241,12 @@ int kbase_arbif_init(struct kbase_device *kbdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_destroy() - De-init Kbase arbiter interface
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* De-initialise Kbase arbiter interface
|
||||
*/
|
||||
void kbase_arbif_destroy(struct kbase_device *kbdev)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
@@ -133,16 +261,45 @@ void kbase_arbif_destroy(struct kbase_device *kbdev)
|
||||
kbdev->arb.arb_dev = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_get_max_config() - Request max config info
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* call back function from arb interface to arbiter requesting max config info
|
||||
*/
|
||||
void kbase_arbif_get_max_config(struct kbase_device *kbdev)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
|
||||
if (arb_if && arb_if->vm_ops.vm_arb_get_max_config) {
|
||||
dev_dbg(kbdev->dev, "%s\n", __func__);
|
||||
arb_if->vm_ops.vm_arb_get_max_config(arb_if);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_gpu_request() - Request GPU from
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* call back function from arb interface to arbiter requesting GPU for VM
|
||||
*/
|
||||
void kbase_arbif_gpu_request(struct kbase_device *kbdev)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
|
||||
if (arb_if && arb_if->vm_ops.vm_arb_gpu_request) {
|
||||
dev_dbg(kbdev->dev, "%s\n", __func__);
|
||||
KBASE_TLSTREAM_TL_ARBITER_REQUESTED(kbdev, kbdev);
|
||||
arb_if->vm_ops.vm_arb_gpu_request(arb_if);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_gpu_stopped() - send GPU stopped message to the arbiter
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
* @gpu_required: GPU request flag
|
||||
*
|
||||
*/
|
||||
void kbase_arbif_gpu_stopped(struct kbase_device *kbdev, u8 gpu_required)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
@@ -154,6 +311,12 @@ void kbase_arbif_gpu_stopped(struct kbase_device *kbdev, u8 gpu_required)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_gpu_active() - Sends a GPU_ACTIVE message to the Arbiter
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Informs the arbiter VM is active
|
||||
*/
|
||||
void kbase_arbif_gpu_active(struct kbase_device *kbdev)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
@@ -164,6 +327,12 @@ void kbase_arbif_gpu_active(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbif_gpu_idle() - Inform the arbiter that the VM has gone idle
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Informs the arbiter VM is idle
|
||||
*/
|
||||
void kbase_arbif_gpu_idle(struct kbase_device *kbdev)
|
||||
{
|
||||
struct arbiter_if_dev *arb_if = kbdev->arb.arb_if;
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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
|
||||
@@ -38,12 +17,10 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Mali arbiter interface APIs to share GPU between Virtual Machines
|
||||
* DOC: Mali arbiter interface APIs to share GPU between Virtual Machines
|
||||
*/
|
||||
|
||||
#ifndef _MALI_KBASE_ARBIF_H_
|
||||
@@ -94,6 +71,14 @@ int kbase_arbif_init(struct kbase_device *kbdev);
|
||||
*/
|
||||
void kbase_arbif_destroy(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbif_get_max_config() - Request max config info
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* call back function from arb interface to arbiter requesting max config info
|
||||
*/
|
||||
void kbase_arbif_get_max_config(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbif_gpu_request() - Send GPU request message to the arbiter
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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
|
||||
@@ -38,7 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -66,7 +44,8 @@
|
||||
* @vm_resume_work: Work item for vm_arb_wq to resume current work on GPU
|
||||
* @vm_arb_starting: Work queue resume in progress
|
||||
* @vm_arb_stopping: Work queue suspend in progress
|
||||
* @vm_arb_users_waiting: Count of users waiting for GPU
|
||||
* @interrupts_installed: Flag set when interrupts are installed
|
||||
* @vm_request_timer: Timer to monitor GPU request
|
||||
*/
|
||||
struct kbase_arbiter_vm_state {
|
||||
struct kbase_device *kbdev;
|
||||
@@ -78,7 +57,8 @@ struct kbase_arbiter_vm_state {
|
||||
struct work_struct vm_resume_work;
|
||||
bool vm_arb_starting;
|
||||
bool vm_arb_stopping;
|
||||
int vm_arb_users_waiting;
|
||||
bool interrupts_installed;
|
||||
struct hrtimer vm_request_timer;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,10 +66,12 @@ struct kbase_arbiter_vm_state {
|
||||
* allocated from the probe method of Mali driver
|
||||
* @arb_if: Pointer to the arbiter interface device
|
||||
* @arb_dev: Pointer to the arbiter device
|
||||
* @arb_freq: GPU clock frequency retrieved from arbiter.
|
||||
*/
|
||||
struct kbase_arbiter_device {
|
||||
struct arbiter_if_dev *arb_if;
|
||||
struct device *arb_dev;
|
||||
struct kbase_arbiter_freq arb_freq;
|
||||
};
|
||||
|
||||
#endif /* _MALI_KBASE_ARBITER_DEFS_H_ */
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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
|
||||
@@ -38,7 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -50,7 +28,7 @@
|
||||
#define _MALI_KBASE_ARBITER_INTERFACE_H_
|
||||
|
||||
/**
|
||||
* @brief Mali arbiter interface version
|
||||
* Mali arbiter interface version
|
||||
*
|
||||
* This specifies the current version of the configuration interface. Whenever
|
||||
* the arbiter interface changes, so that integration effort is required, the
|
||||
@@ -61,8 +39,15 @@
|
||||
* 1 - Added the Mali arbiter configuration interface.
|
||||
* 2 - Strip out reference code from header
|
||||
* 3 - Removed DVFS utilization interface (DVFS moved to arbiter side)
|
||||
* 4 - Added max_config support
|
||||
* 5 - Added GPU clock frequency reporting support from arbiter
|
||||
*/
|
||||
#define MALI_KBASE_ARBITER_INTERFACE_VERSION 3
|
||||
#define MALI_KBASE_ARBITER_INTERFACE_VERSION 5
|
||||
|
||||
/**
|
||||
* NO_FREQ is used in case platform doesn't support reporting frequency
|
||||
*/
|
||||
#define NO_FREQ 0
|
||||
|
||||
struct arbiter_if_dev;
|
||||
|
||||
@@ -108,6 +93,27 @@ struct arbiter_if_arb_vm_ops {
|
||||
* If successful, will respond with a vm_arb_gpu_stopped message.
|
||||
*/
|
||||
void (*arb_vm_gpu_lost)(struct device *dev);
|
||||
|
||||
/**
|
||||
* arb_vm_max_config() - Send max config info to the VM
|
||||
* @dev: The arbif kernel module device.
|
||||
* @max_l2_slices: The maximum number of L2 slices.
|
||||
* @max_core_mask: The largest core mask.
|
||||
*
|
||||
* Informs KBase the maximum resources that can be allocated to the
|
||||
* partition in use.
|
||||
*/
|
||||
void (*arb_vm_max_config)(struct device *dev, uint32_t max_l2_slices,
|
||||
uint32_t max_core_mask);
|
||||
|
||||
/**
|
||||
* arb_vm_update_freq() - GPU clock frequency has been updated
|
||||
* @dev: The arbif kernel module device.
|
||||
* @freq: GPU clock frequency value reported from arbiter
|
||||
*
|
||||
* Informs KBase that the GPU clock frequency has been updated.
|
||||
*/
|
||||
void (*arb_vm_update_freq)(struct device *dev, uint32_t freq);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -136,6 +142,13 @@ struct arbiter_if_vm_arb_ops {
|
||||
*/
|
||||
void (*vm_arb_unregister_dev)(struct arbiter_if_dev *arbif_dev);
|
||||
|
||||
/**
|
||||
* vm_arb_gpu_get_max_config() - Request the max config from the
|
||||
* Arbiter.
|
||||
* @arbif_dev: The arbiter interface we want to issue the request.
|
||||
*/
|
||||
void (*vm_arb_get_max_config)(struct arbiter_if_dev *arbif_dev);
|
||||
|
||||
/**
|
||||
* vm_arb_gpu_request() - Ask the arbiter interface for GPU access.
|
||||
* @arbif_dev: The arbiter interface we want to issue the request.
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -18,12 +17,10 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mali_kbase_arbiter_pm.c
|
||||
* @file
|
||||
* Mali arbiter power manager state machine and APIs
|
||||
*/
|
||||
|
||||
@@ -34,11 +31,34 @@
|
||||
#include <mali_kbase_hwcnt_context.h>
|
||||
#include <mali_kbase_pm_internal.h>
|
||||
#include <tl/mali_kbase_tracepoints.h>
|
||||
#include <mali_kbase_gpuprops.h>
|
||||
|
||||
/* A dmesg warning will occur if the GPU is not granted
|
||||
* after the following time (in milliseconds) has ellapsed.
|
||||
*/
|
||||
#define GPU_REQUEST_TIMEOUT 1000
|
||||
|
||||
#define MAX_L2_SLICES_MASK 0xFF
|
||||
|
||||
/* Maximum time in ms, before deferring probe incase
|
||||
* GPU_GRANTED message is not received
|
||||
*/
|
||||
static int gpu_req_timeout = 1;
|
||||
module_param(gpu_req_timeout, int, 0644);
|
||||
MODULE_PARM_DESC(gpu_req_timeout,
|
||||
"On a virtualized platform, if the GPU is not granted within this time(ms) kbase will defer the probe");
|
||||
|
||||
static void kbase_arbiter_pm_vm_wait_gpu_assignment(struct kbase_device *kbdev);
|
||||
static inline bool kbase_arbiter_pm_vm_gpu_assigned_lockheld(
|
||||
struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_state_str() - Helper function to get string
|
||||
* for kbase VM state.(debug)
|
||||
* @state: kbase VM state
|
||||
*
|
||||
* Return: string representation of Kbase_vm_state
|
||||
*/
|
||||
static inline const char *kbase_arbiter_pm_vm_state_str(
|
||||
enum kbase_vm_state state)
|
||||
{
|
||||
@@ -73,6 +93,13 @@ static inline const char *kbase_arbiter_pm_vm_state_str(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_event_str() - Helper function to get string
|
||||
* for kbase VM event.(debug)
|
||||
* @evt: kbase VM state
|
||||
*
|
||||
* Return: String representation of Kbase_arbif_event
|
||||
*/
|
||||
static inline const char *kbase_arbiter_pm_vm_event_str(
|
||||
enum kbase_arbif_evt evt)
|
||||
{
|
||||
@@ -99,6 +126,13 @@ static inline const char *kbase_arbiter_pm_vm_event_str(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_set_state() - Sets new kbase_arbiter_vm_state
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
* @new_state: kbase VM new state
|
||||
*
|
||||
* This function sets the new state for the VM
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_set_state(struct kbase_device *kbdev,
|
||||
enum kbase_vm_state new_state)
|
||||
{
|
||||
@@ -107,11 +141,22 @@ static void kbase_arbiter_pm_vm_set_state(struct kbase_device *kbdev,
|
||||
dev_dbg(kbdev->dev, "VM set_state %s -> %s",
|
||||
kbase_arbiter_pm_vm_state_str(arb_vm_state->vm_state),
|
||||
kbase_arbiter_pm_vm_state_str(new_state));
|
||||
|
||||
lockdep_assert_held(&arb_vm_state->vm_state_lock);
|
||||
arb_vm_state->vm_state = new_state;
|
||||
if (new_state != KBASE_VM_STATE_INITIALIZING_WITH_GPU &&
|
||||
new_state != KBASE_VM_STATE_INITIALIZING)
|
||||
KBASE_KTRACE_ADD(kbdev, ARB_VM_STATE, NULL, new_state);
|
||||
wake_up(&arb_vm_state->vm_state_wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_suspend_wq() - suspend work queue of the driver.
|
||||
* @data: work queue
|
||||
*
|
||||
* Suspends work queue of the driver, when VM is in SUSPEND_PENDING or
|
||||
* STOPPING_IDLE or STOPPING_ACTIVE state
|
||||
*/
|
||||
static void kbase_arbiter_pm_suspend_wq(struct work_struct *data)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = container_of(data,
|
||||
@@ -136,6 +181,13 @@ static void kbase_arbiter_pm_suspend_wq(struct work_struct *data)
|
||||
dev_dbg(kbdev->dev, "<%s\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_resume_wq() -Kbase resume work queue.
|
||||
* @data: work item
|
||||
*
|
||||
* Resume work queue of the driver when VM is in STARTING state,
|
||||
* else if its in STOPPING_ACTIVE will request a stop event.
|
||||
*/
|
||||
static void kbase_arbiter_pm_resume_wq(struct work_struct *data)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = container_of(data,
|
||||
@@ -157,9 +209,74 @@ static void kbase_arbiter_pm_resume_wq(struct work_struct *data)
|
||||
}
|
||||
arb_vm_state->vm_arb_starting = false;
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
KBASE_TLSTREAM_TL_ARBITER_STARTED(kbdev, kbdev);
|
||||
dev_dbg(kbdev->dev, "<%s\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* request_timer_callback() - Issue warning on request timer expiration
|
||||
* @timer: Request hr timer data
|
||||
*
|
||||
* Called when the Arbiter takes too long to grant the GPU after a
|
||||
* request has been made. Issues a warning in dmesg.
|
||||
*
|
||||
* Return: Always returns HRTIMER_NORESTART
|
||||
*/
|
||||
static enum hrtimer_restart request_timer_callback(struct hrtimer *timer)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = container_of(timer,
|
||||
struct kbase_arbiter_vm_state, vm_request_timer);
|
||||
|
||||
KBASE_DEBUG_ASSERT(arb_vm_state);
|
||||
KBASE_DEBUG_ASSERT(arb_vm_state->kbdev);
|
||||
|
||||
dev_warn(arb_vm_state->kbdev->dev,
|
||||
"Still waiting for GPU to be granted from Arbiter after %d ms\n",
|
||||
GPU_REQUEST_TIMEOUT);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* start_request_timer() - Start a timer after requesting GPU
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Start a timer to track when kbase is waiting for the GPU from the
|
||||
* Arbiter. If the timer expires before GPU is granted, a warning in
|
||||
* dmesg will be issued.
|
||||
*/
|
||||
static void start_request_timer(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
hrtimer_start(&arb_vm_state->vm_request_timer,
|
||||
HR_TIMER_DELAY_MSEC(GPU_REQUEST_TIMEOUT),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
/**
|
||||
* cancel_request_timer() - Stop the request timer
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Stops the request timer once GPU has been granted. Safe to call
|
||||
* even if timer is no longer running.
|
||||
*/
|
||||
static void cancel_request_timer(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
hrtimer_cancel(&arb_vm_state->vm_request_timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_early_init() - Initialize arbiter for VM
|
||||
* Paravirtualized use.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Initialize the arbiter and other required resources during the runtime
|
||||
* and request the GPU for the VM for the first time.
|
||||
*
|
||||
* Return: 0 if success, or a Linux error code
|
||||
*/
|
||||
int kbase_arbiter_pm_early_init(struct kbase_device *kbdev)
|
||||
{
|
||||
int err;
|
||||
@@ -179,12 +296,17 @@ int kbase_arbiter_pm_early_init(struct kbase_device *kbdev)
|
||||
WQ_HIGHPRI);
|
||||
if (!arb_vm_state->vm_arb_wq) {
|
||||
dev_err(kbdev->dev, "Failed to allocate vm_arb workqueue\n");
|
||||
kfree(arb_vm_state);
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_WORK(&arb_vm_state->vm_suspend_work, kbase_arbiter_pm_suspend_wq);
|
||||
INIT_WORK(&arb_vm_state->vm_resume_work, kbase_arbiter_pm_resume_wq);
|
||||
arb_vm_state->vm_arb_starting = false;
|
||||
arb_vm_state->vm_arb_users_waiting = 0;
|
||||
atomic_set(&kbdev->pm.gpu_users_waiting, 0);
|
||||
hrtimer_init(&arb_vm_state->vm_request_timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL);
|
||||
arb_vm_state->vm_request_timer.function =
|
||||
request_timer_callback;
|
||||
kbdev->pm.arb_vm_state = arb_vm_state;
|
||||
|
||||
err = kbase_arbif_init(kbdev);
|
||||
@@ -192,17 +314,31 @@ int kbase_arbiter_pm_early_init(struct kbase_device *kbdev)
|
||||
dev_err(kbdev->dev, "Failed to initialise arbif module\n");
|
||||
goto arbif_init_fail;
|
||||
}
|
||||
|
||||
if (kbdev->arb.arb_if) {
|
||||
kbase_arbif_gpu_request(kbdev);
|
||||
dev_dbg(kbdev->dev, "Waiting for initial GPU assignment...\n");
|
||||
wait_event(arb_vm_state->vm_state_wait,
|
||||
err = wait_event_timeout(arb_vm_state->vm_state_wait,
|
||||
arb_vm_state->vm_state ==
|
||||
KBASE_VM_STATE_INITIALIZING_WITH_GPU);
|
||||
KBASE_VM_STATE_INITIALIZING_WITH_GPU,
|
||||
msecs_to_jiffies(gpu_req_timeout));
|
||||
|
||||
if (!err) {
|
||||
dev_dbg(kbdev->dev,
|
||||
"Kbase probe Deferred after waiting %d ms to receive GPU_GRANT\n",
|
||||
gpu_req_timeout);
|
||||
err = -EPROBE_DEFER;
|
||||
goto arbif_eprobe_defer;
|
||||
}
|
||||
|
||||
dev_dbg(kbdev->dev,
|
||||
"Waiting for initial GPU assignment - done\n");
|
||||
}
|
||||
return 0;
|
||||
|
||||
arbif_eprobe_defer:
|
||||
kbase_arbiter_pm_early_term(kbdev);
|
||||
return err;
|
||||
arbif_init_fail:
|
||||
destroy_workqueue(arb_vm_state->vm_arb_wq);
|
||||
kfree(arb_vm_state);
|
||||
@@ -210,36 +346,72 @@ arbif_init_fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_early_term() - Shutdown arbiter and free resources
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Clean up all the resources
|
||||
*/
|
||||
void kbase_arbiter_pm_early_term(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
cancel_request_timer(kbdev);
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
if (arb_vm_state->vm_state > KBASE_VM_STATE_STOPPED_GPU_REQUESTED) {
|
||||
kbase_pm_set_gpu_lost(kbdev, false);
|
||||
kbase_arbif_gpu_stopped(kbdev, false);
|
||||
}
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
kbase_arbif_destroy(kbdev);
|
||||
destroy_workqueue(arb_vm_state->vm_arb_wq);
|
||||
kbase_arbif_destroy(kbdev);
|
||||
arb_vm_state->vm_arb_wq = NULL;
|
||||
kfree(kbdev->pm.arb_vm_state);
|
||||
kbdev->pm.arb_vm_state = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_release_interrupts() - Release the GPU interrupts
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Releases interrupts and set the interrupt flag to false
|
||||
*/
|
||||
void kbase_arbiter_pm_release_interrupts(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
if (!kbdev->arb.arb_if ||
|
||||
arb_vm_state->vm_state >
|
||||
KBASE_VM_STATE_STOPPED_GPU_REQUESTED)
|
||||
if (arb_vm_state->interrupts_installed == true) {
|
||||
arb_vm_state->interrupts_installed = false;
|
||||
kbase_release_interrupts(kbdev);
|
||||
|
||||
}
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_install_interrupts() - Install the GPU interrupts
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Install interrupts and set the interrupt_install flag to true.
|
||||
*/
|
||||
int kbase_arbiter_pm_install_interrupts(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
int err;
|
||||
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
arb_vm_state->interrupts_installed = true;
|
||||
err = kbase_install_interrupts(kbdev);
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_stopped() - Handle stop state for the VM
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Handles a stop state for the VM
|
||||
*/
|
||||
void kbase_arbiter_pm_vm_stopped(struct kbase_device *kbdev)
|
||||
{
|
||||
bool request_gpu = false;
|
||||
@@ -247,14 +419,19 @@ void kbase_arbiter_pm_vm_stopped(struct kbase_device *kbdev)
|
||||
|
||||
lockdep_assert_held(&arb_vm_state->vm_state_lock);
|
||||
|
||||
if (arb_vm_state->vm_arb_users_waiting > 0 &&
|
||||
if (atomic_read(&kbdev->pm.gpu_users_waiting) > 0 &&
|
||||
arb_vm_state->vm_state == KBASE_VM_STATE_STOPPING_IDLE)
|
||||
kbase_arbiter_pm_vm_set_state(kbdev,
|
||||
KBASE_VM_STATE_STOPPING_ACTIVE);
|
||||
|
||||
dev_dbg(kbdev->dev, "%s %s\n", __func__,
|
||||
kbase_arbiter_pm_vm_state_str(arb_vm_state->vm_state));
|
||||
kbase_release_interrupts(kbdev);
|
||||
|
||||
if (arb_vm_state->interrupts_installed) {
|
||||
arb_vm_state->interrupts_installed = false;
|
||||
kbase_release_interrupts(kbdev);
|
||||
}
|
||||
|
||||
switch (arb_vm_state->vm_state) {
|
||||
case KBASE_VM_STATE_STOPPING_ACTIVE:
|
||||
request_gpu = true;
|
||||
@@ -275,13 +452,85 @@ void kbase_arbiter_pm_vm_stopped(struct kbase_device *kbdev)
|
||||
|
||||
kbase_pm_set_gpu_lost(kbdev, false);
|
||||
kbase_arbif_gpu_stopped(kbdev, request_gpu);
|
||||
if (request_gpu)
|
||||
start_request_timer(kbdev);
|
||||
}
|
||||
|
||||
void kbase_arbiter_set_max_config(struct kbase_device *kbdev,
|
||||
uint32_t max_l2_slices,
|
||||
uint32_t max_core_mask)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state;
|
||||
struct max_config_props max_config;
|
||||
|
||||
if (!kbdev)
|
||||
return;
|
||||
|
||||
/* Mask the max_l2_slices as it is stored as 8 bits into kbase */
|
||||
max_config.l2_slices = max_l2_slices & MAX_L2_SLICES_MASK;
|
||||
max_config.core_mask = max_core_mask;
|
||||
arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
/* Just set the max_props in kbase during initialization. */
|
||||
if (arb_vm_state->vm_state == KBASE_VM_STATE_INITIALIZING)
|
||||
kbase_gpuprops_set_max_config(kbdev, &max_config);
|
||||
else
|
||||
dev_dbg(kbdev->dev, "Unexpected max_config on VM state %s",
|
||||
kbase_arbiter_pm_vm_state_str(arb_vm_state->vm_state));
|
||||
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
}
|
||||
|
||||
int kbase_arbiter_pm_gpu_assigned(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state;
|
||||
int result = -EINVAL;
|
||||
|
||||
if (!kbdev)
|
||||
return result;
|
||||
|
||||
/* First check the GPU_LOST state */
|
||||
kbase_pm_lock(kbdev);
|
||||
if (kbase_pm_is_gpu_lost(kbdev)) {
|
||||
kbase_pm_unlock(kbdev);
|
||||
return 0;
|
||||
}
|
||||
kbase_pm_unlock(kbdev);
|
||||
|
||||
/* Then the arbitration state machine */
|
||||
arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
switch (arb_vm_state->vm_state) {
|
||||
case KBASE_VM_STATE_INITIALIZING:
|
||||
case KBASE_VM_STATE_SUSPENDED:
|
||||
case KBASE_VM_STATE_STOPPED:
|
||||
case KBASE_VM_STATE_STOPPED_GPU_REQUESTED:
|
||||
case KBASE_VM_STATE_SUSPEND_WAIT_FOR_GRANT:
|
||||
result = 0;
|
||||
break;
|
||||
default:
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_gpu_start() - Handles the start state of the VM
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Handles the start state of the VM
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_gpu_start(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
lockdep_assert_held(&arb_vm_state->vm_state_lock);
|
||||
cancel_request_timer(kbdev);
|
||||
switch (arb_vm_state->vm_state) {
|
||||
case KBASE_VM_STATE_INITIALIZING:
|
||||
kbase_arbiter_pm_vm_set_state(kbdev,
|
||||
@@ -289,7 +538,14 @@ static void kbase_arbiter_pm_vm_gpu_start(struct kbase_device *kbdev)
|
||||
break;
|
||||
case KBASE_VM_STATE_STOPPED_GPU_REQUESTED:
|
||||
kbase_arbiter_pm_vm_set_state(kbdev, KBASE_VM_STATE_STARTING);
|
||||
arb_vm_state->interrupts_installed = true;
|
||||
kbase_install_interrupts(kbdev);
|
||||
/*
|
||||
* GPU GRANTED received while in stop can be a result of a
|
||||
* repartitioning.
|
||||
*/
|
||||
kbase_gpuprops_req_curr_config_update(kbdev);
|
||||
/* curr_config will be updated while resuming the PM. */
|
||||
queue_work(arb_vm_state->vm_arb_wq,
|
||||
&arb_vm_state->vm_resume_work);
|
||||
break;
|
||||
@@ -306,6 +562,12 @@ static void kbase_arbiter_pm_vm_gpu_start(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_gpu_stop() - Handles the stop state of the VM
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Handles the start state of the VM
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_gpu_stop(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
@@ -348,6 +610,12 @@ static void kbase_arbiter_pm_vm_gpu_stop(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_gpu_lost() - Kbase signals GPU is lost on a lost event signal
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* On GPU lost event signals GPU_LOST to the aribiter
|
||||
*/
|
||||
static void kbase_gpu_lost(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
@@ -396,6 +664,13 @@ static void kbase_gpu_lost(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_os_suspend_ready_state() - checks if VM is ready
|
||||
* to be moved to suspended state.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Return: True if its ready to be suspended else False.
|
||||
*/
|
||||
static inline bool kbase_arbiter_pm_vm_os_suspend_ready_state(
|
||||
struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -410,6 +685,14 @@ static inline bool kbase_arbiter_pm_vm_os_suspend_ready_state(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_os_prepare_suspend() - Prepare OS to be in suspend state
|
||||
* until it receives the grant message from arbiter
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Prepares OS to be in suspend state until it receives GRANT message
|
||||
* from Arbiter asynchronously.
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_os_prepare_suspend(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
@@ -475,6 +758,14 @@ static void kbase_arbiter_pm_vm_os_prepare_suspend(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_os_resume() - Resume OS function once it receives
|
||||
* a grant message from arbiter
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Resume OS function once it receives GRANT message
|
||||
* from Arbiter asynchronously.
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_os_resume(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
@@ -487,6 +778,7 @@ static void kbase_arbiter_pm_vm_os_resume(struct kbase_device *kbdev)
|
||||
kbase_arbiter_pm_vm_set_state(kbdev,
|
||||
KBASE_VM_STATE_STOPPED_GPU_REQUESTED);
|
||||
kbase_arbif_gpu_request(kbdev);
|
||||
start_request_timer(kbdev);
|
||||
|
||||
/* Release lock and block resume OS function until we have
|
||||
* asynchronously received the GRANT message from the Arbiter and
|
||||
@@ -498,6 +790,14 @@ static void kbase_arbiter_pm_vm_os_resume(struct kbase_device *kbdev)
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_event() - Dispatch VM event to the state machine.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
* @evt: VM event
|
||||
*
|
||||
* The state machine function. Receives events and transitions states
|
||||
* according the event received and the current state
|
||||
*/
|
||||
void kbase_arbiter_pm_vm_event(struct kbase_device *kbdev,
|
||||
enum kbase_arbif_evt evt)
|
||||
{
|
||||
@@ -509,7 +809,9 @@ void kbase_arbiter_pm_vm_event(struct kbase_device *kbdev,
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
dev_dbg(kbdev->dev, "%s %s\n", __func__,
|
||||
kbase_arbiter_pm_vm_event_str(evt));
|
||||
|
||||
if (arb_vm_state->vm_state != KBASE_VM_STATE_INITIALIZING_WITH_GPU &&
|
||||
arb_vm_state->vm_state != KBASE_VM_STATE_INITIALIZING)
|
||||
KBASE_KTRACE_ADD(kbdev, ARB_VM_EVT, NULL, evt);
|
||||
switch (evt) {
|
||||
case KBASE_VM_GPU_GRANTED_EVT:
|
||||
kbase_arbiter_pm_vm_gpu_start(kbdev);
|
||||
@@ -542,8 +844,6 @@ void kbase_arbiter_pm_vm_event(struct kbase_device *kbdev,
|
||||
case KBASE_VM_REF_EVENT:
|
||||
switch (arb_vm_state->vm_state) {
|
||||
case KBASE_VM_STATE_STARTING:
|
||||
KBASE_TLSTREAM_TL_ARBITER_STARTED(kbdev, kbdev);
|
||||
/* FALL THROUGH */
|
||||
case KBASE_VM_STATE_IDLE:
|
||||
kbase_arbiter_pm_vm_set_state(kbdev,
|
||||
KBASE_VM_STATE_ACTIVE);
|
||||
@@ -586,6 +886,12 @@ void kbase_arbiter_pm_vm_event(struct kbase_device *kbdev,
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_arbiter_pm_vm_event);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_wait_gpu_assignment() - VM wait for a GPU assignment.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* VM waits for a GPU assignment.
|
||||
*/
|
||||
static void kbase_arbiter_pm_vm_wait_gpu_assignment(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
@@ -597,6 +903,12 @@ static void kbase_arbiter_pm_vm_wait_gpu_assignment(struct kbase_device *kbdev)
|
||||
dev_dbg(kbdev->dev, "Waiting for GPU assignment - done\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_gpu_assigned_lockheld() - Check if VM holds VM state lock
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Checks if the virtual machine holds VM state lock.
|
||||
*/
|
||||
static inline bool kbase_arbiter_pm_vm_gpu_assigned_lockheld(
|
||||
struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -607,6 +919,19 @@ static inline bool kbase_arbiter_pm_vm_gpu_assigned_lockheld(
|
||||
arb_vm_state->vm_state == KBASE_VM_STATE_ACTIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_ctx_active_handle_suspend() - Handle suspend operation for
|
||||
* arbitration mode
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
* @suspend_handler: The handler code for how to handle a suspend
|
||||
* that might occur
|
||||
*
|
||||
* This function handles a suspend event from the driver,
|
||||
* communicating with the arbiter and waiting synchronously for the GPU
|
||||
* to be granted again depending on the VM state.
|
||||
*
|
||||
* Return: 0 on success else 1 suspend handler isn not possible.
|
||||
*/
|
||||
int kbase_arbiter_pm_ctx_active_handle_suspend(struct kbase_device *kbdev,
|
||||
enum kbase_pm_suspend_handler suspend_handler)
|
||||
{
|
||||
@@ -627,6 +952,7 @@ int kbase_arbiter_pm_ctx_active_handle_suspend(struct kbase_device *kbdev,
|
||||
kbase_arbiter_pm_vm_set_state(kbdev,
|
||||
KBASE_VM_STATE_STOPPED_GPU_REQUESTED);
|
||||
kbase_arbif_gpu_request(kbdev);
|
||||
start_request_timer(kbdev);
|
||||
} else if (arb_vm_state->vm_state ==
|
||||
KBASE_VM_STATE_INITIALIZING_WITH_GPU)
|
||||
break;
|
||||
@@ -660,7 +986,7 @@ int kbase_arbiter_pm_ctx_active_handle_suspend(struct kbase_device *kbdev,
|
||||
}
|
||||
|
||||
/* Need to synchronously wait for GPU assignment */
|
||||
arb_vm_state->vm_arb_users_waiting++;
|
||||
atomic_inc(&kbdev->pm.gpu_users_waiting);
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
mutex_unlock(&kbdev->pm.lock);
|
||||
mutex_unlock(&js_devdata->runpool_mutex);
|
||||
@@ -668,9 +994,66 @@ int kbase_arbiter_pm_ctx_active_handle_suspend(struct kbase_device *kbdev,
|
||||
mutex_lock(&js_devdata->runpool_mutex);
|
||||
mutex_lock(&kbdev->pm.lock);
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
arb_vm_state->vm_arb_users_waiting--;
|
||||
atomic_dec(&kbdev->pm.gpu_users_waiting);
|
||||
}
|
||||
mutex_unlock(&arb_vm_state->vm_state_lock);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_update_gpu_freq() - Updates GPU clock frequency received
|
||||
* from arbiter.
|
||||
* @arb_freq - Pointer to struchture holding GPU clock frequenecy data
|
||||
* @freq - New frequency value
|
||||
*/
|
||||
void kbase_arbiter_pm_update_gpu_freq(struct kbase_arbiter_freq *arb_freq,
|
||||
uint32_t freq)
|
||||
{
|
||||
mutex_lock(&arb_freq->arb_freq_lock);
|
||||
arb_freq->arb_freq = freq;
|
||||
mutex_unlock(&arb_freq->arb_freq_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* enumerate_arb_gpu_clk() - Enumerate a GPU clock on the given index
|
||||
* @kbdev - kbase_device pointer
|
||||
* @index - GPU clock index
|
||||
*
|
||||
* Returns pointer to structure holding GPU clock frequency data reported from
|
||||
* arbiter, only index 0 is valid.
|
||||
*/
|
||||
static void *enumerate_arb_gpu_clk(struct kbase_device *kbdev,
|
||||
unsigned int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return &kbdev->arb.arb_freq;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_arb_gpu_clk_rate() - Get the current rate of GPU clock frequency value
|
||||
* @kbdev - kbase_device pointer
|
||||
* @index - GPU clock index
|
||||
*
|
||||
* Returns the GPU clock frequency value saved when gpu is granted from arbiter
|
||||
*/
|
||||
static unsigned long get_arb_gpu_clk_rate(struct kbase_device *kbdev,
|
||||
void *gpu_clk_handle)
|
||||
{
|
||||
uint32_t freq;
|
||||
struct kbase_arbiter_freq *arb_dev_freq =
|
||||
(struct kbase_arbiter_freq *) gpu_clk_handle;
|
||||
|
||||
mutex_lock(&arb_dev_freq->arb_freq_lock);
|
||||
freq = arb_dev_freq->arb_freq;
|
||||
mutex_unlock(&arb_dev_freq->arb_freq_lock);
|
||||
return freq;
|
||||
}
|
||||
|
||||
struct kbase_clk_rate_trace_op_conf arb_clk_rate_trace_ops = {
|
||||
.get_gpu_clk_rate = get_arb_gpu_clk_rate,
|
||||
.enumerate_gpu_clk = enumerate_arb_gpu_clk,
|
||||
.gpu_clk_notifier_register = NULL,
|
||||
.gpu_clk_notifier_unregister = NULL
|
||||
};
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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
|
||||
@@ -38,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -116,10 +93,18 @@ void kbase_arbiter_pm_early_term(struct kbase_device *kbdev);
|
||||
* kbase_arbiter_pm_release_interrupts() - Release the GPU interrupts
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Releases interrupts if needed (GPU is available) otherwise does nothing
|
||||
* Releases interrupts and set the interrupt flag to false
|
||||
*/
|
||||
void kbase_arbiter_pm_release_interrupts(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_install_interrupts() - Install the GPU interrupts
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Install interrupts and set the interrupt_install flag to true.
|
||||
*/
|
||||
int kbase_arbiter_pm_install_interrupts(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_vm_event() - Dispatch VM event to the state machine
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
@@ -156,4 +141,42 @@ int kbase_arbiter_pm_ctx_active_handle_suspend(struct kbase_device *kbdev,
|
||||
*/
|
||||
void kbase_arbiter_pm_vm_stopped(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_set_max_config() - Set the max config data in kbase device.
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer).
|
||||
* @max_l2_slices: The maximum number of L2 slices.
|
||||
* @max_core_mask: The largest core mask.
|
||||
*
|
||||
* This function handles a stop event for the VM.
|
||||
* It will update the VM state and forward the stop event to the driver.
|
||||
*/
|
||||
void kbase_arbiter_set_max_config(struct kbase_device *kbdev,
|
||||
uint32_t max_l2_slices,
|
||||
uint32_t max_core_mask);
|
||||
|
||||
/**
|
||||
* kbase_arbiter_pm_gpu_assigned() - Determine if this VM has access to the GPU
|
||||
* @kbdev: The kbase device structure for the device (must be a valid pointer)
|
||||
*
|
||||
* Return: 0 if the VM does not have access, 1 if it does, and a negative number
|
||||
* if an error occurred
|
||||
*/
|
||||
int kbase_arbiter_pm_gpu_assigned(struct kbase_device *kbdev);
|
||||
|
||||
extern struct kbase_clk_rate_trace_op_conf arb_clk_rate_trace_ops;
|
||||
|
||||
/**
|
||||
* struct kbase_arbiter_freq - Holding the GPU clock frequency data retrieved
|
||||
* from arbiter
|
||||
* @arb_freq: GPU clock frequency value
|
||||
* @arb_freq_lock: Mutex protecting access to arbfreq value
|
||||
*/
|
||||
struct kbase_arbiter_freq {
|
||||
uint32_t arb_freq;
|
||||
struct mutex arb_freq_lock;
|
||||
};
|
||||
|
||||
void kbase_arbiter_pm_update_gpu_freq(struct kbase_arbiter_freq *arb_freq,
|
||||
uint32_t freq);
|
||||
|
||||
#endif /*_MALI_KBASE_ARBITER_PM_H_ */
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
# (C) COPYRIGHT 2014-2021 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,15 +16,12 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
BACKEND += \
|
||||
backend/gpu/mali_kbase_cache_policy_backend.c \
|
||||
backend/gpu/mali_kbase_gpuprops_backend.c \
|
||||
backend/gpu/mali_kbase_irq_linux.c \
|
||||
backend/gpu/mali_kbase_instr_backend.c \
|
||||
backend/gpu/mali_kbase_js_backend.c \
|
||||
backend/gpu/mali_kbase_pm_backend.c \
|
||||
backend/gpu/mali_kbase_pm_driver.c \
|
||||
@@ -40,6 +38,7 @@ ifeq ($(MALI_USE_CSF),1)
|
||||
# empty
|
||||
else
|
||||
BACKEND += \
|
||||
backend/gpu/mali_kbase_instr_backend.c \
|
||||
backend/gpu/mali_kbase_jm_as.c \
|
||||
backend/gpu/mali_kbase_debug_job_fault_backend.c \
|
||||
backend/gpu/mali_kbase_jm_hw.c \
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2016, 2018, 2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backend/gpu/mali_kbase_cache_policy_backend.h"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2015-2016 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2016, 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,16 +17,13 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _KBASE_CACHE_POLICY_BACKEND_H_
|
||||
#define _KBASE_CACHE_POLICY_BACKEND_H_
|
||||
|
||||
#include "mali_kbase.h"
|
||||
#include "mali_base_kernel.h"
|
||||
#include <uapi/gpu/arm/bifrost/mali_base_kernel.h>
|
||||
|
||||
/**
|
||||
* kbase_cache_set_coherency_mode() - Sets the system coherency mode
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -40,6 +39,38 @@
|
||||
#define CLK_RATE_TRACE_OPS (NULL)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* get_clk_rate_trace_callbacks() - Returns pointer to clk trace ops.
|
||||
* @kbdev: Pointer to kbase device, used to check if arbitration is enabled
|
||||
* when compiled with arbiter support.
|
||||
* Return: Pointer to clk trace ops if supported or NULL.
|
||||
*/
|
||||
static struct kbase_clk_rate_trace_op_conf *
|
||||
get_clk_rate_trace_callbacks(struct kbase_device *kbdev __maybe_unused)
|
||||
{
|
||||
/* base case */
|
||||
struct kbase_clk_rate_trace_op_conf *callbacks =
|
||||
(struct kbase_clk_rate_trace_op_conf *)CLK_RATE_TRACE_OPS;
|
||||
#if defined(CONFIG_MALI_ARBITER_SUPPORT) && defined(CONFIG_OF)
|
||||
const void *arbiter_if_node;
|
||||
|
||||
if (WARN_ON(!kbdev) || WARN_ON(!kbdev->dev))
|
||||
return callbacks;
|
||||
|
||||
arbiter_if_node =
|
||||
of_get_property(kbdev->dev->of_node, "arbiter_if", NULL);
|
||||
/* Arbitration enabled, override the callback pointer.*/
|
||||
if (arbiter_if_node)
|
||||
callbacks = &arb_clk_rate_trace_ops;
|
||||
else
|
||||
dev_dbg(kbdev->dev,
|
||||
"Arbitration supported but disabled by platform. Leaving clk rate callbacks as default.\n");
|
||||
|
||||
#endif
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
static int gpu_clk_rate_change_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
@@ -70,12 +101,13 @@ static int gpu_clk_rate_change_notifier(struct notifier_block *nb,
|
||||
static int gpu_clk_data_init(struct kbase_device *kbdev,
|
||||
void *gpu_clk_handle, unsigned int index)
|
||||
{
|
||||
struct kbase_clk_rate_trace_op_conf *callbacks =
|
||||
(struct kbase_clk_rate_trace_op_conf *)CLK_RATE_TRACE_OPS;
|
||||
struct kbase_clk_rate_trace_op_conf *callbacks;
|
||||
struct kbase_clk_data *clk_data;
|
||||
struct kbase_clk_rate_trace_manager *clk_rtm = &kbdev->pm.clk_rtm;
|
||||
int ret = 0;
|
||||
|
||||
callbacks = get_clk_rate_trace_callbacks(kbdev);
|
||||
|
||||
if (WARN_ON(!callbacks) ||
|
||||
WARN_ON(!gpu_clk_handle) ||
|
||||
WARN_ON(index >= BASE_MAX_NR_CLOCKS_REGULATORS))
|
||||
@@ -109,8 +141,9 @@ static int gpu_clk_data_init(struct kbase_device *kbdev,
|
||||
clk_data->clk_rate_change_nb.notifier_call =
|
||||
gpu_clk_rate_change_notifier;
|
||||
|
||||
ret = callbacks->gpu_clk_notifier_register(kbdev, gpu_clk_handle,
|
||||
&clk_data->clk_rate_change_nb);
|
||||
if (callbacks->gpu_clk_notifier_register)
|
||||
ret = callbacks->gpu_clk_notifier_register(kbdev,
|
||||
gpu_clk_handle, &clk_data->clk_rate_change_nb);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev, "Failed to register notifier for clock enumerated at index %u", index);
|
||||
kfree(clk_data);
|
||||
@@ -121,19 +154,22 @@ static int gpu_clk_data_init(struct kbase_device *kbdev,
|
||||
|
||||
int kbase_clk_rate_trace_manager_init(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_clk_rate_trace_op_conf *callbacks =
|
||||
(struct kbase_clk_rate_trace_op_conf *)CLK_RATE_TRACE_OPS;
|
||||
struct kbase_clk_rate_trace_op_conf *callbacks;
|
||||
struct kbase_clk_rate_trace_manager *clk_rtm = &kbdev->pm.clk_rtm;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
/* Return early if no callbacks provided for clock rate tracing */
|
||||
if (!callbacks)
|
||||
return 0;
|
||||
callbacks = get_clk_rate_trace_callbacks(kbdev);
|
||||
|
||||
spin_lock_init(&clk_rtm->lock);
|
||||
INIT_LIST_HEAD(&clk_rtm->listeners);
|
||||
|
||||
/* Return early if no callbacks provided for clock rate tracing */
|
||||
if (!callbacks) {
|
||||
WRITE_ONCE(clk_rtm->clk_rate_trace_ops, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
clk_rtm->gpu_idle = true;
|
||||
|
||||
for (i = 0; i < BASE_MAX_NR_CLOCKS_REGULATORS; i++) {
|
||||
@@ -151,10 +187,12 @@ int kbase_clk_rate_trace_manager_init(struct kbase_device *kbdev)
|
||||
/* Activate clock rate trace manager if at least one GPU clock was
|
||||
* enumerated.
|
||||
*/
|
||||
if (i)
|
||||
if (i) {
|
||||
WRITE_ONCE(clk_rtm->clk_rate_trace_ops, callbacks);
|
||||
else
|
||||
} else {
|
||||
dev_info(kbdev->dev, "No clock(s) available for rate tracing");
|
||||
WRITE_ONCE(clk_rtm->clk_rate_trace_ops, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -183,9 +221,10 @@ void kbase_clk_rate_trace_manager_term(struct kbase_device *kbdev)
|
||||
if (!clk_rtm->clks[i])
|
||||
break;
|
||||
|
||||
clk_rtm->clk_rate_trace_ops->gpu_clk_notifier_unregister(
|
||||
kbdev, clk_rtm->clks[i]->gpu_clk_handle,
|
||||
&clk_rtm->clks[i]->clk_rate_change_nb);
|
||||
if (clk_rtm->clk_rate_trace_ops->gpu_clk_notifier_unregister)
|
||||
clk_rtm->clk_rate_trace_ops->gpu_clk_notifier_unregister
|
||||
(kbdev, clk_rtm->clks[i]->gpu_clk_handle,
|
||||
&clk_rtm->clks[i]->clk_rate_change_nb);
|
||||
kfree(clk_rtm->clks[i]);
|
||||
}
|
||||
|
||||
@@ -284,4 +323,3 @@ void kbase_clk_rate_trace_manager_notify_all(
|
||||
}
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_clk_rate_trace_manager_notify_all);
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,17 +17,15 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CLK_RATE_TRACE_MGR_
|
||||
#define _KBASE_CLK_RATE_TRACE_MGR_
|
||||
|
||||
/** The index of top clock domain in kbase_clk_rate_trace_manager:clks. */
|
||||
/* The index of top clock domain in kbase_clk_rate_trace_manager:clks. */
|
||||
#define KBASE_CLOCK_DOMAIN_TOP (0)
|
||||
|
||||
/** The index of shader-cores clock domain in
|
||||
/* The index of shader-cores clock domain in
|
||||
* kbase_clk_rate_trace_manager:clks.
|
||||
*/
|
||||
#define KBASE_CLOCK_DOMAIN_SHADER_CORES (1)
|
||||
@@ -139,7 +138,7 @@ static inline void kbase_clk_rate_trace_manager_unsubscribe(
|
||||
* rate listeners.
|
||||
*
|
||||
* @clk_rtm: Clock rate manager instance.
|
||||
* @clk_index: Clock index.
|
||||
* @clock_index: Clock index.
|
||||
* @new_rate: New clock frequency(Hz)
|
||||
*
|
||||
* kbase_clk_rate_trace_manager:lock must be locked.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2012-2015, 2018-2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
@@ -32,20 +33,8 @@
|
||||
#endif
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
#include <linux/pm_opp.h>
|
||||
#else /* Linux >= 3.13 */
|
||||
/* In 3.13 the OPP include header file, types, and functions were all
|
||||
* renamed. Use the old filename for the include, and define the new names to
|
||||
* the old, when an old kernel is detected.
|
||||
*/
|
||||
#include <linux/opp.h>
|
||||
#define dev_pm_opp opp
|
||||
#define dev_pm_opp_get_voltage opp_get_voltage
|
||||
#define dev_pm_opp_get_opp_count opp_get_opp_count
|
||||
#define dev_pm_opp_find_freq_ceil opp_find_freq_ceil
|
||||
#define dev_pm_opp_find_freq_floor opp_find_freq_floor
|
||||
#endif /* Linux >= 3.13 */
|
||||
|
||||
#include <soc/rockchip/rockchip_ipa.h>
|
||||
#include <soc/rockchip/rockchip_opp_select.h>
|
||||
#include <soc/rockchip/rockchip_system_monitor.h>
|
||||
@@ -59,22 +48,46 @@ static struct monitor_dev_profile mali_mdevp = {
|
||||
};
|
||||
|
||||
/**
|
||||
* opp_translate - Translate nominal OPP frequency from devicetree into real
|
||||
* frequency and core mask
|
||||
* @kbdev: Device pointer
|
||||
* @freq: Nominal frequency
|
||||
* @volt: Nominal voltage
|
||||
* @core_mask: Pointer to u64 to store core mask to
|
||||
* @freqs: Pointer to array of frequencies
|
||||
* @volts: Pointer to array of voltages
|
||||
* get_voltage() - Get the voltage value corresponding to the nominal frequency
|
||||
* used by devfreq.
|
||||
* @kbdev: Device pointer
|
||||
* @freq: Nominal frequency in Hz passed by devfreq.
|
||||
*
|
||||
* This function will only perform translation if an operating-points-v2-mali
|
||||
* table is present in devicetree. If one is not present then it will return an
|
||||
* untranslated frequency and all cores enabled.
|
||||
* This function will be called only when the opp table which is compatible with
|
||||
* "operating-points-v2-mali", is not present in the devicetree for GPU device.
|
||||
*
|
||||
* Return: Voltage value in milli volts, 0 in case of error.
|
||||
*/
|
||||
static void opp_translate(struct kbase_device *kbdev, unsigned long freq,
|
||||
unsigned long volt, u64 *core_mask,
|
||||
unsigned long *freqs, unsigned long *volts)
|
||||
static unsigned long get_voltage(struct kbase_device *kbdev, unsigned long freq)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long voltage = 0;
|
||||
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
|
||||
opp = dev_pm_opp_find_freq_exact(kbdev->dev, freq, true);
|
||||
|
||||
if (IS_ERR_OR_NULL(opp))
|
||||
dev_err(kbdev->dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
|
||||
else {
|
||||
voltage = dev_pm_opp_get_voltage(opp);
|
||||
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
|
||||
dev_pm_opp_put(opp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
|
||||
/* Return the voltage in milli volts */
|
||||
return voltage / 1000;
|
||||
}
|
||||
|
||||
void kbase_devfreq_opp_translate(struct kbase_device *kbdev, unsigned long freq,
|
||||
u64 *core_mask, unsigned long *freqs, unsigned long *volts)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -95,13 +108,16 @@ static void opp_translate(struct kbase_device *kbdev, unsigned long freq,
|
||||
}
|
||||
|
||||
/* If failed to find OPP, return all cores enabled
|
||||
* and nominal frequency
|
||||
* and nominal frequency and the corresponding voltage.
|
||||
*/
|
||||
if (i == kbdev->num_opps) {
|
||||
unsigned long voltage = get_voltage(kbdev, freq);
|
||||
|
||||
*core_mask = kbdev->gpu_props.props.raw_props.shader_present;
|
||||
|
||||
for (i = 0; i < kbdev->nr_clocks; i++) {
|
||||
freqs[i] = freq;
|
||||
volts[i] = volt;
|
||||
volts[i] = voltage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,12 +136,12 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
|
||||
|
||||
nominal_freq = *target_freq;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
opp = devfreq_recommended_opp(dev, &nominal_freq, flags);
|
||||
if (IS_ERR_OR_NULL(opp)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
|
||||
@@ -135,12 +151,15 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
|
||||
dev_pm_opp_put(opp);
|
||||
#endif
|
||||
|
||||
opp_translate(kbdev, nominal_freq, nominal_volt, &core_mask, freqs,
|
||||
volts);
|
||||
kbase_devfreq_opp_translate(kbdev,
|
||||
nominal_freq,
|
||||
&core_mask,
|
||||
freqs,
|
||||
volts);
|
||||
|
||||
/*
|
||||
* Only update if there is a change of frequency
|
||||
@@ -168,6 +187,7 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%lu-->%lu\n", kbdev->current_nominal_freq, nominal_freq);
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
@@ -285,6 +305,10 @@ kbase_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
|
||||
stat->current_frequency = kbdev->current_nominal_freq;
|
||||
stat->private_data = NULL;
|
||||
|
||||
#if MALI_USE_CSF && defined CONFIG_DEVFREQ_THERMAL
|
||||
kbase_ipa_reset_data(kbdev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -296,11 +320,11 @@ static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
|
||||
unsigned long freq;
|
||||
struct dev_pm_opp *opp;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
count = dev_pm_opp_get_opp_count(kbdev->dev);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
if (count < 0)
|
||||
@@ -311,20 +335,20 @@ static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
|
||||
if (!dp->freq_table)
|
||||
return -ENOMEM;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
for (i = 0, freq = ULONG_MAX; i < count; i++, freq--) {
|
||||
opp = dev_pm_opp_find_freq_floor(kbdev->dev, &freq);
|
||||
if (IS_ERR(opp))
|
||||
break;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
|
||||
dev_pm_opp_put(opp);
|
||||
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
|
||||
#endif /* KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE */
|
||||
|
||||
dp->freq_table[i] = freq;
|
||||
}
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
|
||||
@@ -407,7 +431,7 @@ static void kbasep_devfreq_read_suspend_clock(struct kbase_device *kbdev,
|
||||
|
||||
static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
|
||||
{
|
||||
#if KERNEL_VERSION(3, 18, 0) > LINUX_VERSION_CODE || !defined(CONFIG_OF)
|
||||
#ifndef CONFIG_OF
|
||||
/* OPP table initialization requires at least the capability to get
|
||||
* regulators and clocks from the device tree, as well as parsing
|
||||
* arrays of unsigned integer values.
|
||||
@@ -541,11 +565,9 @@ static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
|
||||
kbdev->num_opps = i;
|
||||
|
||||
return 0;
|
||||
#endif /* KERNEL_VERSION(3, 18, 0) > LINUX_VERSION_CODE */
|
||||
#endif /* CONFIG_OF */
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||
|
||||
static const char *kbase_devfreq_req_type_name(enum kbase_devfreq_work_type type)
|
||||
{
|
||||
const char *p;
|
||||
@@ -602,12 +624,9 @@ static void kbase_devfreq_suspend_resume_worker(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
|
||||
|
||||
void kbase_devfreq_enqueue_work(struct kbase_device *kbdev,
|
||||
enum kbase_devfreq_work_type work_type)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(work_type == DEVFREQ_WORK_NONE);
|
||||
@@ -617,12 +636,10 @@ void kbase_devfreq_enqueue_work(struct kbase_device *kbdev,
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
dev_dbg(kbdev->dev, "Enqueuing devfreq req: %s\n",
|
||||
kbase_devfreq_req_type_name(work_type));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kbase_devfreq_work_init(struct kbase_device *kbdev)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||
kbdev->devfreq_queue.req_type = DEVFREQ_WORK_NONE;
|
||||
kbdev->devfreq_queue.acted_type = DEVFREQ_WORK_RESUME;
|
||||
|
||||
@@ -632,15 +649,12 @@ static int kbase_devfreq_work_init(struct kbase_device *kbdev)
|
||||
|
||||
INIT_WORK(&kbdev->devfreq_queue.work,
|
||||
kbase_devfreq_suspend_resume_worker);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbase_devfreq_work_term(struct kbase_device *kbdev)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||
destroy_workqueue(kbdev->devfreq_queue.workq);
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned long kbase_devfreq_get_static_power(struct devfreq *devfreq,
|
||||
@@ -661,9 +675,9 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
|
||||
struct devfreq_cooling_power *kbase_dcp = &kbase_cooling_power;
|
||||
struct device_node *np = kbdev->dev->of_node;
|
||||
struct devfreq_dev_profile *dp;
|
||||
int err;
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long opp_rate;
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
if (kbdev->nr_clocks == 0) {
|
||||
@@ -726,7 +740,8 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
/* devfreq_add_device only copies a few of kbdev->dev's fields, so
|
||||
* set drvdata explicitly so IPA models can access kbdev. */
|
||||
* set drvdata explicitly so IPA models can access kbdev.
|
||||
*/
|
||||
dev_set_drvdata(&kbdev->devfreq->dev, kbdev);
|
||||
|
||||
err = devfreq_register_opp_notifier(kbdev->dev, kbdev->devfreq);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014, 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014, 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _BASE_DEVFREQ_H_
|
||||
@@ -44,4 +43,20 @@ void kbase_devfreq_force_freq(struct kbase_device *kbdev, unsigned long freq);
|
||||
void kbase_devfreq_enqueue_work(struct kbase_device *kbdev,
|
||||
enum kbase_devfreq_work_type work_type);
|
||||
|
||||
/**
|
||||
* kbase_devfreq_opp_translate - Translate nominal OPP frequency from devicetree
|
||||
* into real frequency & voltage pair, along with
|
||||
* core mask
|
||||
* @kbdev: Device pointer
|
||||
* @freq: Nominal frequency
|
||||
* @core_mask: Pointer to u64 to store core mask to
|
||||
* @freqs: Pointer to array of frequencies
|
||||
* @volts: Pointer to array of voltages
|
||||
*
|
||||
* This function will only perform translation if an operating-points-v2-mali
|
||||
* table is present in devicetree. If one is not present then it will return an
|
||||
* untranslated frequency (and corresponding voltage) and all cores enabled.
|
||||
*/
|
||||
void kbase_devfreq_opp_translate(struct kbase_device *kbdev, unsigned long freq,
|
||||
u64 *core_mask, unsigned long *freqs, unsigned long *volts);
|
||||
#endif /* _BASE_DEVFREQ_H_ */
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -41,12 +39,13 @@ int kbase_backend_gpuprops_get(struct kbase_device *kbdev,
|
||||
|
||||
registers.l2_features = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(L2_FEATURES));
|
||||
registers.core_features = 0;
|
||||
#if !MALI_USE_CSF
|
||||
/* TGOx */
|
||||
registers.core_features = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(CORE_FEATURES));
|
||||
#else /* !MALI_USE_CSF */
|
||||
registers.core_features = 0;
|
||||
#endif /* !MALI_USE_CSF */
|
||||
#endif /* MALI_USE_CSF */
|
||||
registers.tiler_features = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(TILER_FEATURES));
|
||||
registers.mem_features = kbase_reg_read(kbdev,
|
||||
@@ -105,6 +104,16 @@ int kbase_backend_gpuprops_get(struct kbase_device *kbdev,
|
||||
registers.stack_present_hi = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(STACK_PRESENT_HI));
|
||||
|
||||
if (registers.gpu_id >= GPU_ID2_PRODUCT_MAKE(11, 8, 5, 2)) {
|
||||
registers.gpu_features_lo = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(GPU_FEATURES_LO));
|
||||
registers.gpu_features_hi = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(GPU_FEATURES_HI));
|
||||
} else {
|
||||
registers.gpu_features_lo = 0;
|
||||
registers.gpu_features_hi = 0;
|
||||
}
|
||||
|
||||
if (!kbase_is_gpu_removed(kbdev)) {
|
||||
*regdump = registers;
|
||||
return 0;
|
||||
@@ -112,6 +121,32 @@ int kbase_backend_gpuprops_get(struct kbase_device *kbdev,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int kbase_backend_gpuprops_get_curr_config(struct kbase_device *kbdev,
|
||||
struct kbase_current_config_regdump *curr_config_regdump)
|
||||
{
|
||||
if (WARN_ON(!kbdev) || WARN_ON(!curr_config_regdump))
|
||||
return -EINVAL;
|
||||
|
||||
curr_config_regdump->mem_features = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(MEM_FEATURES));
|
||||
|
||||
curr_config_regdump->shader_present_lo = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(SHADER_PRESENT_LO));
|
||||
curr_config_regdump->shader_present_hi = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(SHADER_PRESENT_HI));
|
||||
|
||||
curr_config_regdump->l2_present_lo = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(L2_PRESENT_LO));
|
||||
curr_config_regdump->l2_present_hi = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(L2_PRESENT_HI));
|
||||
|
||||
if (WARN_ON(kbase_is_gpu_removed(kbdev)))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int kbase_backend_gpuprops_get_features(struct kbase_device *kbdev,
|
||||
struct kbase_gpuprops_regdump *regdump)
|
||||
{
|
||||
@@ -147,11 +182,15 @@ int kbase_backend_gpuprops_get_l2_features(struct kbase_device *kbdev,
|
||||
if (kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_L2_CONFIG)) {
|
||||
u32 l2_features = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(L2_FEATURES));
|
||||
u32 l2_config =
|
||||
kbase_reg_read(kbdev, GPU_CONTROL_REG(L2_CONFIG));
|
||||
|
||||
|
||||
if (kbase_is_gpu_removed(kbdev))
|
||||
return -EIO;
|
||||
|
||||
regdump->l2_features = l2_features;
|
||||
regdump->l2_config = l2_config;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* GPU backend instrumentation APIs.
|
||||
*/
|
||||
@@ -39,9 +36,7 @@ int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = -EINVAL;
|
||||
#if !MALI_USE_CSF
|
||||
u32 irq_mask;
|
||||
#endif
|
||||
u32 prfcnt_config;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
@@ -58,12 +53,10 @@ int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
/* Enable interrupt */
|
||||
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK));
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK), irq_mask |
|
||||
PRFCNT_SAMPLE_COMPLETED);
|
||||
#endif
|
||||
|
||||
/* In use, this context is the owner */
|
||||
kbdev->hwcnt.kctx = kctx;
|
||||
@@ -75,36 +68,13 @@ int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
|
||||
|
||||
/* Configure */
|
||||
prfcnt_config = kctx->as_nr << PRFCNT_CONFIG_AS_SHIFT;
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS
|
||||
if (kbdev->hwcnt.backend.use_secondary_override)
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS
|
||||
prfcnt_config |= kbdev->hwcnt.backend.override_counter_set
|
||||
<< PRFCNT_CONFIG_SETSELECT_SHIFT;
|
||||
#else
|
||||
if (enable->use_secondary)
|
||||
prfcnt_config |= enable->counter_set << PRFCNT_CONFIG_SETSELECT_SHIFT;
|
||||
#endif
|
||||
prfcnt_config |= 1 << PRFCNT_CONFIG_SETSELECT_SHIFT;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_CONFIG),
|
||||
prfcnt_config | PRFCNT_CONFIG_MODE_OFF);
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_BASE_LO),
|
||||
enable->dump_buffer & 0xFFFFFFFF);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_BASE_HI),
|
||||
enable->dump_buffer >> 32);
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_CSHW_EN),
|
||||
enable->fe_bm);
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_SHADER_EN),
|
||||
enable->shader_bm);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_MMU_L2_EN),
|
||||
enable->mmu_l2_bm);
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_TILER_EN),
|
||||
enable->tiler_bm);
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_CONFIG),
|
||||
prfcnt_config | PRFCNT_CONFIG_MODE_MANUAL);
|
||||
#else
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG),
|
||||
prfcnt_config | PRFCNT_CONFIG_MODE_OFF);
|
||||
|
||||
@@ -126,7 +96,6 @@ int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
|
||||
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG),
|
||||
prfcnt_config | PRFCNT_CONFIG_MODE_MANUAL);
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
|
||||
|
||||
@@ -138,7 +107,7 @@ int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
|
||||
|
||||
err = 0;
|
||||
|
||||
dev_dbg(kbdev->dev, "HW counters dumping set-up for context %p", kctx);
|
||||
dev_dbg(kbdev->dev, "HW counters dumping set-up for context %pK", kctx);
|
||||
return err;
|
||||
out_err:
|
||||
return err;
|
||||
@@ -148,9 +117,7 @@ int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx)
|
||||
{
|
||||
unsigned long flags, pm_flags;
|
||||
int err = -EINVAL;
|
||||
#if !MALI_USE_CSF
|
||||
u32 irq_mask;
|
||||
#endif
|
||||
struct kbase_device *kbdev = kctx->kbdev;
|
||||
|
||||
while (1) {
|
||||
@@ -185,10 +152,6 @@ int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx)
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DISABLED;
|
||||
kbdev->hwcnt.backend.triggered = 0;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* Disable the counters */
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_CONFIG), 0);
|
||||
#else
|
||||
/* Disable interrupt */
|
||||
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK));
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK),
|
||||
@@ -196,7 +159,6 @@ int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx)
|
||||
|
||||
/* Disable the counters */
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG), 0);
|
||||
#endif
|
||||
|
||||
kbdev->hwcnt.kctx = NULL;
|
||||
kbdev->hwcnt.addr = 0ULL;
|
||||
@@ -205,11 +167,10 @@ int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx)
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, pm_flags);
|
||||
|
||||
dev_dbg(kbdev->dev, "HW counters dumping disabled for context %p",
|
||||
dev_dbg(kbdev->dev, "HW counters dumping disabled for context %pK",
|
||||
kctx);
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -229,7 +190,8 @@ int kbase_instr_hwcnt_request_dump(struct kbase_context *kctx)
|
||||
|
||||
if (kbdev->hwcnt.backend.state != KBASE_INSTR_STATE_IDLE) {
|
||||
/* HW counters are disabled or another dump is ongoing, or we're
|
||||
* resetting */
|
||||
* resetting
|
||||
*/
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -239,44 +201,26 @@ int kbase_instr_hwcnt_request_dump(struct kbase_context *kctx)
|
||||
*/
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DUMPING;
|
||||
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* Reconfigure the dump address */
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_BASE_LO),
|
||||
kbdev->hwcnt.addr & 0xFFFFFFFF);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(PRFCNT_BASE_HI),
|
||||
kbdev->hwcnt.addr >> 32);
|
||||
#else
|
||||
/* Reconfigure the dump address */
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_LO),
|
||||
kbdev->hwcnt.addr & 0xFFFFFFFF);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_HI),
|
||||
kbdev->hwcnt.addr >> 32);
|
||||
#endif
|
||||
|
||||
/* Start dumping */
|
||||
KBASE_KTRACE_ADD(kbdev, CORE_GPU_PRFCNT_SAMPLE, NULL,
|
||||
kbdev->hwcnt.addr);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_PRFCNT_SAMPLE);
|
||||
#else
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_PRFCNT_SAMPLE);
|
||||
#endif
|
||||
|
||||
dev_dbg(kbdev->dev, "HW counters dumping done for context %p", kctx);
|
||||
dev_dbg(kbdev->dev, "HW counters dumping done for context %pK", kctx);
|
||||
|
||||
err = 0;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
tasklet_schedule(&kbdev->hwcnt.backend.csf_hwc_irq_poll_tasklet);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_request_dump);
|
||||
@@ -305,86 +249,6 @@ bool kbase_instr_hwcnt_dump_complete(struct kbase_context *kctx,
|
||||
}
|
||||
KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_dump_complete);
|
||||
|
||||
void kbasep_cache_clean_worker(struct work_struct *data)
|
||||
{
|
||||
struct kbase_device *kbdev;
|
||||
unsigned long flags, pm_flags;
|
||||
|
||||
kbdev = container_of(data, struct kbase_device,
|
||||
hwcnt.backend.cache_clean_work);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, pm_flags);
|
||||
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
|
||||
|
||||
/* Clean and invalidate the caches so we're sure the mmu tables for the
|
||||
* dump buffer is valid.
|
||||
*/
|
||||
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
|
||||
KBASE_INSTR_STATE_REQUEST_CLEAN);
|
||||
kbase_gpu_start_cache_clean_nolock(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, pm_flags);
|
||||
|
||||
kbase_gpu_wait_cache_clean(kbdev);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
|
||||
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
|
||||
KBASE_INSTR_STATE_REQUEST_CLEAN);
|
||||
/* All finished and idle */
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
|
||||
kbdev->hwcnt.backend.triggered = 1;
|
||||
wake_up(&kbdev->hwcnt.backend.wait);
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
}
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/**
|
||||
* kbasep_hwcnt_irq_poll_tasklet - tasklet to poll MCU IRQ status register
|
||||
*
|
||||
* @data: tasklet parameter which pointer to kbdev
|
||||
*
|
||||
* This tasklet poll GPU_IRQ_STATUS register in GPU_CONTROL_MCU page to check
|
||||
* PRFCNT_SAMPLE_COMPLETED bit.
|
||||
*
|
||||
* Tasklet is needed here since work_queue is too slow and cuased some test
|
||||
* cases timeout, the poll_count variable is introduced to avoid infinite
|
||||
* loop in unexpected cases, the poll_count is 1 or 2 in normal case, 128
|
||||
* should be big enough to exit the tasklet in abnormal cases.
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
static void kbasep_hwcnt_irq_poll_tasklet(unsigned long int data)
|
||||
{
|
||||
struct kbase_device *kbdev = (struct kbase_device *)data;
|
||||
unsigned long flags, pm_flags;
|
||||
u32 mcu_gpu_irq_raw_status = 0;
|
||||
u32 poll_count = 0;
|
||||
|
||||
while (1) {
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, pm_flags);
|
||||
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
|
||||
mcu_gpu_irq_raw_status = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_MCU_REG(GPU_IRQ_RAWSTAT));
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, pm_flags);
|
||||
if (mcu_gpu_irq_raw_status & PRFCNT_SAMPLE_COMPLETED) {
|
||||
kbase_reg_write(kbdev,
|
||||
GPU_CONTROL_MCU_REG(GPU_IRQ_CLEAR),
|
||||
PRFCNT_SAMPLE_COMPLETED);
|
||||
kbase_instr_hwcnt_sample_done(kbdev);
|
||||
break;
|
||||
} else if (poll_count++ > 128) {
|
||||
dev_err(kbdev->dev,
|
||||
"Err: HWC dump timeout, count: %u", poll_count);
|
||||
/* Still call sample_done to unblock waiting thread */
|
||||
kbase_instr_hwcnt_sample_done(kbdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void kbase_instr_hwcnt_sample_done(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -395,20 +259,10 @@ void kbase_instr_hwcnt_sample_done(struct kbase_device *kbdev)
|
||||
kbdev->hwcnt.backend.triggered = 1;
|
||||
wake_up(&kbdev->hwcnt.backend.wait);
|
||||
} else if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_DUMPING) {
|
||||
if (kbdev->mmu_mode->flags & KBASE_MMU_MODE_HAS_NON_CACHEABLE) {
|
||||
/* All finished and idle */
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
|
||||
kbdev->hwcnt.backend.triggered = 1;
|
||||
wake_up(&kbdev->hwcnt.backend.wait);
|
||||
} else {
|
||||
int ret;
|
||||
/* Always clean and invalidate the cache after a successful dump
|
||||
*/
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_REQUEST_CLEAN;
|
||||
ret = queue_work(kbdev->hwcnt.backend.cache_clean_wq,
|
||||
&kbdev->hwcnt.backend.cache_clean_work);
|
||||
KBASE_DEBUG_ASSERT(ret);
|
||||
}
|
||||
/* All finished and idle */
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
|
||||
kbdev->hwcnt.backend.triggered = 1;
|
||||
wake_up(&kbdev->hwcnt.backend.wait);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
|
||||
@@ -450,20 +304,16 @@ int kbase_instr_hwcnt_clear(struct kbase_context *kctx)
|
||||
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
|
||||
|
||||
/* Check it's the context previously set up and we're not already
|
||||
* dumping */
|
||||
* dumping
|
||||
*/
|
||||
if (kbdev->hwcnt.kctx != kctx || kbdev->hwcnt.backend.state !=
|
||||
KBASE_INSTR_STATE_IDLE)
|
||||
goto out;
|
||||
|
||||
/* Clear the counters */
|
||||
KBASE_KTRACE_ADD(kbdev, CORE_GPU_PRFCNT_CLEAR, NULL, 0);
|
||||
#if MALI_USE_CSF
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_MCU_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_PRFCNT_CLEAR);
|
||||
#else
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_PRFCNT_CLEAR);
|
||||
#endif
|
||||
|
||||
err = 0;
|
||||
|
||||
@@ -475,46 +325,45 @@ KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_clear);
|
||||
|
||||
int kbase_instr_backend_init(struct kbase_device *kbdev)
|
||||
{
|
||||
int ret = 0;
|
||||
spin_lock_init(&kbdev->hwcnt.lock);
|
||||
|
||||
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DISABLED;
|
||||
|
||||
init_waitqueue_head(&kbdev->hwcnt.backend.wait);
|
||||
INIT_WORK(&kbdev->hwcnt.backend.cache_clean_work,
|
||||
kbasep_cache_clean_worker);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
tasklet_init(&kbdev->hwcnt.backend.csf_hwc_irq_poll_tasklet,
|
||||
kbasep_hwcnt_irq_poll_tasklet, (unsigned long int)kbdev);
|
||||
#endif
|
||||
|
||||
kbdev->hwcnt.backend.triggered = 0;
|
||||
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS
|
||||
kbdev->hwcnt.backend.use_secondary_override = false;
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS
|
||||
/* Use the build time option for the override default. */
|
||||
#if defined(CONFIG_MALI_BIFROST_PRFCNT_SET_SECONDARY)
|
||||
kbdev->hwcnt.backend.override_counter_set = KBASE_HWCNT_SET_SECONDARY;
|
||||
#elif defined(CONFIG_MALI_PRFCNT_SET_TERTIARY)
|
||||
kbdev->hwcnt.backend.override_counter_set = KBASE_HWCNT_SET_TERTIARY;
|
||||
#else
|
||||
/* Default to primary */
|
||||
kbdev->hwcnt.backend.override_counter_set = KBASE_HWCNT_SET_PRIMARY;
|
||||
#endif
|
||||
|
||||
kbdev->hwcnt.backend.cache_clean_wq =
|
||||
alloc_workqueue("Mali cache cleaning workqueue", 0, 1);
|
||||
if (NULL == kbdev->hwcnt.backend.cache_clean_wq)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kbase_instr_backend_term(struct kbase_device *kbdev)
|
||||
{
|
||||
#if MALI_USE_CSF
|
||||
tasklet_kill(&kbdev->hwcnt.backend.csf_hwc_irq_poll_tasklet);
|
||||
#endif
|
||||
destroy_workqueue(kbdev->hwcnt.backend.cache_clean_wq);
|
||||
CSTD_UNUSED(kbdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS
|
||||
void kbase_instr_backend_debugfs_init(struct kbase_device *kbdev)
|
||||
{
|
||||
debugfs_create_bool("hwcnt_use_secondary", S_IRUGO | S_IWUSR,
|
||||
kbdev->mali_debugfs_directory,
|
||||
&kbdev->hwcnt.backend.use_secondary_override);
|
||||
/* No validation is done on the debugfs input. Invalid input could cause
|
||||
* performance counter errors. This is acceptable since this is a debug
|
||||
* only feature and users should know what they are doing.
|
||||
*
|
||||
* Valid inputs are the values accepted bythe SET_SELECT bits of the
|
||||
* PRFCNT_CONFIG register as defined in the architecture specification.
|
||||
*/
|
||||
debugfs_create_u8("hwcnt_set_select", S_IRUGO | S_IWUSR,
|
||||
kbdev->mali_debugfs_directory,
|
||||
(u8 *)&kbdev->hwcnt.backend.override_counter_set);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014, 2016, 2018, 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014, 2016, 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -27,6 +26,8 @@
|
||||
#ifndef _KBASE_INSTR_DEFS_H_
|
||||
#define _KBASE_INSTR_DEFS_H_
|
||||
|
||||
#include "../../mali_kbase_hwcnt_gpu.h"
|
||||
|
||||
/*
|
||||
* Instrumentation State Machine States
|
||||
*/
|
||||
@@ -37,8 +38,6 @@ enum kbase_instr_state {
|
||||
KBASE_INSTR_STATE_IDLE,
|
||||
/* Hardware is currently dumping a frame. */
|
||||
KBASE_INSTR_STATE_DUMPING,
|
||||
/* We've requested a clean to occur on a workqueue */
|
||||
KBASE_INSTR_STATE_REQUEST_CLEAN,
|
||||
/* An error has occured during DUMPING (page fault). */
|
||||
KBASE_INSTR_STATE_FAULT
|
||||
};
|
||||
@@ -47,17 +46,11 @@ enum kbase_instr_state {
|
||||
struct kbase_instr_backend {
|
||||
wait_queue_head_t wait;
|
||||
int triggered;
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS
|
||||
bool use_secondary_override;
|
||||
#ifdef CONFIG_MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS
|
||||
enum kbase_hwcnt_physical_set override_counter_set;
|
||||
#endif
|
||||
|
||||
enum kbase_instr_state state;
|
||||
struct workqueue_struct *cache_clean_wq;
|
||||
struct work_struct cache_clean_work;
|
||||
#if MALI_USE_CSF
|
||||
struct tasklet_struct csf_hwc_irq_poll_tasklet;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _KBASE_INSTR_DEFS_H_ */
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014, 2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014, 2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Backend-specific HW access instrumentation APIs
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2015 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2015, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2016,2018-2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
@@ -215,20 +214,21 @@ int kbase_set_custom_irq_handler(struct kbase_device *kbdev,
|
||||
int result = 0;
|
||||
irq_handler_t requested_irq_handler = NULL;
|
||||
|
||||
KBASE_DEBUG_ASSERT((JOB_IRQ_HANDLER <= irq_type) &&
|
||||
(GPU_IRQ_HANDLER >= irq_type));
|
||||
KBASE_DEBUG_ASSERT((irq_type >= JOB_IRQ_HANDLER) &&
|
||||
(irq_type <= GPU_IRQ_HANDLER));
|
||||
|
||||
/* Release previous handler */
|
||||
if (kbdev->irqs[irq_type].irq)
|
||||
free_irq(kbdev->irqs[irq_type].irq, kbase_tag(kbdev, irq_type));
|
||||
|
||||
requested_irq_handler = (NULL != custom_handler) ? custom_handler :
|
||||
kbase_handler_table[irq_type];
|
||||
requested_irq_handler = (custom_handler != NULL) ?
|
||||
custom_handler :
|
||||
kbase_handler_table[irq_type];
|
||||
|
||||
if (0 != request_irq(kbdev->irqs[irq_type].irq,
|
||||
requested_irq_handler,
|
||||
if (request_irq(kbdev->irqs[irq_type].irq, requested_irq_handler,
|
||||
kbdev->irqs[irq_type].flags | IRQF_SHARED,
|
||||
dev_name(kbdev->dev), kbase_tag(kbdev, irq_type))) {
|
||||
dev_name(kbdev->dev),
|
||||
kbase_tag(kbdev, irq_type)) != 0) {
|
||||
result = -EINVAL;
|
||||
dev_err(kbdev->dev, "Can't request interrupt %d (index %d)\n",
|
||||
kbdev->irqs[irq_type].irq, irq_type);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register backend context / address space management
|
||||
*/
|
||||
@@ -190,8 +188,8 @@ int kbase_backend_find_and_release_free_address_space(
|
||||
}
|
||||
|
||||
/* Context was retained while locks were dropped,
|
||||
* continue looking for free AS */
|
||||
|
||||
* continue looking for free AS
|
||||
*/
|
||||
mutex_unlock(&js_devdata->runpool_mutex);
|
||||
mutex_unlock(&as_js_kctx_info->ctx.jsctx_mutex);
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2016, 2018-2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register-based HW access backend specific definitions
|
||||
*/
|
||||
@@ -78,9 +76,8 @@ struct slot_rb {
|
||||
* The hwaccess_lock (a spinlock) must be held when accessing this structure
|
||||
*/
|
||||
struct kbase_backend_data {
|
||||
struct slot_rb slot_rb[BASE_JM_MAX_NR_SLOTS];
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
struct slot_rb slot_rb[BASE_JM_MAX_NR_SLOTS];
|
||||
struct hrtimer scheduling_timer;
|
||||
|
||||
bool timer_running;
|
||||
@@ -94,13 +91,16 @@ struct kbase_backend_data {
|
||||
/* kbase_prepare_to_reset_gpu has been called */
|
||||
#define KBASE_RESET_GPU_PREPARED 1
|
||||
/* kbase_reset_gpu has been called - the reset will now definitely happen
|
||||
* within the timeout period */
|
||||
* within the timeout period
|
||||
*/
|
||||
#define KBASE_RESET_GPU_COMMITTED 2
|
||||
/* The GPU reset process is currently occuring (timeout has expired or
|
||||
* kbasep_try_reset_gpu_early was called) */
|
||||
* kbasep_try_reset_gpu_early was called)
|
||||
*/
|
||||
#define KBASE_RESET_GPU_HAPPENING 3
|
||||
/* Reset the GPU silently, used when resetting the GPU as part of normal
|
||||
* behavior (e.g. when exiting protected mode). */
|
||||
* behavior (e.g. when exiting protected mode).
|
||||
*/
|
||||
#define KBASE_RESET_GPU_SILENT 4
|
||||
struct workqueue_struct *reset_workq;
|
||||
struct work_struct reset_work;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -41,10 +40,12 @@
|
||||
#include <mali_kbase_regs_history_debugfs.h>
|
||||
|
||||
static void kbasep_try_reset_gpu_early_locked(struct kbase_device *kbdev);
|
||||
static u64 kbasep_apply_limited_core_mask(const struct kbase_device *kbdev,
|
||||
const u64 affinity, const u64 limited_core_mask);
|
||||
|
||||
static u64 kbase_job_write_affinity(struct kbase_device *kbdev,
|
||||
base_jd_core_req core_req,
|
||||
int js)
|
||||
int js, const u64 limited_core_mask)
|
||||
{
|
||||
u64 affinity;
|
||||
|
||||
@@ -73,14 +74,21 @@ static u64 kbase_job_write_affinity(struct kbase_device *kbdev,
|
||||
*/
|
||||
if (js == 2 && num_core_groups > 1)
|
||||
affinity &= coherency_info->group[1].core_mask;
|
||||
else
|
||||
else if (num_core_groups > 1)
|
||||
affinity &= coherency_info->group[0].core_mask;
|
||||
else
|
||||
affinity &= kbdev->gpu_props.curr_config.shader_present;
|
||||
} else {
|
||||
/* Use all cores */
|
||||
affinity = kbdev->pm.backend.shaders_avail &
|
||||
kbdev->pm.debug_core_mask[js];
|
||||
}
|
||||
|
||||
if (core_req & BASE_JD_REQ_LIMITED_CORE_MASK) {
|
||||
/* Limiting affinity due to BASE_JD_REQ_LIMITED_CORE_MASK by applying the limited core mask. */
|
||||
affinity = kbasep_apply_limited_core_mask(kbdev, affinity, limited_core_mask);
|
||||
}
|
||||
|
||||
if (unlikely(!affinity)) {
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
u64 shaders_ready =
|
||||
@@ -90,6 +98,16 @@ static u64 kbase_job_write_affinity(struct kbase_device *kbdev,
|
||||
#endif
|
||||
|
||||
affinity = kbdev->pm.backend.shaders_avail;
|
||||
|
||||
if (core_req & BASE_JD_REQ_LIMITED_CORE_MASK) {
|
||||
/* Limiting affinity again to make sure it only enables shader cores with backed TLS memory. */
|
||||
affinity = kbasep_apply_limited_core_mask(kbdev, affinity, limited_core_mask);
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
/* affinity should never be 0 */
|
||||
WARN_ON(!affinity);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
kbase_reg_write(kbdev, JOB_SLOT_REG(js, JS_AFFINITY_NEXT_LO),
|
||||
@@ -170,7 +188,7 @@ static u64 select_job_chain(struct kbase_jd_atom *katom)
|
||||
}
|
||||
|
||||
dev_dbg(kctx->kbdev->dev,
|
||||
"Selected job chain 0x%llx for end atom %p in state %d\n",
|
||||
"Selected job chain 0x%llx for end atom %pK in state %d\n",
|
||||
jc, (void *)katom, (int)rp->state);
|
||||
|
||||
katom->jc = jc;
|
||||
@@ -194,7 +212,7 @@ void kbase_job_hw_submit(struct kbase_device *kbdev,
|
||||
/* Command register must be available */
|
||||
KBASE_DEBUG_ASSERT(kbasep_jm_is_js_free(kbdev, js, kctx));
|
||||
|
||||
dev_dbg(kctx->kbdev->dev, "Write JS_HEAD_NEXT 0x%llx for atom %p\n",
|
||||
dev_dbg(kctx->kbdev->dev, "Write JS_HEAD_NEXT 0x%llx for atom %pK\n",
|
||||
jc_head, (void *)katom);
|
||||
|
||||
kbase_reg_write(kbdev, JOB_SLOT_REG(js, JS_HEAD_NEXT_LO),
|
||||
@@ -202,10 +220,12 @@ void kbase_job_hw_submit(struct kbase_device *kbdev,
|
||||
kbase_reg_write(kbdev, JOB_SLOT_REG(js, JS_HEAD_NEXT_HI),
|
||||
jc_head >> 32);
|
||||
|
||||
affinity = kbase_job_write_affinity(kbdev, katom->core_req, js);
|
||||
affinity = kbase_job_write_affinity(kbdev, katom->core_req, js,
|
||||
kctx->limited_core_mask);
|
||||
|
||||
/* start MMU, medium priority, cache clean/flush on end, clean/flush on
|
||||
* start */
|
||||
* start
|
||||
*/
|
||||
cfg = kctx->as_nr;
|
||||
|
||||
if (kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_FLUSH_REDUCTION) &&
|
||||
@@ -257,7 +277,7 @@ void kbase_job_hw_submit(struct kbase_device *kbdev,
|
||||
katom->start_timestamp = ktime_get();
|
||||
|
||||
/* GO ! */
|
||||
dev_dbg(kbdev->dev, "JS: Submitting atom %p from ctx %p to js[%d] with head=0x%llx",
|
||||
dev_dbg(kbdev->dev, "JS: Submitting atom %pK from ctx %pK to js[%d] with head=0x%llx",
|
||||
katom, kctx, js, jc_head);
|
||||
|
||||
KBASE_KTRACE_ADD_JM_SLOT_INFO(kbdev, JM_SUBMIT, kctx, katom, jc_head, js,
|
||||
@@ -331,7 +351,8 @@ static void kbasep_job_slot_update_head_start_timestamp(
|
||||
/* Only update the timestamp if it's a better estimate
|
||||
* than what's currently stored. This is because our
|
||||
* estimate that accounts for the throttle time may be
|
||||
* too much of an overestimate */
|
||||
* too much of an overestimate
|
||||
*/
|
||||
katom->start_timestamp = end_timestamp;
|
||||
}
|
||||
}
|
||||
@@ -374,9 +395,9 @@ void kbase_job_done(struct kbase_device *kbdev, u32 done)
|
||||
/* treat failed slots as finished slots */
|
||||
u32 finished = (done & 0xFFFF) | failed;
|
||||
|
||||
/* Note: This is inherently unfair, as we always check
|
||||
* for lower numbered interrupts before the higher
|
||||
* numbered ones.*/
|
||||
/* Note: This is inherently unfair, as we always check for lower
|
||||
* numbered interrupts before the higher numbered ones.
|
||||
*/
|
||||
i = ffs(finished) - 1;
|
||||
KBASE_DEBUG_ASSERT(i >= 0);
|
||||
|
||||
@@ -388,7 +409,8 @@ void kbase_job_done(struct kbase_device *kbdev, u32 done)
|
||||
|
||||
if (failed & (1u << i)) {
|
||||
/* read out the job slot status code if the job
|
||||
* slot reported failure */
|
||||
* slot reported failure
|
||||
*/
|
||||
completion_code = kbase_reg_read(kbdev,
|
||||
JOB_SLOT_REG(i, JS_STATUS));
|
||||
|
||||
@@ -402,7 +424,8 @@ void kbase_job_done(struct kbase_device *kbdev, u32 done)
|
||||
|
||||
/* Soft-stopped job - read the value of
|
||||
* JS<n>_TAIL so that the job chain can
|
||||
* be resumed */
|
||||
* be resumed
|
||||
*/
|
||||
job_tail = (u64)kbase_reg_read(kbdev,
|
||||
JOB_SLOT_REG(i, JS_TAIL_LO)) |
|
||||
((u64)kbase_reg_read(kbdev,
|
||||
@@ -411,21 +434,26 @@ void kbase_job_done(struct kbase_device *kbdev, u32 done)
|
||||
} else if (completion_code ==
|
||||
BASE_JD_EVENT_NOT_STARTED) {
|
||||
/* PRLAM-10673 can cause a TERMINATED
|
||||
* job to come back as NOT_STARTED, but
|
||||
* the error interrupt helps us detect
|
||||
* it */
|
||||
* job to come back as NOT_STARTED,
|
||||
* but the error interrupt helps us
|
||||
* detect it
|
||||
*/
|
||||
completion_code =
|
||||
BASE_JD_EVENT_TERMINATED;
|
||||
}
|
||||
|
||||
kbase_gpu_irq_evict(kbdev, i, completion_code);
|
||||
|
||||
/* Some jobs that encounter a BUS FAULT may result in corrupted
|
||||
* state causing future jobs to hang. Reset GPU before
|
||||
* allowing any other jobs on the slot to continue. */
|
||||
/* Some jobs that encounter a BUS FAULT may
|
||||
* result in corrupted state causing future
|
||||
* jobs to hang. Reset GPU before allowing
|
||||
* any other jobs on the slot to continue.
|
||||
*/
|
||||
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TTRX_3076)) {
|
||||
if (completion_code == BASE_JD_EVENT_JOB_BUS_FAULT) {
|
||||
if (kbase_prepare_to_reset_gpu_locked(kbdev))
|
||||
if (kbase_prepare_to_reset_gpu_locked(
|
||||
kbdev,
|
||||
RESET_FLAGS_NONE))
|
||||
kbase_reset_gpu_locked(kbdev);
|
||||
}
|
||||
}
|
||||
@@ -483,7 +511,8 @@ void kbase_job_done(struct kbase_device *kbdev, u32 done)
|
||||
|
||||
if ((rawstat >> (i + 16)) & 1) {
|
||||
/* There is a failed job that we've
|
||||
* missed - add it back to active */
|
||||
* missed - add it back to active
|
||||
*/
|
||||
active |= (1u << i);
|
||||
}
|
||||
}
|
||||
@@ -585,7 +614,8 @@ void kbasep_job_slot_soft_or_hard_stop_do_action(struct kbase_device *kbdev,
|
||||
}
|
||||
|
||||
/* We are about to issue a soft stop, so mark the atom as having
|
||||
* been soft stopped */
|
||||
* been soft stopped
|
||||
*/
|
||||
target_katom->atom_flags |= KBASE_KATOM_FLAG_BEEN_SOFT_STOPPED;
|
||||
|
||||
/* Mark the point where we issue the soft-stop command */
|
||||
@@ -781,7 +811,7 @@ static int softstop_start_rp_nolock(
|
||||
|
||||
if (!(katom->core_req & BASE_JD_REQ_START_RENDERPASS)) {
|
||||
dev_dbg(kctx->kbdev->dev,
|
||||
"Atom %p on job slot is not start RP\n", (void *)katom);
|
||||
"Atom %pK on job slot is not start RP\n", (void *)katom);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
@@ -794,13 +824,13 @@ static int softstop_start_rp_nolock(
|
||||
rp->state != KBASE_JD_RP_RETRY))
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(kctx->kbdev->dev, "OOM in state %d with region %p\n",
|
||||
dev_dbg(kctx->kbdev->dev, "OOM in state %d with region %pK\n",
|
||||
(int)rp->state, (void *)reg);
|
||||
|
||||
if (WARN_ON(katom != rp->start_katom))
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(kctx->kbdev->dev, "Adding region %p to list %p\n",
|
||||
dev_dbg(kctx->kbdev->dev, "Adding region %pK to list %pK\n",
|
||||
(void *)reg, (void *)&rp->oom_reg_list);
|
||||
list_move_tail(®->link, &rp->oom_reg_list);
|
||||
dev_dbg(kctx->kbdev->dev, "Added region to list\n");
|
||||
@@ -845,9 +875,9 @@ void kbase_jm_wait_for_zero_jobs(struct kbase_context *kctx)
|
||||
if (timeout != 0)
|
||||
goto exit;
|
||||
|
||||
if (kbase_prepare_to_reset_gpu(kbdev)) {
|
||||
if (kbase_prepare_to_reset_gpu(kbdev, RESET_FLAGS_NONE)) {
|
||||
dev_err(kbdev->dev,
|
||||
"Issueing GPU soft-reset because jobs failed to be killed (within %d ms) as part of context termination (e.g. process exit)\n",
|
||||
"Issuing GPU soft-reset because jobs failed to be killed (within %d ms) as part of context termination (e.g. process exit)\n",
|
||||
ZAP_TIMEOUT);
|
||||
kbase_reset_gpu(kbdev);
|
||||
}
|
||||
@@ -855,7 +885,7 @@ void kbase_jm_wait_for_zero_jobs(struct kbase_context *kctx)
|
||||
/* Wait for the reset to complete */
|
||||
kbase_reset_gpu_wait(kbdev);
|
||||
exit:
|
||||
dev_dbg(kbdev->dev, "Zap: Finished Context %p", kctx);
|
||||
dev_dbg(kbdev->dev, "Zap: Finished Context %pK", kctx);
|
||||
|
||||
/* Ensure that the signallers of the waitqs have finished */
|
||||
mutex_lock(&kctx->jctx.lock);
|
||||
@@ -916,7 +946,7 @@ KBASE_EXPORT_TEST_API(kbase_job_slot_term);
|
||||
void kbase_job_slot_softstop_swflags(struct kbase_device *kbdev, int js,
|
||||
struct kbase_jd_atom *target_katom, u32 sw_flags)
|
||||
{
|
||||
dev_dbg(kbdev->dev, "Soft-stop atom %p with flags 0x%x (s:%d)\n",
|
||||
dev_dbg(kbdev->dev, "Soft-stop atom %pK with flags 0x%x (s:%d)\n",
|
||||
target_katom, sw_flags, js);
|
||||
|
||||
KBASE_DEBUG_ASSERT(!(sw_flags & JS_COMMAND_MASK));
|
||||
@@ -1020,6 +1050,33 @@ void kbase_job_check_leave_disjoint(struct kbase_device *kbdev,
|
||||
}
|
||||
}
|
||||
|
||||
int kbase_reset_gpu_prevent_and_wait(struct kbase_device *kbdev)
|
||||
{
|
||||
WARN(true, "%s Not implemented for JM GPUs", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kbase_reset_gpu_try_prevent(struct kbase_device *kbdev)
|
||||
{
|
||||
WARN(true, "%s Not implemented for JM GPUs", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void kbase_reset_gpu_allow(struct kbase_device *kbdev)
|
||||
{
|
||||
WARN(true, "%s Not implemented for JM GPUs", __func__);
|
||||
}
|
||||
|
||||
void kbase_reset_gpu_assert_prevented(struct kbase_device *kbdev)
|
||||
{
|
||||
WARN(true, "%s Not implemented for JM GPUs", __func__);
|
||||
}
|
||||
|
||||
void kbase_reset_gpu_assert_failed_or_prevented(struct kbase_device *kbdev)
|
||||
{
|
||||
WARN(true, "%s Not implemented for JM GPUs", __func__);
|
||||
}
|
||||
|
||||
static void kbase_debug_dump_registers(struct kbase_device *kbdev)
|
||||
{
|
||||
int i;
|
||||
@@ -1086,13 +1143,15 @@ static void kbasep_reset_timeout_worker(struct work_struct *data)
|
||||
|
||||
/* Make sure the timer has completed - this cannot be done from
|
||||
* interrupt context, so this cannot be done within
|
||||
* kbasep_try_reset_gpu_early. */
|
||||
* kbasep_try_reset_gpu_early.
|
||||
*/
|
||||
hrtimer_cancel(&kbdev->hwaccess.backend.reset_timer);
|
||||
|
||||
if (kbase_pm_context_active_handle_suspend(kbdev,
|
||||
KBASE_PM_SUSPEND_HANDLER_DONT_REACTIVATE)) {
|
||||
/* This would re-activate the GPU. Since it's already idle,
|
||||
* there's no need to reset it */
|
||||
* there's no need to reset it
|
||||
*/
|
||||
atomic_set(&kbdev->hwaccess.backend.reset_gpu,
|
||||
KBASE_RESET_GPU_NOT_PENDING);
|
||||
kbase_disjoint_state_down(kbdev);
|
||||
@@ -1113,14 +1172,16 @@ static void kbasep_reset_timeout_worker(struct work_struct *data)
|
||||
kbdev->irq_reset_flush = true;
|
||||
|
||||
/* Disable IRQ to avoid IRQ handlers to kick in after releasing the
|
||||
* spinlock; this also clears any outstanding interrupts */
|
||||
* spinlock; this also clears any outstanding interrupts
|
||||
*/
|
||||
kbase_pm_disable_interrupts_nolock(kbdev);
|
||||
|
||||
spin_unlock(&kbdev->mmu_mask_change);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
/* Ensure that any IRQ handlers have finished
|
||||
* Must be done without any locks IRQ handlers will take */
|
||||
* Must be done without any locks IRQ handlers will take
|
||||
*/
|
||||
kbase_synchronize_irqs(kbdev);
|
||||
|
||||
/* Flush out any in-flight work items */
|
||||
@@ -1131,7 +1192,8 @@ static void kbasep_reset_timeout_worker(struct work_struct *data)
|
||||
|
||||
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TMIX_8463)) {
|
||||
/* Ensure that L2 is not transitioning when we send the reset
|
||||
* command */
|
||||
* command
|
||||
*/
|
||||
while (--max_loops && kbase_pm_get_trans_cores(kbdev,
|
||||
KBASE_PM_CORE_L2))
|
||||
;
|
||||
@@ -1146,14 +1208,16 @@ static void kbasep_reset_timeout_worker(struct work_struct *data)
|
||||
/* All slot have been soft-stopped and we've waited
|
||||
* SOFT_STOP_RESET_TIMEOUT for the slots to clear, at this point we
|
||||
* assume that anything that is still left on the GPU is stuck there and
|
||||
* we'll kill it when we reset the GPU */
|
||||
* we'll kill it when we reset the GPU
|
||||
*/
|
||||
|
||||
if (!silent)
|
||||
dev_err(kbdev->dev, "Resetting GPU (allowing up to %d ms)",
|
||||
RESET_TIMEOUT);
|
||||
|
||||
/* Output the state of some interesting registers to help in the
|
||||
* debugging of GPU resets */
|
||||
* debugging of GPU resets
|
||||
*/
|
||||
if (!silent)
|
||||
kbase_debug_dump_registers(kbdev);
|
||||
|
||||
@@ -1192,7 +1256,8 @@ static void kbasep_reset_timeout_worker(struct work_struct *data)
|
||||
kbase_pm_update_cores_state(kbdev);
|
||||
|
||||
/* Synchronously request and wait for those cores, because if
|
||||
* instrumentation is enabled it would need them immediately. */
|
||||
* instrumentation is enabled it would need them immediately.
|
||||
*/
|
||||
kbase_pm_wait_for_desired_state(kbdev);
|
||||
|
||||
mutex_unlock(&kbdev->pm.lock);
|
||||
@@ -1269,7 +1334,8 @@ static void kbasep_try_reset_gpu_early_locked(struct kbase_device *kbdev)
|
||||
|
||||
/* Check that the reset has been committed to (i.e. kbase_reset_gpu has
|
||||
* been called), and that no other thread beat this thread to starting
|
||||
* the reset */
|
||||
* the reset
|
||||
*/
|
||||
if (atomic_cmpxchg(&kbdev->hwaccess.backend.reset_gpu,
|
||||
KBASE_RESET_GPU_COMMITTED, KBASE_RESET_GPU_HAPPENING) !=
|
||||
KBASE_RESET_GPU_COMMITTED) {
|
||||
@@ -1293,6 +1359,7 @@ static void kbasep_try_reset_gpu_early(struct kbase_device *kbdev)
|
||||
/**
|
||||
* kbase_prepare_to_reset_gpu_locked - Prepare for resetting the GPU
|
||||
* @kbdev: kbase device
|
||||
* @flags: Bitfield indicating impact of reset (see flag defines)
|
||||
*
|
||||
* This function just soft-stops all the slots to ensure that as many jobs as
|
||||
* possible are saved.
|
||||
@@ -1303,10 +1370,12 @@ static void kbasep_try_reset_gpu_early(struct kbase_device *kbdev)
|
||||
* false - Another thread is performing a reset, kbase_reset_gpu should
|
||||
* not be called.
|
||||
*/
|
||||
bool kbase_prepare_to_reset_gpu_locked(struct kbase_device *kbdev)
|
||||
bool kbase_prepare_to_reset_gpu_locked(struct kbase_device *kbdev,
|
||||
unsigned int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
CSTD_UNUSED(flags);
|
||||
KBASE_DEBUG_ASSERT(kbdev);
|
||||
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
@@ -1334,14 +1403,14 @@ bool kbase_prepare_to_reset_gpu_locked(struct kbase_device *kbdev)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool kbase_prepare_to_reset_gpu(struct kbase_device *kbdev)
|
||||
bool kbase_prepare_to_reset_gpu(struct kbase_device *kbdev, unsigned int flags)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long lock_flags;
|
||||
bool ret;
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
ret = kbase_prepare_to_reset_gpu_locked(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, lock_flags);
|
||||
ret = kbase_prepare_to_reset_gpu_locked(kbdev, flags);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, lock_flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1362,7 +1431,8 @@ void kbase_reset_gpu(struct kbase_device *kbdev)
|
||||
KBASE_DEBUG_ASSERT(kbdev);
|
||||
|
||||
/* Note this is an assert/atomic_set because it is a software issue for
|
||||
* a race to be occuring here */
|
||||
* a race to be occurring here
|
||||
*/
|
||||
KBASE_DEBUG_ASSERT(atomic_read(&kbdev->hwaccess.backend.reset_gpu) ==
|
||||
KBASE_RESET_GPU_PREPARED);
|
||||
atomic_set(&kbdev->hwaccess.backend.reset_gpu,
|
||||
@@ -1385,7 +1455,8 @@ void kbase_reset_gpu_locked(struct kbase_device *kbdev)
|
||||
KBASE_DEBUG_ASSERT(kbdev);
|
||||
|
||||
/* Note this is an assert/atomic_set because it is a software issue for
|
||||
* a race to be occuring here */
|
||||
* a race to be occurring here
|
||||
*/
|
||||
KBASE_DEBUG_ASSERT(atomic_read(&kbdev->hwaccess.backend.reset_gpu) ==
|
||||
KBASE_RESET_GPU_PREPARED);
|
||||
atomic_set(&kbdev->hwaccess.backend.reset_gpu,
|
||||
@@ -1460,3 +1531,21 @@ void kbase_reset_gpu_term(struct kbase_device *kbdev)
|
||||
{
|
||||
destroy_workqueue(kbdev->hwaccess.backend.reset_workq);
|
||||
}
|
||||
|
||||
static u64 kbasep_apply_limited_core_mask(const struct kbase_device *kbdev,
|
||||
const u64 affinity, const u64 limited_core_mask)
|
||||
{
|
||||
const u64 result = affinity & limited_core_mask;
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
dev_dbg(kbdev->dev,
|
||||
"Limiting affinity due to BASE_JD_REQ_LIMITED_CORE_MASK from 0x%lx to 0x%lx (mask is 0x%lx)\n",
|
||||
(unsigned long int)affinity,
|
||||
(unsigned long int)result,
|
||||
(unsigned long int)limited_core_mask);
|
||||
#else
|
||||
CSTD_UNUSED(kbdev);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2011-2016, 2018-2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Job Manager backend-specific low-level APIs.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register-based HW access backend specific APIs
|
||||
*/
|
||||
@@ -40,10 +38,12 @@
|
||||
#include <backend/gpu/mali_kbase_pm_internal.h>
|
||||
|
||||
/* Return whether the specified ringbuffer is empty. HW access lock must be
|
||||
* held */
|
||||
* held
|
||||
*/
|
||||
#define SLOT_RB_EMPTY(rb) (rb->write_idx == rb->read_idx)
|
||||
/* Return number of atoms currently in the specified ringbuffer. HW access lock
|
||||
* must be held */
|
||||
* must be held
|
||||
*/
|
||||
#define SLOT_RB_ENTRIES(rb) (int)(s8)(rb->write_idx - rb->read_idx)
|
||||
|
||||
static void kbase_gpu_release_atom(struct kbase_device *kbdev,
|
||||
@@ -284,7 +284,8 @@ static void kbase_gpu_release_atom(struct kbase_device *kbdev,
|
||||
kbase_kinstr_jm_atom_hw_release(katom);
|
||||
/* Inform power management at start/finish of atom so it can
|
||||
* update its GPU utilisation metrics. Mark atom as not
|
||||
* submitted beforehand. */
|
||||
* submitted beforehand.
|
||||
*/
|
||||
katom->gpu_rb_state = KBASE_ATOM_GPU_RB_READY;
|
||||
kbase_pm_metrics_update(kbdev, end_timestamp);
|
||||
|
||||
@@ -544,7 +545,8 @@ static int kbase_jm_enter_protected_mode(struct kbase_device *kbdev,
|
||||
KBASE_TLSTREAM_AUX_PROTECTED_ENTER_START(kbdev, kbdev);
|
||||
/* The checks in KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV
|
||||
* should ensure that we are not already transitiong, and that
|
||||
* there are no atoms currently on the GPU. */
|
||||
* there are no atoms currently on the GPU.
|
||||
*/
|
||||
WARN_ON(kbdev->protected_mode_transition);
|
||||
WARN_ON(kbase_gpu_atoms_submitted_any(kbdev));
|
||||
/* If hwcnt is disabled, it means we didn't clean up correctly
|
||||
@@ -570,19 +572,15 @@ static int kbase_jm_enter_protected_mode(struct kbase_device *kbdev,
|
||||
|
||||
/* We couldn't disable atomically, so kick off a worker */
|
||||
if (!kbdev->protected_mode_hwcnt_disabled) {
|
||||
#if KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE
|
||||
queue_work(system_wq,
|
||||
kbase_hwcnt_context_queue_work(
|
||||
kbdev->hwcnt_gpu_ctx,
|
||||
&kbdev->protected_mode_hwcnt_disable_work);
|
||||
#else
|
||||
queue_work(system_highpri_wq,
|
||||
&kbdev->protected_mode_hwcnt_disable_work);
|
||||
#endif
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Once reaching this point GPU must be
|
||||
* switched to protected mode or hwcnt
|
||||
* re-enabled. */
|
||||
/* Once reaching this point GPU must be switched to protected
|
||||
* mode or hwcnt re-enabled.
|
||||
*/
|
||||
|
||||
if (kbase_pm_protected_entry_override_enable(kbdev))
|
||||
return -EAGAIN;
|
||||
@@ -722,7 +720,8 @@ static int kbase_jm_exit_protected_mode(struct kbase_device *kbdev,
|
||||
KBASE_TLSTREAM_AUX_PROTECTED_LEAVE_START(kbdev, kbdev);
|
||||
/* The checks in KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV
|
||||
* should ensure that we are not already transitiong, and that
|
||||
* there are no atoms currently on the GPU. */
|
||||
* there are no atoms currently on the GPU.
|
||||
*/
|
||||
WARN_ON(kbdev->protected_mode_transition);
|
||||
WARN_ON(kbase_gpu_atoms_submitted_any(kbdev));
|
||||
|
||||
@@ -768,8 +767,8 @@ static int kbase_jm_exit_protected_mode(struct kbase_device *kbdev,
|
||||
katom[idx]->event_code = BASE_JD_EVENT_JOB_INVALID;
|
||||
kbase_gpu_mark_atom_for_return(kbdev, katom[idx]);
|
||||
/* Only return if head atom or previous atom
|
||||
* already removed - as atoms must be returned
|
||||
* in order */
|
||||
* already removed - as atoms must be returned in order
|
||||
*/
|
||||
if (idx == 0 || katom[0]->gpu_rb_state ==
|
||||
KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) {
|
||||
kbase_gpu_dequeue_atom(kbdev, js, NULL);
|
||||
@@ -912,12 +911,14 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
kbase_gpu_mark_atom_for_return(kbdev,
|
||||
katom[idx]);
|
||||
/* Set EVENT_DONE so this atom will be
|
||||
completed, not unpulled. */
|
||||
* completed, not unpulled.
|
||||
*/
|
||||
katom[idx]->event_code =
|
||||
BASE_JD_EVENT_DONE;
|
||||
/* Only return if head atom or previous
|
||||
* atom already removed - as atoms must
|
||||
* be returned in order. */
|
||||
* be returned in order.
|
||||
*/
|
||||
if (idx == 0 || katom[0]->gpu_rb_state ==
|
||||
KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) {
|
||||
kbase_gpu_dequeue_atom(kbdev, js, NULL);
|
||||
@@ -948,7 +949,8 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
|
||||
if (idx == 1) {
|
||||
/* Only submit if head atom or previous
|
||||
* atom already submitted */
|
||||
* atom already submitted
|
||||
*/
|
||||
if ((katom[0]->gpu_rb_state !=
|
||||
KBASE_ATOM_GPU_RB_SUBMITTED &&
|
||||
katom[0]->gpu_rb_state !=
|
||||
@@ -964,7 +966,8 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
/* If inter-slot serialization in use then don't
|
||||
* submit atom if any other slots are in use */
|
||||
* submit atom if any other slots are in use
|
||||
*/
|
||||
if ((kbdev->serialize_jobs &
|
||||
KBASE_SERIALIZE_INTER_SLOT) &&
|
||||
other_slots_busy(kbdev, js))
|
||||
@@ -976,7 +979,8 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
break;
|
||||
#endif
|
||||
/* Check if this job needs the cycle counter
|
||||
* enabled before submission */
|
||||
* enabled before submission
|
||||
*/
|
||||
if (katom[idx]->core_req & BASE_JD_REQ_PERMON)
|
||||
kbase_pm_request_gpu_cycle_counter_l2_is_on(
|
||||
kbdev);
|
||||
@@ -987,7 +991,8 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
|
||||
/* Inform power management at start/finish of
|
||||
* atom so it can update its GPU utilisation
|
||||
* metrics. */
|
||||
* metrics.
|
||||
*/
|
||||
kbase_pm_metrics_update(kbdev,
|
||||
&katom[idx]->start_timestamp);
|
||||
|
||||
@@ -1000,7 +1005,8 @@ void kbase_backend_slot_update(struct kbase_device *kbdev)
|
||||
case KBASE_ATOM_GPU_RB_RETURN_TO_JS:
|
||||
/* Only return if head atom or previous atom
|
||||
* already removed - as atoms must be returned
|
||||
* in order */
|
||||
* in order
|
||||
*/
|
||||
if (idx == 0 || katom[0]->gpu_rb_state ==
|
||||
KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) {
|
||||
kbase_gpu_dequeue_atom(kbdev, js, NULL);
|
||||
@@ -1018,7 +1024,7 @@ void kbase_backend_run_atom(struct kbase_device *kbdev,
|
||||
struct kbase_jd_atom *katom)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
dev_dbg(kbdev->dev, "Backend running atom %p\n", (void *)katom);
|
||||
dev_dbg(kbdev->dev, "Backend running atom %pK\n", (void *)katom);
|
||||
|
||||
kbase_gpu_enqueue_atom(kbdev, katom);
|
||||
kbase_backend_slot_update(kbdev);
|
||||
@@ -1079,7 +1085,7 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
struct kbase_context *kctx = katom->kctx;
|
||||
|
||||
dev_dbg(kbdev->dev,
|
||||
"Atom %p completed on hw with code 0x%x and job_tail 0x%llx (s:%d)\n",
|
||||
"Atom %pK completed on hw with code 0x%x and job_tail 0x%llx (s:%d)\n",
|
||||
(void *)katom, completion_code, job_tail, js);
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
@@ -1103,7 +1109,8 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
* BASE_JD_REQ_SKIP_CACHE_END is set, the GPU cache is not
|
||||
* flushed. To prevent future evictions causing possible memory
|
||||
* corruption we need to flush the cache manually before any
|
||||
* affected memory gets reused. */
|
||||
* affected memory gets reused.
|
||||
*/
|
||||
katom->need_cache_flush_cores_retained = true;
|
||||
}
|
||||
|
||||
@@ -1184,7 +1191,8 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
katom_idx1->gpu_rb_state !=
|
||||
KBASE_ATOM_GPU_RB_SUBMITTED) {
|
||||
/* Can not dequeue this atom yet - will be
|
||||
* dequeued when atom at idx0 completes */
|
||||
* dequeued when atom at idx0 completes
|
||||
*/
|
||||
katom_idx1->event_code = BASE_JD_EVENT_STOPPED;
|
||||
kbase_gpu_mark_atom_for_return(kbdev,
|
||||
katom_idx1);
|
||||
@@ -1197,7 +1205,7 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
if (job_tail != 0 && job_tail != katom->jc) {
|
||||
/* Some of the job has been executed */
|
||||
dev_dbg(kbdev->dev,
|
||||
"Update job chain address of atom %p to resume from 0x%llx\n",
|
||||
"Update job chain address of atom %pK to resume from 0x%llx\n",
|
||||
(void *)katom, job_tail);
|
||||
|
||||
katom->jc = job_tail;
|
||||
@@ -1258,7 +1266,7 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
|
||||
if (katom) {
|
||||
dev_dbg(kbdev->dev,
|
||||
"Cross-slot dependency %p has become runnable.\n",
|
||||
"Cross-slot dependency %pK has become runnable.\n",
|
||||
(void *)katom);
|
||||
|
||||
/* Check if there are lower priority jobs to soft stop */
|
||||
@@ -1271,7 +1279,8 @@ void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
|
||||
kbase_pm_update_state(kbdev);
|
||||
|
||||
/* Job completion may have unblocked other atoms. Try to update all job
|
||||
* slots */
|
||||
* slots
|
||||
*/
|
||||
kbase_backend_slot_update(kbdev);
|
||||
}
|
||||
|
||||
@@ -1322,7 +1331,8 @@ void kbase_backend_reset(struct kbase_device *kbdev, ktime_t *end_timestamp)
|
||||
katom->protected_state.exit = KBASE_ATOM_EXIT_PROTECTED_CHECK;
|
||||
/* As the atom was not removed, increment the
|
||||
* index so that we read the correct atom in the
|
||||
* next iteration. */
|
||||
* next iteration.
|
||||
*/
|
||||
atom_idx++;
|
||||
continue;
|
||||
}
|
||||
@@ -1425,7 +1435,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
katom_idx0_valid = (katom_idx0 == katom);
|
||||
/* If idx0 is to be removed and idx1 is on the same context,
|
||||
* then idx1 must also be removed otherwise the atoms might be
|
||||
* returned out of order */
|
||||
* returned out of order
|
||||
*/
|
||||
if (katom_idx1)
|
||||
katom_idx1_valid = (katom_idx1 == katom) ||
|
||||
(katom_idx0_valid &&
|
||||
@@ -1472,7 +1483,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
if (kbase_reg_read(kbdev, JOB_SLOT_REG(js,
|
||||
JS_COMMAND_NEXT)) == 0) {
|
||||
/* idx0 has already completed - stop
|
||||
* idx1 if needed*/
|
||||
* idx1 if needed
|
||||
*/
|
||||
if (katom_idx1_valid) {
|
||||
kbase_gpu_stop_atom(kbdev, js,
|
||||
katom_idx1,
|
||||
@@ -1481,7 +1493,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
}
|
||||
} else {
|
||||
/* idx1 is in NEXT registers - attempt
|
||||
* to remove */
|
||||
* to remove
|
||||
*/
|
||||
kbase_reg_write(kbdev,
|
||||
JOB_SLOT_REG(js,
|
||||
JS_COMMAND_NEXT),
|
||||
@@ -1496,7 +1509,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
JS_HEAD_NEXT_HI))
|
||||
!= 0) {
|
||||
/* idx1 removed successfully,
|
||||
* will be handled in IRQ */
|
||||
* will be handled in IRQ
|
||||
*/
|
||||
kbase_gpu_remove_atom(kbdev,
|
||||
katom_idx1,
|
||||
action, true);
|
||||
@@ -1510,7 +1524,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
ret = true;
|
||||
} else if (katom_idx1_valid) {
|
||||
/* idx0 has already completed,
|
||||
* stop idx1 if needed */
|
||||
* stop idx1 if needed
|
||||
*/
|
||||
kbase_gpu_stop_atom(kbdev, js,
|
||||
katom_idx1,
|
||||
action);
|
||||
@@ -1529,7 +1544,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
* flow was also interrupted, and this function
|
||||
* might not enter disjoint state e.g. if we
|
||||
* don't actually do a hard stop on the head
|
||||
* atom */
|
||||
* atom
|
||||
*/
|
||||
kbase_gpu_stop_atom(kbdev, js, katom_idx0,
|
||||
action);
|
||||
ret = true;
|
||||
@@ -1557,7 +1573,8 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
ret = true;
|
||||
} else {
|
||||
/* idx1 is in NEXT registers - attempt to
|
||||
* remove */
|
||||
* remove
|
||||
*/
|
||||
kbase_reg_write(kbdev, JOB_SLOT_REG(js,
|
||||
JS_COMMAND_NEXT),
|
||||
JS_COMMAND_NOP);
|
||||
@@ -1567,13 +1584,15 @@ bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
|
||||
kbase_reg_read(kbdev, JOB_SLOT_REG(js,
|
||||
JS_HEAD_NEXT_HI)) != 0) {
|
||||
/* idx1 removed successfully, will be
|
||||
* handled in IRQ once idx0 completes */
|
||||
* handled in IRQ once idx0 completes
|
||||
*/
|
||||
kbase_gpu_remove_atom(kbdev, katom_idx1,
|
||||
action,
|
||||
false);
|
||||
} else {
|
||||
/* idx0 has already completed - stop
|
||||
* idx1 */
|
||||
* idx1
|
||||
*/
|
||||
kbase_gpu_stop_atom(kbdev, js,
|
||||
katom_idx1,
|
||||
action);
|
||||
@@ -1647,7 +1666,7 @@ void kbase_gpu_dump_slots(struct kbase_device *kbdev)
|
||||
|
||||
if (katom)
|
||||
dev_info(kbdev->dev,
|
||||
" js%d idx%d : katom=%p gpu_rb_state=%d\n",
|
||||
" js%d idx%d : katom=%pK gpu_rb_state=%d\n",
|
||||
js, idx, katom, katom->gpu_rb_state);
|
||||
else
|
||||
dev_info(kbdev->dev, " js%d idx%d : empty\n",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register-based HW access backend specific APIs
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register-based HW access backend specific job scheduler APIs
|
||||
*/
|
||||
@@ -48,7 +46,8 @@ static inline bool timer_callback_should_run(struct kbase_device *kbdev)
|
||||
|
||||
/* nr_contexts_pullable is updated with the runpool_mutex. However, the
|
||||
* locking in the caller gives us a barrier that ensures
|
||||
* nr_contexts_pullable is up-to-date for reading */
|
||||
* nr_contexts_pullable is up-to-date for reading
|
||||
*/
|
||||
nr_running_ctxs = atomic_read(&kbdev->js_data.nr_contexts_runnable);
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
@@ -114,7 +113,8 @@ static enum hrtimer_restart timer_callback(struct hrtimer *timer)
|
||||
|
||||
if (atom != NULL) {
|
||||
/* The current version of the model doesn't support
|
||||
* Soft-Stop */
|
||||
* Soft-Stop
|
||||
*/
|
||||
if (!kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_5736)) {
|
||||
u32 ticks = atom->ticks++;
|
||||
|
||||
@@ -142,7 +142,8 @@ static enum hrtimer_restart timer_callback(struct hrtimer *timer)
|
||||
* new soft_stop timeout. This ensures that
|
||||
* atoms do not miss any of the timeouts due to
|
||||
* races between this worker and the thread
|
||||
* changing the timeouts. */
|
||||
* changing the timeouts.
|
||||
*/
|
||||
if (backend->timeouts_updated &&
|
||||
ticks > soft_stop_ticks)
|
||||
ticks = atom->ticks = soft_stop_ticks;
|
||||
@@ -172,10 +173,11 @@ static enum hrtimer_restart timer_callback(struct hrtimer *timer)
|
||||
*
|
||||
* Similarly, if it's about to be
|
||||
* decreased, the last job from another
|
||||
* context has already finished, so it's
|
||||
* not too bad that we observe the older
|
||||
* value and register a disjoint event
|
||||
* when we try soft-stopping */
|
||||
* context has already finished, so
|
||||
* it's not too bad that we observe the
|
||||
* older value and register a disjoint
|
||||
* event when we try soft-stopping
|
||||
*/
|
||||
if (js_devdata->nr_user_contexts_running
|
||||
>= disjoint_threshold)
|
||||
softstop_flags |=
|
||||
@@ -253,9 +255,9 @@ static enum hrtimer_restart timer_callback(struct hrtimer *timer)
|
||||
}
|
||||
}
|
||||
if (reset_needed) {
|
||||
dev_err(kbdev->dev, "JS: Job has been on the GPU for too long (JS_RESET_TICKS_SS/DUMPING timeout hit). Issueing GPU soft-reset to resolve.");
|
||||
dev_err(kbdev->dev, "JS: Job has been on the GPU for too long (JS_RESET_TICKS_SS/DUMPING timeout hit). Issuing GPU soft-reset to resolve.");
|
||||
|
||||
if (kbase_prepare_to_reset_gpu_locked(kbdev))
|
||||
if (kbase_prepare_to_reset_gpu_locked(kbdev, RESET_FLAGS_NONE))
|
||||
kbase_reset_gpu_locked(kbdev);
|
||||
}
|
||||
/* the timer is re-issued if there is contexts in the run-pool */
|
||||
@@ -287,11 +289,12 @@ void kbase_backend_ctx_count_changed(struct kbase_device *kbdev)
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
backend->timer_running = false;
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
/* From now on, return value of timer_callback_should_run() will
|
||||
* also cause the timer to not requeue itself. Its return value
|
||||
* cannot change, because it depends on variables updated with
|
||||
* the runpool_mutex held, which the caller of this must also
|
||||
* hold */
|
||||
/* From now on, return value of timer_callback_should_run()
|
||||
* will also cause the timer to not requeue itself. Its return
|
||||
* value cannot change, because it depends on variables updated
|
||||
* with the runpool_mutex held, which the caller of this must
|
||||
* also hold
|
||||
*/
|
||||
hrtimer_cancel(&backend->scheduling_timer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2015 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2015, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Register-based HW access backend specific job scheduler APIs
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* (C) COPYRIGHT 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2015, 2018-2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2015, 2018-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* "Always on" power management policy
|
||||
*/
|
||||
@@ -62,6 +59,9 @@ const struct kbase_pm_policy kbase_pm_always_on_policy_ops = {
|
||||
always_on_shaders_needed, /* shaders_needed */
|
||||
always_on_get_core_active, /* get_core_active */
|
||||
KBASE_PM_POLICY_ID_ALWAYS_ON, /* id */
|
||||
#if MALI_USE_CSF
|
||||
ALWAYS_ON_PM_SCHED_FLAGS, /* pm_sched_flags */
|
||||
#endif
|
||||
};
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_always_on_policy_ops);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2011-2015,2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2011-2015, 2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* "Always on" power management policy
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* GPU backend implementation of base kernel power management APIs
|
||||
*/
|
||||
@@ -156,15 +154,25 @@ int kbase_hwaccess_pm_init(struct kbase_device *kbdev)
|
||||
#endif /* CONFIG_MALI_BIFROST_DEBUG */
|
||||
init_waitqueue_head(&kbdev->pm.backend.gpu_in_desired_state_wait);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
/* Initialise the metrics subsystem */
|
||||
ret = kbasep_pm_metrics_init(kbdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
#else
|
||||
mutex_init(&kbdev->pm.backend.policy_change_lock);
|
||||
kbdev->pm.backend.policy_change_clamp_state_to_off = false;
|
||||
/* Due to dependency on kbase_ipa_control, the metrics subsystem can't
|
||||
* be initialized here.
|
||||
*/
|
||||
CSTD_UNUSED(ret);
|
||||
#endif
|
||||
|
||||
init_waitqueue_head(&kbdev->pm.backend.reset_done_wait);
|
||||
kbdev->pm.backend.reset_done = false;
|
||||
|
||||
init_waitqueue_head(&kbdev->pm.zero_active_count_wait);
|
||||
init_waitqueue_head(&kbdev->pm.resume_wait);
|
||||
kbdev->pm.active_count = 0;
|
||||
|
||||
spin_lock_init(&kbdev->pm.backend.gpu_cycle_counter_requests_lock);
|
||||
@@ -221,7 +229,9 @@ pm_state_machine_fail:
|
||||
kbase_pm_policy_term(kbdev);
|
||||
kbase_pm_ca_term(kbdev);
|
||||
workq_fail:
|
||||
#if !MALI_USE_CSF
|
||||
kbasep_pm_metrics_term(kbdev);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -230,7 +240,8 @@ void kbase_pm_do_poweron(struct kbase_device *kbdev, bool is_resume)
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
/* Turn clocks and interrupts on - no-op if we haven't done a previous
|
||||
* kbase_pm_clock_off() */
|
||||
* kbase_pm_clock_off()
|
||||
*/
|
||||
kbase_pm_clock_on(kbdev, is_resume);
|
||||
|
||||
if (!is_resume) {
|
||||
@@ -248,7 +259,8 @@ void kbase_pm_do_poweron(struct kbase_device *kbdev, bool is_resume)
|
||||
kbase_pm_update_cores_state(kbdev);
|
||||
|
||||
/* NOTE: We don't wait to reach the desired state, since running atoms
|
||||
* will wait for that state to be reached anyway */
|
||||
* will wait for that state to be reached anyway
|
||||
*/
|
||||
}
|
||||
|
||||
static void kbase_pm_gpu_poweroff_wait_wq(struct work_struct *data)
|
||||
@@ -486,7 +498,15 @@ static void kbase_pm_hwcnt_disable_worker(struct work_struct *data)
|
||||
/* PM state was updated while we were doing the disable,
|
||||
* so we need to undo the disable we just performed.
|
||||
*/
|
||||
#if MALI_USE_CSF
|
||||
unsigned long lock_flags;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &lock_flags);
|
||||
#endif
|
||||
kbase_hwcnt_context_enable(kbdev->hwcnt_gpu_ctx);
|
||||
#if MALI_USE_CSF
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, lock_flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
@@ -562,20 +582,35 @@ int kbase_hwaccess_pm_powerup(struct kbase_device *kbdev,
|
||||
KBASE_DEBUG_ASSERT(!kbase_pm_is_suspending(kbdev));
|
||||
|
||||
/* Power up the GPU, don't enable IRQs as we are not ready to receive
|
||||
* them. */
|
||||
* them
|
||||
*/
|
||||
ret = kbase_pm_init_hw(kbdev, flags);
|
||||
if (ret) {
|
||||
kbase_pm_unlock(kbdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if MALI_USE_CSF
|
||||
kbdev->pm.debug_core_mask =
|
||||
kbdev->gpu_props.props.raw_props.shader_present;
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, irq_flags);
|
||||
/* Set the initial value for 'shaders_avail'. It would be later
|
||||
* modified only from the MCU state machine, when the shader core
|
||||
* allocation enable mask request has completed. So its value would
|
||||
* indicate the mask of cores that are currently being used by FW for
|
||||
* the allocation of endpoints requested by CSGs.
|
||||
*/
|
||||
kbdev->pm.backend.shaders_avail = kbase_pm_ca_get_core_mask(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, irq_flags);
|
||||
#else
|
||||
kbdev->pm.debug_core_mask_all = kbdev->pm.debug_core_mask[0] =
|
||||
kbdev->pm.debug_core_mask[1] =
|
||||
kbdev->pm.debug_core_mask[2] =
|
||||
kbdev->gpu_props.props.raw_props.shader_present;
|
||||
#endif
|
||||
|
||||
/* Pretend the GPU is active to prevent a power policy turning the GPU
|
||||
* cores off */
|
||||
* cores off
|
||||
*/
|
||||
kbdev->pm.active_count = 1;
|
||||
|
||||
spin_lock_irqsave(&kbdev->pm.backend.gpu_cycle_counter_requests_lock,
|
||||
@@ -587,7 +622,8 @@ int kbase_hwaccess_pm_powerup(struct kbase_device *kbdev,
|
||||
irq_flags);
|
||||
|
||||
/* We are ready to receive IRQ's now as power policy is set up, so
|
||||
* enable them now. */
|
||||
* enable them now.
|
||||
*/
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
kbdev->pm.backend.driver_ready_for_irqs = true;
|
||||
#endif
|
||||
@@ -620,6 +656,8 @@ void kbase_hwaccess_pm_halt(struct kbase_device *kbdev)
|
||||
mutex_lock(&kbdev->pm.lock);
|
||||
kbase_pm_do_poweroff(kbdev);
|
||||
mutex_unlock(&kbdev->pm.lock);
|
||||
|
||||
kbase_pm_wait_for_poweroff_complete(kbdev);
|
||||
}
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_hwaccess_pm_halt);
|
||||
@@ -634,10 +672,15 @@ void kbase_hwaccess_pm_term(struct kbase_device *kbdev)
|
||||
|
||||
if (kbdev->pm.backend.hwcnt_disabled) {
|
||||
unsigned long flags;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
kbase_hwcnt_context_enable(kbdev->hwcnt_gpu_ctx);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
#else
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kbase_hwcnt_context_enable(kbdev->hwcnt_gpu_ctx);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free any resources the policy allocated */
|
||||
@@ -645,8 +688,16 @@ void kbase_hwaccess_pm_term(struct kbase_device *kbdev)
|
||||
kbase_pm_policy_term(kbdev);
|
||||
kbase_pm_ca_term(kbdev);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
/* Shut down the metrics subsystem */
|
||||
kbasep_pm_metrics_term(kbdev);
|
||||
#else
|
||||
if (WARN_ON(mutex_is_locked(&kbdev->pm.backend.policy_change_lock))) {
|
||||
mutex_lock(&kbdev->pm.backend.policy_change_lock);
|
||||
mutex_unlock(&kbdev->pm.backend.policy_change_lock);
|
||||
}
|
||||
mutex_destroy(&kbdev->pm.backend.policy_change_lock);
|
||||
#endif
|
||||
|
||||
destroy_workqueue(kbdev->pm.backend.gpu_poweroff_wait_wq);
|
||||
}
|
||||
@@ -665,6 +716,17 @@ void kbase_pm_power_changed(struct kbase_device *kbdev)
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
}
|
||||
|
||||
#if MALI_USE_CSF
|
||||
void kbase_pm_set_debug_core_mask(struct kbase_device *kbdev, u64 new_core_mask)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
kbdev->pm.debug_core_mask = new_core_mask;
|
||||
kbase_pm_update_dynamic_cores_onoff(kbdev);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_set_debug_core_mask);
|
||||
#else
|
||||
void kbase_pm_set_debug_core_mask(struct kbase_device *kbdev,
|
||||
u64 new_core_mask_js0, u64 new_core_mask_js1,
|
||||
u64 new_core_mask_js2)
|
||||
@@ -685,6 +747,7 @@ void kbase_pm_set_debug_core_mask(struct kbase_device *kbdev,
|
||||
|
||||
kbase_pm_update_dynamic_cores_onoff(kbdev);
|
||||
}
|
||||
#endif /* MALI_USE_CSF */
|
||||
|
||||
void kbase_hwaccess_pm_gpu_active(struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -700,7 +763,8 @@ void kbase_hwaccess_pm_suspend(struct kbase_device *kbdev)
|
||||
{
|
||||
/* Force power off the GPU and all cores (regardless of policy), only
|
||||
* after the PM active count reaches zero (otherwise, we risk turning it
|
||||
* off prematurely) */
|
||||
* off prematurely)
|
||||
*/
|
||||
kbase_pm_lock(kbdev);
|
||||
|
||||
kbase_pm_do_poweroff(kbdev);
|
||||
@@ -735,6 +799,7 @@ void kbase_hwaccess_pm_resume(struct kbase_device *kbdev)
|
||||
kbase_backend_timer_resume(kbdev);
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
wake_up_all(&kbdev->pm.resume_wait);
|
||||
kbase_pm_unlock(kbdev);
|
||||
}
|
||||
|
||||
@@ -745,6 +810,9 @@ void kbase_pm_handle_gpu_lost(struct kbase_device *kbdev)
|
||||
ktime_t end_timestamp = ktime_get();
|
||||
struct kbase_arbiter_vm_state *arb_vm_state = kbdev->pm.arb_vm_state;
|
||||
|
||||
if (!kbdev->arb.arb_if)
|
||||
return;
|
||||
|
||||
mutex_lock(&kbdev->pm.lock);
|
||||
mutex_lock(&arb_vm_state->vm_state_lock);
|
||||
if (kbdev->pm.backend.gpu_powered &&
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2013-2018, 2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -59,6 +58,14 @@ void kbase_devfreq_set_core_mask(struct kbase_device *kbdev, u64 core_mask)
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
if (!(core_mask & kbdev->pm.debug_core_mask)) {
|
||||
dev_err(kbdev->dev,
|
||||
"OPP core mask 0x%llX does not intersect with debug mask 0x%llX\n",
|
||||
core_mask, kbdev->pm.debug_core_mask);
|
||||
goto unlock;
|
||||
}
|
||||
#else
|
||||
if (!(core_mask & kbdev->pm.debug_core_mask_all)) {
|
||||
dev_err(kbdev->dev, "OPP core mask 0x%llX does not intersect with debug mask 0x%llX\n",
|
||||
core_mask, kbdev->pm.debug_core_mask_all);
|
||||
@@ -69,6 +76,7 @@ void kbase_devfreq_set_core_mask(struct kbase_device *kbdev, u64 core_mask)
|
||||
dev_err(kbdev->dev, "Dynamic core scaling not supported as dummy job WA is enabled");
|
||||
goto unlock;
|
||||
}
|
||||
#endif /* MALI_USE_CSF */
|
||||
|
||||
pm_backend->ca_cores_enabled = core_mask;
|
||||
|
||||
@@ -80,21 +88,32 @@ unlock:
|
||||
dev_dbg(kbdev->dev, "Devfreq policy : new core mask=%llX\n",
|
||||
pm_backend->ca_cores_enabled);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_devfreq_set_core_mask);
|
||||
#endif
|
||||
|
||||
u64 kbase_pm_ca_get_core_mask(struct kbase_device *kbdev)
|
||||
{
|
||||
#ifdef CONFIG_MALI_BIFROST_DEVFREQ
|
||||
struct kbase_pm_backend_data *pm_backend = &kbdev->pm.backend;
|
||||
#if MALI_USE_CSF
|
||||
u64 debug_core_mask = kbdev->pm.debug_core_mask;
|
||||
#else
|
||||
u64 debug_core_mask = kbdev->pm.debug_core_mask_all;
|
||||
#endif
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DEVFREQ
|
||||
return pm_backend->ca_cores_enabled & kbdev->pm.debug_core_mask_all;
|
||||
/*
|
||||
* Although in the init we let the pm_backend->ca_cores_enabled to be
|
||||
* the max config (it uses the base_gpu_props), at this function we need
|
||||
* to limit it to be a subgroup of the curr config, otherwise the
|
||||
* shaders state machine on the PM does not evolve.
|
||||
*/
|
||||
return kbdev->gpu_props.curr_config.shader_present &
|
||||
kbdev->pm.backend.ca_cores_enabled &
|
||||
debug_core_mask;
|
||||
#else
|
||||
return kbdev->gpu_props.props.raw_props.shader_present &
|
||||
kbdev->pm.debug_core_mask_all;
|
||||
return kbdev->gpu_props.curr_config.shader_present &
|
||||
debug_core_mask;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2011-2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2011-2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2017 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2017, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2012-2016, 2018-2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2012-2016, 2018-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* "Coarse Demand" power management policy
|
||||
*/
|
||||
@@ -61,6 +58,9 @@ const struct kbase_pm_policy kbase_pm_coarse_demand_policy_ops = {
|
||||
coarse_demand_shaders_needed, /* shaders_needed */
|
||||
coarse_demand_get_core_active, /* get_core_active */
|
||||
KBASE_PM_POLICY_ID_COARSE_DEMAND, /* id */
|
||||
#if MALI_USE_CSF
|
||||
COARSE_ON_DEMAND_PM_SCHED_FLAGS, /* pm_sched_flags */
|
||||
#endif
|
||||
};
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_coarse_demand_policy_ops);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2012-2015,2018 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2012-2015, 2018, 2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* "Coarse Demand" power management policy
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -62,24 +61,9 @@ enum kbase_pm_core_type {
|
||||
KBASE_PM_CORE_STACK = STACK_PRESENT_LO
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* enum kbase_l2_core_state - The states used for the L2 cache & tiler power
|
||||
* state machine.
|
||||
*
|
||||
* @KBASE_L2_OFF: The L2 cache and tiler are off
|
||||
* @KBASE_L2_PEND_ON: The L2 cache and tiler are powering on
|
||||
* @KBASE_L2_RESTORE_CLOCKS: The GPU clock is restored. Conditionally used.
|
||||
* @KBASE_L2_ON_HWCNT_ENABLE: The L2 cache and tiler are on, and hwcnt is being
|
||||
* enabled
|
||||
* @KBASE_L2_ON: The L2 cache and tiler are on, and hwcnt is enabled
|
||||
* @KBASE_L2_ON_HWCNT_DISABLE: The L2 cache and tiler are on, and hwcnt is being
|
||||
* disabled
|
||||
* @KBASE_L2_SLOW_DOWN_CLOCKS: The GPU clock is set to appropriate or lowest
|
||||
* clock. Conditionally used.
|
||||
* @KBASE_L2_POWER_DOWN: The L2 cache and tiler are about to be powered off
|
||||
* @KBASE_L2_PEND_OFF: The L2 cache and tiler are powering off
|
||||
* @KBASE_L2_RESET_WAIT: The GPU is resetting, L2 cache and tiler power state
|
||||
* are unknown
|
||||
*/
|
||||
enum kbase_l2_core_state {
|
||||
#define KBASEP_L2_STATE(n) KBASE_L2_ ## n,
|
||||
@@ -88,24 +72,8 @@ enum kbase_l2_core_state {
|
||||
};
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/**
|
||||
/*
|
||||
* enum kbase_mcu_state - The states used for the MCU state machine.
|
||||
*
|
||||
* @KBASE_MCU_OFF: The MCU is powered off.
|
||||
* @KBASE_MCU_PEND_ON_RELOAD: The warm boot of MCU or cold boot of MCU (with
|
||||
* firmware reloading) is in progress.
|
||||
* @KBASE_MCU_ON_GLB_REINIT_PEND: The MCU is enabled and Global configuration
|
||||
* requests have been sent to the firmware.
|
||||
* @KBASE_MCU_ON_HWCNT_ENABLE: The Global requests have completed and MCU is
|
||||
* now ready for use and hwcnt is being enabled.
|
||||
* @KBASE_MCU_ON: The MCU is active and hwcnt has been enabled.
|
||||
* @KBASE_MCU_ON_HWCNT_DISABLE: The MCU is on and hwcnt is being disabled.
|
||||
* @KBASE_MCU_ON_HALT: The MCU is on and hwcnt has been disabled,
|
||||
* MCU halt would be triggered.
|
||||
* @KBASE_MCU_ON_PEND_HALT: MCU halt in progress, confirmation pending.
|
||||
* @KBASE_MCU_POWER_DOWN: MCU halted operations, pending being disabled.
|
||||
* @KBASE_MCU_PEND_OFF: MCU is being disabled, pending on powering off.
|
||||
* @KBASE_MCU_RESET_WAIT: The GPU is resetting, MCU state is unknown.
|
||||
*/
|
||||
enum kbase_mcu_state {
|
||||
#define KBASEP_MCU_STATE(n) KBASE_MCU_ ## n,
|
||||
@@ -114,45 +82,8 @@ enum kbase_mcu_state {
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
/*
|
||||
* enum kbase_shader_core_state - The states used for the shaders' state machine.
|
||||
*
|
||||
* @KBASE_SHADERS_OFF_CORESTACK_OFF: The shaders and core stacks are off
|
||||
* @KBASE_SHADERS_OFF_CORESTACK_PEND_ON: The shaders are off, core stacks have
|
||||
* been requested to power on and hwcnt
|
||||
* is being disabled
|
||||
* @KBASE_SHADERS_PEND_ON_CORESTACK_ON: Core stacks are on, shaders have been
|
||||
* requested to power on. Or after doing
|
||||
* partial shader on/off, checking whether
|
||||
* it's the desired state.
|
||||
* @KBASE_SHADERS_ON_CORESTACK_ON: The shaders and core stacks are on, and hwcnt
|
||||
* already enabled.
|
||||
* @KBASE_SHADERS_ON_CORESTACK_ON_RECHECK: The shaders and core stacks
|
||||
* are on, hwcnt disabled, and checks
|
||||
* to powering down or re-enabling
|
||||
* hwcnt.
|
||||
* @KBASE_SHADERS_WAIT_OFF_CORESTACK_ON: The shaders have been requested to
|
||||
* power off, but they remain on for the
|
||||
* duration of the hysteresis timer
|
||||
* @KBASE_SHADERS_WAIT_GPU_IDLE: The shaders partial poweroff needs to reach
|
||||
* a state where jobs on the GPU are finished
|
||||
* including jobs currently running and in the
|
||||
* GPU queue because of GPU2017-861
|
||||
* @KBASE_SHADERS_WAIT_FINISHED_CORESTACK_ON: The hysteresis timer has expired
|
||||
* @KBASE_SHADERS_L2_FLUSHING_CORESTACK_ON: The core stacks are on and the
|
||||
* level 2 cache is being flushed.
|
||||
* @KBASE_SHADERS_READY_OFF_CORESTACK_ON: The core stacks are on and the shaders
|
||||
* are ready to be powered off.
|
||||
* @KBASE_SHADERS_PEND_OFF_CORESTACK_ON: The core stacks are on, and the shaders
|
||||
* have been requested to power off
|
||||
* @KBASE_SHADERS_OFF_CORESTACK_PEND_OFF: The shaders are off, and the core stacks
|
||||
* have been requested to power off
|
||||
* @KBASE_SHADERS_OFF_CORESTACK_OFF_TIMER_PEND_OFF: Shaders and corestacks are
|
||||
* off, but the tick timer
|
||||
* cancellation is still
|
||||
* pending.
|
||||
* @KBASE_SHADERS_RESET_WAIT: The GPU is resetting, shader and core stack power
|
||||
* states are unknown
|
||||
*/
|
||||
enum kbase_shader_core_state {
|
||||
#define KBASEP_SHADER_STATE(n) KBASE_SHADERS_ ## n,
|
||||
@@ -164,28 +95,40 @@ enum kbase_shader_core_state {
|
||||
* struct kbasep_pm_metrics - Metrics data collected for use by the power
|
||||
* management framework.
|
||||
*
|
||||
* @time_busy: number of ns the GPU was busy executing jobs since the
|
||||
* @time_period_start timestamp.
|
||||
* @time_idle: number of ns since time_period_start the GPU was not executing
|
||||
* jobs since the @time_period_start timestamp.
|
||||
* @busy_cl: number of ns the GPU was busy executing CL jobs. Note that
|
||||
* if two CL jobs were active for 400ns, this value would be updated
|
||||
* with 800.
|
||||
* @busy_gl: number of ns the GPU was busy executing GL jobs. Note that
|
||||
* if two GL jobs were active for 400ns, this value would be updated
|
||||
* with 800.
|
||||
* @time_busy: the amount of time the GPU was busy executing jobs since the
|
||||
* @time_period_start timestamp, in units of 256ns. This also includes
|
||||
* time_in_protm, the time spent in protected mode, since it's assumed
|
||||
* the GPU was busy 100% during this period.
|
||||
* @time_idle: the amount of time the GPU was not executing jobs since the
|
||||
* time_period_start timestamp, measured in units of 256ns.
|
||||
* @time_in_protm: The amount of time the GPU has spent in protected mode since
|
||||
* the time_period_start timestamp, measured in units of 256ns.
|
||||
* @busy_cl: the amount of time the GPU was busy executing CL jobs. Note that
|
||||
* if two CL jobs were active for 256ns, this value would be updated
|
||||
* with 2 (2x256ns).
|
||||
* @busy_gl: the amount of time the GPU was busy executing GL jobs. Note that
|
||||
* if two GL jobs were active for 256ns, this value would be updated
|
||||
* with 2 (2x256ns).
|
||||
*/
|
||||
struct kbasep_pm_metrics {
|
||||
u32 time_busy;
|
||||
u32 time_idle;
|
||||
#if MALI_USE_CSF
|
||||
u32 time_in_protm;
|
||||
#else
|
||||
u32 busy_cl[2];
|
||||
u32 busy_gl;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbasep_pm_metrics_state - State required to collect the metrics in
|
||||
* struct kbasep_pm_metrics
|
||||
* @time_period_start: time at which busy/idle measurements started
|
||||
* @ipa_control_client: Handle returned on registering DVFS as a
|
||||
* kbase_ipa_control client
|
||||
* @skip_gpu_active_sanity_check: Decide whether to skip GPU_ACTIVE sanity
|
||||
* check in DVFS utilisation calculation
|
||||
* @gpu_active: true when the GPU is executing jobs. false when
|
||||
* not. Updated when the job scheduler informs us a job in submitted
|
||||
* or removed from a GPU slot.
|
||||
@@ -197,6 +140,7 @@ struct kbasep_pm_metrics {
|
||||
* @values: The current values of the power management metrics. The
|
||||
* kbase_pm_get_dvfs_metrics() function is used to compare these
|
||||
* current values with the saved values from a previous invocation.
|
||||
* @initialized: tracks whether metrics_state has been initialized or not.
|
||||
* @timer: timer to regularly make DVFS decisions based on the power
|
||||
* management metrics.
|
||||
* @timer_active: boolean indicating @timer is running
|
||||
@@ -205,9 +149,14 @@ struct kbasep_pm_metrics {
|
||||
*/
|
||||
struct kbasep_pm_metrics_state {
|
||||
ktime_t time_period_start;
|
||||
#if MALI_USE_CSF
|
||||
void *ipa_control_client;
|
||||
bool skip_gpu_active_sanity_check;
|
||||
#else
|
||||
bool gpu_active;
|
||||
u32 active_cl_ctx[2];
|
||||
u32 active_gl_ctx[3];
|
||||
#endif
|
||||
spinlock_t lock;
|
||||
|
||||
void *platform_data;
|
||||
@@ -216,6 +165,7 @@ struct kbasep_pm_metrics_state {
|
||||
struct kbasep_pm_metrics values;
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DVFS
|
||||
bool initialized;
|
||||
struct hrtimer timer;
|
||||
bool timer_active;
|
||||
struct kbasep_pm_metrics dvfs_last;
|
||||
@@ -326,6 +276,8 @@ union kbase_pm_policy_data {
|
||||
* @callback_soft_reset: Optional callback to software reset the GPU. See
|
||||
* &struct kbase_pm_callback_conf
|
||||
* @ca_cores_enabled: Cores that are currently available
|
||||
* @mcu_state: The current state of the micro-control unit, only applicable
|
||||
* to GPUs that have such a component
|
||||
* @l2_state: The current state of the L2 cache state machine. See
|
||||
* &enum kbase_l2_core_state
|
||||
* @l2_desired: True if the L2 cache should be powered on by the L2 cache state
|
||||
@@ -335,10 +287,10 @@ union kbase_pm_policy_data {
|
||||
* @shaders_avail: This is updated by the state machine when it is in a state
|
||||
* where it can write to the SHADER_PWRON or PWROFF registers
|
||||
* to have the same set of available cores as specified by
|
||||
* @shaders_desired_mask. So it would eventually have the same
|
||||
* value as @shaders_desired_mask and would precisely indicate
|
||||
* the cores that are currently available. This is internal to
|
||||
* shader state machine and should *not* be modified elsewhere.
|
||||
* @shaders_desired_mask. So would precisely indicate the cores
|
||||
* that are currently available. This is internal to shader
|
||||
* state machine of JM GPUs and should *not* be modified
|
||||
* elsewhere.
|
||||
* @shaders_desired_mask: This is updated by the state machine when it is in
|
||||
* a state where it can handle changes to the core
|
||||
* availability (either by DVFS or sysfs). This is
|
||||
@@ -350,6 +302,16 @@ union kbase_pm_policy_data {
|
||||
* cores may be different, but there should be transitions in
|
||||
* progress that will eventually achieve this state (assuming
|
||||
* that the policy doesn't change its mind in the mean time).
|
||||
* @mcu_desired: True if the micro-control unit should be powered on
|
||||
* @policy_change_clamp_state_to_off: Signaling the backend is in PM policy
|
||||
* change transition, needs the mcu/L2 to be brought back to the
|
||||
* off state and remain in that state until the flag is cleared.
|
||||
* @csf_pm_sched_flags: CSF Dynamic PM control flags in accordance to the
|
||||
* current active PM policy. This field is updated whenever a
|
||||
* new policy is activated.
|
||||
* @policy_change_lock: Used to serialize the policy change calls. In CSF case,
|
||||
* the change of policy may involve the scheduler to
|
||||
* suspend running CSGs and then reconfigure the MCU.
|
||||
* @in_reset: True if a GPU is resetting and normal power manager operation is
|
||||
* suspended
|
||||
* @partial_shaderoff: True if we want to partial power off shader cores,
|
||||
@@ -440,9 +402,6 @@ struct kbase_pm_backend_data {
|
||||
u64 ca_cores_enabled;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* The current state of the micro-control unit, only applicable
|
||||
* to GPUs that has such a component
|
||||
*/
|
||||
enum kbase_mcu_state mcu_state;
|
||||
#endif
|
||||
enum kbase_l2_core_state l2_state;
|
||||
@@ -450,8 +409,10 @@ struct kbase_pm_backend_data {
|
||||
u64 shaders_avail;
|
||||
u64 shaders_desired_mask;
|
||||
#if MALI_USE_CSF
|
||||
/* True if the micro-control unit should be powered on */
|
||||
bool mcu_desired;
|
||||
bool policy_change_clamp_state_to_off;
|
||||
unsigned int csf_pm_sched_flags;
|
||||
struct mutex policy_change_lock;
|
||||
#endif
|
||||
bool l2_desired;
|
||||
bool l2_always_on;
|
||||
@@ -476,6 +437,23 @@ struct kbase_pm_backend_data {
|
||||
struct work_struct gpu_clock_control_work;
|
||||
};
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* CSF PM flag, signaling that the MCU CORE should be kept on */
|
||||
#define CSF_DYNAMIC_PM_CORE_KEEP_ON (1 << 0)
|
||||
/* CSF PM flag, signaling no scheduler suspension on idle groups */
|
||||
#define CSF_DYNAMIC_PM_SCHED_IGNORE_IDLE (1 << 1)
|
||||
/* CSF PM flag, signaling no scheduler suspension on no runnable groups */
|
||||
#define CSF_DYNAMIC_PM_SCHED_NO_SUSPEND (1 << 2)
|
||||
|
||||
/* The following flags corresponds to existing defined PM policies */
|
||||
#define ALWAYS_ON_PM_SCHED_FLAGS (CSF_DYNAMIC_PM_CORE_KEEP_ON | \
|
||||
CSF_DYNAMIC_PM_SCHED_IGNORE_IDLE | \
|
||||
CSF_DYNAMIC_PM_SCHED_NO_SUSPEND)
|
||||
#define COARSE_ON_DEMAND_PM_SCHED_FLAGS (0)
|
||||
#if !MALI_CUSTOMER_RELEASE
|
||||
#define ALWAYS_ON_DEMAND_PM_SCHED_FLAGS (CSF_DYNAMIC_PM_SCHED_IGNORE_IDLE)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* List of policy IDs */
|
||||
enum kbase_pm_policy_id {
|
||||
@@ -502,11 +480,15 @@ enum kbase_pm_policy_id {
|
||||
* necessarily the same as its index in the list returned
|
||||
* by kbase_pm_list_policies().
|
||||
* It is used purely for debugging.
|
||||
* @pm_sched_flags: Policy associated with CSF PM scheduling operational flags.
|
||||
* Pre-defined required flags exist for each of the
|
||||
* ARM released policies, such as 'always_on', 'coarse_demand'
|
||||
* and etc.
|
||||
*/
|
||||
struct kbase_pm_policy {
|
||||
char *name;
|
||||
|
||||
/**
|
||||
/*
|
||||
* Function called when the policy is selected
|
||||
*
|
||||
* This should initialize the kbdev->pm.pm_policy_data structure. It
|
||||
@@ -520,7 +502,7 @@ struct kbase_pm_policy {
|
||||
*/
|
||||
void (*init)(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Function called when the policy is unselected.
|
||||
*
|
||||
* @kbdev: The kbase device structure for the device (must be a
|
||||
@@ -528,7 +510,7 @@ struct kbase_pm_policy {
|
||||
*/
|
||||
void (*term)(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Function called to find out if shader cores are needed
|
||||
*
|
||||
* This needs to at least satisfy kbdev->pm.backend.shaders_desired,
|
||||
@@ -541,7 +523,7 @@ struct kbase_pm_policy {
|
||||
*/
|
||||
bool (*shaders_needed)(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Function called to get the current overall GPU power state
|
||||
*
|
||||
* This function must meet or exceed the requirements for power
|
||||
@@ -555,6 +537,15 @@ struct kbase_pm_policy {
|
||||
bool (*get_core_active)(struct kbase_device *kbdev);
|
||||
|
||||
enum kbase_pm_policy_id id;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* Policy associated with CSF PM scheduling operational flags.
|
||||
* There are pre-defined required flags exist for each of the
|
||||
* ARM released policies, such as 'always_on', 'coarse_demand'
|
||||
* and etc.
|
||||
*/
|
||||
unsigned int pm_sched_flags;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _KBASE_PM_HWACCESS_DEFS_H_ */
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -32,9 +30,13 @@
|
||||
#include <mali_kbase_pm.h>
|
||||
#include <mali_kbase_config_defaults.h>
|
||||
#include <mali_kbase_smc.h>
|
||||
#if !MALI_USE_CSF
|
||||
|
||||
#if MALI_USE_CSF
|
||||
#include <csf/ipa_control/mali_kbase_csf_ipa_control.h>
|
||||
#else
|
||||
#include <mali_kbase_hwaccess_jm.h>
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
#include <mali_kbase_reset_gpu.h>
|
||||
#include <mali_kbase_ctx_sched.h>
|
||||
#include <mali_kbase_hwcnt_context.h>
|
||||
@@ -47,6 +49,9 @@
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
#include <arbiter/mali_kbase_arbiter_pm.h>
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
#if MALI_USE_CSF
|
||||
#include <csf/ipa_control/mali_kbase_csf_ipa_control.h>
|
||||
#endif
|
||||
|
||||
#include <linux/of.h>
|
||||
|
||||
@@ -103,13 +108,13 @@ bool kbase_pm_is_mcu_desired(struct kbase_device *kbdev)
|
||||
return true;
|
||||
|
||||
/* MCU is supposed to be ON, only when scheduler.pm_active_count is
|
||||
* non zero. But for always_on policy also MCU needs to be ON.
|
||||
* GPUCORE-24926 will add the proper handling for always_on
|
||||
* power policy.
|
||||
* non zero. But for always_on policy, the MCU needs to be kept on,
|
||||
* unless policy changing transition needs it off.
|
||||
*/
|
||||
|
||||
return (kbdev->pm.backend.mcu_desired &&
|
||||
(kbdev->pm.backend.pm_current_policy ==
|
||||
&kbase_pm_always_on_policy_ops));
|
||||
kbase_pm_no_mcu_core_pwroff(kbdev) &&
|
||||
!kbdev->pm.backend.policy_change_clamp_state_to_off);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -126,6 +131,11 @@ bool kbase_pm_is_l2_desired(struct kbase_device *kbdev)
|
||||
!kbdev->pm.backend.shaders_desired)
|
||||
return false;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
if (kbdev->pm.backend.policy_change_clamp_state_to_off)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return kbdev->pm.backend.l2_desired;
|
||||
}
|
||||
|
||||
@@ -257,7 +267,8 @@ static void mali_cci_flush_l2(struct kbase_device *kbdev)
|
||||
GPU_CONTROL_REG(GPU_IRQ_RAWSTAT));
|
||||
|
||||
/* Wait for cache flush to complete before continuing, exit on
|
||||
* gpu resets or loop expiry. */
|
||||
* gpu resets or loop expiry.
|
||||
*/
|
||||
while (((raw & mask) == 0) && --loops) {
|
||||
raw = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(GPU_IRQ_RAWSTAT));
|
||||
@@ -396,9 +407,9 @@ u64 kbase_pm_get_present_cores(struct kbase_device *kbdev,
|
||||
|
||||
switch (type) {
|
||||
case KBASE_PM_CORE_L2:
|
||||
return kbdev->gpu_props.props.raw_props.l2_present;
|
||||
return kbdev->gpu_props.curr_config.l2_present;
|
||||
case KBASE_PM_CORE_SHADER:
|
||||
return kbdev->gpu_props.props.raw_props.shader_present;
|
||||
return kbdev->gpu_props.curr_config.shader_present;
|
||||
case KBASE_PM_CORE_TILER:
|
||||
return kbdev->gpu_props.props.raw_props.tiler_present;
|
||||
case KBASE_PM_CORE_STACK:
|
||||
@@ -492,14 +503,10 @@ static void kbase_pm_trigger_hwcnt_disable(struct kbase_device *kbdev)
|
||||
*/
|
||||
if (kbase_hwcnt_context_disable_atomic(kbdev->hwcnt_gpu_ctx)) {
|
||||
backend->hwcnt_disabled = true;
|
||||
|
||||
} else {
|
||||
#if KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE
|
||||
queue_work(system_wq,
|
||||
&backend->hwcnt_disable_work);
|
||||
#else
|
||||
queue_work(system_highpri_wq,
|
||||
&backend->hwcnt_disable_work);
|
||||
#endif
|
||||
kbase_hwcnt_context_queue_work(kbdev->hwcnt_gpu_ctx,
|
||||
&backend->hwcnt_disable_work);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +524,8 @@ static void kbase_pm_l2_config_override(struct kbase_device *kbdev)
|
||||
* Skip if size and hash are not given explicitly,
|
||||
* which means default values are used.
|
||||
*/
|
||||
if ((kbdev->l2_size_override == 0) && (kbdev->l2_hash_override == 0))
|
||||
if ((kbdev->l2_size_override == 0) && (kbdev->l2_hash_override == 0) &&
|
||||
(!kbdev->l2_hash_values_override))
|
||||
return;
|
||||
|
||||
val = kbase_reg_read(kbdev, GPU_CONTROL_REG(L2_CONFIG));
|
||||
@@ -528,13 +536,25 @@ static void kbase_pm_l2_config_override(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
if (kbdev->l2_hash_override) {
|
||||
WARN_ON(kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_ASN_HASH));
|
||||
val &= ~L2_CONFIG_HASH_MASK;
|
||||
val |= (kbdev->l2_hash_override << L2_CONFIG_HASH_SHIFT);
|
||||
} else if (kbdev->l2_hash_values_override) {
|
||||
int i;
|
||||
|
||||
WARN_ON(!kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_ASN_HASH));
|
||||
val &= ~L2_CONFIG_ASN_HASH_ENABLE_MASK;
|
||||
val |= (0x1 << L2_CONFIG_ASN_HASH_ENABLE_SHIFT);
|
||||
|
||||
for (i = 0; i < ASN_HASH_COUNT; i++) {
|
||||
dev_dbg(kbdev->dev, "Program 0x%x to ASN_HASH[%d]\n",
|
||||
kbdev->l2_hash_values[i], i);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(ASN_HASH(i)),
|
||||
kbdev->l2_hash_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(kbdev->dev, "Program 0x%x to L2_CONFIG\n", val);
|
||||
|
||||
/* Write L2_CONFIG to override */
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(L2_CONFIG), val);
|
||||
}
|
||||
|
||||
@@ -561,6 +581,35 @@ static const char *kbase_mcu_state_to_string(enum kbase_mcu_state state)
|
||||
return strings[state];
|
||||
}
|
||||
|
||||
static inline bool kbase_pm_handle_mcu_core_attr_update(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_pm_backend_data *backend = &kbdev->pm.backend;
|
||||
bool timer_update;
|
||||
bool core_mask_update;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
WARN_ON(backend->mcu_state != KBASE_MCU_ON);
|
||||
|
||||
/* This function is only for cases where the MCU managing Cores, if
|
||||
* the firmware mode is with host control, do nothing here.
|
||||
*/
|
||||
if (unlikely(kbdev->csf.firmware_hctl_core_pwr))
|
||||
return false;
|
||||
|
||||
core_mask_update =
|
||||
backend->shaders_avail != backend->shaders_desired_mask;
|
||||
|
||||
timer_update = kbdev->csf.mcu_core_pwroff_dur_count !=
|
||||
kbdev->csf.mcu_core_pwroff_reg_shadow;
|
||||
|
||||
if (core_mask_update || timer_update)
|
||||
kbase_csf_firmware_update_core_attr(kbdev, timer_update,
|
||||
core_mask_update, backend->shaders_desired_mask);
|
||||
|
||||
return (core_mask_update || timer_update);
|
||||
}
|
||||
|
||||
static int kbase_pm_mcu_update_state(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_pm_backend_data *backend = &kbdev->pm.backend;
|
||||
@@ -578,11 +627,20 @@ static int kbase_pm_mcu_update_state(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
do {
|
||||
u64 shaders_trans = kbase_pm_get_trans_cores(kbdev, KBASE_PM_CORE_SHADER);
|
||||
u64 shaders_ready = kbase_pm_get_ready_cores(kbdev, KBASE_PM_CORE_SHADER);
|
||||
|
||||
/* mask off ready from trans in case transitions finished
|
||||
* between the register reads
|
||||
*/
|
||||
shaders_trans &= ~shaders_ready;
|
||||
|
||||
prev_state = backend->mcu_state;
|
||||
|
||||
switch (backend->mcu_state) {
|
||||
case KBASE_MCU_OFF:
|
||||
if (kbase_pm_is_mcu_desired(kbdev) &&
|
||||
!backend->policy_change_clamp_state_to_off &&
|
||||
backend->l2_state == KBASE_L2_ON) {
|
||||
kbase_csf_firmware_trigger_reload(kbdev);
|
||||
backend->mcu_state = KBASE_MCU_PEND_ON_RELOAD;
|
||||
@@ -591,35 +649,116 @@ static int kbase_pm_mcu_update_state(struct kbase_device *kbdev)
|
||||
|
||||
case KBASE_MCU_PEND_ON_RELOAD:
|
||||
if (kbdev->csf.firmware_reloaded) {
|
||||
kbase_csf_firmware_global_reinit(kbdev);
|
||||
backend->shaders_desired_mask =
|
||||
kbase_pm_ca_get_core_mask(kbdev);
|
||||
kbase_csf_firmware_global_reinit(kbdev,
|
||||
backend->shaders_desired_mask);
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_ON_GLB_REINIT_PEND;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON_GLB_REINIT_PEND:
|
||||
if (kbase_csf_firmware_global_reinit_complete(kbdev))
|
||||
if (kbase_csf_firmware_global_reinit_complete(kbdev)) {
|
||||
backend->shaders_avail =
|
||||
backend->shaders_desired_mask;
|
||||
backend->pm_shaders_core_mask = 0;
|
||||
if (kbdev->csf.firmware_hctl_core_pwr) {
|
||||
kbase_pm_invoke(kbdev, KBASE_PM_CORE_SHADER,
|
||||
backend->shaders_avail, ACTION_PWRON);
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_SHADERS_PEND_ON;
|
||||
} else
|
||||
backend->mcu_state = KBASE_MCU_ON_HWCNT_ENABLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_HCTL_SHADERS_PEND_ON:
|
||||
if (!shaders_trans &&
|
||||
shaders_ready == backend->shaders_avail) {
|
||||
/* Cores now stable, notify MCU the stable mask */
|
||||
kbase_csf_firmware_update_core_attr(kbdev,
|
||||
false, true, shaders_ready);
|
||||
|
||||
backend->pm_shaders_core_mask = shaders_ready;
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_CORES_NOTIFY_PEND;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_HCTL_CORES_NOTIFY_PEND:
|
||||
/* Wait for the acknowledgement */
|
||||
if (kbase_csf_firmware_core_attr_updated(kbdev))
|
||||
backend->mcu_state = KBASE_MCU_ON_HWCNT_ENABLE;
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON_HWCNT_ENABLE:
|
||||
backend->hwcnt_desired = true;
|
||||
if (backend->hwcnt_disabled) {
|
||||
unsigned long flags;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
kbase_hwcnt_context_enable(
|
||||
kbdev->hwcnt_gpu_ctx);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
backend->hwcnt_disabled = false;
|
||||
}
|
||||
backend->mcu_state = KBASE_MCU_ON;
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON:
|
||||
backend->shaders_desired_mask = kbase_pm_ca_get_core_mask(kbdev);
|
||||
|
||||
if (!kbase_pm_is_mcu_desired(kbdev))
|
||||
backend->mcu_state = KBASE_MCU_ON_HWCNT_DISABLE;
|
||||
else if (kbdev->csf.firmware_hctl_core_pwr) {
|
||||
/* Host control add additional Cores to be active */
|
||||
if (backend->shaders_desired_mask & ~shaders_ready) {
|
||||
backend->hwcnt_desired = false;
|
||||
if (!backend->hwcnt_disabled)
|
||||
kbase_pm_trigger_hwcnt_disable(kbdev);
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_MCU_ON_RECHECK;
|
||||
}
|
||||
} else if (kbase_pm_handle_mcu_core_attr_update(kbdev))
|
||||
kbdev->pm.backend.mcu_state =
|
||||
KBASE_MCU_ON_CORE_ATTR_UPDATE_PEND;
|
||||
break;
|
||||
|
||||
/* ToDo. Add new state(s) if shader cores mask change for DVFS
|
||||
* has to be accommodated in the MCU state machine.
|
||||
*/
|
||||
case KBASE_MCU_HCTL_MCU_ON_RECHECK:
|
||||
backend->shaders_desired_mask = kbase_pm_ca_get_core_mask(kbdev);
|
||||
|
||||
if (!backend->hwcnt_disabled) {
|
||||
/* Wait for being disabled */
|
||||
;
|
||||
} else if (!kbase_pm_is_mcu_desired(kbdev)) {
|
||||
/* Converging to MCU powering down flow */
|
||||
backend->mcu_state = KBASE_MCU_ON_HWCNT_DISABLE;
|
||||
} else if (backend->shaders_desired_mask & ~shaders_ready) {
|
||||
/* set cores ready but not available to
|
||||
* meet SHADERS_PEND_ON check pass
|
||||
*/
|
||||
backend->shaders_avail =
|
||||
(backend->shaders_desired_mask | shaders_ready);
|
||||
|
||||
kbase_pm_invoke(kbdev, KBASE_PM_CORE_SHADER,
|
||||
backend->shaders_avail & ~shaders_ready,
|
||||
ACTION_PWRON);
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_SHADERS_PEND_ON;
|
||||
} else {
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_SHADERS_PEND_ON;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON_CORE_ATTR_UPDATE_PEND:
|
||||
if (kbase_csf_firmware_core_attr_updated(kbdev)) {
|
||||
backend->shaders_avail =
|
||||
backend->shaders_desired_mask;
|
||||
backend->mcu_state = KBASE_MCU_ON;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON_HWCNT_DISABLE:
|
||||
if (kbase_pm_is_mcu_desired(kbdev)) {
|
||||
@@ -639,14 +778,32 @@ static int kbase_pm_mcu_update_state(struct kbase_device *kbdev)
|
||||
if (!kbase_pm_is_mcu_desired(kbdev)) {
|
||||
kbase_csf_firmware_trigger_mcu_halt(kbdev);
|
||||
backend->mcu_state = KBASE_MCU_ON_PEND_HALT;
|
||||
} else if (kbase_pm_is_mcu_desired(kbdev)) {
|
||||
} else
|
||||
backend->mcu_state = KBASE_MCU_ON_HWCNT_ENABLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_ON_PEND_HALT:
|
||||
if (kbase_csf_firmware_mcu_halted(kbdev))
|
||||
if (kbase_csf_firmware_mcu_halted(kbdev)) {
|
||||
if (kbdev->csf.firmware_hctl_core_pwr)
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_SHADERS_READY_OFF;
|
||||
else
|
||||
backend->mcu_state = KBASE_MCU_POWER_DOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_HCTL_SHADERS_READY_OFF:
|
||||
kbase_pm_invoke(kbdev, KBASE_PM_CORE_SHADER,
|
||||
shaders_ready, ACTION_PWROFF);
|
||||
backend->mcu_state =
|
||||
KBASE_MCU_HCTL_SHADERS_PEND_OFF;
|
||||
break;
|
||||
|
||||
case KBASE_MCU_HCTL_SHADERS_PEND_OFF:
|
||||
if (!shaders_trans && !shaders_ready) {
|
||||
backend->pm_shaders_core_mask = 0;
|
||||
backend->mcu_state = KBASE_MCU_POWER_DOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_MCU_POWER_DOWN:
|
||||
@@ -698,8 +855,10 @@ static const char *kbase_l2_core_state_to_string(enum kbase_l2_core_state state)
|
||||
static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_pm_backend_data *backend = &kbdev->pm.backend;
|
||||
u64 l2_present = kbdev->gpu_props.props.raw_props.l2_present;
|
||||
u64 l2_present = kbdev->gpu_props.curr_config.l2_present;
|
||||
#if !MALI_USE_CSF
|
||||
u64 tiler_present = kbdev->gpu_props.props.raw_props.tiler_present;
|
||||
#endif
|
||||
enum kbase_l2_core_state prev_state;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
@@ -710,10 +869,13 @@ static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
KBASE_PM_CORE_L2);
|
||||
u64 l2_ready = kbase_pm_get_ready_cores(kbdev,
|
||||
KBASE_PM_CORE_L2);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
u64 tiler_trans = kbase_pm_get_trans_cores(kbdev,
|
||||
KBASE_PM_CORE_TILER);
|
||||
u64 tiler_ready = kbase_pm_get_ready_cores(kbdev,
|
||||
KBASE_PM_CORE_TILER);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* kbase_pm_get_ready_cores and kbase_pm_get_trans_cores
|
||||
@@ -736,8 +898,9 @@ static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
* between the register reads
|
||||
*/
|
||||
l2_trans &= ~l2_ready;
|
||||
#if !MALI_USE_CSF
|
||||
tiler_trans &= ~tiler_ready;
|
||||
|
||||
#endif
|
||||
prev_state = backend->l2_state;
|
||||
|
||||
switch (backend->l2_state) {
|
||||
@@ -748,7 +911,7 @@ static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
* powering it on
|
||||
*/
|
||||
kbase_pm_l2_config_override(kbdev);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
/* L2 is required, power on. Powering on the
|
||||
* tiler will also power the first L2 cache.
|
||||
*/
|
||||
@@ -762,14 +925,30 @@ static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
kbase_pm_invoke(kbdev, KBASE_PM_CORE_L2,
|
||||
l2_present & ~1,
|
||||
ACTION_PWRON);
|
||||
#else
|
||||
/* With CSF firmware, Host driver doesn't need to
|
||||
* handle power management with both shader and tiler cores.
|
||||
* The CSF firmware will power up the cores appropriately.
|
||||
* So only power the l2 cache explicitly.
|
||||
*/
|
||||
kbase_pm_invoke(kbdev, KBASE_PM_CORE_L2,
|
||||
l2_present, ACTION_PWRON);
|
||||
#endif
|
||||
backend->l2_state = KBASE_L2_PEND_ON;
|
||||
}
|
||||
break;
|
||||
|
||||
case KBASE_L2_PEND_ON:
|
||||
#if !MALI_USE_CSF
|
||||
if (!l2_trans && l2_ready == l2_present && !tiler_trans
|
||||
&& tiler_ready == tiler_present) {
|
||||
KBASE_KTRACE_ADD(kbdev, PM_CORES_CHANGE_AVAILABLE_TILER, NULL, tiler_ready);
|
||||
KBASE_KTRACE_ADD(kbdev, PM_CORES_CHANGE_AVAILABLE_TILER, NULL,
|
||||
tiler_ready);
|
||||
#else
|
||||
if (!l2_trans && l2_ready == l2_present) {
|
||||
KBASE_KTRACE_ADD(kbdev, PM_CORES_CHANGE_AVAILABLE_L2, NULL,
|
||||
l2_ready);
|
||||
#endif
|
||||
/*
|
||||
* Ensure snoops are enabled after L2 is powered
|
||||
* up. Note that kbase keeps track of the snoop
|
||||
@@ -948,9 +1127,11 @@ static int kbase_pm_l2_update_state(struct kbase_device *kbdev)
|
||||
*/
|
||||
kbase_gpu_start_cache_clean_nolock(
|
||||
kbdev);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
KBASE_KTRACE_ADD(kbdev, PM_CORES_CHANGE_AVAILABLE_TILER, NULL, 0u);
|
||||
|
||||
#else
|
||||
KBASE_KTRACE_ADD(kbdev, PM_CORES_CHANGE_AVAILABLE_L2, NULL, 0u);
|
||||
#endif
|
||||
backend->l2_state = KBASE_L2_PEND_OFF;
|
||||
break;
|
||||
|
||||
@@ -1078,7 +1259,6 @@ static int kbase_pm_shaders_update_state(struct kbase_device *kbdev)
|
||||
&kbdev->pm.backend.shader_tick_timer;
|
||||
enum kbase_shader_core_state prev_state;
|
||||
u64 stacks_avail = 0;
|
||||
int err = 0;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
@@ -1173,8 +1353,18 @@ static int kbase_pm_shaders_update_state(struct kbase_device *kbdev)
|
||||
backend->pm_shaders_core_mask = shaders_ready;
|
||||
backend->hwcnt_desired = true;
|
||||
if (backend->hwcnt_disabled) {
|
||||
#if MALI_USE_CSF
|
||||
unsigned long flags;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev,
|
||||
&flags);
|
||||
#endif
|
||||
kbase_hwcnt_context_enable(
|
||||
kbdev->hwcnt_gpu_ctx);
|
||||
#if MALI_USE_CSF
|
||||
kbase_csf_scheduler_spin_unlock(kbdev,
|
||||
flags);
|
||||
#endif
|
||||
backend->hwcnt_disabled = false;
|
||||
}
|
||||
|
||||
@@ -1354,8 +1544,18 @@ static int kbase_pm_shaders_update_state(struct kbase_device *kbdev)
|
||||
backend->pm_shaders_core_mask = 0;
|
||||
backend->hwcnt_desired = true;
|
||||
if (backend->hwcnt_disabled) {
|
||||
#if MALI_USE_CSF
|
||||
unsigned long flags;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev,
|
||||
&flags);
|
||||
#endif
|
||||
kbase_hwcnt_context_enable(
|
||||
kbdev->hwcnt_gpu_ctx);
|
||||
#if MALI_USE_CSF
|
||||
kbase_csf_scheduler_spin_unlock(kbdev,
|
||||
flags);
|
||||
#endif
|
||||
backend->hwcnt_disabled = false;
|
||||
}
|
||||
backend->shaders_state = KBASE_SHADERS_OFF_CORESTACK_OFF_TIMER_PEND_OFF;
|
||||
@@ -1382,7 +1582,7 @@ static int kbase_pm_shaders_update_state(struct kbase_device *kbdev)
|
||||
|
||||
} while (backend->shaders_state != prev_state);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1647,7 +1847,8 @@ void kbase_pm_reset_complete(struct kbase_device *kbdev)
|
||||
|
||||
/* Timeout for kbase_pm_wait_for_desired_state when wait_event_killable has
|
||||
* aborted due to a fatal signal. If the time spent waiting has exceeded this
|
||||
* threshold then there is most likely a hardware issue. */
|
||||
* threshold then there is most likely a hardware issue.
|
||||
*/
|
||||
#define PM_TIMEOUT_MS (5000) /* 5s */
|
||||
|
||||
static void kbase_pm_timed_out(struct kbase_device *kbdev)
|
||||
@@ -1706,7 +1907,8 @@ static void kbase_pm_timed_out(struct kbase_device *kbdev)
|
||||
L2_PWRTRANS_LO)));
|
||||
|
||||
dev_err(kbdev->dev, "Sending reset to GPU - all running jobs will be lost\n");
|
||||
if (kbase_prepare_to_reset_gpu(kbdev))
|
||||
if (kbase_prepare_to_reset_gpu(kbdev,
|
||||
RESET_FLAGS_HWC_UNRECOVERABLE_ERROR))
|
||||
kbase_reset_gpu(kbdev);
|
||||
}
|
||||
|
||||
@@ -1774,7 +1976,7 @@ void kbase_pm_enable_interrupts(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
KBASE_DEBUG_ASSERT(NULL != kbdev);
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
/*
|
||||
* Clear all interrupts,
|
||||
* and unmask them all.
|
||||
@@ -1800,7 +2002,7 @@ KBASE_EXPORT_TEST_API(kbase_pm_enable_interrupts);
|
||||
|
||||
void kbase_pm_disable_interrupts_nolock(struct kbase_device *kbdev)
|
||||
{
|
||||
KBASE_DEBUG_ASSERT(NULL != kbdev);
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
/*
|
||||
* Mask all interrupts,
|
||||
* and clear them all.
|
||||
@@ -1827,6 +2029,22 @@ void kbase_pm_disable_interrupts(struct kbase_device *kbdev)
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_disable_interrupts);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
static void update_user_reg_page_mapping(struct kbase_device *kbdev)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
if (kbdev->csf.mali_file_inode) {
|
||||
/* This would zap the pte corresponding to the mapping of User
|
||||
* register page for all the Kbase contexts.
|
||||
*/
|
||||
unmap_mapping_range(kbdev->csf.mali_file_inode->i_mapping,
|
||||
BASEP_MEM_CSF_USER_REG_PAGE_HANDLE,
|
||||
PAGE_SIZE, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* pmu layout:
|
||||
* 0x0000: PMU TAG (RO) (0xCAFECAFE)
|
||||
@@ -1838,7 +2056,7 @@ void kbase_pm_clock_on(struct kbase_device *kbdev, bool is_resume)
|
||||
bool reset_required = is_resume;
|
||||
unsigned long flags;
|
||||
|
||||
KBASE_DEBUG_ASSERT(NULL != kbdev);
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
#if !MALI_USE_CSF
|
||||
lockdep_assert_held(&kbdev->js_data.runpool_mutex);
|
||||
#endif /* !MALI_USE_CSF */
|
||||
@@ -1876,24 +2094,39 @@ void kbase_pm_clock_on(struct kbase_device *kbdev, bool is_resume)
|
||||
kbdev->pm.backend.gpu_powered = true;
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* GPU has been turned on, can switch to actual register page */
|
||||
update_user_reg_page_mapping(kbdev);
|
||||
#endif
|
||||
|
||||
if (reset_required) {
|
||||
/* GPU state was lost, reset GPU to ensure it is in a
|
||||
* consistent state */
|
||||
* consistent state
|
||||
*/
|
||||
kbase_pm_init_hw(kbdev, PM_ENABLE_IRQS);
|
||||
}
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
else {
|
||||
struct kbase_arbiter_vm_state *arb_vm_state =
|
||||
if (kbdev->arb.arb_if) {
|
||||
struct kbase_arbiter_vm_state *arb_vm_state =
|
||||
kbdev->pm.arb_vm_state;
|
||||
|
||||
/* In the case that the GPU has just been granted by
|
||||
* the Arbiter, a reset will have already been done.
|
||||
* However, it is still necessary to initialize the GPU.
|
||||
*/
|
||||
if (arb_vm_state->vm_arb_starting)
|
||||
kbase_pm_init_hw(kbdev, PM_ENABLE_IRQS |
|
||||
PM_NO_RESET);
|
||||
/* In the case that the GPU has just been granted by
|
||||
* the Arbiter, a reset will have already been done.
|
||||
* However, it is still necessary to initialize the GPU.
|
||||
*/
|
||||
if (arb_vm_state->vm_arb_starting)
|
||||
kbase_pm_init_hw(kbdev, PM_ENABLE_IRQS |
|
||||
PM_NO_RESET);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This point means that the GPU trasitioned to ON. So there is a chance
|
||||
* that a repartitioning occurred. In this case the current config
|
||||
* should be read again.
|
||||
*/
|
||||
kbase_gpuprops_get_curr_config_props(kbdev,
|
||||
&kbdev->gpu_props.curr_config);
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
|
||||
mutex_lock(&kbdev->mmu_hw_mutex);
|
||||
@@ -1918,6 +2151,17 @@ void kbase_pm_clock_on(struct kbase_device *kbdev, bool is_resume)
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kbdev->pm.backend.gpu_ready = true;
|
||||
kbdev->pm.backend.l2_desired = true;
|
||||
#if MALI_USE_CSF
|
||||
if (reset_required) {
|
||||
/* GPU reset was done after the power on, so send the post
|
||||
* reset event instead. This is okay as GPU power off event
|
||||
* is same as pre GPU reset event.
|
||||
*/
|
||||
kbase_ipa_control_handle_gpu_reset_post(kbdev);
|
||||
} else {
|
||||
kbase_ipa_control_handle_gpu_power_on(kbdev);
|
||||
}
|
||||
#endif
|
||||
kbase_pm_update_state(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
}
|
||||
@@ -1928,7 +2172,7 @@ bool kbase_pm_clock_off(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
KBASE_DEBUG_ASSERT(NULL != kbdev);
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
/* ASSERT that the cores should now be unavailable. No lock needed. */
|
||||
@@ -1952,12 +2196,16 @@ bool kbase_pm_clock_off(struct kbase_device *kbdev)
|
||||
|
||||
if (atomic_read(&kbdev->faults_pending)) {
|
||||
/* Page/bus faults are still being processed. The GPU can not
|
||||
* be powered off until they have completed */
|
||||
* be powered off until they have completed
|
||||
*/
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
kbase_pm_cache_snoop_disable(kbdev);
|
||||
#if MALI_USE_CSF
|
||||
kbase_ipa_control_handle_gpu_power_off(kbdev);
|
||||
#endif
|
||||
|
||||
kbdev->pm.backend.gpu_ready = false;
|
||||
|
||||
@@ -1974,6 +2222,12 @@ bool kbase_pm_clock_off(struct kbase_device *kbdev)
|
||||
#endif
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* GPU is about to be turned off, switch to dummy page */
|
||||
update_user_reg_page_mapping(kbdev);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
kbase_arbiter_pm_vm_event(kbdev, KBASE_VM_GPU_IDLE_EVENT);
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
@@ -2021,23 +2275,23 @@ static enum hrtimer_restart kbasep_reset_timeout(struct hrtimer *timer)
|
||||
struct kbasep_reset_timeout_data *rtdata =
|
||||
container_of(timer, struct kbasep_reset_timeout_data, timer);
|
||||
|
||||
rtdata->timed_out = 1;
|
||||
rtdata->timed_out = true;
|
||||
|
||||
/* Set the wait queue to wake up kbase_pm_init_hw even though the reset
|
||||
* hasn't completed */
|
||||
* hasn't completed
|
||||
*/
|
||||
kbase_pm_reset_done(rtdata->kbdev);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int kbase_set_jm_quirks(struct kbase_device *kbdev, const u32 prod_id)
|
||||
static int kbase_set_gpu_quirks(struct kbase_device *kbdev, const u32 prod_id)
|
||||
{
|
||||
#if MALI_USE_CSF
|
||||
kbdev->hw_quirks_jm = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(CSF_CONFIG));
|
||||
kbdev->hw_quirks_gpu =
|
||||
kbase_reg_read(kbdev, GPU_CONTROL_REG(CSF_CONFIG));
|
||||
#else
|
||||
u32 hw_quirks_jm = kbase_reg_read(kbdev,
|
||||
GPU_CONTROL_REG(JM_CONFIG));
|
||||
u32 hw_quirks_gpu = kbase_reg_read(kbdev, GPU_CONTROL_REG(JM_CONFIG));
|
||||
|
||||
if (GPU_ID2_MODEL_MATCH_VALUE(prod_id) == GPU_ID2_PRODUCT_TMIX) {
|
||||
/* Only for tMIx */
|
||||
@@ -2051,39 +2305,38 @@ static int kbase_set_jm_quirks(struct kbase_device *kbdev, const u32 prod_id)
|
||||
*/
|
||||
if (coherency_features ==
|
||||
COHERENCY_FEATURE_BIT(COHERENCY_ACE)) {
|
||||
hw_quirks_jm |= (COHERENCY_ACE_LITE |
|
||||
COHERENCY_ACE) <<
|
||||
JM_FORCE_COHERENCY_FEATURES_SHIFT;
|
||||
hw_quirks_gpu |= (COHERENCY_ACE_LITE | COHERENCY_ACE)
|
||||
<< JM_FORCE_COHERENCY_FEATURES_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (kbase_is_gpu_removed(kbdev))
|
||||
return -EIO;
|
||||
|
||||
kbdev->hw_quirks_jm = hw_quirks_jm;
|
||||
kbdev->hw_quirks_gpu = hw_quirks_gpu;
|
||||
|
||||
#endif /* !MALI_USE_CSF */
|
||||
if (kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_IDVS_GROUP_SIZE)) {
|
||||
int default_idvs_group_size = 0xF;
|
||||
u32 tmp;
|
||||
u32 group_size = 0;
|
||||
|
||||
if (of_property_read_u32(kbdev->dev->of_node,
|
||||
"idvs-group-size", &tmp))
|
||||
tmp = default_idvs_group_size;
|
||||
if (of_property_read_u32(kbdev->dev->of_node, "idvs-group-size",
|
||||
&group_size))
|
||||
group_size = default_idvs_group_size;
|
||||
|
||||
if (tmp > IDVS_GROUP_MAX_SIZE) {
|
||||
if (group_size > IDVS_GROUP_MAX_SIZE) {
|
||||
dev_err(kbdev->dev,
|
||||
"idvs-group-size of %d is too large. Maximum value is %d",
|
||||
tmp, IDVS_GROUP_MAX_SIZE);
|
||||
tmp = default_idvs_group_size;
|
||||
group_size, IDVS_GROUP_MAX_SIZE);
|
||||
group_size = default_idvs_group_size;
|
||||
}
|
||||
|
||||
kbdev->hw_quirks_jm |= tmp << IDVS_GROUP_SIZE_SHIFT;
|
||||
kbdev->hw_quirks_gpu |= group_size << IDVS_GROUP_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
#define MANUAL_POWER_CONTROL ((u32)(1 << 8))
|
||||
if (corestack_driver_control)
|
||||
kbdev->hw_quirks_jm |= MANUAL_POWER_CONTROL;
|
||||
kbdev->hw_quirks_gpu |= MANUAL_POWER_CONTROL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2137,18 +2390,17 @@ static int kbase_pm_hw_issues_detect(struct kbase_device *kbdev)
|
||||
GPU_ID_VERSION_PRODUCT_ID_SHIFT;
|
||||
int error = 0;
|
||||
|
||||
kbdev->hw_quirks_jm = 0;
|
||||
kbdev->hw_quirks_gpu = 0;
|
||||
kbdev->hw_quirks_sc = 0;
|
||||
kbdev->hw_quirks_tiler = 0;
|
||||
kbdev->hw_quirks_mmu = 0;
|
||||
|
||||
if (!of_property_read_u32(np, "quirks_jm",
|
||||
&kbdev->hw_quirks_jm)) {
|
||||
if (!of_property_read_u32(np, "quirks_gpu", &kbdev->hw_quirks_gpu)) {
|
||||
dev_info(kbdev->dev,
|
||||
"Found quirks_jm = [0x%x] in Devicetree\n",
|
||||
kbdev->hw_quirks_jm);
|
||||
"Found quirks_gpu = [0x%x] in Devicetree\n",
|
||||
kbdev->hw_quirks_gpu);
|
||||
} else {
|
||||
error = kbase_set_jm_quirks(kbdev, prod_id);
|
||||
error = kbase_set_gpu_quirks(kbdev, prod_id);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
@@ -2199,10 +2451,10 @@ static void kbase_pm_hw_issues_apply(struct kbase_device *kbdev)
|
||||
kbdev->hw_quirks_mmu);
|
||||
#if MALI_USE_CSF
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(CSF_CONFIG),
|
||||
kbdev->hw_quirks_jm);
|
||||
kbdev->hw_quirks_gpu);
|
||||
#else
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(JM_CONFIG),
|
||||
kbdev->hw_quirks_jm);
|
||||
kbdev->hw_quirks_gpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2233,6 +2485,7 @@ void kbase_pm_cache_snoop_disable(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
static void reenable_protected_mode_hwcnt(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
@@ -2245,6 +2498,7 @@ static void reenable_protected_mode_hwcnt(struct kbase_device *kbdev)
|
||||
}
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, irq_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int kbase_pm_do_reset(struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -2271,7 +2525,7 @@ static int kbase_pm_do_reset(struct kbase_device *kbdev)
|
||||
|
||||
/* Initialize a structure for tracking the status of the reset */
|
||||
rtdata.kbdev = kbdev;
|
||||
rtdata.timed_out = 0;
|
||||
rtdata.timed_out = false;
|
||||
|
||||
/* Create a timer to use as a timeout on the reset */
|
||||
hrtimer_init_on_stack(&rtdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
@@ -2283,7 +2537,7 @@ static int kbase_pm_do_reset(struct kbase_device *kbdev)
|
||||
/* Wait for the RESET_COMPLETED interrupt to be raised */
|
||||
kbase_pm_wait_for_reset(kbdev);
|
||||
|
||||
if (rtdata.timed_out == 0) {
|
||||
if (!rtdata.timed_out) {
|
||||
/* GPU has been reset */
|
||||
hrtimer_cancel(&rtdata.timer);
|
||||
destroy_hrtimer_on_stack(&rtdata.timer);
|
||||
@@ -2291,11 +2545,13 @@ static int kbase_pm_do_reset(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
/* No interrupt has been received - check if the RAWSTAT register says
|
||||
* the reset has completed */
|
||||
* the reset has completed
|
||||
*/
|
||||
if ((kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_RAWSTAT)) &
|
||||
RESET_COMPLETED)) {
|
||||
/* The interrupt is set in the RAWSTAT; this suggests that the
|
||||
* interrupts are not getting to the CPU */
|
||||
* interrupts are not getting to the CPU
|
||||
*/
|
||||
dev_err(kbdev->dev, "Reset interrupt didn't reach CPU. Check interrupt assignments.\n");
|
||||
/* If interrupts aren't working we can't continue. */
|
||||
destroy_hrtimer_on_stack(&rtdata.timer);
|
||||
@@ -2309,33 +2565,40 @@ static int kbase_pm_do_reset(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
/* The GPU doesn't seem to be responding to the reset so try a hard
|
||||
* reset */
|
||||
dev_err(kbdev->dev, "Failed to soft-reset GPU (timed out after %d ms), now attempting a hard reset\n",
|
||||
RESET_TIMEOUT);
|
||||
KBASE_KTRACE_ADD(kbdev, CORE_GPU_HARD_RESET, NULL, 0);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_HARD_RESET);
|
||||
* reset, but only when NOT in arbitration mode.
|
||||
*/
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
if (!kbdev->arb.arb_if) {
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
dev_err(kbdev->dev, "Failed to soft-reset GPU (timed out after %d ms), now attempting a hard reset\n",
|
||||
RESET_TIMEOUT);
|
||||
KBASE_KTRACE_ADD(kbdev, CORE_GPU_HARD_RESET, NULL, 0);
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_HARD_RESET);
|
||||
|
||||
/* Restart the timer to wait for the hard reset to complete */
|
||||
rtdata.timed_out = 0;
|
||||
/* Restart the timer to wait for the hard reset to complete */
|
||||
rtdata.timed_out = false;
|
||||
|
||||
hrtimer_start(&rtdata.timer, HR_TIMER_DELAY_MSEC(RESET_TIMEOUT),
|
||||
HRTIMER_MODE_REL);
|
||||
hrtimer_start(&rtdata.timer, HR_TIMER_DELAY_MSEC(RESET_TIMEOUT),
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
/* Wait for the RESET_COMPLETED interrupt to be raised */
|
||||
kbase_pm_wait_for_reset(kbdev);
|
||||
/* Wait for the RESET_COMPLETED interrupt to be raised */
|
||||
kbase_pm_wait_for_reset(kbdev);
|
||||
|
||||
if (!rtdata.timed_out) {
|
||||
/* GPU has been reset */
|
||||
hrtimer_cancel(&rtdata.timer);
|
||||
destroy_hrtimer_on_stack(&rtdata.timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rtdata.timed_out == 0) {
|
||||
/* GPU has been reset */
|
||||
hrtimer_cancel(&rtdata.timer);
|
||||
destroy_hrtimer_on_stack(&rtdata.timer);
|
||||
return 0;
|
||||
|
||||
dev_err(kbdev->dev, "Failed to hard-reset the GPU (timed out after %d ms)\n",
|
||||
RESET_TIMEOUT);
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
}
|
||||
|
||||
destroy_hrtimer_on_stack(&rtdata.timer);
|
||||
|
||||
dev_err(kbdev->dev, "Failed to hard-reset the GPU (timed out after %d ms)\n",
|
||||
RESET_TIMEOUT);
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -2359,7 +2622,7 @@ int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags)
|
||||
unsigned long irq_flags;
|
||||
int err = 0;
|
||||
|
||||
KBASE_DEBUG_ASSERT(NULL != kbdev);
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
/* Ensure the clock is on before attempting to access the hardware */
|
||||
@@ -2371,7 +2634,8 @@ int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags)
|
||||
}
|
||||
|
||||
/* Ensure interrupts are off to begin with, this also clears any
|
||||
* outstanding interrupts */
|
||||
* outstanding interrupts
|
||||
*/
|
||||
kbase_pm_disable_interrupts(kbdev);
|
||||
/* Ensure cache snoops are disabled before reset. */
|
||||
kbase_pm_cache_snoop_disable(kbdev);
|
||||
@@ -2392,6 +2656,17 @@ int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags)
|
||||
kbdev->protected_dev);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, irq_flags);
|
||||
#if MALI_USE_CSF
|
||||
if (kbdev->protected_mode) {
|
||||
unsigned long flags;
|
||||
|
||||
kbase_ipa_control_protm_exited(kbdev);
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
kbase_hwcnt_backend_csf_protm_exited(&kbdev->hwcnt_gpu_iface);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
}
|
||||
#endif
|
||||
kbdev->protected_mode = false;
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, irq_flags);
|
||||
|
||||
@@ -2412,7 +2687,8 @@ int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags)
|
||||
GPU_STATUS_PROTECTED_MODE_ACTIVE);
|
||||
|
||||
/* If cycle counter was in use re-enable it, enable_irqs will only be
|
||||
* false when called from kbase_pm_powerup */
|
||||
* false when called from kbase_pm_powerup
|
||||
*/
|
||||
if (kbdev->pm.backend.gpu_cycle_counter_requests &&
|
||||
(flags & PM_ENABLE_IRQS)) {
|
||||
kbase_pm_enable_interrupts(kbdev);
|
||||
@@ -2435,12 +2711,14 @@ int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags)
|
||||
kbase_pm_enable_interrupts(kbdev);
|
||||
|
||||
exit:
|
||||
#if !MALI_USE_CSF
|
||||
if (!kbdev->pm.backend.protected_entry_transition_override) {
|
||||
/* Re-enable GPU hardware counters if we're resetting from
|
||||
* protected mode.
|
||||
*/
|
||||
reenable_protected_mode_hwcnt(kbdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -2467,12 +2745,22 @@ kbase_pm_request_gpu_cycle_counter_do_request(struct kbase_device *kbdev)
|
||||
|
||||
spin_lock_irqsave(&kbdev->pm.backend.gpu_cycle_counter_requests_lock,
|
||||
flags);
|
||||
|
||||
++kbdev->pm.backend.gpu_cycle_counter_requests;
|
||||
|
||||
if (1 == kbdev->pm.backend.gpu_cycle_counter_requests)
|
||||
if (kbdev->pm.backend.gpu_cycle_counter_requests == 1)
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_CYCLE_COUNT_START);
|
||||
else {
|
||||
/* This might happen after GPU reset.
|
||||
* Then counter needs to be kicked.
|
||||
*/
|
||||
if (!IS_ENABLED(CONFIG_MALI_BIFROST_NO_MALI) &&
|
||||
(!(kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_STATUS)) &
|
||||
GPU_STATUS_CYCLE_COUNT_ACTIVE))) {
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_CYCLE_COUNT_START);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(
|
||||
&kbdev->pm.backend.gpu_cycle_counter_requests_lock,
|
||||
@@ -2488,6 +2776,8 @@ void kbase_pm_request_gpu_cycle_counter(struct kbase_device *kbdev)
|
||||
KBASE_DEBUG_ASSERT(kbdev->pm.backend.gpu_cycle_counter_requests <
|
||||
INT_MAX);
|
||||
|
||||
kbase_pm_wait_for_l2_powered(kbdev);
|
||||
|
||||
kbase_pm_request_gpu_cycle_counter_do_request(kbdev);
|
||||
}
|
||||
|
||||
@@ -2522,7 +2812,7 @@ void kbase_pm_release_gpu_cycle_counter_nolock(struct kbase_device *kbdev)
|
||||
|
||||
--kbdev->pm.backend.gpu_cycle_counter_requests;
|
||||
|
||||
if (0 == kbdev->pm.backend.gpu_cycle_counter_requests)
|
||||
if (kbdev->pm.backend.gpu_cycle_counter_requests == 0)
|
||||
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
|
||||
GPU_COMMAND_CYCLE_COUNT_STOP);
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Power management API definitions used internally by GPU backend
|
||||
*/
|
||||
@@ -227,6 +224,7 @@ void kbase_pm_reset_done(struct kbase_device *kbdev);
|
||||
*
|
||||
* Return: 0 on success, error code on error
|
||||
*/
|
||||
int kbase_pm_wait_for_desired_state(struct kbase_device *kbdev);
|
||||
#else
|
||||
/**
|
||||
* kbase_pm_wait_for_desired_state - Wait for the desired power state to be
|
||||
@@ -250,8 +248,8 @@ void kbase_pm_reset_done(struct kbase_device *kbdev);
|
||||
*
|
||||
* Return: 0 on success, error code on error
|
||||
*/
|
||||
#endif
|
||||
int kbase_pm_wait_for_desired_state(struct kbase_device *kbdev);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* kbase_pm_wait_for_l2_powered - Wait for the L2 cache to be powered on
|
||||
@@ -492,7 +490,8 @@ void kbase_pm_register_access_enable(struct kbase_device *kbdev);
|
||||
void kbase_pm_register_access_disable(struct kbase_device *kbdev);
|
||||
|
||||
/* NOTE: kbase_pm_is_suspending is in mali_kbase.h, because it is an inline
|
||||
* function */
|
||||
* function
|
||||
*/
|
||||
|
||||
/**
|
||||
* kbase_pm_metrics_is_active - Check if the power management metrics
|
||||
@@ -536,8 +535,22 @@ void kbase_pm_get_dvfs_metrics(struct kbase_device *kbdev,
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DVFS
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/**
|
||||
* kbase_platform_dvfs_event - Report utilisation to DVFS code
|
||||
* kbase_platform_dvfs_event - Report utilisation to DVFS code for CSF GPU
|
||||
*
|
||||
* Function provided by platform specific code when DVFS is enabled to allow
|
||||
* the power management metrics system to report utilisation.
|
||||
*
|
||||
* @kbdev: The kbase device structure for the device (must be a
|
||||
* valid pointer)
|
||||
* @utilisation: The current calculated utilisation by the metrics system.
|
||||
* Return: Returns 0 on failure and non zero on success.
|
||||
*/
|
||||
int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation);
|
||||
#else
|
||||
/**
|
||||
* kbase_platform_dvfs_event - Report utilisation to DVFS code for JM GPU
|
||||
*
|
||||
* Function provided by platform specific code when DVFS is enabled to allow
|
||||
* the power management metrics system to report utilisation.
|
||||
@@ -550,11 +563,12 @@ void kbase_pm_get_dvfs_metrics(struct kbase_device *kbdev,
|
||||
* group.
|
||||
* Return: Returns 0 on failure and non zero on success.
|
||||
*/
|
||||
|
||||
int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation,
|
||||
u32 util_gl_share, u32 util_cl_share[2]);
|
||||
u32 util_gl_share, u32 util_cl_share[2]);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_MALI_BIFROST_DVFS */
|
||||
|
||||
void kbase_pm_power_changed(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
@@ -708,6 +722,72 @@ extern bool corestack_driver_control;
|
||||
*/
|
||||
bool kbase_pm_is_l2_desired(struct kbase_device *kbdev);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/**
|
||||
* kbase_pm_is_mcu_desired - Check whether MCU is desired
|
||||
*
|
||||
* @kbdev: Device pointer
|
||||
*
|
||||
* This shall be called to check whether MCU needs to be enabled.
|
||||
*
|
||||
* Return: true if MCU needs to be enabled.
|
||||
*/
|
||||
bool kbase_pm_is_mcu_desired(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_pm_idle_groups_sched_suspendable - Check whether the scheduler can be
|
||||
* suspended to low power state when all
|
||||
* the CSGs are idle
|
||||
*
|
||||
* @kbdev: Device pointer
|
||||
*
|
||||
* Return: true if allowed to enter the suspended state.
|
||||
*/
|
||||
static inline
|
||||
bool kbase_pm_idle_groups_sched_suspendable(struct kbase_device *kbdev)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
return !(kbdev->pm.backend.csf_pm_sched_flags &
|
||||
CSF_DYNAMIC_PM_SCHED_IGNORE_IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_pm_no_runnables_sched_suspendable - Check whether the scheduler can be
|
||||
* suspended to low power state when
|
||||
* there are no runnable CSGs.
|
||||
*
|
||||
* @kbdev: Device pointer
|
||||
*
|
||||
* Return: true if allowed to enter the suspended state.
|
||||
*/
|
||||
static inline
|
||||
bool kbase_pm_no_runnables_sched_suspendable(struct kbase_device *kbdev)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
return !(kbdev->pm.backend.csf_pm_sched_flags &
|
||||
CSF_DYNAMIC_PM_SCHED_NO_SUSPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_pm_no_mcu_core_pwroff - Check whether the PM is required to keep the
|
||||
* MCU core powered in accordance to the active
|
||||
* power management policy
|
||||
*
|
||||
* @kbdev: Device pointer
|
||||
*
|
||||
* Return: true if the MCU is to retain powered.
|
||||
*/
|
||||
static inline bool kbase_pm_no_mcu_core_pwroff(struct kbase_device *kbdev)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
return kbdev->pm.backend.csf_pm_sched_flags &
|
||||
CSF_DYNAMIC_PM_CORE_KEEP_ON;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* kbase_pm_lock - Lock all necessary mutexes to perform PM actions
|
||||
*
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -25,6 +24,19 @@
|
||||
* The function-like macro KBASEP_L2_STATE() must be defined before including
|
||||
* this header file. This header file can be included multiple times in the
|
||||
* same compilation unit with different definitions of KBASEP_L2_STATE().
|
||||
*
|
||||
* @OFF: The L2 cache and tiler are off
|
||||
* @PEND_ON: The L2 cache and tiler are powering on
|
||||
* @RESTORE_CLOCKS: The GPU clock is restored. Conditionally used.
|
||||
* @ON_HWCNT_ENABLE: The L2 cache and tiler are on, and hwcnt is being enabled
|
||||
* @ON: The L2 cache and tiler are on, and hwcnt is enabled
|
||||
* @ON_HWCNT_DISABLE: The L2 cache and tiler are on, and hwcnt is being disabled
|
||||
* @SLOW_DOWN_CLOCKS: The GPU clock is set to appropriate or lowest clock.
|
||||
* Conditionally used.
|
||||
* @POWER_DOWN: The L2 cache and tiler are about to be powered off
|
||||
* @PEND_OFF: The L2 cache and tiler are powering off
|
||||
* @RESET_WAIT: The GPU is resetting, L2 cache and tiler power state are
|
||||
* unknown
|
||||
*/
|
||||
KBASEP_L2_STATE(OFF)
|
||||
KBASEP_L2_STATE(PEND_ON)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -25,15 +24,40 @@
|
||||
* The function-like macro KBASEP_MCU_STATE() must be defined before including
|
||||
* this header file. This header file can be included multiple times in the
|
||||
* same compilation unit with different definitions of KBASEP_MCU_STATE().
|
||||
*
|
||||
* @OFF: The MCU is powered off.
|
||||
* @PEND_ON_RELOAD: The warm boot of MCU or cold boot of MCU (with
|
||||
* firmware reloading) is in progress.
|
||||
* @ON_GLB_REINIT_PEND: The MCU is enabled and Global configuration
|
||||
* requests have been sent to the firmware.
|
||||
* @ON_HWCNT_ENABLE: The Global requests have completed and MCU is now
|
||||
* ready for use and hwcnt is being enabled.
|
||||
* @ON: The MCU is active and hwcnt has been enabled.
|
||||
* @ON_CORE_ATTR_UPDATE_PEND: The MCU is active and mask of enabled shader cores
|
||||
* is being updated.
|
||||
* @ON_HWCNT_DISABLE: The MCU is on and hwcnt is being disabled.
|
||||
* @ON_HALT: The MCU is on and hwcnt has been disabled, MCU
|
||||
* halt would be triggered.
|
||||
* @ON_PEND_HALT: MCU halt in progress, confirmation pending.
|
||||
* @POWER_DOWN: MCU halted operations, pending being disabled.
|
||||
* @PEND_OFF: MCU is being disabled, pending on powering off.
|
||||
* @RESET_WAIT: The GPU is resetting, MCU state is unknown.
|
||||
*/
|
||||
KBASEP_MCU_STATE(OFF)
|
||||
KBASEP_MCU_STATE(PEND_ON_RELOAD)
|
||||
KBASEP_MCU_STATE(ON_GLB_REINIT_PEND)
|
||||
KBASEP_MCU_STATE(ON_HWCNT_ENABLE)
|
||||
KBASEP_MCU_STATE(ON)
|
||||
KBASEP_MCU_STATE(ON_CORE_ATTR_UPDATE_PEND)
|
||||
KBASEP_MCU_STATE(ON_HWCNT_DISABLE)
|
||||
KBASEP_MCU_STATE(ON_HALT)
|
||||
KBASEP_MCU_STATE(ON_PEND_HALT)
|
||||
KBASEP_MCU_STATE(POWER_DOWN)
|
||||
KBASEP_MCU_STATE(PEND_OFF)
|
||||
KBASEP_MCU_STATE(RESET_WAIT)
|
||||
/* Additional MCU states with HOST_CONTROL_SHADERS */
|
||||
KBASEP_MCU_STATE(HCTL_SHADERS_PEND_ON)
|
||||
KBASEP_MCU_STATE(HCTL_CORES_NOTIFY_PEND)
|
||||
KBASEP_MCU_STATE(HCTL_MCU_ON_RECHECK)
|
||||
KBASEP_MCU_STATE(HCTL_SHADERS_READY_OFF)
|
||||
KBASEP_MCU_STATE(HCTL_SHADERS_PEND_OFF)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2011-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2011-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,12 +17,8 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Metrics for power management
|
||||
*/
|
||||
@@ -29,24 +26,28 @@
|
||||
#include <mali_kbase.h>
|
||||
#include <mali_kbase_pm.h>
|
||||
#include <backend/gpu/mali_kbase_pm_internal.h>
|
||||
#if !MALI_USE_CSF
|
||||
|
||||
#if MALI_USE_CSF
|
||||
#include "mali_kbase_clk_rate_trace_mgr.h"
|
||||
#include <csf/ipa_control/mali_kbase_csf_ipa_control.h>
|
||||
#else
|
||||
#include <backend/gpu/mali_kbase_jm_rb.h>
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
#include <backend/gpu/mali_kbase_pm_defs.h>
|
||||
#include <mali_linux_trace.h>
|
||||
|
||||
/* When VSync is being hit aim for utilisation between 70-90% */
|
||||
#define KBASE_PM_VSYNC_MIN_UTILISATION 70
|
||||
#define KBASE_PM_VSYNC_MAX_UTILISATION 90
|
||||
/* Otherwise aim for 10-40% */
|
||||
#define KBASE_PM_NO_VSYNC_MIN_UTILISATION 10
|
||||
#define KBASE_PM_NO_VSYNC_MAX_UTILISATION 40
|
||||
|
||||
/* Shift used for kbasep_pm_metrics_data.time_busy/idle - units of (1 << 8) ns
|
||||
* This gives a maximum period between samples of 2^(32+8)/100 ns = slightly
|
||||
* under 11s. Exceeding this will cause overflow */
|
||||
* under 11s. Exceeding this will cause overflow
|
||||
*/
|
||||
#define KBASE_PM_TIME_SHIFT 8
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* To get the GPU_ACTIVE value in nano seconds unit */
|
||||
#define GPU_ACTIVE_SCALING_FACTOR ((u64)1E9)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DVFS
|
||||
static enum hrtimer_restart dvfs_callback(struct hrtimer *timer)
|
||||
{
|
||||
@@ -73,11 +74,45 @@ static enum hrtimer_restart dvfs_callback(struct hrtimer *timer)
|
||||
|
||||
int kbasep_pm_metrics_init(struct kbase_device *kbdev)
|
||||
{
|
||||
#if MALI_USE_CSF
|
||||
struct kbase_ipa_control_perf_counter perf_counter;
|
||||
int err;
|
||||
|
||||
/* One counter group */
|
||||
const size_t NUM_PERF_COUNTERS = 1;
|
||||
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
|
||||
kbdev->pm.backend.metrics.kbdev = kbdev;
|
||||
|
||||
kbdev->pm.backend.metrics.time_period_start = ktime_get();
|
||||
kbdev->pm.backend.metrics.values.time_busy = 0;
|
||||
kbdev->pm.backend.metrics.values.time_idle = 0;
|
||||
kbdev->pm.backend.metrics.values.time_in_protm = 0;
|
||||
|
||||
perf_counter.scaling_factor = GPU_ACTIVE_SCALING_FACTOR;
|
||||
|
||||
/* Normalize values by GPU frequency */
|
||||
perf_counter.gpu_norm = true;
|
||||
|
||||
/* We need the GPU_ACTIVE counter, which is in the CSHW group */
|
||||
perf_counter.type = KBASE_IPA_CORE_TYPE_CSHW;
|
||||
|
||||
/* We need the GPU_ACTIVE counter */
|
||||
perf_counter.idx = GPU_ACTIVE_CNT_IDX;
|
||||
|
||||
err = kbase_ipa_control_register(
|
||||
kbdev, &perf_counter, NUM_PERF_COUNTERS,
|
||||
&kbdev->pm.backend.metrics.ipa_control_client);
|
||||
if (err) {
|
||||
dev_err(kbdev->dev,
|
||||
"Failed to register IPA with kbase_ipa_control: err=%d",
|
||||
err);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
kbdev->pm.backend.metrics.kbdev = kbdev;
|
||||
kbdev->pm.backend.metrics.time_period_start = ktime_get();
|
||||
|
||||
kbdev->pm.backend.metrics.gpu_active = false;
|
||||
kbdev->pm.backend.metrics.active_cl_ctx[0] = 0;
|
||||
kbdev->pm.backend.metrics.active_cl_ctx[1] = 0;
|
||||
@@ -91,16 +126,25 @@ int kbasep_pm_metrics_init(struct kbase_device *kbdev)
|
||||
kbdev->pm.backend.metrics.values.busy_cl[1] = 0;
|
||||
kbdev->pm.backend.metrics.values.busy_gl = 0;
|
||||
|
||||
#endif
|
||||
spin_lock_init(&kbdev->pm.backend.metrics.lock);
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DVFS
|
||||
hrtimer_init(&kbdev->pm.backend.metrics.timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL);
|
||||
kbdev->pm.backend.metrics.timer.function = dvfs_callback;
|
||||
|
||||
kbdev->pm.backend.metrics.initialized = true;
|
||||
kbase_pm_metrics_start(kbdev);
|
||||
#endif /* CONFIG_MALI_BIFROST_DVFS */
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* The sanity check on the GPU_ACTIVE performance counter
|
||||
* is skipped for Juno platforms that have timing problems.
|
||||
*/
|
||||
kbdev->pm.backend.metrics.skip_gpu_active_sanity_check =
|
||||
of_machine_is_compatible("arm,juno");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbasep_pm_metrics_init);
|
||||
@@ -117,7 +161,13 @@ void kbasep_pm_metrics_term(struct kbase_device *kbdev)
|
||||
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
|
||||
|
||||
hrtimer_cancel(&kbdev->pm.backend.metrics.timer);
|
||||
kbdev->pm.backend.metrics.initialized = false;
|
||||
#endif /* CONFIG_MALI_BIFROST_DVFS */
|
||||
|
||||
#if MALI_USE_CSF
|
||||
kbase_ipa_control_unregister(
|
||||
kbdev, kbdev->pm.backend.metrics.ipa_control_client);
|
||||
#endif
|
||||
}
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbasep_pm_metrics_term);
|
||||
@@ -125,8 +175,121 @@ KBASE_EXPORT_TEST_API(kbasep_pm_metrics_term);
|
||||
/* caller needs to hold kbdev->pm.backend.metrics.lock before calling this
|
||||
* function
|
||||
*/
|
||||
#if MALI_USE_CSF
|
||||
#if defined(CONFIG_MALI_BIFROST_DEVFREQ) || defined(CONFIG_MALI_BIFROST_DVFS)
|
||||
static void kbase_pm_get_dvfs_utilisation_calc(struct kbase_device *kbdev)
|
||||
{
|
||||
int err;
|
||||
u64 gpu_active_counter;
|
||||
u64 protected_time;
|
||||
ktime_t now;
|
||||
|
||||
lockdep_assert_held(&kbdev->pm.backend.metrics.lock);
|
||||
|
||||
/* Query IPA_CONTROL for the latest GPU-active and protected-time
|
||||
* info.
|
||||
*/
|
||||
err = kbase_ipa_control_query(
|
||||
kbdev, kbdev->pm.backend.metrics.ipa_control_client,
|
||||
&gpu_active_counter, 1, &protected_time);
|
||||
|
||||
/* Read the timestamp after reading the GPU_ACTIVE counter value.
|
||||
* This ensures the time gap between the 2 reads is consistent for
|
||||
* a meaningful comparison between the increment of GPU_ACTIVE and
|
||||
* elapsed time. The lock taken inside kbase_ipa_control_query()
|
||||
* function can cause lot of variation.
|
||||
*/
|
||||
now = ktime_get();
|
||||
|
||||
if (err) {
|
||||
dev_err(kbdev->dev,
|
||||
"Failed to query the increment of GPU_ACTIVE counter: err=%d",
|
||||
err);
|
||||
} else {
|
||||
u64 diff_ns, margin_ns;
|
||||
s64 diff_ns_signed;
|
||||
u32 ns_time;
|
||||
ktime_t diff = ktime_sub(
|
||||
now, kbdev->pm.backend.metrics.time_period_start);
|
||||
|
||||
diff_ns_signed = ktime_to_ns(diff);
|
||||
|
||||
if (diff_ns_signed < 0)
|
||||
return;
|
||||
|
||||
diff_ns = (u64)diff_ns_signed;
|
||||
|
||||
/* Use a margin value that is approximately 1% of the time
|
||||
* difference.
|
||||
*/
|
||||
margin_ns = diff_ns >> 6;
|
||||
|
||||
/* Calculate time difference in units of 256ns */
|
||||
ns_time = (u32)(diff_ns >> KBASE_PM_TIME_SHIFT);
|
||||
|
||||
#ifndef CONFIG_MALI_BIFROST_NO_MALI
|
||||
/* The GPU_ACTIVE counter shouldn't clock-up more time than has
|
||||
* actually elapsed - but still some margin needs to be given
|
||||
* when doing the comparison. There could be some drift between
|
||||
* the CPU and GPU clock.
|
||||
*
|
||||
* Can do the check only in a real driver build, as an arbitrary
|
||||
* value for GPU_ACTIVE can be fed into dummy model in no_mali
|
||||
* configuration which may not correspond to the real elapsed
|
||||
* time.
|
||||
*/
|
||||
if (!kbdev->pm.backend.metrics.skip_gpu_active_sanity_check) {
|
||||
if (gpu_active_counter > (diff_ns + margin_ns)) {
|
||||
dev_info(
|
||||
kbdev->dev,
|
||||
"GPU activity takes longer than time interval: %llu ns > %llu ns",
|
||||
(unsigned long long)gpu_active_counter,
|
||||
(unsigned long long)diff_ns);
|
||||
}
|
||||
}
|
||||
#else
|
||||
CSTD_UNUSED(margin_ns);
|
||||
#endif
|
||||
|
||||
/* Add protected_time to gpu_active_counter so that time in
|
||||
* protected mode is included in the apparent GPU active time,
|
||||
* then convert it from units of 1ns to units of 256ns, to
|
||||
* match what JM GPUs use. The assumption is made here that the
|
||||
* GPU is 100% busy while in protected mode, so we should add
|
||||
* this since the GPU can't (and thus won't) update these
|
||||
* counters while it's actually in protected mode.
|
||||
*
|
||||
* Perform the add after dividing each value down, to reduce
|
||||
* the chances of overflows.
|
||||
*/
|
||||
protected_time >>= KBASE_PM_TIME_SHIFT;
|
||||
gpu_active_counter >>= KBASE_PM_TIME_SHIFT;
|
||||
gpu_active_counter += protected_time;
|
||||
|
||||
/* Ensure the following equations don't go wrong if ns_time is
|
||||
* slightly larger than gpu_active_counter somehow
|
||||
*/
|
||||
gpu_active_counter = MIN(gpu_active_counter, ns_time);
|
||||
|
||||
kbdev->pm.backend.metrics.values.time_busy +=
|
||||
gpu_active_counter;
|
||||
|
||||
kbdev->pm.backend.metrics.values.time_idle +=
|
||||
ns_time - gpu_active_counter;
|
||||
|
||||
/* Also make time in protected mode available explicitly,
|
||||
* so users of this data have this info, too.
|
||||
*/
|
||||
kbdev->pm.backend.metrics.values.time_in_protm +=
|
||||
protected_time;
|
||||
}
|
||||
|
||||
kbdev->pm.backend.metrics.time_period_start = now;
|
||||
}
|
||||
#endif /* defined(CONFIG_MALI_BIFROST_DEVFREQ) || defined(CONFIG_MALI_BIFROST_DVFS) */
|
||||
#else
|
||||
static void kbase_pm_get_dvfs_utilisation_calc(struct kbase_device *kbdev,
|
||||
ktime_t now)
|
||||
ktime_t now)
|
||||
{
|
||||
ktime_t diff;
|
||||
|
||||
@@ -151,12 +314,13 @@ static void kbase_pm_get_dvfs_utilisation_calc(struct kbase_device *kbdev,
|
||||
if (kbdev->pm.backend.metrics.active_gl_ctx[2])
|
||||
kbdev->pm.backend.metrics.values.busy_gl += ns_time;
|
||||
} else {
|
||||
kbdev->pm.backend.metrics.values.time_idle += (u32) (ktime_to_ns(diff)
|
||||
>> KBASE_PM_TIME_SHIFT);
|
||||
kbdev->pm.backend.metrics.values.time_idle +=
|
||||
(u32)(ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
|
||||
}
|
||||
|
||||
kbdev->pm.backend.metrics.time_period_start = now;
|
||||
}
|
||||
#endif /* MALI_USE_CSF */
|
||||
|
||||
#if defined(CONFIG_MALI_BIFROST_DEVFREQ) || defined(CONFIG_MALI_BIFROST_DVFS)
|
||||
void kbase_pm_get_dvfs_metrics(struct kbase_device *kbdev,
|
||||
@@ -167,14 +331,23 @@ void kbase_pm_get_dvfs_metrics(struct kbase_device *kbdev,
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
|
||||
#if MALI_USE_CSF
|
||||
kbase_pm_get_dvfs_utilisation_calc(kbdev);
|
||||
#else
|
||||
kbase_pm_get_dvfs_utilisation_calc(kbdev, ktime_get());
|
||||
#endif
|
||||
|
||||
memset(diff, 0, sizeof(*diff));
|
||||
diff->time_busy = cur->time_busy - last->time_busy;
|
||||
diff->time_idle = cur->time_idle - last->time_idle;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
diff->time_in_protm = cur->time_in_protm - last->time_in_protm;
|
||||
#else
|
||||
diff->busy_cl[0] = cur->busy_cl[0] - last->busy_cl[0];
|
||||
diff->busy_cl[1] = cur->busy_cl[1] - last->busy_cl[1];
|
||||
diff->busy_gl = cur->busy_gl - last->busy_gl;
|
||||
#endif
|
||||
|
||||
*last = *cur;
|
||||
|
||||
@@ -186,26 +359,42 @@ KBASE_EXPORT_TEST_API(kbase_pm_get_dvfs_metrics);
|
||||
#ifdef CONFIG_MALI_BIFROST_DVFS
|
||||
void kbase_pm_get_dvfs_action(struct kbase_device *kbdev)
|
||||
{
|
||||
int utilisation, util_gl_share;
|
||||
int util_cl_share[2];
|
||||
int busy;
|
||||
int utilisation;
|
||||
struct kbasep_pm_metrics *diff;
|
||||
#if !MALI_USE_CSF
|
||||
int busy;
|
||||
int util_gl_share;
|
||||
int util_cl_share[2];
|
||||
#endif
|
||||
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
|
||||
diff = &kbdev->pm.backend.metrics.dvfs_diff;
|
||||
|
||||
kbase_pm_get_dvfs_metrics(kbdev, &kbdev->pm.backend.metrics.dvfs_last, diff);
|
||||
kbase_pm_get_dvfs_metrics(kbdev, &kbdev->pm.backend.metrics.dvfs_last,
|
||||
diff);
|
||||
|
||||
utilisation = (100 * diff->time_busy) /
|
||||
max(diff->time_busy + diff->time_idle, 1u);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
busy = max(diff->busy_gl + diff->busy_cl[0] + diff->busy_cl[1], 1u);
|
||||
|
||||
util_gl_share = (100 * diff->busy_gl) / busy;
|
||||
util_cl_share[0] = (100 * diff->busy_cl[0]) / busy;
|
||||
util_cl_share[1] = (100 * diff->busy_cl[1]) / busy;
|
||||
|
||||
kbase_platform_dvfs_event(kbdev, utilisation, util_gl_share, util_cl_share);
|
||||
kbase_platform_dvfs_event(kbdev, utilisation, util_gl_share,
|
||||
util_cl_share);
|
||||
#else
|
||||
/* Note that, at present, we don't pass protected-mode time to the
|
||||
* platform here. It's unlikely to be useful, however, as the platform
|
||||
* probably just cares whether the GPU is busy or not; time in
|
||||
* protected mode is already added to busy-time at this point, though,
|
||||
* so we should be good.
|
||||
*/
|
||||
kbase_platform_dvfs_event(kbdev, utilisation);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool kbase_pm_metrics_is_active(struct kbase_device *kbdev)
|
||||
@@ -226,11 +415,20 @@ KBASE_EXPORT_TEST_API(kbase_pm_metrics_is_active);
|
||||
void kbase_pm_metrics_start(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool update = true;
|
||||
|
||||
if (unlikely(!kbdev->pm.backend.metrics.initialized))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
|
||||
kbdev->pm.backend.metrics.timer_active = true;
|
||||
if (!kbdev->pm.backend.metrics.timer_active)
|
||||
kbdev->pm.backend.metrics.timer_active = true;
|
||||
else
|
||||
update = false;
|
||||
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
|
||||
hrtimer_start(&kbdev->pm.backend.metrics.timer,
|
||||
|
||||
if (update)
|
||||
hrtimer_start(&kbdev->pm.backend.metrics.timer,
|
||||
HR_TIMER_DELAY_MSEC(kbdev->pm.dvfs_period),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
@@ -238,11 +436,20 @@ void kbase_pm_metrics_start(struct kbase_device *kbdev)
|
||||
void kbase_pm_metrics_stop(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool update = true;
|
||||
|
||||
if (unlikely(!kbdev->pm.backend.metrics.initialized))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
|
||||
kbdev->pm.backend.metrics.timer_active = false;
|
||||
if (kbdev->pm.backend.metrics.timer_active)
|
||||
kbdev->pm.backend.metrics.timer_active = false;
|
||||
else
|
||||
update = false;
|
||||
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
|
||||
hrtimer_cancel(&kbdev->pm.backend.metrics.timer);
|
||||
|
||||
if (update)
|
||||
hrtimer_cancel(&kbdev->pm.backend.metrics.timer);
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +480,8 @@ static void kbase_pm_metrics_active_calc(struct kbase_device *kbdev)
|
||||
struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, 0);
|
||||
|
||||
/* Head atom may have just completed, so if it isn't running
|
||||
* then try the next atom */
|
||||
* then try the next atom
|
||||
*/
|
||||
if (katom && katom->gpu_rb_state != KBASE_ATOM_GPU_RB_SUBMITTED)
|
||||
katom = kbase_gpu_inspect(kbdev, js, 1);
|
||||
|
||||
@@ -296,7 +504,6 @@ static void kbase_pm_metrics_active_calc(struct kbase_device *kbdev)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
/* called when job is submitted to or removed from a GPU slot */
|
||||
void kbase_pm_metrics_update(struct kbase_device *kbdev, ktime_t *timestamp)
|
||||
@@ -313,12 +520,12 @@ void kbase_pm_metrics_update(struct kbase_device *kbdev, ktime_t *timestamp)
|
||||
timestamp = &now;
|
||||
}
|
||||
|
||||
/* Track how long CL and/or GL jobs have been busy for */
|
||||
/* Track how much of time has been spent busy or idle. For JM GPUs,
|
||||
* this also evaluates how long CL and/or GL jobs have been busy for.
|
||||
*/
|
||||
kbase_pm_get_dvfs_utilisation_calc(kbdev, *timestamp);
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
kbase_pm_metrics_active_calc(kbdev);
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
|
||||
}
|
||||
#endif /* !MALI_USE_CSF */
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -28,6 +27,11 @@
|
||||
#include <gpu/mali_kbase_gpu_regmap.h>
|
||||
#include <mali_kbase_pm.h>
|
||||
#include <backend/gpu/mali_kbase_pm_internal.h>
|
||||
#include <mali_kbase_reset_gpu.h>
|
||||
|
||||
#if MALI_USE_CSF && defined CONFIG_MALI_BIFROST_DEBUG
|
||||
#include <csf/mali_kbase_csf_firmware.h>
|
||||
#endif
|
||||
|
||||
static const struct kbase_pm_policy *const all_policy_list[] = {
|
||||
#ifdef CONFIG_MALI_BIFROST_NO_MALI
|
||||
@@ -45,11 +49,45 @@ static const struct kbase_pm_policy *const all_policy_list[] = {
|
||||
#endif /* CONFIG_MALI_BIFROST_NO_MALI */
|
||||
};
|
||||
|
||||
#if MALI_USE_CSF
|
||||
void kbase_pm_policy_init(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
const struct kbase_pm_policy *default_policy = all_policy_list[0];
|
||||
|
||||
#if defined CONFIG_MALI_BIFROST_DEBUG
|
||||
/* Use always_on policy if module param fw_debug=1 is
|
||||
* passed, to aid firmware debugging.
|
||||
*/
|
||||
if (fw_debug)
|
||||
default_policy = &kbase_pm_always_on_policy_ops;
|
||||
#endif
|
||||
|
||||
|
||||
default_policy->init(kbdev);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kbdev->pm.backend.pm_current_policy = default_policy;
|
||||
kbdev->pm.backend.csf_pm_sched_flags =
|
||||
default_policy->pm_sched_flags;
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
}
|
||||
#else /* MALI_USE_CSF */
|
||||
void kbase_pm_policy_init(struct kbase_device *kbdev)
|
||||
{
|
||||
kbdev->pm.backend.pm_current_policy = all_policy_list[0];
|
||||
|
||||
#if MALI_USE_CSF && defined CONFIG_MALI_BIFROST_DEBUG
|
||||
/* Use always_on policy if module param fw_debug=1 is
|
||||
* passed, to aid firmware debugging.
|
||||
*/
|
||||
if (fw_debug)
|
||||
kbdev->pm.backend.pm_current_policy =
|
||||
&kbase_pm_always_on_policy_ops;
|
||||
#endif
|
||||
kbdev->pm.backend.pm_current_policy->init(kbdev);
|
||||
}
|
||||
#endif /* MALI_USE_CSF */
|
||||
|
||||
void kbase_pm_policy_term(struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -102,7 +140,8 @@ void kbase_pm_update_active(struct kbase_device *kbdev)
|
||||
}
|
||||
} else {
|
||||
/* It is an error for the power policy to power off the GPU
|
||||
* when there are contexts active */
|
||||
* when there are contexts active
|
||||
*/
|
||||
KBASE_DEBUG_ASSERT(pm->active_count == 0);
|
||||
|
||||
pm->backend.poweron_required = false;
|
||||
@@ -126,18 +165,20 @@ void kbase_pm_update_dynamic_cores_onoff(struct kbase_device *kbdev)
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
lockdep_assert_held(&kbdev->pm.lock);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* On CSF GPUs, Host driver isn't supposed to do the power management
|
||||
* for shader cores. CSF firmware will power up the cores appropriately
|
||||
* and so from Driver's standpoint 'shaders_desired' flag shall always
|
||||
* remain 0.
|
||||
*/
|
||||
return;
|
||||
#endif
|
||||
if (kbdev->pm.backend.pm_current_policy == NULL)
|
||||
return;
|
||||
if (kbdev->pm.backend.poweroff_wait_in_progress)
|
||||
return;
|
||||
|
||||
#if MALI_USE_CSF
|
||||
CSTD_UNUSED(shaders_desired);
|
||||
/* Invoke the MCU state machine to send a request to FW for updating
|
||||
* the mask of shader cores that can be used for allocation of
|
||||
* endpoints requested by CSGs.
|
||||
*/
|
||||
if (kbase_pm_is_mcu_desired(kbdev))
|
||||
kbase_pm_update_state(kbdev);
|
||||
#else
|
||||
/* In protected transition, don't allow outside shader core request
|
||||
* affect transition, return directly
|
||||
*/
|
||||
@@ -149,6 +190,7 @@ void kbase_pm_update_dynamic_cores_onoff(struct kbase_device *kbdev)
|
||||
if (shaders_desired && kbase_pm_is_l2_desired(kbdev)) {
|
||||
kbase_pm_update_state(kbdev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void kbase_pm_update_cores_state_nolock(struct kbase_device *kbdev)
|
||||
@@ -164,7 +206,8 @@ void kbase_pm_update_cores_state_nolock(struct kbase_device *kbdev)
|
||||
|
||||
if (kbdev->pm.backend.protected_transition_override)
|
||||
/* We are trying to change in/out of protected mode - force all
|
||||
* cores off so that the L2 powers down */
|
||||
* cores off so that the L2 powers down
|
||||
*/
|
||||
shaders_desired = false;
|
||||
else
|
||||
shaders_desired = kbdev->pm.backend.pm_current_policy->shaders_needed(kbdev);
|
||||
@@ -216,20 +259,106 @@ const struct kbase_pm_policy *kbase_pm_get_policy(struct kbase_device *kbdev)
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_get_policy);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
static int policy_change_wait_for_L2_off(struct kbase_device *kbdev)
|
||||
{
|
||||
#define WAIT_DURATION_MS (3000)
|
||||
long remaining;
|
||||
long timeout = kbase_csf_timeout_in_jiffies(WAIT_DURATION_MS);
|
||||
int err = 0;
|
||||
|
||||
/* Wait for L2 becoming off, by which the MCU is also implicitly off
|
||||
* since the L2 state machine would only start its power-down
|
||||
* sequence when the MCU is in off state. The L2 off is required
|
||||
* as the tiler may need to be power cycled for MCU reconfiguration
|
||||
* for host control of shader cores.
|
||||
*/
|
||||
#if KERNEL_VERSION(4, 13, 1) <= LINUX_VERSION_CODE
|
||||
remaining = wait_event_killable_timeout(
|
||||
kbdev->pm.backend.gpu_in_desired_state_wait,
|
||||
kbdev->pm.backend.l2_state == KBASE_L2_OFF, timeout);
|
||||
#else
|
||||
remaining = wait_event_timeout(
|
||||
kbdev->pm.backend.gpu_in_desired_state_wait,
|
||||
kbdev->pm.backend.l2_state == KBASE_L2_OFF, timeout);
|
||||
#endif
|
||||
|
||||
if (!remaining) {
|
||||
err = -ETIMEDOUT;
|
||||
} else if (remaining < 0) {
|
||||
dev_info(kbdev->dev,
|
||||
"Wait for L2_off got interrupted");
|
||||
err = (int)remaining;
|
||||
}
|
||||
|
||||
dev_dbg(kbdev->dev, "%s: err=%d mcu_state=%d, L2_state=%d\n", __func__,
|
||||
err, kbdev->pm.backend.mcu_state, kbdev->pm.backend.l2_state);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kbase_pm_set_policy(struct kbase_device *kbdev,
|
||||
const struct kbase_pm_policy *new_policy)
|
||||
{
|
||||
const struct kbase_pm_policy *old_policy;
|
||||
unsigned long flags;
|
||||
#if MALI_USE_CSF
|
||||
unsigned int new_policy_csf_pm_sched_flags;
|
||||
bool sched_suspend;
|
||||
bool reset_gpu = false;
|
||||
#endif
|
||||
|
||||
KBASE_DEBUG_ASSERT(kbdev != NULL);
|
||||
KBASE_DEBUG_ASSERT(new_policy != NULL);
|
||||
|
||||
KBASE_KTRACE_ADD(kbdev, PM_SET_POLICY, NULL, new_policy->id);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* Serialize calls on kbase_pm_set_policy() */
|
||||
mutex_lock(&kbdev->pm.backend.policy_change_lock);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
/* policy_change_clamp_state_to_off, when needed, is set/cleared in
|
||||
* this function, a very limited temporal scope for covering the
|
||||
* change transition.
|
||||
*/
|
||||
WARN_ON(kbdev->pm.backend.policy_change_clamp_state_to_off);
|
||||
new_policy_csf_pm_sched_flags = new_policy->pm_sched_flags;
|
||||
|
||||
/* Requiring the scheduler PM suspend operation when changes involving
|
||||
* the always_on policy, reflected by the CSF_DYNAMIC_PM_CORE_KEEP_ON
|
||||
* flag bit.
|
||||
*/
|
||||
sched_suspend = kbdev->csf.firmware_inited &&
|
||||
(CSF_DYNAMIC_PM_CORE_KEEP_ON &
|
||||
(new_policy_csf_pm_sched_flags |
|
||||
kbdev->pm.backend.csf_pm_sched_flags));
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
if (sched_suspend)
|
||||
kbase_csf_scheduler_pm_suspend(kbdev);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
/* If the current active policy is always_on, one needs to clamp the
|
||||
* MCU/L2 for reaching off-state
|
||||
*/
|
||||
if (sched_suspend)
|
||||
kbdev->pm.backend.policy_change_clamp_state_to_off =
|
||||
CSF_DYNAMIC_PM_CORE_KEEP_ON & kbdev->pm.backend.csf_pm_sched_flags;
|
||||
|
||||
kbase_pm_update_state(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
if (sched_suspend)
|
||||
reset_gpu = policy_change_wait_for_L2_off(kbdev);
|
||||
#endif
|
||||
|
||||
/* During a policy change we pretend the GPU is active */
|
||||
/* A suspend won't happen here, because we're in a syscall from a
|
||||
* userspace thread */
|
||||
* userspace thread
|
||||
*/
|
||||
kbase_pm_context_active(kbdev);
|
||||
|
||||
kbase_pm_lock(kbdev);
|
||||
@@ -250,19 +379,40 @@ void kbase_pm_set_policy(struct kbase_device *kbdev,
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kbdev->pm.backend.pm_current_policy = new_policy;
|
||||
#if MALI_USE_CSF
|
||||
kbdev->pm.backend.csf_pm_sched_flags = new_policy_csf_pm_sched_flags;
|
||||
/* New policy in place, release the clamping on mcu/L2 off state */
|
||||
kbdev->pm.backend.policy_change_clamp_state_to_off = false;
|
||||
kbase_pm_update_state(kbdev);
|
||||
#endif
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
/* If any core power state changes were previously attempted, but
|
||||
* couldn't be made because the policy was changing (current_policy was
|
||||
* NULL), then re-try them here. */
|
||||
* NULL), then re-try them here.
|
||||
*/
|
||||
kbase_pm_update_active(kbdev);
|
||||
kbase_pm_update_cores_state(kbdev);
|
||||
|
||||
kbase_pm_unlock(kbdev);
|
||||
|
||||
/* Now the policy change is finished, we release our fake context active
|
||||
* reference */
|
||||
* reference
|
||||
*/
|
||||
kbase_pm_context_idle(kbdev);
|
||||
|
||||
#if MALI_USE_CSF
|
||||
/* Reverse the suspension done */
|
||||
if (reset_gpu) {
|
||||
dev_warn(kbdev->dev, "Resorting to GPU reset for policy change\n");
|
||||
if (kbase_prepare_to_reset_gpu(kbdev, RESET_FLAGS_NONE))
|
||||
kbase_reset_gpu(kbdev);
|
||||
kbase_reset_gpu_wait(kbdev);
|
||||
} else if (sched_suspend)
|
||||
kbase_csf_scheduler_pm_resume(kbdev);
|
||||
|
||||
mutex_unlock(&kbdev->pm.backend.policy_change_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
KBASE_EXPORT_TEST_API(kbase_pm_set_policy);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2010-2015, 2018-2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2010-2015, 2018-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2018-2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,6 +25,41 @@
|
||||
* including this header file. This header file can be included multiple
|
||||
* times in the same compilation unit with different definitions of
|
||||
* KBASEP_SHADER_STATE().
|
||||
*
|
||||
* @OFF_CORESTACK_OFF: The shaders and core stacks are off
|
||||
* @OFF_CORESTACK_PEND_ON: The shaders are off, core stacks have been
|
||||
* requested to power on and hwcnt is being
|
||||
* disabled
|
||||
* @PEND_ON_CORESTACK_ON: Core stacks are on, shaders have been
|
||||
* requested to power on. Or after doing
|
||||
* partial shader on/off, checking whether
|
||||
* it's the desired state.
|
||||
* @ON_CORESTACK_ON: The shaders and core stacks are on, and
|
||||
* hwcnt already enabled.
|
||||
* @ON_CORESTACK_ON_RECHECK: The shaders and core stacks are on, hwcnt
|
||||
* disabled, and checks to powering down or
|
||||
* re-enabling hwcnt.
|
||||
* @WAIT_OFF_CORESTACK_ON: The shaders have been requested to power
|
||||
* off, but they remain on for the duration
|
||||
* of the hysteresis timer
|
||||
* @WAIT_GPU_IDLE: The shaders partial poweroff needs to
|
||||
* reach a state where jobs on the GPU are
|
||||
* finished including jobs currently running
|
||||
* and in the GPU queue because of
|
||||
* GPU2017-861
|
||||
* @WAIT_FINISHED_CORESTACK_ON: The hysteresis timer has expired
|
||||
* @L2_FLUSHING_CORESTACK_ON: The core stacks are on and the level 2
|
||||
* cache is being flushed.
|
||||
* @READY_OFF_CORESTACK_ON: The core stacks are on and the shaders are
|
||||
* ready to be powered off.
|
||||
* @PEND_OFF_CORESTACK_ON: The core stacks are on, and the shaders
|
||||
* have been requested to power off
|
||||
* @OFF_CORESTACK_PEND_OFF: The shaders are off, and the core stacks
|
||||
* have been requested to power off
|
||||
* @OFF_CORESTACK_OFF_TIMER_PEND_OFF: Shaders and corestacks are off, but the
|
||||
* tick timer cancellation is still pending.
|
||||
* @RESET_WAIT: The GPU is resetting, shader and core
|
||||
* stack power states are unknown
|
||||
*/
|
||||
KBASEP_SHADER_STATE(OFF_CORESTACK_OFF)
|
||||
KBASEP_SHADER_STATE(OFF_CORESTACK_PEND_ON)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2014-2016,2018-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2014-2016, 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
@@ -67,14 +66,47 @@ void kbase_backend_get_gpu_time_norequest(struct kbase_device *kbdev,
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !MALI_USE_CSF
|
||||
/**
|
||||
* timedwait_cycle_count_active() - Timed wait till CYCLE_COUNT_ACTIVE is active
|
||||
*
|
||||
* @kbdev: Kbase device
|
||||
*
|
||||
* Return: true if CYCLE_COUNT_ACTIVE is active within the timeout.
|
||||
*/
|
||||
static bool timedwait_cycle_count_active(struct kbase_device *kbdev)
|
||||
{
|
||||
#ifdef CONFIG_MALI_BIFROST_NO_MALI
|
||||
return true;
|
||||
#else
|
||||
bool success = false;
|
||||
const unsigned int timeout = 100;
|
||||
const unsigned long remaining = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
while (time_is_after_jiffies(remaining)) {
|
||||
if ((kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_STATUS)) &
|
||||
GPU_STATUS_CYCLE_COUNT_ACTIVE)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void kbase_backend_get_gpu_time(struct kbase_device *kbdev, u64 *cycle_counter,
|
||||
u64 *system_time, struct timespec64 *ts)
|
||||
{
|
||||
#if !MALI_USE_CSF
|
||||
kbase_pm_request_gpu_cycle_counter(kbdev);
|
||||
WARN_ONCE(kbdev->pm.backend.l2_state != KBASE_L2_ON,
|
||||
"L2 not powered up");
|
||||
WARN_ONCE((!timedwait_cycle_count_active(kbdev)),
|
||||
"Timed out on CYCLE_COUNT_ACTIVE");
|
||||
#endif
|
||||
kbase_backend_get_gpu_time_norequest(
|
||||
kbdev, cycle_counter, system_time, ts);
|
||||
kbase_backend_get_gpu_time_norequest(kbdev, cycle_counter, system_time,
|
||||
ts);
|
||||
#if !MALI_USE_CSF
|
||||
kbase_pm_release_gpu_cycle_counter(kbdev);
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2017-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2017-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* 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.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -135,8 +141,11 @@ bob_kernel_module {
|
||||
cinstr_secondary_hwc: {
|
||||
kbuild_options: ["CONFIG_MALI_BIFROST_PRFCNT_SET_SECONDARY=y"],
|
||||
},
|
||||
cinstr_secondary_hwc_via_debug_fs: {
|
||||
kbuild_options: ["CONFIG_MALI_PRFCNT_SET_SECONDARY_VIA_DEBUG_FS=y"],
|
||||
cinstr_tertiary_hwc: {
|
||||
kbuild_options: ["CONFIG_MALI_PRFCNT_SET_TERTIARY=y"],
|
||||
},
|
||||
cinstr_hwc_set_select_via_debug_fs: {
|
||||
kbuild_options: ["CONFIG_MALI_PRFCNT_SET_SELECT_VIA_DEBUG_FS=y"],
|
||||
},
|
||||
mali_2mb_alloc: {
|
||||
kbuild_options: ["CONFIG_MALI_2MB_ALLOC=y"],
|
||||
@@ -158,14 +167,20 @@ bob_kernel_module {
|
||||
"jm/*.h",
|
||||
"tl/backend/*_jm.c",
|
||||
"mmu/backend/*_jm.c",
|
||||
"ipa/backend/*_jm.c",
|
||||
"ipa/backend/*_jm.h",
|
||||
],
|
||||
},
|
||||
gpu_has_csf: {
|
||||
kbuild_options: ["CONFIG_MALI_CSF_SUPPORT=y"],
|
||||
srcs: [
|
||||
"context/backend/*_csf.c",
|
||||
"csf/*.c",
|
||||
"csf/*.h",
|
||||
"csf/Kbuild",
|
||||
"csf/ipa_control/*.c",
|
||||
"csf/ipa_control/*.h",
|
||||
"csf/ipa_control/Kbuild",
|
||||
"debug/backend/*_csf.c",
|
||||
"debug/backend/*_csf.h",
|
||||
"device/backend/*_csf.c",
|
||||
@@ -173,6 +188,8 @@ bob_kernel_module {
|
||||
"gpu/backend/*_csf.h",
|
||||
"tl/backend/*_csf.c",
|
||||
"mmu/backend/*_csf.c",
|
||||
"ipa/backend/*_csf.c",
|
||||
"ipa/backend/*_csf.h",
|
||||
],
|
||||
},
|
||||
mali_arbiter_support: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -28,18 +26,17 @@
|
||||
#include <context/mali_kbase_context_internal.h>
|
||||
#include <gpu/mali_kbase_gpu_regmap.h>
|
||||
#include <mali_kbase.h>
|
||||
#include <mali_kbase_ctx_sched.h>
|
||||
#include <mali_kbase_dma_fence.h>
|
||||
#include <mali_kbase_mem_linux.h>
|
||||
#include <mali_kbase_mem_pool_group.h>
|
||||
#include <mmu/mali_kbase_mmu.h>
|
||||
#include <tl/mali_kbase_timeline.h>
|
||||
#include <tl/mali_kbase_tracepoints.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <csf/mali_kbase_csf_csg_debugfs.h>
|
||||
#include <csf/mali_kbase_csf_kcpu_debugfs.h>
|
||||
#include <csf/mali_kbase_csf_tiler_heap_debugfs.h>
|
||||
#include <csf/mali_kbase_csf_cpu_queue_debugfs.h>
|
||||
#include <mali_kbase_debug_mem_view.h>
|
||||
#include <mali_kbase_mem_pool_debugfs.h>
|
||||
|
||||
@@ -51,6 +48,7 @@ void kbase_context_debugfs_init(struct kbase_context *const kctx)
|
||||
kbase_csf_queue_group_debugfs_init(kctx);
|
||||
kbase_csf_kcpu_debugfs_init(kctx);
|
||||
kbase_csf_tiler_heap_debugfs_init(kctx);
|
||||
kbase_csf_cpu_queue_debugfs_init(kctx);
|
||||
}
|
||||
KBASE_EXPORT_SYMBOL(kbase_context_debugfs_init);
|
||||
|
||||
@@ -73,24 +71,34 @@ void kbase_context_debugfs_term(struct kbase_context *const kctx)
|
||||
KBASE_EXPORT_SYMBOL(kbase_context_debugfs_term);
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static void kbase_context_free(struct kbase_context *kctx)
|
||||
{
|
||||
kbase_timeline_post_kbase_context_destroy(kctx);
|
||||
|
||||
vfree(kctx);
|
||||
}
|
||||
|
||||
static const struct kbase_context_init context_init[] = {
|
||||
{kbase_context_common_init, kbase_context_common_term, NULL},
|
||||
{kbase_context_mem_pool_group_init, kbase_context_mem_pool_group_term,
|
||||
"Memory pool goup initialization failed"},
|
||||
{kbase_mem_evictable_init, kbase_mem_evictable_deinit,
|
||||
"Memory evictable initialization failed"},
|
||||
{kbase_context_mmu_init, kbase_context_mmu_term,
|
||||
"MMU initialization failed"},
|
||||
{kbase_context_mem_alloc_page, kbase_context_mem_pool_free,
|
||||
"Memory alloc page failed"},
|
||||
{kbase_region_tracker_init, kbase_region_tracker_term,
|
||||
"Region tracker initialization failed"},
|
||||
{kbase_sticky_resource_init, kbase_context_sticky_resource_term,
|
||||
"Sticky resource initialization failed"},
|
||||
{kbase_jit_init, kbase_jit_term,
|
||||
"JIT initialization failed"},
|
||||
{kbase_csf_ctx_init, kbase_csf_ctx_term,
|
||||
"CSF context initialization failed"},
|
||||
{ NULL, kbase_context_free, NULL },
|
||||
{ kbase_context_common_init, kbase_context_common_term,
|
||||
"Common context initialization failed" },
|
||||
{ kbase_context_mem_pool_group_init, kbase_context_mem_pool_group_term,
|
||||
"Memory pool group initialization failed" },
|
||||
{ kbase_mem_evictable_init, kbase_mem_evictable_deinit,
|
||||
"Memory evictable initialization failed" },
|
||||
{ kbase_context_mmu_init, kbase_context_mmu_term,
|
||||
"MMU initialization failed" },
|
||||
{ kbase_context_mem_alloc_page, kbase_context_mem_pool_free,
|
||||
"Memory alloc page failed" },
|
||||
{ kbase_region_tracker_init, kbase_region_tracker_term,
|
||||
"Region tracker initialization failed" },
|
||||
{ kbase_sticky_resource_init, kbase_context_sticky_resource_term,
|
||||
"Sticky resource initialization failed" },
|
||||
{ kbase_jit_init, kbase_jit_term, "JIT initialization failed" },
|
||||
{ kbase_csf_ctx_init, kbase_csf_ctx_term,
|
||||
"CSF context initialization failed" },
|
||||
{ kbase_context_add_to_dev_list, kbase_context_remove_from_dev_list,
|
||||
"Adding kctx to device failed" },
|
||||
};
|
||||
|
||||
static void kbase_context_term_partial(
|
||||
@@ -134,14 +142,23 @@ struct kbase_context *kbase_create_context(struct kbase_device *kbdev,
|
||||
#if defined(CONFIG_64BIT)
|
||||
else
|
||||
kbase_ctx_flag_set(kctx, KCTX_FORCE_SAME_VA);
|
||||
#endif /* !defined(CONFIG_64BIT) */
|
||||
#endif /* defined(CONFIG_64BIT) */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(context_init); i++) {
|
||||
int err = context_init[i].init(kctx);
|
||||
int err = 0;
|
||||
|
||||
if (context_init[i].init)
|
||||
err = context_init[i].init(kctx);
|
||||
|
||||
if (err) {
|
||||
dev_err(kbdev->dev, "%s error = %d\n",
|
||||
context_init[i].err_mes, err);
|
||||
|
||||
/* kctx should be freed by kbase_context_free().
|
||||
* Otherwise it will result in memory leak.
|
||||
*/
|
||||
WARN_ON(i == 0);
|
||||
|
||||
kbase_context_term_partial(kctx, i);
|
||||
return NULL;
|
||||
}
|
||||
@@ -162,11 +179,18 @@ void kbase_destroy_context(struct kbase_context *kctx)
|
||||
if (WARN_ON(!kbdev))
|
||||
return;
|
||||
|
||||
/* Ensure the core is powered up for the destroy process
|
||||
* A suspend won't happen here, because we're in a syscall
|
||||
* from a userspace thread.
|
||||
/* Context termination could happen whilst the system suspend of
|
||||
* the GPU device is ongoing or has completed. It has been seen on
|
||||
* Customer side that a hang could occur if context termination is
|
||||
* not blocked until the resume of GPU device.
|
||||
*/
|
||||
kbase_pm_context_active(kbdev);
|
||||
while (kbase_pm_context_active_handle_suspend(
|
||||
kbdev, KBASE_PM_SUSPEND_HANDLER_DONT_INCREASE)) {
|
||||
dev_info(kbdev->dev,
|
||||
"Suspend in progress when destroying context");
|
||||
wait_event(kbdev->pm.resume_wait,
|
||||
!kbase_pm_is_suspending(kbdev));
|
||||
}
|
||||
|
||||
kbase_mem_pool_group_mark_dying(&kctx->mem_pools);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -35,7 +33,6 @@
|
||||
#include <mali_kbase_mem_pool_group.h>
|
||||
#include <mmu/mali_kbase_mmu.h>
|
||||
#include <tl/mali_kbase_timeline.h>
|
||||
#include <tl/mali_kbase_tracepoints.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <mali_kbase_debug_mem_view.h>
|
||||
@@ -47,14 +44,12 @@ void kbase_context_debugfs_init(struct kbase_context *const kctx)
|
||||
kbase_mem_pool_debugfs_init(kctx->kctx_dentry, kctx);
|
||||
kbase_jit_debugfs_init(kctx);
|
||||
kbasep_jd_debugfs_ctx_init(kctx);
|
||||
kbase_debug_job_fault_context_init(kctx);
|
||||
}
|
||||
KBASE_EXPORT_SYMBOL(kbase_context_debugfs_init);
|
||||
|
||||
void kbase_context_debugfs_term(struct kbase_context *const kctx)
|
||||
{
|
||||
debugfs_remove_recursive(kctx->kctx_dentry);
|
||||
kbase_debug_job_fault_context_term(kctx);
|
||||
}
|
||||
KBASE_EXPORT_SYMBOL(kbase_context_debugfs_term);
|
||||
#else
|
||||
@@ -73,12 +68,7 @@ KBASE_EXPORT_SYMBOL(kbase_context_debugfs_term);
|
||||
|
||||
static int kbase_context_kbase_kinstr_jm_init(struct kbase_context *kctx)
|
||||
{
|
||||
int ret = kbase_kinstr_jm_init(&kctx->kinstr_jm);
|
||||
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return kbase_kinstr_jm_init(&kctx->kinstr_jm);
|
||||
}
|
||||
|
||||
static void kbase_context_kbase_kinstr_jm_term(struct kbase_context *kctx)
|
||||
@@ -114,12 +104,27 @@ static int kbase_context_submit_check(struct kbase_context *kctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbase_context_flush_jobs(struct kbase_context *kctx)
|
||||
{
|
||||
kbase_jd_zap_context(kctx);
|
||||
flush_workqueue(kctx->jctx.job_done_wq);
|
||||
}
|
||||
|
||||
static void kbase_context_free(struct kbase_context *kctx)
|
||||
{
|
||||
kbase_timeline_post_kbase_context_destroy(kctx);
|
||||
|
||||
vfree(kctx);
|
||||
}
|
||||
|
||||
static const struct kbase_context_init context_init[] = {
|
||||
{ kbase_context_common_init, kbase_context_common_term, NULL },
|
||||
{ NULL, kbase_context_free, NULL },
|
||||
{ kbase_context_common_init, kbase_context_common_term,
|
||||
"Common context initialization failed" },
|
||||
{ kbase_dma_fence_init, kbase_dma_fence_term,
|
||||
"DMA fence initialization failed" },
|
||||
{ kbase_context_mem_pool_group_init, kbase_context_mem_pool_group_term,
|
||||
"Memory pool goup initialization failed" },
|
||||
"Memory pool group initialization failed" },
|
||||
{ kbase_mem_evictable_init, kbase_mem_evictable_deinit,
|
||||
"Memory evictable initialization failed" },
|
||||
{ kbase_context_mmu_init, kbase_context_mmu_term,
|
||||
@@ -134,13 +139,22 @@ static const struct kbase_context_init context_init[] = {
|
||||
{ kbase_context_kbase_kinstr_jm_init,
|
||||
kbase_context_kbase_kinstr_jm_term,
|
||||
"JM instrumentation initialization failed" },
|
||||
{ kbase_context_kbase_timer_setup, NULL, NULL },
|
||||
{ kbase_context_kbase_timer_setup, NULL,
|
||||
"Timers initialization failed" },
|
||||
{ kbase_event_init, kbase_event_cleanup,
|
||||
"Event initialization failed" },
|
||||
{ kbasep_js_kctx_init, kbasep_js_kctx_term,
|
||||
"JS kctx initialization failed" },
|
||||
{ kbase_jd_init, kbase_jd_exit, "JD initialization failed" },
|
||||
{ kbase_context_submit_check, NULL, NULL },
|
||||
{ kbase_context_submit_check, NULL, "Enabling job submission failed" },
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
{ kbase_debug_job_fault_context_init,
|
||||
kbase_debug_job_fault_context_term,
|
||||
"Job fault context initialization failed" },
|
||||
#endif
|
||||
{ NULL, kbase_context_flush_jobs, NULL },
|
||||
{ kbase_context_add_to_dev_list, kbase_context_remove_from_dev_list,
|
||||
"Adding kctx to device failed" },
|
||||
};
|
||||
|
||||
static void kbase_context_term_partial(
|
||||
@@ -184,14 +198,23 @@ struct kbase_context *kbase_create_context(struct kbase_device *kbdev,
|
||||
#if defined(CONFIG_64BIT)
|
||||
else
|
||||
kbase_ctx_flag_set(kctx, KCTX_FORCE_SAME_VA);
|
||||
#endif /* !defined(CONFIG_64BIT) */
|
||||
#endif /* defined(CONFIG_64BIT) */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(context_init); i++) {
|
||||
int err = context_init[i].init(kctx);
|
||||
int err = 0;
|
||||
|
||||
if (context_init[i].init)
|
||||
err = context_init[i].init(kctx);
|
||||
|
||||
if (err) {
|
||||
dev_err(kbdev->dev, "%s error = %d\n",
|
||||
context_init[i].err_mes, err);
|
||||
|
||||
/* kctx should be freed by kbase_context_free().
|
||||
* Otherwise it will result in memory leak.
|
||||
*/
|
||||
WARN_ON(i == 0);
|
||||
|
||||
kbase_context_term_partial(kctx, i);
|
||||
return NULL;
|
||||
}
|
||||
@@ -212,17 +235,27 @@ void kbase_destroy_context(struct kbase_context *kctx)
|
||||
if (WARN_ON(!kbdev))
|
||||
return;
|
||||
|
||||
/* Ensure the core is powered up for the destroy process
|
||||
* A suspend won't happen here, because we're in a syscall
|
||||
* from a userspace thread.
|
||||
/* Context termination could happen whilst the system suspend of
|
||||
* the GPU device is ongoing or has completed. It has been seen on
|
||||
* Customer side that a hang could occur if context termination is
|
||||
* not blocked until the resume of GPU device.
|
||||
*/
|
||||
kbase_pm_context_active(kbdev);
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
atomic_inc(&kbdev->pm.gpu_users_waiting);
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
while (kbase_pm_context_active_handle_suspend(
|
||||
kbdev, KBASE_PM_SUSPEND_HANDLER_DONT_INCREASE)) {
|
||||
dev_dbg(kbdev->dev,
|
||||
"Suspend in progress when destroying context");
|
||||
wait_event(kbdev->pm.resume_wait,
|
||||
!kbase_pm_is_suspending(kbdev));
|
||||
}
|
||||
#ifdef CONFIG_MALI_ARBITER_SUPPORT
|
||||
atomic_dec(&kbdev->pm.gpu_users_waiting);
|
||||
#endif /* CONFIG_MALI_ARBITER_SUPPORT */
|
||||
|
||||
kbase_mem_pool_group_mark_dying(&kctx->mem_pools);
|
||||
|
||||
kbase_jd_zap_context(kctx);
|
||||
flush_workqueue(kctx->jctx.job_done_wq);
|
||||
|
||||
kbase_context_term_partial(kctx, ARRAY_SIZE(context_init));
|
||||
|
||||
kbase_pm_context_idle(kbdev);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -28,10 +26,8 @@
|
||||
#include <mali_kbase.h>
|
||||
#include <gpu/mali_kbase_gpu_regmap.h>
|
||||
#include <mali_kbase_mem_linux.h>
|
||||
#include <mali_kbase_dma_fence.h>
|
||||
#include <mali_kbase_ctx_sched.h>
|
||||
#include <mali_kbase_mem_pool_group.h>
|
||||
#include <tl/mali_kbase_tracepoints.h>
|
||||
#include <tl/mali_kbase_timeline.h>
|
||||
#include <mmu/mali_kbase_mmu.h>
|
||||
#include <context/mali_kbase_context_internal.h>
|
||||
@@ -170,22 +166,49 @@ int kbase_context_common_init(struct kbase_context *kctx)
|
||||
mutex_init(&kctx->legacy_hwcnt_lock);
|
||||
|
||||
mutex_lock(&kctx->kbdev->kctx_list_lock);
|
||||
list_add(&kctx->kctx_list_link, &kctx->kbdev->kctx_list);
|
||||
|
||||
err = kbase_insert_kctx_to_process(kctx);
|
||||
if (err)
|
||||
dev_err(kctx->kbdev->dev,
|
||||
"(err:%d) failed to insert kctx to kbase_process\n", err);
|
||||
|
||||
KBASE_TLSTREAM_TL_KBASE_NEW_CTX(kctx->kbdev, kctx->id,
|
||||
kctx->kbdev->gpu_props.props.raw_props.gpu_id);
|
||||
KBASE_TLSTREAM_TL_NEW_CTX(kctx->kbdev, kctx, kctx->id,
|
||||
(u32)(kctx->tgid));
|
||||
mutex_unlock(&kctx->kbdev->kctx_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int kbase_context_add_to_dev_list(struct kbase_context *kctx)
|
||||
{
|
||||
if (WARN_ON(!kctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(!kctx->kbdev))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kctx->kbdev->kctx_list_lock);
|
||||
list_add(&kctx->kctx_list_link, &kctx->kbdev->kctx_list);
|
||||
mutex_unlock(&kctx->kbdev->kctx_list_lock);
|
||||
|
||||
kbase_timeline_post_kbase_context_create(kctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kbase_context_remove_from_dev_list(struct kbase_context *kctx)
|
||||
{
|
||||
if (WARN_ON(!kctx))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!kctx->kbdev))
|
||||
return;
|
||||
|
||||
kbase_timeline_pre_kbase_context_destroy(kctx);
|
||||
|
||||
mutex_lock(&kctx->kbdev->kctx_list_lock);
|
||||
list_del_init(&kctx->kctx_list_link);
|
||||
mutex_unlock(&kctx->kbdev->kctx_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_remove_kctx_from_process - remove a terminating context from
|
||||
* the process list.
|
||||
@@ -238,24 +261,9 @@ void kbase_context_common_term(struct kbase_context *kctx)
|
||||
|
||||
mutex_lock(&kctx->kbdev->kctx_list_lock);
|
||||
kbase_remove_kctx_from_process(kctx);
|
||||
|
||||
KBASE_TLSTREAM_TL_KBASE_DEL_CTX(kctx->kbdev, kctx->id);
|
||||
|
||||
KBASE_TLSTREAM_TL_DEL_CTX(kctx->kbdev, kctx);
|
||||
list_del(&kctx->kctx_list_link);
|
||||
mutex_unlock(&kctx->kbdev->kctx_list_lock);
|
||||
|
||||
KBASE_KTRACE_ADD(kctx->kbdev, CORE_CTX_DESTROY, kctx, 0u);
|
||||
|
||||
/* Flush the timeline stream, so the user can see the termination
|
||||
* tracepoints being fired.
|
||||
* The "if" statement below is for optimization. It is safe to call
|
||||
* kbase_timeline_streams_flush when timeline is disabled.
|
||||
*/
|
||||
if (atomic_read(&kctx->kbdev->timeline_flags) != 0)
|
||||
kbase_timeline_streams_flush(kctx->kbdev->timeline);
|
||||
|
||||
vfree(kctx);
|
||||
}
|
||||
|
||||
int kbase_context_mem_pool_group_init(struct kbase_context *kctx)
|
||||
@@ -273,11 +281,9 @@ void kbase_context_mem_pool_group_term(struct kbase_context *kctx)
|
||||
|
||||
int kbase_context_mmu_init(struct kbase_context *kctx)
|
||||
{
|
||||
kbase_mmu_init(kctx->kbdev,
|
||||
&kctx->mmu, kctx,
|
||||
return kbase_mmu_init(
|
||||
kctx->kbdev, &kctx->mmu, kctx,
|
||||
base_context_mmu_group_id_get(kctx->create_flags));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kbase_context_mmu_term(struct kbase_context *kctx)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2011-2017, 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,18 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2011-2017, 2019 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CONTEXT_H_
|
||||
@@ -117,25 +106,7 @@ static inline bool kbase_ctx_flag(struct kbase_context *kctx,
|
||||
static inline void kbase_ctx_flag_clear(struct kbase_context *kctx,
|
||||
enum kbase_context_flags flag)
|
||||
{
|
||||
#if KERNEL_VERSION(4, 3, 0) > LINUX_VERSION_CODE
|
||||
/*
|
||||
* Earlier kernel versions doesn't have atomic_andnot() or
|
||||
* atomic_and(). atomic_clear_mask() was only available on some
|
||||
* architectures and removed on arm in v3.13 on arm and arm64.
|
||||
*
|
||||
* Use a compare-exchange loop to clear the flag on pre 4.3 kernels,
|
||||
* when atomic_andnot() becomes available.
|
||||
*/
|
||||
int old, new;
|
||||
|
||||
do {
|
||||
old = atomic_read(&kctx->flags);
|
||||
new = old & ~flag;
|
||||
|
||||
} while (atomic_cmpxchg(&kctx->flags, old, new) != old);
|
||||
#else
|
||||
atomic_andnot(flag, &kctx->flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,16 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*//* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* (C) COPYRIGHT 2019 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.
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
@@ -58,3 +49,6 @@ int kbase_context_mem_alloc_page(struct kbase_context *kctx);
|
||||
void kbase_context_mem_pool_free(struct kbase_context *kctx);
|
||||
|
||||
void kbase_context_sticky_resource_term(struct kbase_context *kctx);
|
||||
|
||||
int kbase_context_add_to_dev_list(struct kbase_context *kctx);
|
||||
void kbase_context_remove_from_dev_list(struct kbase_context *kctx);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2018-2020 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.
|
||||
# of such GNU license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -15,11 +16,9 @@
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
mali_kbase-y += \
|
||||
bifrost_kbase-y += \
|
||||
csf/mali_kbase_csf_firmware_cfg.o \
|
||||
csf/mali_kbase_csf_trace_buffer.o \
|
||||
csf/mali_kbase_csf.o \
|
||||
@@ -33,8 +32,11 @@ mali_kbase-y += \
|
||||
csf/mali_kbase_csf_csg_debugfs.o \
|
||||
csf/mali_kbase_csf_kcpu_debugfs.o \
|
||||
csf/mali_kbase_csf_protected_memory.o \
|
||||
csf/mali_kbase_csf_tiler_heap_debugfs.o
|
||||
csf/mali_kbase_csf_tiler_heap_debugfs.o \
|
||||
csf/mali_kbase_csf_cpu_queue_debugfs.o
|
||||
|
||||
mali_kbase-$(CONFIG_MALI_REAL_HW) += csf/mali_kbase_csf_firmware.o
|
||||
bifrost_kbase-$(CONFIG_MALI_REAL_HW) += csf/mali_kbase_csf_firmware.o
|
||||
|
||||
mali_kbase-$(CONFIG_MALI_BIFROST_NO_MALI) += csf/mali_kbase_csf_firmware_no_mali.o
|
||||
bifrost_kbase-$(CONFIG_MALI_BIFROST_NO_MALI) += csf/mali_kbase_csf_firmware_no_mali.o
|
||||
|
||||
include $(src)/csf/ipa_control/Kbuild
|
||||
|
||||
22
drivers/gpu/arm/bifrost/csf/ipa_control/Kbuild
Normal file
22
drivers/gpu/arm/bifrost/csf/ipa_control/Kbuild
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# (C) COPYRIGHT 2020 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 license.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
#
|
||||
|
||||
bifrost_kbase-y += \
|
||||
csf/ipa_control/mali_kbase_csf_ipa_control.o
|
||||
@@ -0,0 +1,925 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
#include "mali_kbase_clk_rate_trace_mgr.h"
|
||||
#include "mali_kbase_csf_ipa_control.h"
|
||||
|
||||
/*
|
||||
* Status flags from the STATUS register of the IPA Control interface.
|
||||
*/
|
||||
#define STATUS_COMMAND_ACTIVE ((u32)1 << 0)
|
||||
#define STATUS_TIMER_ACTIVE ((u32)1 << 1)
|
||||
#define STATUS_AUTO_ACTIVE ((u32)1 << 2)
|
||||
#define STATUS_PROTECTED_MODE ((u32)1 << 8)
|
||||
#define STATUS_RESET ((u32)1 << 9)
|
||||
#define STATUS_TIMER_ENABLED ((u32)1 << 31)
|
||||
|
||||
/*
|
||||
* Commands for the COMMAND register of the IPA Control interface.
|
||||
*/
|
||||
#define COMMAND_NOP ((u32)0)
|
||||
#define COMMAND_APPLY ((u32)1)
|
||||
#define COMMAND_CLEAR ((u32)2)
|
||||
#define COMMAND_SAMPLE ((u32)3)
|
||||
#define COMMAND_PROTECTED_ACK ((u32)4)
|
||||
#define COMMAND_RESET_ACK ((u32)5)
|
||||
|
||||
/**
|
||||
* Default value for the TIMER register of the IPA Control interface,
|
||||
* expressed in milliseconds.
|
||||
*
|
||||
* The chosen value is a trade off between two requirements: the IPA Control
|
||||
* interface should sample counters with a resolution in the order of
|
||||
* milliseconds, while keeping GPU overhead as limited as possible.
|
||||
*/
|
||||
#define TIMER_DEFAULT_VALUE_MS ((u32)10) /* 10 milliseconds */
|
||||
|
||||
/**
|
||||
* Number of timer events per second.
|
||||
*/
|
||||
#define TIMER_EVENTS_PER_SECOND ((u32)1000 / TIMER_DEFAULT_VALUE_MS)
|
||||
|
||||
/**
|
||||
* Maximum number of loops polling the GPU before we assume the GPU has hung.
|
||||
*/
|
||||
#define IPA_INACTIVE_MAX_LOOPS ((unsigned int)8000000)
|
||||
|
||||
/**
|
||||
* Number of bits used to configure a performance counter in SELECT registers.
|
||||
*/
|
||||
#define IPA_CONTROL_SELECT_BITS_PER_CNT ((u64)8)
|
||||
|
||||
/**
|
||||
* Maximum value of a performance counter.
|
||||
*/
|
||||
#define MAX_PRFCNT_VALUE (((u64)1 << 48) - 1)
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_listener_data - Data for the GPU clock frequency
|
||||
* listener
|
||||
*
|
||||
* @listener: GPU clock frequency listener.
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*/
|
||||
struct kbase_ipa_control_listener_data {
|
||||
struct kbase_clk_rate_listener listener;
|
||||
struct kbase_device *kbdev;
|
||||
};
|
||||
|
||||
static u32 timer_value(u32 gpu_rate)
|
||||
{
|
||||
return gpu_rate / TIMER_EVENTS_PER_SECOND;
|
||||
}
|
||||
|
||||
static int wait_status(struct kbase_device *kbdev, u32 flags)
|
||||
{
|
||||
unsigned int max_loops = IPA_INACTIVE_MAX_LOOPS;
|
||||
u32 status = kbase_reg_read(kbdev, IPA_CONTROL_REG(STATUS));
|
||||
|
||||
/*
|
||||
* Wait for the STATUS register to indicate that flags have been
|
||||
* cleared, in case a transition is pending.
|
||||
*/
|
||||
while (--max_loops && (status & flags))
|
||||
status = kbase_reg_read(kbdev, IPA_CONTROL_REG(STATUS));
|
||||
if (max_loops == 0) {
|
||||
dev_err(kbdev->dev, "IPA_CONTROL STATUS register stuck");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_select_config(struct kbase_device *kbdev, u64 *select)
|
||||
{
|
||||
int ret;
|
||||
|
||||
u32 select_cshw_lo = (u32)(select[KBASE_IPA_CORE_TYPE_CSHW] & U32_MAX);
|
||||
u32 select_cshw_hi =
|
||||
(u32)((select[KBASE_IPA_CORE_TYPE_CSHW] >> 32) & U32_MAX);
|
||||
u32 select_memsys_lo =
|
||||
(u32)(select[KBASE_IPA_CORE_TYPE_MEMSYS] & U32_MAX);
|
||||
u32 select_memsys_hi =
|
||||
(u32)((select[KBASE_IPA_CORE_TYPE_MEMSYS] >> 32) & U32_MAX);
|
||||
u32 select_tiler_lo =
|
||||
(u32)(select[KBASE_IPA_CORE_TYPE_TILER] & U32_MAX);
|
||||
u32 select_tiler_hi =
|
||||
(u32)((select[KBASE_IPA_CORE_TYPE_TILER] >> 32) & U32_MAX);
|
||||
u32 select_shader_lo =
|
||||
(u32)(select[KBASE_IPA_CORE_TYPE_SHADER] & U32_MAX);
|
||||
u32 select_shader_hi =
|
||||
(u32)((select[KBASE_IPA_CORE_TYPE_SHADER] >> 32) & U32_MAX);
|
||||
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_CSHW_LO), select_cshw_lo);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_CSHW_HI), select_cshw_hi);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_MEMSYS_LO),
|
||||
select_memsys_lo);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_MEMSYS_HI),
|
||||
select_memsys_hi);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_TILER_LO),
|
||||
select_tiler_lo);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_TILER_HI),
|
||||
select_tiler_hi);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_SHADER_LO),
|
||||
select_shader_lo);
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(SELECT_SHADER_HI),
|
||||
select_shader_hi);
|
||||
|
||||
ret = wait_status(kbdev, STATUS_COMMAND_ACTIVE);
|
||||
|
||||
if (!ret)
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND), COMMAND_APPLY);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 read_value_cnt(struct kbase_device *kbdev, u8 type, int select_idx)
|
||||
{
|
||||
u32 value_lo, value_hi;
|
||||
|
||||
switch (type) {
|
||||
case KBASE_IPA_CORE_TYPE_CSHW:
|
||||
value_lo = kbase_reg_read(
|
||||
kbdev, IPA_CONTROL_REG(VALUE_CSHW_REG_LO(select_idx)));
|
||||
value_hi = kbase_reg_read(
|
||||
kbdev, IPA_CONTROL_REG(VALUE_CSHW_REG_HI(select_idx)));
|
||||
break;
|
||||
case KBASE_IPA_CORE_TYPE_MEMSYS:
|
||||
value_lo = kbase_reg_read(
|
||||
kbdev,
|
||||
IPA_CONTROL_REG(VALUE_MEMSYS_REG_LO(select_idx)));
|
||||
value_hi = kbase_reg_read(
|
||||
kbdev,
|
||||
IPA_CONTROL_REG(VALUE_MEMSYS_REG_HI(select_idx)));
|
||||
break;
|
||||
case KBASE_IPA_CORE_TYPE_TILER:
|
||||
value_lo = kbase_reg_read(
|
||||
kbdev, IPA_CONTROL_REG(VALUE_TILER_REG_LO(select_idx)));
|
||||
value_hi = kbase_reg_read(
|
||||
kbdev, IPA_CONTROL_REG(VALUE_TILER_REG_HI(select_idx)));
|
||||
break;
|
||||
case KBASE_IPA_CORE_TYPE_SHADER:
|
||||
value_lo = kbase_reg_read(
|
||||
kbdev,
|
||||
IPA_CONTROL_REG(VALUE_SHADER_REG_LO(select_idx)));
|
||||
value_hi = kbase_reg_read(
|
||||
kbdev,
|
||||
IPA_CONTROL_REG(VALUE_SHADER_REG_HI(select_idx)));
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown core type: %u\n", type);
|
||||
value_lo = value_hi = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return (((u64)value_hi << 32) | value_lo);
|
||||
}
|
||||
|
||||
static void build_select_config(struct kbase_ipa_control *ipa_ctrl,
|
||||
u64 *select_config)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < KBASE_IPA_CORE_TYPE_NUM; i++) {
|
||||
size_t j;
|
||||
|
||||
select_config[i] = 0ULL;
|
||||
|
||||
for (j = 0; j < KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS; j++) {
|
||||
struct kbase_ipa_control_prfcnt_config *prfcnt_config =
|
||||
&ipa_ctrl->blocks[i].select[j];
|
||||
|
||||
select_config[i] |=
|
||||
((u64)prfcnt_config->idx
|
||||
<< (IPA_CONTROL_SELECT_BITS_PER_CNT * j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void calc_prfcnt_delta(struct kbase_device *kbdev,
|
||||
struct kbase_ipa_control_prfcnt *prfcnt,
|
||||
bool gpu_ready)
|
||||
{
|
||||
u64 delta_value, raw_value;
|
||||
|
||||
if (gpu_ready)
|
||||
raw_value = read_value_cnt(kbdev, (u8)prfcnt->type,
|
||||
prfcnt->select_idx);
|
||||
else
|
||||
raw_value = prfcnt->latest_raw_value;
|
||||
|
||||
if (raw_value < prfcnt->latest_raw_value) {
|
||||
delta_value = (MAX_PRFCNT_VALUE - prfcnt->latest_raw_value) +
|
||||
raw_value;
|
||||
} else {
|
||||
delta_value = raw_value - prfcnt->latest_raw_value;
|
||||
}
|
||||
|
||||
delta_value *= prfcnt->scaling_factor;
|
||||
|
||||
if (!WARN_ON_ONCE(kbdev->csf.ipa_control.cur_gpu_rate == 0))
|
||||
if (prfcnt->gpu_norm)
|
||||
delta_value /= kbdev->csf.ipa_control.cur_gpu_rate;
|
||||
|
||||
prfcnt->latest_raw_value = raw_value;
|
||||
|
||||
/* Accumulate the difference */
|
||||
prfcnt->accumulated_diff += delta_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_rate_change_notify - GPU frequency change callback
|
||||
*
|
||||
* @listener: Clock frequency change listener.
|
||||
* @clk_index: Index of the clock for which the change has occurred.
|
||||
* @clk_rate_hz: Clock frequency(Hz).
|
||||
*
|
||||
* This callback notifies kbase_ipa_control about GPU frequency changes.
|
||||
* Only top-level clock changes are meaningful. GPU frequency updates
|
||||
* affect all performance counters which require GPU normalization
|
||||
* in every session.
|
||||
*/
|
||||
static void
|
||||
kbase_ipa_control_rate_change_notify(struct kbase_clk_rate_listener *listener,
|
||||
u32 clk_index, u32 clk_rate_hz)
|
||||
{
|
||||
if ((clk_index == KBASE_CLOCK_DOMAIN_TOP) && (clk_rate_hz != 0)) {
|
||||
size_t i;
|
||||
unsigned long flags;
|
||||
struct kbase_ipa_control_listener_data *listener_data =
|
||||
container_of(listener,
|
||||
struct kbase_ipa_control_listener_data,
|
||||
listener);
|
||||
struct kbase_device *kbdev = listener_data->kbdev;
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
if (!kbdev->pm.backend.gpu_ready) {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: GPU frequency cannot change while GPU is off",
|
||||
__func__);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Interrupts are already disabled and interrupt state is also saved */
|
||||
spin_lock(&ipa_ctrl->lock);
|
||||
|
||||
for (i = 0; i < ipa_ctrl->num_active_sessions; i++) {
|
||||
size_t j;
|
||||
struct kbase_ipa_control_session *session = &ipa_ctrl->sessions[i];
|
||||
|
||||
for (j = 0; j < session->num_prfcnts; j++) {
|
||||
struct kbase_ipa_control_prfcnt *prfcnt =
|
||||
&session->prfcnts[j];
|
||||
|
||||
if (prfcnt->gpu_norm)
|
||||
calc_prfcnt_delta(kbdev, prfcnt, true);
|
||||
}
|
||||
}
|
||||
|
||||
ipa_ctrl->cur_gpu_rate = clk_rate_hz;
|
||||
|
||||
/* Update the timer for automatic sampling if active sessions
|
||||
* are present. Counters have already been manually sampled.
|
||||
*/
|
||||
if (ipa_ctrl->num_active_sessions > 0) {
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(TIMER),
|
||||
timer_value(ipa_ctrl->cur_gpu_rate));
|
||||
}
|
||||
|
||||
spin_unlock(&ipa_ctrl->lock);
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void kbase_ipa_control_init(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
struct kbase_clk_rate_trace_manager *clk_rtm = &kbdev->pm.clk_rtm;
|
||||
struct kbase_ipa_control_listener_data *listener_data;
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < KBASE_IPA_CORE_TYPE_NUM; i++) {
|
||||
for (j = 0; j < KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS; j++) {
|
||||
ipa_ctrl->blocks[i].select[j].idx = 0;
|
||||
ipa_ctrl->blocks[i].select[j].refcount = 0;
|
||||
}
|
||||
ipa_ctrl->blocks[i].num_available_counters =
|
||||
KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS;
|
||||
}
|
||||
|
||||
spin_lock_init(&ipa_ctrl->lock);
|
||||
ipa_ctrl->num_active_sessions = 0;
|
||||
for (i = 0; i < KBASE_IPA_CONTROL_MAX_SESSIONS; i++) {
|
||||
ipa_ctrl->sessions[i].active = false;
|
||||
}
|
||||
|
||||
listener_data = kmalloc(sizeof(struct kbase_ipa_control_listener_data),
|
||||
GFP_KERNEL);
|
||||
if (listener_data) {
|
||||
listener_data->listener.notify =
|
||||
kbase_ipa_control_rate_change_notify;
|
||||
listener_data->kbdev = kbdev;
|
||||
ipa_ctrl->rtm_listener_data = listener_data;
|
||||
}
|
||||
|
||||
spin_lock(&clk_rtm->lock);
|
||||
if (clk_rtm->clks[KBASE_CLOCK_DOMAIN_TOP])
|
||||
ipa_ctrl->cur_gpu_rate =
|
||||
clk_rtm->clks[KBASE_CLOCK_DOMAIN_TOP]->clock_val;
|
||||
if (listener_data)
|
||||
kbase_clk_rate_trace_manager_subscribe_no_lock(
|
||||
clk_rtm, &listener_data->listener);
|
||||
spin_unlock(&clk_rtm->lock);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_init);
|
||||
|
||||
void kbase_ipa_control_term(struct kbase_device *kbdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kbase_clk_rate_trace_manager *clk_rtm = &kbdev->pm.clk_rtm;
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
struct kbase_ipa_control_listener_data *listener_data =
|
||||
ipa_ctrl->rtm_listener_data;
|
||||
|
||||
WARN_ON(ipa_ctrl->num_active_sessions);
|
||||
|
||||
if (listener_data)
|
||||
kbase_clk_rate_trace_manager_unsubscribe(clk_rtm, &listener_data->listener);
|
||||
kfree(ipa_ctrl->rtm_listener_data);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
if (kbdev->pm.backend.gpu_powered)
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(TIMER), 0);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_term);
|
||||
|
||||
int kbase_ipa_control_register(
|
||||
struct kbase_device *kbdev,
|
||||
const struct kbase_ipa_control_perf_counter *perf_counters,
|
||||
size_t num_counters, void **client)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t i, session_idx, req_counters[KBASE_IPA_CORE_TYPE_NUM];
|
||||
bool already_configured[KBASE_IPA_CONTROL_MAX_COUNTERS];
|
||||
bool new_config = false;
|
||||
struct kbase_ipa_control *ipa_ctrl;
|
||||
struct kbase_ipa_control_session *session = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (WARN_ON(kbdev == NULL) || WARN_ON(perf_counters == NULL) ||
|
||||
WARN_ON(client == NULL) ||
|
||||
WARN_ON(num_counters > KBASE_IPA_CONTROL_MAX_COUNTERS)) {
|
||||
dev_err(kbdev->dev, "%s: wrong input arguments", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kbase_pm_context_active(kbdev);
|
||||
|
||||
ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
spin_lock_irqsave(&ipa_ctrl->lock, flags);
|
||||
|
||||
if (ipa_ctrl->num_active_sessions == KBASE_IPA_CONTROL_MAX_SESSIONS) {
|
||||
dev_err(kbdev->dev, "%s: too many sessions", __func__);
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < KBASE_IPA_CORE_TYPE_NUM; i++)
|
||||
req_counters[i] = 0;
|
||||
|
||||
/*
|
||||
* Count how many counters would need to be configured in order to
|
||||
* satisfy the request. Requested counters which happen to be already
|
||||
* configured can be skipped.
|
||||
*/
|
||||
for (i = 0; i < num_counters; i++) {
|
||||
size_t j;
|
||||
enum kbase_ipa_core_type type = perf_counters[i].type;
|
||||
u8 idx = perf_counters[i].idx;
|
||||
|
||||
if ((type >= KBASE_IPA_CORE_TYPE_NUM) ||
|
||||
(idx >= KBASE_IPA_CONTROL_CNT_MAX_IDX)) {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: invalid requested type %u and/or index %u",
|
||||
__func__, type, idx);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (j = 0; j < KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS; j++) {
|
||||
struct kbase_ipa_control_prfcnt_config *prfcnt_config =
|
||||
&ipa_ctrl->blocks[type].select[j];
|
||||
|
||||
if (prfcnt_config->refcount > 0) {
|
||||
if (prfcnt_config->idx == idx) {
|
||||
already_configured[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j == KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS) {
|
||||
already_configured[i] = false;
|
||||
req_counters[type]++;
|
||||
new_config = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < KBASE_IPA_CORE_TYPE_NUM; i++)
|
||||
if (req_counters[i] >
|
||||
ipa_ctrl->blocks[i].num_available_counters) {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: more counters (%zu) than available (%zu) have been requested for type %zu",
|
||||
__func__, req_counters[i],
|
||||
ipa_ctrl->blocks[i].num_available_counters, i);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* The request has been validated.
|
||||
* Firstly, find an available session and then set up the initial state
|
||||
* of the session and update the configuration of performance counters
|
||||
* in the internal state of kbase_ipa_control.
|
||||
*/
|
||||
for (session_idx = 0; session_idx < KBASE_IPA_CONTROL_MAX_SESSIONS;
|
||||
session_idx++) {
|
||||
session = &ipa_ctrl->sessions[session_idx];
|
||||
if (!session->active)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
dev_err(kbdev->dev, "%s: wrong or corrupt session state",
|
||||
__func__);
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_counters; i++) {
|
||||
struct kbase_ipa_control_prfcnt_config *prfcnt_config;
|
||||
size_t j;
|
||||
u8 type = perf_counters[i].type;
|
||||
u8 idx = perf_counters[i].idx;
|
||||
|
||||
for (j = 0; j < KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS; j++) {
|
||||
prfcnt_config = &ipa_ctrl->blocks[type].select[j];
|
||||
|
||||
if (already_configured[i]) {
|
||||
if ((prfcnt_config->refcount > 0) &&
|
||||
(prfcnt_config->idx == idx)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (prfcnt_config->refcount == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON((prfcnt_config->refcount > 0 &&
|
||||
prfcnt_config->idx != idx) ||
|
||||
(j == KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS))) {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: invalid internal state: counter already configured or no counter available to configure",
|
||||
__func__);
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (prfcnt_config->refcount == 0) {
|
||||
prfcnt_config->idx = idx;
|
||||
ipa_ctrl->blocks[type].num_available_counters--;
|
||||
}
|
||||
|
||||
session->prfcnts[i].accumulated_diff = 0;
|
||||
session->prfcnts[i].type = type;
|
||||
session->prfcnts[i].select_idx = j;
|
||||
session->prfcnts[i].scaling_factor =
|
||||
perf_counters[i].scaling_factor;
|
||||
session->prfcnts[i].gpu_norm = perf_counters[i].gpu_norm;
|
||||
|
||||
/* Reports to this client for GPU time spent in protected mode
|
||||
* should begin from the point of registration.
|
||||
*/
|
||||
session->last_query_time = ktime_get_ns();
|
||||
|
||||
/* Initially, no time has been spent in protected mode */
|
||||
session->protm_time = 0;
|
||||
|
||||
prfcnt_config->refcount++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply new configuration, if necessary.
|
||||
* As a temporary solution, make sure that the GPU is on
|
||||
* before applying the new configuration.
|
||||
*/
|
||||
if (new_config) {
|
||||
u64 select_config[KBASE_IPA_CORE_TYPE_NUM];
|
||||
|
||||
build_select_config(ipa_ctrl, select_config);
|
||||
ret = apply_select_config(kbdev, select_config);
|
||||
if (ret)
|
||||
dev_err(kbdev->dev,
|
||||
"%s: failed to apply SELECT configuration",
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
/* Accumulator registers don't contain any sample if the timer
|
||||
* has not been enabled first. Take a sample manually before
|
||||
* enabling the timer.
|
||||
*/
|
||||
if (ipa_ctrl->num_active_sessions == 0) {
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND),
|
||||
COMMAND_SAMPLE);
|
||||
ret = wait_status(kbdev, STATUS_COMMAND_ACTIVE);
|
||||
if (!ret) {
|
||||
kbase_reg_write(
|
||||
kbdev, IPA_CONTROL_REG(TIMER),
|
||||
timer_value(ipa_ctrl->cur_gpu_rate));
|
||||
} else {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: failed to sample new counters",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
session->num_prfcnts = num_counters;
|
||||
session->active = true;
|
||||
ipa_ctrl->num_active_sessions++;
|
||||
*client = session;
|
||||
|
||||
/*
|
||||
* Read current raw value to initialize the session.
|
||||
* This is necessary to put the first query in condition
|
||||
* to generate a correct value by calculating the difference
|
||||
* from the beginning of the session.
|
||||
*/
|
||||
for (i = 0; i < session->num_prfcnts; i++) {
|
||||
struct kbase_ipa_control_prfcnt *prfcnt =
|
||||
&session->prfcnts[i];
|
||||
u64 raw_value = read_value_cnt(kbdev, (u8)prfcnt->type,
|
||||
prfcnt->select_idx);
|
||||
prfcnt->latest_raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
spin_unlock_irqrestore(&ipa_ctrl->lock, flags);
|
||||
kbase_pm_context_idle(kbdev);
|
||||
return ret;
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_register);
|
||||
|
||||
int kbase_ipa_control_unregister(struct kbase_device *kbdev, const void *client)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl;
|
||||
struct kbase_ipa_control_session *session;
|
||||
int ret = 0;
|
||||
size_t i;
|
||||
unsigned long flags;
|
||||
bool new_config = false, valid_session = false;
|
||||
|
||||
if (WARN_ON(kbdev == NULL) || WARN_ON(client == NULL)) {
|
||||
dev_err(kbdev->dev, "%s: wrong input arguments", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kbase_pm_context_active(kbdev);
|
||||
|
||||
ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
session = (struct kbase_ipa_control_session *)client;
|
||||
|
||||
spin_lock_irqsave(&ipa_ctrl->lock, flags);
|
||||
|
||||
for (i = 0; i < KBASE_IPA_CONTROL_MAX_SESSIONS; i++) {
|
||||
if (session == &ipa_ctrl->sessions[i]) {
|
||||
valid_session = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_session) {
|
||||
dev_err(kbdev->dev, "%s: invalid session handle", __func__);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ipa_ctrl->num_active_sessions == 0) {
|
||||
dev_err(kbdev->dev, "%s: no active sessions found", __func__);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!session->active) {
|
||||
dev_err(kbdev->dev, "%s: session is already inactive",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < session->num_prfcnts; i++) {
|
||||
struct kbase_ipa_control_prfcnt_config *prfcnt_config;
|
||||
u8 type = session->prfcnts[i].type;
|
||||
u8 idx = session->prfcnts[i].select_idx;
|
||||
|
||||
prfcnt_config = &ipa_ctrl->blocks[type].select[idx];
|
||||
|
||||
if (!WARN_ON(prfcnt_config->refcount == 0)) {
|
||||
prfcnt_config->refcount--;
|
||||
if (prfcnt_config->refcount == 0) {
|
||||
new_config = true;
|
||||
ipa_ctrl->blocks[type].num_available_counters++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_config) {
|
||||
u64 select_config[KBASE_IPA_CORE_TYPE_NUM];
|
||||
|
||||
build_select_config(ipa_ctrl, select_config);
|
||||
ret = apply_select_config(kbdev, select_config);
|
||||
if (ret)
|
||||
dev_err(kbdev->dev,
|
||||
"%s: failed to apply SELECT configuration",
|
||||
__func__);
|
||||
}
|
||||
|
||||
session->num_prfcnts = 0;
|
||||
session->active = false;
|
||||
ipa_ctrl->num_active_sessions--;
|
||||
|
||||
exit:
|
||||
spin_unlock_irqrestore(&ipa_ctrl->lock, flags);
|
||||
kbase_pm_context_idle(kbdev);
|
||||
return ret;
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_unregister);
|
||||
|
||||
int kbase_ipa_control_query(struct kbase_device *kbdev, const void *client,
|
||||
u64 *values, size_t num_values, u64 *protected_time)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl;
|
||||
struct kbase_ipa_control_session *session;
|
||||
size_t i;
|
||||
unsigned long flags;
|
||||
bool gpu_ready;
|
||||
|
||||
if (WARN_ON(kbdev == NULL) || WARN_ON(client == NULL) ||
|
||||
WARN_ON(values == NULL)) {
|
||||
dev_err(kbdev->dev, "%s: wrong input arguments", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
session = (struct kbase_ipa_control_session *)client;
|
||||
|
||||
if (WARN_ON(num_values < session->num_prfcnts)) {
|
||||
dev_err(kbdev->dev,
|
||||
"%s: not enough space (%zu) to return all counter values (%zu)",
|
||||
__func__, num_values, session->num_prfcnts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
gpu_ready = kbdev->pm.backend.gpu_ready;
|
||||
|
||||
for (i = 0; i < session->num_prfcnts; i++) {
|
||||
struct kbase_ipa_control_prfcnt *prfcnt = &session->prfcnts[i];
|
||||
|
||||
calc_prfcnt_delta(kbdev, prfcnt, gpu_ready);
|
||||
/* Return all the accumulated difference */
|
||||
values[i] = prfcnt->accumulated_diff;
|
||||
prfcnt->accumulated_diff = 0;
|
||||
}
|
||||
|
||||
if (protected_time) {
|
||||
u64 time_now = ktime_get_ns();
|
||||
|
||||
/* This is the amount of protected-mode time spent prior to
|
||||
* the current protm period.
|
||||
*/
|
||||
*protected_time = session->protm_time;
|
||||
|
||||
if (kbdev->protected_mode) {
|
||||
*protected_time +=
|
||||
time_now - MAX(session->last_query_time,
|
||||
ipa_ctrl->protm_start);
|
||||
}
|
||||
session->last_query_time = time_now;
|
||||
session->protm_time = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
for (i = session->num_prfcnts; i < num_values; i++)
|
||||
values[i] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_query);
|
||||
|
||||
void kbase_ipa_control_handle_gpu_power_off(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
size_t session_idx;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
/* GPU should still be ready for use when this function gets called */
|
||||
WARN_ON(!kbdev->pm.backend.gpu_ready);
|
||||
|
||||
/* Interrupts are already disabled and interrupt state is also saved */
|
||||
spin_lock(&ipa_ctrl->lock);
|
||||
|
||||
/* First disable the automatic sampling through TIMER */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(TIMER), 0);
|
||||
ret = wait_status(kbdev, STATUS_TIMER_ENABLED);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev,
|
||||
"Wait for disabling of IPA control timer failed: %d",
|
||||
ret);
|
||||
}
|
||||
|
||||
/* Now issue the manual SAMPLE command */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND), COMMAND_SAMPLE);
|
||||
ret = wait_status(kbdev, STATUS_COMMAND_ACTIVE);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev,
|
||||
"Wait for the completion of manual sample failed: %d",
|
||||
ret);
|
||||
}
|
||||
|
||||
for (session_idx = 0; session_idx < ipa_ctrl->num_active_sessions;
|
||||
session_idx++) {
|
||||
struct kbase_ipa_control_session *session =
|
||||
&ipa_ctrl->sessions[session_idx];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < session->num_prfcnts; i++) {
|
||||
struct kbase_ipa_control_prfcnt *prfcnt =
|
||||
&session->prfcnts[i];
|
||||
|
||||
calc_prfcnt_delta(kbdev, prfcnt, true);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&ipa_ctrl->lock);
|
||||
}
|
||||
|
||||
void kbase_ipa_control_handle_gpu_power_on(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
/* GPU should have become ready for use when this function gets called */
|
||||
WARN_ON(!kbdev->pm.backend.gpu_ready);
|
||||
|
||||
/* Interrupts are already disabled and interrupt state is also saved */
|
||||
spin_lock(&ipa_ctrl->lock);
|
||||
|
||||
/* Re-issue the APPLY command, this is actually needed only for CSHW */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND), COMMAND_APPLY);
|
||||
ret = wait_status(kbdev, STATUS_COMMAND_ACTIVE);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev,
|
||||
"Wait for the completion of apply command failed: %d",
|
||||
ret);
|
||||
}
|
||||
|
||||
/* Re-enable the timer for periodic sampling */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(TIMER),
|
||||
timer_value(ipa_ctrl->cur_gpu_rate));
|
||||
|
||||
spin_unlock(&ipa_ctrl->lock);
|
||||
}
|
||||
|
||||
void kbase_ipa_control_handle_gpu_reset_pre(struct kbase_device *kbdev)
|
||||
{
|
||||
/* A soft reset is treated as a power down */
|
||||
kbase_ipa_control_handle_gpu_power_off(kbdev);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_handle_gpu_reset_pre);
|
||||
|
||||
void kbase_ipa_control_handle_gpu_reset_post(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
int ret;
|
||||
u32 status;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
/* GPU should have become ready for use when this function gets called */
|
||||
WARN_ON(!kbdev->pm.backend.gpu_ready);
|
||||
|
||||
/* Interrupts are already disabled and interrupt state is also saved */
|
||||
spin_lock(&ipa_ctrl->lock);
|
||||
|
||||
/* Check the status reset bit is set before acknowledging it */
|
||||
status = kbase_reg_read(kbdev, IPA_CONTROL_REG(STATUS));
|
||||
if (status & STATUS_RESET) {
|
||||
/* Acknowledge the reset command */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND), COMMAND_RESET_ACK);
|
||||
ret = wait_status(kbdev, STATUS_RESET);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev,
|
||||
"Wait for the reset ack command failed: %d",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&ipa_ctrl->lock);
|
||||
|
||||
kbase_ipa_control_handle_gpu_power_on(kbdev);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_handle_gpu_reset_post);
|
||||
|
||||
#if MALI_UNIT_TEST
|
||||
void kbase_ipa_control_rate_change_notify_test(struct kbase_device *kbdev,
|
||||
u32 clk_index, u32 clk_rate_hz)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
struct kbase_ipa_control_listener_data *listener_data =
|
||||
ipa_ctrl->rtm_listener_data;
|
||||
|
||||
kbase_ipa_control_rate_change_notify(&listener_data->listener,
|
||||
clk_index, clk_rate_hz);
|
||||
}
|
||||
KBASE_EXPORT_TEST_API(kbase_ipa_control_rate_change_notify_test);
|
||||
#endif
|
||||
|
||||
void kbase_ipa_control_protm_entered(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
ipa_ctrl->protm_start = ktime_get_ns();
|
||||
}
|
||||
|
||||
void kbase_ipa_control_protm_exited(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_ipa_control *ipa_ctrl = &kbdev->csf.ipa_control;
|
||||
size_t i;
|
||||
u64 time_now = ktime_get_ns();
|
||||
u32 status;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
for (i = 0; i < ipa_ctrl->num_active_sessions; i++) {
|
||||
struct kbase_ipa_control_session *session =
|
||||
&ipa_ctrl->sessions[i];
|
||||
u64 protm_time = time_now - MAX(session->last_query_time,
|
||||
ipa_ctrl->protm_start);
|
||||
|
||||
session->protm_time += protm_time;
|
||||
}
|
||||
|
||||
/* Acknowledge the protected_mode bit in the IPA_CONTROL STATUS
|
||||
* register
|
||||
*/
|
||||
status = kbase_reg_read(kbdev, IPA_CONTROL_REG(STATUS));
|
||||
if (status & STATUS_PROTECTED_MODE) {
|
||||
int ret;
|
||||
|
||||
/* Acknowledge the protm command */
|
||||
kbase_reg_write(kbdev, IPA_CONTROL_REG(COMMAND),
|
||||
COMMAND_PROTECTED_ACK);
|
||||
ret = wait_status(kbdev, STATUS_PROTECTED_MODE);
|
||||
if (ret) {
|
||||
dev_err(kbdev->dev,
|
||||
"Wait for the protm ack command failed: %d",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_IPA_CONTROL_H_
|
||||
#define _KBASE_CSF_IPA_CONTROL_H_
|
||||
|
||||
#include <mali_kbase.h>
|
||||
|
||||
/**
|
||||
* Maximum index accepted to configure an IPA Control performance counter.
|
||||
*/
|
||||
#define KBASE_IPA_CONTROL_CNT_MAX_IDX ((u8)64 * 3)
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_perf_counter - Performance counter description
|
||||
*
|
||||
* @scaling_factor: Scaling factor by which the counter's value shall be
|
||||
* multiplied. A scaling factor of 1 corresponds to units
|
||||
* of 1 second if values are normalised by GPU frequency.
|
||||
* @gpu_norm: Indicating whether counter values shall be normalized by
|
||||
* GPU frequency. If true, returned values represent
|
||||
* an interval of time expressed in seconds (when the scaling
|
||||
* factor is set to 1).
|
||||
* @type: Type of counter block for performance counter.
|
||||
* @idx: Index of the performance counter inside the block.
|
||||
* It may be dependent on GPU architecture.
|
||||
* It cannot be greater than KBASE_IPA_CONTROL_CNT_MAX_IDX.
|
||||
*
|
||||
* This structure is used by clients of the IPA Control component to describe
|
||||
* a performance counter that they intend to read. The counter is identified
|
||||
* by block and index. In addition to that, the client also specifies how
|
||||
* values shall be represented. Raw values are a number of GPU cycles;
|
||||
* if normalized, they are divided by GPU frequency and become an interval
|
||||
* of time expressed in seconds, since the GPU frequency is given in Hz.
|
||||
* The client may specify a scaling factor to multiply counter values before
|
||||
* they are divided by frequency, in case the unit of time of 1 second is
|
||||
* too low in resolution. For instance: a scaling factor of 1000 implies
|
||||
* that the returned value is a time expressed in milliseconds; a scaling
|
||||
* factor of 1000 * 1000 implies that the returned value is a time expressed
|
||||
* in microseconds.
|
||||
*/
|
||||
struct kbase_ipa_control_perf_counter {
|
||||
u64 scaling_factor;
|
||||
bool gpu_norm;
|
||||
enum kbase_ipa_core_type type;
|
||||
u8 idx;
|
||||
};
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_init - Initialize the IPA Control component
|
||||
*
|
||||
* @kbdev: Pointer to Kbase device.
|
||||
*/
|
||||
void kbase_ipa_control_init(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_term - Terminate the IPA Control component
|
||||
*
|
||||
* @kbdev: Pointer to Kbase device.
|
||||
*/
|
||||
void kbase_ipa_control_term(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_register - Register a client to the IPA Control component
|
||||
*
|
||||
* @kbdev: Pointer to Kbase device.
|
||||
* @perf_counters: Array of performance counters the client intends to read.
|
||||
* For each counter the client specifies block, index,
|
||||
* scaling factor and whether it must be normalized by GPU
|
||||
* frequency.
|
||||
* @num_counters: Number of performance counters. It cannot exceed the total
|
||||
* number of counters that exist on the IPA Control interface.
|
||||
* @client: Handle to an opaque structure set by IPA Control if
|
||||
* the registration is successful. This handle identifies
|
||||
* a client's session and shall be provided in its future
|
||||
* queries.
|
||||
*
|
||||
* A client needs to subscribe to the IPA Control component by declaring which
|
||||
* performance counters it intends to read, and specifying a scaling factor
|
||||
* and whether normalization is requested for each performance counter.
|
||||
* The function shall configure the IPA Control interface accordingly and start
|
||||
* a session for the client that made the request. A unique handle is returned
|
||||
* if registration is successful in order to identify the client's session
|
||||
* and be used for future queries.
|
||||
*
|
||||
* Return: 0 on success, negative -errno on error
|
||||
*/
|
||||
int kbase_ipa_control_register(
|
||||
struct kbase_device *kbdev,
|
||||
const struct kbase_ipa_control_perf_counter *perf_counters,
|
||||
size_t num_counters, void **client);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_unregister - Unregister a client from IPA Control
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
* @client: Handle to an opaque structure that identifies the client session
|
||||
* to terminate, as returned by kbase_ipa_control_register.
|
||||
*
|
||||
* Return: 0 on success, negative -errno on error
|
||||
*/
|
||||
int kbase_ipa_control_unregister(struct kbase_device *kbdev,
|
||||
const void *client);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_query - Query performance counters
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
* @client: Handle to an opaque structure that identifies the client
|
||||
* session, as returned by kbase_ipa_control_register.
|
||||
* @values: Array of values queried from performance counters, whose
|
||||
* length depends on the number of counters requested at
|
||||
* the time of registration. Values are scaled and normalized
|
||||
* and represent the difference since the last query.
|
||||
* @num_values: Number of entries in the array of values that has been
|
||||
* passed by the caller. It must be at least equal to the
|
||||
* number of performance counters the client registered itself
|
||||
* to read.
|
||||
* @protected_time: Time spent in protected mode since last query,
|
||||
* expressed in nanoseconds. This pointer may be NULL if the
|
||||
* client doesn't want to know about this.
|
||||
*
|
||||
* A client that has already opened a session by registering itself to read
|
||||
* some performance counters may use this function to query the values of
|
||||
* those counters. The values returned are normalized by GPU frequency if
|
||||
* requested and then multiplied by the scaling factor provided at the time
|
||||
* of registration. Values always represent a difference since the last query.
|
||||
*
|
||||
* Performance counters are not updated while the GPU operates in protected
|
||||
* mode. For this reason, returned values may be unreliable if the GPU has
|
||||
* been in protected mode since the last query. The function returns success
|
||||
* in that case, but it also gives a measure of how much time has been spent
|
||||
* in protected mode.
|
||||
*
|
||||
* Return: 0 on success, negative -errno on error
|
||||
*/
|
||||
int kbase_ipa_control_query(struct kbase_device *kbdev, const void *client,
|
||||
u64 *values, size_t num_values,
|
||||
u64 *protected_time);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_handle_gpu_power_on - Handle the GPU power on event
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*
|
||||
* This function is called after GPU has been powered and is ready for use.
|
||||
* After the GPU power on, IPA Control component needs to ensure that the
|
||||
* counters start incrementing again.
|
||||
*/
|
||||
void kbase_ipa_control_handle_gpu_power_on(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_handle_gpu_power_off - Handle the GPU power off event
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*
|
||||
* This function is called just before the GPU is powered off when it is still
|
||||
* ready for use.
|
||||
* IPA Control component needs to be aware of the GPU power off so that it can
|
||||
* handle the query from Clients appropriately and return meaningful values
|
||||
* to them.
|
||||
*/
|
||||
void kbase_ipa_control_handle_gpu_power_off(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_handle_gpu_reset_pre - Handle the pre GPU reset event
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*
|
||||
* This function is called when the GPU is about to be reset.
|
||||
*/
|
||||
void kbase_ipa_control_handle_gpu_reset_pre(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_handle_gpu_reset_post - Handle the post GPU reset event
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*
|
||||
* This function is called after the GPU has been reset.
|
||||
*/
|
||||
void kbase_ipa_control_handle_gpu_reset_post(struct kbase_device *kbdev);
|
||||
|
||||
#if MALI_UNIT_TEST
|
||||
/**
|
||||
* kbase_ipa_control_rate_change_notify_test - Notify GPU rate change
|
||||
* (only for testing)
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
* @clk_index: Index of the clock for which the change has occurred.
|
||||
* @clk_rate_hz: Clock frequency(Hz).
|
||||
*
|
||||
* Notify the IPA Control component about a GPU rate change.
|
||||
*/
|
||||
void kbase_ipa_control_rate_change_notify_test(struct kbase_device *kbdev,
|
||||
u32 clk_index, u32 clk_rate_hz);
|
||||
#endif /* MALI_UNIT_TEST */
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_protm_entered - Tell IPA_CONTROL that protected mode
|
||||
* has been entered.
|
||||
*
|
||||
* @kbdev: Pointer to kbase device.
|
||||
*
|
||||
* This function provides a means through which IPA_CONTROL can be informed
|
||||
* that the GPU has entered protected mode. Since the GPU cannot access
|
||||
* performance counters while in this mode, this information is useful as
|
||||
* it implies (a) the values of these registers cannot change, so theres no
|
||||
* point trying to read them, and (b) IPA_CONTROL has a means through which
|
||||
* to record the duration of time the GPU is in protected mode, which can
|
||||
* then be forwarded on to clients, who may wish, for example, to assume
|
||||
* that the GPU was busy 100% of the time while in this mode.
|
||||
*/
|
||||
void kbase_ipa_control_protm_entered(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_ipa_control_protm_exited - Tell IPA_CONTROL that protected mode
|
||||
* has been exited.
|
||||
*
|
||||
* @kbdev: Pointer to kbase device
|
||||
*
|
||||
* This function provides a means through which IPA_CONTROL can be informed
|
||||
* that the GPU has exited from protected mode.
|
||||
*/
|
||||
void kbase_ipa_control_protm_exited(struct kbase_device *kbdev);
|
||||
|
||||
#endif /* _KBASE_CSF_IPA_CONTROL_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2018-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_H_
|
||||
@@ -28,11 +27,11 @@
|
||||
#include "mali_kbase_csf_firmware.h"
|
||||
#include "mali_kbase_csf_protected_memory.h"
|
||||
|
||||
/* Indicate invalid command stream h/w interface
|
||||
/* Indicate invalid CS h/w interface
|
||||
*/
|
||||
#define KBASEP_IF_NR_INVALID ((s8)-1)
|
||||
|
||||
/* Indicate invalid command stream group number for a GPU command queue group
|
||||
/* Indicate invalid CSG number for a GPU command queue group
|
||||
*/
|
||||
#define KBASEP_CSG_NR_INVALID ((s8)-1)
|
||||
|
||||
@@ -40,13 +39,10 @@
|
||||
*/
|
||||
#define KBASEP_USER_DB_NR_INVALID ((s8)-1)
|
||||
|
||||
/* Waiting timeout for global request completion acknowledgment */
|
||||
#define GLB_REQ_WAIT_TIMEOUT_MS (300) /* 300 milliseconds */
|
||||
|
||||
#define CSG_REQ_EP_CFG (0x1 << CSG_REQ_EP_CFG_SHIFT)
|
||||
#define CSG_REQ_SYNC_UPDATE (0x1 << CSG_REQ_SYNC_UPDATE_SHIFT)
|
||||
#define FIRMWARE_PING_INTERVAL_MS (2000) /* 2 seconds */
|
||||
|
||||
#define FIRMWARE_IDLE_HYSTERESIS_TIME_MS (10) /* Default 10 milliseconds */
|
||||
|
||||
/**
|
||||
* enum kbase_csf_event_callback_action - return type for CSF event callbacks.
|
||||
*
|
||||
@@ -124,9 +120,9 @@ void kbase_csf_event_wait_remove(struct kbase_context *kctx,
|
||||
void kbase_csf_event_wait_remove_all(struct kbase_context *kctx);
|
||||
|
||||
/**
|
||||
* kbase_csf_read_error - Read command stream fatal error
|
||||
* kbase_csf_read_error - Read CS fatal error
|
||||
*
|
||||
* This function takes the command stream fatal error from context's ordered
|
||||
* This function takes the CS fatal error from context's ordered
|
||||
* error_list, copies its contents to @event_data.
|
||||
*
|
||||
* @kctx: The kbase context to read fatal error from
|
||||
@@ -150,8 +146,8 @@ bool kbase_csf_error_pending(struct kbase_context *kctx);
|
||||
* kbase_csf_event_signal - Signal a CSF event
|
||||
*
|
||||
* This function triggers all the CSF event callbacks that are registered to
|
||||
* a given Kbase context, and also signals the thread of userspace driver
|
||||
* (front-end), waiting for the CSF event.
|
||||
* a given Kbase context, and also signals the event handling thread of
|
||||
* userspace driver waiting for the CSF event.
|
||||
*
|
||||
* @kctx: The kbase context whose CSF event callbacks shall be triggered.
|
||||
* @notify_gpu: Flag to indicate if CSF firmware should be notified of the
|
||||
@@ -171,8 +167,7 @@ static inline void kbase_csf_event_signal_cpu_only(struct kbase_context *kctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_ctx_init - Initialize the command-stream front-end for a GPU
|
||||
* address space.
|
||||
* kbase_csf_ctx_init - Initialize the CSF interface for a GPU address space.
|
||||
*
|
||||
* @kctx: Pointer to the kbase context which is being initialized.
|
||||
*
|
||||
@@ -194,8 +189,7 @@ void kbase_csf_ctx_handle_fault(struct kbase_context *kctx,
|
||||
struct kbase_fault *fault);
|
||||
|
||||
/**
|
||||
* kbase_csf_ctx_term - Terminate the command-stream front-end for a GPU
|
||||
* address space.
|
||||
* kbase_csf_ctx_term - Terminate the CSF interface for a GPU address space.
|
||||
*
|
||||
* This function terminates any remaining CSGs and CSs which weren't destroyed
|
||||
* before context termination.
|
||||
@@ -268,6 +262,16 @@ int kbase_csf_queue_bind(struct kbase_context *kctx,
|
||||
*/
|
||||
void kbase_csf_queue_unbind(struct kbase_queue *queue);
|
||||
|
||||
/**
|
||||
* kbase_csf_queue_unbind_stopped - Unbind a GPU command queue in the case
|
||||
* where it was never started.
|
||||
* @queue: Pointer to queue to be unbound.
|
||||
*
|
||||
* Variant of kbase_csf_queue_unbind() for use on error paths for cleaning up
|
||||
* queues that failed to fully bind.
|
||||
*/
|
||||
void kbase_csf_queue_unbind_stopped(struct kbase_queue *queue);
|
||||
|
||||
/**
|
||||
* kbase_csf_queue_kick - Schedule a GPU command queue on the firmware
|
||||
*
|
||||
@@ -280,7 +284,9 @@ void kbase_csf_queue_unbind(struct kbase_queue *queue);
|
||||
int kbase_csf_queue_kick(struct kbase_context *kctx,
|
||||
struct kbase_ioctl_cs_queue_kick *kick);
|
||||
|
||||
/** Find if given the queue group handle is valid.
|
||||
/**
|
||||
* kbase_csf_queue_group_handle_is_valid - Find if the given queue group handle
|
||||
* is valid.
|
||||
*
|
||||
* This function is used to determine if the queue group handle is valid.
|
||||
*
|
||||
@@ -340,7 +346,6 @@ void kbase_csf_term_descheduled_queue_group(struct kbase_queue_group *group);
|
||||
* suspended.
|
||||
* @sus_buf: Pointer to the structure which contains details of the
|
||||
* user buffer and its kernel pinned pages.
|
||||
* @size: The size in bytes for the user provided buffer.
|
||||
* @group_handle: Handle for the group which uniquely identifies it within
|
||||
* the context within which it was created.
|
||||
*
|
||||
@@ -350,6 +355,16 @@ void kbase_csf_term_descheduled_queue_group(struct kbase_queue_group *group);
|
||||
int kbase_csf_queue_group_suspend(struct kbase_context *kctx,
|
||||
struct kbase_suspend_copy_buffer *sus_buf, u8 group_handle);
|
||||
|
||||
/**
|
||||
* kbase_csf_add_group_fatal_error - Report a fatal group error to userspace
|
||||
*
|
||||
* @group: GPU command queue group.
|
||||
* @err_payload: Error payload to report.
|
||||
*/
|
||||
void kbase_csf_add_group_fatal_error(
|
||||
struct kbase_queue_group *const group,
|
||||
struct base_gpu_queue_group_error const *const err_payload);
|
||||
|
||||
/**
|
||||
* kbase_csf_interrupt - Handle interrupts issued by CSF firmware.
|
||||
*
|
||||
@@ -359,55 +374,96 @@ int kbase_csf_queue_group_suspend(struct kbase_context *kctx,
|
||||
void kbase_csf_interrupt(struct kbase_device *kbdev, u32 val);
|
||||
|
||||
/**
|
||||
* kbase_csf_doorbell_mapping_init - Initialize the bitmap of Hw doorbell pages
|
||||
* used to track their availability.
|
||||
* kbase_csf_doorbell_mapping_init - Initialize the fields that facilitates
|
||||
* the update of userspace mapping of HW
|
||||
* doorbell page.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* The function creates a file and allocates a dummy page to facilitate the
|
||||
* update of userspace mapping to point to the dummy page instead of the real
|
||||
* HW doorbell page after the suspend of queue group.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Return: 0 on success, or negative on failure.
|
||||
*/
|
||||
int kbase_csf_doorbell_mapping_init(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_doorbell_mapping_term - Free the dummy page & close the file used
|
||||
* to update the userspace mapping of HW doorbell page
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_doorbell_mapping_term(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_ring_csg_doorbell - ring the doorbell for a command stream group
|
||||
* interface.
|
||||
* kbase_csf_setup_dummy_user_reg_page - Setup the dummy page that is accessed
|
||||
* instead of the User register page after
|
||||
* the GPU power down.
|
||||
*
|
||||
* The function kicks a notification on the command stream group interface to
|
||||
* firmware.
|
||||
* The function allocates a dummy page which is used to replace the User
|
||||
* register page in the userspace mapping after the power down of GPU.
|
||||
* On the power up of GPU, the mapping is updated to point to the real
|
||||
* User register page. The mapping is used to allow access to LATEST_FLUSH
|
||||
* register from userspace.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @slot: Index of command stream group interface for ringing the door-bell.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Return: 0 on success, or negative on failure.
|
||||
*/
|
||||
int kbase_csf_setup_dummy_user_reg_page(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_free_dummy_user_reg_page - Free the dummy page that was used
|
||||
* used to replace the User register page
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_free_dummy_user_reg_page(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_ring_csg_doorbell - ring the doorbell for a CSG interface.
|
||||
*
|
||||
* The function kicks a notification on the CSG interface to firmware.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @slot: Index of CSG interface for ringing the door-bell.
|
||||
*/
|
||||
void kbase_csf_ring_csg_doorbell(struct kbase_device *kbdev, int slot);
|
||||
|
||||
/**
|
||||
* kbase_csf_ring_csg_slots_doorbell - ring the doorbell for a set of command
|
||||
* stream group interfaces.
|
||||
* kbase_csf_ring_csg_slots_doorbell - ring the doorbell for a set of CSG
|
||||
* interfaces.
|
||||
*
|
||||
* The function kicks a notification on a set of command stream group
|
||||
* interfaces to firmware.
|
||||
* The function kicks a notification on a set of CSG interfaces to firmware.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @slot_bitmap: bitmap for the given slots, slot-0 on bit-0, etc.
|
||||
*/
|
||||
void kbase_csf_ring_csg_slots_doorbell(struct kbase_device *kbdev,
|
||||
u32 slot_bitmap);
|
||||
|
||||
/**
|
||||
* kbase_csf_ring_cs_kernel_doorbell - ring the kernel doorbell for a queue
|
||||
* kbase_csf_ring_cs_kernel_doorbell - ring the kernel doorbell for a CSI
|
||||
* assigned to a GPU queue
|
||||
*
|
||||
* The function kicks a notification to the firmware for the command stream
|
||||
* interface to which the queue is bound.
|
||||
* The function sends a doorbell interrupt notification to the firmware for
|
||||
* a CSI assigned to a GPU queue.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @queue: Pointer to the queue for ringing the door-bell.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @csi_index: ID of the CSI assigned to the GPU queue.
|
||||
* @csg_nr: Index of the CSG slot assigned to the queue
|
||||
* group to which the GPU queue is bound.
|
||||
* @ring_csg_doorbell: Flag to indicate if the CSG doorbell needs to be rung
|
||||
* after updating the CSG_DB_REQ. So if this flag is false
|
||||
* the doorbell interrupt will not be sent to FW.
|
||||
* The flag is supposed be false only when the input page
|
||||
* for bound GPU queues is programmed at the time of
|
||||
* starting/resuming the group on a CSG slot.
|
||||
*/
|
||||
void kbase_csf_ring_cs_kernel_doorbell(struct kbase_device *kbdev,
|
||||
struct kbase_queue *queue);
|
||||
int csi_index, int csg_nr,
|
||||
bool ring_csg_doorbell);
|
||||
|
||||
/**
|
||||
* kbase_csf_ring_cs_user_doorbell - ring the user doorbell allocated for a
|
||||
@@ -416,8 +472,7 @@ void kbase_csf_ring_cs_kernel_doorbell(struct kbase_device *kbdev,
|
||||
* The function kicks a notification to the firmware on the doorbell assigned
|
||||
* to the queue.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @queue: Pointer to the queue for ringing the door-bell.
|
||||
*/
|
||||
void kbase_csf_ring_cs_user_doorbell(struct kbase_device *kbdev,
|
||||
@@ -427,9 +482,8 @@ void kbase_csf_ring_cs_user_doorbell(struct kbase_device *kbdev,
|
||||
* kbase_csf_active_queue_groups_reset - Reset the state of all active GPU
|
||||
* command queue groups associated with the context.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kctx: The kbase context.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @kctx: The kbase context.
|
||||
*
|
||||
* This function will iterate through all the active/scheduled GPU command
|
||||
* queue groups associated with the context, deschedule and mark them as
|
||||
@@ -441,4 +495,54 @@ void kbase_csf_ring_cs_user_doorbell(struct kbase_device *kbdev,
|
||||
void kbase_csf_active_queue_groups_reset(struct kbase_device *kbdev,
|
||||
struct kbase_context *kctx);
|
||||
|
||||
/**
|
||||
* kbase_csf_priority_check - Check the priority requested
|
||||
*
|
||||
* @kbdev: Device pointer
|
||||
* @req_priority: Requested priority
|
||||
*
|
||||
* This will determine whether the requested priority can be satisfied.
|
||||
*
|
||||
* Return: The same or lower priority than requested.
|
||||
*/
|
||||
u8 kbase_csf_priority_check(struct kbase_device *kbdev, u8 req_priority);
|
||||
|
||||
extern const u8 kbasep_csf_queue_group_priority_to_relative[BASE_QUEUE_GROUP_PRIORITY_COUNT];
|
||||
extern const u8 kbasep_csf_relative_to_queue_group_priority[KBASE_QUEUE_GROUP_PRIORITY_COUNT];
|
||||
|
||||
/**
|
||||
* kbase_csf_priority_relative_to_queue_group_priority - Convert relative to base priority
|
||||
*
|
||||
* @priority: kbase relative priority
|
||||
*
|
||||
* This will convert the monotonically increasing realtive priority to the
|
||||
* fixed base priority list.
|
||||
*
|
||||
* Return: base_queue_group_priority priority.
|
||||
*/
|
||||
static inline u8 kbase_csf_priority_relative_to_queue_group_priority(u8 priority)
|
||||
{
|
||||
if (priority >= KBASE_QUEUE_GROUP_PRIORITY_COUNT)
|
||||
priority = KBASE_QUEUE_GROUP_PRIORITY_LOW;
|
||||
return kbasep_csf_relative_to_queue_group_priority[priority];
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_priority_queue_group_priority_to_relative - Convert base priority to relative
|
||||
*
|
||||
* @priority: base_queue_group_priority priority
|
||||
*
|
||||
* This will convert the fixed base priority list to monotonically increasing realtive priority.
|
||||
*
|
||||
* Return: kbase relative priority.
|
||||
*/
|
||||
static inline u8 kbase_csf_priority_queue_group_priority_to_relative(u8 priority)
|
||||
{
|
||||
/* Apply low priority in case of invalid priority */
|
||||
if (priority >= BASE_QUEUE_GROUP_PRIORITY_COUNT)
|
||||
priority = BASE_QUEUE_GROUP_PRIORITY_LOW;
|
||||
return kbasep_csf_queue_group_priority_to_relative[priority];
|
||||
}
|
||||
|
||||
|
||||
#endif /* _KBASE_CSF_H_ */
|
||||
|
||||
191
drivers/gpu/arm/bifrost/csf/mali_kbase_csf_cpu_queue_debugfs.c
Normal file
191
drivers/gpu/arm/bifrost/csf/mali_kbase_csf_cpu_queue_debugfs.c
Normal file
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020-2021 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mali_kbase_csf_cpu_queue_debugfs.h"
|
||||
#include <mali_kbase.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
bool kbase_csf_cpu_queue_read_dump_req(struct kbase_context *kctx,
|
||||
struct base_csf_notification *req)
|
||||
{
|
||||
if (atomic_cmpxchg(&kctx->csf.cpu_queue.dump_req_status,
|
||||
BASE_CSF_CPU_QUEUE_DUMP_ISSUED,
|
||||
BASE_CSF_CPU_QUEUE_DUMP_PENDING) !=
|
||||
BASE_CSF_CPU_QUEUE_DUMP_ISSUED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
req->type = BASE_CSF_NOTIFICATION_CPU_QUEUE_DUMP;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbasep_csf_cpu_queue_debugfs_show() - Print cpu queue information for per context
|
||||
*
|
||||
* @file: The seq_file for printing to
|
||||
* @data: The debugfs dentry private data, a pointer to kbase_context
|
||||
*
|
||||
* Return: Negative error code or 0 on success.
|
||||
*/
|
||||
static int kbasep_csf_cpu_queue_debugfs_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct kbase_context *kctx = file->private;
|
||||
|
||||
mutex_lock(&kctx->csf.lock);
|
||||
if (atomic_read(&kctx->csf.cpu_queue.dump_req_status) !=
|
||||
BASE_CSF_CPU_QUEUE_DUMP_COMPLETE) {
|
||||
seq_printf(file, "Dump request already started! (try again)\n");
|
||||
mutex_unlock(&kctx->csf.lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
atomic_set(&kctx->csf.cpu_queue.dump_req_status, BASE_CSF_CPU_QUEUE_DUMP_ISSUED);
|
||||
init_completion(&kctx->csf.cpu_queue.dump_cmp);
|
||||
kbase_event_wakeup(kctx);
|
||||
mutex_unlock(&kctx->csf.lock);
|
||||
|
||||
seq_printf(file, "CPU Queues table (version:v%u):\n", MALI_CSF_CPU_QUEUE_DEBUGFS_VERSION);
|
||||
|
||||
wait_for_completion_timeout(&kctx->csf.cpu_queue.dump_cmp,
|
||||
msecs_to_jiffies(3000));
|
||||
|
||||
mutex_lock(&kctx->csf.lock);
|
||||
if (kctx->csf.cpu_queue.buffer) {
|
||||
WARN_ON(atomic_read(&kctx->csf.cpu_queue.dump_req_status) !=
|
||||
BASE_CSF_CPU_QUEUE_DUMP_PENDING);
|
||||
|
||||
seq_printf(file, "%s\n", kctx->csf.cpu_queue.buffer);
|
||||
|
||||
kfree(kctx->csf.cpu_queue.buffer);
|
||||
kctx->csf.cpu_queue.buffer = NULL;
|
||||
kctx->csf.cpu_queue.buffer_size = 0;
|
||||
}
|
||||
else
|
||||
seq_printf(file, "Dump error! (time out)\n");
|
||||
|
||||
atomic_set(&kctx->csf.cpu_queue.dump_req_status,
|
||||
BASE_CSF_CPU_QUEUE_DUMP_COMPLETE);
|
||||
|
||||
mutex_unlock(&kctx->csf.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kbasep_csf_cpu_queue_debugfs_open(struct inode *in, struct file *file)
|
||||
{
|
||||
return single_open(file, kbasep_csf_cpu_queue_debugfs_show, in->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations kbasep_csf_cpu_queue_debugfs_fops = {
|
||||
.open = kbasep_csf_cpu_queue_debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void kbase_csf_cpu_queue_debugfs_init(struct kbase_context *kctx)
|
||||
{
|
||||
struct dentry *file;
|
||||
|
||||
if (WARN_ON(!kctx || IS_ERR_OR_NULL(kctx->kctx_dentry)))
|
||||
return;
|
||||
|
||||
file = debugfs_create_file("cpu_queue", 0444, kctx->kctx_dentry,
|
||||
kctx, &kbasep_csf_cpu_queue_debugfs_fops);
|
||||
|
||||
if (IS_ERR_OR_NULL(file)) {
|
||||
dev_warn(kctx->kbdev->dev,
|
||||
"Unable to create cpu queue debugfs entry");
|
||||
}
|
||||
|
||||
kctx->csf.cpu_queue.buffer = NULL;
|
||||
kctx->csf.cpu_queue.buffer_size = 0;
|
||||
atomic_set(&kctx->csf.cpu_queue.dump_req_status,
|
||||
BASE_CSF_CPU_QUEUE_DUMP_COMPLETE);
|
||||
}
|
||||
|
||||
int kbase_csf_cpu_queue_dump(struct kbase_context *kctx,
|
||||
u64 buffer, size_t buf_size)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
size_t alloc_size = buf_size;
|
||||
char *dump_buffer;
|
||||
|
||||
if (!buffer || !alloc_size)
|
||||
goto done;
|
||||
|
||||
alloc_size = (alloc_size + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||||
dump_buffer = kzalloc(alloc_size, GFP_KERNEL);
|
||||
if (ZERO_OR_NULL_PTR(dump_buffer)) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
WARN_ON(kctx->csf.cpu_queue.buffer != NULL);
|
||||
|
||||
err = copy_from_user(dump_buffer,
|
||||
u64_to_user_ptr(buffer),
|
||||
buf_size);
|
||||
if (err) {
|
||||
kfree(dump_buffer);
|
||||
err = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mutex_lock(&kctx->csf.lock);
|
||||
|
||||
kfree(kctx->csf.cpu_queue.buffer);
|
||||
|
||||
if (atomic_read(&kctx->csf.cpu_queue.dump_req_status) ==
|
||||
BASE_CSF_CPU_QUEUE_DUMP_PENDING) {
|
||||
kctx->csf.cpu_queue.buffer = dump_buffer;
|
||||
kctx->csf.cpu_queue.buffer_size = buf_size;
|
||||
complete_all(&kctx->csf.cpu_queue.dump_cmp);
|
||||
} else {
|
||||
kfree(dump_buffer);
|
||||
}
|
||||
|
||||
mutex_unlock(&kctx->csf.lock);
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Stub functions for when debugfs is disabled
|
||||
*/
|
||||
void kbase_csf_cpu_queue_debugfs_init(struct kbase_context *kctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool kbase_csf_cpu_queue_read_dump_req(struct kbase_context *kctx,
|
||||
struct base_csf_notification *req)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int kbase_csf_cpu_queue_dump(struct kbase_context *kctx,
|
||||
u64 buffer, size_t buf_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
@@ -0,0 +1,90 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020-2021 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 license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_CPU_QUEUE_DEBUGFS_H_
|
||||
#define _KBASE_CSF_CPU_QUEUE_DEBUGFS_H_
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "mali_kbase.h"
|
||||
|
||||
/* Forward declaration */
|
||||
struct base_csf_notification;
|
||||
|
||||
#define MALI_CSF_CPU_QUEUE_DEBUGFS_VERSION 0
|
||||
|
||||
/* CPU queue dump status */
|
||||
/* Dumping is done or no dumping is in progress. */
|
||||
#define BASE_CSF_CPU_QUEUE_DUMP_COMPLETE 0
|
||||
/* Dumping request is pending. */
|
||||
#define BASE_CSF_CPU_QUEUE_DUMP_PENDING 1
|
||||
/* Dumping request is issued to Userspace */
|
||||
#define BASE_CSF_CPU_QUEUE_DUMP_ISSUED 2
|
||||
|
||||
|
||||
/**
|
||||
* kbase_csf_cpu_queue_debugfs_init() - Create a debugfs entry for per context cpu queue(s)
|
||||
*
|
||||
* @kctx: The kbase_context for which to create the debugfs entry
|
||||
*/
|
||||
void kbase_csf_cpu_queue_debugfs_init(struct kbase_context *kctx);
|
||||
|
||||
/**
|
||||
* kbase_csf_cpu_queue_read_dump_req - Read cpu queue dump request event
|
||||
*
|
||||
* @kctx: The kbase_context which cpu queue dumpped belongs to
|
||||
* @req: Notification with cpu queue dump request.
|
||||
*
|
||||
* Return: true if needs CPU queue dump, or false otherwise.
|
||||
*/
|
||||
bool kbase_csf_cpu_queue_read_dump_req(struct kbase_context *kctx,
|
||||
struct base_csf_notification *req);
|
||||
|
||||
/**
|
||||
* kbase_csf_cpu_queue_dump_needed - Check the requirement for cpu queue dump
|
||||
*
|
||||
* @kctx: The kbase_context which cpu queue dumpped belongs to
|
||||
*
|
||||
* Return: true if it needs cpu queue dump, or false otherwise.
|
||||
*/
|
||||
static inline bool kbase_csf_cpu_queue_dump_needed(struct kbase_context *kctx)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
return (atomic_read(&kctx->csf.cpu_queue.dump_req_status) ==
|
||||
BASE_CSF_CPU_QUEUE_DUMP_ISSUED);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_cpu_queue_dump - dump buffer containing cpu queue information to debugfs
|
||||
*
|
||||
* @kctx: The kbase_context which cpu queue dumpped belongs to
|
||||
* @buffer: Buffer containing the cpu queue information.
|
||||
* @buf_size: Buffer size.
|
||||
*
|
||||
* Return: Return 0 for dump successfully, or error code.
|
||||
*/
|
||||
int kbase_csf_cpu_queue_dump(struct kbase_context *kctx,
|
||||
u64 buffer, size_t buf_size);
|
||||
#endif /* _KBASE_CSF_CPU_QUEUE_DEBUGFS_H_ */
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019-2020 ARM Limited. All rights reserved.
|
||||
@@ -5,7 +6,7 @@
|
||||
* 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mali_kbase_csf_csg_debugfs.h"
|
||||
@@ -29,12 +28,37 @@
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include "mali_kbase_csf_tl_reader.h"
|
||||
|
||||
/**
|
||||
* blocked_reason_to_string() - Convert blocking reason id to a string
|
||||
*
|
||||
* @reason_id: blocked_reason
|
||||
*
|
||||
* Return: Suitable string
|
||||
*/
|
||||
static const char *blocked_reason_to_string(u32 reason_id)
|
||||
{
|
||||
/* possible blocking reasons of a cs */
|
||||
static const char *const cs_blocked_reason[] = {
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_UNBLOCKED] = "UNBLOCKED",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_WAIT] = "WAIT",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_PROGRESS_WAIT] =
|
||||
"PROGRESS_WAIT",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_SYNC_WAIT] = "SYNC_WAIT",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_DEFERRED] = "DEFERRED",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_RESOURCE] = "RESOURCE",
|
||||
[CS_STATUS_BLOCKED_REASON_REASON_FLUSH] = "FLUSH"
|
||||
};
|
||||
|
||||
if (WARN_ON(reason_id >= ARRAY_SIZE(cs_blocked_reason)))
|
||||
return "UNKNOWN_BLOCKED_REASON_ID";
|
||||
|
||||
return cs_blocked_reason[reason_id];
|
||||
}
|
||||
|
||||
static void kbasep_csf_scheduler_dump_active_queue_cs_status_wait(
|
||||
struct seq_file *file,
|
||||
u32 wait_status,
|
||||
u32 wait_sync_value,
|
||||
u64 wait_sync_live_value,
|
||||
u64 wait_sync_pointer)
|
||||
struct seq_file *file, u32 wait_status, u32 wait_sync_value,
|
||||
u64 wait_sync_live_value, u64 wait_sync_pointer, u32 sb_status,
|
||||
u32 blocked_reason)
|
||||
{
|
||||
#define WAITING "Waiting"
|
||||
#define NOT_WAITING "Not waiting"
|
||||
@@ -56,6 +80,11 @@ static void kbasep_csf_scheduler_dump_active_queue_cs_status_wait(
|
||||
seq_printf(file, "SYNC_POINTER: 0x%llx\n", wait_sync_pointer);
|
||||
seq_printf(file, "SYNC_VALUE: %d\n", wait_sync_value);
|
||||
seq_printf(file, "SYNC_LIVE_VALUE: 0x%016llx\n", wait_sync_live_value);
|
||||
seq_printf(file, "SB_STATUS: %u\n",
|
||||
CS_STATUS_SCOREBOARDS_NONZERO_GET(sb_status));
|
||||
seq_printf(file, "BLOCKED_REASON: %s\n",
|
||||
blocked_reason_to_string(CS_STATUS_BLOCKED_REASON_REASON_GET(
|
||||
blocked_reason)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +103,8 @@ static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file,
|
||||
u32 cs_active;
|
||||
u64 wait_sync_pointer;
|
||||
u32 wait_status, wait_sync_value;
|
||||
u32 sb_status;
|
||||
u32 blocked_reason;
|
||||
struct kbase_vmap_struct *mapping;
|
||||
u64 *evt;
|
||||
u64 wait_sync_live_value;
|
||||
@@ -109,6 +140,8 @@ static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file,
|
||||
wait_status = queue->status_wait;
|
||||
wait_sync_value = queue->sync_value;
|
||||
wait_sync_pointer = queue->sync_ptr;
|
||||
sb_status = queue->sb_status;
|
||||
blocked_reason = queue->blocked_reason;
|
||||
|
||||
evt = (u64 *)kbase_phy_alloc_mapping_get(queue->kctx, wait_sync_pointer, &mapping);
|
||||
if (evt) {
|
||||
@@ -120,7 +153,8 @@ static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file,
|
||||
|
||||
kbasep_csf_scheduler_dump_active_queue_cs_status_wait(
|
||||
file, wait_status, wait_sync_value,
|
||||
wait_sync_live_value, wait_sync_pointer);
|
||||
wait_sync_live_value, wait_sync_pointer,
|
||||
sb_status, blocked_reason);
|
||||
}
|
||||
} else {
|
||||
struct kbase_device const *const kbdev =
|
||||
@@ -161,6 +195,11 @@ static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file,
|
||||
wait_sync_pointer |= (u64)kbase_csf_firmware_cs_output(stream,
|
||||
CS_STATUS_WAIT_SYNC_POINTER_HI) << 32;
|
||||
|
||||
sb_status = kbase_csf_firmware_cs_output(stream,
|
||||
CS_STATUS_SCOREBOARDS);
|
||||
blocked_reason = kbase_csf_firmware_cs_output(
|
||||
stream, CS_STATUS_BLOCKED_REASON);
|
||||
|
||||
evt = (u64 *)kbase_phy_alloc_mapping_get(queue->kctx, wait_sync_pointer, &mapping);
|
||||
if (evt) {
|
||||
wait_sync_live_value = evt[0];
|
||||
@@ -171,7 +210,8 @@ static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file,
|
||||
|
||||
kbasep_csf_scheduler_dump_active_queue_cs_status_wait(
|
||||
file, wait_status, wait_sync_value,
|
||||
wait_sync_live_value, wait_sync_pointer);
|
||||
wait_sync_live_value, wait_sync_pointer, sb_status,
|
||||
blocked_reason);
|
||||
}
|
||||
|
||||
seq_puts(file, "\n");
|
||||
@@ -428,6 +468,61 @@ DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_kick_fops,
|
||||
&kbasep_csf_debugfs_scheduling_timer_kick_set,
|
||||
"%llu\n");
|
||||
|
||||
/**
|
||||
* kbase_csf_debugfs_scheduler_suspend_get() - get if the scheduler is suspended.
|
||||
*
|
||||
* @data: The debugfs dentry private data, a pointer to kbase_device
|
||||
* @val: The debugfs output value, boolean: 1 suspended, 0 otherwise
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
static int kbase_csf_debugfs_scheduler_suspend_get(
|
||||
void *data, u64 *val)
|
||||
{
|
||||
struct kbase_device *kbdev = data;
|
||||
struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler;
|
||||
|
||||
kbase_csf_scheduler_lock(kbdev);
|
||||
*val = (scheduler->state == SCHED_SUSPENDED);
|
||||
kbase_csf_scheduler_unlock(kbdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_debugfs_scheduler_suspend_set() - set the scheduler to suspended.
|
||||
*
|
||||
* @data: The debugfs dentry private data, a pointer to kbase_device
|
||||
* @val: The debugfs input value, boolean: 1 suspend, 0 otherwise
|
||||
*
|
||||
* Return: Negative value if already in requested state, 0 otherwise.
|
||||
*/
|
||||
static int kbase_csf_debugfs_scheduler_suspend_set(
|
||||
void *data, u64 val)
|
||||
{
|
||||
struct kbase_device *kbdev = data;
|
||||
struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler;
|
||||
enum kbase_csf_scheduler_state state;
|
||||
|
||||
kbase_csf_scheduler_lock(kbdev);
|
||||
state = scheduler->state;
|
||||
kbase_csf_scheduler_unlock(kbdev);
|
||||
|
||||
if (val && (state != SCHED_SUSPENDED))
|
||||
kbase_csf_scheduler_pm_suspend(kbdev);
|
||||
else if (!val && (state == SCHED_SUSPENDED))
|
||||
kbase_csf_scheduler_pm_resume(kbdev);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduler_suspend_fops,
|
||||
&kbase_csf_debugfs_scheduler_suspend_get,
|
||||
&kbase_csf_debugfs_scheduler_suspend_set,
|
||||
"%llu\n");
|
||||
|
||||
void kbase_csf_debugfs_init(struct kbase_device *kbdev)
|
||||
{
|
||||
debugfs_create_file("active_groups", 0444,
|
||||
@@ -440,6 +535,9 @@ void kbase_csf_debugfs_init(struct kbase_device *kbdev)
|
||||
debugfs_create_file("scheduling_timer_kick", 0200,
|
||||
kbdev->mali_debugfs_directory, kbdev,
|
||||
&kbasep_csf_debugfs_scheduling_timer_kick_fops);
|
||||
debugfs_create_file("scheduler_suspend", 0644,
|
||||
kbdev->mali_debugfs_directory, kbdev,
|
||||
&kbasep_csf_debugfs_scheduler_suspend_fops);
|
||||
|
||||
kbase_csf_tl_reader_debugfs_init(kbdev);
|
||||
kbase_csf_firmware_trace_buffer_debugfs_init(kbdev);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_CSG_DEBUGFS_H_
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2018-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,11 +17,9 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/* Definitions (types, defines, etcs) common to the command stream frontend.
|
||||
/* Definitions (types, defines, etcs) common to the CSF.
|
||||
* They are placed here to allow the hierarchy of header files to work.
|
||||
*/
|
||||
|
||||
@@ -46,6 +45,14 @@
|
||||
*/
|
||||
#define MAX_TILER_HEAPS (128)
|
||||
|
||||
#define CSF_FIRMWARE_ENTRY_READ (1ul << 0)
|
||||
#define CSF_FIRMWARE_ENTRY_WRITE (1ul << 1)
|
||||
#define CSF_FIRMWARE_ENTRY_EXECUTE (1ul << 2)
|
||||
#define CSF_FIRMWARE_ENTRY_CACHE_MODE (3ul << 3)
|
||||
#define CSF_FIRMWARE_ENTRY_PROTECTED (1ul << 5)
|
||||
#define CSF_FIRMWARE_ENTRY_SHARED (1ul << 30)
|
||||
#define CSF_FIRMWARE_ENTRY_ZERO (1ul << 31)
|
||||
|
||||
/**
|
||||
* enum kbase_csf_bind_state - bind state of the queue
|
||||
*
|
||||
@@ -66,18 +73,36 @@ enum kbase_csf_queue_bind_state {
|
||||
* enum kbase_csf_reset_gpu_state - state of the gpu reset
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_NOT_PENDING: Set when the GPU reset isn't pending
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_PREPARED: Set when kbase_prepare_to_reset_gpu() has
|
||||
* been called. This is just for debugging checks to encourage callers to call
|
||||
* kbase_prepare_to_reset_gpu() before kbase_reset_gpu().
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_COMMITTED: Set when the GPU reset process has been
|
||||
* committed and so will definitely happen, but the procedure to reset the GPU
|
||||
* has not yet begun. Other threads must finish accessing the HW before we
|
||||
* reach %KBASE_CSF_RESET_GPU_HAPPENING.
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_HAPPENING: Set when the GPU reset process is occurring
|
||||
* @KBASE_CSF_RESET_GPU_SILENT: Set when the GPU reset process is occurring,
|
||||
* used when resetting the GPU as part of normal behavior (e.g. when exiting
|
||||
* protected mode).
|
||||
* (silent or otherwise), and is actively accessing the HW. Any changes to the
|
||||
* HW in other threads might get lost, overridden, or corrupted.
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_COMMITTED_SILENT: Set when the GPU reset process has
|
||||
* been committed but has not started happening. This is used when resetting
|
||||
* the GPU as part of normal behavior (e.g. when exiting protected mode).
|
||||
* Other threads must finish accessing the HW before we reach
|
||||
* %KBASE_CSF_RESET_GPU_HAPPENING.
|
||||
*
|
||||
* @KBASE_CSF_RESET_GPU_FAILED: Set when an error is encountered during the
|
||||
* GPU reset process. No more work could then be executed on GPU, unloading
|
||||
* the Driver module is the only option.
|
||||
*/
|
||||
enum kbase_csf_reset_gpu_state {
|
||||
KBASE_CSF_RESET_GPU_NOT_PENDING,
|
||||
KBASE_CSF_RESET_GPU_PREPARED,
|
||||
KBASE_CSF_RESET_GPU_COMMITTED,
|
||||
KBASE_CSF_RESET_GPU_HAPPENING,
|
||||
KBASE_CSF_RESET_GPU_SILENT,
|
||||
KBASE_CSF_RESET_GPU_COMMITTED_SILENT,
|
||||
KBASE_CSF_RESET_GPU_FAILED,
|
||||
};
|
||||
|
||||
@@ -86,17 +111,17 @@ enum kbase_csf_reset_gpu_state {
|
||||
*
|
||||
* @KBASE_CSF_GROUP_INACTIVE: Group is inactive and won't be
|
||||
* considered by scheduler for running on
|
||||
* command stream group slot.
|
||||
* CSG slot.
|
||||
* @KBASE_CSF_GROUP_RUNNABLE: Group is in the list of runnable groups
|
||||
* and is subjected to time-slice based
|
||||
* scheduling. A start request would be
|
||||
* sent (or already has been sent) if the
|
||||
* group is assigned the command stream
|
||||
* group is assigned the CS
|
||||
* group slot for the fist time.
|
||||
* @KBASE_CSF_GROUP_IDLE: Group is currently on a command stream
|
||||
* group slot but all the command streams
|
||||
* bound to the group have become either
|
||||
* idle or waiting on sync object.
|
||||
* @KBASE_CSF_GROUP_IDLE: Group is currently on a CSG slot
|
||||
* but all the CSs bound to the group have
|
||||
* become either idle or waiting on sync
|
||||
* object.
|
||||
* Group could be evicted from the slot on
|
||||
* the next tick if there are no spare
|
||||
* slots left after scheduling non-idle
|
||||
@@ -110,12 +135,11 @@ enum kbase_csf_reset_gpu_state {
|
||||
* KBASE_CSF_GROUP_SUSPENDED_ON_IDLE or
|
||||
* KBASE_CSF_GROUP_SUSPENDED_ON_WAIT_SYNC
|
||||
* state.
|
||||
* @KBASE_CSF_GROUP_SUSPENDED: Group was evicted from the command
|
||||
* stream group slot and is not running but
|
||||
* is still in the list of runnable groups
|
||||
* and subjected to time-slice based
|
||||
* scheduling. A resume request would be
|
||||
* sent when a command stream group slot is
|
||||
* @KBASE_CSF_GROUP_SUSPENDED: Group was evicted from the CSG slot
|
||||
* and is not running but is still in the
|
||||
* list of runnable groups and subjected
|
||||
* to time-slice based scheduling. A resume
|
||||
* request would be sent when a CSG slot is
|
||||
* re-assigned to the group and once the
|
||||
* resume is complete group would be moved
|
||||
* back to the RUNNABLE state.
|
||||
@@ -128,8 +152,8 @@ enum kbase_csf_reset_gpu_state {
|
||||
* bound to the group is kicked it would be
|
||||
* moved to the SUSPENDED state.
|
||||
* @KBASE_CSF_GROUP_SUSPENDED_ON_WAIT_SYNC: Same as GROUP_SUSPENDED_ON_IDLE
|
||||
* except that at least one command
|
||||
* stream bound to this group was
|
||||
* except that at least one CS
|
||||
* bound to this group was
|
||||
* waiting for synchronization object
|
||||
* before the suspension.
|
||||
* @KBASE_CSF_GROUP_FAULT_EVICTED: Group is evicted from the scheduler due
|
||||
@@ -185,10 +209,10 @@ enum kbase_csf_csg_slot_state {
|
||||
* enum kbase_csf_scheduler_state - state of the scheduler operational phases.
|
||||
*
|
||||
* @SCHED_BUSY: The scheduler is busy performing on tick schedule
|
||||
* operations, the state of command stream group slots
|
||||
* operations, the state of CSG slots
|
||||
* can't be changed.
|
||||
* @SCHED_INACTIVE: The scheduler is inactive, it is allowed to modify the
|
||||
* state of command stream group slots by in-cycle
|
||||
* state of CSG slots by in-cycle
|
||||
* priority scheduling.
|
||||
* @SCHED_SUSPENDED: The scheduler is in low-power mode with scheduling
|
||||
* operations suspended and is not holding the power
|
||||
@@ -202,6 +226,24 @@ enum kbase_csf_scheduler_state {
|
||||
SCHED_SUSPENDED,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum kbase_queue_group_priority - Kbase internal relative priority list.
|
||||
*
|
||||
* @KBASE_QUEUE_GROUP_PRIORITY_REALTIME: The realtime queue group priority.
|
||||
* @KBASE_QUEUE_GROUP_PRIORITY_HIGH: The high queue group priority.
|
||||
* @KBASE_QUEUE_GROUP_PRIORITY_MEDIUM: The medium queue group priority.
|
||||
* @KBASE_QUEUE_GROUP_PRIORITY_LOW: The low queue group priority.
|
||||
* @KBASE_QUEUE_GROUP_PRIORITY_COUNT: The number of priority levels.
|
||||
*/
|
||||
enum kbase_queue_group_priority {
|
||||
KBASE_QUEUE_GROUP_PRIORITY_REALTIME = 0,
|
||||
KBASE_QUEUE_GROUP_PRIORITY_HIGH,
|
||||
KBASE_QUEUE_GROUP_PRIORITY_MEDIUM,
|
||||
KBASE_QUEUE_GROUP_PRIORITY_LOW,
|
||||
KBASE_QUEUE_GROUP_PRIORITY_COUNT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct kbase_csf_notification - Event or error generated as part of command
|
||||
* queue execution
|
||||
@@ -240,37 +282,43 @@ struct kbase_csf_notification {
|
||||
* @refcount: Reference count, stands for the number of times the queue
|
||||
* has been referenced. The reference is taken when it is
|
||||
* created, when it is bound to the group and also when the
|
||||
* @oom_event_work or @fault_event_work work item is queued
|
||||
* @oom_event_work work item is queued
|
||||
* for it.
|
||||
* @group: Pointer to the group to which this queue is bound.
|
||||
* @queue_reg: Pointer to the VA region allocated for command
|
||||
* stream buffer.
|
||||
* @queue_reg: Pointer to the VA region allocated for CS buffer.
|
||||
* @oom_event_work: Work item corresponding to the out of memory event for
|
||||
* chunked tiler heap being used for this queue.
|
||||
* @fault_event_work: Work item corresponding to the firmware fault event.
|
||||
* @base_addr: Base address of the command stream buffer.
|
||||
* @size: Size of the command stream buffer.
|
||||
* @base_addr: Base address of the CS buffer.
|
||||
* @size: Size of the CS buffer.
|
||||
* @priority: Priority of this queue within the group.
|
||||
* @bind_state: Bind state of the queue.
|
||||
* @csi_index: The ID of the assigned command stream hardware interface.
|
||||
* @enabled: Indicating whether the command stream is running, or not.
|
||||
* @status_wait: Value of CS_STATUS_WAIT register of the command stream will
|
||||
* be kept when the command stream gets blocked by sync wait.
|
||||
* @bind_state: Bind state of the queue as enum @kbase_csf_queue_bind_state
|
||||
* @csi_index: The ID of the assigned CS hardware interface.
|
||||
* @enabled: Indicating whether the CS is running, or not.
|
||||
* @status_wait: Value of CS_STATUS_WAIT register of the CS will
|
||||
* be kept when the CS gets blocked by sync wait.
|
||||
* CS_STATUS_WAIT provides information on conditions queue is
|
||||
* blocking on. This is set when the group, to which queue is
|
||||
* bound, is suspended after getting blocked, i.e. in
|
||||
* KBASE_CSF_GROUP_SUSPENDED_ON_WAIT_SYNC state.
|
||||
* @sync_ptr: Value of CS_STATUS_WAIT_SYNC_POINTER register of the command
|
||||
* stream will be kept when the command stream gets blocked by
|
||||
* @sync_ptr: Value of CS_STATUS_WAIT_SYNC_POINTER register of the CS
|
||||
* will be kept when the CS gets blocked by
|
||||
* sync wait. CS_STATUS_WAIT_SYNC_POINTER contains the address
|
||||
* of synchronization object being waited on.
|
||||
* Valid only when @status_wait is set.
|
||||
* @sync_value: Value of CS_STATUS_WAIT_SYNC_VALUE register of the command
|
||||
* stream will be kept when the command stream gets blocked by
|
||||
* @sync_value: Value of CS_STATUS_WAIT_SYNC_VALUE register of the CS
|
||||
* will be kept when the CS gets blocked by
|
||||
* sync wait. CS_STATUS_WAIT_SYNC_VALUE contains the value
|
||||
* tested against the synchronization object.
|
||||
* Valid only when @status_wait is set.
|
||||
* @sb_status: Value indicates which of the scoreboard entries in the queue
|
||||
* are non-zero
|
||||
* @blocked_reason: Value shows if the queue is blocked, and if so,
|
||||
* the reason why it is blocked
|
||||
* @error: GPU command queue fatal information to pass to user space.
|
||||
* @fatal_event_work: Work item to handle the CS fatal event reported for this
|
||||
* queue.
|
||||
* @cs_fatal_info: Records additional information about the CS fatal event.
|
||||
* @cs_fatal: Records information about the CS fatal event.
|
||||
*/
|
||||
struct kbase_queue {
|
||||
struct kbase_context *kctx;
|
||||
@@ -285,17 +333,21 @@ struct kbase_queue {
|
||||
struct kbase_queue_group *group;
|
||||
struct kbase_va_region *queue_reg;
|
||||
struct work_struct oom_event_work;
|
||||
struct work_struct fault_event_work;
|
||||
u64 base_addr;
|
||||
u32 size;
|
||||
u8 priority;
|
||||
u8 bind_state;
|
||||
s8 csi_index;
|
||||
enum kbase_csf_queue_bind_state bind_state;
|
||||
bool enabled;
|
||||
u32 status_wait;
|
||||
u64 sync_ptr;
|
||||
u32 sync_value;
|
||||
u32 sb_status;
|
||||
u32 blocked_reason;
|
||||
struct kbase_csf_notification error;
|
||||
struct work_struct fatal_event_work;
|
||||
u64 cs_fatal_info;
|
||||
u32 cs_fatal;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -335,9 +387,9 @@ struct kbase_protected_suspend_buffer {
|
||||
* buffer. Protected-mode suspend buffer that is
|
||||
* used for group context switch.
|
||||
* @handle: Handle which identifies this queue group.
|
||||
* @csg_nr: Number/index of the command stream group to
|
||||
* which this queue group is mapped; KBASEP_CSG_NR_INVALID
|
||||
* indicates that the queue group is not scheduled.
|
||||
* @csg_nr: Number/index of the CSG to which this queue group is
|
||||
* mapped; KBASEP_CSG_NR_INVALID indicates that the queue
|
||||
* group is not scheduled.
|
||||
* @priority: Priority of the queue group, 0 being the highest,
|
||||
* BASE_QUEUE_GROUP_PRIORITY_COUNT - 1 being the lowest.
|
||||
* @tiler_max: Maximum number of tiler endpoints the group is allowed
|
||||
@@ -349,18 +401,21 @@ struct kbase_protected_suspend_buffer {
|
||||
* @tiler_mask: Mask of tiler endpoints the group is allowed to use.
|
||||
* @fragment_mask: Mask of fragment endpoints the group is allowed to use.
|
||||
* @compute_mask: Mask of compute endpoints the group is allowed to use.
|
||||
* @group_uid: 32-bit wide unsigned identifier for the group, unique
|
||||
* across all kbase devices and contexts.
|
||||
* @link: Link to this queue group in the 'runnable_groups' list of
|
||||
* the corresponding kctx.
|
||||
* @link_to_schedule: Link to this queue group in the list of prepared groups
|
||||
* to be scheduled, if the group is runnable/suspended.
|
||||
* If the group is idle or waiting for CQS, it would be a
|
||||
* link to the list of idle/blocked groups list.
|
||||
* @timer_event_work: Work item corresponding to the event generated when a task
|
||||
* started by a queue in this group takes too long to execute
|
||||
* on an endpoint.
|
||||
* @run_state: Current state of the queue group.
|
||||
* @prepared_seq_num: Indicates the position of queue group in the list of
|
||||
* prepared groups to be scheduled.
|
||||
* @scan_seq_num: Scan out sequence number before adjusting for dynamic
|
||||
* idle conditions. It is used for setting a group's
|
||||
* onslot priority. It could differ from prepared_seq_number
|
||||
* when there are idle groups.
|
||||
* @faulted: Indicates that a GPU fault occurred for the queue group.
|
||||
* This flag persists until the fault has been queued to be
|
||||
* reported to userspace.
|
||||
@@ -369,7 +424,7 @@ struct kbase_protected_suspend_buffer {
|
||||
* group.
|
||||
* @protm_event_work: Work item corresponding to the protected mode entry
|
||||
* event for this queue.
|
||||
* @protm_pending_bitmap: Bit array to keep a track of command streams that
|
||||
* @protm_pending_bitmap: Bit array to keep a track of CSs that
|
||||
* have pending protected mode entry requests.
|
||||
* @error_fatal: An error of type BASE_GPU_QUEUE_GROUP_ERROR_FATAL to be
|
||||
* returned to userspace if such an error has occurred.
|
||||
@@ -377,6 +432,8 @@ struct kbase_protected_suspend_buffer {
|
||||
* to be returned to userspace if such an error has occurred.
|
||||
* @error_tiler_oom: An error of type BASE_GPU_QUEUE_GROUP_ERROR_TILER_HEAP_OOM
|
||||
* to be returned to userspace if such an error has occurred.
|
||||
* @timer_event_work: Work item to handle the progress timeout fatal event
|
||||
* for the group.
|
||||
*/
|
||||
struct kbase_queue_group {
|
||||
struct kbase_context *kctx;
|
||||
@@ -394,11 +451,13 @@ struct kbase_queue_group {
|
||||
u64 fragment_mask;
|
||||
u64 compute_mask;
|
||||
|
||||
u32 group_uid;
|
||||
|
||||
struct list_head link;
|
||||
struct list_head link_to_schedule;
|
||||
struct work_struct timer_event_work;
|
||||
enum kbase_csf_group_state run_state;
|
||||
u32 prepared_seq_num;
|
||||
u32 scan_seq_num;
|
||||
bool faulted;
|
||||
|
||||
struct kbase_queue *bound_queues[MAX_SUPPORTED_STREAMS_PER_GROUP];
|
||||
@@ -410,6 +469,8 @@ struct kbase_queue_group {
|
||||
struct kbase_csf_notification error_fatal;
|
||||
struct kbase_csf_notification error_timeout;
|
||||
struct kbase_csf_notification error_tiler_oom;
|
||||
|
||||
struct work_struct timer_event_work;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -442,6 +503,22 @@ struct kbase_csf_kcpu_queue_context {
|
||||
struct list_head jit_blocked_queues;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_cpu_queue_context - Object representing the cpu queue
|
||||
* information.
|
||||
*
|
||||
* @buffer: Buffer containing CPU queue information provided by Userspace.
|
||||
* @buffer_size: The size of @buffer.
|
||||
* @dump_req_status: Indicates the current status for CPU queues dump request.
|
||||
* @dump_cmp: Dumping cpu queue completion event.
|
||||
*/
|
||||
struct kbase_csf_cpu_queue_context {
|
||||
char *buffer;
|
||||
size_t buffer_size;
|
||||
atomic_t dump_req_status;
|
||||
struct completion dump_cmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_heap_context_allocator - Allocator of heap contexts
|
||||
*
|
||||
@@ -472,18 +549,21 @@ struct kbase_csf_heap_context_allocator {
|
||||
* struct kbase_csf_tiler_heap_context - Object representing the tiler heaps
|
||||
* context for a GPU address space.
|
||||
*
|
||||
* This contains all of the command-stream front-end state relating to chunked
|
||||
* tiler heaps for one @kbase_context. It is not the same as a heap context
|
||||
* structure allocated by the kernel for use by the firmware.
|
||||
* This contains all of the CSF state relating to chunked tiler heaps for one
|
||||
* @kbase_context. It is not the same as a heap context structure allocated by
|
||||
* the kernel for use by the firmware.
|
||||
*
|
||||
* @lock: Lock preventing concurrent access to the tiler heaps.
|
||||
* @list: List of tiler heaps.
|
||||
* @ctx_alloc: Allocator for heap context structures.
|
||||
* @lock: Lock preventing concurrent access to the tiler heaps.
|
||||
* @list: List of tiler heaps.
|
||||
* @ctx_alloc: Allocator for heap context structures.
|
||||
* @nr_of_heaps: Total number of tiler heaps that were added during the
|
||||
* life time of the context.
|
||||
*/
|
||||
struct kbase_csf_tiler_heap_context {
|
||||
struct mutex lock;
|
||||
struct list_head list;
|
||||
struct kbase_csf_heap_context_allocator ctx_alloc;
|
||||
u64 nr_of_heaps;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -491,7 +571,7 @@ struct kbase_csf_tiler_heap_context {
|
||||
* context for a GPU address space.
|
||||
*
|
||||
* @runnable_groups: Lists of runnable GPU command queue groups in the kctx,
|
||||
* one per queue group priority level.
|
||||
* one per queue group relative-priority level.
|
||||
* @num_runnable_grps: Total number of runnable groups across all priority
|
||||
* levels in @runnable_groups.
|
||||
* @idle_wait_groups: A list of GPU command queue groups in which all enabled
|
||||
@@ -500,7 +580,7 @@ struct kbase_csf_tiler_heap_context {
|
||||
* @num_idle_wait_grps: Length of the @idle_wait_groups list.
|
||||
* @sync_update_wq: Dedicated workqueue to process work items corresponding
|
||||
* to the sync_update events by sync_set/sync_add
|
||||
* instruction execution on command streams bound to groups
|
||||
* instruction execution on CSs bound to groups
|
||||
* of @idle_wait_groups list.
|
||||
* @sync_update_work: work item to process the sync_update events by
|
||||
* sync_set / sync_add instruction execution on command
|
||||
@@ -509,7 +589,7 @@ struct kbase_csf_tiler_heap_context {
|
||||
* 'groups_to_schedule' list of scheduler instance.
|
||||
*/
|
||||
struct kbase_csf_scheduler_context {
|
||||
struct list_head runnable_groups[BASE_QUEUE_GROUP_PRIORITY_COUNT];
|
||||
struct list_head runnable_groups[KBASE_QUEUE_GROUP_PRIORITY_COUNT];
|
||||
u32 num_runnable_grps;
|
||||
struct list_head idle_wait_groups;
|
||||
u32 num_idle_wait_grps;
|
||||
@@ -519,8 +599,7 @@ struct kbase_csf_scheduler_context {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_context - Object representing command-stream front-end
|
||||
* for a GPU address space.
|
||||
* struct kbase_csf_context - Object representing CSF for a GPU address space.
|
||||
*
|
||||
* @event_pages_head: A list of pages allocated for the event memory used by
|
||||
* the synchronization objects. A separate list would help
|
||||
@@ -534,7 +613,7 @@ struct kbase_csf_scheduler_context {
|
||||
* deferred manner of a pair of User mode input/output pages
|
||||
* & a hardware doorbell page.
|
||||
* The pages are allocated when a GPU command queue is
|
||||
* bound to a command stream group in kbase_csf_queue_bind.
|
||||
* bound to a CSG in kbase_csf_queue_bind.
|
||||
* This helps returning unique handles to Userspace from
|
||||
* kbase_csf_queue_bind and later retrieving the pointer to
|
||||
* queue in the mmap handler.
|
||||
@@ -550,7 +629,8 @@ struct kbase_csf_scheduler_context {
|
||||
* userspace mapping created for them on bind operation
|
||||
* hasn't been removed.
|
||||
* @kcpu_queues: Kernel CPU command queues.
|
||||
* @event_lock: Lock protecting access to @event_callback_list
|
||||
* @event_lock: Lock protecting access to @event_callback_list and
|
||||
* @error_list.
|
||||
* @event_callback_list: List of callbacks which are registered to serve CSF
|
||||
* events.
|
||||
* @tiler_heaps: Chunked tiler memory heaps.
|
||||
@@ -563,10 +643,12 @@ struct kbase_csf_scheduler_context {
|
||||
* of the USER register page. Currently used only for sanity
|
||||
* checking.
|
||||
* @sched: Object representing the scheduler's context
|
||||
* @error_list: List for command stream fatal errors in this context.
|
||||
* @error_list: List for CS fatal errors in this context.
|
||||
* Link of fatal error is
|
||||
* &struct_kbase_csf_notification.link.
|
||||
* @lock needs to be held to access to this list.
|
||||
* @event_lock needs to be held to access this list.
|
||||
* @cpu_queue: CPU queue information. Only be available when DEBUG_FS
|
||||
* is enabled.
|
||||
*/
|
||||
struct kbase_csf_context {
|
||||
struct list_head event_pages_head;
|
||||
@@ -585,6 +667,9 @@ struct kbase_csf_context {
|
||||
struct vm_area_struct *user_reg_vma;
|
||||
struct kbase_csf_scheduler_context sched;
|
||||
struct list_head error_list;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct kbase_csf_cpu_queue_context cpu_queue;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -593,23 +678,28 @@ struct kbase_csf_context {
|
||||
* @workq: Workqueue to execute the GPU reset work item @work.
|
||||
* @work: Work item for performing the GPU reset.
|
||||
* @wait: Wait queue used to wait for the GPU reset completion.
|
||||
* @sem: RW Semaphore to ensure no other thread attempts to use the
|
||||
* GPU whilst a reset is in process. Unlike traditional
|
||||
* semaphores and wait queues, this allows Linux's lockdep
|
||||
* mechanism to check for deadlocks involving reset waits.
|
||||
* @state: Tracks if the GPU reset is in progress or not.
|
||||
* The state is represented by enum @kbase_csf_reset_gpu_state.
|
||||
*/
|
||||
struct kbase_csf_reset_gpu {
|
||||
struct workqueue_struct *workq;
|
||||
struct work_struct work;
|
||||
wait_queue_head_t wait;
|
||||
struct rw_semaphore sem;
|
||||
atomic_t state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_csg_slot - Object containing members for tracking the state
|
||||
* of command stream group slots.
|
||||
* @resident_group: pointer to the queue group that is resident on the
|
||||
* command stream group slot.
|
||||
* @state: state of the slot as per enum kbase_csf_csg_slot_state.
|
||||
* of CSG slots.
|
||||
* @resident_group: pointer to the queue group that is resident on the CSG slot.
|
||||
* @state: state of the slot as per enum @kbase_csf_csg_slot_state.
|
||||
* @trigger_jiffies: value of jiffies when change in slot state is recorded.
|
||||
* @priority: dynamic priority assigned to command stream group slot.
|
||||
* @priority: dynamic priority assigned to CSG slot.
|
||||
*/
|
||||
struct kbase_csf_csg_slot {
|
||||
struct kbase_queue_group *resident_group;
|
||||
@@ -620,8 +710,7 @@ struct kbase_csf_csg_slot {
|
||||
|
||||
/**
|
||||
* struct kbase_csf_scheduler - Object representing the scheduler used for
|
||||
* command-stream front-end for an instance of
|
||||
* GPU platform device.
|
||||
* CSF for an instance of GPU platform device.
|
||||
* @lock: Lock to serialize the scheduler operations and
|
||||
* access to the data members.
|
||||
* @interrupt_lock: Lock to protect members accessed by interrupt
|
||||
@@ -632,26 +721,29 @@ struct kbase_csf_csg_slot {
|
||||
* @doorbell_inuse_bitmap: Bitmap of hardware doorbell pages keeping track of
|
||||
* which pages are currently available for assignment
|
||||
* to clients.
|
||||
* @csg_inuse_bitmap: Bitmap to keep a track of command stream group slots
|
||||
* @csg_inuse_bitmap: Bitmap to keep a track of CSG slots
|
||||
* that are currently in use.
|
||||
* @csg_slots: The array for tracking the state of command stream
|
||||
* @csg_slots: The array for tracking the state of CS
|
||||
* group slots.
|
||||
* @runnable_kctxs: List of Kbase contexts that have runnable command
|
||||
* queue groups.
|
||||
* @groups_to_schedule: List of runnable queue groups prepared on every
|
||||
* scheduler tick. The dynamic priority of the command
|
||||
* stream group slot assigned to a group will depend
|
||||
* upon the position of group in the list.
|
||||
* scheduler tick. The dynamic priority of the CSG
|
||||
* slot assigned to a group will depend upon the
|
||||
* position of group in the list.
|
||||
* @ngrp_to_schedule: Number of groups in the @groups_to_schedule list,
|
||||
* incremented when a group is added to the list, used
|
||||
* to record the position of group in the list.
|
||||
* @num_active_address_spaces: Number of GPU address space slots that would get
|
||||
* used to program the groups in @groups_to_schedule
|
||||
* list on all the available command stream group
|
||||
* list on all the available CSG
|
||||
* slots.
|
||||
* @num_csg_slots_for_tick: Number of command stream group slots that can be
|
||||
* @num_csg_slots_for_tick: Number of CSG slots that can be
|
||||
* active in the given tick/tock. This depends on the
|
||||
* value of @num_active_address_spaces.
|
||||
* @remaining_tick_slots: Tracking the number of remaining available slots
|
||||
* for @num_csg_slots_for_tick during the scheduling
|
||||
* operation in a tick/tock.
|
||||
* @idle_groups_to_schedule: List of runnable queue groups, in which all GPU
|
||||
* command queues became idle or are waiting for
|
||||
* synchronization object, prepared on every
|
||||
@@ -659,11 +751,14 @@ struct kbase_csf_csg_slot {
|
||||
* appended to the tail of @groups_to_schedule list
|
||||
* after the scan out so that the idle groups aren't
|
||||
* preferred for scheduling over the non-idle ones.
|
||||
* @csg_scan_count_for_tick: CSG scanout count for assign the scan_seq_num for
|
||||
* each scanned out group during scheduling operation
|
||||
* in a tick/tock.
|
||||
* @total_runnable_grps: Total number of runnable groups across all KCTXs.
|
||||
* @csgs_events_enable_mask: Use for temporary masking off asynchronous events
|
||||
* from firmware (such as OoM events) before a group
|
||||
* is suspended.
|
||||
* @csg_slots_idle_mask: Bit array for storing the mask of command stream
|
||||
* @csg_slots_idle_mask: Bit array for storing the mask of CS
|
||||
* group slots for which idle notification was
|
||||
* received.
|
||||
* @csg_slots_prio_update: Bit array for tracking slots that have an on-slot
|
||||
@@ -677,39 +772,53 @@ struct kbase_csf_csg_slot {
|
||||
* then it will only perform scheduling under the
|
||||
* influence of external factors e.g., IRQs, IOCTLs.
|
||||
* @wq: Dedicated workqueue to execute the @tick_work.
|
||||
* @tick_work: Work item that would perform the schedule on tick
|
||||
* operation to implement the time slice based
|
||||
* scheduling.
|
||||
* @tick_timer: High-resolution timer employed to schedule tick
|
||||
* workqueue items (kernel-provided delayed_work
|
||||
* items do not use hrtimer and for some reason do
|
||||
* not provide sufficiently reliable periodicity).
|
||||
* @tick_work: Work item that performs the "schedule on tick"
|
||||
* operation to implement timeslice-based scheduling.
|
||||
* @tock_work: Work item that would perform the schedule on tock
|
||||
* operation to implement the asynchronous scheduling.
|
||||
* @ping_work: Work item that would ping the firmware at regular
|
||||
* intervals, only if there is a single active command
|
||||
* stream group slot, to check if firmware is alive
|
||||
* and would initiate a reset if the ping request
|
||||
* isn't acknowledged.
|
||||
* intervals, only if there is a single active CSG
|
||||
* slot, to check if firmware is alive and would
|
||||
* initiate a reset if the ping request isn't
|
||||
* acknowledged.
|
||||
* @top_ctx: Pointer to the Kbase context corresponding to the
|
||||
* @top_grp.
|
||||
* @top_grp: Pointer to queue group inside @groups_to_schedule
|
||||
* list that was assigned the highest slot priority.
|
||||
* @head_slot_priority: The dynamic slot priority to be used for the
|
||||
* queue group at the head of @groups_to_schedule
|
||||
* list. Once the queue group is assigned a command
|
||||
* stream group slot, it is removed from the list and
|
||||
* priority is decremented.
|
||||
* @tock_pending_request: A "tock" request is pending: a group that is not
|
||||
* currently on the GPU demands to be scheduled.
|
||||
* @active_protm_grp: Indicates if firmware has been permitted to let GPU
|
||||
* enter protected mode with the given group. On exit
|
||||
* from protected mode the pointer is reset to NULL.
|
||||
* @gpu_idle_fw_timer_enabled: Whether the CSF scheduler has activiated the
|
||||
* firmware idle hysteresis timer for preparing a
|
||||
* GPU suspend on idle.
|
||||
* @gpu_idle_work: Work item for facilitating the scheduler to bring
|
||||
* the GPU to a low-power mode on becoming idle.
|
||||
* @non_idle_suspended_grps: Count of suspended queue groups not idle.
|
||||
* @non_idle_offslot_grps: Count of off-slot non-idle groups. Reset during
|
||||
* the scheduler active phase in a tick. It then
|
||||
* tracks the count of non-idle groups across all the
|
||||
* other phases.
|
||||
* @non_idle_scanout_grps: Count on the non-idle groups in the scan-out
|
||||
* list at the scheduling prepare stage.
|
||||
* @pm_active_count: Count indicating if the scheduler is owning a power
|
||||
* management reference count. Reference is taken when
|
||||
* the count becomes 1 and is dropped when the count
|
||||
* becomes 0. It is used to enable the power up of MCU
|
||||
* after GPU and L2 cache have been powered up. So when
|
||||
* this count is zero, MCU will not be powered up.
|
||||
* @csg_scheduling_period_ms: Duration of Scheduling tick in milliseconds.
|
||||
* @tick_timer_active: Indicates whether the @tick_timer is effectively
|
||||
* active or not, as the callback function of
|
||||
* @tick_timer will enqueue @tick_work only if this
|
||||
* flag is true. This is mainly useful for the case
|
||||
* when scheduling tick needs to be advanced from
|
||||
* interrupt context, without actually deactivating
|
||||
* the @tick_timer first and then enqueing @tick_work.
|
||||
*/
|
||||
struct kbase_csf_scheduler {
|
||||
struct mutex lock;
|
||||
@@ -723,7 +832,9 @@ struct kbase_csf_scheduler {
|
||||
u32 ngrp_to_schedule;
|
||||
u32 num_active_address_spaces;
|
||||
u32 num_csg_slots_for_tick;
|
||||
u32 remaining_tick_slots;
|
||||
struct list_head idle_groups_to_schedule;
|
||||
u32 csg_scan_count_for_tick;
|
||||
u32 total_runnable_grps;
|
||||
DECLARE_BITMAP(csgs_events_enable_mask, MAX_SUPPORTED_CSGS);
|
||||
DECLARE_BITMAP(csg_slots_idle_mask, MAX_SUPPORTED_CSGS);
|
||||
@@ -731,17 +842,21 @@ struct kbase_csf_scheduler {
|
||||
unsigned long last_schedule;
|
||||
bool timer_enabled;
|
||||
struct workqueue_struct *wq;
|
||||
struct delayed_work tick_work;
|
||||
struct hrtimer tick_timer;
|
||||
struct work_struct tick_work;
|
||||
struct delayed_work tock_work;
|
||||
struct delayed_work ping_work;
|
||||
struct kbase_context *top_ctx;
|
||||
struct kbase_queue_group *top_grp;
|
||||
u8 head_slot_priority;
|
||||
bool tock_pending_request;
|
||||
struct kbase_queue_group *active_protm_grp;
|
||||
struct delayed_work gpu_idle_work;
|
||||
atomic_t non_idle_suspended_grps;
|
||||
bool gpu_idle_fw_timer_enabled;
|
||||
struct work_struct gpu_idle_work;
|
||||
atomic_t non_idle_offslot_grps;
|
||||
u32 non_idle_scanout_grps;
|
||||
u32 pm_active_count;
|
||||
unsigned int csg_scheduling_period_ms;
|
||||
bool tick_timer_active;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -758,8 +873,205 @@ struct kbase_csf_scheduler {
|
||||
GLB_PROGRESS_TIMER_TIMEOUT_SCALE)
|
||||
|
||||
/**
|
||||
* struct kbase_csf - Object representing command-stream front-end for an
|
||||
* instance of GPU platform device.
|
||||
* Default GLB_PWROFF_TIMER_TIMEOUT value in unit of micro-seconds.
|
||||
*/
|
||||
#define DEFAULT_GLB_PWROFF_TIMEOUT_US (800)
|
||||
|
||||
/**
|
||||
* In typical operations, the management of the shader core power transitions
|
||||
* is delegated to the MCU/firmware. However, if the host driver is configured
|
||||
* to take direct control, one needs to disable the MCU firmware GLB_PWROFF
|
||||
* timer.
|
||||
*/
|
||||
#define DISABLE_GLB_PWROFF_TIMER (0)
|
||||
|
||||
/* Index of the GPU_ACTIVE counter within the CSHW counter block */
|
||||
#define GPU_ACTIVE_CNT_IDX (4)
|
||||
|
||||
/**
|
||||
* Maximum number of sessions that can be managed by the IPA Control component.
|
||||
*/
|
||||
#if MALI_UNIT_TEST
|
||||
#define KBASE_IPA_CONTROL_MAX_SESSIONS ((size_t)8)
|
||||
#else
|
||||
#define KBASE_IPA_CONTROL_MAX_SESSIONS ((size_t)2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* enum kbase_ipa_core_type - Type of counter block for performance counters
|
||||
*
|
||||
* @KBASE_IPA_CORE_TYPE_CSHW: CS Hardware counters.
|
||||
* @KBASE_IPA_CORE_TYPE_MEMSYS: Memory System counters.
|
||||
* @KBASE_IPA_CORE_TYPE_TILER: Tiler counters.
|
||||
* @KBASE_IPA_CORE_TYPE_SHADER: Shader Core counters.
|
||||
* @KBASE_IPA_CORE_TYPE_NUM: Number of core types.
|
||||
*/
|
||||
enum kbase_ipa_core_type {
|
||||
KBASE_IPA_CORE_TYPE_CSHW = 0,
|
||||
KBASE_IPA_CORE_TYPE_MEMSYS,
|
||||
KBASE_IPA_CORE_TYPE_TILER,
|
||||
KBASE_IPA_CORE_TYPE_SHADER,
|
||||
KBASE_IPA_CORE_TYPE_NUM
|
||||
};
|
||||
|
||||
/**
|
||||
* Number of configurable counters per type of block on the IPA Control
|
||||
* interface.
|
||||
*/
|
||||
#define KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS ((size_t)8)
|
||||
|
||||
/**
|
||||
* Total number of configurable counters existing on the IPA Control interface.
|
||||
*/
|
||||
#define KBASE_IPA_CONTROL_MAX_COUNTERS \
|
||||
((size_t)KBASE_IPA_CORE_TYPE_NUM * KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS)
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_prfcnt - Session for a single performance counter
|
||||
*
|
||||
* @latest_raw_value: Latest raw value read from the counter.
|
||||
* @scaling_factor: Factor raw value shall be multiplied by.
|
||||
* @accumulated_diff: Partial sum of scaled and normalized values from
|
||||
* previous samples. This represent all the values
|
||||
* that were read before the latest raw value.
|
||||
* @type: Type of counter block for performance counter.
|
||||
* @select_idx: Index of the performance counter as configured on
|
||||
* the IPA Control interface.
|
||||
* @gpu_norm: Indicating whether values shall be normalized by
|
||||
* GPU frequency. If true, returned values represent
|
||||
* an interval of time expressed in seconds (when the
|
||||
* scaling factor is set to 1).
|
||||
*/
|
||||
struct kbase_ipa_control_prfcnt {
|
||||
u64 latest_raw_value;
|
||||
u64 scaling_factor;
|
||||
u64 accumulated_diff;
|
||||
enum kbase_ipa_core_type type;
|
||||
u8 select_idx;
|
||||
bool gpu_norm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_session - Session for an IPA Control client
|
||||
*
|
||||
* @prfcnts: Sessions for individual performance counters.
|
||||
* @num_prfcnts: Number of performance counters.
|
||||
* @active: Indicates whether this slot is in use or not
|
||||
* @last_query_time: Time of last query, in ns
|
||||
* @protm_time: Amount of time (in ns) that GPU has been in protected
|
||||
*/
|
||||
struct kbase_ipa_control_session {
|
||||
struct kbase_ipa_control_prfcnt prfcnts[KBASE_IPA_CONTROL_MAX_COUNTERS];
|
||||
size_t num_prfcnts;
|
||||
bool active;
|
||||
u64 last_query_time;
|
||||
u64 protm_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_prfcnt_config - Performance counter configuration
|
||||
*
|
||||
* @idx: Index of the performance counter inside the block, as specified
|
||||
* in the GPU architecture.
|
||||
* @refcount: Number of client sessions bound to this counter.
|
||||
*
|
||||
* This structure represents one configurable performance counter of
|
||||
* the IPA Control interface. The entry may be mapped to a specific counter
|
||||
* by one or more client sessions. The counter is considered to be unused
|
||||
* if it isn't part of any client session.
|
||||
*/
|
||||
struct kbase_ipa_control_prfcnt_config {
|
||||
u8 idx;
|
||||
u8 refcount;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control_prfcnt_block - Block of performance counters
|
||||
*
|
||||
* @select: Current performance counter configuration.
|
||||
* @num_available_counters: Number of counters that are not already configured.
|
||||
*
|
||||
*/
|
||||
struct kbase_ipa_control_prfcnt_block {
|
||||
struct kbase_ipa_control_prfcnt_config
|
||||
select[KBASE_IPA_CONTROL_NUM_BLOCK_COUNTERS];
|
||||
size_t num_available_counters;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_ipa_control - Manager of the IPA Control interface.
|
||||
*
|
||||
* @blocks: Current configuration of performance counters
|
||||
* for the IPA Control interface.
|
||||
* @sessions: State of client sessions, storing information
|
||||
* like performance counters the client subscribed to
|
||||
* and latest value read from each counter.
|
||||
* @lock: Spinlock to serialize access by concurrent clients.
|
||||
* @rtm_listener_data: Private data for allocating a GPU frequency change
|
||||
* listener.
|
||||
* @num_active_sessions: Number of sessions opened by clients.
|
||||
* @cur_gpu_rate: Current GPU top-level operating frequency, in Hz.
|
||||
* @rtm_listener_data: Private data for allocating a GPU frequency change
|
||||
* listener.
|
||||
* @protm_start: Time (in ns) at which the GPU entered protected mode
|
||||
*/
|
||||
struct kbase_ipa_control {
|
||||
struct kbase_ipa_control_prfcnt_block blocks[KBASE_IPA_CORE_TYPE_NUM];
|
||||
struct kbase_ipa_control_session
|
||||
sessions[KBASE_IPA_CONTROL_MAX_SESSIONS];
|
||||
spinlock_t lock;
|
||||
void *rtm_listener_data;
|
||||
size_t num_active_sessions;
|
||||
u32 cur_gpu_rate;
|
||||
u64 protm_start;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_firmware_interface - Interface in the MCU firmware
|
||||
*
|
||||
* @node: Interface objects are on the kbase_device:csf.firmware_interfaces
|
||||
* list using this list_head to link them
|
||||
* @phys: Array of the physical (tagged) addresses making up this interface
|
||||
* @name: NULL-terminated string naming the interface
|
||||
* @num_pages: Number of entries in @phys and @pma (and length of the interface)
|
||||
* @virtual: Starting GPU virtual address this interface is mapped at
|
||||
* @flags: bitmask of CSF_FIRMWARE_ENTRY_* conveying the interface attributes
|
||||
* @data_start: Offset into firmware image at which the interface data starts
|
||||
* @data_end: Offset into firmware image at which the interface data ends
|
||||
* @kernel_map: A kernel mapping of the memory or NULL if not required to be
|
||||
* mapped in the kernel
|
||||
* @pma: Array of pointers to protected memory allocations.
|
||||
*/
|
||||
struct kbase_csf_firmware_interface {
|
||||
struct list_head node;
|
||||
struct tagged_addr *phys;
|
||||
char *name;
|
||||
u32 num_pages;
|
||||
u32 virtual;
|
||||
u32 flags;
|
||||
u32 data_start;
|
||||
u32 data_end;
|
||||
void *kernel_map;
|
||||
struct protected_memory_allocation **pma;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct kbase_csf_hwcnt - Object containing members for handling the dump of
|
||||
* HW counters.
|
||||
*
|
||||
* @request_pending: Flag set when HWC requested and used for HWC sample
|
||||
* done interrupt.
|
||||
* @enable_pending: Flag set when HWC enable status change and used for
|
||||
* enable done interrupt.
|
||||
*/
|
||||
struct kbase_csf_hwcnt {
|
||||
bool request_pending;
|
||||
bool enable_pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_device - Object representing CSF for an instance of GPU
|
||||
* platform device.
|
||||
*
|
||||
* @mcu_mmu: MMU page tables for the MCU firmware
|
||||
* @firmware_interfaces: List of interfaces defined in the firmware image
|
||||
@@ -794,6 +1106,17 @@ struct kbase_csf_scheduler {
|
||||
* of the real Hw doorbell page for the active GPU
|
||||
* command queues after they are stopped or after the
|
||||
* GPU is powered down.
|
||||
* @dummy_user_reg_page: Address of the dummy page that is mapped in place
|
||||
* of the real User register page just before the GPU
|
||||
* is powered down. The User register page is mapped
|
||||
* in the address space of every process, that created
|
||||
* a Base context, to enable the access to LATEST_FLUSH
|
||||
* register from userspace.
|
||||
* @mali_file_inode: Pointer to the inode corresponding to mali device
|
||||
* file. This is needed in order to switch to the
|
||||
* @dummy_user_reg_page on GPU power down.
|
||||
* All instances of the mali device file will point to
|
||||
* the same inode.
|
||||
* @reg_lock: Lock to serialize the MCU firmware related actions
|
||||
* that affect all contexts such as allocation of
|
||||
* regions from shared interface area, assignment of
|
||||
@@ -806,7 +1129,7 @@ struct kbase_csf_scheduler {
|
||||
* @global_iface: The result of parsing the global interface
|
||||
* structure set up by the firmware, including the
|
||||
* CSGs, CSs, and their properties
|
||||
* @scheduler: The command stream scheduler instance.
|
||||
* @scheduler: The CS scheduler instance.
|
||||
* @reset: Contain members required for GPU reset handling.
|
||||
* @progress_timeout: Maximum number of GPU clock cycles without forward
|
||||
* progress to allow, for all tasks running on
|
||||
@@ -820,11 +1143,39 @@ struct kbase_csf_scheduler {
|
||||
* in GPU reset has completed.
|
||||
* @firmware_reload_needed: Flag for indicating that the firmware needs to be
|
||||
* reloaded as part of the GPU reset action.
|
||||
* @firmware_hctl_core_pwr: Flag for indicating that the host diver is in
|
||||
* charge of the shader core's power transitions, and
|
||||
* the mcu_core_pwroff timeout feature is disabled
|
||||
* (i.e. configured 0 in the register field). If
|
||||
* false, the control is delegated to the MCU.
|
||||
* @firmware_reload_work: Work item for facilitating the procedural actions
|
||||
* on reloading the firmware.
|
||||
* @glb_init_request_pending: Flag to indicate that Global requests have been
|
||||
* sent to the FW after MCU was re-enabled and their
|
||||
* acknowledgement is pending.
|
||||
* @fw_error_work: Work item for handling the firmware internal error
|
||||
* fatal event.
|
||||
* @ipa_control: IPA Control component manager.
|
||||
* @mcu_core_pwroff_dur_us: Sysfs attribute for the glb_pwroff timeout input
|
||||
* in unit of micro-seconds. The firmware does not use
|
||||
* it directly.
|
||||
* @mcu_core_pwroff_dur_count: The counterpart of the glb_pwroff timeout input
|
||||
* in interface required format, ready to be used
|
||||
* directly in the firmware.
|
||||
* @mcu_core_pwroff_reg_shadow: The actual value that has been programed into
|
||||
* the glb_pwoff register. This is separated from
|
||||
* the @p mcu_core_pwroff_dur_count as an update
|
||||
* to the latter is asynchronous.
|
||||
* @gpu_idle_hysteresis_ms: Sysfs attribute for the idle hysteresis time
|
||||
* window in unit of ms. The firmware does not use it
|
||||
* directly.
|
||||
* @gpu_idle_dur_count: The counterpart of the hysteresis time window in
|
||||
* interface required format, ready to be used
|
||||
* directly in the firmware.
|
||||
* @fw_timeout_ms: Timeout value (in milliseconds) used when waiting
|
||||
* for any request sent to the firmware.
|
||||
* @hwcnt: Contain members required for handling the dump of
|
||||
* HW counters.
|
||||
*/
|
||||
struct kbase_csf_device {
|
||||
struct kbase_mmu_table mcu_mmu;
|
||||
@@ -838,6 +1189,8 @@ struct kbase_csf_device {
|
||||
struct file *db_filp;
|
||||
u32 db_file_offsets;
|
||||
struct tagged_addr dummy_db_page;
|
||||
struct tagged_addr dummy_user_reg_page;
|
||||
struct inode *mali_file_inode;
|
||||
struct mutex reg_lock;
|
||||
wait_queue_head_t event_wait;
|
||||
bool interrupt_received;
|
||||
@@ -849,8 +1202,18 @@ struct kbase_csf_device {
|
||||
bool firmware_inited;
|
||||
bool firmware_reloaded;
|
||||
bool firmware_reload_needed;
|
||||
bool firmware_hctl_core_pwr;
|
||||
struct work_struct firmware_reload_work;
|
||||
bool glb_init_request_pending;
|
||||
struct work_struct fw_error_work;
|
||||
struct kbase_ipa_control ipa_control;
|
||||
u32 mcu_core_pwroff_dur_us;
|
||||
u32 mcu_core_pwroff_dur_count;
|
||||
u32 mcu_core_pwroff_reg_shadow;
|
||||
u32 gpu_idle_hysteresis_ms;
|
||||
u32 gpu_idle_dur_count;
|
||||
unsigned int fw_timeout_ms;
|
||||
struct kbase_csf_hwcnt hwcnt;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2018-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,15 +17,13 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_FIRMWARE_H_
|
||||
#define _KBASE_CSF_FIRMWARE_H_
|
||||
|
||||
#include "device/mali_kbase_device.h"
|
||||
#include "mali_gpu_csf_registers.h"
|
||||
#include <uapi/gpu/arm/bifrost/csf/mali_gpu_csf_registers.h>
|
||||
|
||||
/*
|
||||
* PAGE_KERNEL_RO was only defined on 32bit ARM in 4.19 in:
|
||||
@@ -71,14 +70,17 @@
|
||||
/* All implementations of the host interface with major version 0 must comply
|
||||
* with these restrictions:
|
||||
*/
|
||||
/* GLB_GROUP_NUM: At least 3 command stream groups, but no more than 31 */
|
||||
/* GLB_GROUP_NUM: At least 3 CSGs, but no more than 31 */
|
||||
#define MIN_SUPPORTED_CSGS 3
|
||||
#define MAX_SUPPORTED_CSGS 31
|
||||
/* GROUP_STREAM_NUM: At least 8 command streams per CSG, but no more than 32 */
|
||||
/* GROUP_STREAM_NUM: At least 8 CSs per CSG, but no more than 32 */
|
||||
#define MIN_SUPPORTED_STREAMS_PER_GROUP 8
|
||||
/* Maximum command streams per csg. */
|
||||
/* Maximum CSs per csg. */
|
||||
#define MAX_SUPPORTED_STREAMS_PER_GROUP 32
|
||||
|
||||
/* Waiting timeout for status change acknowledgment, in milliseconds */
|
||||
#define CSF_FIRMWARE_TIMEOUT_MS (800) /* Relaxed to 800ms from 100ms */
|
||||
|
||||
struct kbase_device;
|
||||
|
||||
|
||||
@@ -111,16 +113,15 @@ struct kbase_csf_trace_buffers {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kbase_csf_cmd_stream_info - Command stream interface provided by the
|
||||
* firmware.
|
||||
* struct kbase_csf_cmd_stream_info - CSI provided by the firmware.
|
||||
*
|
||||
* @kbdev: Address of the instance of a GPU platform device that implements
|
||||
* this interface.
|
||||
* @features: Bit field of command stream features (e.g. which types of jobs
|
||||
* @features: Bit field of CS features (e.g. which types of jobs
|
||||
* are supported). Bits 7:0 specify the number of work registers(-1).
|
||||
* Bits 11:8 specify the number of scoreboard entries(-1).
|
||||
* @input: Address of command stream interface input page.
|
||||
* @output: Address of command stream interface output page.
|
||||
* @input: Address of CSI input page.
|
||||
* @output: Address of CSI output page.
|
||||
*/
|
||||
struct kbase_csf_cmd_stream_info {
|
||||
struct kbase_device *kbdev;
|
||||
@@ -130,9 +131,9 @@ struct kbase_csf_cmd_stream_info {
|
||||
};
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_cs_input() - Set a word in a command stream's input page
|
||||
* kbase_csf_firmware_cs_input() - Set a word in a CS's input page
|
||||
*
|
||||
* @info: Command stream interface provided by the firmware.
|
||||
* @info: CSI provided by the firmware.
|
||||
* @offset: Offset of the word to be written, in bytes.
|
||||
* @value: Value to be written.
|
||||
*/
|
||||
@@ -140,22 +141,20 @@ void kbase_csf_firmware_cs_input(
|
||||
const struct kbase_csf_cmd_stream_info *info, u32 offset, u32 value);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_cs_input_read() - Read a word in a command stream's input
|
||||
* page
|
||||
* kbase_csf_firmware_cs_input_read() - Read a word in a CS's input page
|
||||
*
|
||||
* Return: Value of the word read from the command stream's input page.
|
||||
* Return: Value of the word read from the CS's input page.
|
||||
*
|
||||
* @info: Command stream interface provided by the firmware.
|
||||
* @info: CSI provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_cs_input_read(
|
||||
const struct kbase_csf_cmd_stream_info *const info, const u32 offset);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_cs_input_mask() - Set part of a word in a command stream's
|
||||
* input page
|
||||
* kbase_csf_firmware_cs_input_mask() - Set part of a word in a CS's input page
|
||||
*
|
||||
* @info: Command stream interface provided by the firmware.
|
||||
* @info: CSI provided by the firmware.
|
||||
* @offset: Offset of the word to be modified, in bytes.
|
||||
* @value: Value to be written.
|
||||
* @mask: Bitmask with the bits to be modified set.
|
||||
@@ -165,19 +164,18 @@ void kbase_csf_firmware_cs_input_mask(
|
||||
u32 value, u32 mask);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_cs_output() - Read a word in a command stream's output
|
||||
* page
|
||||
* kbase_csf_firmware_cs_output() - Read a word in a CS's output page
|
||||
*
|
||||
* Return: Value of the word read from the command stream's output page.
|
||||
* Return: Value of the word read from the CS's output page.
|
||||
*
|
||||
* @info: Command stream interface provided by the firmware.
|
||||
* @info: CSI provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_cs_output(
|
||||
const struct kbase_csf_cmd_stream_info *info, u32 offset);
|
||||
/**
|
||||
* struct kbase_csf_cmd_stream_group_info - Command stream group interface
|
||||
* provided by the firmware.
|
||||
* struct kbase_csf_cmd_stream_group_info - CSG interface provided by the
|
||||
* firmware.
|
||||
*
|
||||
* @kbdev: Address of the instance of a GPU platform device that implements
|
||||
* this interface.
|
||||
@@ -185,14 +183,13 @@ u32 kbase_csf_firmware_cs_output(
|
||||
* be ignored.
|
||||
* @input: Address of global interface input page.
|
||||
* @output: Address of global interface output page.
|
||||
* @suspend_size: Size in bytes for normal suspend buffer for the command
|
||||
* stream group.
|
||||
* @suspend_size: Size in bytes for normal suspend buffer for the CSG
|
||||
* @protm_suspend_size: Size in bytes for protected mode suspend buffer
|
||||
* for the command stream group.
|
||||
* @stream_num: Number of command streams in the command stream group.
|
||||
* for the CSG.
|
||||
* @stream_num: Number of CSs in the CSG.
|
||||
* @stream_stride: Stride in bytes in JASID0 virtual address between
|
||||
* command stream capability structures.
|
||||
* @streams: Address of an array of command stream capability structures.
|
||||
* CS capability structures.
|
||||
* @streams: Address of an array of CS capability structures.
|
||||
*/
|
||||
struct kbase_csf_cmd_stream_group_info {
|
||||
struct kbase_device *kbdev;
|
||||
@@ -207,10 +204,9 @@ struct kbase_csf_cmd_stream_group_info {
|
||||
};
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_csg_input() - Set a word in a command stream group's
|
||||
* input page
|
||||
* kbase_csf_firmware_csg_input() - Set a word in a CSG's input page
|
||||
*
|
||||
* @info: Command stream group interface provided by the firmware.
|
||||
* @info: CSG interface provided by the firmware.
|
||||
* @offset: Offset of the word to be written, in bytes.
|
||||
* @value: Value to be written.
|
||||
*/
|
||||
@@ -219,22 +215,21 @@ void kbase_csf_firmware_csg_input(
|
||||
u32 value);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_csg_input_read() - Read a word in a command stream group's
|
||||
* input page
|
||||
* kbase_csf_firmware_csg_input_read() - Read a word in a CSG's input page
|
||||
*
|
||||
* Return: Value of the word read from the command stream group's input page.
|
||||
* Return: Value of the word read from the CSG's input page.
|
||||
*
|
||||
* @info: Command stream group interface provided by the firmware.
|
||||
* @info: CSG interface provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_csg_input_read(
|
||||
const struct kbase_csf_cmd_stream_group_info *info, u32 offset);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_csg_input_mask() - Set part of a word in a command stream
|
||||
* group's input page
|
||||
* kbase_csf_firmware_csg_input_mask() - Set part of a word in a CSG's
|
||||
* input page
|
||||
*
|
||||
* @info: Command stream group interface provided by the firmware.
|
||||
* @info: CSG interface provided by the firmware.
|
||||
* @offset: Offset of the word to be modified, in bytes.
|
||||
* @value: Value to be written.
|
||||
* @mask: Bitmask with the bits to be modified set.
|
||||
@@ -244,19 +239,18 @@ void kbase_csf_firmware_csg_input_mask(
|
||||
u32 value, u32 mask);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_csg_output()- Read a word in a command stream group's
|
||||
* output page
|
||||
* kbase_csf_firmware_csg_output()- Read a word in a CSG's output page
|
||||
*
|
||||
* Return: Value of the word read from the command stream group's output page.
|
||||
* Return: Value of the word read from the CSG's output page.
|
||||
*
|
||||
* @info: Command stream group interface provided by the firmware.
|
||||
* @info: CSG interface provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_csg_output(
|
||||
const struct kbase_csf_cmd_stream_group_info *info, u32 offset);
|
||||
|
||||
/**
|
||||
* struct kbase_csf_global_iface - Global command stream front-end interface
|
||||
* struct kbase_csf_global_iface - Global CSF interface
|
||||
* provided by the firmware.
|
||||
*
|
||||
* @kbdev: Address of the instance of a GPU platform device that implements
|
||||
@@ -268,11 +262,12 @@ u32 kbase_csf_firmware_csg_output(
|
||||
* be suspended). Reserved bits should be 0, and should be ignored.
|
||||
* @input: Address of global interface input page.
|
||||
* @output: Address of global interface output page.
|
||||
* @group_num: Number of command stream groups supported.
|
||||
* @group_num: Number of CSGs supported.
|
||||
* @group_stride: Stride in bytes in JASID0 virtual address between
|
||||
* command stream group capability structures.
|
||||
* CSG capability structures.
|
||||
* @prfcnt_size: Performance counters size.
|
||||
* @groups: Address of an array of command stream group capability structures.
|
||||
* @instr_features: Instrumentation features.
|
||||
* @groups: Address of an array of CSG capability structures.
|
||||
*/
|
||||
struct kbase_csf_global_iface {
|
||||
struct kbase_device *kbdev;
|
||||
@@ -283,13 +278,14 @@ struct kbase_csf_global_iface {
|
||||
u32 group_num;
|
||||
u32 group_stride;
|
||||
u32 prfcnt_size;
|
||||
u32 instr_features;
|
||||
struct kbase_csf_cmd_stream_group_info *groups;
|
||||
};
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_global_input() - Set a word in the global input page
|
||||
*
|
||||
* @iface: Command stream front-end interface provided by the firmware.
|
||||
* @iface: CSF interface provided by the firmware.
|
||||
* @offset: Offset of the word to be written, in bytes.
|
||||
* @value: Value to be written.
|
||||
*/
|
||||
@@ -300,7 +296,7 @@ void kbase_csf_firmware_global_input(
|
||||
* kbase_csf_firmware_global_input_mask() - Set part of a word in the global
|
||||
* input page
|
||||
*
|
||||
* @iface: Command stream front-end interface provided by the firmware.
|
||||
* @iface: CSF interface provided by the firmware.
|
||||
* @offset: Offset of the word to be modified, in bytes.
|
||||
* @value: Value to be written.
|
||||
* @mask: Bitmask with the bits to be modified set.
|
||||
@@ -314,7 +310,7 @@ void kbase_csf_firmware_global_input_mask(
|
||||
*
|
||||
* Return: Value of the word read from the global input page.
|
||||
*
|
||||
* @info: Command stream group interface provided by the firmware.
|
||||
* @info: CSG interface provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_global_input_read(
|
||||
@@ -325,7 +321,7 @@ u32 kbase_csf_firmware_global_input_read(
|
||||
*
|
||||
* Return: Value of the word read from the global output page.
|
||||
*
|
||||
* @iface: Command stream front-end interface provided by the firmware.
|
||||
* @iface: CSF interface provided by the firmware.
|
||||
* @offset: Offset of the word to be read, in bytes.
|
||||
*/
|
||||
u32 kbase_csf_firmware_global_output(
|
||||
@@ -403,20 +399,28 @@ void kbase_csf_firmware_term(struct kbase_device *kbdev);
|
||||
/**
|
||||
* kbase_csf_firmware_ping - Send the ping request to firmware.
|
||||
*
|
||||
* The function sends the ping request to firmware to confirm it is alive.
|
||||
* The function sends the ping request to firmware.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_firmware_ping(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_ping_wait - Send the ping request to firmware and waits.
|
||||
*
|
||||
* The function sends the ping request to firmware and waits to confirm it is
|
||||
* alive.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Return: 0 on success, or negative on failure.
|
||||
*/
|
||||
int kbase_csf_firmware_ping(struct kbase_device *kbdev);
|
||||
int kbase_csf_firmware_ping_wait(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_set_timeout - Set a hardware endpoint progress timeout.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @timeout: The maximum number of GPU cycles that is allowed to elapse
|
||||
* without forward progress before the driver terminates a GPU
|
||||
* command queue group.
|
||||
@@ -433,8 +437,7 @@ int kbase_csf_firmware_set_timeout(struct kbase_device *kbdev, u64 timeout);
|
||||
* enter protected mode and wait for its
|
||||
* completion.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_enter_protected_mode(struct kbase_device *kbdev);
|
||||
|
||||
@@ -454,16 +457,14 @@ static inline bool kbase_csf_firmware_mcu_halted(struct kbase_device *kbdev)
|
||||
* into a known internal state for warm
|
||||
* boot later.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_firmware_trigger_mcu_halt(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_enable_mcu - Send the command to enable MCU
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
static inline void kbase_csf_firmware_enable_mcu(struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -477,8 +478,7 @@ static inline void kbase_csf_firmware_enable_mcu(struct kbase_device *kbdev)
|
||||
/**
|
||||
* kbase_csf_firmware_disable_mcu - Send the command to disable MCU
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
static inline void kbase_csf_firmware_disable_mcu(struct kbase_device *kbdev)
|
||||
{
|
||||
@@ -489,8 +489,7 @@ static inline void kbase_csf_firmware_disable_mcu(struct kbase_device *kbdev)
|
||||
* kbase_csf_firmware_disable_mcu_wait - Wait for the MCU to reach disabled
|
||||
* status.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_firmware_disable_mcu_wait(struct kbase_device *kbdev);
|
||||
|
||||
@@ -499,8 +498,7 @@ void kbase_csf_firmware_disable_mcu_wait(struct kbase_device *kbdev);
|
||||
* cold boot case firmware image would be
|
||||
* reloaded from filesystem into memory.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_firmware_trigger_reload(struct kbase_device *kbdev);
|
||||
|
||||
@@ -508,8 +506,7 @@ void kbase_csf_firmware_trigger_reload(struct kbase_device *kbdev);
|
||||
* kbase_csf_firmware_reload_completed - The reboot of MCU firmware has
|
||||
* completed.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
void kbase_csf_firmware_reload_completed(struct kbase_device *kbdev);
|
||||
|
||||
@@ -517,10 +514,11 @@ void kbase_csf_firmware_reload_completed(struct kbase_device *kbdev);
|
||||
* kbase_csf_firmware_global_reinit - Send the Global configuration requests
|
||||
* after the reboot of MCU firmware.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @core_mask: Mask of the enabled shader cores.
|
||||
*/
|
||||
void kbase_csf_firmware_global_reinit(struct kbase_device *kbdev);
|
||||
void kbase_csf_firmware_global_reinit(struct kbase_device *kbdev,
|
||||
u64 core_mask);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_global_reinit_complete - Check the Global configuration
|
||||
@@ -529,45 +527,69 @@ void kbase_csf_firmware_global_reinit(struct kbase_device *kbdev);
|
||||
*
|
||||
* Return: true if the Global configuration requests completed otherwise false.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
bool kbase_csf_firmware_global_reinit_complete(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_update_core_attr - Send the Global configuration request
|
||||
* to update the requested core attribute
|
||||
* changes.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @update_core_pwroff_timer: If true, signal the firmware needs to update
|
||||
* the MCU power-off timer value.
|
||||
* @update_core_mask: If true, need to do the core_mask update with
|
||||
* the supplied core_mask value.
|
||||
* @core_mask: New core mask value if update_core_mask is true,
|
||||
* otherwise unused.
|
||||
*/
|
||||
void kbase_csf_firmware_update_core_attr(struct kbase_device *kbdev,
|
||||
bool update_core_pwroff_timer, bool update_core_mask, u64 core_mask);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_core_attr_updated - Check the Global configuration
|
||||
* request has completed or not, that was sent to update
|
||||
* the core attributes.
|
||||
*
|
||||
* Return: true if the Global configuration request to update the core
|
||||
* attributes has completed, otherwise false.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*/
|
||||
bool kbase_csf_firmware_core_attr_updated(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* Request the global control block of CSF interface capabilities
|
||||
*
|
||||
* Return: Total number of command streams, summed across all groups.
|
||||
* Return: Total number of CSs, summed across all groups.
|
||||
*
|
||||
* @kbdev: Kbase device.
|
||||
* @group_data: Pointer where to store all the group data
|
||||
* (sequentially).
|
||||
* @max_group_num: The maximum number of groups to be read.
|
||||
* Can be 0, in which case group_data is unused.
|
||||
* @stream_data: Pointer where to store all the stream data
|
||||
* @stream_data: Pointer where to store all the CS data
|
||||
* (sequentially).
|
||||
* @max_total_stream_num: The maximum number of streams to be read.
|
||||
* @max_total_stream_num: The maximum number of CSs to be read.
|
||||
* Can be 0, in which case stream_data is unused.
|
||||
* @glb_version: Where to store the global interface version.
|
||||
* Bits 31:16 hold the major version number and
|
||||
* 15:0 hold the minor version number.
|
||||
* A higher minor version is backwards-compatible
|
||||
* with a lower minor version for the same major
|
||||
* version.
|
||||
* @features: Where to store a bit mask of features (e.g.
|
||||
* whether certain types of job can be suspended).
|
||||
* @group_num: Where to store the number of command stream groups
|
||||
* @group_num: Where to store the number of CSGs
|
||||
* supported.
|
||||
* @prfcnt_size: Where to store the size of CSF performance counters,
|
||||
* in bytes. Bits 31:16 hold the size of firmware
|
||||
* performance counter data and 15:0 hold the size of
|
||||
* hardware performance counter data.
|
||||
* @instr_features: Instrumentation features. Bits 7:4 hold the max size
|
||||
* of events. Bits 3:0 hold the offset update rate.
|
||||
*/
|
||||
u32 kbase_csf_firmware_get_glb_iface(struct kbase_device *kbdev,
|
||||
struct basep_cs_group_control *group_data, u32 max_group_num,
|
||||
struct basep_cs_stream_control *stream_data, u32 max_total_stream_num,
|
||||
u32 *glb_version, u32 *features, u32 *group_num, u32 *prfcnt_size);
|
||||
|
||||
u32 kbase_csf_firmware_get_glb_iface(
|
||||
struct kbase_device *kbdev, struct basep_cs_group_control *group_data,
|
||||
u32 max_group_num, struct basep_cs_stream_control *stream_data,
|
||||
u32 max_total_stream_num, u32 *glb_version, u32 *features,
|
||||
u32 *group_num, u32 *prfcnt_size, u32 *instr_features);
|
||||
|
||||
/**
|
||||
* Get CSF firmware header timeline metadata content
|
||||
@@ -660,4 +682,125 @@ static inline long kbase_csf_timeout_in_jiffies(const unsigned int msecs)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_enable_gpu_idle_timer() - Activate the idle hysteresis
|
||||
* monitoring operation
|
||||
*
|
||||
* Program the firmware interface with its configured hysteresis count value
|
||||
* and enable the firmware to act on it. The Caller is
|
||||
* assumed to hold the kbdev->csf.scheduler.interrupt_lock.
|
||||
*
|
||||
* @kbdev: Kbase device structure
|
||||
*/
|
||||
void kbase_csf_firmware_enable_gpu_idle_timer(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_disable_gpu_idle_timer() - Disable the idle time
|
||||
* hysteresis monitoring operation
|
||||
*
|
||||
* Program the firmware interface to disable the idle hysteresis timer. The
|
||||
* Caller is assumed to hold the kbdev->csf.scheduler.interrupt_lock.
|
||||
*
|
||||
* @kbdev: Kbase device structure
|
||||
*/
|
||||
void kbase_csf_firmware_disable_gpu_idle_timer(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_get_gpu_idle_hysteresis_time - Get the firmware GPU idle
|
||||
* detection hysteresis duration
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Return: the internally recorded hysteresis (nominal) value.
|
||||
*/
|
||||
u32 kbase_csf_firmware_get_gpu_idle_hysteresis_time(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_set_gpu_idle_hysteresis_time - Set the firmware GPU idle
|
||||
* detection hysteresis duration
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @dur: The duration value (unit: milliseconds) for the configuring
|
||||
* hysteresis field for GPU idle detection
|
||||
*
|
||||
* The supplied value will be recorded internally without any change. But the
|
||||
* actual field value will be subject to hysteresis source frequency scaling
|
||||
* and maximum value limiting. The default source will be SYSTEM_TIMESTAMP
|
||||
* counter. But in case the platform is not able to supply it, the GPU
|
||||
* CYCLE_COUNTER source will be used as an alternative. Bit-31 on the
|
||||
* returned value is the source configuration flag, and it is set to '1'
|
||||
* when CYCLE_COUNTER alternative source is used.
|
||||
*
|
||||
* Return: the actual internally configured hysteresis field value.
|
||||
*/
|
||||
u32 kbase_csf_firmware_set_gpu_idle_hysteresis_time(struct kbase_device *kbdev, u32 dur);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_get_mcu_core_pwroff_time - Get the MCU core power-off
|
||||
* time value
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Return: the internally recorded MCU core power-off (nominal) value. The unit
|
||||
* of the value is in micro-seconds.
|
||||
*/
|
||||
u32 kbase_csf_firmware_get_mcu_core_pwroff_time(struct kbase_device *kbdev);
|
||||
|
||||
/**
|
||||
* kbase_csf_firmware_set_mcu_core_pwroff_time - Set the MCU core power-off
|
||||
* time value
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
* @dur: The duration value (unit: micro-seconds) for configuring MCU
|
||||
* core power-off timer, when the shader cores' power
|
||||
* transitions are delegated to the MCU (normal operational
|
||||
* mode)
|
||||
*
|
||||
* The supplied value will be recorded internally without any change. But the
|
||||
* actual field value will be subject to core power-off timer source frequency
|
||||
* scaling and maximum value limiting. The default source will be
|
||||
* SYSTEM_TIMESTAMP counter. But in case the platform is not able to supply it,
|
||||
* the GPU CYCLE_COUNTER source will be used as an alternative. Bit-31 on the
|
||||
* returned value is the source configuration flag, and it is set to '1'
|
||||
* when CYCLE_COUNTER alternative source is used.
|
||||
*
|
||||
* The configured MCU core power-off timer will only have effect when the host
|
||||
* driver has delegated the shader cores' power management to MCU.
|
||||
*
|
||||
* Return: the actual internal core power-off timer value in register defined
|
||||
* format.
|
||||
*/
|
||||
u32 kbase_csf_firmware_set_mcu_core_pwroff_time(struct kbase_device *kbdev, u32 dur);
|
||||
|
||||
/**
|
||||
* kbase_csf_interface_version - Helper function to build the full firmware
|
||||
* interface version in a format compatible with
|
||||
* with GLB_VERSION register
|
||||
*
|
||||
* @major: major version of csf interface
|
||||
* @minor: minor version of csf interface
|
||||
* @patch: patch version of csf interface
|
||||
*
|
||||
* Return: firmware interface version
|
||||
*/
|
||||
static inline u32 kbase_csf_interface_version(u32 major, u32 minor, u32 patch)
|
||||
{
|
||||
return ((major << GLB_VERSION_MAJOR_SHIFT) |
|
||||
(minor << GLB_VERSION_MINOR_SHIFT) |
|
||||
(patch << GLB_VERSION_PATCH_SHIFT));
|
||||
}
|
||||
|
||||
/**
|
||||
* kbase_csf_trigger_firmware_config_update - Send a firmware config update.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Any changes done to firmware configuration entry or tracebuffer entry
|
||||
* requires a GPU silent reset to reflect the configuration changes
|
||||
* requested, but if Firmware.header.entry.bit(30) is set then we can request a
|
||||
* FIRMWARE_CONFIG_UPDATE rather than doing a silent reset.
|
||||
*
|
||||
* Return: 0 if success, or negative error code on failure.
|
||||
*/
|
||||
int kbase_csf_trigger_firmware_config_update(struct kbase_device *kbdev);
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
@@ -41,6 +40,8 @@
|
||||
* inside CSF_FIRMWARE_CFG_SYSFS_DIR_NAME directory,
|
||||
* representing the configuration option @name.
|
||||
* @kobj_inited: kobject initialization state
|
||||
* @updatable: Indicates whether config items can be updated with
|
||||
* FIRMWARE_CONFIG_UPDATE
|
||||
* @name: NUL-terminated string naming the option
|
||||
* @address: The address in the firmware image of the configuration option
|
||||
* @min: The lowest legal value of the configuration option
|
||||
@@ -52,6 +53,7 @@ struct firmware_config {
|
||||
struct kbase_device *kbdev;
|
||||
struct kobject kobj;
|
||||
bool kobj_inited;
|
||||
bool updatable;
|
||||
char *name;
|
||||
u32 address;
|
||||
u32 min;
|
||||
@@ -142,14 +144,20 @@ static ssize_t store_fw_cfg(struct kobject *kobj,
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is already a GPU reset pending then inform
|
||||
* the User to retry the write.
|
||||
/* If configuration update cannot be performed with
|
||||
* FIRMWARE_CONFIG_UPDATE then we need to do a
|
||||
* silent reset before we update the memory.
|
||||
*/
|
||||
if (kbase_reset_gpu_silent(kbdev)) {
|
||||
spin_unlock_irqrestore(
|
||||
&kbdev->hwaccess_lock, flags);
|
||||
return -EAGAIN;
|
||||
if (!config->updatable) {
|
||||
/*
|
||||
* If there is already a GPU reset pending then inform
|
||||
* the User to retry the write.
|
||||
*/
|
||||
if (kbase_reset_gpu_silent(kbdev)) {
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock,
|
||||
flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -165,10 +173,21 @@ static ssize_t store_fw_cfg(struct kobject *kobj,
|
||||
kbdev, config->address, val);
|
||||
|
||||
config->cur_val = val;
|
||||
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
/* If we can update the config without firmware reset then
|
||||
* we need to just trigger FIRMWARE_CONFIG_UPDATE.
|
||||
*/
|
||||
if (config->updatable) {
|
||||
ret = kbase_csf_trigger_firmware_config_update(kbdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for the config update to take effect */
|
||||
kbase_reset_gpu_wait(kbdev);
|
||||
if (!config->updatable)
|
||||
kbase_reset_gpu_wait(kbdev);
|
||||
} else {
|
||||
dev_warn(kbdev->dev,
|
||||
"Unexpected write to entry %s/%s",
|
||||
@@ -254,8 +273,9 @@ void kbase_csf_firmware_cfg_term(struct kbase_device *kbdev)
|
||||
}
|
||||
|
||||
int kbase_csf_firmware_cfg_option_entry_parse(struct kbase_device *kbdev,
|
||||
const struct firmware *fw,
|
||||
const u32 *entry, unsigned int size)
|
||||
const struct firmware *fw,
|
||||
const u32 *entry,
|
||||
unsigned int size, bool updatable)
|
||||
{
|
||||
const char *name = (char *)&entry[3];
|
||||
struct firmware_config *config;
|
||||
@@ -270,6 +290,7 @@ int kbase_csf_firmware_cfg_option_entry_parse(struct kbase_device *kbdev,
|
||||
return -ENOMEM;
|
||||
|
||||
config->kbdev = kbdev;
|
||||
config->updatable = updatable;
|
||||
config->name = (char *)(config+1);
|
||||
config->address = entry[0];
|
||||
config->min = entry[1];
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2020-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _KBASE_CSF_FIRMWARE_CFG_H_
|
||||
@@ -61,12 +60,15 @@ void kbase_csf_firmware_cfg_term(struct kbase_device *kbdev);
|
||||
*
|
||||
* Return: 0 if successful, negative error code on failure
|
||||
*
|
||||
* @kbdev: Kbase device structure
|
||||
* @fw: Firmware image containing the section
|
||||
* @entry: Pointer to the section
|
||||
* @size: Size (in bytes) of the section
|
||||
* @kbdev: Kbase device structure
|
||||
* @fw: Firmware image containing the section
|
||||
* @entry: Pointer to the section
|
||||
* @size: Size (in bytes) of the section
|
||||
* @updatable: Indicates if entry can be updated with FIRMWARE_CONFIG_UPDATE
|
||||
*/
|
||||
int kbase_csf_firmware_cfg_option_entry_parse(struct kbase_device *kbdev,
|
||||
const struct firmware *fw,
|
||||
const u32 *entry, unsigned int size);
|
||||
const struct firmware *fw,
|
||||
const u32 *entry,
|
||||
unsigned int size,
|
||||
bool updatable);
|
||||
#endif /* _KBASE_CSF_FIRMWARE_CFG_H_ */
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2018-2020 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2018-2021 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mali_kbase.h"
|
||||
@@ -26,19 +25,23 @@
|
||||
#include "mali_kbase_csf_timeout.h"
|
||||
#include "mali_kbase_mem.h"
|
||||
#include "mali_kbase_reset_gpu.h"
|
||||
#include "mali_kbase_ctx_sched.h"
|
||||
#include "device/mali_kbase_device.h"
|
||||
#include "backend/gpu/mali_kbase_pm_internal.h"
|
||||
#include "mali_kbase_csf_scheduler.h"
|
||||
#include "mmu/mali_kbase_mmu.h"
|
||||
#include "backend/gpu/mali_kbase_clk_rate_trace_mgr.h"
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mutex.h>
|
||||
#if (KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE)
|
||||
#include <linux/set_memory.h>
|
||||
#endif
|
||||
#include <asm/arch_timer.h>
|
||||
|
||||
#ifdef CONFIG_MALI_BIFROST_DEBUG
|
||||
/* Makes Driver wait indefinitely for an acknowledgment for the different
|
||||
@@ -56,7 +59,7 @@ MODULE_PARM_DESC(fw_debug,
|
||||
#define DUMMY_FW_PAGE_SIZE SZ_4K
|
||||
|
||||
/**
|
||||
* struct dummy_firmware_csi - Represents a dummy interface for MCU firmware streams
|
||||
* struct dummy_firmware_csi - Represents a dummy interface for MCU firmware CSs
|
||||
*
|
||||
* @cs_kernel_input: CS kernel input memory region
|
||||
* @cs_kernel_output: CS kernel output memory region
|
||||
@@ -67,7 +70,7 @@ struct dummy_firmware_csi {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dummy_firmware_csg - Represents a dummy interface for MCU firmware stream groups
|
||||
* struct dummy_firmware_csg - Represents a dummy interface for MCU firmware CSGs
|
||||
*
|
||||
* @csg_input: CSG kernel input memory region
|
||||
* @csg_output: CSG kernel output memory region
|
||||
@@ -95,8 +98,9 @@ struct dummy_firmware_interface {
|
||||
struct list_head node;
|
||||
} dummy_firmware_interface;
|
||||
|
||||
#define CSF_GLB_REQ_CFG_MASK \
|
||||
(GLB_REQ_CFG_ALLOC_EN_MASK | GLB_REQ_CFG_PROGRESS_TIMER_MASK)
|
||||
#define CSF_GLB_REQ_CFG_MASK \
|
||||
(GLB_REQ_CFG_ALLOC_EN_MASK | GLB_REQ_CFG_PROGRESS_TIMER_MASK | \
|
||||
GLB_REQ_CFG_PWROFF_TIMER_MASK)
|
||||
|
||||
static inline u32 input_page_read(const u32 *const input, const u32 offset)
|
||||
{
|
||||
@@ -233,6 +237,9 @@ static int invent_capabilities(struct kbase_device *kbdev)
|
||||
iface->kbdev = kbdev;
|
||||
iface->features = 0;
|
||||
iface->prfcnt_size = 64;
|
||||
iface->instr_features =
|
||||
0x81; /* update rate=1, max event size = 1<<8 = 256 */
|
||||
|
||||
iface->group_num = ARRAY_SIZE(interface->csg);
|
||||
iface->group_stride = 0;
|
||||
|
||||
@@ -416,6 +423,69 @@ u32 kbase_csf_firmware_global_output(
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_internal_firmware_fatal - Handler for CS internal firmware fault.
|
||||
*
|
||||
* @kbdev: Pointer to kbase device
|
||||
*
|
||||
* Report group fatal error to user space for all GPU command queue groups
|
||||
* in the device, terminate them and reset GPU.
|
||||
*/
|
||||
static void handle_internal_firmware_fatal(struct kbase_device *const kbdev)
|
||||
{
|
||||
int as;
|
||||
|
||||
for (as = 0; as < kbdev->nr_hw_address_spaces; as++) {
|
||||
unsigned long flags;
|
||||
struct kbase_context *kctx;
|
||||
struct kbase_fault fault;
|
||||
|
||||
if (as == MCU_AS_NR)
|
||||
continue;
|
||||
|
||||
/* Only handle the fault for an active address space. Lock is
|
||||
* taken here to atomically get reference to context in an
|
||||
* active address space and retain its refcount.
|
||||
*/
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kctx = kbase_ctx_sched_as_to_ctx_nolock(kbdev, as);
|
||||
|
||||
if (kctx) {
|
||||
kbase_ctx_sched_retain_ctx_refcount(kctx);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
continue;
|
||||
}
|
||||
|
||||
fault = (struct kbase_fault) {
|
||||
.status = GPU_EXCEPTION_TYPE_SW_FAULT_1,
|
||||
};
|
||||
|
||||
kbase_csf_ctx_handle_fault(kctx, &fault);
|
||||
kbase_ctx_sched_release_ctx_lock(kctx);
|
||||
}
|
||||
|
||||
if (kbase_prepare_to_reset_gpu(kbdev,
|
||||
RESET_FLAGS_HWC_UNRECOVERABLE_ERROR))
|
||||
kbase_reset_gpu(kbdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* firmware_error_worker - Worker function for handling firmware internal error
|
||||
*
|
||||
* @data: Pointer to a work_struct embedded in kbase device.
|
||||
*
|
||||
* Handle the CS internal firmware error
|
||||
*/
|
||||
static void firmware_error_worker(struct work_struct *const data)
|
||||
{
|
||||
struct kbase_device *const kbdev =
|
||||
container_of(data, struct kbase_device, csf.fw_error_work);
|
||||
|
||||
handle_internal_firmware_fatal(kbdev);
|
||||
}
|
||||
|
||||
static bool global_request_complete(struct kbase_device *const kbdev,
|
||||
u32 const req_mask)
|
||||
{
|
||||
@@ -441,7 +511,7 @@ static int wait_for_global_request(struct kbase_device *const kbdev,
|
||||
u32 const req_mask)
|
||||
{
|
||||
const long wait_timeout =
|
||||
kbase_csf_timeout_in_jiffies(GLB_REQ_WAIT_TIMEOUT_MS);
|
||||
kbase_csf_timeout_in_jiffies(kbdev->csf.fw_timeout_ms);
|
||||
long remaining;
|
||||
int err = 0;
|
||||
|
||||
@@ -464,7 +534,7 @@ static void set_global_request(
|
||||
{
|
||||
u32 glb_req;
|
||||
|
||||
lockdep_assert_held(&global_iface->kbdev->csf.reg_lock);
|
||||
kbase_csf_scheduler_spin_lock_assert_held(global_iface->kbdev);
|
||||
|
||||
glb_req = kbase_csf_firmware_global_output(global_iface, GLB_ACK);
|
||||
glb_req ^= req_mask;
|
||||
@@ -484,6 +554,26 @@ static void enable_endpoints_global(
|
||||
set_global_request(global_iface, GLB_REQ_CFG_ALLOC_EN_MASK);
|
||||
}
|
||||
|
||||
static void enable_shader_poweroff_timer(struct kbase_device *const kbdev,
|
||||
const struct kbase_csf_global_iface *const global_iface)
|
||||
{
|
||||
u32 pwroff_reg;
|
||||
|
||||
if (kbdev->csf.firmware_hctl_core_pwr)
|
||||
pwroff_reg =
|
||||
GLB_PWROFF_TIMER_TIMER_SOURCE_SET(DISABLE_GLB_PWROFF_TIMER,
|
||||
GLB_PWROFF_TIMER_TIMER_SOURCE_SYSTEM_TIMESTAMP);
|
||||
else
|
||||
pwroff_reg = kbdev->csf.mcu_core_pwroff_dur_count;
|
||||
|
||||
kbase_csf_firmware_global_input(global_iface, GLB_PWROFF_TIMER,
|
||||
pwroff_reg);
|
||||
set_global_request(global_iface, GLB_REQ_CFG_PWROFF_TIMER_MASK);
|
||||
|
||||
/* Save the programed reg value in its shadow field */
|
||||
kbdev->csf.mcu_core_pwroff_reg_shadow = pwroff_reg;
|
||||
}
|
||||
|
||||
static void set_timeout_global(
|
||||
const struct kbase_csf_global_iface *const global_iface,
|
||||
u64 const timeout)
|
||||
@@ -494,13 +584,16 @@ static void set_timeout_global(
|
||||
set_global_request(global_iface, GLB_REQ_CFG_PROGRESS_TIMER_MASK);
|
||||
}
|
||||
|
||||
static void global_init(struct kbase_device *const kbdev, u32 req_mask)
|
||||
static void global_init(struct kbase_device *const kbdev, u64 core_mask)
|
||||
{
|
||||
u32 const ack_irq_mask = GLB_ACK_IRQ_MASK_CFG_ALLOC_EN_MASK |
|
||||
GLB_ACK_IRQ_MASK_PING_MASK |
|
||||
GLB_ACK_IRQ_MASK_CFG_PROGRESS_TIMER_MASK |
|
||||
GLB_ACK_IRQ_MASK_PROTM_ENTER_MASK |
|
||||
GLB_ACK_IRQ_MASK_PROTM_EXIT_MASK;
|
||||
u32 const ack_irq_mask = GLB_ACK_IRQ_MASK_CFG_ALLOC_EN_MASK |
|
||||
GLB_ACK_IRQ_MASK_PING_MASK |
|
||||
GLB_ACK_IRQ_MASK_CFG_PROGRESS_TIMER_MASK |
|
||||
GLB_ACK_IRQ_MASK_PROTM_ENTER_MASK |
|
||||
GLB_ACK_IRQ_MASK_FIRMWARE_CONFIG_UPDATE_MASK |
|
||||
GLB_ACK_IRQ_MASK_PROTM_EXIT_MASK |
|
||||
GLB_ACK_IRQ_MASK_CFG_PWROFF_TIMER_MASK |
|
||||
GLB_ACK_IRQ_MASK_IDLE_EVENT_MASK;
|
||||
|
||||
const struct kbase_csf_global_iface *const global_iface =
|
||||
&kbdev->csf.global_iface;
|
||||
@@ -508,9 +601,9 @@ static void global_init(struct kbase_device *const kbdev, u32 req_mask)
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
|
||||
/* Enable endpoints on all present shader cores */
|
||||
enable_endpoints_global(global_iface,
|
||||
kbase_pm_get_present_cores(kbdev, KBASE_PM_CORE_SHADER));
|
||||
/* Update shader core allocation enable mask */
|
||||
enable_endpoints_global(global_iface, core_mask);
|
||||
enable_shader_poweroff_timer(kbdev, global_iface);
|
||||
|
||||
set_timeout_global(global_iface, kbase_csf_timeout_get(kbdev));
|
||||
|
||||
@@ -526,8 +619,7 @@ static void global_init(struct kbase_device *const kbdev, u32 req_mask)
|
||||
/**
|
||||
* global_init_on_boot - Sends a global request to control various features.
|
||||
*
|
||||
* @kbdev: Instance of a GPU platform device that implements a command
|
||||
* stream front-end interface.
|
||||
* @kbdev: Instance of a GPU platform device that implements a CSF interface.
|
||||
*
|
||||
* Currently only the request to enable endpoints and cycle counter is sent.
|
||||
*
|
||||
@@ -535,19 +627,29 @@ static void global_init(struct kbase_device *const kbdev, u32 req_mask)
|
||||
*/
|
||||
static int global_init_on_boot(struct kbase_device *const kbdev)
|
||||
{
|
||||
u32 const req_mask = CSF_GLB_REQ_CFG_MASK;
|
||||
unsigned long flags;
|
||||
u64 core_mask;
|
||||
|
||||
global_init(kbdev, req_mask);
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
core_mask = kbase_pm_ca_get_core_mask(kbdev);
|
||||
kbdev->csf.firmware_hctl_core_pwr =
|
||||
kbase_pm_no_mcu_core_pwroff(kbdev);
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
return wait_for_global_request(kbdev, req_mask);
|
||||
global_init(kbdev, core_mask);
|
||||
|
||||
return wait_for_global_request(kbdev, CSF_GLB_REQ_CFG_MASK);
|
||||
}
|
||||
|
||||
void kbase_csf_firmware_global_reinit(struct kbase_device *kbdev)
|
||||
void kbase_csf_firmware_global_reinit(struct kbase_device *kbdev,
|
||||
u64 core_mask)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
kbdev->csf.glb_init_request_pending = true;
|
||||
global_init(kbdev, CSF_GLB_REQ_CFG_MASK);
|
||||
kbdev->csf.firmware_hctl_core_pwr =
|
||||
kbase_pm_no_mcu_core_pwroff(kbdev);
|
||||
global_init(kbdev, core_mask);
|
||||
}
|
||||
|
||||
bool kbase_csf_firmware_global_reinit_complete(struct kbase_device *kbdev)
|
||||
@@ -561,6 +663,31 @@ bool kbase_csf_firmware_global_reinit_complete(struct kbase_device *kbdev)
|
||||
return !kbdev->csf.glb_init_request_pending;
|
||||
}
|
||||
|
||||
void kbase_csf_firmware_update_core_attr(struct kbase_device *kbdev,
|
||||
bool update_core_pwroff_timer, bool update_core_mask, u64 core_mask)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
if (update_core_mask)
|
||||
enable_endpoints_global(&kbdev->csf.global_iface, core_mask);
|
||||
if (update_core_pwroff_timer)
|
||||
enable_shader_poweroff_timer(kbdev, &kbdev->csf.global_iface);
|
||||
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
}
|
||||
|
||||
bool kbase_csf_firmware_core_attr_updated(struct kbase_device *kbdev)
|
||||
{
|
||||
lockdep_assert_held(&kbdev->hwaccess_lock);
|
||||
|
||||
return global_request_complete(kbdev, GLB_REQ_CFG_ALLOC_EN_MASK |
|
||||
GLB_REQ_CFG_PWROFF_TIMER_MASK);
|
||||
}
|
||||
|
||||
static void kbase_csf_firmware_reload_worker(struct work_struct *work)
|
||||
{
|
||||
struct kbase_device *kbdev = container_of(work, struct kbase_device,
|
||||
@@ -604,6 +731,129 @@ void kbase_csf_firmware_reload_completed(struct kbase_device *kbdev)
|
||||
kbase_pm_update_state(kbdev);
|
||||
}
|
||||
|
||||
static u32 convert_dur_to_idle_count(struct kbase_device *kbdev, const u32 dur_ms)
|
||||
{
|
||||
#define HYSTERESIS_VAL_UNIT_SHIFT (10)
|
||||
/* Get the cntfreq_el0 value, which drives the SYSTEM_TIMESTAMP */
|
||||
u64 freq = arch_timer_get_cntfrq();
|
||||
u64 dur_val = dur_ms;
|
||||
u32 cnt_val_u32, reg_val_u32;
|
||||
bool src_system_timestamp = freq > 0;
|
||||
|
||||
if (!src_system_timestamp) {
|
||||
/* Get the cycle_counter source alternative */
|
||||
spin_lock(&kbdev->pm.clk_rtm.lock);
|
||||
if (kbdev->pm.clk_rtm.clks[0])
|
||||
freq = kbdev->pm.clk_rtm.clks[0]->clock_val;
|
||||
else
|
||||
dev_warn(kbdev->dev, "No GPU clock, unexpected intregration issue!");
|
||||
spin_unlock(&kbdev->pm.clk_rtm.lock);
|
||||
|
||||
dev_info(kbdev->dev, "Can't get the timestamp frequency, "
|
||||
"use cycle counter format with firmware idle hysteresis!");
|
||||
}
|
||||
|
||||
/* Formula for dur_val = ((dur_ms/1000) * freq_HZ) >> 10) */
|
||||
dur_val = (dur_val * freq) >> HYSTERESIS_VAL_UNIT_SHIFT;
|
||||
dur_val = div_u64(dur_val, 1000);
|
||||
|
||||
/* Interface limits the value field to S32_MAX */
|
||||
cnt_val_u32 = (dur_val > S32_MAX) ? S32_MAX : (u32)dur_val;
|
||||
|
||||
reg_val_u32 = GLB_IDLE_TIMER_TIMEOUT_SET(0, cnt_val_u32);
|
||||
/* add the source flag */
|
||||
if (src_system_timestamp)
|
||||
reg_val_u32 = GLB_IDLE_TIMER_TIMER_SOURCE_SET(reg_val_u32,
|
||||
GLB_IDLE_TIMER_TIMER_SOURCE_SYSTEM_TIMESTAMP);
|
||||
else
|
||||
reg_val_u32 = GLB_IDLE_TIMER_TIMER_SOURCE_SET(reg_val_u32,
|
||||
GLB_IDLE_TIMER_TIMER_SOURCE_GPU_COUNTER);
|
||||
|
||||
return reg_val_u32;
|
||||
}
|
||||
|
||||
u32 kbase_csf_firmware_get_gpu_idle_hysteresis_time(struct kbase_device *kbdev)
|
||||
{
|
||||
return kbdev->csf.gpu_idle_hysteresis_ms;
|
||||
}
|
||||
|
||||
u32 kbase_csf_firmware_set_gpu_idle_hysteresis_time(struct kbase_device *kbdev, u32 dur)
|
||||
{
|
||||
unsigned long flags;
|
||||
const u32 hysteresis_val = convert_dur_to_idle_count(kbdev, dur);
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
kbdev->csf.gpu_idle_hysteresis_ms = dur;
|
||||
kbdev->csf.gpu_idle_dur_count = hysteresis_val;
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
|
||||
dev_dbg(kbdev->dev, "CSF set firmware idle hysteresis count-value: 0x%.8x",
|
||||
hysteresis_val);
|
||||
|
||||
return hysteresis_val;
|
||||
}
|
||||
|
||||
static u32 convert_dur_to_core_pwroff_count(struct kbase_device *kbdev, const u32 dur_us)
|
||||
{
|
||||
#define PWROFF_VAL_UNIT_SHIFT (10)
|
||||
/* Get the cntfreq_el0 value, which drives the SYSTEM_TIMESTAMP */
|
||||
u64 freq = arch_timer_get_cntfrq();
|
||||
u64 dur_val = dur_us;
|
||||
u32 cnt_val_u32, reg_val_u32;
|
||||
bool src_system_timestamp = freq > 0;
|
||||
|
||||
if (!src_system_timestamp) {
|
||||
/* Get the cycle_counter source alternative */
|
||||
spin_lock(&kbdev->pm.clk_rtm.lock);
|
||||
if (kbdev->pm.clk_rtm.clks[0])
|
||||
freq = kbdev->pm.clk_rtm.clks[0]->clock_val;
|
||||
else
|
||||
dev_warn(kbdev->dev, "No GPU clock, unexpected integration issue!");
|
||||
spin_unlock(&kbdev->pm.clk_rtm.lock);
|
||||
|
||||
dev_info(kbdev->dev, "Can't get the timestamp frequency, "
|
||||
"use cycle counter with MCU Core Poweroff timer!");
|
||||
}
|
||||
|
||||
/* Formula for dur_val = ((dur_us/1e6) * freq_HZ) >> 10) */
|
||||
dur_val = (dur_val * freq) >> HYSTERESIS_VAL_UNIT_SHIFT;
|
||||
dur_val = div_u64(dur_val, 1000000);
|
||||
|
||||
/* Interface limits the value field to S32_MAX */
|
||||
cnt_val_u32 = (dur_val > S32_MAX) ? S32_MAX : (u32)dur_val;
|
||||
|
||||
reg_val_u32 = GLB_PWROFF_TIMER_TIMEOUT_SET(0, cnt_val_u32);
|
||||
/* add the source flag */
|
||||
if (src_system_timestamp)
|
||||
reg_val_u32 = GLB_PWROFF_TIMER_TIMER_SOURCE_SET(reg_val_u32,
|
||||
GLB_PWROFF_TIMER_TIMER_SOURCE_SYSTEM_TIMESTAMP);
|
||||
else
|
||||
reg_val_u32 = GLB_PWROFF_TIMER_TIMER_SOURCE_SET(reg_val_u32,
|
||||
GLB_PWROFF_TIMER_TIMER_SOURCE_GPU_COUNTER);
|
||||
|
||||
return reg_val_u32;
|
||||
}
|
||||
|
||||
u32 kbase_csf_firmware_get_mcu_core_pwroff_time(struct kbase_device *kbdev)
|
||||
{
|
||||
return kbdev->csf.mcu_core_pwroff_dur_us;
|
||||
}
|
||||
|
||||
u32 kbase_csf_firmware_set_mcu_core_pwroff_time(struct kbase_device *kbdev, u32 dur)
|
||||
{
|
||||
unsigned long flags;
|
||||
const u32 pwroff = convert_dur_to_core_pwroff_count(kbdev, dur);
|
||||
|
||||
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
||||
kbdev->csf.mcu_core_pwroff_dur_us = dur;
|
||||
kbdev->csf.mcu_core_pwroff_dur_count = pwroff;
|
||||
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
||||
|
||||
dev_dbg(kbdev->dev, "MCU Core Poweroff input update: 0x%.8x", pwroff);
|
||||
|
||||
return pwroff;
|
||||
}
|
||||
|
||||
int kbase_csf_firmware_init(struct kbase_device *kbdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -623,15 +873,21 @@ int kbase_csf_firmware_init(struct kbase_device *kbdev)
|
||||
|
||||
init_waitqueue_head(&kbdev->csf.event_wait);
|
||||
kbdev->csf.interrupt_received = false;
|
||||
kbdev->csf.fw_timeout_ms = CSF_FIRMWARE_TIMEOUT_MS;
|
||||
|
||||
INIT_LIST_HEAD(&kbdev->csf.firmware_interfaces);
|
||||
INIT_LIST_HEAD(&kbdev->csf.firmware_config);
|
||||
INIT_LIST_HEAD(&kbdev->csf.firmware_trace_buffers.list);
|
||||
INIT_WORK(&kbdev->csf.firmware_reload_work,
|
||||
kbase_csf_firmware_reload_worker);
|
||||
INIT_WORK(&kbdev->csf.fw_error_work, firmware_error_worker);
|
||||
|
||||
mutex_init(&kbdev->csf.reg_lock);
|
||||
|
||||
kbdev->csf.gpu_idle_hysteresis_ms = FIRMWARE_IDLE_HYSTERESIS_TIME_MS;
|
||||
kbdev->csf.gpu_idle_dur_count = convert_dur_to_idle_count(kbdev,
|
||||
FIRMWARE_IDLE_HYSTERESIS_TIME_MS);
|
||||
|
||||
ret = kbase_mcu_shared_interface_region_tracker_init(kbdev);
|
||||
if (ret != 0) {
|
||||
dev_err(kbdev->dev, "Failed to setup the rb tree for managing shared interface segment\n");
|
||||
@@ -659,6 +915,10 @@ int kbase_csf_firmware_init(struct kbase_device *kbdev)
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
|
||||
ret = kbase_csf_setup_dummy_user_reg_page(kbdev);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
|
||||
ret = kbase_csf_scheduler_init(kbdev);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
@@ -680,6 +940,8 @@ error:
|
||||
|
||||
void kbase_csf_firmware_term(struct kbase_device *kbdev)
|
||||
{
|
||||
cancel_work_sync(&kbdev->csf.fw_error_work);
|
||||
|
||||
kbase_csf_timeout_term(kbdev);
|
||||
|
||||
/* NO_MALI: Don't stop firmware or unload MMU tables */
|
||||
@@ -688,6 +950,8 @@ void kbase_csf_firmware_term(struct kbase_device *kbdev)
|
||||
|
||||
kbase_csf_scheduler_term(kbdev);
|
||||
|
||||
kbase_csf_free_dummy_user_reg_page(kbdev);
|
||||
|
||||
kbase_csf_doorbell_mapping_term(kbdev);
|
||||
|
||||
free_global_iface(kbdev);
|
||||
@@ -721,7 +985,51 @@ void kbase_csf_firmware_term(struct kbase_device *kbdev)
|
||||
kbase_mcu_shared_interface_region_tracker_term(kbdev);
|
||||
}
|
||||
|
||||
int kbase_csf_firmware_ping(struct kbase_device *const kbdev)
|
||||
void kbase_csf_firmware_enable_gpu_idle_timer(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_csf_global_iface *global_iface = &kbdev->csf.global_iface;
|
||||
u32 glb_req;
|
||||
|
||||
kbase_csf_scheduler_spin_lock_assert_held(kbdev);
|
||||
|
||||
/* The scheduler is assumed to only call the enable when its internal
|
||||
* state indicates that the idle timer has previously been disabled. So
|
||||
* on entry the expected field values are:
|
||||
* 1. GLOBAL_INPUT_BLOCK.GLB_REQ.IDLE_ENABLE: 0
|
||||
* 2. GLOBAL_OUTPUT_BLOCK.GLB_ACK.IDLE_ENABLE: 0, or, on 1 -> 0
|
||||
*/
|
||||
|
||||
glb_req = kbase_csf_firmware_global_input_read(global_iface, GLB_REQ);
|
||||
if (glb_req & GLB_REQ_IDLE_ENABLE_MASK)
|
||||
dev_err(kbdev->dev, "Incoherent scheduler state on REQ_IDLE_ENABLE!");
|
||||
|
||||
kbase_csf_firmware_global_input(global_iface, GLB_IDLE_TIMER,
|
||||
kbdev->csf.gpu_idle_dur_count);
|
||||
|
||||
kbase_csf_firmware_global_input_mask(global_iface, GLB_REQ,
|
||||
GLB_REQ_REQ_IDLE_ENABLE, GLB_REQ_IDLE_ENABLE_MASK);
|
||||
|
||||
dev_dbg(kbdev->dev, "Enabling GPU idle timer with count-value: 0x%.8x",
|
||||
kbdev->csf.gpu_idle_dur_count);
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
}
|
||||
|
||||
void kbase_csf_firmware_disable_gpu_idle_timer(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_csf_global_iface *global_iface = &kbdev->csf.global_iface;
|
||||
|
||||
kbase_csf_scheduler_spin_lock_assert_held(kbdev);
|
||||
|
||||
kbase_csf_firmware_global_input_mask(global_iface, GLB_REQ,
|
||||
GLB_REQ_REQ_IDLE_DISABLE,
|
||||
GLB_REQ_IDLE_DISABLE_MASK);
|
||||
|
||||
dev_dbg(kbdev->dev, "Sending request to disable gpu idle timer");
|
||||
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
}
|
||||
|
||||
void kbase_csf_firmware_ping(struct kbase_device *const kbdev)
|
||||
{
|
||||
const struct kbase_csf_global_iface *const global_iface =
|
||||
&kbdev->csf.global_iface;
|
||||
@@ -731,7 +1039,11 @@ int kbase_csf_firmware_ping(struct kbase_device *const kbdev)
|
||||
set_global_request(global_iface, GLB_REQ_PING_MASK);
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
}
|
||||
|
||||
int kbase_csf_firmware_ping_wait(struct kbase_device *const kbdev)
|
||||
{
|
||||
kbase_csf_firmware_ping(kbdev);
|
||||
return wait_for_global_request(kbdev, GLB_REQ_PING_MASK);
|
||||
}
|
||||
|
||||
@@ -763,13 +1075,9 @@ void kbase_csf_enter_protected_mode(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_csf_global_iface *global_iface = &kbdev->csf.global_iface;
|
||||
unsigned long flags;
|
||||
unsigned int value;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
value = kbase_csf_firmware_global_output(global_iface, GLB_ACK);
|
||||
value ^= GLB_REQ_PROTM_ENTER_MASK;
|
||||
kbase_csf_firmware_global_input_mask(global_iface, GLB_REQ, value,
|
||||
GLB_REQ_PROTM_ENTER_MASK);
|
||||
set_global_request(global_iface, GLB_REQ_PROTM_ENTER_MASK);
|
||||
dev_dbg(kbdev->dev, "Sending request to enter protected mode");
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
@@ -781,22 +1089,41 @@ void kbase_csf_firmware_trigger_mcu_halt(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_csf_global_iface *global_iface = &kbdev->csf.global_iface;
|
||||
unsigned long flags;
|
||||
unsigned int value;
|
||||
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
value = kbase_csf_firmware_global_output(global_iface, GLB_ACK);
|
||||
value ^= GLB_REQ_HALT_MASK;
|
||||
kbase_csf_firmware_global_input_mask(global_iface, GLB_REQ, value,
|
||||
GLB_REQ_HALT_MASK);
|
||||
set_global_request(global_iface, GLB_REQ_HALT_MASK);
|
||||
dev_dbg(kbdev->dev, "Sending request to HALT MCU");
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
}
|
||||
|
||||
int kbase_csf_trigger_firmware_config_update(struct kbase_device *kbdev)
|
||||
{
|
||||
struct kbase_csf_global_iface *global_iface = &kbdev->csf.global_iface;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
/* The 'reg_lock' is also taken and is held till the update is
|
||||
* complete, to ensure the config update gets serialized.
|
||||
*/
|
||||
mutex_lock(&kbdev->csf.reg_lock);
|
||||
kbase_csf_scheduler_spin_lock(kbdev, &flags);
|
||||
|
||||
set_global_request(global_iface, GLB_REQ_FIRMWARE_CONFIG_UPDATE_MASK);
|
||||
dev_dbg(kbdev->dev, "Sending request for FIRMWARE_CONFIG_UPDATE");
|
||||
kbase_csf_ring_doorbell(kbdev, CSF_KERNEL_DOORBELL_NR);
|
||||
kbase_csf_scheduler_spin_unlock(kbdev, flags);
|
||||
|
||||
err = wait_for_global_request(kbdev,
|
||||
GLB_REQ_FIRMWARE_CONFIG_UPDATE_MASK);
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_grp_and_stm - Copy command stream and/or group data
|
||||
* copy_grp_and_stm - Copy CS and/or group data
|
||||
*
|
||||
* @iface: Global command stream front-end interface provided by
|
||||
* @iface: Global CSF interface provided by
|
||||
* the firmware.
|
||||
* @group_data: Pointer where to store all the group data
|
||||
* (sequentially).
|
||||
@@ -807,7 +1134,7 @@ void kbase_csf_firmware_trigger_mcu_halt(struct kbase_device *kbdev)
|
||||
* @max_total_stream_num: The maximum number of streams to be read.
|
||||
* Can be 0, in which case stream_data is unused.
|
||||
*
|
||||
* Return: Total number of command streams, summed across all groups.
|
||||
* Return: Total number of CSs, summed across all groups.
|
||||
*/
|
||||
static u32 copy_grp_and_stm(
|
||||
const struct kbase_csf_global_iface * const iface,
|
||||
@@ -830,6 +1157,8 @@ static u32 copy_grp_and_stm(
|
||||
if (i < max_group_num) {
|
||||
group_data[i].features = iface->groups[i].features;
|
||||
group_data[i].stream_num = iface->groups[i].stream_num;
|
||||
group_data[i].suspend_size =
|
||||
iface->groups[i].suspend_size;
|
||||
}
|
||||
for (j = 0; j < iface->groups[i].stream_num; j++) {
|
||||
if (total_stream_num < max_total_stream_num)
|
||||
@@ -842,26 +1171,28 @@ static u32 copy_grp_and_stm(
|
||||
return total_stream_num;
|
||||
}
|
||||
|
||||
u32 kbase_csf_firmware_get_glb_iface(struct kbase_device *kbdev,
|
||||
u32 kbase_csf_firmware_get_glb_iface(
|
||||
struct kbase_device *kbdev,
|
||||
struct basep_cs_group_control *const group_data,
|
||||
u32 const max_group_num,
|
||||
struct basep_cs_stream_control *const stream_data,
|
||||
u32 const max_total_stream_num, u32 *const glb_version,
|
||||
u32 *const features, u32 *const group_num, u32 *const prfcnt_size)
|
||||
u32 *const features, u32 *const group_num, u32 *const prfcnt_size,
|
||||
u32 *const instr_features)
|
||||
{
|
||||
const struct kbase_csf_global_iface * const iface =
|
||||
&kbdev->csf.global_iface;
|
||||
|
||||
if (WARN_ON(!glb_version) ||
|
||||
WARN_ON(!features) ||
|
||||
WARN_ON(!group_num) ||
|
||||
WARN_ON(!prfcnt_size))
|
||||
if (WARN_ON(!glb_version) || WARN_ON(!features) ||
|
||||
WARN_ON(!group_num) || WARN_ON(!prfcnt_size) ||
|
||||
WARN_ON(!instr_features))
|
||||
return 0;
|
||||
|
||||
*glb_version = iface->version;
|
||||
*features = iface->features;
|
||||
*group_num = iface->group_num;
|
||||
*prfcnt_size = iface->prfcnt_size;
|
||||
*instr_features = iface->instr_features;
|
||||
|
||||
return copy_grp_and_stm(iface, group_data, max_group_num,
|
||||
stream_data, max_total_stream_num);
|
||||
@@ -941,9 +1272,9 @@ int kbase_csf_firmware_mcu_shared_mapping_init(
|
||||
mutex_lock(&kbdev->csf.reg_lock);
|
||||
ret = kbase_add_va_region_rbtree(kbdev, va_reg, 0, num_pages, 1);
|
||||
va_reg->flags &= ~KBASE_REG_FREE;
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
if (ret)
|
||||
goto va_region_add_error;
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
|
||||
gpu_map_properties &= (KBASE_REG_GPU_RD | KBASE_REG_GPU_WR);
|
||||
gpu_map_properties |= gpu_map_prot;
|
||||
@@ -965,9 +1296,9 @@ int kbase_csf_firmware_mcu_shared_mapping_init(
|
||||
mmu_insert_pages_error:
|
||||
mutex_lock(&kbdev->csf.reg_lock);
|
||||
kbase_remove_va_region(va_reg);
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
va_region_add_error:
|
||||
kbase_free_alloced_region(va_reg);
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
va_region_alloc_error:
|
||||
vunmap(cpu_addr);
|
||||
vmap_error:
|
||||
@@ -981,7 +1312,8 @@ page_list_alloc_error:
|
||||
kfree(phys);
|
||||
out:
|
||||
/* Zero-initialize the mapping to make sure that the termination
|
||||
* function doesn't try to unmap or free random addresses. */
|
||||
* function doesn't try to unmap or free random addresses.
|
||||
*/
|
||||
csf_mapping->phys = NULL;
|
||||
csf_mapping->cpu_addr = NULL;
|
||||
csf_mapping->va_reg = NULL;
|
||||
@@ -996,8 +1328,8 @@ void kbase_csf_firmware_mcu_shared_mapping_term(
|
||||
if (csf_mapping->va_reg) {
|
||||
mutex_lock(&kbdev->csf.reg_lock);
|
||||
kbase_remove_va_region(csf_mapping->va_reg);
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
kbase_free_alloced_region(csf_mapping->va_reg);
|
||||
mutex_unlock(&kbdev->csf.reg_lock);
|
||||
}
|
||||
|
||||
if (csf_mapping->phys) {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* (C) COPYRIGHT 2019-2020 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.
|
||||
* of such GNU license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,8 +17,6 @@
|
||||
* along with this program; if not, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <mali_kbase.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user