Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,66 @@
config EXYNOS_APM
bool "APM Driver"
select MAILBOX
select EXYNOS_MBOX
if EXYNOS_APM
config EXYNOS_MBOX
tristate "Exynos Mailbox Controller"
help
An implementation of the Samsung Interprocessor Communication
Mailbox (IPCM). It is used to send short messages between CortexM3 cores
and the Power Control Processor or Power Coprocessor firmware.
Say Y here if you want to use the Exynos mailbox support.
config EXYNOS8890_APM
bool "EXYNOS8890 APM Driver"
default y
depends on SOC_EXYNOS8890
menu "EXYNOS_CL_DVFS"
config EXYNOS_CL_DVFS_CPU
bool "EXYNOS_CL_DVFS_CPU"
help
CPU closed loop dvfs feature
config EXYNOS_CL_DVFS_G3D
bool "EXYNOS_CL_DVFS_G3D"
help
G3D closed loop dvfs feature
config EXYNOS_CL_DVFS_MIF
bool "EXYNOS_CL_DVFS_MIF"
help
MIF closed loop dvfs feature
endmenu
choice
prompt "Default communction mode"
depends on EXYNOS_MBOX
default EXYNOS_MBOX_DEFAULT_POLLING
help
config EXYNOS_MBOX_DEFAULT_INTERRUPT
bool "Interrupt"
select EXYNOS_MBOX_INTERRUPT
help
This select is interrupt mode.
config EXYNOS_MBOX_DEFAULT_POLLING
bool "Polling"
select EXYNOS_MBOX_POLLING
help
This select is interrupt mode.
endchoice
config EXYNOS_MBOX_INTERRUPT
depends on EXYNOS_MBOX
tristate "Interrupt mode"
config EXYNOS_MBOX_POLLING
depends on EXYNOS_MBOX
tristate "Polling mode"
config EXYNOS_APM_VOLTAGE_DEBUG
bool "APM Voltage Debug"
default y
endif

View file

@ -0,0 +1,2 @@
# SAMSUNG MAILBOX API
obj-$(CONFIG_EXYNOS8890_APM) += mailbox-exynos8.o apm-exynos.o apm-exynos8890.o

View file

@ -0,0 +1,339 @@
/* linux/arch/arm/mach-exynos/apm-exynos.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS - 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/mailbox-exynos.h>
#include <linux/apm-exynos.h>
#include <asm/io.h>
#include <soc/samsung/asv-exynos.h>
/* Add OPS */
struct cl_ops *cl_ops;
int apm_wfi_prepare = APM_ON;
static int cm3_status;
static unsigned int cl_mode_status = CL_ON;
extern struct cl_ops exynos_cl_function_ops;
static DEFINE_MUTEX(cl_lock);
void cl_dvfs_lock(void)
{
mutex_lock(&cl_lock);
}
EXPORT_SYMBOL_GPL(cl_dvfs_lock);
void cl_dvfs_unlock(void)
{
mutex_unlock(&cl_lock);
}
EXPORT_SYMBOL_GPL(cl_dvfs_unlock);
/* Routines for PM-transition notifications */
static BLOCKING_NOTIFIER_HEAD(apm_chain_head);
int register_apm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&apm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_apm_notifier);
int unregister_apm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&apm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(unregister_apm_notifier);
int apm_notifier_call_chain(unsigned long val)
{
int ret;
ret = blocking_notifier_call_chain(&apm_chain_head, val, NULL);
return notifier_to_errno(ret);
}
/* exynos_apm_reset_release()
* exynos_apm_reset_release set PMU register.
*/
void exynos_apm_reset_release(void)
{
cl_ops->apm_reset();
}
/* exynos_apm_power_up()
* exynos_apm_power_up set PMU register.
*/
void exynos_apm_power_up(void)
{
cl_ops->apm_power_up();
}
/* exynos_apm_power_down()
* exynos_apm_power_down set PMU register.
*/
void exynos_apm_power_down(void)
{
cl_ops->apm_power_down();
}
/* exynos_cl_dvfs_setup()
* exynos_cl_dvfs_setup set voltage margin limit and period.
*/
int exynos_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)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
ret = cl_ops->cl_dvfs_setup(atlas_cl_limit, apollo_cl_limit,
g3d_cl_limit, mif_cl_limit, cl_period);
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_cl_dvfs_setup);
/* exynos7420_cl_dvfs_start()
* cl_dvfs_start means os send cl_dvfs start command.
* We change voltage and frequency, after than start cl-dvfs.
*/
int exynos_cl_dvfs_start(unsigned int cl_domain)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
if (cl_mode_status)
ret = cl_ops->cl_dvfs_start(cl_domain);
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_cl_dvfs_start);
/* exynos_cl_dvfs_stop()
* cl_dvfs_stop means os send cl_dvfs stop command to CM3.
* We need change voltage and frequency. first, we stop cl-dvfs.
*/
int exynos_cl_dvfs_stop(unsigned int cl_domain, unsigned int level)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
if (cl_mode_status)
ret = cl_ops->cl_dvfs_stop(cl_domain, level);
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_cl_dvfs_stop);
/* exynos7420_cl_dvfs_mode_disable()
* cl_dvfs_stop means os send cl_dvfs stop command to CM3.
* We need change voltage and frequency. first, we stop cl-dvfs.
*/
int exynos_cl_dvfs_mode_enable(void)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
ret = cl_ops->cl_dvfs_enable();
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_cl_dvfs_mode_enable);
/* exynos7420_cl_dvfs_mode_disable()
* cl_dvfs_stop means os send cl_dvfs stop command to CM3.
* We need change voltage and frequency. first, we stop cl-dvfs.
*/
int exynos_cl_dvfs_mode_disable(void)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
ret = cl_ops->cl_dvfs_disable();
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_cl_dvfs_mode_disable);
/* exynos_g3d_power_on_noti_apm()
* APM driver notice g3d power on status to CM3.
*/
int exynos_g3d_power_on_noti_apm(void)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
ret = cl_ops->g3d_power_on();
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_g3d_power_on_noti_apm);
/* exynos7420_g3d_power_on_noti_apm()
* APM driver notice g3d power off status to CM3.
*/
int exynos_g3d_power_down_noti_apm(void)
{
int ret = 0;
sec_core_lock();
cl_dvfs_lock();
ret = cl_ops->g3d_power_down();
cl_dvfs_unlock();
sec_core_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(exynos_g3d_power_down_noti_apm);
/* exynos_apm_enter_wfi();
* This function send CM3 go to WFI message to CM3.
*/
int exynos_apm_enter_wfi(void)
{
cl_ops->enter_wfi();
return 0;
}
EXPORT_SYMBOL_GPL(exynos_apm_enter_wfi);
static int exynos_cm3_status_show(struct seq_file *buf, void *d)
{
/* Show pmic communcation mode */
if (cm3_status == HSI2C_MODE) seq_printf(buf, "mode : HSI2C \n");
else if (cm3_status == APM_MODE) seq_printf(buf, "mode : APM \n");
else if (cm3_status == APM_TIMOUT) seq_printf(buf, "mode : HSI2C (CM3 timeout) \n");
return 0;
}
int cm3_status_open(struct inode *inode, struct file *file)
{
return single_open(file, exynos_cm3_status_show, inode->i_private);
}
#ifdef CONFIG_EXYNOS_MBOX
static int exynos_apm_function_notifier(struct notifier_block *notifier,
unsigned long pm_event, void *v)
{
switch (pm_event) {
case APM_READY:
cm3_status = APM_MODE;
apm_wfi_prepare = APM_OFF;
#ifdef CONFIG_EXYNOS_MBOX_INTERRUPT
samsung_mbox_enable_irq();
#endif
pr_info("mailbox: hsi2c -> apm mode \n");
break;
case APM_SLEEP:
#ifdef CONFIG_EXYNOS_MBOX_INTERRUPT
if (cm3_status != APM_TIMOUT)
samsung_mbox_disable_irq();
#endif
if (cm3_status == APM_MODE)
pr_info("mailbox: apm -> hsi2c mode \n");
cm3_status = HSI2C_MODE;
apm_wfi_prepare = APM_ON;
break;
case APM_TIMEOUT:
cm3_status = APM_TIMOUT;
apm_wfi_prepare = APM_WFI_TIMEOUT;
#ifdef CONFIG_EXYNOS_MBOX_INTERRUPT
samsung_mbox_disable_irq();
#endif
pr_info("mailbox: apm -> hsi2c mode(timeout) \n");
break;
case CL_ENABLE:
cl_mode_status = CL_ON;
break;
case CL_DISABLE:
cl_mode_status = CL_OFF;
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block exynos_apm_notifier = {
.notifier_call = exynos_apm_function_notifier,
};
#endif
static int exynos_apm_probe(struct platform_device *pdev)
{
#ifdef CONFIG_EXYNOS_MBOX
register_apm_notifier(&exynos_apm_notifier);
cl_ops = &exynos_cl_function_ops;
exynos_mbox_client_init(&pdev->dev);
#endif
return 0;
}
static int exynos_apm_remove(struct platform_device *pdev)
{
#ifdef CONFIG_EXYNOS_MBOX
unregister_apm_notifier(&exynos_apm_notifier);
#endif
return 0;
}
static const struct of_device_id apm_smc_match[] = {
{ .compatible = "samsung,exynos-apm" },
{},
};
static struct platform_driver exynos_apm_driver = {
.probe = exynos_apm_probe,
.remove = exynos_apm_remove,
.driver = {
.name = "exynos-apm-driver",
.owner = THIS_MODULE,
.of_match_table = apm_smc_match,
},
};
static int __init exynos_apm_init(void)
{
return platform_driver_register(&exynos_apm_driver);
}
fs_initcall(exynos_apm_init);
static void __exit exynos_apm_exit(void)
{
platform_driver_unregister(&exynos_apm_driver);
}
module_exit(exynos_apm_exit);

View file

@ -0,0 +1,852 @@
/* 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,
};

File diff suppressed because it is too large Load diff