| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
| /* |
| * |
| * (C) COPYRIGHT 2014-2021 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU license. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| */ |
| |
| #include <linux/bitops.h> |
| #include <mali_kbase.h> |
| #include <mali_kbase_mem.h> |
| #include <mmu/mali_kbase_mmu_hw.h> |
| #include <tl/mali_kbase_tracepoints.h> |
| #include <device/mali_kbase_device.h> |
| |
| /** |
| * lock_region() - Generate lockaddr to lock memory region in MMU |
| * @pfn: Starting page frame number of the region to lock |
| * @num_pages: Number of pages to lock. It must be greater than 0. |
| * @lockaddr: Address and size of memory region to lock |
| * |
| * The lockaddr value is a combination of the starting address and |
| * the size of the region that encompasses all the memory pages to lock. |
| * |
| * The size is expressed as a logarithm: it is represented in a way |
| * that is compatible with the HW specification and it also determines |
| * how many of the lowest bits of the address are cleared. |
| * |
| * Return: 0 if success, or an error code on failure. |
| */ |
| static int lock_region(u64 pfn, u32 num_pages, u64 *lockaddr) |
| { |
| const u64 lockaddr_base = pfn << PAGE_SHIFT; |
| u64 lockaddr_size_log2, region_frame_number_start, |
| region_frame_number_end; |
| |
| if (num_pages == 0) |
| return -EINVAL; |
| |
| /* The size is expressed as a logarithm and should take into account |
| * the possibility that some pages might spill into the next region. |
| */ |
| lockaddr_size_log2 = fls(num_pages) + PAGE_SHIFT - 1; |
| |
| /* Round up if the number of pages is not a power of 2. */ |
| if (num_pages != ((u32)1 << (lockaddr_size_log2 - PAGE_SHIFT))) |
| lockaddr_size_log2 += 1; |
| |
| /* Round up if some memory pages spill into the next region. */ |
| region_frame_number_start = pfn >> (lockaddr_size_log2 - PAGE_SHIFT); |
| region_frame_number_end = |
| (pfn + num_pages - 1) >> (lockaddr_size_log2 - PAGE_SHIFT); |
| |
| if (region_frame_number_start < region_frame_number_end) |
| lockaddr_size_log2 += 1; |
| |
| /* Represent the size according to the HW specification. */ |
| lockaddr_size_log2 = MAX(lockaddr_size_log2, |
| KBASE_LOCK_REGION_MIN_SIZE_LOG2); |
| |
| if (lockaddr_size_log2 > KBASE_LOCK_REGION_MAX_SIZE_LOG2) |
| return -EINVAL; |
| |
| /* The lowest bits are cleared and then set to size - 1 to represent |
| * the size in a way that is compatible with the HW specification. |
| */ |
| *lockaddr = lockaddr_base & ~((1ull << lockaddr_size_log2) - 1); |
| *lockaddr |= lockaddr_size_log2 - 1; |
| |
| return 0; |
| } |
| |
| static int wait_ready(struct kbase_device *kbdev, |
| unsigned int as_nr) |
| { |
| unsigned int max_loops = KBASE_AS_INACTIVE_MAX_LOOPS; |
| u32 val = kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS)); |
| |
| /* Wait for the MMU status to indicate there is no active command, in |
| * case one is pending. Do not log remaining register accesses. |
| */ |
| while (--max_loops && (val & AS_STATUS_AS_ACTIVE)) |
| val = kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS)); |
| |
| if (max_loops == 0) { |
| dev_err(kbdev->dev, "AS_ACTIVE bit stuck, might be caused by slow/unstable GPU clock or possible faulty FPGA connector\n"); |
| return -1; |
| } |
| |
| /* If waiting in loop was performed, log last read value. */ |
| if (KBASE_AS_INACTIVE_MAX_LOOPS - 1 > max_loops) |
| kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS)); |
| |
| return 0; |
| } |
| |
| static int write_cmd(struct kbase_device *kbdev, int as_nr, u32 cmd) |
| { |
| int status; |
| |
| /* write AS_COMMAND when MMU is ready to accept another command */ |
| status = wait_ready(kbdev, as_nr); |
| if (status == 0) |
| kbase_reg_write(kbdev, MMU_AS_REG(as_nr, AS_COMMAND), cmd); |
| |
| return status; |
| } |
| |
| void kbase_mmu_hw_configure(struct kbase_device *kbdev, struct kbase_as *as) |
| { |
| struct kbase_mmu_setup *current_setup = &as->current_setup; |
| u64 transcfg = 0; |
| |
| transcfg = current_setup->transcfg; |
| |
| /* Set flag AS_TRANSCFG_PTW_MEMATTR_WRITE_BACK |
| * Clear PTW_MEMATTR bits |
| */ |
| transcfg &= ~AS_TRANSCFG_PTW_MEMATTR_MASK; |
| /* Enable correct PTW_MEMATTR bits */ |
| transcfg |= AS_TRANSCFG_PTW_MEMATTR_WRITE_BACK; |
| /* Ensure page-tables reads use read-allocate cache-policy in |
| * the L2 |
| */ |
| transcfg |= AS_TRANSCFG_R_ALLOCATE; |
| |
| if (kbdev->system_coherency != COHERENCY_NONE) { |
| /* Set flag AS_TRANSCFG_PTW_SH_OS (outer shareable) |
| * Clear PTW_SH bits |
| */ |
| transcfg = (transcfg & ~AS_TRANSCFG_PTW_SH_MASK); |
| /* Enable correct PTW_SH bits */ |
| transcfg = (transcfg | AS_TRANSCFG_PTW_SH_OS); |
| } |
| |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSCFG_LO), |
| transcfg); |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSCFG_HI), |
| (transcfg >> 32) & 0xFFFFFFFFUL); |
| |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSTAB_LO), |
| current_setup->transtab & 0xFFFFFFFFUL); |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSTAB_HI), |
| (current_setup->transtab >> 32) & 0xFFFFFFFFUL); |
| |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_MEMATTR_LO), |
| current_setup->memattr & 0xFFFFFFFFUL); |
| kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_MEMATTR_HI), |
| (current_setup->memattr >> 32) & 0xFFFFFFFFUL); |
| |
| KBASE_TLSTREAM_TL_ATTRIB_AS_CONFIG(kbdev, as, |
| current_setup->transtab, |
| current_setup->memattr, |
| transcfg); |
| |
| write_cmd(kbdev, as->number, AS_COMMAND_UPDATE); |
| } |
| |
| int kbase_mmu_hw_do_operation(struct kbase_device *kbdev, struct kbase_as *as, |
| u64 vpfn, u32 nr, u32 op, |
| unsigned int handling_irq) |
| { |
| int ret; |
| |
| lockdep_assert_held(&kbdev->mmu_hw_mutex); |
| |
| if (op == AS_COMMAND_UNLOCK) { |
| /* Unlock doesn't require a lock first */ |
| ret = write_cmd(kbdev, as->number, AS_COMMAND_UNLOCK); |
| } else { |
| u64 lock_addr; |
| |
| ret = lock_region(vpfn, nr, &lock_addr); |
| |
| if (!ret) { |
| /* Lock the region that needs to be updated */ |
| kbase_reg_write(kbdev, |
| MMU_AS_REG(as->number, AS_LOCKADDR_LO), |
| lock_addr & 0xFFFFFFFFUL); |
| kbase_reg_write(kbdev, |
| MMU_AS_REG(as->number, AS_LOCKADDR_HI), |
| (lock_addr >> 32) & 0xFFFFFFFFUL); |
| write_cmd(kbdev, as->number, AS_COMMAND_LOCK); |
| |
| /* Run the MMU operation */ |
| write_cmd(kbdev, as->number, op); |
| |
| /* Wait for the flush to complete */ |
| ret = wait_ready(kbdev, as->number); |
| } |
| } |
| |
| return ret; |
| } |
| |
| void kbase_mmu_hw_clear_fault(struct kbase_device *kbdev, struct kbase_as *as, |
| enum kbase_mmu_fault_type type) |
| { |
| unsigned long flags; |
| u32 pf_bf_mask; |
| |
| spin_lock_irqsave(&kbdev->mmu_mask_change, flags); |
| |
| /* |
| * A reset is in-flight and we're flushing the IRQ + bottom half |
| * so don't update anything as it could race with the reset code. |
| */ |
| if (kbdev->irq_reset_flush) |
| goto unlock; |
| |
| /* Clear the page (and bus fault IRQ as well in case one occurred) */ |
| pf_bf_mask = MMU_PAGE_FAULT(as->number); |
| #if !MALI_USE_CSF |
| if (type == KBASE_MMU_FAULT_TYPE_BUS || |
| type == KBASE_MMU_FAULT_TYPE_BUS_UNEXPECTED) |
| pf_bf_mask |= MMU_BUS_ERROR(as->number); |
| #endif |
| kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_CLEAR), pf_bf_mask); |
| |
| unlock: |
| spin_unlock_irqrestore(&kbdev->mmu_mask_change, flags); |
| } |
| |
| void kbase_mmu_hw_enable_fault(struct kbase_device *kbdev, struct kbase_as *as, |
| enum kbase_mmu_fault_type type) |
| { |
| unsigned long flags; |
| u32 irq_mask; |
| |
| /* Enable the page fault IRQ |
| * (and bus fault IRQ as well in case one occurred) |
| */ |
| spin_lock_irqsave(&kbdev->mmu_mask_change, flags); |
| |
| /* |
| * A reset is in-flight and we're flushing the IRQ + bottom half |
| * so don't update anything as it could race with the reset code. |
| */ |
| if (kbdev->irq_reset_flush) |
| goto unlock; |
| |
| irq_mask = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_MASK)) | |
| MMU_PAGE_FAULT(as->number); |
| |
| #if !MALI_USE_CSF |
| if (type == KBASE_MMU_FAULT_TYPE_BUS || |
| type == KBASE_MMU_FAULT_TYPE_BUS_UNEXPECTED) |
| irq_mask |= MMU_BUS_ERROR(as->number); |
| #endif |
| kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_MASK), irq_mask); |
| |
| unlock: |
| spin_unlock_irqrestore(&kbdev->mmu_mask_change, flags); |
| } |