Files
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

577 lines
14 KiB
C

/*
* Copyright (C) 2015 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <asm/cacheflush.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/topology.h>
#ifdef CONFIG_MTK_CACHE_FLUSH_BY_SF
#include <mtk_secure_api.h>
#endif
#include "cmo-mtk.h"
void __attribute__((weak)) mt_fiq_cache_flush_all(void) {}
/*
* inner_dcache_flush_all: Flush (clean + invalidate) the entire L1 data cache.
*
* This can be used ONLY by the M4U driver!!
* Other drivers should NOT use this function at all!!
* Others should use DMA-mapping APIs!!
*
* After calling the function, the buffer should not be touched anymore.
* And the M4U driver should then call outer_flush_all() immediately.
* Here is the example:
* // Cannot touch the buffer from here.
* inner_dcache_flush_all();
* outer_flush_all();
* // Can touch the buffer from here.
* If preemption occurs and the driver cannot guarantee that no other
* process will touch the buffer,
* the driver should use LOCK to protect this code segment.
*/
void inner_dcache_flush_all(void)
{
__inner_flush_dcache_all();
}
EXPORT_SYMBOL(inner_dcache_flush_all);
void inner_dcache_flush_L1(void)
{
__inner_flush_dcache_L1();
}
void inner_dcache_flush_L2(void)
{
__inner_flush_dcache_L2();
}
void inner_dcache_disable(void)
{
__disable_dcache();
}
#ifdef CONFIG_MTK_CACHE_FLUSH_BY_SF
static DEFINE_SPINLOCK(cache_lock);
static void mt_cache_flush_by_sf(void)
{
unsigned long flags;
spin_lock_irqsave(&cache_lock, flags);
trace_printk("[snoop] starts\n");
mt_secure_call(MTK_SIP_KERNEL_CACHE_FLUSH_BY_SF, 0, 0, 0, 0);
trace_printk("[snoop] done\n");
spin_unlock_irqrestore(&cache_lock, flags);
}
#endif
/*
* smp_inner_dcache_flush_all: Flush (clean + invalidate)
* the entire L1 data cache.
*
* This can be used ONLY by the M4U driver!!
* Other drivers should NOT use this function at all!!
* Others should use DMA-mapping APIs!!
*
* This is the smp version of inner_dcache_flush_all().
* It will use IPI to do flush on all CPUs.
* Must not call this function with disabled interrupts or from a
* hardware interrupt handler or from a bottom half handler.
*/
void smp_inner_dcache_flush_all(void)
{
#if defined(CONFIG_MTK_FIQ_CACHE)
mt_fiq_cache_flush_all();
return;
#elif defined(CONFIG_MTK_CACHE_FLUSH_BY_SF)
mt_cache_flush_by_sf();
return;
#else
int i, total_core, cid, last_cid;
struct cpumask mask;
if (in_interrupt())
return;
get_online_cpus();
preempt_disable();
/* Find first online cpu in each cluster */
last_cid = -1;
cpumask_clear(&mask);
total_core = num_possible_cpus();
for (i = 0; i < total_core; i++) {
if (!cpu_online(i))
continue;
cid = arch_get_cluster_id(i);
if (last_cid != cid) {
cpumask_set_cpu(i, &mask);
last_cid = cid;
}
}
on_each_cpu((smp_call_func_t)inner_dcache_flush_L1, NULL, true);
smp_call_function_many(&mask, (smp_call_func_t)inner_dcache_flush_L2,
NULL, true);
/*
* smp_call_function_many only run on "other Cpus".
* Flush L2 here if this is one of the first cores
*/
if (cpumask_test_cpu(smp_processor_id(), &mask))
inner_dcache_flush_L2();
preempt_enable();
put_online_cpus();
#endif
}
EXPORT_SYMBOL(smp_inner_dcache_flush_all);
#ifdef CONFIG_MTK_CACHE_FLUSH_RANGE_PARALLEL
#include <asm/spinlock.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/scatterlist.h>
#include <linux/sizes.h>
#include <linux/vmalloc.h>
#ifdef PERF_MEASURE
unsigned long raw_range;
unsigned long raw_setway;
#endif
int (*ion_sync_kernel_func)(unsigned long start, size_t size,
unsigned int sync_type);
struct smp_sync_sg_list_arg {
struct sg_table *table;
unsigned int sync_type;
int ret_value;
int npages;
};
struct vm_struct *cache_flush_vm_struct[NR_CPUS];
static void *cache_flush_map_page_va(struct vm_struct *vm, struct page *page)
{
int ret;
struct page **ppPage = &page;
ret = map_vm_area(vm, PAGE_KERNEL, ppPage);
if (ret) {
pr_err("error to map page (err %d)\n", ret);
return NULL;
}
return vm->addr;
}
static void cache_flush_unmap_page_va(struct vm_struct *vm)
{
unmap_kernel_range((unsigned long) vm->addr, PAGE_SIZE);
}
/* must be involked after we got hotplug.lock */
static void _smp_inner_dcache_flush_all(void)
{
int i, j, num_core, total_core, online_cpu;
struct cpumask mask;
#ifdef PERF_MEASURE
struct timespec time_stamp0, time_stamp1;
#endif
preempt_disable();
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp0);
#endif
on_each_cpu((smp_call_func_t) inner_dcache_flush_L1, NULL, true);
num_core = get_cluster_core_count();
total_core = num_possible_cpus();
for (i = 0; i < total_core; i += num_core) {
cpumask_clear(&mask);
for (j = i; j < (i + num_core); j++) {
/* check the online status, then set bit */
if (cpu_online(j))
cpumask_set_cpu(j, &mask);
}
online_cpu = cpumask_first_and(cpu_online_mask, &mask);
smp_call_function_single(online_cpu,
(smp_call_func_t) inner_dcache_flush_L2,
NULL, true);
}
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp1);
raw_setway = 1000000000 * (time_stamp1.tv_sec - time_stamp0.tv_sec) +
(time_stamp1.tv_nsec - time_stamp0.tv_nsec);
#endif
preempt_enable();
}
enum {
FAILED = 0,
STARTING,
FLUSHING,
WAITING_FOR_OTHERS,
};
static struct scatterlist *sg_sync;
static int f_state;
spinlock_t sg_flush_lock;
spinlock_t smp_cache_flush_lock;
atomic_t nr_done_pages;
void _flush_sg_list(struct smp_sync_sg_list_arg *args)
{
unsigned long start = -1;
unsigned int this_cpu = smp_processor_id();
unsigned int sync_type = args->sync_type;
int npages_this_entry, j;
struct sg_table *table = (struct sg_table *) args->table;
struct page *page;
npages_this_entry = -1;
page = NULL;
do {
/* update sg_sync and f_state */
spin_lock(&sg_flush_lock);
switch (f_state) {
case STARTING:
/* get the first scatter list */
sg_sync = (table->sgl);
/* update state */
f_state = FLUSHING;
break;
case FLUSHING:
sg_sync = sg_next(sg_sync);
break;
default:
break;
}
if (sg_sync != NULL) {
npages_this_entry = PAGE_ALIGN(sg_sync->length) /
PAGE_SIZE;
page = sg_page(sg_sync);
} else {
/* sg_sync == NULL when reaching the end of sg list */
if (f_state != FAILED)
f_state = WAITING_FOR_OTHERS;
}
spin_unlock(&sg_flush_lock);
switch (f_state) {
case FLUSHING:
if (npages_this_entry == -1 && page == NULL)
break;
/* flush pages */
for (j = 0; j < npages_this_entry; j++) {
start = (unsigned long)
cache_flush_map_page_va(
cache_flush_vm_struct[this_cpu],
page++);
if (unlikely(IS_ERR_OR_NULL((void *) start))) {
pr_err("cannot do cache sync: ret=%lu\n",
start);
args->ret_value = -EFAULT;
spin_lock(&sg_flush_lock);
f_state = FAILED;
spin_unlock(&sg_flush_lock);
return;
}
if (likely(ion_sync_kernel_func))
ion_sync_kernel_func(start,
PAGE_SIZE,
sync_type);
else
pr_err("ion_sync_kernel_func is NULL\n");
cache_flush_unmap_page_va(
cache_flush_vm_struct[this_cpu]);
}
/* atomic increment */
atomic_add(npages_this_entry, &nr_done_pages);
break;
default:
break;
}
} while (f_state == FLUSHING);
}
int smp_sync_cache_by_cpu_pool_ion(struct sg_table *table,
unsigned int sync_type, int npages)
{
struct smp_sync_sg_list_arg smp_call_args;
struct cpumask mask;
unsigned long flags, cpu = 0, largest_affinity_cpu = -1, this_cpu;
#ifdef PERF_MEASURE
struct timespec time_stamp0, time_stamp1;
#endif
spin_lock(&smp_cache_flush_lock);
preempt_disable();
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp0);
#endif
atomic_set(&nr_done_pages, 0);
spin_lock_irqsave(&sg_flush_lock, flags);
f_state = STARTING;
spin_unlock_irqrestore(&sg_flush_lock, flags);
smp_call_args.table = table;
smp_call_args.npages = npages;
smp_call_args.sync_type = sync_type;
smp_call_args.ret_value = 0;
/**
* mask = {cpu_online_mask} -
* {the other online cpus with largest affinity}
*/
this_cpu = smp_processor_id();
cpumask_copy(&mask, cpu_online_mask);
for_each_cpu(cpu, &mask)
if (cpu != this_cpu)
largest_affinity_cpu = cpu;
if (largest_affinity_cpu != -1)
cpumask_clear_cpu(largest_affinity_cpu, &mask);
on_each_cpu_mask(&mask, (smp_call_func_t) _flush_sg_list,
(void *)&smp_call_args, false);
/* this cpu waits for the flush complete before returns */
while ((atomic_read(&nr_done_pages) < npages) && (f_state != FAILED))
;
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp1);
raw_range = 1000000000 * (time_stamp1.tv_sec - time_stamp0.tv_sec) +
(time_stamp1.tv_nsec - time_stamp0.tv_nsec);
#endif
preempt_enable();
spin_unlock(&smp_cache_flush_lock);
return smp_call_args.ret_value;
}
int mt_smp_cache_flush(struct sg_table *table, unsigned int sync_type,
int npages)
{
int ret = -1;
bool get_lock = false;
long timeout = CACHE_FLUSH_TIMEOUT;
if (in_interrupt())
return -EFAULT;
/* trylock for timeout nsec */
while (!(get_lock = try_get_online_cpus()) && (timeout-- > 0))
udelay(1);
if (get_lock) {
/* call flush all by set/way if we got lock */
_smp_inner_dcache_flush_all();
put_online_cpus();
return CACHE_FLUSH_BY_SETWAY;
}
ret = smp_sync_cache_by_cpu_pool_ion(table, sync_type, npages);
return (ret >= 0) ? CACHE_FLUSH_BY_MVA : ret;
}
EXPORT_SYMBOL(mt_smp_cache_flush);
/* === m4u version === */
#include <asm/cacheflush.h>
struct smp_sync_range_arg {
unsigned int sync_type;
unsigned long va_start;
unsigned long va_end;
unsigned long size;
int ret_value;
};
#define NR_PAGES_PER_FLUSH 2
atomic_t done_range;
unsigned long flush_range_head;
void _flush_range(struct smp_sync_range_arg *args)
{
unsigned long start = 0;
unsigned long size = 0;
do {
/* update sg_sync and f_state */
spin_lock(&sg_flush_lock);
switch (f_state) {
case STARTING:
/* update state */
f_state = FLUSHING;
break;
case FLUSHING:
start = flush_range_head;
if (flush_range_head +
(NR_PAGES_PER_FLUSH * PAGE_SIZE) > args->va_end) {
size = (NR_PAGES_PER_FLUSH * PAGE_SIZE);
flush_range_head += (NR_PAGES_PER_FLUSH *
PAGE_SIZE);
} else {
size = args->va_end - flush_range_head;
flush_range_head = args->va_end + 1;
if (f_state != FAILED)
f_state = WAITING_FOR_OTHERS;
}
break;
default:
break;
}
spin_unlock(&sg_flush_lock);
switch (f_state) {
case FLUSHING:
if ((size == 0) || (start <= args->va_start) ||
(start > args->va_end))
break;
#ifdef CONFIG_ARM64
__dma_flush_range((void *)start, (void *) start + size);
#else
dma_flush_range((void *)start, (void *) start + size);
#endif
/* atomic increment */
atomic_add(size, &done_range);
break;
default:
break;
}
} while (f_state == FLUSHING);
}
int smp_sync_cache_by_cpu_pool_m4u(const void *va, const unsigned long size)
{
struct smp_sync_range_arg smp_call_args;
struct cpumask mask;
unsigned long flags, cpu = 0, largest_affinity_cpu = -1, this_cpu;
#ifdef PERF_MEASURE
struct timespec time_stamp0, time_stamp1;
#endif
spin_lock(&smp_cache_flush_lock);
preempt_disable();
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp0);
#endif
atomic_set(&done_range, 0);
spin_lock_irqsave(&sg_flush_lock, flags);
f_state = STARTING;
spin_unlock_irqrestore(&sg_flush_lock, flags);
smp_call_args.va_start = (unsigned long)va;
smp_call_args.va_end = (unsigned long)va + size - 1;
smp_call_args.size = size;
smp_call_args.ret_value = 0;
/**
* mask = {cpu_online_mask} -
* {the other online cpus with largest affinity}
*/
this_cpu = smp_processor_id();
cpumask_copy(&mask, cpu_online_mask);
for_each_cpu(cpu, &mask)
if (cpu != this_cpu)
largest_affinity_cpu = cpu;
if (largest_affinity_cpu != -1)
cpumask_clear_cpu(largest_affinity_cpu, &mask);
on_each_cpu_mask(&mask, (smp_call_func_t) _flush_range,
(void *)&smp_call_args, false);
/* this cpu waits for the flush complete before returns */
while ((atomic_read(&done_range) < size) && (f_state != FAILED))
;
#ifdef PERF_MEASURE
getnstimeofday(&time_stamp1);
raw_range = 1000000000 * (time_stamp1.tv_sec - time_stamp0.tv_sec) +
(time_stamp1.tv_nsec - time_stamp0.tv_nsec);
#endif
preempt_enable();
spin_unlock(&smp_cache_flush_lock);
return smp_call_args.ret_value;
}
int mt_smp_cache_flush_m4u(const void *va, const unsigned long size)
{
int ret = -1;
bool get_lock = false;
long timeout = CACHE_FLUSH_TIMEOUT;
if (in_interrupt()) {
pr_err("Cannot invoke mt_smp_cache_flush() in interrupt/softirq context\n");
return -EFAULT;
}
/* trylock for timeout nsec */
while (!(get_lock = try_get_online_cpus()) && (timeout-- > 0))
udelay(1);
if (get_lock) {
/* call flush all by set/way if we got lock */
_smp_inner_dcache_flush_all();
put_online_cpus();
return CACHE_FLUSH_BY_SETWAY;
}
ret = smp_sync_cache_by_cpu_pool_m4u(va, size);
return (ret >= 0) ? CACHE_FLUSH_BY_MVA : ret;
}
EXPORT_SYMBOL(mt_smp_cache_flush_m4u);
static int __init cache_flush_module(void)
{
int i;
/* initialize vm_struct for each cpu to map/unmap vm */
for (i = 0; i <= num_possible_cpus()-1; ++i) {
cache_flush_vm_struct[i] = get_vm_area(PAGE_SIZE, VM_ALLOC);
WARN_ON(!cache_flush_vm_struct[i]);
}
spin_lock_init(&smp_cache_flush_lock);
spin_lock_init(&sg_flush_lock);
return 0;
}
module_init(cache_flush_module);
#endif