blob: 85fcbfad1e677431cf0548925d71dc7e6d6b88a7 [file] [log] [blame]
/*
* arch/arm/plat-ambarella/misc/service.c
*
* Author: Cao Rongrong <rrcao@ambarella.com>
*
* Copyright (C) 2012-2016, Ambarella, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <plat/iav_helper.h>
#include <mach/init.h>
/*===========================================================================*/
static LIST_HEAD(ambarella_svc_list);
int ambarella_register_service(struct ambarella_service *amb_svc)
{
struct ambarella_service *svc;
if (!amb_svc || !amb_svc->func)
return -EINVAL;
list_for_each_entry(svc, &ambarella_svc_list, node) {
if (svc->service == amb_svc->service) {
pr_err("%s: service (%d) is already existed\n",
__func__, amb_svc->service);
return -EEXIST;
}
}
list_add_tail(&amb_svc->node, &ambarella_svc_list);
return 0;
}
EXPORT_SYMBOL(ambarella_register_service);
int ambarella_unregister_service(struct ambarella_service *amb_svc)
{
struct ambarella_service *svc;
if (!amb_svc)
return -EINVAL;
list_for_each_entry(svc, &ambarella_svc_list, node) {
if (svc->service == amb_svc->service) {
list_del(&svc->node);
break;
}
}
return 0;
}
EXPORT_SYMBOL(ambarella_unregister_service);
int ambarella_request_service(int service, void *arg, void *result)
{
struct ambarella_service *svc;
int found = 0;
list_for_each_entry(svc, &ambarella_svc_list, node) {
if (svc->service == service) {
found = 1;
break;
}
}
if (found == 0) {
pr_err("%s: no such service (%d)\n", __func__, service);
return -ENODEV;
}
return svc->func(arg, result);
}
EXPORT_SYMBOL(ambarella_request_service);
/*===========================================================================*/
#define CACHE_LINE_SIZE 32
#define CACHE_LINE_MASK ~(CACHE_LINE_SIZE - 1)
void ambcache_clean_all(void *addr, unsigned int size)
{
u32 vstart;
u32 vend;
#ifdef CONFIG_OUTER_CACHE
u32 pstart;
#endif
//u32 addr_tmp;
u32 set, way;
u32 reg_data;
u32 sscidr = 0;
u32 value_set = 0,value_way = 0;
vstart = (u32)addr & CACHE_LINE_MASK;
vend = ((u32)addr + size + CACHE_LINE_SIZE - 1) & CACHE_LINE_MASK;
#ifdef CONFIG_OUTER_CACHE
pstart = ambarella_virt_to_phys(vstart);
#endif
/*format of SSCIDR*/
/* |31 |30 |29|28 |27-13 |12-3 |2-0 |*/
/* |WT|WB|RA|WA|NumSets|Ways|Linsize|*/
asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (sscidr));
value_set = ((sscidr & 0x0FFFE000)>>13);
value_way =((sscidr & 0x00001FF8)>>3);
//printk("\033[31m[%s][%d] cache SSCIDR (0x%x), Set(%d),Way(%d)\033[0m\n",__FILE__,__LINE__,sscidr,value_set,value_way);
/* Cache level 1 */
for (way = 0; way <= value_way; way++) {
/* Way, bits[31:30], the number of the way to operate on. */
for (set = 0; set <=value_set; set++) {
/* Set, bits[13:6], the number of the set to operate on. */
/* clean cache lines in all ways */
reg_data = (way << 30) | (set << 5);
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 2"
:
: "r" (reg_data)
: );
}
}
dsb();
#ifdef CONFIG_OUTER_CACHE
outer_clean_range(pstart, (pstart + size));
#endif
}
void ambcache_clean_range(void *addr, unsigned int size)
{
u32 vstart;
u32 vend;
#ifdef CONFIG_OUTER_CACHE
u32 pstart;
#endif
u32 addr_tmp;
vstart = (u32)addr & CACHE_LINE_MASK;
vend = ((u32)addr + size + CACHE_LINE_SIZE - 1) & CACHE_LINE_MASK;
#ifdef CONFIG_OUTER_CACHE
pstart = ambarella_virt_to_phys(vstart);
#endif
for (addr_tmp = vstart; addr_tmp < vend; addr_tmp += CACHE_LINE_SIZE) {
__asm__ __volatile__ (
"mcr p15, 0, %0, c7, c10, 1" : : "r" (addr_tmp));
}
dsb();
#ifdef CONFIG_OUTER_CACHE
outer_clean_range(pstart, (pstart + size));
#endif
}
EXPORT_SYMBOL(ambcache_clean_range);
EXPORT_SYMBOL(ambcache_clean_all);
void ambcache_inv_range(void *addr, unsigned int size)
{
u32 vstart;
u32 vend;
#ifdef CONFIG_OUTER_CACHE
u32 pstart;
#endif
u32 addr_tmp;
vstart = (u32)addr & CACHE_LINE_MASK;
vend = ((u32)addr + size + CACHE_LINE_SIZE - 1) & CACHE_LINE_MASK;
#ifdef CONFIG_OUTER_CACHE
pstart = ambarella_virt_to_phys(vstart);
outer_inv_range(pstart, (pstart + size));
#endif
for (addr_tmp = vstart; addr_tmp < vend; addr_tmp += CACHE_LINE_SIZE) {
__asm__ __volatile__ (
"mcr p15, 0, %0, c7, c6, 1" : : "r" (addr_tmp));
}
dsb();
#ifdef CONFIG_OUTER_CACHE
outer_inv_range(pstart, (pstart + size));
for (addr_tmp = vstart; addr_tmp < vend; addr_tmp += CACHE_LINE_SIZE) {
__asm__ __volatile__ (
"mcr p15, 0, %0, c7, c6, 1" : : "r" (addr_tmp));
}
dsb();
#endif
}
EXPORT_SYMBOL(ambcache_inv_range);