60 Commits

Author SHA1 Message Date
Ashish Mhetre
a34ad11c0a arm64: configs: Enable CMDQV
Enable CMDQV in defconfigs

Bug 5158829
Bug 5111712
Bug 5419379

Change-Id: I93fdc23c3590091016f1a6f99157a4d1cb2fb638
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3439904
(cherry-picked from commit a9e268cc859f893579b1071d31451f1511cc1e65)
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3495589
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 08:13:30 -08:00
Ashish Mhetre
e3677b3425 Merge 'drivers/iommu' from dev-main into rel-38
Relevant changes:
9fc5b88382e6 DOWNSTREAM: iommu/arm-smmu-v3: Fix arm_smmu_impl_ops
75436e3f13e4 [DOWNSTREAM] iommu/arm-smmu-v3: Retain prod and cons after resume
b82c7a458d43 [DOWNSTREAM]: iommu/tegra241-cmdqv: WAR for 64-bit writes on NV HV
61d9b92ef30a [UPSTREAM PENDING] iommu/arm-smmu-v3: Add device-tree support in tegra241-cmdqv driver
d4303ed2cb0d DOWNSTREAM: iommu/arm-smmu-v3: Add pm suspend op
69677a732a7d [UPSTREAM PENDING] iommu/arm-smmu-v3: add suspend/resume support
15a4351b19b6 Revert "NVIDIA: SAUCE: iommu/arm-smmu-v3: add suspend/resume support"
415c7fb8e350 [DOWNSTREAM]iommu/arm-smmu-v3: use reserved memory for allocations
0d90af2451b4 iommu/tegra241-cmdqv: Fix warnings due to dmam_free_coherent()
680d181b9007 iommu/tegra241-cmdqv: Read SMMU IDR1.CMDQS instead of hardcoding
2553592d2c24 iommu/tegra241-cmdqv: do not use smp_processor_id in preemptible context
7f390db97e23 iommu/tegra241-cmdqv: Fix alignment failure at max_n_shift
0695cfe98a97 iommu/tegra241-cmdqv: Fix unused variable warning
b266c532562d iommu/tegra241-cmdqv: Staticize cmdqv_debugfs_dir
d5bdc04d1988 iommu/tegra241-cmdqv: Do not allocate vcmdq until dma_set_mask_and_coherent
0c7291931d8c iommu/tegra241-cmdqv: Drop static at local variable
aa469aa3dfa5 iommu/tegra241-cmdqv: Fix ioremap() error handling in probe()
d639d2b0554b iommu/tegra241-cmdqv: Fix -Wformat-truncation warnings in lvcmdq_error_header
2215e2358dff iommu/tegra241-cmdqv: Limit CMDs for VCMDQs of a guest owned VINTF
eefc77955d2e iommu/arm-smmu-v3: Start a new batch if new command is not supported
f70fe98429c7 iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
44a9231b800b iommu/arm-smmu-v3: Add struct arm_smmu_impl_ops
b66ffedc113c iommu/arm-smmu-v3: Add acpi_smmu_iort_probe_model for impl
33f689e1cecd iommu/arm-smmu-v3: Add ARM_SMMU_OPT_TEGRA241_CMDQV
fb85f6f304f2 iommu/arm-smmu-v3: Make symbols public for CONFIG_TEGRA241_CMDQV
8e3c873d5ab3 iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_init
4fae6fb536f6 iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_build_sync_cmd
3c255f7d9cbd iommu/arm-smmu-v3: Issue a batch of commands to the same cmdq

Change-Id: I3e28a37c04d9bbfb6e22a1bc51771bc83db11b07
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
2025-11-20 10:25:56 +00:00
Ashish Mhetre
5865348c39 DOWNSTREAM: iommu/arm-smmu-v3: Fix arm_smmu_impl_ops
While backporting the upstream commit:
44a9231b800b454befac069d468e2f2b71827474
(iommu/arm-smmu-v3: Add struct arm_smmu_impl_ops)

it didn't apply correctly causing cmdqv reset not getting invoked during
resume. Fix it by moving the device reset in arm_smmu_device_reset as
in upstream commit.

Bug 5419379
Bug 5111712

Change-Id: Ifc873bccd0b43b4e6890ac5d6672b4ea3e71cc88
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3452819
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:55 +00:00
Ashish Mhetre
c79ae7acb3 [DOWNSTREAM] iommu/arm-smmu-v3: Retain prod and cons after resume
Set PROD and CONS registers of VCMDQs with retained values from
prod and cons variables after SC7 resume.

Bug 5117507

Change-Id: Ida34bb04ce669fdc7901fa935e2a2eff806e3d1e
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3447044
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:55 +00:00
Ashish Mhetre
c16f161cd7 [DOWNSTREAM]: iommu/tegra241-cmdqv: WAR for 64-bit writes on NV HV
NVIDIA’s hypervisor does not support 64-bit writes to consecutive
two 32-bit registers e.g., VCMDQ_BASE_LO and VCMDQ_BASE_HI. The driver
currently issues a 64-bit write to such registers, which works fine on
real hardware but fails under NV HV.
This is not a functional bug in the driver, but rather a quirk of the
hypervisor which does not fully emulate the HW behavior. Add a workaround
to split the write into two 32-bit accesses.

Bug 5111712

Change-Id: I6fb6a926a80326e2d7a8a2ec9e475106af843f7c
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3439898
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:55 +00:00
Ashish Mhetre
9cbafdf4c3 [UPSTREAM PENDING] iommu/arm-smmu-v3: Add device-tree support in tegra241-cmdqv driver
Add support for initialization from device-tree in CMDQV driver required
for T264 which mimics the current ACPI probe.
Drop ACPI dependency in Kconfig since the inline ifdef would be enough
to depend on ACPI

Bug 4900238

Change-Id: I4ff0996c9ee0688a0ea795892e2fe59133303658
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3439897
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2025-11-20 10:22:54 +00:00
Ashish Mhetre
a7ae04815b DOWNSTREAM: iommu/arm-smmu-v3: Add pm suspend op
- Issue CMD_OP_CFGI and CMD_OP_TBI_ALL to ensure all pending
transactions are complete before going into suspend and then disable
SMMU device so that there won't be any new map/unmap requests.
- Change to sleep ops to late sleep ops so that SMMU will suspend
late after clients and resume early before clients.
- Add few debug prints

Bug 5117507
Bug 5165373

Change-Id: I108861e6288fd63cebd6d2da2aa93ece071d419f
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3447041
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:54 +00:00
Pritesh Raithatha
09c630f285 [UPSTREAM PENDING] iommu/arm-smmu-v3: add suspend/resume support
Add suspend/resume support for arm-smmu-v3. Move irq initialization
to probe and re-use the reset function for restoring registers in
resume.

Bug 4267541

Change-Id: I7bf410f0b69b56f1e1c138e9802449bcd3634a1e
Signed-off-by: Pritesh Raithatha <praithatha@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-stable/+/3112738
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3447965
Tested-by: Ashish Mhetre <amhetre@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:54 +00:00
Ashish Mhetre
a0eab0226d Revert "NVIDIA: SAUCE: iommu/arm-smmu-v3: add suspend/resume support"
There is other similar commit added for suspend/resume on K6.1.
Using that commit e6edc95c25dc52fcebf985206ce61fbf817abc98

This reverts commit be979fd7a1.

Bug 5506739

Change-Id: I32d88bc63d9f94d4eb6efdac298e7c2932b7b6e3
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3449096
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:54 +00:00
Pritesh Raithatha
56a7b340cd [DOWNSTREAM]iommu/arm-smmu-v3: use reserved memory for allocations
CMA allocations are reusable. When not allocated, it can be used
for temporary allocations. When there is allocation request,
temporary allocations will be reclaimed and that takes time.

SMMU uses CMA allocations and causing boot time increase. To avoid
this, add reserved memory pool and use for SMMU allocations instead
of CMA.

With reserved memory 1MB allocation time is reduced from 17999343ns
to 166037ns.

Bug 5115195

Change-Id: I34febac4235da68027908969b9348cbfd2feffc4
Signed-off-by: Pritesh Raithatha <praithatha@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3446227
Reviewed-by: svcacv <svcacv@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
2025-11-20 10:22:54 +00:00
Nicolin Chen
fee12cf9e3 iommu/tegra241-cmdqv: Fix warnings due to dmam_free_coherent()
Two WARNINGs are observed when SMMU driver rolls back upon failure:
 arm-smmu-v3.9.auto: Failed to register iommu
 arm-smmu-v3.9.auto: probe with driver arm-smmu-v3 failed with error -22
 ------------[ cut here ]------------
 WARNING: CPU: 5 PID: 1 at kernel/dma/mapping.c:74 dmam_free_coherent+0xc0/0xd8
 Call trace:
  dmam_free_coherent+0xc0/0xd8 (P)
  tegra241_vintf_free_lvcmdq+0x74/0x188
  tegra241_cmdqv_remove_vintf+0x60/0x148
  tegra241_cmdqv_remove+0x48/0xc8
  arm_smmu_impl_remove+0x28/0x60
  devm_action_release+0x1c/0x40
 ------------[ cut here ]------------
 128 pages are still in use!
 WARNING: CPU: 16 PID: 1 at mm/page_alloc.c:6902 free_contig_range+0x18c/0x1c8
 Call trace:
  free_contig_range+0x18c/0x1c8 (P)
  cma_release+0x154/0x2f0
  dma_free_contiguous+0x38/0xa0
  dma_direct_free+0x10c/0x248
  dma_free_attrs+0x100/0x290
  dmam_free_coherent+0x78/0xd8
  tegra241_vintf_free_lvcmdq+0x74/0x160
  tegra241_cmdqv_remove+0x98/0x198
  arm_smmu_impl_remove+0x28/0x60
  devm_action_release+0x1c/0x40

This is because the LVCMDQ queue memory are managed by devres, while that
dmam_free_coherent() is called in the context of devm_action_release().

Jason pointed out that "arm_smmu_impl_probe() has mis-ordered the devres
callbacks if ops->device_remove() is going to be manually freeing things
that probe allocated":
https://lore.kernel.org/linux-iommu/20250407174408.GB1722458@nvidia.com/

In fact, tegra241_cmdqv_init_structures() only allocates memory resources
which means any failure that it generates would be similar to -ENOMEM, so
there is no point in having that "falling back to standard SMMU" routine,
as the standard SMMU would likely fail to allocate memory too.

Remove the unwind part in tegra241_cmdqv_init_structures(), and return a
proper error code to ask SMMU driver to call tegra241_cmdqv_remove() via
impl_ops->device_remove(). Then, drop tegra241_vintf_free_lvcmdq() since
devres will take care of that.

Fixes: 483e0bd8883a ("iommu/tegra241-cmdqv: Do not allocate vcmdq until dma_set_mask_and_coherent")
Cc: stable@vger.kernel.org
Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I4458d5b155a1a3844c0004e6bfe2863b7f967220
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/20250407201908.172225-1-nicolinc@nvidia.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435447
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
873bfe47e3 iommu/tegra241-cmdqv: Read SMMU IDR1.CMDQS instead of hardcoding
The hardware limitation "max=19" actually comes from SMMU Command Queue.
So, it'd be more natural for tegra241-cmdqv driver to read it out rather
than hardcoding it itself.

This is not an issue yet for a kernel on a baremetal system, but a guest
kernel setting the queue base/size in form of IPA/gPA might result in a
noncontiguous queue in the physical address space, if underlying physical
pages backing up the guest RAM aren't contiguous entirely: e.g. 2MB-page
backed guest RAM cannot guarantee a contiguous queue if it is 8MB (capped
to VCMDQ_LOG2SIZE_MAX=19). This might lead to command errors when HW does
linear-read from a noncontiguous queue memory.

Adding this extra IDR1.CMDQS cap (in the guest kernel) allows VMM to set
SMMU's IDR1.CMDQS=17 for the case mentioned above, so a guest-level queue
will be capped to maximum 2MB, ensuring a contiguous queue memory.

Fixes: a3799717b881 ("iommu/tegra241-cmdqv: Fix alignment failure at max_n_shift")
Reported-by: Ian Kalinowski <ikalinowski@nvidia.com>
Cc: stable@vger.kernel.org
Change-Id: I3de2a6e757001ef6bb797ab4368f808550a87d06
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/20241219051421.1850267-1-nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435446
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Luis Claudio R. Goncalves
6e8b7fa767 iommu/tegra241-cmdqv: do not use smp_processor_id in preemptible context
During boot some of the calls to tegra241_cmdqv_get_cmdq() will happen
in preemptible context. As this function calls smp_processor_id(), if
CONFIG_DEBUG_PREEMPT is enabled, these calls will trigger a series of
"BUG: using smp_processor_id() in preemptible" backtraces.

As tegra241_cmdqv_get_cmdq() only calls smp_processor_id() to use the
CPU number as a factor to balance out traffic on cmdq usage, it is safe
to use raw_smp_processor_id() here.

Cc: <stable@vger.kernel.org>
Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Change-Id: I170a6c2f6846d75228750bca5ecd8e5efd90f231
Signed-off-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/Z1L1mja3nXzsJ0Pk@uudg.org
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435445
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
f0d493630e iommu/tegra241-cmdqv: Fix alignment failure at max_n_shift
When configuring a kernel with PAGE_SIZE=4KB, depending on its setting of
CONFIG_CMA_ALIGNMENT, VCMDQ_LOG2SIZE_MAX=19 could fail the alignment test
and trigger a WARN_ON:
    WARNING: at drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:3646
    Call trace:
     arm_smmu_init_one_queue+0x15c/0x210
     tegra241_cmdqv_init_structures+0x114/0x338
     arm_smmu_device_probe+0xb48/0x1d90

Fix it by capping max_n_shift to CMDQ_MAX_SZ_SHIFT as SMMUv3 CMDQ does.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Change-Id: I7136ff56bcb624a2f53503ecbb1f494ee90f681d
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/20241111030226.1940737-1-nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435444
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:53 +00:00
Will Deacon
e3a532d6e4 iommu/tegra241-cmdqv: Fix unused variable warning
While testing some io-pgtable changes, I ran into a compiler warning
from the Tegra CMDQ driver:

  drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c:803:23: warning: unused variable 'cmdqv_debugfs_dir' [-Wunused-variable]
    803 | static struct dentry *cmdqv_debugfs_dir;
        |                       ^~~~~~~~~~~~~~~~~
  1 warning generated.

Guard the variable declaration with CONFIG_IOMMU_DEBUGFS to silence the
warning.

Change-Id: I3699eaccd846f23418c9d5cae609ffd67429a76e
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435443
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
78730786af iommu/tegra241-cmdqv: Staticize cmdqv_debugfs_dir
Fix a sparse warning.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202410172003.bRQEReTc-lkp@intel.com/
Change-Id: Icbd7e06c479e96f6b813021fb3eaf07fb0ae899e
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/20241021230847.811218-1-nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435442
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
737d405b11 iommu/tegra241-cmdqv: Do not allocate vcmdq until dma_set_mask_and_coherent
It's observed that, when the first 4GB of system memory was reserved, all
VCMDQ allocations failed (even with the smallest qsz in the last attempt):
    arm-smmu-v3: found companion CMDQV device: NVDA200C:00
    arm-smmu-v3: option mask 0x10
    arm-smmu-v3: failed to allocate queue (0x8000 bytes) for vcmdq0
    acpi NVDA200C:00: tegra241_cmdqv: Falling back to standard SMMU CMDQ
    arm-smmu-v3: ias 48-bit, oas 48-bit (features 0x001e1fbf)
    arm-smmu-v3: allocated 524288 entries for cmdq
    arm-smmu-v3: allocated 524288 entries for evtq
    arm-smmu-v3: allocated 524288 entries for priq

This is because the 4GB reserved memory shifted the entire DMA zone from a
lower 32-bit range (on a system without the 4GB carveout) to higher range,
while the dev->coherent_dma_mask was set to DMA_BIT_MASK(32) by default.

The dma_set_mask_and_coherent() call is done in arm_smmu_device_hw_probe()
of the SMMU driver. So any DMA allocation from tegra241_cmdqv_probe() must
wait until the coherent_dma_mask is correctly set.

Move the vintf/vcmdq structure initialization routine into a different op,
"init_structures". Call it at the end of arm_smmu_init_structures(), where
standard SMMU queues get allocated.

Most of the impl_ops aren't ready until vintf/vcmdq structure are init-ed.
So replace the full impl_ops with an init_ops in __tegra241_cmdqv_probe().

And switch to tegra241_cmdqv_impl_ops later in arm_smmu_init_structures().
Note that tegra241_cmdqv_impl_ops does not link to the new init_structures
op after this switch, since there is no point in having it once it's done.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Reported-by: Matt Ochs <mochs@nvidia.com>
Change-Id: Ie7725620055e79702ce2b1bfb3beef624602ccd1
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/530993c3aafa1b0fc3d879b8119e13c629d12e2b.1725503154.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435441
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
98f97f6df7 iommu/tegra241-cmdqv: Drop static at local variable
This is likely a typo. Drop it.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Change-Id: I903366a8fa224ae3542dd1b592c8e51be9c4cb90
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/13fd3accb5b7ed6ec11cc6b7435f79f84af9f45f.1725503154.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435440
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Dan Carpenter
b9be69606e iommu/tegra241-cmdqv: Fix ioremap() error handling in probe()
The ioremap() function doesn't return error pointers, it returns NULL
on error so update the error handling.  Also just return directly
instead of calling iounmap() on the NULL pointer.  Calling
iounmap(NULL) doesn't cause a problem on ARM but on other architectures
it can trigger a warning so it'a bad habbit.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Change-Id: I2ffed2cf23db0632c47f667a4b35584c4d181b7a
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/5a6c1e9a-0724-41b1-86d4-36335d3768ea@stanley.mountain
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435439
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
3c1a5006e3 iommu/tegra241-cmdqv: Fix -Wformat-truncation warnings in lvcmdq_error_header
Kernel test robot reported a few trucation warnings at the snprintf:
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c:
	In function ‘tegra241_vintf_free_lvcmdq’:
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c:239:56:
	warning: ‘%u’ directive output may be truncated writing between 1 and
	5 bytes into a region of size between 3 and 11 [-Wformat-truncation=]
  239 |         snprintf(header, hlen, "VINTF%u: VCMDQ%u/LVCMDQ%u: ",
      |                                                        ^~
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c:239:32: note: directive argument
	in the range [0, 65535]
  239 |         snprintf(header, hlen, "VINTF%u: VCMDQ%u/LVCMDQ%u: ",
      |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c:239:9: note: ‘snprintf’ output
	between 25 and 37 bytes into a destination of size 32
  239 |         snprintf(header, hlen, "VINTF%u: VCMDQ%u/LVCMDQ%u: ",
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  240 |                  vcmdq->vintf->idx, vcmdq->idx, vcmdq->lidx);

Fix by bumping up the size of the header to hold more characters.

Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202409020406.7ed5uojF-lkp@intel.com/
Change-Id: I4329a30cc73b2333eaa6812a5c5e4cd52619549a
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/20240902055745.629456-1-nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435438
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
009e96a7b5 iommu/tegra241-cmdqv: Limit CMDs for VCMDQs of a guest owned VINTF
When VCMDQs are assigned to a VINTF owned by a guest (HYP_OWN bit unset),
only TLB and ATC invalidation commands are supported by the VCMDQ HW. So,
implement the new cmdq->supports_cmd op to scan the input cmd in order to
make sure that it is supported by the selected queue.

Note that the guest VM shouldn't have HYP_OWN bit being set regardless of
guest kernel driver writing it or not, i.e. the hypervisor running in the
host OS should wire this bit to zero when trapping a write access to this
VINTF_CONFIG register from a guest kernel.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: Ic3375566b85f6f75796d23a483897f4e2c83c7fa
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/8160292337059b91271045800e5c62f7295e2c24.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435437
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:53 +00:00
Nicolin Chen
c6f6edb534 iommu/arm-smmu-v3: Start a new batch if new command is not supported
The VCMDQ in the tegra241-cmdqv driver has a guest mode that supports only
a few invalidation commands. A batch is initialized with a cmdq, so it has
to confirm whether a new command is supported or not.

Add a supports_cmd function pointer to the cmdq structure, where the vcmdq
driver should hook a command scan function. Add an inline helper too so it
can be used by both sides.

If a new command is not supported, simply issue the existing batch and re-
init it as a new batch.

Change-Id: I0b8890b4e0c2df2a2824a3950ee23bb885167a40
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/aafb24b881504f18c5d0c7c15f2134e40ad2c486.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435436
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Nate Watterson
2e2b4c777b iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
NVIDIA's Tegra241 Soc has a CMDQ-Virtualization (CMDQV) hardware, extending
the standard ARM SMMU v3 IP to support multiple VCMDQs with virtualization
capabilities. In terms of command queue, they are very like a standard SMMU
CMDQ (or ECMDQs), but only support CS_NONE in the CS field of CMD_SYNC.

Add a new tegra241-cmdqv driver, and insert its structure pointer into the
existing arm_smmu_device, and then add related function calls in the SMMUv3
driver to interact with the CMDQV driver.

In the CMDQV driver, add a minimal part for the in-kernel support: reserve
VINTF0 for in-kernel use, and assign some of the VCMDQs to the VINTF0, and
select one VCMDQ based on the current CPU ID to execute supported commands.
This multi-queue design for in-kernel use gives some limited improvements:
up to 20% reduction of invalidation time was measured by a multi-threaded
DMA unmap benchmark, compared to a single queue.

The other part of the CMDQV driver will be user-space support that gives a
hypervisor running on the host OS to talk to the driver for virtualization
use cases, allowing VMs to use VCMDQs without trappings, i.e. no VM Exits.
This is designed based on IOMMUFD, and its RFC series is also under review.
It will provide a guest OS a bigger improvement: 70% to 90% reductions of
TLB invalidation time were measured by DMA unmap tests running in a guest,
compared to nested SMMU CMDQ (with trappings).

As the initial version, the CMDQV driver only supports ACPI configurations.

Change-Id: Ib51b81f62fa115ce20dc542715493506d4d7f557
Signed-off-by: Nate Watterson <nwatterson@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Co-developed-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/dce50490b2c10b7254fb36aa73ed7ffd812b283a.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435435
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Ashish Mhetre
8eac48a1ba iommu/arm-smmu-v3: Add struct arm_smmu_impl_ops
Mimicing the arm-smmu (v2) driver, introduce a struct arm_smmu_impl_ops to
accommodate impl routines.

Suggested-by: Will Deacon <will@kernel.org>
Change-Id: I9d5d5e1cf32dcd6c282dc217903d6c424d1417cb
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/8fe9f3805568aabf771fc6706c116459016bf62d.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435434
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Ashish Mhetre
3f925f4d6b iommu/arm-smmu-v3: Add acpi_smmu_iort_probe_model for impl
For model-specific implementation, repurpose the acpi_smmu_get_options()
to a wider acpi_smmu_acpi_probe_model(). A new model can add to the list
in this new function.

Suggested-by: Will Deacon <will@kernel.org>
Change-Id: I6a9912f55cde32a78da1203f1abffc50e59c1438
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/79716299829aeab2e55b8c7932f2634b209bb4d5.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435433
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:52 +00:00
Nicolin Chen
5903705a33 iommu/arm-smmu-v3: Add ARM_SMMU_OPT_TEGRA241_CMDQV
The CMDQV extension in NVIDIA Tegra241 SoC only supports CS_NONE in the
CS field of CMD_SYNC. Add a new SMMU option to accommodate that.

Suggested-by: Will Deacon <will@kernel.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I8767faf8b780474c93c78305e89bd51308f45369
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/a3cb9bb2429fbae4a59f7ef517614d226763d717.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435432
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Ashish Mhetre
bf155e5de1 iommu/arm-smmu-v3: Make symbols public for CONFIG_TEGRA241_CMDQV
The symbols __arm_smmu_cmdq_skip_err(), arm_smmu_init_one_queue(), and
arm_smmu_cmdq_init() need to be used by the tegra241-cmdqv compilation
unit in a following patch.

Remove the static and put prototypes in the header.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I7b66675548ca8f3ed475d4fdd021d473feac07f2
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/c4f2aa5f5f40a2e7c68b132c6d3171d6403de57a.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435431
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Nicolin Chen
af82bcacd5 iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_init
So that this function can be used by other cmdqs than &smmu->cmdq only.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I7eb1698fe46941ed326f0c0a11ad28d6ecddddd8
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/e11a3c0bde172c9652c2946f12bc2ceed4c3a355.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435430
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:52 +00:00
Nicolin Chen
d6b2a909b9 iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_build_sync_cmd
The CMDQV extension on NVIDIA Tegra241 SoC only supports CS_NONE in the
CS field of CMD_SYNC, v.s. standard SMMU CMDQ. Pass in the cmdq pointer
directly, so the function can identify a different cmdq implementation.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I3f29960566b7fb75a76046b70ef55594ffbd0e56
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/723288287997b6dfbcd2a904d2c11e9b23f82250.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435429
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
2025-11-20 10:22:52 +00:00
Ashish Mhetre
7a77903641 iommu/arm-smmu-v3: Issue a batch of commands to the same cmdq
The driver calls in different places the arm_smmu_get_cmdq() helper, and
it's fine to do so since the helper always returns the single SMMU CMDQ.
However, with NVIDIA CMDQV extension or SMMU ECMDQ, there can be multiple
cmdqs in the system to select one from. And either case requires a batch
of commands to be issued to the same cmdq. Thus, a cmdq has to be decided
in the higher-level callers.

Add a cmdq pointer in arm_smmu_cmdq_batch structure, and decide the cmdq
when initializing the batch. Pass its pointer down to the bottom function.
Update __arm_smmu_cmdq_issue_cmd() accordingly for single command issuers.

Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Change-Id: I22baeeb40b0e1d58b63c85246c7033397be7811b
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/2cbf5ddefb6ea611e48d67c642271bd24421eb21.1724970714.git.nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435428
Reviewed-by: Pritesh Raithatha <praithatha@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-20 10:22:52 +00:00
Sheetal
4821cdbb60 dmaengine: tegra-adma: Fix use-after-free
A use-after-free bug exists in the Tegra ADMA driver when audio streams
are terminated, particularly during XRUN conditions. The issue occurs
when the DMA buffer is freed by tegra_adma_terminate_all() before the
vchan completion tasklet finishes accessing it.

The race condition follows this sequence:

  1. DMA transfer completes, triggering an interrupt that schedules the
     completion tasklet (tasklet has not executed yet)
  2. Audio playback stops, calling tegra_adma_terminate_all() which
     frees the DMA buffer memory via kfree()
  3. The scheduled tasklet finally executes, calling vchan_complete()
     which attempts to access the already-freed memory

Since tasklets can execute at any time after being scheduled, there is
no guarantee that the buffer will remain valid when vchan_complete()
runs.

Fix this by properly synchronizing the virtual channel completion:
 - Calling vchan_terminate_vdesc() in tegra_adma_stop() to mark the
   descriptors as terminated instead of freeing the descriptor.
 - Add the callback tegra_adma_synchronize() that calls
   vchan_synchronize() which kills any pending tasklets and frees any
   terminated descriptors.

Crash logs:
[  337.427523] BUG: KASAN: use-after-free in vchan_complete+0x124/0x3b0
[  337.427544] Read of size 8 at addr ffff000132055428 by task swapper/0/0

[  337.427562] Call trace:
[  337.427564]  dump_backtrace+0x0/0x320
[  337.427571]  show_stack+0x20/0x30
[  337.427575]  dump_stack_lvl+0x68/0x84
[  337.427584]  print_address_description.constprop.0+0x74/0x2b8
[  337.427590]  kasan_report+0x1f4/0x210
[  337.427598]  __asan_load8+0xa0/0xd0
[  337.427603]  vchan_complete+0x124/0x3b0
[  337.427609]  tasklet_action_common.constprop.0+0x190/0x1d0
[  337.427617]  tasklet_action+0x30/0x40
[  337.427623]  __do_softirq+0x1a0/0x5c4
[  337.427628]  irq_exit+0x110/0x140
[  337.427633]  handle_domain_irq+0xa4/0xe0
[  337.427640]  gic_handle_irq+0x64/0x160
[  337.427644]  call_on_irq_stack+0x20/0x4c
[  337.427649]  do_interrupt_handler+0x7c/0x90
[  337.427654]  el1_interrupt+0x30/0x80
[  337.427659]  el1h_64_irq_handler+0x18/0x30
[  337.427663]  el1h_64_irq+0x7c/0x80
[  337.427667]  cpuidle_enter_state+0xe4/0x540
[  337.427674]  cpuidle_enter+0x54/0x80
[  337.427679]  do_idle+0x2e0/0x380
[  337.427685]  cpu_startup_entry+0x2c/0x70
[  337.427690]  rest_init+0x114/0x130
[  337.427695]  arch_call_rest_init+0x18/0x24
[  337.427702]  start_kernel+0x380/0x3b4
[  337.427706]  __primary_switched+0xc0/0xc8

Fixes: f46b195799 ("dmaengine: tegra-adma: Add support for Tegra210 ADMA")

Bug 5638368
Bug 4879633

Change-Id: I2ea7f38ef7041db2fac63d1e62ecdef04fb929de
Signed-off-by: sheetal <sheetal@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3482483
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Sameer Pujar <spujar@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Mohan kumar <mkumard@nvidia.com>
2025-11-13 00:13:25 -08:00
Sumit Gupta
e47f83da16 soc/tegra: cbb: set ERD bit during resume for error interrupt
During resume, set ERD bit to mask SError and generate interrupt
to report error.

Change-Id: I3e88b8ca4f1cfb59d90be07e321931b4ee9a4a01
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-jammy/+/2948752
(cherry picked from commit c1498a4761d3a77fcae29b9ee1f17340957ebed6)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-jammy/+/2950204
(cherry picked from commit 86755e5c0f8d2560094452c0c79aedb2b06ff95a)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3485551
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-10 22:11:58 -08:00
Hiteshkumar Patel
a384bb60f7 NVIDIA: SAUCE: tegra-epl: update epl mailbox timeout
There is intermittent issue observed that pm-notify call is failed
if we call it immediately after handshake. The issue is very random.
So we updated mailbox timeout as 13 miliseconds, which solve the
issue.

Bug 4774805

Change-Id: Ia43b418b904dcb23a526af1045fd12946165c285
Signed-off-by: Hiteshkumar Patel <hiteshkumarg@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3483089
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
2025-11-06 00:57:38 -08:00
Hiteshkumar Patel
cc660d12b9 NVIDIA: SAUCE: mailbox: tegra-hsp: Do not clear interrupt routing at probe
HSP interrupts are shared across firmware & OS. Currently, if a
firmware configures interrupt routing before kernel boots,
the tegra-hsp driver can clear that routing if the same shared
interrupt is assigned to Linux. This disables the interrupts for
that firmware.

Bug 4774805

Change-Id: Icd819a31398ab53f8e915b9b2ef1ffd234972eb3
Signed-off-by: Hiteshkumar Patel <hiteshkumarg@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3483088
Reviewed-by: Kartik Rajput <kkartik@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-06 00:57:33 -08:00
Wayne Chang
fc8056e062 NVIDIA: SAUCE: usb: host: tegra: Remove manual wake IRQ disposal
We found that calling irq_dispose_mapping() caused a kernel warning
when removing the driver. The IRQs are obtained using
platform_get_irq(), which returns a Linux virtual IRQ number directly
managed by the device core, not by the OF subsystem. Therefore, the
driver should not call irq_dispose_mapping() for these IRQs.

Bug 5421820

Change-Id: I44bb119def6d85b85fe06e1c0fc6771b63d35fd8
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3479600
Reviewed-by: Henry Lin <henryl@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-by: WK Tsai <wtsai@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-11-03 01:12:07 -08:00
Johnny Liu
9340df62f6 NVIDIA: SAUCE: clk: tegra: make nocache for clocks
Clock scaling for specific clocks, such as EMC/SCF/MCF/SMMU clocks
, are managed by the BPMP itself without kernel involvement.

When the clock is updated on BPMP side, and kernel consumer drivers
try to use clk_get_rate to fetch the current clock rate, CCF framework
will return the cached version of the clock rate instead of the correct
one being scaled by the BPMP itself.

This change makes all the clk_get_rate requests "CLK_GET_RATE_NOCACHE"
to force through the requests to BPMP and get the current clock rate.

Bug 5621554

Change-Id: Ib31aa00810aef57cd50cd7aea0b54752a78462d6
Signed-off-by: Johnny Liu <johnliu@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3479075
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-10-30 02:42:17 -07:00
Petlozu Pravareshwar
f26de42a86 soc/tegra: pmc: Add sysfs nodes to select boot chain
Add sysfs nodes to select A/B boot chain so that BootROM can select
the right boot path on warm boot.

Bug 4510385
Bug 5310687

Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Change-Id: Idb9534ea533cd88094b6835a28f3b7141e954c29
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3456564
(cherry picked from commit cbc5d03fb9fe9790586b3ace4ebb98078e17cce3)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3468493
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
2025-10-22 21:42:39 -07:00
Wayne Chang
62eaf46abc NVIDIA: SAUCE: usb: xhci: tegra: Use platform_get_irq_optional() for wake IRQs
When some wake IRQs are disabled in the device tree, the corresponding
interrupt entries are removed from DT. In such cases, the driver
currently calls platform_get_irq(), which returns -ENXIO and logs
an error like:

  tegra-xusb a80aa10000.usb: error -ENXIO: IRQ index 9 not found

However, not all wake IRQs are mandatory. the hardware can operate
normally even if some wake sources are not defined in DT. To avoid this
false alarm and allow missing wake IRQs gracefully, use
platform_get_irq_optional() instead of platform_get_irq().

Bug 5421820

Change-Id: Ib03b4edb933d481983ec8b9f752ab26004ba4784
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3470345
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-17 11:43:37 -07:00
Wayne Chang
bfe324d0df NVIDIA: SAUCE: xhci: Enable USB wakeup function for Tegra264
Enable USB wakeup function for Tegra264

Bug 5421820

Change-Id: Ic3dba730b5de5e59ade67cee9b3bfb3a857b10f6
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469951
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-17 11:43:32 -07:00
Haotien Hsu
434b01fc8e usb: xhci: tegra: Support USB wakeup function for Tegra234
When the system is suspended, USB hot-plugging/unplugging can trigger
wake events of the Tegra USB host controller.
Enable support for USB wake-up events by parsing device-tree to see if
the interrupts for the wake-up events are present and if so configure
those interrupts. Note that if wake-up events are not present, still
allow the USB host controller to probe as normal.

Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
Link: https://lore.kernel.org/r/20250811074558.1062048-5-haotienh@nvidia.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 5df186e2ef11dca3fb6f0f332dc09c4ac0bed870)

Bug 5421820

Change-Id: Ib2f90473625e4aff45b7f333925581ceed3a29c2
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469950
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-17 11:43:27 -07:00
Wayne Chang
147bb0d836 Revert "NVIDIA: SAUCE: WAR: soc: tegra: pmc: Disable wakeup capability of USB3 port2"
This reverts commit 6957293414.

Reason for revert: The change conflicts with the cherry-picked upstream
updates. Disabling the wakeup capability in the device tree instead.

Bug 5421820

Change-Id: I39877389385d2e880c53d483272d8dc139183158
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469949
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-10-17 11:43:22 -07:00
Wayne Chang
72e7ef8a21 Revert "NVIDIA: SAUCE: xhci: tegra: Add remote wakeup support"
This reverts commit 0d675e2670.

Reason for revert: Replaced with a proper fix

Bug 5421820

Change-Id: I3bf60e959f639872d4dec5610bca6592ed7b7e02
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469948
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-10-17 11:43:16 -07:00
Wayne Chang
2ee17fb833 Revert "NVIDIA: SAUCE: xhci: tegra: Enable USB wake-up for Tegra234"
This reverts commit a190f81809.

Reason for revert: replaced with a proper fix

Bug 5421820

Change-Id: I4646e380a3dde9dc67a4f6efed8140fd2bdcde24
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469947
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-10-17 11:43:11 -07:00
Wayne Chang
8bfd2f594b Revert "NVIDIA: SAUCE: xhci: tegra: Fix irq_dispose_mapping() warning"
This reverts commit ef4c7b0620.

Reason for revert: replaced with a proper fix

Bug 5421820

Change-Id: I447e08bc8aedb0a5819aaf4feae05ded4082375c
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469946
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-17 11:43:05 -07:00
Brad Griffis
3c68f26fcb NVIDIA: SAUCE: arm64: defconfig: build EFI test module
In order to run the tests from fwts you need this option:

CONFIG_EFI_TEST=m

Bug 5490446

Change-Id: I9b05236d2b1859fba02392745907f0db6749f51a
Signed-off-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3467905
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2025-10-14 09:57:44 -07:00
Jon Hunter
29d6e78f9b NVIDIA: SAUCE: memory: Remove unused variable
The variable 'intstat' is not used and so remove this.

Bug 4655916

Change-Id: Iceceffef423e3fbec3f835c9879fd11778746956
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3465067
(cherry picked from commit 1a819646504fe5788e348025d350a67bf478514e)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469210
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-14 07:43:15 -07:00
Jon Hunter
e1c5eb7217 NVIDIA: SAUCE: cpufreq: tegra194: Remove unused variable
The variable 'maxcpus_per_clock' is no longer used and so remove this to
fix a unused variable compiler warning.

Bug 5213905

Change-Id: I92aa24c6cece6f2a620982102c3fbce9ab18e6c7
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3465057
(cherry picked from commit 9ec1570750b14ac2590484906c9e66d1b125b57b)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469209
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
2025-10-14 07:43:10 -07:00
Jon Hunter
5337e233d5 NVIDIA: SAUCE: net: phy: realtek: Fix RTL8211F suspend
When integrating WoL support for the RTL8211F device, a duplicated
suspend entry was added for this device. The difference between the
rtl821x_suspend() and the genphy_suspend() functions is that the
rtl821x_suspend() will only call genphy_suspend() and disable clocks
if WoL is not enabled. Therefore, remove the duplicated suspend entry
that calls genphy_suspend() to ensure the PHY is not suspend if WoL is
enabled.

Bug 200562291

Change-Id: I9d5be01d46ee63caba9c6817b910283bf2fac69c
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3464990
(cherry picked from commit ca29769be9997f8196e0a2209e40a134c344807a)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3469208
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
2025-10-14 07:43:06 -07:00
Santosh Reddy Galma
2b7ff9fb2c NVIDIA: SAUCE: arm64: enable DRM_SIMPLEDRM config
Enable CONFIG_DRM_SIMPLEDRM for the simpledrm driver
required for display handoff from BL to kernel and
show kernel boot up logs on display instead of
CONFIG_FB_SIMPLE used for simplefb

Bug 5371131
Bug 5576705

Change-Id: I3af469b4044d5900bfa71eea51d94b21ebff3684
Signed-off-by: Santosh Reddy Galma <galmar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3414764
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Lennox Wu <lennoxw@nvidia.com>
2025-10-14 07:42:21 -07:00
Johnny Liu
67ad13e13f pwm: tegra: Fix types of input arguments
Given the structure of pwm_state

    struct pwm_state {
        u64 period;
        u64 duty_cycle;
	...
    };

Correct the types of input arguments of tegra_pwm_config to avoid loss
of precision or quantization error.

Bug 5308986

Signed-off-by: Johnny Liu <johnliu@nvidia.com>
Signed-off-by: Robert Lin <robelin@nvidia.com>
Change-Id: I3362bda20b9a96476176fbfede5f87d7a125ad7e
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3455750
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
2025-10-14 00:58:51 -07:00
Jon Hunter
392f036ad4 NVIDIA: SAUCE: soc/tegra: pmc: Initialise mutex
In Linux v6.15, commit 337369f8ce9e ("locking/mutex: Add MUTEX_WARN_ON()
into fast path") added a WARN to detect incorrectly initialised mutexes
and since this commit the following warning has been observed for
Tegra264 platforms ...

 DEBUG_LOCKS_WARN_ON(lock->magic != lock)
 WARNING: CPU: 1 PID: 168 at kernel/locking/mutex.c:155
  mutex_lock+0xd8/0xe8
 ...
 Call trace:
  mutex_lock+0xd8/0xe8 (P)
  tegra_io_pad_pinconf_set+0xf4/0x308
  pinconf_apply_setting+0xa8/0x140
  pinctrl_commit_state+0x104/0x2b0
  pinctrl_select_state+0x24/0x50
  tegra_sdhci_set_padctrl+0x98/0x258
  sdhci_tegra_start_signal_voltage_switch+0x84/0x170
  mmc_set_initial_signal_voltage+0x40/0x1a0
  mmc_power_up.part.0+0x60/0x190
  mmc_start_host+0xb8/0xd0
  mmc_add_host+0x8c/0x110
  __sdhci_add_host+0x23c/0x390
  sdhci_add_host+0x4c/0x68
  sdhci_tegra_probe+0x800/0xba8
  platform_probe+0x70/0x118
  really_probe+0xd4/0x3e0
  __driver_probe_device+0x90/0x1a8
  driver_probe_device+0x44/0x170
  __device_attach_driver+0xf0/0x1e8
  bus_for_each_drv+0x90/0x110
  __device_attach_async_helper+0xc4/0x128
  async_run_entry_fn+0x40/0x190
  process_one_work+0x180/0x448
  worker_thread+0x274/0x390
  kthread+0x158/0x260
  ret_from_fork+0x10/0x20

Reviewing the Tegra264 PMC driver code, the mutex for the powergates is
never initialised and so fix this by ensuring the mutex is initialised
correctly during probe.

Bug 4470933
Bug 5344712

Change-Id: Icf4b1a03af342334918ed707faaf2565d215e94e
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3465120
(cherry picked from commit 1a9032659680530998cc353699c0ca64b176b4d2)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3468705
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-13 12:57:00 -07:00
Eric Funsten
14898fb272 NVIDIA: SAUCE: perf: arm_cspmu: NVIDIA T264 PMU leakage workaround
The NVIDIA Tegra T264 SOC has a HW issue where events captured on a
prior experiment can corrupt the current experiment. This adds a
workaround which involves the following steps:
1. First experiment ends; Disable PMCR.E as we do normally
2. Clear PMCNTEN for all counters
3. Enable PMCR.E
4. Disable PMCR.E
5. Enable back PMCNTEN for counters cleared in step 2

Bug 5524939

Change-Id: Ie5885b9bb9495aa0cfb1844a88cbdc7e0509ce67
Signed-off-by: Eric Funsten <efunsten@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3459618
Reviewed-by: Besar Wicaksono <bwicaksono@nvidia.com>
Tested-by: Ryan Bissell <rbissell@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-09-30 04:15:35 -07:00
Besar Wicaksono
bb1aeb903a NVIDIA: SAUCE: perf: arm_cspmu: nvidia: add T264 support
Adds PMU support for the following IPs in NVIDIA Tegra T264 SOC:
- Unified Coherency Fabric (UCF)
- Vision
- Display
- High-speed IO
- UCF GPU

Bug 5524939

Change-Id: I595dc746e3b45b9f40c5f4343212c37f42f0faa1
Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3459617
Tested-by: Ryan Bissell <rbissell@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
2025-09-30 04:15:30 -07:00
Brad Griffis
b837fb9d0d NVIDIA: SAUCE: arm64: defconfig: more Calico modules
Configure the following in order to get Calico operational:

CONFIG_NET_IPIP=m
CONFIG_IP_SET_MAX=512
CONFIG_IP_SET_BITMAP_IP=m
CONFIG_IP_SET_BITMAP_IPMAC=m
CONFIG_IP_SET_BITMAP_PORT=m
CONFIG_IP_SET_HASH_IP=m
CONFIG_IP_SET_HASH_IPMARK=m
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
CONFIG_IP_SET_HASH_NETNET=m
CONFIG_IP_SET_HASH_NETPORT=m
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m

Bug 5438065

Change-Id: I35d5510f9a572baf66944ad3489d1e050499d25e
Signed-off-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3449304
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Tested-by: Yongchang Liu <yongchangl@nvidia.com>
Reviewed-by: Yongchang Liu <yongchangl@nvidia.com>
2025-09-26 21:28:53 -07:00
Rahul Bedarkar
f29136b334 NVIDIA: SAUCE: tegra-epl: add plausibility checks and improve error handling
Add comprehensive plausibility checks and validation throughout the EPL driver:

- Add timestamp validation with configurable resolution for TEGRA234/TEGRA264
- Implement timestamp overflow handling and range validation (90ms limit)
- Add input parameter validation in device file ioctl operations
- Improve error handling in HSP mailbox communication with proper channel validation
- Add register mapping validation before write operations
- Enhance power management state handling with proper error checking
- Replace generic print statements with device-specific debug messages
- Add proper error handling in probe, suspend, resume, and shutdown functions
- Improve handshake retry logic with better error reporting
- Add validation for state parameters in PM notification functions

The changes improve driver robustness by adding proper validation
for all critical operations and providing better error reporting
for debugging purposes.

Bug 5320023

Change-Id: I8095416921ec8d229e2cdab47d1b3c3e50fa1bbf
Signed-off-by: Rahul Bedarkar <rabedarkar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3437098
(cherry picked from commit a7415c43437d37f2732d334cfe90ef2f4c7d7575)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3459572
Tested-by: Hiteshkumar Patel <hiteshkumarg@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Dipen Patel <dipenp@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
2025-09-26 19:27:52 -07:00
Shubham Jain
a385828614 NVIDIA: SAUCE: tegra-epl: allow tegra-epl to be built as module
- updating kconfig file to allow tegra-epl to
  be built as module.
- Updating misc ec sw generic error index offset
  depeding on Orin or Thor chip.

Bug 5142445
Bug 5119438
Bug 5405209
Bug 5415787

Change-Id: Iea589710e1a90856550623543f9ac342854c2a2c
Signed-off-by: Shubham Jain <shubhamj@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3430974
(cherry picked from commit 2a56160a9c270b5b411a88f9e79865e5442581d3)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3459571
Tested-by: Hiteshkumar Patel <hiteshkumarg@nvidia.com>
Reviewed-by: Dipen Patel <dipenp@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-09-26 19:27:47 -07:00
Rahul Bedarkar
d684657795 NVIDIA: SAUCE: tegra-epl: Map mission status reg if only required
Mission status register is only required if either of the MISC EC
registers is mapped.

Bug 5100266
Bug 5415787
Bug 5405209

Change-Id: Idf9c64050d3d106ac1b78b7c1e0d64257f9195d3
Signed-off-by: Rahul Bedarkar <rabedarkar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3430973
(cherry picked from commit c28c7e50c460912f60a1b4e265d37a88fc522136)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3459570
Tested-by: Hiteshkumar Patel <hiteshkumarg@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-by: Dipen Patel <dipenp@nvidia.com>
2025-09-26 19:27:42 -07:00
Petlozu Pravareshwar
072b8487aa NVIDIA: SAUCE: soc/tegra: pmc: Remove reset status sysfs nodes
Reset status related sysfs nodes are no longer supported in T264
because of security reasons. Change accordingly deletes these
sysfs nodes.

Bug 5245235

Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Change-Id: Ia96872c083d23ca7f3bfc774ca9f614bcb3c63bd
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3419584
(cherry picked from commit 25870226d1aef86ea221d10f6376cc694122d196)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3446575
Tested-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
2025-09-04 12:56:56 -07:00
Kartik Rajput
094a99a56d NVIDIA: SAUCE: serial: amba-pl011: Do not use IBRD
Tegra UART controllers does not support FBRD register, which cause it to
not support various standard baudrates that HW supports.

Use clk_set_rate to program UART clock rate instead.

Bug 5406304

Change-Id: I6fcf14b0186e54a6f418287791d80e12d17600a0
Signed-off-by: Kartik Rajput <kkartik@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3441884
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
(cherry picked from commit daa72589899560fb3570b165163bd8e35cf704e1)
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3445145
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-09-04 01:56:57 -07:00
Oleg Nesterov
6768ecea00 posix-cpu-timers: fix race between handle_posix_cpu_timers() and posix_cpu_timer_del()
If an exiting non-autoreaping task has already passed exit_notify() and
calls handle_posix_cpu_timers() from IRQ, it can be reaped by its parent
or debugger right after unlock_task_sighand().

If a concurrent posix_cpu_timer_del() runs at that moment, it won't be
able to detect timer->it.cpu.firing != 0: cpu_timer_task_rcu() and/or
lock_task_sighand() will fail.

Add the tsk->exit_state check into run_posix_cpu_timers() to fix this.

This fix is not needed if CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, because
exit_task_work() is called before exit_notify(). But the check still
makes sense, task_work_add(&tsk->posix_cputimers_work.work) will fail
anyway in this case.

Cc: stable@vger.kernel.org
Reported-by: Benoît Sevens <bsevens@google.com>
Fixes: 0bdd2ed413 ("sched: run_posix_cpu_timers: Don't check ->exit_state, use lock_task_sighand()")
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(cherry picked from commit f90fff1e152dedf52b932240ebbd670d83330eca)

Bug 5341153

CVE-2025-38352
Change-Id: I2146869cc8f9684d4e4d56eaa247f54ed0225e1e
Signed-off-by: Brad Griffis <bgriffis@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/3rdparty/canonical/linux-noble/+/3435903
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Paritosh Dixit <paritoshd@nvidia.com>
2025-08-20 09:12:13 -07:00
26 changed files with 2118 additions and 306 deletions

View File

@@ -10,6 +10,11 @@ metrics like memory bandwidth, latency, and utilization:
* NVLink-C2C1
* CNVLink
* PCIE
* Unified Coherency Fabric (UCF)
* Vision
* Display
* High-speed IO
* UCF-GPU
PMU Driver
----------
@@ -183,6 +188,159 @@ Example usage:
perf stat -a -e nvidia_pcie_pmu_1/event=0x0,root_port=0x3/
UCF PMU
-------
The UCF PMU monitors system level cache events and DRAM traffic that flows
through UCF.
The events and configuration options of this PMU device are described in sysfs,
see /sys/bus/event_sources/devices/nvidia_ucf_pmu_<socket-id>.
User can configure the PMU to capture events from specific source and destination.
The source/destination filter is described in
/sys/bus/event_sources/devices/nvidia_ucf_pmu_<socket-id>/format/. By default
traffic from all sources and destinations will be captured if no source/destination
is specified.
Example usage:
* Count event id 0x0 from any source/destination of socket 0::
perf stat -a -e nvidia_ucf_pmu_0/event=0x0/
* Count event id 0x1 from socket 0's CPUs to socket 0's DRAM::
perf stat -a -e nvidia_ucf_pmu_0/event=0x1,src_loc_cpu=0x1,dst_loc=0x1/
* Count event id 0x1 from remote source of socket 0 to local and remote DRAM::
perf stat -a -e nvidia_ucf_pmu_0/event=0x1,src_rem=0x1,dst_loc=0x1,dst_rem=0x1/
* Count event id 0x2 from any source/destination of socket 1::
perf stat -a -e nvidia_ucf_pmu_1/event=0x2/
* Count event id 0x3 from socket 1's CPUs to socket 1's DRAM::
perf stat -a -e nvidia_ucf_pmu_1/event=0x3,src_loc_cpu=0x1,dst_loc=0x1/
Vision PMU
------------
The vision PMU monitors memory traffic from the multimedia IPs in the SOC.
The events and configuration options of this PMU device are described in sysfs,
see /sys/bus/event_sources/devices/nvidia_vision_pmu_<socket-id>.
User can configure the PMU to capture events from specific IPs.
/sys/bus/event_sources/devices/nvidia_vision_pmu_<socket-id>/format/ contains
the filter attribute name of each multimedia IP. This filter attribute is a
bitmask to select the AXI/hub interface of the IP to monitor. By default traffic
from all interfaces of all IPs will be captured if no IPs are specified.
Example usage:
* Count event id 0x0 from all multimedia IPs in socket 0::
perf stat -a -e nvidia_vision_pmu_0/event=0x0/
* Count event id 0x1 from AXI/hub interface 0 in VI-0 of socket 0::
perf stat -a -e nvidia_vision_pmu_0/event=0x1,vi_0=0x1/
* Count event id 0x1 from AXI/hub interface 0 and 1 in VI-0 of socket 0::
perf stat -a -e nvidia_vision_pmu_0/event=0x1,vi_0=0x3/
* Count event id 0x2 from all multimedia IPs in socket 1::
perf stat -a -e nvidia_vision_pmu_1/event=0x2/
* Count event id 0x3 from AXI/hub interface 0 in VI-0 and PVA of socket 1::
perf stat -a -e nvidia_vision_pmu_1/event=0x3,vi_0=0x1,pva=0x1/
Display PMU
------------
The display PMU monitors memory traffic from the display IP in the SOC.
The events and configuration options of this PMU device are described in sysfs,
see /sys/bus/event_sources/devices/nvidia_display_pmu_<socket-id>.
Example usage:
* Count event id 0x0 in socket 0::
perf stat -a -e nvidia_display_pmu_0/event=0x0/
* Count event id 0x0 in socket 1::
perf stat -a -e nvidia_display_pmu_1/event=0x0/
High-speed I/O PMU
-------------------
The high-speed I/O PMU monitors memory traffic from the high speed I/O devices
in the SOC.
The events and configuration options of this PMU device are described in sysfs,
see /sys/bus/event_sources/devices/nvidia_uphy_pmu_<socket-id>.
User can configure the PMU to capture events from specific I/Os.
/sys/bus/event_sources/devices/nvidia_uphy_pmu_<socket-id>/format/ contains
the filter attribute name of each I/O. This filter attribute is a
bitmask to select the AXI/hub interface of the I/O to monitor. By default
traffic from all interfaces of all I/Os will be captured if no I/Os are
specified.
Example usage:
* Count event id 0x0 from all I/Os in socket 0::
perf stat -a -e nvidia_uphy_pmu_0/event=0x0/
* Count event id 0x1 from PCIE Root Port 1 of socket 0::
perf stat -a -e nvidia_uphy_pmu_0/event=0x1,pcie_rp_1=0x1/
* Count event id 0x1 from PCIE Root Port 1 and Root Port 2 of socket 0::
perf stat -a -e nvidia_uphy_pmu_0/event=0x1,pcie_rp_1=0x1,pcie_rp_2=0x1/
* Count event id 0x2 from all IPs in socket 1::
perf stat -a -e nvidia_uphy_pmu_1/event=0x2/
* Count event id 0x3 from PCIE Root Port 3 and UFS of socket 1::
perf stat -a -e nvidia_uphy_pmu_1/event=0x1,pcie_rp_3=0x1,ufs=0x1/
UCF-GPU PMU
------------
The UCF-GPU PMU monitors integrated GPU physical address traffic flowing through
UCF.
The events and configuration options of this PMU device are described in sysfs,
see /sys/bus/event_sources/devices/nvidia_ucf_gpu_pmu_<socket-id>.
Example usage:
* Count event id 0x0 in socket 0::
perf stat -a -e nvidia_ucf_gpu_pmu_0/event=0x0/
* Count event id 0x0 in socket 1::
perf stat -a -e nvidia_ucf_gpu_pmu_1/event=0x0/
.. _NVIDIA_Uncore_PMU_Traffic_Coverage_Section:
Traffic Coverage

View File

@@ -21625,6 +21625,7 @@ M: Thierry Reding <thierry.reding@gmail.com>
R: Krishna Reddy <vdumpa@nvidia.com>
L: linux-tegra@vger.kernel.org
S: Supported
F: drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
F: drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c
F: drivers/iommu/tegra*

View File

@@ -151,6 +151,7 @@ CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_NET_IPIP=m
CONFIG_SYN_COOKIES=y
CONFIG_IPV6=m
CONFIG_NETFILTER=y
@@ -181,6 +182,23 @@ CONFIG_NETFILTER_XT_MATCH_RECENT=m
CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
CONFIG_NETFILTER_XT_MATCH_U32=m
CONFIG_IP_SET=m
CONFIG_IP_SET_MAX=512
CONFIG_IP_SET_BITMAP_IP=m
CONFIG_IP_SET_BITMAP_IPMAC=m
CONFIG_IP_SET_BITMAP_PORT=m
CONFIG_IP_SET_HASH_IP=m
CONFIG_IP_SET_HASH_IPMARK=m
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
CONFIG_IP_SET_HASH_NETNET=m
CONFIG_IP_SET_HASH_NETPORT=m
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_IP_VS=m
CONFIG_NF_TABLES_ARP=y
CONFIG_IP_NF_IPTABLES=m
@@ -304,6 +322,7 @@ CONFIG_MTK_ADSP_IPC=m
CONFIG_ARM_FFA_TRANSPORT=y
# CONFIG_EFI_VARS_PSTORE is not set
CONFIG_EFI_CAPSULE_LOADER=y
CONFIG_EFI_TEST=m
CONFIG_IMX_SCU=y
CONFIG_QCOM_QSEECOM=y
CONFIG_QCOM_QSEECOM_UEFISECAPP=y
@@ -915,7 +934,7 @@ CONFIG_VIDEO_HANTRO=m
CONFIG_VIDEO_IMX412=m
CONFIG_VIDEO_OV5640=m
CONFIG_VIDEO_OV5645=m
CONFIG_DRM=m
CONFIG_DRM=y
CONFIG_DRM_I2C_NXP_TDA998X=m
CONFIG_DRM_HDLCD=m
CONFIG_DRM_MALI_DISPLAY=m
@@ -980,6 +999,7 @@ CONFIG_DRM_MEDIATEK_HDMI=m
CONFIG_DRM_MXSFB=m
CONFIG_DRM_IMX_LCDIF=m
CONFIG_DRM_MESON=m
CONFIG_DRM_SIMPLEDRM=y
CONFIG_DRM_PL111=m
CONFIG_DRM_LIMA=m
CONFIG_DRM_PANFROST=m
@@ -987,7 +1007,6 @@ CONFIG_DRM_TIDSS=m
CONFIG_DRM_POWERVR=m
CONFIG_FB=y
CONFIG_FB_EFI=y
CONFIG_FB_SIMPLE=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_PWM=m
@@ -1406,6 +1425,7 @@ CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_ARM_SMMU_V3_SVA=y
CONFIG_TEGRA241_CMDQV=y
CONFIG_MTK_IOMMU=y
CONFIG_QCOM_IOMMU=y
CONFIG_REMOTEPROC=y

View File

@@ -483,6 +483,7 @@ CONFIG_PLATFORM_MHU=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_TEGRA241_CMDQV=y
CONFIG_MTK_IOMMU=y
CONFIG_REMOTEPROC=y
CONFIG_MTK_PMIC_WRAP=y

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016-2022 NVIDIA Corporation
* Copyright (C) 2016-2025 NVIDIA Corporation
*/
#include <linux/clk-provider.h>
@@ -584,6 +584,7 @@ tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
}
init.parent_names = parents;
init.flags = CLK_GET_RATE_NOCACHE;
err = devm_clk_hw_register(bpmp->dev, &clk->hw);

View File

@@ -615,8 +615,6 @@ static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
{
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
int maxcpus_per_clock = data->soc->maxcpus_per_cluster *
data->soc->clusters_per_clk;
u32 clusterid = data->cpu_data[policy->cpu].clusterid;
struct cpufreq_frequency_table *freq_table;
struct cpufreq_frequency_table *bpmp_lut;

View File

@@ -420,10 +420,17 @@ static void tegra_adma_stop(struct tegra_adma_chan *tdc)
return;
}
kfree(tdc->desc);
vchan_terminate_vdesc(&tdc->desc->vd);
tdc->desc = NULL;
}
static void tegra_adma_synchronize(struct dma_chan *dc)
{
struct tegra_adma_chan *tdc = to_tegra_adma_chan(dc);
vchan_synchronize(&tdc->vc);
}
static void tegra_adma_start(struct tegra_adma_chan *tdc)
{
struct virt_dma_desc *vd = vchan_next_desc(&tdc->vc);
@@ -1157,6 +1164,7 @@ static int tegra_adma_probe(struct platform_device *pdev)
tdma->dma_dev.device_config = tegra_adma_slave_config;
tdma->dma_dev.device_tx_status = tegra_adma_tx_status;
tdma->dma_dev.device_terminate_all = tegra_adma_terminate_all;
tdma->dma_dev.device_synchronize = tegra_adma_synchronize;
tdma->dma_dev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
tdma->dma_dev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
tdma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);

View File

@@ -406,6 +406,18 @@ config ARM_SMMU_V3_SVA
Say Y here if your system supports SVA extensions such as PCIe PASID
and PRI.
config TEGRA241_CMDQV
bool "NVIDIA Tegra241 CMDQ-V extension support for ARM SMMUv3"
depends on ARM_SMMU_V3
help
Support for NVIDIA CMDQ-Virtualization extension for ARM SMMUv3. The
CMDQ-V extension is similar to v3.3 ECMDQ for multi command queues
support, except with virtualization capabilities.
Say Y here if your system is NVIDIA Tegra241 (Grace) or it has the same
CMDQ-V extension.
config S390_IOMMU
def_bool y if S390 && PCI
depends on S390 && PCI

View File

@@ -2,4 +2,5 @@
obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o
arm_smmu_v3-objs-y += arm-smmu-v3.o
arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
arm_smmu_v3-objs-$(CONFIG_TEGRA241_CMDQV) += tegra241-cmdqv.o
arm_smmu_v3-objs := $(arm_smmu_v3-objs-y)

View File

@@ -2,7 +2,8 @@
/*
* IOMMU API for ARM architected SMMUv3 implementations.
*
* Copyright (C) 2015 ARM Limited
* SPDX-FileCopyrightText: Copyright (C) 2015 ARM Limited
* SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
*
* Author: Will Deacon <will.deacon@arm.com>
*
@@ -23,6 +24,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/pci.h>
#include <linux/pci-ats.h>
#include <linux/platform_device.h>
@@ -345,14 +347,30 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
return 0;
}
static struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu)
static struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_ent *ent)
{
return &smmu->cmdq;
struct arm_smmu_cmdq *cmdq = NULL;
if (smmu->impl_ops && smmu->impl_ops->get_secondary_cmdq)
cmdq = smmu->impl_ops->get_secondary_cmdq(smmu, ent);
return cmdq ?: &smmu->cmdq;
}
static bool arm_smmu_cmdq_needs_busy_polling(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq)
{
if (cmdq == &smmu->cmdq)
return false;
return smmu->options & ARM_SMMU_OPT_TEGRA241_CMDQV;
}
static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, u32 prod)
struct arm_smmu_cmdq *cmdq, u32 prod)
{
struct arm_smmu_queue *q = &cmdq->q;
struct arm_smmu_cmdq_ent ent = {
.opcode = CMDQ_OP_CMD_SYNC,
};
@@ -367,10 +385,12 @@ static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,
}
arm_smmu_cmdq_build_cmd(cmd, &ent);
if (arm_smmu_cmdq_needs_busy_polling(smmu, cmdq))
u64p_replace_bits(cmd, CMDQ_SYNC_0_CS_NONE, CMDQ_SYNC_0_CS);
}
static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q)
void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq)
{
static const char * const cerror_str[] = {
[CMDQ_ERR_CERROR_NONE_IDX] = "No error",
@@ -378,6 +398,7 @@ static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
[CMDQ_ERR_CERROR_ABT_IDX] = "Abort on command fetch",
[CMDQ_ERR_CERROR_ATC_INV_IDX] = "ATC invalidate timeout",
};
struct arm_smmu_queue *q = &cmdq->q;
int i;
u64 cmd[CMDQ_ENT_DWORDS];
@@ -420,13 +441,15 @@ static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
/* Convert the erroneous command into a CMD_SYNC */
arm_smmu_cmdq_build_cmd(cmd, &cmd_sync);
if (arm_smmu_cmdq_needs_busy_polling(smmu, cmdq))
u64p_replace_bits(cmd, CMDQ_SYNC_0_CS_NONE, CMDQ_SYNC_0_CS);
queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);
}
static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
{
__arm_smmu_cmdq_skip_err(smmu, &smmu->cmdq.q);
__arm_smmu_cmdq_skip_err(smmu, &smmu->cmdq);
}
/*
@@ -591,11 +614,11 @@ static void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq,
/* Wait for the command queue to become non-full */
static int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq,
struct arm_smmu_ll_queue *llq)
{
unsigned long flags;
struct arm_smmu_queue_poll qp;
struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
int ret = 0;
/*
@@ -626,11 +649,11 @@ static int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu,
* Must be called with the cmdq lock held in some capacity.
*/
static int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq,
struct arm_smmu_ll_queue *llq)
{
int ret = 0;
struct arm_smmu_queue_poll qp;
struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod));
queue_poll_init(smmu, &qp);
@@ -650,10 +673,10 @@ static int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu,
* Must be called with the cmdq lock held in some capacity.
*/
static int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq,
struct arm_smmu_ll_queue *llq)
{
struct arm_smmu_queue_poll qp;
struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
u32 prod = llq->prod;
int ret = 0;
@@ -700,12 +723,14 @@ static int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu,
}
static int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq,
struct arm_smmu_ll_queue *llq)
{
if (smmu->options & ARM_SMMU_OPT_MSIPOLL)
return __arm_smmu_cmdq_poll_until_msi(smmu, llq);
if (smmu->options & ARM_SMMU_OPT_MSIPOLL &&
!arm_smmu_cmdq_needs_busy_polling(smmu, cmdq))
return __arm_smmu_cmdq_poll_until_msi(smmu, cmdq, llq);
return __arm_smmu_cmdq_poll_until_consumed(smmu, llq);
return __arm_smmu_cmdq_poll_until_consumed(smmu, cmdq, llq);
}
static void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds,
@@ -742,13 +767,13 @@ static void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds,
* CPU will appear before any of the commands from the other CPU.
*/
static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq,
u64 *cmds, int n, bool sync)
{
u64 cmd_sync[CMDQ_ENT_DWORDS];
u32 prod;
unsigned long flags;
bool owner;
struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
struct arm_smmu_ll_queue llq, head;
int ret = 0;
@@ -762,7 +787,7 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
while (!queue_has_space(&llq, n + sync)) {
local_irq_restore(flags);
if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq))
if (arm_smmu_cmdq_poll_until_not_full(smmu, cmdq, &llq))
dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
local_irq_save(flags);
}
@@ -788,7 +813,7 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n);
if (sync) {
prod = queue_inc_prod_n(&llq, n);
arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, &cmdq->q, prod);
arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, cmdq, prod);
queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS);
/*
@@ -838,7 +863,7 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
/* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */
if (sync) {
llq.prod = queue_inc_prod_n(&llq, n);
ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq);
ret = arm_smmu_cmdq_poll_until_sync(smmu, cmdq, &llq);
if (ret) {
dev_err_ratelimited(smmu->dev,
"CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n",
@@ -873,7 +898,8 @@ static int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
return -EINVAL;
}
return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, sync);
return arm_smmu_cmdq_issue_cmdlist(
smmu, arm_smmu_get_cmdq(smmu, ent), cmd, 1, sync);
}
static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
@@ -888,21 +914,33 @@ static int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu,
return __arm_smmu_cmdq_issue_cmd(smmu, ent, true);
}
static void arm_smmu_cmdq_batch_init(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_batch *cmds,
struct arm_smmu_cmdq_ent *ent)
{
cmds->num = 0;
cmds->cmdq = arm_smmu_get_cmdq(smmu, ent);
}
static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_batch *cmds,
struct arm_smmu_cmdq_ent *cmd)
{
bool unsupported_cmd = !arm_smmu_cmdq_supports_cmd(cmds->cmdq, cmd);
bool force_sync = (cmds->num == CMDQ_BATCH_ENTRIES - 1) &&
(smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC);
int index;
if (cmds->num == CMDQ_BATCH_ENTRIES - 1 &&
(smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC)) {
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true);
cmds->num = 0;
if (force_sync || unsupported_cmd) {
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds,
cmds->num, true);
arm_smmu_cmdq_batch_init(smmu, cmds, cmd);
}
if (cmds->num == CMDQ_BATCH_ENTRIES) {
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false);
cmds->num = 0;
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds,
cmds->num, false);
arm_smmu_cmdq_batch_init(smmu, cmds, cmd);
}
index = cmds->num * CMDQ_ENT_DWORDS;
@@ -918,7 +956,9 @@ static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_batch *cmds)
{
return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true);
return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds,
cmds->num, true);
}
static int arm_smmu_page_response(struct device *dev,
@@ -985,7 +1025,7 @@ static void arm_smmu_sync_cd(struct arm_smmu_master *master,
},
};
cmds.num = 0;
arm_smmu_cmdq_batch_init(smmu, &cmds, &cmd);
for (i = 0; i < master->num_streams; i++) {
cmd.cfgi.sid = master->streams[i].id;
arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
@@ -1786,7 +1826,7 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master)
arm_smmu_atc_inv_to_cmd(IOMMU_NO_PASID, 0, 0, &cmd);
cmds.num = 0;
arm_smmu_cmdq_batch_init(master->smmu, &cmds, &cmd);
for (i = 0; i < master->num_streams; i++) {
cmd.atc.sid = master->streams[i].id;
arm_smmu_cmdq_batch_add(master->smmu, &cmds, &cmd);
@@ -1800,7 +1840,9 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,
{
int i;
unsigned long flags;
struct arm_smmu_cmdq_ent cmd;
struct arm_smmu_cmdq_ent cmd = {
.opcode = CMDQ_OP_ATC_INV,
};
struct arm_smmu_master *master;
struct arm_smmu_cmdq_batch cmds;
@@ -1826,7 +1868,7 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,
arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd);
cmds.num = 0;
arm_smmu_cmdq_batch_init(smmu_domain->smmu, &cmds, &cmd);
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_for_each_entry(master, &smmu_domain->devices, domain_head) {
@@ -1903,7 +1945,7 @@ static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd,
num_pages++;
}
cmds.num = 0;
arm_smmu_cmdq_batch_init(smmu, &cmds, cmd);
while (iova < end) {
if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
@@ -2872,12 +2914,10 @@ static struct iommu_ops arm_smmu_ops = {
};
/* Probing and initialisation functions */
static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q,
void __iomem *page,
unsigned long prod_off,
unsigned long cons_off,
size_t dwords, const char *name)
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
size_t dwords, const char *name)
{
size_t qsz;
@@ -2915,9 +2955,9 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
return 0;
}
static int arm_smmu_cmdq_init(struct arm_smmu_device *smmu)
int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq)
{
struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
atomic_set(&cmdq->owner_prod, 0);
@@ -2942,7 +2982,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
if (ret)
return ret;
ret = arm_smmu_cmdq_init(smmu);
ret = arm_smmu_cmdq_init(smmu, &smmu->cmdq);
if (ret)
return ret;
@@ -3088,7 +3128,14 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
if (ret)
return ret;
return arm_smmu_init_strtab(smmu);
ret = arm_smmu_init_strtab(smmu);
if (ret)
return ret;
if (smmu->impl_ops && smmu->impl_ops->init_structures)
return smmu->impl_ops->init_structures(smmu);
return 0;
}
static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
@@ -3149,6 +3196,15 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
int ret, nvec = ARM_SMMU_MAX_MSIS;
struct device *dev = smmu->dev;
/* Clear the MSI address regs */
writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
if (smmu->features & ARM_SMMU_FEAT_PRI)
writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
else
nvec--;
if (!(smmu->features & ARM_SMMU_FEAT_MSI))
return;
@@ -3157,9 +3213,6 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
return;
}
if (!(smmu->features & ARM_SMMU_FEAT_PRI))
nvec--;
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
if (ret) {
@@ -3221,9 +3274,9 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
}
}
static int arm_smmu_reset_irqs(struct arm_smmu_device *smmu)
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
{
int ret;
int ret, irq;
u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
/* Disable IRQs first */
@@ -3234,35 +3287,7 @@ static int arm_smmu_reset_irqs(struct arm_smmu_device *smmu)
return ret;
}
if (!smmu->combined_irq) {
/*
* Clear the MSI address regs. These registers will be reset
* in arm_smmu_write_msi_msg callback function by irq_domain
* upon a new MSI message.
*/
writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
if (smmu->features & ARM_SMMU_FEAT_PRI)
writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
}
if (smmu->features & ARM_SMMU_FEAT_PRI)
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
/* Enable interrupt generation on the SMMU */
ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
if (ret)
dev_warn(smmu->dev, "failed to enable irqs\n");
return ret;
}
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
{
int ret = 0, irq = smmu->combined_irq;
irq = smmu->combined_irq;
if (irq) {
/*
* Cavium ThunderX2 implementation doesn't support unique irq
@@ -3278,7 +3303,16 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
} else
arm_smmu_setup_unique_irqs(smmu);
return ret;
if (smmu->features & ARM_SMMU_FEAT_PRI)
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
/* Enable interrupt generation on the SMMU */
ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
if (ret)
dev_warn(smmu->dev, "failed to enable irqs\n");
return 0;
}
static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
@@ -3292,7 +3326,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
return ret;
}
static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
{
int ret;
u32 reg, enables;
@@ -3400,17 +3434,11 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
}
}
ret = arm_smmu_reset_irqs(smmu);
if (ret) {
dev_err(smmu->dev, "failed to reset irqs\n");
return ret;
}
if (is_kdump_kernel())
enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
/* Enable the SMMU interface, or ensure bypass */
if (!smmu->bypass || disable_bypass) {
if (!bypass || disable_bypass) {
enables |= CR0_SMMUEN;
} else {
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
@@ -3424,6 +3452,14 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
return ret;
}
if (smmu->impl_ops && smmu->impl_ops->device_reset) {
ret = smmu->impl_ops->device_reset(smmu);
if (ret) {
dev_err(smmu->dev, "failed to reset impl\n");
return ret;
}
}
return 0;
}
@@ -3685,19 +3721,84 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
return 0;
}
#ifdef CONFIG_ACPI
static void acpi_smmu_get_options(u32 model, struct arm_smmu_device *smmu)
#ifdef CONFIG_TEGRA241_CMDQV
static void tegra_cmdqv_dt_probe(struct device_node *smmu_node,
struct arm_smmu_device *smmu)
{
switch (model) {
struct platform_device *pdev;
struct device_node *np;
np = of_parse_phandle(smmu_node, "nvidia,cmdqv", 0);
if (!np)
return;
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return;
smmu->impl_dev = &pdev->dev;
smmu->options |= ARM_SMMU_OPT_TEGRA241_CMDQV;
dev_info(smmu->dev, "found companion CMDQV device: %s\n",
dev_name(smmu->impl_dev));
}
#else
static void tegra_cmdqv_dt_probe(struct device_node *smmu_node,
struct arm_smmu_device *smmu)
{
}
#endif
#ifdef CONFIG_ACPI
#ifdef CONFIG_TEGRA241_CMDQV
static void acpi_smmu_dsdt_probe_tegra241_cmdqv(struct acpi_iort_node *node,
struct arm_smmu_device *smmu)
{
const char *uid = kasprintf(GFP_KERNEL, "%u", node->identifier);
struct acpi_device *adev;
/* Look for an NVDA200C node whose _UID matches the SMMU node ID */
adev = acpi_dev_get_first_match_dev("NVDA200C", uid, -1);
if (adev) {
/* Tegra241 CMDQV driver is responsible for put_device() */
smmu->impl_dev = &adev->dev;
smmu->options |= ARM_SMMU_OPT_TEGRA241_CMDQV;
dev_info(smmu->dev, "found companion CMDQV device: %s\n",
dev_name(smmu->impl_dev));
}
kfree(uid);
}
#else
static void acpi_smmu_dsdt_probe_tegra241_cmdqv(struct acpi_iort_node *node,
struct arm_smmu_device *smmu)
{
}
#endif
static int acpi_smmu_iort_probe_model(struct acpi_iort_node *node,
struct arm_smmu_device *smmu)
{
struct acpi_iort_smmu_v3 *iort_smmu =
(struct acpi_iort_smmu_v3 *)node->node_data;
switch (iort_smmu->model) {
case ACPI_IORT_SMMU_V3_CAVIUM_CN99XX:
smmu->options |= ARM_SMMU_OPT_PAGE0_REGS_ONLY;
break;
case ACPI_IORT_SMMU_V3_HISILICON_HI161X:
smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH;
break;
case ACPI_IORT_SMMU_V3_GENERIC:
/*
* Tegra241 implementation stores its SMMU options and impl_dev
* in DSDT. Thus, go through the ACPI tables unconditionally.
*/
acpi_smmu_dsdt_probe_tegra241_cmdqv(node, smmu);
break;
}
dev_notice(smmu->dev, "option mask 0x%x\n", smmu->options);
return 0;
}
static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
@@ -3712,12 +3813,10 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
/* Retrieve SMMUv3 specific data */
iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
acpi_smmu_get_options(iort_smmu->model, smmu);
if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
smmu->features |= ARM_SMMU_FEAT_COHERENCY;
return 0;
return acpi_smmu_iort_probe_model(node, smmu);
}
#else
static inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
@@ -3734,6 +3833,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
u32 cells;
int ret = -EINVAL;
if (!of_reserved_mem_device_init(dev))
dev_info(dev, "using device-specific reserved memory\n");
if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
dev_err(dev, "missing #iommu-cells property\n");
else if (cells != 1)
@@ -3746,6 +3848,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
if (of_dma_is_coherent(dev->of_node))
smmu->features |= ARM_SMMU_FEAT_COHERENCY;
tegra_cmdqv_dt_probe(dev->of_node, smmu);
return ret;
}
@@ -3795,6 +3899,39 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu)
iort_put_rmr_sids(dev_fwnode(smmu->dev), &rmr_list);
}
static void arm_smmu_impl_remove(void *data)
{
struct arm_smmu_device *smmu = data;
if (smmu->impl_ops && smmu->impl_ops->device_remove)
smmu->impl_ops->device_remove(smmu);
}
/*
* Probe all the compiled in implementations. Each one checks to see if it
* matches this HW and if so returns a devm_krealloc'd arm_smmu_device which
* replaces the callers. Otherwise the original is returned or ERR_PTR.
*/
static struct arm_smmu_device *arm_smmu_impl_probe(struct arm_smmu_device *smmu)
{
struct arm_smmu_device *new_smmu = ERR_PTR(-ENODEV);
int ret;
if (smmu->impl_dev && (smmu->options & ARM_SMMU_OPT_TEGRA241_CMDQV))
new_smmu = tegra241_cmdqv_probe(smmu);
if (new_smmu == ERR_PTR(-ENODEV))
return smmu;
if (IS_ERR(new_smmu))
return new_smmu;
ret = devm_add_action_or_reset(new_smmu->dev, arm_smmu_impl_remove,
new_smmu);
if (ret)
return ERR_PTR(ret);
return new_smmu;
}
static int arm_smmu_device_probe(struct platform_device *pdev)
{
int irq, ret;
@@ -3815,10 +3952,13 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (ret == -ENODEV)
return ret;
}
/* Set bypass mode according to firmware probing result */
smmu->bypass = !!ret;
smmu = arm_smmu_impl_probe(smmu);
if (IS_ERR(smmu))
return PTR_ERR(smmu);
/* Base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -3880,15 +4020,17 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
/* Check for RMRs and install bypass STEs if any */
arm_smmu_rmr_install_bypass_ste(smmu);
ret = arm_smmu_setup_irqs(smmu);
if (ret)
return ret;
/* Reset the device */
ret = arm_smmu_device_reset(smmu);
ret = arm_smmu_device_reset(smmu, smmu->bypass);
if (ret)
goto err_disable;
ret = arm_smmu_setup_irqs(smmu);
if (ret) {
dev_err(smmu->dev, "failed to setup irqs\n");
return ret;
}
/* And we're up. Go go go! */
ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
"smmu3.%pa", &ioaddr);
@@ -3946,11 +4088,32 @@ static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
{
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
return arm_smmu_device_reset(smmu);
dev_dbg(dev, "Resuming\n");
arm_smmu_device_reset(smmu, smmu->bypass);
return 0;
}
static int __maybe_unused arm_smmu_runtime_suspend(struct device *dev)
{
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
struct arm_smmu_cmdq_ent cmd;
cmd.opcode = CMDQ_OP_CFGI_ALL;
arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
dev_dbg(dev, "Disabling\n");
arm_smmu_device_disable(smmu);
dev_dbg(dev, "Suspending\n");
return 0;
}
static const struct dev_pm_ops arm_smmu_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, arm_smmu_runtime_resume)
SET_LATE_SYSTEM_SLEEP_PM_OPS(arm_smmu_runtime_suspend, arm_smmu_runtime_resume)
};
static struct platform_driver arm_smmu_driver = {

View File

@@ -14,6 +14,8 @@
#include <linux/mmzone.h>
#include <linux/sizes.h>
struct arm_smmu_device;
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
#define IDR0_ST_LVL GENMASK(28, 27)
@@ -555,10 +557,18 @@ struct arm_smmu_cmdq {
atomic_long_t *valid_map;
atomic_t owner_prod;
atomic_t lock;
bool (*supports_cmd)(struct arm_smmu_cmdq_ent *ent);
};
static inline bool arm_smmu_cmdq_supports_cmd(struct arm_smmu_cmdq *cmdq,
struct arm_smmu_cmdq_ent *ent)
{
return cmdq->supports_cmd ? cmdq->supports_cmd(ent) : true;
}
struct arm_smmu_cmdq_batch {
u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS];
struct arm_smmu_cmdq *cmdq;
int num;
};
@@ -623,9 +633,20 @@ struct arm_smmu_strtab_cfg {
u32 strtab_base_cfg;
};
struct arm_smmu_impl_ops {
int (*device_reset)(struct arm_smmu_device *smmu);
void (*device_remove)(struct arm_smmu_device *smmu);
int (*init_structures)(struct arm_smmu_device *smmu);
struct arm_smmu_cmdq *(*get_secondary_cmdq)(
struct arm_smmu_device *smmu, struct arm_smmu_cmdq_ent *ent);
};
/* An SMMUv3 instance */
struct arm_smmu_device {
struct device *dev;
struct device *impl_dev;
const struct arm_smmu_impl_ops *impl_ops;
void __iomem *base;
void __iomem *page1;
@@ -655,6 +676,7 @@ struct arm_smmu_device {
#define ARM_SMMU_OPT_PAGE0_REGS_ONLY (1 << 1)
#define ARM_SMMU_OPT_MSIPOLL (1 << 2)
#define ARM_SMMU_OPT_CMDQ_FORCE_SYNC (1 << 3)
#define ARM_SMMU_OPT_TEGRA241_CMDQV (1 << 4)
u32 options;
struct arm_smmu_cmdq cmdq;
@@ -686,7 +708,7 @@ struct arm_smmu_device {
struct rb_root streams;
struct mutex streams_mutex;
bool bypass;
bool bypass;
};
struct arm_smmu_stream {
@@ -760,6 +782,15 @@ bool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd);
int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,
unsigned long iova, size_t size);
void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq);
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
size_t dwords, const char *name);
int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq);
#ifdef CONFIG_ARM_SMMU_V3_SVA
bool arm_smmu_sva_supported(struct arm_smmu_device *smmu);
bool arm_smmu_master_sva_supported(struct arm_smmu_master *master);
@@ -815,4 +846,14 @@ static inline void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,
{
}
#endif /* CONFIG_ARM_SMMU_V3_SVA */
#ifdef CONFIG_TEGRA241_CMDQV
struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu);
#else /* CONFIG_TEGRA241_CMDQV */
static inline struct arm_smmu_device *
tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
{
return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_TEGRA241_CMDQV */
#endif /* _ARM_SMMU_V3_H */

View File

@@ -0,0 +1,941 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2021-2024 NVIDIA CORPORATION & AFFILIATES. */
#define dev_fmt(fmt) "tegra241_cmdqv: " fmt
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <acpi/acpixf.h>
#include "arm-smmu-v3.h"
/* CMDQV register page base and size defines */
#define TEGRA241_CMDQV_CONFIG_BASE (0)
#define TEGRA241_CMDQV_CONFIG_SIZE (SZ_64K)
#define TEGRA241_VCMDQ_PAGE0_BASE (TEGRA241_CMDQV_CONFIG_BASE + SZ_64K)
#define TEGRA241_VCMDQ_PAGE1_BASE (TEGRA241_VCMDQ_PAGE0_BASE + SZ_64K)
#define TEGRA241_VINTF_PAGE_BASE (TEGRA241_VCMDQ_PAGE1_BASE + SZ_64K)
/* CMDQV global base regs */
#define TEGRA241_CMDQV_CONFIG 0x0000
#define CMDQV_EN BIT(0)
#define TEGRA241_CMDQV_PARAM 0x0004
#define CMDQV_NUM_VINTF_LOG2 GENMASK(11, 8)
#define CMDQV_NUM_VCMDQ_LOG2 GENMASK(7, 4)
#define TEGRA241_CMDQV_STATUS 0x0008
#define CMDQV_ENABLED BIT(0)
#define TEGRA241_CMDQV_VINTF_ERR_MAP 0x0014
#define TEGRA241_CMDQV_VINTF_INT_MASK 0x001C
#define TEGRA241_CMDQV_CMDQ_ERR_MAP(m) (0x0024 + 0x4*(m))
#define TEGRA241_CMDQV_CMDQ_ALLOC(q) (0x0200 + 0x4*(q))
#define CMDQV_CMDQ_ALLOC_VINTF GENMASK(20, 15)
#define CMDQV_CMDQ_ALLOC_LVCMDQ GENMASK(7, 1)
#define CMDQV_CMDQ_ALLOCATED BIT(0)
/* VINTF base regs */
#define TEGRA241_VINTF(v) (0x1000 + 0x100*(v))
#define TEGRA241_VINTF_CONFIG 0x0000
#define VINTF_HYP_OWN BIT(17)
#define VINTF_VMID GENMASK(16, 1)
#define VINTF_EN BIT(0)
#define TEGRA241_VINTF_STATUS 0x0004
#define VINTF_STATUS GENMASK(3, 1)
#define VINTF_ENABLED BIT(0)
#define TEGRA241_VINTF_LVCMDQ_ERR_MAP_64(m) \
(0x00C0 + 0x8*(m))
#define LVCMDQ_ERR_MAP_NUM_64 2
/* VCMDQ base regs */
/* -- PAGE0 -- */
#define TEGRA241_VCMDQ_PAGE0(q) (TEGRA241_VCMDQ_PAGE0_BASE + 0x80*(q))
#define TEGRA241_VCMDQ_CONS 0x00000
#define VCMDQ_CONS_ERR GENMASK(30, 24)
#define TEGRA241_VCMDQ_PROD 0x00004
#define TEGRA241_VCMDQ_CONFIG 0x00008
#define VCMDQ_EN BIT(0)
#define TEGRA241_VCMDQ_STATUS 0x0000C
#define VCMDQ_ENABLED BIT(0)
#define TEGRA241_VCMDQ_GERROR 0x00010
#define TEGRA241_VCMDQ_GERRORN 0x00014
/* -- PAGE1 -- */
#define TEGRA241_VCMDQ_PAGE1(q) (TEGRA241_VCMDQ_PAGE1_BASE + 0x80*(q))
#define VCMDQ_ADDR GENMASK(47, 5)
#define VCMDQ_LOG2SIZE GENMASK(4, 0)
#define TEGRA241_VCMDQ_BASE 0x00000
#define TEGRA241_VCMDQ_BASE_H 0x00004
#define TEGRA241_VCMDQ_CONS_INDX_BASE 0x00008
#define TEGRA241_VCMDQ_CONS_INDX_BASE_H 0x0000C
/* VINTF logical-VCMDQ pages */
#define TEGRA241_VINTFi_PAGE0(i) (TEGRA241_VINTF_PAGE_BASE + SZ_128K*(i))
#define TEGRA241_VINTFi_PAGE1(i) (TEGRA241_VINTFi_PAGE0(i) + SZ_64K)
#define TEGRA241_VINTFi_LVCMDQ_PAGE0(i, q) \
(TEGRA241_VINTFi_PAGE0(i) + 0x80*(q))
#define TEGRA241_VINTFi_LVCMDQ_PAGE1(i, q) \
(TEGRA241_VINTFi_PAGE1(i) + 0x80*(q))
/* MMIO helpers */
#define REG_CMDQV(_cmdqv, _regname) \
((_cmdqv)->base + TEGRA241_CMDQV_##_regname)
#define REG_VINTF(_vintf, _regname) \
((_vintf)->base + TEGRA241_VINTF_##_regname)
#define REG_VCMDQ_PAGE0(_vcmdq, _regname) \
((_vcmdq)->page0 + TEGRA241_VCMDQ_##_regname)
#define REG_VCMDQ_PAGE1(_vcmdq, _regname) \
((_vcmdq)->page1 + TEGRA241_VCMDQ_##_regname)
static bool disable_cmdqv;
module_param(disable_cmdqv, bool, 0444);
MODULE_PARM_DESC(disable_cmdqv,
"This allows to disable CMDQV HW and use default SMMU internal CMDQ.");
static bool bypass_vcmdq;
module_param(bypass_vcmdq, bool, 0444);
MODULE_PARM_DESC(bypass_vcmdq,
"This allows to bypass VCMDQ for debugging use or perf comparison.");
/**
* struct tegra241_vcmdq - Virtual Command Queue
* @idx: Global index in the CMDQV
* @lidx: Local index in the VINTF
* @enabled: Enable status
* @cmdqv: Parent CMDQV pointer
* @vintf: Parent VINTF pointer
* @cmdq: Command Queue struct
* @page0: MMIO Page0 base address
* @page1: MMIO Page1 base address
*/
struct tegra241_vcmdq {
u16 idx;
u16 lidx;
bool enabled;
struct tegra241_cmdqv *cmdqv;
struct tegra241_vintf *vintf;
struct arm_smmu_cmdq cmdq;
void __iomem *page0;
void __iomem *page1;
};
/**
* struct tegra241_vintf - Virtual Interface
* @idx: Global index in the CMDQV
* @enabled: Enable status
* @hyp_own: Owned by hypervisor (in-kernel)
* @cmdqv: Parent CMDQV pointer
* @lvcmdqs: List of logical VCMDQ pointers
* @base: MMIO base address
*/
struct tegra241_vintf {
u16 idx;
bool enabled;
bool hyp_own;
struct tegra241_cmdqv *cmdqv;
struct tegra241_vcmdq **lvcmdqs;
void __iomem *base;
};
/**
* struct tegra241_cmdqv - CMDQ-V for SMMUv3
* @smmu: SMMUv3 device
* @dev: CMDQV device
* @base: MMIO base address
* @irq: IRQ number
* @num_vintfs: Total number of VINTFs
* @num_vcmdqs: Total number of VCMDQs
* @num_lvcmdqs_per_vintf: Number of logical VCMDQs per VINTF
* @vintf_ids: VINTF id allocator
* @vintfs: List of VINTFs
*/
struct tegra241_cmdqv {
struct arm_smmu_device smmu;
struct device *dev;
void __iomem *base;
int irq;
/* CMDQV Hardware Params */
u16 num_vintfs;
u16 num_vcmdqs;
u16 num_lvcmdqs_per_vintf;
struct ida vintf_ids;
struct tegra241_vintf **vintfs;
};
/* Config and Polling Helpers */
static inline int tegra241_cmdqv_write_config(struct tegra241_cmdqv *cmdqv,
void __iomem *addr_config,
void __iomem *addr_status,
u32 regval, const char *header,
bool *out_enabled)
{
bool en = regval & BIT(0);
int ret;
writel(regval, addr_config);
ret = readl_poll_timeout(addr_status, regval,
en ? regval & BIT(0) : !(regval & BIT(0)),
1, ARM_SMMU_POLL_TIMEOUT_US);
if (ret)
dev_err(cmdqv->dev, "%sfailed to %sable, STATUS=0x%08X\n",
header, en ? "en" : "dis", regval);
if (out_enabled)
WRITE_ONCE(*out_enabled, regval & BIT(0));
return ret;
}
static inline int cmdqv_write_config(struct tegra241_cmdqv *cmdqv, u32 regval)
{
return tegra241_cmdqv_write_config(cmdqv,
REG_CMDQV(cmdqv, CONFIG),
REG_CMDQV(cmdqv, STATUS),
regval, "CMDQV: ", NULL);
}
static inline int vintf_write_config(struct tegra241_vintf *vintf, u32 regval)
{
char header[16];
snprintf(header, 16, "VINTF%u: ", vintf->idx);
return tegra241_cmdqv_write_config(vintf->cmdqv,
REG_VINTF(vintf, CONFIG),
REG_VINTF(vintf, STATUS),
regval, header, &vintf->enabled);
}
static inline char *lvcmdq_error_header(struct tegra241_vcmdq *vcmdq,
char *header, int hlen)
{
WARN_ON(hlen < 64);
if (WARN_ON(!vcmdq->vintf))
return "";
snprintf(header, hlen, "VINTF%u: VCMDQ%u/LVCMDQ%u: ",
vcmdq->vintf->idx, vcmdq->idx, vcmdq->lidx);
return header;
}
static inline int vcmdq_write_config(struct tegra241_vcmdq *vcmdq, u32 regval)
{
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
return tegra241_cmdqv_write_config(vcmdq->cmdqv,
REG_VCMDQ_PAGE0(vcmdq, CONFIG),
REG_VCMDQ_PAGE0(vcmdq, STATUS),
regval, h, &vcmdq->enabled);
}
/* ISR Functions */
static void tegra241_vintf0_handle_error(struct tegra241_vintf *vintf)
{
int i;
for (i = 0; i < LVCMDQ_ERR_MAP_NUM_64; i++) {
u64 map = readq_relaxed(REG_VINTF(vintf, LVCMDQ_ERR_MAP_64(i)));
while (map) {
unsigned long lidx = __ffs64(map);
struct tegra241_vcmdq *vcmdq = vintf->lvcmdqs[lidx];
u32 gerror = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR));
__arm_smmu_cmdq_skip_err(&vintf->cmdqv->smmu, &vcmdq->cmdq);
writel(gerror, REG_VCMDQ_PAGE0(vcmdq, GERRORN));
map &= ~BIT_ULL(lidx);
}
}
}
static irqreturn_t tegra241_cmdqv_isr(int irq, void *devid)
{
struct tegra241_cmdqv *cmdqv = (struct tegra241_cmdqv *)devid;
void __iomem *reg_vintf_map = REG_CMDQV(cmdqv, VINTF_ERR_MAP);
char err_str[256];
u64 vintf_map;
/* Use readl_relaxed() as register addresses are not 64-bit aligned */
vintf_map = (u64)readl_relaxed(reg_vintf_map + 0x4) << 32 |
(u64)readl_relaxed(reg_vintf_map);
snprintf(err_str, sizeof(err_str),
"vintf_map: %016llx, vcmdq_map %08x:%08x:%08x:%08x", vintf_map,
readl_relaxed(REG_CMDQV(cmdqv, CMDQ_ERR_MAP(3))),
readl_relaxed(REG_CMDQV(cmdqv, CMDQ_ERR_MAP(2))),
readl_relaxed(REG_CMDQV(cmdqv, CMDQ_ERR_MAP(1))),
readl_relaxed(REG_CMDQV(cmdqv, CMDQ_ERR_MAP(0))));
dev_warn(cmdqv->dev, "unexpected error reported. %s\n", err_str);
/* Handle VINTF0 and its LVCMDQs */
if (vintf_map & BIT_ULL(0)) {
tegra241_vintf0_handle_error(cmdqv->vintfs[0]);
vintf_map &= ~BIT_ULL(0);
}
return IRQ_HANDLED;
}
/* Command Queue Function */
static bool tegra241_guest_vcmdq_supports_cmd(struct arm_smmu_cmdq_ent *ent)
{
switch (ent->opcode) {
case CMDQ_OP_TLBI_NH_ASID:
case CMDQ_OP_TLBI_NH_VA:
case CMDQ_OP_ATC_INV:
return true;
default:
return false;
}
}
static struct arm_smmu_cmdq *
tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_ent *ent)
{
struct tegra241_cmdqv *cmdqv =
container_of(smmu, struct tegra241_cmdqv, smmu);
struct tegra241_vintf *vintf = cmdqv->vintfs[0];
struct tegra241_vcmdq *vcmdq;
u16 lidx;
if (READ_ONCE(bypass_vcmdq))
return NULL;
/* Use SMMU CMDQ if VINTF0 is uninitialized */
if (!READ_ONCE(vintf->enabled))
return NULL;
/*
* Select a LVCMDQ to use. Here we use a temporal solution to
* balance out traffic on cmdq issuing: each cmdq has its own
* lock, if all cpus issue cmdlist using the same cmdq, only
* one CPU at a time can enter the process, while the others
* will be spinning at the same lock.
*/
lidx = raw_smp_processor_id() % cmdqv->num_lvcmdqs_per_vintf;
vcmdq = vintf->lvcmdqs[lidx];
if (!vcmdq || !READ_ONCE(vcmdq->enabled))
return NULL;
/* Unsupported CMD goes for smmu->cmdq pathway */
if (!arm_smmu_cmdq_supports_cmd(&vcmdq->cmdq, ent))
return NULL;
return &vcmdq->cmdq;
}
/* HW Reset Functions */
static void tegra241_vcmdq_hw_deinit(struct tegra241_vcmdq *vcmdq)
{
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
u32 gerrorn, gerror;
if (vcmdq_write_config(vcmdq, 0)) {
dev_err(vcmdq->cmdqv->dev,
"%sGERRORN=0x%X, GERROR=0x%X, CONS=0x%X\n", h,
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERRORN)),
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR)),
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, CONS)));
}
writel_relaxed(0, REG_VCMDQ_PAGE0(vcmdq, PROD));
writel_relaxed(0, REG_VCMDQ_PAGE0(vcmdq, CONS));
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, BASE_H));
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, BASE));
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, CONS_INDX_BASE_H));
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, CONS_INDX_BASE));
gerrorn = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERRORN));
gerror = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR));
if (gerror != gerrorn) {
dev_warn(vcmdq->cmdqv->dev,
"%suncleared error detected, resetting\n", h);
writel(gerror, REG_VCMDQ_PAGE0(vcmdq, GERRORN));
}
dev_dbg(vcmdq->cmdqv->dev, "%sdeinited\n", h);
}
static int tegra241_vcmdq_hw_init(struct tegra241_vcmdq *vcmdq)
{
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
int ret;
/* Reset VCMDQ */
tegra241_vcmdq_hw_deinit(vcmdq);
/* Configure and enable VCMDQ */
writel_relaxed(upper_32_bits(vcmdq->cmdq.q.q_base), REG_VCMDQ_PAGE1(vcmdq, BASE_H));
writel_relaxed(lower_32_bits(vcmdq->cmdq.q.q_base), REG_VCMDQ_PAGE1(vcmdq, BASE));
writel_relaxed(vcmdq->cmdq.q.llq.prod, REG_VCMDQ_PAGE0(vcmdq, PROD));
writel_relaxed(vcmdq->cmdq.q.llq.cons, REG_VCMDQ_PAGE0(vcmdq, CONS));
ret = vcmdq_write_config(vcmdq, VCMDQ_EN);
if (ret) {
dev_err(vcmdq->cmdqv->dev,
"%sGERRORN=0x%X, GERROR=0x%X, CONS=0x%X\n", h,
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERRORN)),
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR)),
readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, CONS)));
return ret;
}
dev_dbg(vcmdq->cmdqv->dev, "%sinited\n", h);
return 0;
}
static void tegra241_vintf_hw_deinit(struct tegra241_vintf *vintf)
{
u16 lidx;
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx])
tegra241_vcmdq_hw_deinit(vintf->lvcmdqs[lidx]);
vintf_write_config(vintf, 0);
}
static int tegra241_vintf_hw_init(struct tegra241_vintf *vintf, bool hyp_own)
{
u32 regval;
u16 lidx;
int ret;
/* Reset VINTF */
tegra241_vintf_hw_deinit(vintf);
/* Configure and enable VINTF */
/*
* Note that HYP_OWN bit is wired to zero when running in guest kernel,
* whether enabling it here or not, as !HYP_OWN cmdq HWs only support a
* restricted set of supported commands.
*/
regval = FIELD_PREP(VINTF_HYP_OWN, hyp_own);
writel(regval, REG_VINTF(vintf, CONFIG));
ret = vintf_write_config(vintf, regval | VINTF_EN);
if (ret)
return ret;
/*
* As being mentioned above, HYP_OWN bit is wired to zero for a guest
* kernel, so read it back from HW to ensure that reflects in hyp_own
*/
vintf->hyp_own = !!(VINTF_HYP_OWN & readl(REG_VINTF(vintf, CONFIG)));
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++) {
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) {
ret = tegra241_vcmdq_hw_init(vintf->lvcmdqs[lidx]);
if (ret) {
tegra241_vintf_hw_deinit(vintf);
return ret;
}
}
}
return 0;
}
static int tegra241_cmdqv_hw_reset(struct arm_smmu_device *smmu)
{
struct tegra241_cmdqv *cmdqv =
container_of(smmu, struct tegra241_cmdqv, smmu);
u16 qidx, lidx, idx;
u32 regval;
int ret;
/* Reset CMDQV */
regval = readl_relaxed(REG_CMDQV(cmdqv, CONFIG));
ret = cmdqv_write_config(cmdqv, regval & ~CMDQV_EN);
if (ret)
return ret;
ret = cmdqv_write_config(cmdqv, regval | CMDQV_EN);
if (ret)
return ret;
/* Assign preallocated global VCMDQs to each VINTF as LVCMDQs */
for (idx = 0, qidx = 0; idx < cmdqv->num_vintfs; idx++) {
for (lidx = 0; lidx < cmdqv->num_lvcmdqs_per_vintf; lidx++) {
regval = FIELD_PREP(CMDQV_CMDQ_ALLOC_VINTF, idx);
regval |= FIELD_PREP(CMDQV_CMDQ_ALLOC_LVCMDQ, lidx);
regval |= CMDQV_CMDQ_ALLOCATED;
writel_relaxed(regval,
REG_CMDQV(cmdqv, CMDQ_ALLOC(qidx++)));
}
}
return tegra241_vintf_hw_init(cmdqv->vintfs[0], true);
}
/* VCMDQ Resource Helpers */
static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
{
struct arm_smmu_device *smmu = &vcmdq->cmdqv->smmu;
struct arm_smmu_cmdq *cmdq = &vcmdq->cmdq;
struct arm_smmu_queue *q = &cmdq->q;
char name[16];
u32 regval;
int ret;
snprintf(name, 16, "vcmdq%u", vcmdq->idx);
/* Cap queue size to SMMU's IDR1.CMDQS and ensure natural alignment */
regval = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
q->llq.max_n_shift =
min_t(u32, CMDQ_MAX_SZ_SHIFT, FIELD_GET(IDR1_CMDQS, regval));
/* Use the common helper to init the VCMDQ, and then... */
ret = arm_smmu_init_one_queue(smmu, q, vcmdq->page0,
TEGRA241_VCMDQ_PROD, TEGRA241_VCMDQ_CONS,
CMDQ_ENT_DWORDS, name);
if (ret)
return ret;
/* ...override q_base to write VCMDQ_BASE registers */
q->q_base = q->base_dma & VCMDQ_ADDR;
q->q_base |= FIELD_PREP(VCMDQ_LOG2SIZE, q->llq.max_n_shift);
if (!vcmdq->vintf->hyp_own)
cmdq->supports_cmd = tegra241_guest_vcmdq_supports_cmd;
return arm_smmu_cmdq_init(smmu, cmdq);
}
/* VINTF Logical VCMDQ Resource Helpers */
static void tegra241_vintf_deinit_lvcmdq(struct tegra241_vintf *vintf, u16 lidx)
{
vintf->lvcmdqs[lidx] = NULL;
}
static int tegra241_vintf_init_lvcmdq(struct tegra241_vintf *vintf, u16 lidx,
struct tegra241_vcmdq *vcmdq)
{
struct tegra241_cmdqv *cmdqv = vintf->cmdqv;
u16 idx = vintf->idx;
vcmdq->idx = idx * cmdqv->num_lvcmdqs_per_vintf + lidx;
vcmdq->lidx = lidx;
vcmdq->cmdqv = cmdqv;
vcmdq->vintf = vintf;
vcmdq->page0 = cmdqv->base + TEGRA241_VINTFi_LVCMDQ_PAGE0(idx, lidx);
vcmdq->page1 = cmdqv->base + TEGRA241_VINTFi_LVCMDQ_PAGE1(idx, lidx);
vintf->lvcmdqs[lidx] = vcmdq;
return 0;
}
static void tegra241_vintf_free_lvcmdq(struct tegra241_vintf *vintf, u16 lidx)
{
struct tegra241_vcmdq *vcmdq = vintf->lvcmdqs[lidx];
char header[64];
/* Note that the lvcmdq queue memory space is managed by devres */
tegra241_vintf_deinit_lvcmdq(vintf, lidx);
dev_dbg(vintf->cmdqv->dev,
"%sdeallocated\n", lvcmdq_error_header(vcmdq, header, 64));
kfree(vcmdq);
}
static struct tegra241_vcmdq *
tegra241_vintf_alloc_lvcmdq(struct tegra241_vintf *vintf, u16 lidx)
{
struct tegra241_cmdqv *cmdqv = vintf->cmdqv;
struct tegra241_vcmdq *vcmdq;
char header[64];
int ret;
vcmdq = kzalloc(sizeof(*vcmdq), GFP_KERNEL);
if (!vcmdq)
return ERR_PTR(-ENOMEM);
ret = tegra241_vintf_init_lvcmdq(vintf, lidx, vcmdq);
if (ret)
goto free_vcmdq;
/* Build an arm_smmu_cmdq for each LVCMDQ */
ret = tegra241_vcmdq_alloc_smmu_cmdq(vcmdq);
if (ret)
goto deinit_lvcmdq;
dev_dbg(cmdqv->dev,
"%sallocated\n", lvcmdq_error_header(vcmdq, header, 64));
return vcmdq;
deinit_lvcmdq:
tegra241_vintf_deinit_lvcmdq(vintf, lidx);
free_vcmdq:
kfree(vcmdq);
return ERR_PTR(ret);
}
/* VINTF Resource Helpers */
static void tegra241_cmdqv_deinit_vintf(struct tegra241_cmdqv *cmdqv, u16 idx)
{
kfree(cmdqv->vintfs[idx]->lvcmdqs);
ida_free(&cmdqv->vintf_ids, idx);
cmdqv->vintfs[idx] = NULL;
}
static int tegra241_cmdqv_init_vintf(struct tegra241_cmdqv *cmdqv, u16 max_idx,
struct tegra241_vintf *vintf)
{
u16 idx;
int ret;
ret = ida_alloc_max(&cmdqv->vintf_ids, max_idx, GFP_KERNEL);
if (ret < 0)
return ret;
idx = ret;
vintf->idx = idx;
vintf->cmdqv = cmdqv;
vintf->base = cmdqv->base + TEGRA241_VINTF(idx);
vintf->lvcmdqs = kcalloc(cmdqv->num_lvcmdqs_per_vintf,
sizeof(*vintf->lvcmdqs), GFP_KERNEL);
if (!vintf->lvcmdqs) {
ida_free(&cmdqv->vintf_ids, idx);
return -ENOMEM;
}
cmdqv->vintfs[idx] = vintf;
return ret;
}
/* Remove Helpers */
static void tegra241_vintf_remove_lvcmdq(struct tegra241_vintf *vintf, u16 lidx)
{
tegra241_vcmdq_hw_deinit(vintf->lvcmdqs[lidx]);
tegra241_vintf_free_lvcmdq(vintf, lidx);
}
static void tegra241_cmdqv_remove_vintf(struct tegra241_cmdqv *cmdqv, u16 idx)
{
struct tegra241_vintf *vintf = cmdqv->vintfs[idx];
u16 lidx;
/* Remove LVCMDQ resources */
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)
if (vintf->lvcmdqs[lidx])
tegra241_vintf_remove_lvcmdq(vintf, lidx);
/* Remove VINTF resources */
tegra241_vintf_hw_deinit(vintf);
dev_dbg(cmdqv->dev, "VINTF%u: deallocated\n", vintf->idx);
tegra241_cmdqv_deinit_vintf(cmdqv, idx);
kfree(vintf);
}
static void tegra241_cmdqv_remove(struct arm_smmu_device *smmu)
{
struct tegra241_cmdqv *cmdqv =
container_of(smmu, struct tegra241_cmdqv, smmu);
u16 idx;
/* Remove VINTF resources */
for (idx = 0; idx < cmdqv->num_vintfs; idx++) {
if (cmdqv->vintfs[idx]) {
/* Only vintf0 should remain at this stage */
WARN_ON(idx > 0);
tegra241_cmdqv_remove_vintf(cmdqv, idx);
}
}
/* Remove cmdqv resources */
ida_destroy(&cmdqv->vintf_ids);
if (cmdqv->irq > 0)
free_irq(cmdqv->irq, cmdqv);
iounmap(cmdqv->base);
kfree(cmdqv->vintfs);
put_device(cmdqv->dev); /* smmu->impl_dev */
}
static struct arm_smmu_impl_ops tegra241_cmdqv_impl_ops = {
.get_secondary_cmdq = tegra241_cmdqv_get_cmdq,
.device_reset = tegra241_cmdqv_hw_reset,
.device_remove = tegra241_cmdqv_remove,
};
/* Probe Functions */
static int tegra241_cmdqv_acpi_is_memory(struct acpi_resource *res, void *data)
{
struct resource_win win;
return !acpi_dev_resource_address_space(res, &win);
}
static int tegra241_cmdqv_acpi_get_irqs(struct acpi_resource *ares, void *data)
{
struct resource r;
int *irq = data;
if (*irq <= 0 && acpi_dev_resource_interrupt(ares, 0, &r))
*irq = r.start;
return 1; /* No need to add resource to the list */
}
static struct resource *
tegra241_cmdqv_find_acpi_resource(struct device *dev, int *irq)
{
struct acpi_device *adev = to_acpi_device(dev);
struct list_head resource_list;
struct resource_entry *rentry;
struct resource *res = NULL;
int ret;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
tegra241_cmdqv_acpi_is_memory, NULL);
if (ret < 0) {
dev_err(dev, "failed to get memory resource: %d\n", ret);
return NULL;
}
rentry = list_first_entry_or_null(&resource_list,
struct resource_entry, node);
if (!rentry) {
dev_err(dev, "failed to get memory resource entry\n");
goto free_list;
}
/* Caller must free the res */
res = kzalloc(sizeof(*res), GFP_KERNEL);
if (!res)
goto free_list;
*res = *rentry->res;
acpi_dev_free_resource_list(&resource_list);
INIT_LIST_HEAD(&resource_list);
if (irq)
ret = acpi_dev_get_resources(adev, &resource_list,
tegra241_cmdqv_acpi_get_irqs, irq);
if (ret < 0 || !irq || *irq <= 0)
dev_warn(dev, "no interrupt. errors will not be reported\n");
free_list:
acpi_dev_free_resource_list(&resource_list);
return res;
}
static struct resource *
tegra241_cmdqv_find_dt_resource(struct device *dev, int *irq)
{
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no memory resource found for CMDQV\n");
return NULL;
}
if (irq)
*irq = platform_get_irq_byname_optional(pdev, "cmdqv");
if (!irq || *irq <= 0)
dev_warn(dev, "no interrupt. errors will not be reported\n");
return res;
}
static int tegra241_cmdqv_init_structures(struct arm_smmu_device *smmu)
{
struct tegra241_cmdqv *cmdqv =
container_of(smmu, struct tegra241_cmdqv, smmu);
struct tegra241_vintf *vintf;
int lidx;
int ret;
vintf = kzalloc(sizeof(*vintf), GFP_KERNEL);
if (!vintf)
return -ENOMEM;
/* Init VINTF0 for in-kernel use */
ret = tegra241_cmdqv_init_vintf(cmdqv, 0, vintf);
if (ret) {
dev_err(cmdqv->dev, "failed to init vintf0: %d\n", ret);
return ret;
}
/* Preallocate logical VCMDQs to VINTF0 */
for (lidx = 0; lidx < cmdqv->num_lvcmdqs_per_vintf; lidx++) {
struct tegra241_vcmdq *vcmdq;
vcmdq = tegra241_vintf_alloc_lvcmdq(vintf, lidx);
if (IS_ERR(vcmdq))
return PTR_ERR(vcmdq);
}
/* Now, we are ready to run all the impl ops */
smmu->impl_ops = &tegra241_cmdqv_impl_ops;
return 0;
}
#ifdef CONFIG_IOMMU_DEBUGFS
static struct dentry *cmdqv_debugfs_dir;
#endif
static struct arm_smmu_device *
__tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
int irq)
{
static const struct arm_smmu_impl_ops init_ops = {
.init_structures = tegra241_cmdqv_init_structures,
.device_remove = tegra241_cmdqv_remove,
};
struct tegra241_cmdqv *cmdqv = NULL;
struct arm_smmu_device *new_smmu;
void __iomem *base;
u32 regval;
int ret;
static_assert(offsetof(struct tegra241_cmdqv, smmu) == 0);
base = ioremap(res->start, resource_size(res));
if (!base) {
dev_err(smmu->dev, "failed to ioremap\n");
return NULL;
}
regval = readl(base + TEGRA241_CMDQV_CONFIG);
if (disable_cmdqv) {
dev_info(smmu->dev, "Detected disable_cmdqv=true\n");
writel(regval & ~CMDQV_EN, base + TEGRA241_CMDQV_CONFIG);
goto iounmap;
}
cmdqv = devm_krealloc(smmu->dev, smmu, sizeof(*cmdqv), GFP_KERNEL);
if (!cmdqv)
goto iounmap;
new_smmu = &cmdqv->smmu;
cmdqv->irq = irq;
cmdqv->base = base;
cmdqv->dev = smmu->impl_dev;
if (cmdqv->irq > 0) {
ret = request_irq(irq, tegra241_cmdqv_isr, 0, "tegra241-cmdqv",
cmdqv);
if (ret) {
dev_err(cmdqv->dev, "failed to request irq (%d): %d\n",
cmdqv->irq, ret);
goto iounmap;
}
}
regval = readl_relaxed(REG_CMDQV(cmdqv, PARAM));
cmdqv->num_vintfs = 1 << FIELD_GET(CMDQV_NUM_VINTF_LOG2, regval);
cmdqv->num_vcmdqs = 1 << FIELD_GET(CMDQV_NUM_VCMDQ_LOG2, regval);
cmdqv->num_lvcmdqs_per_vintf = cmdqv->num_vcmdqs / cmdqv->num_vintfs;
cmdqv->vintfs =
kcalloc(cmdqv->num_vintfs, sizeof(*cmdqv->vintfs), GFP_KERNEL);
if (!cmdqv->vintfs)
goto free_irq;
ida_init(&cmdqv->vintf_ids);
#ifdef CONFIG_IOMMU_DEBUGFS
if (!cmdqv_debugfs_dir) {
cmdqv_debugfs_dir =
debugfs_create_dir("tegra241_cmdqv", iommu_debugfs_dir);
debugfs_create_bool("bypass_vcmdq", 0644, cmdqv_debugfs_dir,
&bypass_vcmdq);
}
#endif
/* Provide init-level ops only, until tegra241_cmdqv_init_structures */
new_smmu->impl_ops = &init_ops;
return new_smmu;
free_irq:
if (cmdqv->irq > 0)
free_irq(cmdqv->irq, cmdqv);
iounmap:
iounmap(base);
return NULL;
}
struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
{
struct arm_smmu_device *new_smmu;
struct resource *res = NULL;
int irq;
if (!smmu->dev->of_node)
res = tegra241_cmdqv_find_acpi_resource(smmu->impl_dev, &irq);
else
res = tegra241_cmdqv_find_dt_resource(smmu->impl_dev, &irq);
if (!res)
goto out_fallback;
new_smmu = __tegra241_cmdqv_probe(smmu, res, irq);
if (!smmu->dev->of_node)
kfree(res);
if (new_smmu)
return new_smmu;
out_fallback:
dev_info(smmu->impl_dev, "Falling back to standard SMMU CMDQ\n");
smmu->options &= ~ARM_SMMU_OPT_TEGRA241_CMDQV;
put_device(smmu->impl_dev);
return ERR_PTR(-ENODEV);
}
static const struct of_device_id tegra241_cmdqv_of_match[] = {
{ .compatible = "nvidia,tegra264-cmdqv" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra241_cmdqv_of_match);
static struct platform_driver tegra241_cmdqv_driver = {
.driver = {
.name = "tegra241-cmdqv",
.of_match_table = tegra241_cmdqv_of_match,
},
};
module_platform_driver(tegra241_cmdqv_driver);
MODULE_DESCRIPTION("NVIDIA Tegra241 Command Queue Virtualization Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2016-2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -835,7 +835,6 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
{
unsigned int i, irq = 0;
unsigned int default_si = hsp->num_si;
unsigned int value;
int err;
for (i = 0; i < hsp->num_si; i++) {
@@ -853,13 +852,6 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
hsp->shared_irqs[i].enabled = true;
value = tegra_hsp_readl(hsp, HSP_INT_IE(i));
if (value && !hsp->soc->virtualized) {
dev_warn(hsp->dev,
"disabling interrupts for si: %d\n", i);
tegra_hsp_writel(hsp, 0, HSP_INT_IE(i));
}
/* Use first available interrupt as default. */
if (default_si == hsp->num_si)
default_si = i;

View File

@@ -987,8 +987,6 @@ static int tegra_mc_probe(struct platform_device *pdev)
}
if (mc->soc->has_chiplet_arch) {
unsigned long intstat;
/* Unmask MCF interrupts */
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->mcf_intmask, MCF_INTMASK_0);
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->mcf_intmask, MCF_INTPRIORITY_0);

View File

@@ -1137,7 +1137,6 @@ static struct phy_driver realtek_drvs[] = {
.get_wol = &rtl8211f_get_wol,
.set_wol = &rtl8211f_set_wol,
.suspend = rtl821x_suspend,
.suspend = genphy_suspend,
.resume = rtl821x_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,

View File

@@ -11,6 +11,12 @@
#include "arm_cspmu.h"
#define PMCNTENSET 0xC00
#define PMCNTENCLR 0xC20
#define PMCR 0xE04
#define PMCR_E BIT(0)
#define NV_PCIE_PORT_COUNT 10ULL
#define NV_PCIE_FILTER_ID_MASK GENMASK_ULL(NV_PCIE_PORT_COUNT - 1, 0)
@@ -20,6 +26,16 @@
#define NV_CNVL_PORT_COUNT 4ULL
#define NV_CNVL_FILTER_ID_MASK GENMASK_ULL(NV_CNVL_PORT_COUNT - 1, 0)
#define NV_UCF_FILTER_ID_MASK GENMASK_ULL(4, 0)
#define NV_UPHY_FILTER_ID_MASK GENMASK_ULL(16, 0)
#define NV_VISION_FILTER_ID_MASK GENMASK_ULL(19, 0)
#define NV_DISPLAY_FILTER_ID_MASK BIT(0)
#define NV_UCF_GPU_FILTER_ID_MASK BIT(0)
#define NV_GENERIC_FILTER_ID_MASK GENMASK_ULL(31, 0)
#define NV_PRODID_MASK (ARM_CSPMU_PMIIDR_PRODUCTID | \
@@ -45,6 +61,7 @@ struct nv_cspmu_ctx {
u32 filter_default_val;
struct attribute **event_attr;
struct attribute **format_attr;
u32 *pmcnten;
};
static struct attribute *scf_pmu_event_attrs[] = {
@@ -178,6 +195,72 @@ static struct attribute *mcf_pmu_event_attrs[] = {
NULL,
};
static struct attribute *ucf_pmu_event_attrs[] = {
ARM_CSPMU_EVENT_ATTR(slc_allocate, 0xf0),
ARM_CSPMU_EVENT_ATTR(slc_refill, 0xf1),
ARM_CSPMU_EVENT_ATTR(slc_access, 0xf2),
ARM_CSPMU_EVENT_ATTR(slc_wb, 0xf3),
ARM_CSPMU_EVENT_ATTR(slc_hit, 0x118),
ARM_CSPMU_EVENT_ATTR(slc_access_wr, 0x112),
ARM_CSPMU_EVENT_ATTR(slc_access_rd, 0x111),
ARM_CSPMU_EVENT_ATTR(slc_refill_wr, 0x10a),
ARM_CSPMU_EVENT_ATTR(slc_refill_rd, 0x109),
ARM_CSPMU_EVENT_ATTR(slc_hit_wr, 0x11a),
ARM_CSPMU_EVENT_ATTR(slc_hit_rd, 0x119),
ARM_CSPMU_EVENT_ATTR(slc_access_dataless, 0x183),
ARM_CSPMU_EVENT_ATTR(slc_access_atomic, 0x184),
ARM_CSPMU_EVENT_ATTR(local_snoop, 0x180),
ARM_CSPMU_EVENT_ATTR(ext_snp_access, 0x181),
ARM_CSPMU_EVENT_ATTR(ext_snp_evict, 0x182),
ARM_CSPMU_EVENT_ATTR(ucf_bus_cycles, 0x1d),
ARM_CSPMU_EVENT_ATTR(any_access_wr, 0x112),
ARM_CSPMU_EVENT_ATTR(any_access_rd, 0x111),
ARM_CSPMU_EVENT_ATTR(any_byte_wr, 0x114),
ARM_CSPMU_EVENT_ATTR(any_byte_rd, 0x113),
ARM_CSPMU_EVENT_ATTR(any_outstanding_rd, 0x115),
ARM_CSPMU_EVENT_ATTR(local_dram_access_wr, 0x122),
ARM_CSPMU_EVENT_ATTR(local_dram_access_rd, 0x121),
ARM_CSPMU_EVENT_ATTR(local_dram_byte_wr, 0x124),
ARM_CSPMU_EVENT_ATTR(local_dram_byte_rd, 0x123),
ARM_CSPMU_EVENT_ATTR(mmio_access_wr, 0x132),
ARM_CSPMU_EVENT_ATTR(mmio_access_rd, 0x131),
ARM_CSPMU_EVENT_ATTR(mmio_byte_wr, 0x134),
ARM_CSPMU_EVENT_ATTR(mmio_byte_rd, 0x133),
ARM_CSPMU_EVENT_ATTR(mmio_outstanding_rd, 0x135),
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
NULL,
};
static struct attribute *display_pmu_event_attrs[] = {
ARM_CSPMU_EVENT_ATTR(rd_bytes_loc, 0x0),
ARM_CSPMU_EVENT_ATTR(rd_req_loc, 0x6),
ARM_CSPMU_EVENT_ATTR(rd_cum_outs_loc, 0xc),
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
NULL,
};
static struct attribute *ucf_gpu_pmu_event_attrs[] = {
ARM_CSPMU_EVENT_ATTR(rd_bytes_loc_rem, 0x0),
ARM_CSPMU_EVENT_ATTR(wr_bytes_loc, 0x2),
ARM_CSPMU_EVENT_ATTR(wr_bytes_rem, 0x3),
ARM_CSPMU_EVENT_ATTR(rd_req_loc_rem, 0x6),
ARM_CSPMU_EVENT_ATTR(wr_req_loc, 0x8),
ARM_CSPMU_EVENT_ATTR(wr_req_rem, 0x9),
ARM_CSPMU_EVENT_ATTR(rd_cum_outs_loc_rem, 0xc),
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
NULL,
};
static struct attribute *generic_pmu_event_attrs[] = {
ARM_CSPMU_EVENT_ATTR(cycles, ARM_CSPMU_EVT_CYCLES_DEFAULT),
NULL,
@@ -205,6 +288,54 @@ static struct attribute *cnvlink_pmu_format_attrs[] = {
NULL,
};
static struct attribute *ucf_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
ARM_CSPMU_FORMAT_ATTR(src_loc_noncpu, "config1:0"),
ARM_CSPMU_FORMAT_ATTR(src_loc_cpu, "config1:1"),
ARM_CSPMU_FORMAT_ATTR(src_rem, "config1:2"),
ARM_CSPMU_FORMAT_ATTR(dst_loc, "config1:3"),
ARM_CSPMU_FORMAT_ATTR(dst_rem, "config1:4"),
NULL,
};
static struct attribute *display_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
NULL,
};
static struct attribute *ucf_gpu_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
NULL,
};
static struct attribute *uphy_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
ARM_CSPMU_FORMAT_ATTR(pcie_rp_1, "config1:0"),
ARM_CSPMU_FORMAT_ATTR(pcie_rp_2, "config1:1"),
ARM_CSPMU_FORMAT_ATTR(pcie_rp_3, "config1:2"),
ARM_CSPMU_FORMAT_ATTR(pcie_rp_4, "config1:3"),
ARM_CSPMU_FORMAT_ATTR(pcie_rp_5, "config1:4"),
ARM_CSPMU_FORMAT_ATTR(xusb, "config1:5-10"),
ARM_CSPMU_FORMAT_ATTR(mgbe_0, "config1:11"),
ARM_CSPMU_FORMAT_ATTR(mgbe_1, "config1:12"),
ARM_CSPMU_FORMAT_ATTR(mgbe_2, "config1:13"),
ARM_CSPMU_FORMAT_ATTR(mgbe_3, "config1:14"),
ARM_CSPMU_FORMAT_ATTR(eqos, "config1:15"),
ARM_CSPMU_FORMAT_ATTR(ufs, "config1:16"),
NULL,
};
static struct attribute *vision_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
ARM_CSPMU_FORMAT_ATTR(vi_0, "config1:0-1"),
ARM_CSPMU_FORMAT_ATTR(vi_1, "config1:2-3"),
ARM_CSPMU_FORMAT_ATTR(isp_0, "config1:4-7"),
ARM_CSPMU_FORMAT_ATTR(isp_1, "config1:8-11"),
ARM_CSPMU_FORMAT_ATTR(vic, "config1:12-13"),
ARM_CSPMU_FORMAT_ATTR(pva, "config1:14-19"),
NULL,
};
static struct attribute *generic_pmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
ARM_CSPMU_FORMAT_FILTER_ATTR,
@@ -246,6 +377,43 @@ static u32 nv_cspmu_event_filter(const struct perf_event *event)
return event->attr.config1 & ctx->filter_mask;
}
/*
* UCF leakage workaround:
* Disables PMCR and PMCNTEN for each counter before running a
* dummy experiment. This clears the internal state and prevents
* event leakage from the previous experiment. PMCNTEN is then
* re-enabled.
*/
static void ucf_pmu_stop_counters_leakage(struct arm_cspmu *cspmu)
{
int reg_id;
u32 cntenclr_offset = PMCNTENCLR;
u32 cntenset_offset = PMCNTENSET;
struct nv_cspmu_ctx *ctx = to_nv_cspmu_ctx(cspmu);
/* Step 1: Disable PMCR.E */
writel(0, cspmu->base0 + PMCR);
/* Step 2: Clear PMCNTEN for all counters */
for (reg_id = 0; reg_id < cspmu->num_set_clr_reg; ++reg_id) {
ctx->pmcnten[reg_id] = readl(cspmu->base0 + cntenclr_offset);
writel(ctx->pmcnten[reg_id], cspmu->base0 + cntenclr_offset);
cntenclr_offset += sizeof(u32);
}
/* Step 3: Enable PMCR.E */
writel(PMCR_E, cspmu->base0 + PMCR);
/* Step 4: Disable PMCR.E */
writel(0, cspmu->base0 + PMCR);
/* Step 5: Enable back PMCNTEN for counters cleared in step 2 */
for (reg_id = 0; reg_id < cspmu->num_set_clr_reg; ++reg_id) {
writel(ctx->pmcnten[reg_id], cspmu->base0 + cntenset_offset);
cntenset_offset += sizeof(u32);
}
}
enum nv_cspmu_name_fmt {
NAME_FMT_GENERIC,
NAME_FMT_SOCKET
@@ -260,6 +428,7 @@ struct nv_cspmu_match {
enum nv_cspmu_name_fmt name_fmt;
struct attribute **event_attr;
struct attribute **format_attr;
void (*stop_counters)(struct arm_cspmu *cspmu);
};
static const struct nv_cspmu_match nv_cspmu_match[] = {
@@ -313,6 +482,57 @@ static const struct nv_cspmu_match nv_cspmu_match[] = {
.event_attr = scf_pmu_event_attrs,
.format_attr = scf_pmu_format_attrs
},
{
.prodid = 0x2CF10000,
.prodid_mask = NV_PRODID_MASK,
.filter_mask = NV_UCF_FILTER_ID_MASK,
.filter_default_val = NV_UCF_FILTER_ID_MASK,
.name_pattern = "nvidia_ucf_pmu_%u",
.name_fmt = NAME_FMT_SOCKET,
.event_attr = ucf_pmu_event_attrs,
.format_attr = ucf_pmu_format_attrs,
.stop_counters = ucf_pmu_stop_counters_leakage
},
{
.prodid = 0x10800000,
.prodid_mask = NV_PRODID_MASK,
.filter_mask = NV_UPHY_FILTER_ID_MASK,
.filter_default_val = NV_UPHY_FILTER_ID_MASK,
.name_pattern = "nvidia_uphy_pmu_%u",
.name_fmt = NAME_FMT_SOCKET,
.event_attr = mcf_pmu_event_attrs,
.format_attr = uphy_pmu_format_attrs
},
{
.prodid = 0x10a00000,
.prodid_mask = NV_PRODID_MASK,
.filter_mask = 0,
.filter_default_val = NV_UCF_GPU_FILTER_ID_MASK,
.name_pattern = "nvidia_ucf_gpu_pmu_%u",
.name_fmt = NAME_FMT_SOCKET,
.event_attr = ucf_gpu_pmu_event_attrs,
.format_attr = ucf_gpu_pmu_format_attrs
},
{
.prodid = 0x10d00000,
.prodid_mask = NV_PRODID_MASK,
.filter_mask = 0,
.filter_default_val = NV_DISPLAY_FILTER_ID_MASK,
.name_pattern = "nvidia_display_pmu_%u",
.name_fmt = NAME_FMT_SOCKET,
.event_attr = display_pmu_event_attrs,
.format_attr = display_pmu_format_attrs
},
{
.prodid = 0x10e00000,
.prodid_mask = NV_PRODID_MASK,
.filter_mask = NV_VISION_FILTER_ID_MASK,
.filter_default_val = NV_VISION_FILTER_ID_MASK,
.name_pattern = "nvidia_vision_pmu_%u",
.name_fmt = NAME_FMT_SOCKET,
.event_attr = mcf_pmu_event_attrs,
.format_attr = vision_pmu_format_attrs
},
{
.prodid = 0,
.prodid_mask = 0,
@@ -389,6 +609,13 @@ static int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
impl_ops->get_event_attrs = nv_cspmu_get_event_attrs;
impl_ops->get_format_attrs = nv_cspmu_get_format_attrs;
impl_ops->get_name = nv_cspmu_get_name;
if (match->stop_counters != NULL) {
ctx->pmcnten = devm_kzalloc(dev, cspmu->num_set_clr_reg *
sizeof(u32), GFP_KERNEL);
if (!ctx->pmcnten)
return -ENOMEM;
impl_ops->stop_counters = match->stop_counters;
}
return 0;
}

View File

@@ -16,7 +16,7 @@ menuconfig TEGRA_PLATFORM_DEVICES
if TEGRA_PLATFORM_DEVICES
config TEGRA_EPL
bool "Tegra Error Propagation Layer Driver"
tristate "Tegra Error Propagation Layer Driver"
depends on MAILBOX
help
The tegra-epl driver provides interface for reporting software detected

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2021-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// Copyright (c) 2021-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/module.h>
#include <linux/fs.h>
@@ -14,7 +14,7 @@
#include <linux/pm.h>
/* Timeout in milliseconds */
#define TIMEOUT 5U
#define TIMEOUT 13U
/* 32bit data Length */
#define MAX_LEN 4
@@ -22,12 +22,19 @@
/* Macro indicating total number of Misc Sw generic errors in Misc EC */
#define NUM_SW_GENERIC_ERR 5U
/* Error index offset in mission status register */
#define ERROR_INDEX_OFFSET 24U
/* signature code for HSP pm notify data */
#define PM_STATE_UNI_CODE 0xFDEF
/* Timestamp validation constants */
#define TIMESTAMP_CNT_PERIOD 0x100000000ULL /* 32-bit SoC TSC counter period (2^32) */
/* This value is derived from the DOS FDTI (100ms) - EPL propagation delay (10ms) */
#define TIMESTAMP_VALID_RANGE 90000000ULL /* 90ms in nanoseconds */
/* Timestamp resolution constants (in nanoseconds) */
#define TEGRA234_TIMESTAMP_RESOLUTION_NS 32U
#define TEGRA264_TIMESTAMP_RESOLUTION_NS 1U
/* State Management */
#define EPS_DOS_INIT 0U
#define EPS_DOS_SUSPEND 3U
@@ -60,6 +67,12 @@ struct epl_misc_sw_err_cfg {
const char *dev_configured;
};
/* Error index offset in mission status register */
static uint32_t error_index_offset = 3;
/* Timestamp resolution for current SoC (in nanoseconds) */
static uint32_t timestamp_resolution_ns = TEGRA264_TIMESTAMP_RESOLUTION_NS;
static int device_file_major_number;
static const char device_name[] = "epdaemon";
@@ -80,6 +93,21 @@ static uint32_t handshake_retry_count;
static bool enable_deinit_notify;
/* Helper function to SoC TSC timestamp */
static inline uint32_t epl_get_current_timestamp(void)
{
uint32_t timestamp;
asm volatile("mrs %0, cntvct_el0" : "=r" (timestamp));
return timestamp;
}
/* Helper function to convert SoC TSC timestamp ticks to nanoseconds */
static inline uint64_t epl_ticks_to_ns(uint64_t ticks)
{
return ticks * timestamp_resolution_ns;
}
static void tegra_hsp_tx_empty_notify(struct mbox_client *cl,
void *data, int empty_value)
{
@@ -113,21 +141,21 @@ static int tegra_hsp_mb_init(struct device *dev)
static ssize_t device_file_ioctl(
struct file *fp, unsigned int cmd, unsigned long arg)
{
uint32_t lData[MAX_LEN];
struct epl_error_report_frame error_frame;
int ret;
if (copy_from_user(lData, (void __user *)arg,
MAX_LEN * sizeof(uint32_t)))
/* Validate input parameters */
if (!arg)
return -EINVAL;
if (copy_from_user(&error_frame, (void __user *)arg,
sizeof(error_frame)))
return -EACCES;
switch (cmd) {
case EPL_REPORT_ERROR_CMD:
if (hs_state == HANDSHAKE_DONE)
ret = mbox_send_message(epl_hsp_v->tx.chan, (void *) lData);
else
ret = -ENODEV;
ret = epl_report_error(error_frame);
break;
default:
return -EINVAL;
@@ -148,12 +176,16 @@ int epl_get_misc_ec_err_status(struct device *dev, uint8_t err_number, bool *sta
if (miscerr_cfg[err_number].dev_configured == NULL || isAddrMappOk == false)
return -ENODEV;
/* Validate mission error status register mapping */
if (!mission_err_status_va)
return -ENODEV;
dev_str = dev_driver_string(dev);
if (strcmp(dev_str, miscerr_cfg[err_number].dev_configured) != 0)
return -EACCES;
mask = (1U << ((ERROR_INDEX_OFFSET + err_number) % 32U));
mask = (1U << ((error_index_offset + err_number) % 32U));
mission_err_status = readl(mission_err_status_va);
if ((mission_err_status & mask) != 0U)
@@ -182,6 +214,10 @@ int epl_report_misc_ec_error(struct device *dev, uint8_t err_number,
if (status == false)
return -EAGAIN;
/* Validate register mappings before writing */
if (!miscerr_cfg[err_number].err_code_va || !miscerr_cfg[err_number].err_assert_va)
return -ENODEV;
/* Updating error code */
writel(sw_error_code, miscerr_cfg[err_number].err_code_va);
@@ -195,9 +231,39 @@ EXPORT_SYMBOL_GPL(epl_report_misc_ec_error);
int epl_report_error(struct epl_error_report_frame error_report)
{
int ret = -EINVAL;
uint64_t current_timestamp_64;
uint64_t reported_timestamp_64;
if (epl_hsp_v == NULL || hs_state != HANDSHAKE_DONE) {
/* Validate input parameters */
if (epl_hsp_v == NULL || hs_state != HANDSHAKE_DONE)
return -ENODEV;
/* Validate HSP channel */
if (!epl_hsp_v->tx.chan)
return -ENODEV;
/* Plausibility check for timestamp - only if timestamp is not zero */
if (error_report.timestamp != 0) {
/* Get current timestamp (32-bit LSB) and convert to 64-bit for calculations */
current_timestamp_64 = (uint64_t)epl_get_current_timestamp();
reported_timestamp_64 = (uint64_t)error_report.timestamp;
/* Check for timestamp overflow */
/* If current timestamp is less than reported timestamp, assume overflow occurred */
if (current_timestamp_64 < reported_timestamp_64)
current_timestamp_64 += TIMESTAMP_CNT_PERIOD;
/* Validate timestamp range - reject if difference is more than ~90ms */
/* Convert 90ms to counter ticks based on current resolution */
uint64_t valid_range_ticks = TIMESTAMP_VALID_RANGE / timestamp_resolution_ns;
if ((current_timestamp_64 - reported_timestamp_64) > valid_range_ticks) {
dev_warn(&epl_hsp_v->dev, "epl_report_error: Invalid timestamp - difference %llu ticks (%llu ns) exceeds valid range (%llu ticks)\n",
current_timestamp_64 - reported_timestamp_64,
epl_ticks_to_ns(current_timestamp_64 - reported_timestamp_64),
valid_range_ticks);
return -EINVAL;
}
}
ret = mbox_send_message(epl_hsp_v->tx.chan, (void *)&error_report);
@@ -211,12 +277,16 @@ static int epl_client_fsi_pm_notify(u32 state)
int ret;
u32 pdata[4];
/* Validate state parameter */
if (state > EPS_DOS_UNKNOWN)
return -EINVAL;
pdata[0] = PM_STATE_UNI_CODE;
pdata[1] = state;
pdata[2] = state;
pdata[3] = PM_STATE_UNI_CODE;
if (hs_state == HANDSHAKE_DONE)
if (hs_state == HANDSHAKE_DONE && epl_hsp_v && epl_hsp_v->tx.chan)
ret = mbox_send_message(epl_hsp_v->tx.chan, (void *) pdata);
else
ret = -ENODEV;
@@ -228,7 +298,7 @@ static int epl_client_fsi_handshake(void *arg)
{
uint8_t count = 0;
if (epl_hsp_v) {
if (epl_hsp_v && epl_hsp_v->tx.chan) {
int ret;
const uint32_t handshake_data[] = {0x45504C48, 0x414E4453, 0x48414B45,
0x44415441};
@@ -244,12 +314,15 @@ static int epl_client_fsi_handshake(void *arg)
break;
}
} while (count < handshake_retry_count);
} else {
hs_state = HANDSHAKE_FAILED;
dev_warn(&pdev_local->dev, "epl_client: handshake failed - no valid HSP channel\n");
}
if (hs_state == HANDSHAKE_FAILED)
pr_warn("epl_client: handshake with FSI failed\n");
dev_warn(&pdev_local->dev, "epl_client: handshake with FSI failed\n");
else
pr_info("epl_client: handshake done with FSI, try %u\n", count);
dev_info(&pdev_local->dev, "epl_client: handshake done with FSI, try %u\n", count);
return 0;
}
@@ -257,10 +330,14 @@ static int epl_client_fsi_handshake(void *arg)
static int __maybe_unused epl_client_suspend(struct device *dev)
{
int ret = 0;
pr_debug("tegra-epl: suspend called\n");
if (enable_deinit_notify)
dev_dbg(dev, "tegra-epl: suspend called\n");
if (enable_deinit_notify) {
ret = epl_client_fsi_pm_notify(EPS_DOS_SUSPEND);
if (ret < 0)
dev_warn(dev, "tegra-epl: suspend notification failed: %d\n", ret);
}
hs_state = HANDSHAKE_PENDING;
return ret;
@@ -268,15 +345,32 @@ static int __maybe_unused epl_client_suspend(struct device *dev)
static int __maybe_unused epl_client_resume(struct device *dev)
{
pr_debug("tegra-epl: resume called\n");
int ret;
(void)epl_client_fsi_handshake(NULL);
return epl_client_fsi_pm_notify(EPS_DOS_RESUME);
dev_dbg(dev, "tegra-epl: resume called\n");
ret = epl_client_fsi_handshake(NULL);
if (ret < 0) {
dev_warn(dev, "tegra-epl: handshake failed during resume: %d\n", ret);
return ret;
}
/* Only send PM notify if handshake was successful */
if (hs_state == HANDSHAKE_DONE) {
ret = epl_client_fsi_pm_notify(EPS_DOS_RESUME);
if (ret < 0)
dev_warn(dev, "tegra-epl: resume notification failed: %d\n", ret);
} else {
dev_warn(dev, "tegra-epl: skipping resume notification - handshake not successful\n");
}
return ret;
}
static SIMPLE_DEV_PM_OPS(epl_client_pm, epl_client_suspend, epl_client_resume);
static const struct of_device_id epl_client_dt_match[] = {
{ .compatible = "nvidia,tegra234-epl-client"},
{ .compatible = "nvidia,tegra264-epl-client"},
{}
};
@@ -299,6 +393,7 @@ static int epl_register_device(void)
return result;
}
device_file_major_number = result;
dev_class = class_create(device_name);
if (dev_class == NULL) {
pr_err("%s> Could not create class for device\n", device_name);
@@ -333,18 +428,30 @@ static int epl_client_probe(struct platform_device *pdev)
const struct device_node *np = dev->of_node;
int iterator = 0;
char name[32] = "client-misc-sw-generic-err";
bool is_misc_ec_mapped = false;
hs_state = HANDSHAKE_PENDING;
epl_register_device();
ret = epl_register_device();
if (ret < 0) {
dev_err(dev, "Failed to register device: %d\n", ret);
return ret;
}
ret = tegra_hsp_mb_init(dev);
if (ret < 0) {
dev_err(dev, "Failed to initialize HSP mailbox: %d\n", ret);
epl_unregister_device();
return ret;
}
pdev_local = pdev;
for (iterator = 0; iterator < NUM_SW_GENERIC_ERR; iterator++) {
name[26] = (char)(iterator+48U);
name[27] = '\0';
if (of_property_read_string(np, name, &miscerr_cfg[iterator].dev_configured) == 0) {
pr_info("Misc Sw Generic Err #%d configured to client %s\n",
dev_info(dev, "Misc Sw Generic Err #%d configured to client %s\n",
iterator, miscerr_cfg[iterator].dev_configured);
/* Mapping registers to process address space */
@@ -359,9 +466,12 @@ static int epl_client_probe(struct platform_device *pdev)
ret = -1;
dev_err(&pdev->dev, "error in mapping misc err register for err #%d\n",
iterator);
} else {
is_misc_ec_mapped = true;
}
} else {
pr_info("Misc Sw Generic Err %d not configured for any client\n", iterator);
dev_info(dev, "Misc Sw Generic Err %d not configured for any client\n",
iterator);
}
}
@@ -374,16 +484,41 @@ static int epl_client_probe(struct platform_device *pdev)
dev_info(dev, "handshake-retry-count %u\n", handshake_retry_count);
mission_err_status_va = devm_platform_ioremap_resource(pdev, NUM_SW_GENERIC_ERR * 2);
if (IS_ERR(mission_err_status_va)) {
isAddrMappOk = false;
dev_err(&pdev->dev, "error in mapping mission error status register\n");
return PTR_ERR(mission_err_status_va);
if (of_device_is_compatible(np, "nvidia,tegra234-epl-client")) {
error_index_offset = 24;
timestamp_resolution_ns = TEGRA234_TIMESTAMP_RESOLUTION_NS;
} else if (of_device_is_compatible(np, "nvidia,tegra264-epl-client")) {
error_index_offset = 3;
timestamp_resolution_ns = TEGRA264_TIMESTAMP_RESOLUTION_NS;
} else {
dev_err(dev, "tegra-epl: valid dt compatible string not found\n");
ret = -1;
}
if (is_misc_ec_mapped == true) {
mission_err_status_va = devm_platform_ioremap_resource(pdev, NUM_SW_GENERIC_ERR * 2);
if (IS_ERR(mission_err_status_va)) {
isAddrMappOk = false;
dev_err(&pdev->dev, "error in mapping mission error status register\n");
return PTR_ERR(mission_err_status_va);
}
}
if (ret == 0) {
(void) epl_client_fsi_handshake(NULL);
return epl_client_fsi_pm_notify(EPS_DOS_INIT);
ret = epl_client_fsi_handshake(NULL);
if (ret < 0) {
dev_warn(dev, "tegra-epl: handshake failed during probe: %d\n", ret);
return ret;
}
/* Only send PM notify if handshake was successful */
if (hs_state == HANDSHAKE_DONE) {
ret = epl_client_fsi_pm_notify(EPS_DOS_INIT);
if (ret < 0)
dev_warn(dev, "tegra-epl: init notification failed: %d\n", ret);
} else {
dev_warn(dev, "tegra-epl: skipping init notification - handshake not successful\n");
}
}
return ret;
@@ -391,11 +526,15 @@ static int epl_client_probe(struct platform_device *pdev)
static void epl_client_shutdown(struct platform_device *pdev)
{
pr_debug("tegra-epl: shutdown called\n");
int ret;
if (enable_deinit_notify)
if (epl_client_fsi_pm_notify(EPS_DOS_DEINIT) < 0)
pr_err("Unable to send notification to fsi\n");
dev_dbg(&pdev->dev, "tegra-epl: shutdown called\n");
if (enable_deinit_notify) {
ret = epl_client_fsi_pm_notify(EPS_DOS_DEINIT);
if (ret < 0)
dev_err(&pdev->dev, "Unable to send notification to fsi: %d\n", ret);
}
hs_state = HANDSHAKE_PENDING;

View File

@@ -77,6 +77,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_opp.h>
@@ -147,7 +148,7 @@ static inline void pwm_writel_mask32(struct tegra_pwm_chip *pc,
}
static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
u64 duty_ns, u64 period_ns)
{
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
unsigned long channel_o = pc->soc->channel_offset;
@@ -157,6 +158,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long scale_s = pc->soc->scale_shift;
unsigned long required_clk_rate;
u32 pwm_f, pfm_f;
u64 val;
int err;
/*
@@ -170,8 +172,11 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* per (1 + pc->pwm_depth) cycles and make sure to round to the
* nearest integer during division.
*/
pwm_f = (u32)DIV_ROUND_CLOSEST_ULL(duty_ns * (1 + pc->pwm_depth),
period_ns);
val = mul_u64_u64_div_u64(duty_ns, 1 + pc->pwm_depth, period_ns);
if (val > U32_MAX)
return -EINVAL;
pwm_f = (u32)val;
/* Avoid overflow on 100% duty cycle */
if (pwm_f == 1 + pc->pwm_depth)
@@ -182,8 +187,11 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* required_clk_rate is a reference rate for source clock and
* it is derived based on user requested period.
*/
required_clk_rate = DIV_ROUND_UP_ULL(
(u64)NSEC_PER_SEC * (1 + pc->pwm_depth), period_ns);
val = mul_u64_u64_div_u64(NSEC_PER_SEC, 1 + pc->pwm_depth, period_ns);
if (val > U32_MAX)
return -EINVAL;
required_clk_rate = (u32)val;
pc->clk_rate = clk_get_rate(pc->clk);
if (pc->clk_rate < required_clk_rate)
return -EINVAL;

View File

@@ -1491,6 +1491,10 @@ static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev)
{
struct tegra234_cbb *cbb = dev_get_drvdata(dev);
/* set ERD bit to mask SError and generate interrupt to report error */
if (cbb->fabric->off_mask_erd)
tegra234_cbb_mask_serror(cbb);
tegra234_cbb_error_enable(&cbb->base);
dev_dbg(dev, "%s resumed\n", cbb->fabric->fab_list[cbb->fabric->fab_id].name);

View File

@@ -186,6 +186,29 @@
#define TEGRA_SMC_PMC_READ 0xaa
#define TEGRA_SMC_PMC_WRITE 0xbb
/* Bit field definitions for scratch registers */
#define BR_FAIL_BITMAP_MASK 0xF
#define BR_FAIL_BITMAP_SHIFT 0
#define BR_ACTIVE_CHAIN_MASK 0x3
#define BR_ACTIVE_CHAIN_SHIFT 4
#define BOOT_CHAIN_STATUS_A_MASK 0x1
#define BOOT_CHAIN_STATUS_A_SHIFT 7
#define BOOT_CHAIN_STATUS_B_MASK 0x1
#define BOOT_CHAIN_STATUS_B_SHIFT 8
#define BOOT_CHAIN_CURRENT_MASK 0x3
#define BOOT_CHAIN_CURRENT_SHIFT 11
#define LAST_BOOT_CHAIN_FAILED_MASK 0x1
#define LAST_BOOT_CHAIN_FAILED_SHIFT 13
#define ROOTFS_SR_MAGIC_MASK 0xFFFF
#define ROOTFS_SR_MAGIC_SHIFT 0
#define ROOTFS_CURRENT_MASK 0x3
#define ROOTFS_CURRENT_SHIFT 16
#define ROOTFS_RETRY_COUNT_B_MASK 0x3
#define ROOTFS_RETRY_COUNT_B_SHIFT 18
#define ROOTFS_RETRY_COUNT_A_MASK 0x3
#define ROOTFS_RETRY_COUNT_A_SHIFT 20
struct pmc_clk {
struct clk_hw hw;
unsigned long offs;
@@ -2095,6 +2118,156 @@ void tegra_pmc_reset_sysfs_remove(struct tegra_pmc *pmc)
device_remove_file(dev, &dev_attr_reset_level);
}
/* Helper macros for scratch sysfs attributes */
#define TEGRA_PMC_SCRATCH_ATTR_RW(name, reg_field, mask, shift, min_val, max_val) \
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct tegra_pmc *pmc = dev_get_drvdata(dev); \
u32 value; \
if (!pmc->scratch || !pmc->soc->regs->reg_field) \
return -ENODEV; \
value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->reg_field); \
value = (value >> shift) & mask; \
return sprintf(buf, "%u\n", value); \
} \
static ssize_t name##_store(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct tegra_pmc *pmc = dev_get_drvdata(dev); \
unsigned long value; \
u32 reg_val; \
int ret; \
if (!pmc->scratch || !pmc->soc->regs->reg_field) \
return -ENODEV; \
ret = kstrtoul(buf, 0, &value); \
if (ret) \
return ret; \
if (value < min_val || value > max_val) \
return -EINVAL; \
reg_val = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->reg_field); \
reg_val &= ~(mask << shift); \
reg_val |= (value & mask) << shift; \
tegra_pmc_scratch_writel(pmc, reg_val, pmc->soc->regs->reg_field); \
return count; \
} \
static DEVICE_ATTR_RW(name)
#define TEGRA_PMC_SCRATCH_ATTR_RO(name, reg_field, mask, shift) \
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct tegra_pmc *pmc = dev_get_drvdata(dev); \
u32 value; \
if (!pmc->scratch || !pmc->soc->regs->reg_field) \
return -ENODEV; \
value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->reg_field); \
value = (value >> shift) & mask; \
return sprintf(buf, "%u\n", value); \
} \
static DEVICE_ATTR_RO(name)
/* Scratch register sysfs attributes */
TEGRA_PMC_SCRATCH_ATTR_RW(br_fail_bitmap, scratch_l0_1_0,
BR_FAIL_BITMAP_MASK, BR_FAIL_BITMAP_SHIFT, 0, 0xF);
TEGRA_PMC_SCRATCH_ATTR_RW(br_active_chain, scratch_l0_1_0,
BR_ACTIVE_CHAIN_MASK, BR_ACTIVE_CHAIN_SHIFT, 0, 3);
TEGRA_PMC_SCRATCH_ATTR_RW(boot_chain_status_a, scratch_l0_1_0,
BOOT_CHAIN_STATUS_A_MASK, BOOT_CHAIN_STATUS_A_SHIFT, 0, 1);
TEGRA_PMC_SCRATCH_ATTR_RW(boot_chain_status_b, scratch_l0_1_0,
BOOT_CHAIN_STATUS_B_MASK, BOOT_CHAIN_STATUS_B_SHIFT, 0, 1);
TEGRA_PMC_SCRATCH_ATTR_RO(boot_chain_current, scratch_l0_1_0,
BOOT_CHAIN_CURRENT_MASK, BOOT_CHAIN_CURRENT_SHIFT);
TEGRA_PMC_SCRATCH_ATTR_RO(last_boot_chain_failed, scratch_l0_1_0,
LAST_BOOT_CHAIN_FAILED_MASK, LAST_BOOT_CHAIN_FAILED_SHIFT);
TEGRA_PMC_SCRATCH_ATTR_RW(rootfs_sr_magic, scratch_l0_21_0,
ROOTFS_SR_MAGIC_MASK, ROOTFS_SR_MAGIC_SHIFT, 0, 0xFFFF);
TEGRA_PMC_SCRATCH_ATTR_RW(rootfs_current, scratch_l0_21_0,
ROOTFS_CURRENT_MASK, ROOTFS_CURRENT_SHIFT, 0, 1);
TEGRA_PMC_SCRATCH_ATTR_RW(rootfs_retry_count_b, scratch_l0_21_0,
ROOTFS_RETRY_COUNT_B_MASK, ROOTFS_RETRY_COUNT_B_SHIFT, 0, 3);
TEGRA_PMC_SCRATCH_ATTR_RW(rootfs_retry_count_a, scratch_l0_21_0,
ROOTFS_RETRY_COUNT_A_MASK, ROOTFS_RETRY_COUNT_A_SHIFT, 0, 3);
void tegra_pmc_scratch_sysfs_init(struct tegra_pmc *pmc)
{
struct device *dev = pmc->dev;
int err;
if (!pmc->scratch) {
dev_warn(dev, "scratch registers not available, skipping sysfs init\n");
return;
}
/* Only create attributes if the register fields are defined */
if (pmc->soc->regs->scratch_l0_1_0) {
err = device_create_file(dev, &dev_attr_br_fail_bitmap);
if (err)
dev_warn(dev, "failed to create br_fail_bitmap sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_br_active_chain);
if (err)
dev_warn(dev, "failed to create br_active_chain sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_boot_chain_status_a);
if (err)
dev_warn(dev, "failed to create boot_chain_status_a sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_boot_chain_status_b);
if (err)
dev_warn(dev, "failed to create boot_chain_status_b sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_boot_chain_current);
if (err)
dev_warn(dev, "failed to create boot_chain_current sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_last_boot_chain_failed);
if (err)
dev_warn(dev, "failed to create last_boot_chain_failed sysfs: %d\n", err);
}
if (pmc->soc->regs->scratch_l0_21_0) {
err = device_create_file(dev, &dev_attr_rootfs_sr_magic);
if (err)
dev_warn(dev, "failed to create rootfs_sr_magic sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_rootfs_current);
if (err)
dev_warn(dev, "failed to create rootfs_current sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_rootfs_retry_count_b);
if (err)
dev_warn(dev, "failed to create rootfs_retry_count_b sysfs: %d\n", err);
err = device_create_file(dev, &dev_attr_rootfs_retry_count_a);
if (err)
dev_warn(dev, "failed to create rootfs_retry_count_a sysfs: %d\n", err);
}
}
void tegra_pmc_scratch_sysfs_remove(struct tegra_pmc *pmc)
{
struct device *dev = pmc->dev;
if (!pmc->scratch)
return;
if (pmc->soc->regs->scratch_l0_1_0) {
device_remove_file(dev, &dev_attr_br_fail_bitmap);
device_remove_file(dev, &dev_attr_br_active_chain);
device_remove_file(dev, &dev_attr_boot_chain_status_a);
device_remove_file(dev, &dev_attr_boot_chain_status_b);
device_remove_file(dev, &dev_attr_boot_chain_current);
device_remove_file(dev, &dev_attr_last_boot_chain_failed);
}
if (pmc->soc->regs->scratch_l0_21_0) {
device_remove_file(dev, &dev_attr_rootfs_sr_magic);
device_remove_file(dev, &dev_attr_rootfs_current);
device_remove_file(dev, &dev_attr_rootfs_retry_count_b);
device_remove_file(dev, &dev_attr_rootfs_retry_count_a);
}
}
static int tegra_pmc_irq_translate(struct irq_domain *domain,
struct irq_fwspec *fwspec,
unsigned long *hwirq,

View File

@@ -30,7 +30,6 @@
#include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h>
#include <dt-bindings/gpio/tegra264-gpio.h>
static int tegra_pmc_reboot_notify(struct notifier_block *this,
unsigned long action, void *data)
{
@@ -117,6 +116,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
pmc->soc = of_device_get_match_data(&pdev->dev);
pmc->dev = &pdev->dev;
mutex_init(&pmc->powergates_lock);
/* take over the memory region from the early initialization */
pmc->base = devm_platform_ioremap_resource(pdev, 0);
@@ -160,15 +160,13 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
}
tegra_pmc_reset_sysfs_init(pmc);
err = tegra_pmc_pinctrl_init(pmc);
if (err)
goto cleanup_sysfs;
return err;
err = tegra_pmc_irq_init(pmc);
if (err < 0)
goto cleanup_sysfs;
return err;
/* Some wakes require specific filter configuration */
if (pmc->soc->set_wake_filters)
@@ -176,12 +174,18 @@ static int tegra_pmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pmc);
tegra_pmc_scratch_sysfs_init(pmc);
return 0;
}
cleanup_sysfs:
tegra_pmc_reset_sysfs_remove(pmc);
static int tegra_pmc_remove(struct platform_device *pdev)
{
struct tegra_pmc *pmc = platform_get_drvdata(pdev);
return err;
tegra_pmc_scratch_sysfs_remove(pmc);
return 0;
}
static int __maybe_unused tegra_pmc_resume(struct device *dev)
@@ -204,10 +208,6 @@ static const struct dev_pm_ops tegra_pmc_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pmc_suspend, tegra_pmc_resume)
};
static const char * const tegra264_reset_levels[] = {
"L0", "L1", "L2", "WARM"
};
#define TEGRA264_IO_PAD(_id, _dpd, _request, _status, _has_int_reg, _e_reg06, _e_reg18, _voltage, _e_33v_ctl, _name) \
((struct tegra_io_pad_soc) { \
.id = (_id), \
@@ -262,11 +262,8 @@ static const struct pinctrl_pin_desc tegra264_pin_descs[] = {
static const struct tegra_pmc_regs tegra264_pmc_regs = {
.scratch0 = 0x684,
.rst_status = 0x4,
.rst_source_shift = 0x2,
.rst_source_mask = 0x1fc,
.rst_level_shift = 0x0,
.rst_level_mask = 0x3,
.scratch_l0_1_0 = 0x67C,
.scratch_l0_21_0 = 0x6cc,
.aowake_cntrl = 0x0,
.aowake_mask_w = 0x200,
.aowake_status_w = 0x410,
@@ -278,102 +275,12 @@ static const struct tegra_pmc_regs tegra264_pmc_regs = {
.aowake_ctrl = 0x68c,
};
static const char * const tegra264_reset_sources[] = {
"SYS_RESET_N", /* 0 */
"CSDC_RTC_XTAL",
"VREFRO_POWER_BAD",
"SCPM_SOC_XTAL",
"SCPM_RTC_XTAL",
"FMON_32K",
"FMON_OSC",
"POD_RTC",
"POD_IO",
"POD_PLUS_IO_SPLL",
"POD_PLUS_SOC", /* 10 */
"VMON_PLUS_UV",
"VMON_PLUS_OV",
"FUSECRC_FAULT",
"OSC_FAULT",
"BPMP_BOOT_FAULT",
"SCPM_BPMP_CORE_CLK",
"SCPM_PSC_SE_CLK",
"VMON_SOC_MIN",
"VMON_SOC_MAX",
"VMON_MSS_MIN", /* 20 */
"VMON_MSS_MAX",
"POD_PLUS_IO_U4_TSENSE",
"SOC_THERM_FAULT",
"FSI_THERM_FAULT",
"PSC_TURTLE_MODE",
"SCPM_OESP_SE_CLK",
"SCPM_SB_SE_CLK",
"POD_CPU",
"POD_GPU",
"DCLS_GPU", /* 30 */
"POD_MSS",
"FSI_FMON",
"VMON_FSI_MIN",
"VMON_FSI_MAX",
"VMON_CPU_MIN",
"VMON_CPU_MAX",
"NVJTAG_SEL_MONITOR",
"BPMP_FMON",
"AO_WDT_POR",
"BPMP_WDT_POR", /* 40 */
"AO_TKE_WDT_POR",
"RCE0_WDT_POR",
"RCE1_WDT_POR",
"DCE_WDT_POR",
"PVA_0_WDT_POR",
"FSI_R5_WDT_POR",
"FSI_R52_0_WDT_POR",
"FSI_R52_1_WDT_POR",
"FSI_R52_2_WDT_POR",
"FSI_R52_3_WDT_POR", /* 50 */
"TOP_0_WDT_POR",
"TOP_1_WDT_POR",
"TOP_2_WDT_POR",
"APE_C0_WDT_POR",
"APE_C1_WDT_POR",
"GPU_TKE_WDT_POR",
"OESP_WDT_POR",
"SB_WDT_POR",
"PSC_WDT_POR",
"SW_MAIN", /* 60 */
"L0L1_RST_OUT_N",
"FSI_HSM",
"CSITE_SW",
"AO_WDT_DBG",
"BPMP_WDT_DBG",
"AO_TKE_WDT_DBG",
"RCE0_WDT_DBG",
"RCE1_WDT_DBG",
"DCE_WDT_DBG",
"PVA_0_WDT_DBG", /* 70 */
"FSI_R5_WDT_DBG",
"FSI_R52_0_WDT_DBG",
"FSI_R52_1_WDT_DBG",
"FSI_R52_2_WDT_DBG",
"FSI_R52_3_WDT_DBG",
"TOP_0_WDT_DBG",
"TOP_1_WDT_DBG",
"TOP_2_WDT_DBG",
"APE_C0_WDT_DBG",
"APE_C1_WDT_DBG", /* 80 */
"SB_WDT_DBG",
"OESP_WDT_DBG",
"PSC_WDT_DBG",
"TSC_0_WDT_DBG",
"TSC_1_WDT_DBG",
"L2_RST_OUT_N",
"SC7", /* 87 */
};
static const struct tegra_wake_event tegra264_wake_events[] = {
TEGRA_WAKE_IRQ("pmu", 0, 727),
TEGRA_WAKE_IRQ("rtc", 65, 548),
TEGRA_WAKE_IRQ("usb3_port_0", 79, 965),
TEGRA_WAKE_IRQ("usb3_port_1", 80, 965),
TEGRA_WAKE_IRQ("usb3_port_2", 81, 965),
TEGRA_WAKE_IRQ("usb3_port_3", 82, 965),
TEGRA_WAKE_IRQ("usb2_port_0", 83, 965),
TEGRA_WAKE_IRQ("usb2_port_1", 84, 965),
@@ -403,10 +310,10 @@ static const struct tegra_pmc_soc tegra264_pmc_soc = {
.set_wake_filters = tegra186_pmc_set_wake_filters,
.irq_set_wake = tegra186_pmc_irq_set_wake,
.irq_set_type = tegra186_pmc_irq_set_type,
.reset_sources = tegra264_reset_sources,
.num_reset_sources = ARRAY_SIZE(tegra264_reset_sources),
.reset_levels = tegra264_reset_levels,
.num_reset_levels = ARRAY_SIZE(tegra264_reset_levels),
.reset_sources = NULL,
.num_reset_sources = 0,
.reset_levels = NULL,
.num_reset_levels = 0,
.num_wake_events = ARRAY_SIZE(tegra264_wake_events),
.wake_events = tegra264_wake_events,
.max_wake_events = 128,
@@ -430,6 +337,7 @@ static struct platform_driver tegra_pmc_driver = {
.pm = &tegra_pmc_pm_ops,
},
.probe = tegra_pmc_probe,
.remove = tegra_pmc_remove,
};
builtin_platform_driver(tegra_pmc_driver);

View File

@@ -2,7 +2,7 @@
/*
* NVIDIA Tegra xHCI host controller driver
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2014-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* Copyright (C) 2014 Google, Inc.
*/
@@ -181,6 +181,8 @@ enum build_info_log {
LOG_MEMORY
};
#define WAKE_IRQ_START_INDEX 2
struct tegra_xusb_fw_header {
__le32 boot_loadaddr_in_imem;
__le32 boot_codedfi_offset;
@@ -257,7 +259,7 @@ struct tegra_xusb_soc {
unsigned int num_supplies;
const struct tegra_xusb_phy_type *phy_types;
unsigned int num_types;
unsigned int num_wakes;
unsigned int max_num_wakes;
const struct tegra_xusb_context_soc *context;
struct {
@@ -380,6 +382,7 @@ struct tegra_xusb {
atomic_t hub_ctrl_use_cnt;
struct notifier_block padctl_nb;
int num_wakes;
};
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
@@ -2168,33 +2171,42 @@ static int tegra_xhci_padctl_notify(struct notifier_block *nb,
static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xusb *tegra)
{
int i;
unsigned int i;
if (device_property_read_bool(tegra->dev, "disable-wake"))
if (tegra->soc->max_num_wakes == 0)
return 0;
tegra->wake_irqs = devm_kcalloc(tegra->dev,
tegra->soc->num_wakes,
sizeof(*tegra->wake_irqs), GFP_KERNEL);
tegra->soc->max_num_wakes,
sizeof(*tegra->wake_irqs), GFP_KERNEL);
if (!tegra->wake_irqs)
return -ENOMEM;
for (i = 0; i < tegra->soc->num_wakes; i++) {
char irq_name[] = "wakeX";
/*
* USB wake events are independent of each other, so it is not necessary for a platform
* to utilize all wake-up events supported for a given device. The USB host can operate
* even if wake-up events are not defined or fail to be configured. Therefore, we only
* return critical errors, such as -ENOMEM.
*/
for (i = 0; i < tegra->soc->max_num_wakes; i++) {
struct irq_data *data;
snprintf(irq_name, sizeof(irq_name), "wake%d", i);
tegra->wake_irqs[i] = platform_get_irq_byname(pdev, irq_name);
tegra->wake_irqs[i] = platform_get_irq_optional(pdev, i + WAKE_IRQ_START_INDEX);
if (tegra->wake_irqs[i] < 0)
continue;
break;
data = irq_get_irq_data(tegra->wake_irqs[i]);
if (!data) {
irq_dispose_mapping(tegra->wake_irqs[i]);
tegra->wake_irqs[i] = -ENXIO;
continue;
dev_warn(tegra->dev, "get wake event %d irq data fail\n", i);
break;
}
irq_set_irq_type(tegra->wake_irqs[i], irqd_get_trigger_type(data));
}
tegra->num_wakes = i;
dev_dbg(tegra->dev, "setup %d wake events\n", tegra->num_wakes);
return 0;
}
@@ -2253,16 +2265,14 @@ static int tegra_xusb_probe(struct platform_device *pdev)
return tegra->mbox_irq;
}
err = tegra_xusb_setup_wakeup(pdev, tegra);
if (err)
return err;
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
if (IS_ERR(tegra->padctl))
return PTR_ERR(tegra->padctl);
if (tegra->soc->num_wakes && !tegra->soc->is_xhci_vf) {
err = tegra_xusb_setup_wakeup(pdev, tegra);
if (err)
goto put_padctl;
}
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
if (!np) {
err = -ENODEV;
@@ -2628,7 +2638,6 @@ static void tegra_xusb_remove(struct platform_device *pdev)
{
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
unsigned int i;
if (tegra->soc->is_xhci_vf)
tegra_xusb_padctl_event_unregister(tegra->padctl, &tegra->padctl_nb);
@@ -2651,10 +2660,6 @@ static void tegra_xusb_remove(struct platform_device *pdev)
if (tegra->padctl_irq)
pm_runtime_disable(&pdev->dev);
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
if (tegra->wake_irqs[i] >= 0)
irq_dispose_mapping(tegra->wake_irqs[i]);
pm_runtime_put(&pdev->dev);
tegra_xusb_disable(tegra);
@@ -3130,9 +3135,9 @@ out:
if (enable_irq_wake(tegra->padctl_irq))
dev_err(dev, "failed to enable padctl wakes\n");
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
if (tegra->wake_irqs[i] >= 0)
enable_irq_wake(tegra->wake_irqs[i]);
for (i = 0; i < tegra->num_wakes; i++)
enable_irq_wake(tegra->wake_irqs[i]);
}
}
@@ -3164,9 +3169,9 @@ static __maybe_unused int tegra_xusb_resume(struct device *dev)
if (disable_irq_wake(tegra->padctl_irq))
dev_err(dev, "failed to disable padctl wakes\n");
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
if (tegra->wake_irqs[i] >= 0)
disable_irq_wake(tegra->wake_irqs[i]);
for (i = 0; i < tegra->num_wakes; i++)
disable_irq_wake(tegra->wake_irqs[i]);
}
tegra->suspended = false;
mutex_unlock(&tegra->lock);
@@ -3422,7 +3427,7 @@ static const struct tegra_xusb_soc tegra234_soc = {
.num_supplies = ARRAY_SIZE(tegra194_supply_names),
.phy_types = tegra194_phy_types,
.num_types = ARRAY_SIZE(tegra194_phy_types),
.num_wakes = 7,
.max_num_wakes = 7,
.context = &tegra186_xusb_context,
.ports = {
.usb3 = { .offset = 0, .count = 4, },
@@ -3497,7 +3502,7 @@ static const struct tegra_xusb_soc tegra264_soc = {
.num_supplies = ARRAY_SIZE(tegra194_supply_names),
.phy_types = tegra194_phy_types,
.num_types = ARRAY_SIZE(tegra194_phy_types),
.num_wakes = 8,
.max_num_wakes = 8,
.context = &tegra186_xusb_context,
.ports = {
.usb3 = { .offset = 0, .count = 4, },

View File

@@ -25,7 +25,7 @@ struct epl_error_report_frame {
uint16_t reporter_id;
};
#ifdef CONFIG_TEGRA_EPL
#if IS_ENABLED(CONFIG_TEGRA_EPL)
/**
* @brief API to check if SW error can be reported via Misc EC
* by reading and checking Misc EC error status register value.

View File

@@ -167,6 +167,8 @@ struct tegra_io_pad_soc {
struct tegra_pmc_regs {
unsigned int scratch0;
unsigned int scratch_l0_1_0;
unsigned int scratch_l0_21_0;
unsigned int rst_status;
unsigned int rst_source_shift;
unsigned int rst_source_mask;
@@ -402,6 +404,9 @@ int tegra186_pmc_suspend(struct tegra_pmc *pmc);
void tegra_pmc_reset_sysfs_init(struct tegra_pmc *pmc);
void tegra_pmc_reset_sysfs_remove(struct tegra_pmc *pmc);
void tegra_pmc_scratch_sysfs_init(struct tegra_pmc *pmc);
void tegra_pmc_scratch_sysfs_remove(struct tegra_pmc *pmc);
int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc);
int tegra_pmc_init(struct tegra_pmc *pmc);

View File

@@ -1437,6 +1437,15 @@ void run_posix_cpu_timers(void)
lockdep_assert_irqs_disabled();
/*
* Ensure that release_task(tsk) can't happen while
* handle_posix_cpu_timers() is running. Otherwise, a concurrent
* posix_cpu_timer_del() may fail to lock_task_sighand(tsk) and
* miss timer->it.cpu.firing != 0.
*/
if (tsk->exit_state)
return;
/*
* If the actual expiry is deferred to task work context and the
* work is already scheduled there is no point to do anything here.