android_kernel_samsung_on5x.../drivers/mailbox/samsung/apm-exynos8890.c
2018-06-19 23:16:04 +02:00

852 lines
20 KiB
C

/* drivers/mailbox/samsung/apm-exynos8890.c
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS8890 - APM driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mailbox_client.h>
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/core.h>
#include <linux/apm-exynos.h>
#include <linux/mailbox-exynos.h>
#include <asm/io.h>
#include <soc/samsung/asv-exynos.h>
#define PMIC_MIF_OUT (0x1B)
#define PMIC_ATL_OUT (0x1D)
#define PMIC_APO_OUT (0x1F)
#define PMIC_G3D_OUT (0x26)
extern int apm_wfi_prepare;
static DEFINE_MUTEX(cl_mutex);
char* protocol_name;
#ifdef CONFIG_EXYNOS_APM_VOLTAGE_DEBUG
u32 mif_in_voltage;
u32 atl_in_voltage;
u32 apo_in_voltage;
u32 g3d_in_voltage;
#endif
struct mbox_client cl;
void exynos8890_apm_power_up(void)
{
u32 tmp;
tmp = exynos_cortexm3_pmu_read(EXYNOS_PMU_CORTEXM3_APM_CONFIGURATION);
tmp &= APM_LOCAL_PWR_CFG_RESET;
tmp |= APM_LOCAL_PWR_CFG_RUN;
exynos_cortexm3_pmu_write(tmp, EXYNOS_PMU_CORTEXM3_APM_CONFIGURATION);
}
void exynos8890_apm_power_down(void)
{
u32 tmp;
/* Reset CORTEX M3 */
tmp = exynos_cortexm3_pmu_read(EXYNOS_PMU_CORTEXM3_APM_CONFIGURATION);
tmp &= APM_LOCAL_PWR_CFG_RESET;
exynos_cortexm3_pmu_write(tmp, EXYNOS_PMU_CORTEXM3_APM_CONFIGURATION);
}
/* exynos8890_apm_reset_release
* Reset signal release to PMU setting.
*/
void exynos8890_apm_reset_release(void)
{
unsigned int tmp;
/* Cortex M3 Interrupt bit clear */
exynos_mailbox_reg_write(0x0, EXYNOS_MAILBOX_TX_INT);
exynos_mailbox_reg_write(0x0, EXYNOS_MAILBOX_RX_INT);
/* Set APM device enable */
tmp = exynos_cortexm3_pmu_read(EXYNOS_PMU_CORTEXM3_APM_OPTION);
tmp &= ~ENABLE_APM;
tmp |= ENABLE_APM;
exynos_cortexm3_pmu_write(tmp, EXYNOS_PMU_CORTEXM3_APM_OPTION);
tmp = exynos_mailbox_reg_read(EXYNOS_MAILBOX_MRX_SEM);
tmp &= ~MRX_SEM_ENABLE;
tmp |= MRX_SEM_ENABLE;
exynos_mailbox_reg_write(tmp, EXYNOS_MAILBOX_MRX_SEM);
}
/* check_rx_data function check return value.
* CM3 send return value. 0xA is sucess, 0x0 is fail value.
* check_rx_data function check this value. So sucess return 0.
*/
static int check_rx_data(void *msg)
{
u8 i;
u32 buf[5] = {0, 0, 0, 0, 0};
for (i = 0; i < MBOX_LEN; i++)
buf[i] = exynos_mailbox_reg_read(EXYNOS_MAILBOX_RX(i));
/* Check return command */
buf[4] = exynos_mailbox_reg_read(EXYNOS_MAILBOX_TX(0));
/* Check apm device return value */
if (buf[1] == APM_GPIO_ERR)
return APM_GPIO_ERR;
/* PMIC No ACK return value */
if (buf[1] == PMIC_NO_ACK_ERR)
return PMIC_NO_ACK_ERR;
/* Multi byte condition */
if ((buf[4] >> MULTI_BYTE_SHIFT) & MULTI_BYTE_MASK) {
return 0;
}
/* Normal condition */
if (((buf[4] >> COMMAND_SHIFT) & COMMAND_MASK) != READ_MODE) {
if (buf[1] == APM_RET_SUCESS) {
return 0;
} else {
pr_err("mailbox err : return incorrect\n");
data_history();
return -1;
}
} else if (((buf[4] >> COMMAND_SHIFT) & COMMAND_MASK) == READ_MODE) {
return buf[1];
}
return 0;
}
EXPORT_SYMBOL_GPL(check_rx_data);
/* Setting channel ack_mode condition */
static void channel_ack_mode(struct mbox_client *client)
{
client->rx_callback = NULL;
client->tx_done = NULL;
#ifdef CONFIG_EXYNOS_MBOX_INTERRUPT
client->tx_block = true;
#endif
#ifdef CONFIG_EXYNOS_MBOX_POLLING
client->tx_block = NULL;
#endif
client->tx_tout = TIMEOUT;
client->knows_txdone = false;
}
EXPORT_SYMBOL_GPL(channel_ack_mode);
static int exynos_send_message(struct mbox_client *mbox_cl, void *msg)
{
struct mbox_chan *chan;
int ret;
chan = mbox_request_channel(mbox_cl, 0);
if (IS_ERR(chan)) {
pr_err("mailbox : Did not make a mailbox channel\n");
return PTR_ERR(chan);
}
if (!mbox_send_message(chan, (void *)msg)) {
ret = check_rx_data((void *)msg);
if (ret == APM_GPIO_ERR) {
pr_err("mailbox : gpio not set to gpio-i2c \n");
apm_wfi_prepare = APM_ON;
mbox_free_channel(chan);
return ERR_TIMEOUT;
} else if (ret < 0) {
pr_err("[%s] mailbox send error \n", __func__);
mbox_free_channel(chan);
return ERR_OUT;
}
} else {
pr_err("%s : Mailbox timeout\n", __func__);
pr_err("POLLING status: 0x%x\n", exynos_mailbox_reg_read(EXYNOS_MAILBOX_RX_INT));
apm_wfi_prepare = APM_ON;
mbox_free_channel(chan);
return ERR_TIMEOUT;
}
mbox_free_channel(chan);
return 0;
}
static int exynos_send_message_bulk_read(struct mbox_client *mbox_cl, void *msg)
{
struct mbox_chan *chan;
int ret;
chan = mbox_request_channel(mbox_cl, 0);
if (IS_ERR(chan)) {
pr_err("mailbox : Did not make a mailbox channel\n");
return PTR_ERR(chan);
}
if (!mbox_send_message(chan, (void *)msg)) {
ret = check_rx_data((void *)msg);
if (ret < 0) {
pr_err("[%s] mailbox send error \n", __func__);
return ERR_RETRY;
} else if (ret == APM_GPIO_ERR) {
apm_wfi_prepare = APM_ON;
mbox_free_channel(chan);
return ERR_TIMEOUT;
}
} else {
pr_err("%s : Mailbox timeout error \n", __func__);
apm_wfi_prepare = APM_ON;
mbox_free_channel(chan);
return ERR_TIMEOUT;
}
mbox_free_channel(chan);
return 0;
}
static int exynos8890_do_cl_dvfs_setup(unsigned int atlas_cl_limit, unsigned int apollo_cl_limit,
unsigned int g3d_cl_limit, unsigned int mif_cl_limit, unsigned int cl_period)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "setup";
msg[0] = (NONE << COMMAND_SHIFT) | (INIT_SET << INIT_MODE_SHIFT);
msg[1] = (atlas_cl_limit << ATLAS_SHIFT) | (apollo_cl_limit << APOLLO_SHIFT)
| (g3d_cl_limit << G3D_SHIFT) | (mif_cl_limit << MIF_SHIFT) | (cl_period << PERIOD_SHIFT);
msg[3] = TX_INTERRUPT_ENABLE;
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return 0;
/* out means turn off apm device and then mode change */
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_cl_dvfs_setup);
static int exynos8890_do_cl_dvfs_start(unsigned int cl_domain)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
/* CL-DVFS[29] start, command mode none */
msg[0] = CL_DVFS | (NONE << COMMAND_SHIFT);
msg[3] = ((cl_domain + 1) << CL_DOMAIN_SHIFT) | TX_INTERRUPT_ENABLE;
if (cl_domain == ID_CL1)
protocol_name = "cl_start(ATL)--";
else if (cl_domain == ID_CL0)
protocol_name = "cl_start(APO)--";
else if (cl_domain == ID_MIF)
protocol_name = "cl_start(MIF)--";
else if (cl_domain == ID_G3D)
protocol_name = "cl_start(G3D)--";
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return 0;
/* out means turn off apm device and then mode change */
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_cl_dvfs_start);
static int exynos8890_do_cl_dvfs_mode_enable(void)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret = 0;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "cl_mode_enable";
/* CL-DVFS[29] stop, command mode none */
msg[0] = (1 << CL_ALL_START_SHIFT);
msg[3] = (TX_INTERRUPT_ENABLE);
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
/* out means turn off apm device and then mode change */
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_cl_dvfs_mode_enable);
static int exynos8890_do_cl_dvfs_mode_disable(void)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret = 0;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "cl_mode_disable";
/* CL-DVFS[29] stop, command mode none */
msg[0] = (1 << CL_ALL_STOP_SHIFT);
msg[3] = (TX_INTERRUPT_ENABLE);
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
/* out means turn off apm device and then mode change */
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_cl_dvfs_mode_disable);
static int exynos8890_do_cl_dvfs_stop(unsigned int cl_domain, unsigned int level)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret = 0;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
if (cl_domain == ID_G3D) {
/* G3D driver not use level 0, 1 */
level = level + G3D_LV_OFFSET;
}
channel_ack_mode(&cl);
protocol_name = "cl_stop";
/* CL-DVFS[29] stop, command mode none */
msg[0] = (CL_DVFS_OFF << CL_DVFS_SHIFT) | (NONE << COMMAND_SHIFT);
msg[1] = level;
msg[3] = ((cl_domain + 1) << CL_DOMAIN_SHIFT) | (TX_INTERRUPT_ENABLE);
if (cl_domain == ID_CL1)
protocol_name = "cl_stop(ATL)++";
else if (cl_domain == ID_CL0)
protocol_name = "cl_stop(APO)++";
else if (cl_domain == ID_MIF)
protocol_name = "cl_stop(MIF)++";
else if (cl_domain == ID_G3D)
protocol_name = "cl_stop(G3D)++";
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
/* out means turn off apm device and then mode change */
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_cl_dvfs_stop);
static int exynos8890_do_g3d_power_on_noti_apm(void)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret = 0;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "g3d_power_on";
/* CL-DVFS[29] stop, command mode none */
msg[3] = TX_INTERRUPT_ENABLE | (1 << 13);
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return 0;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_g3d_power_on_noti_apm);
static int exynos8890_do_g3d_power_down_noti_apm(void)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret = 0;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "g3d_power_off";
/* CL-DVFS[29] stop, command mode none */
msg[3] = TX_INTERRUPT_ENABLE | (1 << 12);
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return 0;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_do_g3d_power_down_noti_apm);
/* exynos8890_apm_enter_wfi();
* This function send CM3 go to WFI message to CM3.
*/
int exynos8890_apm_enter_wfi(void)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
struct mbox_chan *chan;
mutex_lock(&cl_mutex);
if (apm_wfi_prepare == APM_TIMEOUT) {
mutex_unlock(&cl_mutex);
return 0;
}
channel_ack_mode(&cl);
protocol_name = "enter_wfi";
/* CL-DVFS[29] stop, command mode none */
msg[0] = 1 << 23;
msg[3] = TX_INTERRUPT_ENABLE;
chan = mbox_request_channel(&cl, 0);
if (IS_ERR(chan)) {
pr_err("mailbox : Did not make a mailbox channel\n");
mutex_unlock(&cl_mutex);
return PTR_ERR(chan);
}
if (!mbox_send_message(chan, (void *)msg)) {
}
mbox_free_channel(chan);
mutex_unlock(&cl_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_enter_wfi);
/**
* exynos8890_apm_update_bits(): Mask a value, after then write value.
* @type: Register pmic section (pm_section(0), rtc_section(1))
* @reg: register address
* @mask : masking value
* @value : Pointer to store write value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int exynos8890_apm_update_bits(unsigned int type, unsigned int reg,
unsigned int mask, unsigned int value)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret;
mutex_lock(&cl_mutex);
channel_ack_mode(&cl);
protocol_name = "update_bits";
/* CL-DVFS[29] stop, command mode write(0x0), mask mode enable */
msg[0] = ((type << PM_SECTION_SHIFT) | (MASK << MASK_SHIFT) | (mask));
msg[1] = reg;
msg[2] = value;
msg[3] = TX_INTERRUPT_ENABLE;
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_update_bits);
/**
* exynos8890_apm_write()
* @type: Register pmic section (pm_section(0), rtc_section(1))
* @reg: register address
* @value : Pointer to store write value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int exynos8890_apm_write(unsigned int type, unsigned int reg, unsigned int value)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
int ret;
mutex_lock(&cl_mutex);
channel_ack_mode(&cl);
protocol_name = "write";
/* CL-DVFS[29] stop, command mode write(0x0) */
msg[0] = (type << PM_SECTION_SHIFT);
msg[1] = reg;
msg[2] = value;
msg[3] = TX_INTERRUPT_ENABLE;
#ifdef CONFIG_EXYNOS_APM_VOLTAGE_DEBUG
if (reg == PMIC_MIF_OUT) {
mif_in_voltage = ((value * (u32)PMIC_STEP) + MIN_VOL);
} else if (reg == PMIC_ATL_OUT) {
atl_in_voltage = ((value * (u32)PMIC_STEP) + MIN_VOL);
} else if (reg == PMIC_APO_OUT) {
apo_in_voltage = ((value * (u32)PMIC_STEP) + MIN_VOL);
} else if (reg == PMIC_G3D_OUT) {
g3d_in_voltage = ((value * (u32)PMIC_STEP) + MIN_VOL);
}
#endif
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_write);
/**
* exynos8890_apm_bulk_write()
* @type: Register pmic section (pm_section(0), rtc_section(1))
* @reg: register address
* @*buf: write buffer section
* @value : Pointer to store write value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int exynos8890_apm_bulk_write(unsigned int type, unsigned char reg, unsigned char *buf, unsigned int count)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
unsigned int i;
int ret;
mutex_lock(&cl_mutex);
channel_ack_mode(&cl);
protocol_name = "bulk_write";
msg[0] = (type << PM_SECTION_SHIFT) | ((count-1) << MULTI_BYTE_CNT_SHIFT) | reg;
for (i = 0; i < count; i++) {
if (i < BYTE_4)
msg[1] |= buf[i] << BYTE_SHIFT * i;
else
msg[2] |= buf[i] << BYTE_SHIFT * (i - BYTE_4);
}
msg[3] = TX_INTERRUPT_ENABLE;
ret = exynos_send_message(&cl, msg);
if (ret == ERR_TIMEOUT || ret == ERR_OUT) {
data_history();
goto timeout;
} else if (ret) {
goto error;
}
mutex_unlock(&cl_mutex);
return ret;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
error :
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_bulk_write);
/**
* exynos8890_apm_read()
* @type: Register pmic section (pm_section(0), rtc_section(1))
* @reg: register address
* @*val: store read value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int exynos8890_apm_read(unsigned int type, unsigned int reg, unsigned int *val)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
struct mbox_chan *chan;
mutex_lock(&cl_mutex);
channel_ack_mode(&cl);
protocol_name = "read";
/* CL-DVFS[29] stop, command mode read(0x1) */
msg[0] = (READ_MODE << COMMAND_SHIFT) | (type << PM_SECTION_SHIFT);
msg[1] = reg;
msg[3] = TX_INTERRUPT_ENABLE;
chan = mbox_request_channel(&cl, 0);
if (IS_ERR(chan)) {
pr_err("mailbox : Did not make a mailbox channel\n");
mutex_unlock(&cl_mutex);
return PTR_ERR(chan);
}
if (!mbox_send_message(chan, (void *)msg)) {
*val = check_rx_data((void *)msg);
if (*val == APM_GPIO_ERR) {
pr_err("%s, gpio error\n", __func__);
mbox_free_channel(chan);
data_history();
goto timeout;
}
} else {
pr_err("%s : Mailbox timeout error \n", __func__);
apm_wfi_prepare = APM_ON;
mbox_free_channel(chan);
data_history();
goto timeout;
}
mbox_free_channel(chan);
mutex_unlock(&cl_mutex);
return 0;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_read);
/**
* exynos8890_apm_bulk_read()
* @type: Register pmic section (pm_section(0), rtc_section(1))
* @reg: register address
* @*buf: read buffer section
* @*count : read count
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int exynos8890_apm_bulk_read(unsigned int type, unsigned char reg, unsigned char *buf, unsigned int count)
{
u32 msg[MBOX_LEN] = {0, 0, 0, 0};
u32 result[2] = {0, 0};
unsigned int ret, i;
mutex_lock(&cl_mutex);
channel_ack_mode(&cl);
protocol_name = "bulk_read";
msg[0] = (READ_MODE << COMMAND_SHIFT) | (type << PM_SECTION_SHIFT)
| ((count-1) << MULTI_BYTE_CNT_SHIFT) | (reg);
msg[3] = TX_INTERRUPT_ENABLE;
ret = exynos_send_message_bulk_read(&cl, msg);
if (ret == ERR_TIMEOUT) {
data_history();
goto timeout;
}
result[0] = exynos_mailbox_reg_read(EXYNOS_MAILBOX_RX(1));
result[1] = exynos_mailbox_reg_read(EXYNOS_MAILBOX_RX(2));
for (i = 0; i < count; i++) {
if (i < BYTE_4)
buf[i] = (result[0] >> i * BYTE_SHIFT) & BYTE_MASK;
else
buf[i] = (result[1] >> (i - BYTE_4) * BYTE_SHIFT) & BYTE_MASK;
}
mutex_unlock(&cl_mutex);
return 0;
timeout :
exynos8890_apm_power_down();
apm_notifier_call_chain(APM_TIMEOUT);
mutex_unlock(&cl_mutex);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(exynos8890_apm_bulk_read);
void exynos_mbox_client_init(struct device *dev)
{
cl.dev = dev;
}
EXPORT_SYMBOL_GPL(exynos_mbox_client_init);
struct cl_ops exynos_cl_function_ops = {
.cl_dvfs_setup = exynos8890_do_cl_dvfs_setup,
.cl_dvfs_start = exynos8890_do_cl_dvfs_start,
.cl_dvfs_stop = exynos8890_do_cl_dvfs_stop,
.cl_dvfs_enable = exynos8890_do_cl_dvfs_mode_enable,
.cl_dvfs_disable = exynos8890_do_cl_dvfs_mode_disable,
.g3d_power_on = exynos8890_do_g3d_power_on_noti_apm,
.g3d_power_down = exynos8890_do_g3d_power_down_noti_apm,
.enter_wfi = exynos8890_apm_enter_wfi,
.apm_reset = exynos8890_apm_reset_release,
.apm_power_up = exynos8890_apm_power_up,
.apm_power_down = exynos8890_apm_power_down,
};
struct apm_ops exynos_apm_function_ops = {
.apm_update_bits = exynos8890_apm_update_bits,
.apm_write = exynos8890_apm_write,
.apm_bulk_write = exynos8890_apm_bulk_write,
.apm_read = exynos8890_apm_read,
.apm_bulk_read = exynos8890_apm_bulk_read,
};