319 lines
6.8 KiB
C
Executable File
319 lines
6.8 KiB
C
Executable File
/*
|
|
* exynos_acpm_tmu.c - ACPM TMU plugin interface
|
|
*
|
|
* Copyright (C) 2017 Samsung Electronics
|
|
*
|
|
* 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/string.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <soc/samsung/acpm_ipc_ctrl.h>
|
|
#include "exynos_acpm_tmu.h"
|
|
|
|
static unsigned int acpm_tmu_ch_num, acpm_tmu_size;
|
|
|
|
static bool acpm_tmu_test_mode;
|
|
static bool acpm_tmu_log;
|
|
|
|
bool exynos_acpm_tmu_is_test_mode(void)
|
|
{
|
|
return acpm_tmu_test_mode;
|
|
}
|
|
|
|
void exynos_acpm_tmu_set_test_mode(bool mode)
|
|
{
|
|
acpm_tmu_test_mode = mode;
|
|
}
|
|
|
|
void exynos_acpm_tmu_log(bool mode)
|
|
{
|
|
acpm_tmu_log = mode;
|
|
}
|
|
|
|
#define acpm_ipc_latency_check() \
|
|
do { \
|
|
if (acpm_tmu_log) { \
|
|
pr_info("[acpm_tmu] type 0x%02x latency %llu ns ret %d\n", \
|
|
message.req.type, latency, ret); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define acpm_ipc_err_check() \
|
|
do { \
|
|
if (ret < 0) { \
|
|
pr_warn("[acpm_tmu] IPC error! type 0x%02x latency %llu ns ret %d\n", \
|
|
message.req.type, latency, ret); \
|
|
return -1; \
|
|
} \
|
|
} while (0)
|
|
|
|
/*
|
|
* TMU_IPC_INIT
|
|
*/
|
|
int exynos_acpm_tmu_set_init(struct acpm_tmu_cap *cap)
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
int ret;
|
|
unsigned long long before, after, latency;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_INIT;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(message.data, config.cmd, sizeof(message.data));
|
|
|
|
if (message.resp.ret & CAP_APM_IRQ)
|
|
cap->acpm_irq = true;
|
|
|
|
if (message.resp.ret & CAP_APM_DIVIDER)
|
|
cap->acpm_divider = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* TMU_IPC_READ_TEMP
|
|
*
|
|
* - tz: thermal zone index registered in device tree
|
|
*/
|
|
int exynos_acpm_tmu_set_read_temp(int tz, int *temp, int *stat)
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
int ret;
|
|
unsigned long long before, after, latency;
|
|
|
|
if (acpm_tmu_test_mode)
|
|
return -1;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_READ_TEMP;
|
|
message.req.tzid = tz;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(message.data, config.cmd, sizeof(message.data));
|
|
if (acpm_tmu_log) {
|
|
pr_info("[acpm_tmu] data 0:0x%08x 1:0x%08x 2:0x%08x 3:0x%08x\n",
|
|
message.data[0],
|
|
message.data[1],
|
|
message.data[2],
|
|
message.data[3]);
|
|
}
|
|
|
|
*temp = message.resp.temp;
|
|
*stat = message.resp.stat;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* TMU_IPC_AP_SUSPEND
|
|
*/
|
|
int exynos_acpm_tmu_set_suspend(void)
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
int ret;
|
|
unsigned long long before, after, latency;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_AP_SUSPEND;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(message.data, config.cmd, sizeof(message.data));
|
|
if (acpm_tmu_log) {
|
|
pr_info("[acpm_tmu] data 0:0x%08x 1:0x%08x 2:0x%08x 3:0x%08x\n",
|
|
message.data[0],
|
|
message.data[1],
|
|
message.data[2],
|
|
message.data[3]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* TMU_IPC_CP_CALL
|
|
*/
|
|
int exynos_acpm_tmu_set_cp_call(void)
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
int ret;
|
|
unsigned long long before, after, latency;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_CP_CALL;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(&message.data, config.cmd, sizeof(message.data));
|
|
if (acpm_tmu_log) {
|
|
pr_info("[acpm_tmu] data 0:0x%08x 1:0x%08x 2:0x%08x 3:0x%08x\n",
|
|
message.data[0],
|
|
message.data[1],
|
|
message.data[2],
|
|
message.data[3]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* TMU_IPC_AP_RESUME
|
|
*/
|
|
int exynos_acpm_tmu_set_resume(void)
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
unsigned long long before, after, latency;
|
|
int ret;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_AP_RESUME;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(&message.data, config.cmd, sizeof(message.data));
|
|
if (acpm_tmu_log) {
|
|
pr_info("[acpm_tmu] data 0:0x%08x 1:0x%08x 2:0x%08x 3:0x%08x\n",
|
|
message.data[0],
|
|
message.data[1],
|
|
message.data[2],
|
|
message.data[3]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int exynos_acpm_tmu_ipc_dump(int no, unsigned int dump[])
|
|
{
|
|
struct ipc_config config;
|
|
union tmu_ipc_message message;
|
|
unsigned long long before, after, latency;
|
|
int ret;
|
|
|
|
memset(&message, 0, sizeof(message));
|
|
|
|
message.req.type = TMU_IPC_READ_TEMP;
|
|
message.req.tzid = no;
|
|
|
|
config.cmd = message.data;
|
|
config.response = true;
|
|
config.indirection = false;
|
|
|
|
before = sched_clock();
|
|
ret = acpm_ipc_send_data(acpm_tmu_ch_num, &config);
|
|
after = sched_clock();
|
|
latency = after - before;
|
|
|
|
acpm_ipc_err_check();
|
|
acpm_ipc_latency_check();
|
|
|
|
memcpy(message.data, config.cmd, sizeof(message.data));
|
|
if (acpm_tmu_log) {
|
|
pr_info("[acpm_tmu_dump] data 0:0x%08x 1:0x%08x 2:0x%08x 3:0x%08x\n",
|
|
message.data[0],
|
|
message.data[1],
|
|
message.data[2],
|
|
message.data[3]);
|
|
}
|
|
|
|
dump[0] = message.data[2];
|
|
dump[1] = message.data[3];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init exynos_acpm_tmu_init(void)
|
|
{
|
|
struct device_node *np;
|
|
|
|
np = of_find_node_by_name(NULL, "acpm_tmu");
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
return acpm_ipc_request_channel(np, NULL, &acpm_tmu_ch_num, &acpm_tmu_size);
|
|
}
|
|
fs_initcall(exynos_acpm_tmu_init);
|