--- /dev/null
+/*
+ * 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 bool cold_comp;
+static unsigned int acpm_tmu_ch_num, acpm_tmu_size;
+
+#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)
+
+#define acpm_ipc_latency_check() \
+ do { \
+ if (latency > 2000000) { \
+ pr_info("[acpm_tmu] type 0x%02x latency %llu ns ret %d\n", \
+ message.req.type, latency, ret); \
+ } \
+ } 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;
+ unsigned long long before, after, latency;
+ int ret;
+
+ 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 *cur_temp)
+{
+ struct ipc_config config;
+ union tmu_ipc_message message;
+ unsigned long long before, after, latency;
+ int ret;
+
+ *cur_temp = 0;
+
+ 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));
+
+ cold_comp = message.resp.cold;
+ *cur_temp = message.resp.temp;
+
+ return 0;
+}
+
+/*
+ * TMU_IPC_AP_SUSPEND
+ */
+int exynos_acpm_tmu_set_suspend(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_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));
+
+ cold_comp = message.resp.cold;
+
+ return 0;
+}
+
+/*
+ * TMU_IPC_CP_CALL
+ */
+int exynos_acpm_tmu_set_cp_call(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_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));
+
+ 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));
+
+ 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);
--- /dev/null
+/*
+ * exynos_acpm_tmu.h - 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
+ */
+
+#ifndef __EXYNOS_ACPM_TMU_H__
+#define __EXYNOS_ACPM_TMU_H__
+
+/* Return values */
+#define RET_SUCCESS 0
+#define RET_FAIL -1
+#define RET_OK RET_SUCCESS
+#define RET_NOK RET_FAIL
+
+/* Return values - error types (minus) */
+#define ERR_REQ_TYPE 2
+#define ERR_TZ_ID 3
+#define ERR_TEMP 4
+#define ERR_APM_IRQ 5
+#define ERR_APM_DIVIDER 6
+
+/* Return values - capabilities */
+#define CAP_APM_IRQ 0x1
+#define CAP_APM_DIVIDER 0x2
+
+/* IPC Request Types */
+#define TMU_IPC_INIT 0x01
+#define TMU_IPC_READ_TEMP 0x02
+#define TMU_IPC_AP_SUSPEND 0x04
+#define TMU_IPC_CP_CALL 0x08
+#define TMU_IPC_AP_RESUME 0x10
+
+/*
+ * 16-byte TMU IPC message format (REQ)
+ * (MSB) 3 2 1 0
+ * ---------------------------------------------
+ * | fw_use | ctx |
+ * ---------------------------------------------
+ * | | tzid | | type |
+ * ---------------------------------------------
+ * | | | | |
+ * ---------------------------------------------
+ * | | | | |
+ * ---------------------------------------------
+ */
+struct tmu_ipc_request {
+ u16 ctx; /* LSB */
+ u16 fw_use; /* MSB */
+ u8 type;
+ u8 rsvd;
+ u8 tzid;
+ u8 rsvd2;
+ u32 reserved;
+ u32 reserved2;
+};
+
+/*
+ * 16-byte TMU IPC message format (RESP)
+ * (MSB) 3 2 1 0
+ * ---------------------------------------------
+ * | fw_use | ctx |
+ * ---------------------------------------------
+ * | temp | tz_id | ret | type |
+ * ---------------------------------------------
+ * | | | | cold |
+ * ---------------------------------------------
+ * | | | | |
+ * ---------------------------------------------
+ */
+struct tmu_ipc_response {
+ u16 ctx; /* LSB */
+ u16 fw_use; /* MSB */
+ u8 type;
+ s8 ret;
+ u8 tzid;
+ u8 temp;
+ u8 cold;
+ u8 rsvd;
+ u8 rsvd2;
+ u8 rsvd3;
+ u32 reserved;
+};
+
+union tmu_ipc_message {
+ u32 data[4];
+ struct tmu_ipc_request req;
+ struct tmu_ipc_response resp;
+};
+
+struct acpm_tmu_cap {
+ bool acpm_irq;
+ bool acpm_divider;
+};
+
+int exynos_acpm_tmu_set_init(struct acpm_tmu_cap *cap);
+int exynos_acpm_tmu_set_read_temp(int tz, int *temp);
+int exynos_acpm_tmu_set_suspend(void);
+int exynos_acpm_tmu_set_cp_call(void);
+int exynos_acpm_tmu_set_resume(void);
+void exynos_acpm_tmu_set_test_mode(bool mode);
+
+#endif /* __EXYNOS_ACPM_TMU_H__ */