#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
#include <linux/ktime.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/units.h>
#include "exynos-acpm.h"
#include "exynos-acpm-tmu.h"
#define ACPM_TMU_INIT 0x01
#define ACPM_TMU_READ_TEMP 0x02
#define ACPM_TMU_SUSPEND 0x04
#define ACPM_TMU_RESUME 0x10
#define ACPM_TMU_THRESHOLD 0x11
#define ACPM_TMU_INTEN 0x12
#define ACPM_TMU_CONTROL 0x13
#define ACPM_TMU_IRQ_CLEAR 0x14
#define ACPM_TMU_TX_DATA_LEN 8
#define ACPM_TMU_RX_DATA_LEN 7
struct acpm_tmu_tx {
u16 ctx;
u16 fw_use;
u8 type;
u8 rsvd0;
u8 tzid;
u8 rsvd1;
u8 data[ACPM_TMU_TX_DATA_LEN];
} __packed;
struct acpm_tmu_rx {
u16 ctx;
u16 fw_use;
u8 type;
s8 ret;
u8 tzid;
s8 temp;
u8 rsvd;
u8 data[ACPM_TMU_RX_DATA_LEN];
} __packed;
union acpm_tmu_msg {
u32 data[4];
struct acpm_tmu_tx tx;
struct acpm_tmu_rx rx;
};
static int acpm_tmu_to_linux_err(s8 fw_err)
{
if (fw_err >= 0)
return 0;
if (fw_err == -1)
return -EACCES;
return -EIO;
}
int acpm_tmu_init(struct acpm_handle *handle, unsigned int acpm_chan_id)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_INIT;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_read_temp(struct acpm_handle *handle, unsigned int acpm_chan_id,
u8 tz, int *temp)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_READ_TEMP;
msg.tx.tzid = tz;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
ret = acpm_tmu_to_linux_err(msg.rx.ret);
if (ret)
return ret;
*temp = msg.rx.temp;
return 0;
}
int acpm_tmu_set_threshold(struct acpm_handle *handle,
unsigned int acpm_chan_id, u8 tz,
const u8 temperature[8], size_t tlen)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
if (tlen > ACPM_TMU_TX_DATA_LEN)
return -EINVAL;
msg.tx.type = ACPM_TMU_THRESHOLD;
msg.tx.tzid = tz;
memcpy(msg.tx.data, temperature, tlen);
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_set_interrupt_enable(struct acpm_handle *handle,
unsigned int acpm_chan_id, u8 tz, u8 inten)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_INTEN;
msg.tx.tzid = tz;
msg.tx.data[0] = inten;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_tz_control(struct acpm_handle *handle, unsigned int acpm_chan_id,
u8 tz, bool enable)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_CONTROL;
msg.tx.tzid = tz;
msg.tx.data[0] = enable ? 1 : 0;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_clear_tz_irq(struct acpm_handle *handle, unsigned int acpm_chan_id,
u8 tz)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_IRQ_CLEAR;
msg.tx.tzid = tz;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_suspend(struct acpm_handle *handle, unsigned int acpm_chan_id)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_SUSPEND;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}
int acpm_tmu_resume(struct acpm_handle *handle, unsigned int acpm_chan_id)
{
union acpm_tmu_msg msg = {0};
struct acpm_xfer xfer;
int ret;
msg.tx.type = ACPM_TMU_RESUME;
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
true);
ret = acpm_do_xfer(handle, &xfer);
if (ret)
return ret;
return acpm_tmu_to_linux_err(msg.rx.ret);
}