894 lines
19 KiB
C
894 lines
19 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
//#define DEBUG
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/string.h>
|
|
#include <linux/amlogic/arm-smccc.h>
|
|
#include <linux/psci.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/mutex.h>
|
|
#include <uapi/linux/psci.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/amlogic/freertos.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/kthread.h>
|
|
|
|
#define AML_RTOS_NAME "freertos"
|
|
|
|
#define SIP_FREERTOS_FID 0x8200004B
|
|
#define RTOS_REQUEST_INFO 0
|
|
#define RTOS_ALLOW_COREUP 1
|
|
#define SMC_UNK -1
|
|
#define RSVED_MAX_IRQ 1024
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
#define RTOS_RUN_FLAG 0xA5A5A5A5
|
|
#endif
|
|
|
|
#define LOGBUF_RDFLG 0x80000000
|
|
struct logbuf_t {
|
|
u32 write;
|
|
u32 read;
|
|
char buf[];
|
|
};
|
|
|
|
static unsigned long rtosinfo_phy;
|
|
static struct xrtosinfo_t *rtosinfo;
|
|
static int freertos_finished;
|
|
static DEFINE_MUTEX(freertos_lock);
|
|
static DEFINE_SPINLOCK(freertos_chk_lock);
|
|
static int ipi_rcv, freertos_disabled;
|
|
static DECLARE_BITMAP(freertos_irqrsv_mask, RSVED_MAX_IRQ);
|
|
static int freertos_irqrsv_inited;
|
|
static struct dentry *rtos_debug_dir;
|
|
static u32 logbuf_len;
|
|
static struct logbuf_t *logbuf;
|
|
static struct dentry *rtos_logbuf_file;
|
|
static DEFINE_MUTEX(freertos_logbuf_lock);
|
|
|
|
static int aml_rtos_logbuf_save(void);
|
|
|
|
static unsigned long long rtos_time_limit = 5000000000;
|
|
static unsigned long rtos_log_save;
|
|
static int rtos_param(char *buf)
|
|
{
|
|
if (!buf)
|
|
return -EINVAL;
|
|
|
|
if (kstrtoul(buf, 0, &rtos_log_save)) {
|
|
pr_err("invalid input:%s\n", buf);
|
|
return 0;
|
|
}
|
|
|
|
pr_debug("%s, buf:%s, rtos_log_save:%ld\n", __func__, buf, rtos_log_save);
|
|
|
|
return 1;
|
|
}
|
|
__setup("rtos_log_save=", rtos_param);
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_NOFITIER)
|
|
static BLOCKING_NOTIFIER_HEAD(rtos_nofitier_chain);
|
|
|
|
int register_freertos_notifier(struct notifier_block *nb)
|
|
{
|
|
int err;
|
|
|
|
err = blocking_notifier_chain_register(&rtos_nofitier_chain, nb);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(register_freertos_notifier);
|
|
|
|
int unregister_freertos_notifier(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_unregister(&rtos_nofitier_chain, nb);
|
|
}
|
|
EXPORT_SYMBOL(unregister_freertos_notifier);
|
|
|
|
int call_freertos_notifiers(unsigned long val, void *v)
|
|
{
|
|
return blocking_notifier_call_chain(&rtos_nofitier_chain, val, v);
|
|
}
|
|
EXPORT_SYMBOL_GPL(call_freertos_notifiers);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_MEMORY_FREE)
|
|
static int free_rtos_memory(void *data)
|
|
{
|
|
struct device_node *rtos_reserved;
|
|
struct reserved_mem *rmem;
|
|
|
|
rtos_reserved = of_find_compatible_node(NULL, NULL, "amlogic, aml_rtos_memory");
|
|
if (!rtos_reserved) {
|
|
pr_err("rtos_reserved device node find error\n");
|
|
return 0;
|
|
}
|
|
|
|
rmem = of_reserved_mem_lookup(rtos_reserved);
|
|
if (!rmem) {
|
|
pr_err("%s no such reserved mem\n", (char *)rtos_reserved->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!rmem->base || !rmem->size) {
|
|
pr_err("%s unexpected reserved memory\n", (char *)rtos_reserved->name);
|
|
return 0;
|
|
}
|
|
|
|
free_reserved_area(__va(rmem->base),
|
|
__va(PAGE_ALIGN(rmem->base + rmem->size)),
|
|
0,
|
|
"free_mem");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
static unsigned long freertos_request_info(void)
|
|
{
|
|
struct device_node *rtos_reserved, *freertos;
|
|
struct reserved_mem *rmem;
|
|
unsigned int info_offset;
|
|
unsigned long info_base;
|
|
int r = 0;
|
|
|
|
rtos_reserved = of_find_compatible_node(NULL, NULL, "amlogic, aml_rtos_memory");
|
|
if (!rtos_reserved) {
|
|
pr_err("rtos_reserved device node find error\n");
|
|
return 0;
|
|
}
|
|
|
|
rmem = of_reserved_mem_lookup(rtos_reserved);
|
|
if (!rmem) {
|
|
pr_err("%s no such reserved mem\n", (char *)rtos_reserved->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!rmem->base || !rmem->size) {
|
|
pr_err("%s unexpected reserved memory\n", (char *)rtos_reserved->name);
|
|
return 0;
|
|
}
|
|
info_base = rmem->base;
|
|
|
|
freertos = of_find_compatible_node(NULL, NULL, "amlogic,freertos");
|
|
if (!freertos) {
|
|
pr_err("freertos device node find error\n");
|
|
return 0;
|
|
}
|
|
r = of_property_read_u32(freertos, "info_offset", &info_offset);
|
|
if (r < 0) {
|
|
pr_err("rtos info offset find error\n");
|
|
return 0;
|
|
}
|
|
pr_debug("rtos base:%lx, info offset:%x\n", info_base, info_offset);
|
|
|
|
return (info_base + info_offset);
|
|
}
|
|
|
|
unsigned long freertos_is_run(void)
|
|
{
|
|
unsigned long phy;
|
|
struct xrtosinfo_t *info;
|
|
|
|
phy = freertos_request_info();
|
|
if (!phy)
|
|
return 0;
|
|
|
|
info = (struct xrtosinfo_t *)memremap(phy, sizeof(struct xrtosinfo_t), MEMREMAP_WB);
|
|
if (info) {
|
|
if (info->rtos_run_flag != RTOS_RUN_FLAG) {
|
|
pr_debug("rtos_run_flag:%x\n", info->rtos_run_flag);
|
|
return 0;
|
|
}
|
|
} else {
|
|
pr_err("%s,map freertos info failed\n", __func__);
|
|
return 0;
|
|
}
|
|
iounmap(info);
|
|
return phy;
|
|
}
|
|
EXPORT_SYMBOL(freertos_is_run);
|
|
#else
|
|
static unsigned long freertos_request_info(void)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(SIP_FREERTOS_FID, RTOS_REQUEST_INFO,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
return res.a0;
|
|
}
|
|
|
|
static unsigned long freertos_allow_coreup(void)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(SIP_FREERTOS_FID, RTOS_ALLOW_COREUP,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
return res.a0;
|
|
}
|
|
|
|
unsigned long freertos_is_run(void)
|
|
{
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(freertos_is_run);
|
|
#endif
|
|
|
|
//////////temporary walk around method///////////
|
|
#include <linux/delay.h>
|
|
#include <uapi/linux/psci.h>
|
|
|
|
#ifdef CONFIG_64BIT
|
|
#define PSCI_AFFINITY_INFO PSCI_0_2_FN64_AFFINITY_INFO
|
|
#else
|
|
#define PSCI_AFFINITY_INFO PSCI_0_2_FN_AFFINITY_INFO
|
|
#endif
|
|
|
|
int meson_psci_affinity_info(unsigned int cpu)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(PSCI_AFFINITY_INFO, cpu, 0, 0, 0, 0, 0, 0, &res);
|
|
return res.a0;
|
|
}
|
|
|
|
/*
|
|
*Can't shutdown C1 now
|
|
*int meson_psci_sys_power_off(void)
|
|
*{
|
|
* struct arm_smccc_res res;
|
|
*
|
|
* arm_smccc_smc(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0, 0, 0, 0, 0, &res);
|
|
* return res.a0;
|
|
*}
|
|
*/
|
|
|
|
int meson_do_core_off(unsigned int cpu)
|
|
{
|
|
int i, err;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
err = meson_psci_affinity_info(cpu);
|
|
if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
|
|
pr_debug("CPU %d killed.\n", cpu);
|
|
return 0;
|
|
}
|
|
|
|
usleep_range(10000, 15000);
|
|
pr_info("Retrying again to check for CPU kill\n");
|
|
}
|
|
|
|
pr_warn("CPU%d may not have shut down cleanly (err: %d)\n",
|
|
cpu, err);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
|
|
static int freertos_coreup_prepare(int cpu, int bootup)
|
|
{
|
|
if (meson_do_core_off(cpu)) {
|
|
pr_err("cpu %u power off fail\n", cpu);
|
|
return -1;
|
|
}
|
|
pr_info("cpu %u power off success\n", cpu);
|
|
#ifndef CONFIG_AMLOGIC_FREERTOS_T7
|
|
freertos_allow_coreup();
|
|
#endif
|
|
#ifndef CONFIG_AMLOGIC_LINUX_S_OPTIMIZE
|
|
if (bootup)
|
|
usleep_range(10000, 15000);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void freertos_coreup(int cpu, int bootup)
|
|
{
|
|
if (bootup) {
|
|
pr_debug("cpu %u power on start\n", cpu);
|
|
if (!device_online(get_cpu_device(cpu)))
|
|
pr_info("cpu %u power on success\n", cpu);
|
|
else
|
|
pr_err("cpu %u power on failed\n", cpu);
|
|
}
|
|
}
|
|
|
|
static void freertos_do_finish(int bootup)
|
|
{
|
|
unsigned int cpu;
|
|
|
|
mutex_lock(&freertos_lock);
|
|
if (!freertos_finished && rtosinfo &&
|
|
rtosinfo->status == ertosstat_done) {
|
|
/*if (rtosinfo->flags & RTOSINFO_FLG_SHUTDOWN) {
|
|
* pr_info("begin system shutdown\n");
|
|
* meson_psci_sys_power_off();
|
|
* pr_info("system shutdown fail\n");
|
|
* goto done;
|
|
*}
|
|
*/
|
|
for_each_present_cpu(cpu) {
|
|
if (rtosinfo->cpumask & (1 << cpu)) {
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_NOFITIER) && \
|
|
(IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_C3) || IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_C4))
|
|
call_freertos_notifiers(1, NULL);
|
|
#endif
|
|
if (!cpu_online(cpu)) {
|
|
pr_info("cpu %u finish\n", cpu);
|
|
if (freertos_coreup_prepare(cpu, bootup) < 0)
|
|
continue;
|
|
|
|
aml_rtos_logbuf_save();
|
|
freertos_coreup(cpu, bootup);
|
|
|
|
} else {
|
|
pr_info("cpu %u already take over\n", cpu);
|
|
}
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_MEMORY_FREE)
|
|
kthread_run(free_rtos_memory, NULL, "rtos_free");
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_NOFITIER) && IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
call_freertos_notifiers(1, NULL);
|
|
#endif
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
//done:
|
|
freertos_finished = 1;
|
|
}
|
|
mutex_unlock(&freertos_lock);
|
|
}
|
|
|
|
static void freertos_do_work(struct work_struct *work)
|
|
{
|
|
freertos_do_finish(1);
|
|
}
|
|
|
|
static DECLARE_WORK(freertos_work, freertos_do_work);
|
|
|
|
int freertos_finish(void)
|
|
{
|
|
int chk = 0;
|
|
unsigned long flg;
|
|
unsigned int cpu;
|
|
|
|
spin_lock_irqsave(&freertos_chk_lock, flg);
|
|
cpu = smp_processor_id();
|
|
if (cpu == 0 && !ipi_rcv && rtosinfo && !freertos_finished &&
|
|
rtosinfo->status == ertosstat_done) {
|
|
pr_info("ipi received\n");
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
for_each_present_cpu(cpu) {
|
|
if (rtosinfo->cpumask & (1 << cpu)) {
|
|
if (cpu_online(cpu))
|
|
pr_info("cpu %u already take over\n", cpu);
|
|
else
|
|
chk = 1;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
for_each_present_cpu(cpu) {
|
|
if ((rtosinfo->cpumask & (1 << cpu)) &&
|
|
cpu_online(cpu))
|
|
goto ipircved;
|
|
}
|
|
chk = 1;
|
|
ipircved:
|
|
#endif
|
|
ipi_rcv = 1;
|
|
}
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_NOFITIER) && IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
else if (cpu == 0 && rtosinfo && !freertos_finished &&
|
|
rtosinfo->status == ertosstat_release) {
|
|
pr_info("rtos release driver notified\n");
|
|
call_freertos_notifiers(1, NULL);
|
|
}
|
|
#endif
|
|
spin_unlock_irqrestore(&freertos_chk_lock, flg);
|
|
|
|
if (chk) {
|
|
schedule_work(&freertos_work);
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(freertos_finish);
|
|
|
|
int freertos_is_finished(void)
|
|
{
|
|
return freertos_finished;
|
|
}
|
|
EXPORT_SYMBOL(freertos_is_finished);
|
|
|
|
struct xrtosinfo_t *freertos_get_info(void)
|
|
{
|
|
return rtosinfo;
|
|
}
|
|
EXPORT_SYMBOL(freertos_get_info);
|
|
|
|
static int __init disable_freertos(char *str)
|
|
{
|
|
freertos_disabled = 1;
|
|
return 0;
|
|
}
|
|
|
|
early_param("disable_freertos", disable_freertos);
|
|
|
|
static inline void _logbuf_begin_get_log(void)
|
|
{
|
|
u32 rd1, rd2;
|
|
|
|
do {
|
|
rd1 = READ_ONCE(logbuf->read);
|
|
rd2 = (rd1 | LOGBUF_RDFLG);
|
|
} while (rd1 != cmpxchg(&logbuf->read, rd1, rd2));
|
|
}
|
|
|
|
static inline void _logbuf_post_get_log(int len, int last)
|
|
{
|
|
u32 rd1, rd2;
|
|
|
|
do {
|
|
rd1 = READ_ONCE(logbuf->read);
|
|
rd2 = (rd1 & ~LOGBUF_RDFLG);
|
|
rd2 += len;
|
|
if (rd2 >= logbuf_len)
|
|
rd2 -= logbuf_len;
|
|
if (last == 0)
|
|
rd2 |= (rd1 & LOGBUF_RDFLG);
|
|
} while (rd1 != cmpxchg(&logbuf->read, rd1, rd2));
|
|
}
|
|
|
|
static inline int _logbuf_get_log(const char **pbuf)
|
|
{
|
|
u32 rd1, rd2, wt;
|
|
int len;
|
|
|
|
rd1 = READ_ONCE(logbuf->read);
|
|
wt = READ_ONCE(logbuf->write);
|
|
rd2 = (rd1 & ~LOGBUF_RDFLG);
|
|
if (rd2 <= wt)
|
|
len = wt - rd2;
|
|
else
|
|
len = logbuf_len - rd2;
|
|
if (pbuf)
|
|
*pbuf = (const char *)logbuf->buf + rd2;
|
|
return len;
|
|
}
|
|
|
|
static ssize_t logbuf_read(struct file *file, char __user *buf,
|
|
size_t size, loff_t *ppos)
|
|
{
|
|
const char *p;
|
|
int cnt = 0, err;
|
|
|
|
while (1) {
|
|
cnt = _logbuf_get_log(&p);
|
|
if (cnt > 0 || signal_pending(current))
|
|
break;
|
|
usleep_range(1000, 1500);
|
|
}
|
|
if (cnt > size)
|
|
cnt = size;
|
|
if (cnt <= 0)
|
|
return 0;
|
|
err = copy_to_user(buf, p, cnt);
|
|
if (err)
|
|
return -1;
|
|
*ppos += cnt;
|
|
_logbuf_post_get_log(cnt, 0);
|
|
return cnt;
|
|
}
|
|
|
|
static int logbuf_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret;
|
|
|
|
ret = mutex_lock_interruptible(&freertos_logbuf_lock);
|
|
if (ret < 0)
|
|
return ret;
|
|
_logbuf_begin_get_log();
|
|
return 0;
|
|
}
|
|
|
|
static int logbuf_release(struct inode *inode, struct file *file)
|
|
{
|
|
_logbuf_post_get_log(0, 1);
|
|
mutex_unlock(&freertos_logbuf_lock);
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations logbuf_fops = {
|
|
.open = logbuf_open,
|
|
.read = logbuf_read,
|
|
.write = NULL,
|
|
.llseek = NULL,
|
|
.release = logbuf_release,
|
|
};
|
|
|
|
static int aml_rtos_logbuf_save(void)
|
|
{
|
|
struct logbuf_t *tmpbuf;
|
|
|
|
if (!rtos_log_save)
|
|
pr_info("parm rtos_log_save not set\n");
|
|
|
|
if (!rtos_debug_dir) {
|
|
pr_err("rtos logbuf dir not exit\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!logbuf) {
|
|
pr_err("logbuf is NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
if (rtosinfo->logbuf_len <= sizeof(struct logbuf_t)) {
|
|
pr_err("rtos logbuf len is error\n");
|
|
return -1;
|
|
}
|
|
|
|
tmpbuf = vmalloc(rtosinfo->logbuf_len);
|
|
pr_info("save logbuf addr: %p, tmpbuf addr: %p\n", logbuf, tmpbuf);
|
|
memcpy(tmpbuf, logbuf, rtosinfo->logbuf_len);
|
|
logbuf = tmpbuf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aml_rtos_logbuf_init(void)
|
|
{
|
|
phys_addr_t phy;
|
|
size_t size;
|
|
|
|
if (!rtos_debug_dir)
|
|
return -1;
|
|
if (rtosinfo->logbuf_len <= sizeof(struct logbuf_t))
|
|
return -1;
|
|
phy = (phys_addr_t)rtosinfo->logbuf_phy;
|
|
size = rtosinfo->logbuf_len;
|
|
pr_info("logbuffer: 0x%x, 0x%x\n",
|
|
rtosinfo->logbuf_phy, rtosinfo->logbuf_len);
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_IS_MAP)
|
|
logbuf = memremap(phy, size, MEMREMAP_WB);
|
|
#else
|
|
logbuf = ioremap_cache(phy, size);
|
|
#endif
|
|
if (!logbuf) {
|
|
pr_err("map log buffer failed\n");
|
|
return -1;
|
|
}
|
|
logbuf_len = (u32)(size - sizeof(struct logbuf_t));
|
|
rtos_logbuf_file = debugfs_create_file("logbuf", S_IFREG | 0440,
|
|
rtos_debug_dir, NULL,
|
|
&logbuf_fops);
|
|
return 0;
|
|
}
|
|
|
|
static void aml_rtos_logbuf_deinit(void)
|
|
{
|
|
debugfs_remove(rtos_logbuf_file);
|
|
rtos_logbuf_file = NULL;
|
|
|
|
if (logbuf) {
|
|
iounmap(logbuf);
|
|
logbuf = NULL;
|
|
}
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_ANDROID_CTRL)
|
|
static ssize_t android_status_store(const struct class *class,
|
|
const struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
long val = 0;
|
|
|
|
if (kstrtoul(buf, 0, &val)) {
|
|
pr_err("invalid input:%s\n", buf);
|
|
return count;
|
|
}
|
|
|
|
pr_info("set android_status is %ld\n", val);
|
|
rtosinfo->android_status = val;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t android_status_show(const struct class *class,
|
|
const struct class_attribute *attr,
|
|
char *buf)
|
|
{
|
|
int cnt = 0;
|
|
|
|
cnt = sprintf(buf, "%d\n", rtosinfo->android_status);
|
|
|
|
return cnt;
|
|
}
|
|
static CLASS_ATTR_RW(android_status);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_IPI_SEND)
|
|
static ssize_t ipi_send_store(const struct class *class,
|
|
const struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
long cpu = 0;
|
|
|
|
if (kstrtoul(buf, 0, &cpu)) {
|
|
pr_err("invalid input cpu number:%s\n", buf);
|
|
return count;
|
|
}
|
|
|
|
pr_info("set ipi to cpu%ld\n", cpu);
|
|
arch_send_ipi_rtos(cpu);
|
|
|
|
return count;
|
|
}
|
|
static CLASS_ATTR_WO(ipi_send);
|
|
#endif
|
|
|
|
static ssize_t time_limit_store(const struct class *class,
|
|
const struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long val = 0;
|
|
|
|
if (kstrtoul(buf, 0, &val)) {
|
|
pr_err("invalid input:%s\n", buf);
|
|
return count;
|
|
}
|
|
pr_info("set time limit is %ld ns\n", val);
|
|
rtos_time_limit = val;
|
|
return count;
|
|
}
|
|
|
|
static ssize_t time_limit_show(const struct class *class,
|
|
const struct class_attribute *attr,
|
|
char *buf)
|
|
{
|
|
int cnt = 0;
|
|
|
|
cnt = sprintf(buf, "time limit value :%lld ns\n", rtos_time_limit);
|
|
return cnt;
|
|
}
|
|
static CLASS_ATTR_RW(time_limit);
|
|
|
|
static struct attribute *freertos_attrs[] = {
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_ANDROID_CTRL)
|
|
&class_attr_android_status.attr,
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_IPI_SEND)
|
|
&class_attr_ipi_send.attr,
|
|
#endif
|
|
&class_attr_time_limit.attr,
|
|
NULL
|
|
};
|
|
ATTRIBUTE_GROUPS(freertos);
|
|
|
|
static struct class freertos_class = {
|
|
.name = "freertos",
|
|
.class_groups = freertos_groups,
|
|
};
|
|
|
|
static int aml_rtos_probe(struct platform_device *pdev)
|
|
{
|
|
rtos_debug_dir = debugfs_create_dir("freertos", NULL);
|
|
rtosinfo_phy = freertos_request_info();
|
|
pr_info("rtosinfo_phy=%lx\n", rtosinfo_phy);
|
|
if (rtosinfo_phy == 0 || (int)rtosinfo_phy == SMC_UNK)
|
|
return 0;
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_IS_MAP)
|
|
rtosinfo = (struct xrtosinfo_t *)memremap(rtosinfo_phy,
|
|
sizeof(struct xrtosinfo_t),
|
|
MEMREMAP_WB);
|
|
#else
|
|
rtosinfo = (struct xrtosinfo_t *)ioremap(rtosinfo_phy,
|
|
sizeof(struct xrtosinfo_t));
|
|
#endif
|
|
if (rtosinfo) {
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
if (rtosinfo->rtos_run_flag == RTOS_RUN_FLAG) {
|
|
#else
|
|
if (1) {
|
|
#endif
|
|
if (class_register(&freertos_class)) {
|
|
pr_err("regist freertos_class failed\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
aml_rtos_logbuf_init();
|
|
freertos_do_finish(1);
|
|
} else {
|
|
pr_err("map freertos info failed\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __exit aml_rtos_remove(struct platform_device *pdev)
|
|
{
|
|
aml_rtos_logbuf_deinit();
|
|
if (rtosinfo)
|
|
iounmap(rtosinfo);
|
|
|
|
debugfs_remove(rtos_debug_dir);
|
|
return 0;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_SHUTDOWN)
|
|
static void aml_rtos_shutdown(struct platform_device *pdev)
|
|
{
|
|
unsigned long long tick_s = 0, tick_e = 0;
|
|
|
|
if (!freertos_finished) {
|
|
arch_send_ipi_rtos(1);
|
|
tick_s = sched_clock();
|
|
while (!freertos_finished) {
|
|
tick_e = sched_clock() - tick_s;
|
|
if (tick_e > rtos_time_limit)
|
|
panic("rtos finished time out\n");
|
|
usleep_range(500, 1000);
|
|
}
|
|
tick_e = sched_clock() - tick_s;
|
|
}
|
|
pr_info("rtos shutdown out, time %lld ns\n", tick_e);
|
|
|
|
}
|
|
#endif
|
|
|
|
static const struct of_device_id aml_freertos_dt_match[] = {
|
|
{
|
|
.compatible = "amlogic,freertos",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver aml_rtos_driver = {
|
|
.driver = {
|
|
.name = AML_RTOS_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = aml_freertos_dt_match,
|
|
},
|
|
.probe = aml_rtos_probe,
|
|
.remove = __exit_p(aml_rtos_remove),
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_SHUTDOWN)
|
|
.shutdown = aml_rtos_shutdown,
|
|
#endif
|
|
};
|
|
|
|
static void freertos_get_irqrsved(void)
|
|
{
|
|
struct device_node *np;
|
|
const __be32 *irqrsv;
|
|
u32 len, irq;
|
|
unsigned long tmp;
|
|
int i;
|
|
|
|
mutex_lock(&freertos_lock);
|
|
if (freertos_irqrsv_inited)
|
|
goto exit;
|
|
freertos_irqrsv_inited = 1;
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_FREERTOS_T7)
|
|
tmp = freertos_is_run();
|
|
if (!tmp)
|
|
goto exit;
|
|
#else
|
|
tmp = freertos_request_info();
|
|
if (tmp == 0 || tmp == SMC_UNK)
|
|
goto exit;
|
|
#endif
|
|
|
|
np = of_find_matching_node_and_match(NULL, aml_freertos_dt_match, NULL);
|
|
if (!np)
|
|
goto exit;
|
|
if (!of_device_is_available(np))
|
|
goto ret;
|
|
irqrsv = of_get_property(np, "irqs_rsv", &len);
|
|
if (!irqrsv)
|
|
goto ret;
|
|
len /= sizeof(*irqrsv);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
irq = be32_to_cpu(irqrsv[i]);
|
|
if (irq >= RSVED_MAX_IRQ)
|
|
continue;
|
|
set_bit(irq, freertos_irqrsv_mask);
|
|
}
|
|
ret:
|
|
of_node_put(np);
|
|
exit:
|
|
mutex_unlock(&freertos_lock);
|
|
}
|
|
|
|
int freertos_is_irq_rsved(unsigned int irq)
|
|
{
|
|
if (freertos_disabled || irq >= RSVED_MAX_IRQ)
|
|
return 0;
|
|
|
|
if (!freertos_irqrsv_inited)
|
|
freertos_get_irqrsved();
|
|
|
|
return test_bit(irq, freertos_irqrsv_mask);
|
|
}
|
|
EXPORT_SYMBOL(freertos_is_irq_rsved);
|
|
|
|
u32 freertos_get_irqregval(u32 val, u32 oldval,
|
|
unsigned int irqbase,
|
|
unsigned int n)
|
|
{
|
|
u32 mask, valmask;
|
|
u32 width;
|
|
unsigned int j;
|
|
|
|
if (freertos_disabled)
|
|
return val;
|
|
if (n > 32 || 32 % n) {
|
|
pr_err("invalid number of irqs: n=%u\n", n);
|
|
return 0;
|
|
}
|
|
width = 32 / n;
|
|
mask = (1UL << width) - 1;
|
|
valmask = 0;
|
|
for (j = 0; j < n; j++) {
|
|
if (freertos_is_irq_rsved(irqbase + j))
|
|
valmask |= (mask << (j * width));
|
|
}
|
|
if (!valmask)
|
|
return val;
|
|
return (oldval & valmask) | (val & (~valmask));
|
|
}
|
|
EXPORT_SYMBOL(freertos_get_irqregval);
|
|
|
|
static int __init aml_freertos_init(void)
|
|
{
|
|
if (freertos_disabled)
|
|
return 0;
|
|
|
|
return platform_driver_register(&aml_rtos_driver);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_LINUX_S_OPTIMIZE)
|
|
postcore_initcall(aml_freertos_init);
|
|
#else
|
|
subsys_initcall_sync(aml_freertos_init);
|
|
#endif
|
|
|
|
static void __exit aml_freertos_exit(void)
|
|
{
|
|
if (freertos_disabled)
|
|
return;
|
|
platform_driver_unregister(&aml_rtos_driver);
|
|
}
|
|
module_exit(aml_freertos_exit);
|
|
|
|
MODULE_DESCRIPTION("FreeRtos Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Amlogic, Inc.");
|