kernel_samsung_a53x/drivers/misc/samsung/scsc/mifpmuman.c
2024-06-15 16:02:09 -03:00

342 lines
8 KiB
C
Executable file

/****************************************************************************
*
* Copyright (c) 2014 - 2021 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
/* uses */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <scsc/scsc_logring.h>
#include "scsc_mif_abs.h"
#include <linux/module.h>
#define PMU_MSG_TIMEOUT (HZ)
#ifdef CONFIG_SOC_S5E8825
/* uses */
#include "pmu_host_if.h"
#endif
static uint pmu_cmd_timeout = 1;
module_param(pmu_cmd_timeout, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pmu_cmd_timeout, "PMU command timeout in seconds, default 1");
/* Implements */
#include "mifpmuman.h"
#ifdef CONFIG_SOC_S5E8825
static void mifpmu_cmd_th_to_string(u32 cmd, char *buf, u8 buffer_size)
{
memset(buf, 0, buffer_size);
switch (cmd) {
case PMU_AP_MSG_COMMAND_COMPLETE_IND:
strncpy(buf, "CMD COMPLETE", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_ERROR_WPAN:
strncpy(buf, "ERROR WPAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_ERROR_WLAN:
strncpy(buf, "ERROR WLAN", buffer_size);
break;
default:
strncpy(buf, "Incorrect command", buffer_size);
break;
}
}
static void mifpmu_cmd_fh_to_string(u32 cmd, char *buf, u8 buffer_size)
{
memset(buf, 0, buffer_size);
switch (cmd) {
case PMU_AP_MSG_SUBSYS_START_WPAN:
strncpy(buf, "START WPAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_START_WLAN:
strncpy(buf, "START WLAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_START_WPAN_WLAN:
strncpy(buf, "START WLAN and WPAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_RESET_WPAN:
strncpy(buf, "RESET WPAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_RESET_WLAN:
strncpy(buf, "RESET WLAN", buffer_size);
break;
case PMU_AP_MSG_SUBSYS_RESET_WPAN_WLAN:
strncpy(buf, "RESET WLAN and WPAN", buffer_size);
break;
default:
strncpy(buf, "Incorrect command", buffer_size);
break;
}
}
#endif /* CONFIG_SOC_S5E8825 */
static void mifpmu_isr(int irq, void *data)
{
struct mifpmuman *pmu = (struct mifpmuman *)data;
struct scsc_mif_abs *mif;
#ifdef CONFIG_SOC_S5E8825
u32 cmd;
char cmd_string[20];
#endif
/* Get abs implementation */
mif = pmu->mif;
#ifdef CONFIG_SOC_S5E8825
pmu->last_msg = cmd = (u32)(mif->get_mbox_pmu(mif));
mifpmu_cmd_th_to_string(cmd, cmd_string, 20);
SCSC_TAG_INFO(MXMAN, "Received PMU IRQ cmd [%s] 0x%x\n", cmd_string, cmd);
switch (cmd) {
case PMU_AP_MSG_COMMAND_COMPLETE_IND:
complete(&pmu->msg_ack);
break;
case PMU_AP_MSG_SUBSYS_ERROR_WPAN:
if (pmu->pmu_irq_handler)
pmu->pmu_irq_handler(pmu->irq_data, MIFPMU_ERROR_WPAN);
else
SCSC_TAG_ERR(MXMAN, "No handler for cmd 0%x\n", cmd);
break;
case PMU_AP_MSG_SUBSYS_ERROR_WLAN:
if (pmu->pmu_irq_handler)
pmu->pmu_irq_handler(pmu->irq_data, MIFPMU_ERROR_WLAN);
else
SCSC_TAG_ERR(MXMAN, "No handler for cmd 0%x\n", cmd);
break;
default:
SCSC_TAG_ERR(MXMAN, "Command not recognized. cmd 0x%x\n", cmd);
return;
}
#else
pmu->last_msg = (enum pmu_msg)(mif->get_mbox_pmu(mif));
SCSC_TAG_INFO(MXMAN, "Received PMU IRQ\n");
complete(&pmu->msg_ack);
#endif
}
int mifpmuman_init(struct mifpmuman *pmu, struct scsc_mif_abs *mif,
mifpmuisr_handler handler, void *irq_data)
{
if (pmu->in_use)
return -EBUSY;
mutex_init(&pmu->lock);
pmu->in_use = true;
pmu->pmu_irq_handler = handler;
pmu->irq_data = irq_data;
/* register isr with mif abstraction */
mif->irq_reg_pmu_handler(mif, mifpmu_isr, (void *)pmu);
/* cache mif */
pmu->mif = mif;
init_completion(&pmu->msg_ack);
return 0;
}
int mifpmuman_load_fw(struct mifpmuman *pmu, int *ka_patch, size_t ka_patch_len)
{
struct scsc_mif_abs *mif;
int ret;
mutex_lock(&pmu->lock);
if (!pmu->in_use) {
SCSC_TAG_ERR(MXMAN, "PMU not initialized\n");
mutex_unlock(&pmu->lock);
return -ENODEV;
}
/* Get abs implementation */
SCSC_TAG_INFO(MXMAN, "ka_patch : 0x%x\n", ka_patch);
SCSC_TAG_INFO(MXMAN, "ka_patch_len : 0x%x\n", ka_patch_len);
mif = pmu->mif;
ret = mif->load_pmu_fw(mif, ka_patch, ka_patch_len);
mutex_unlock(&pmu->lock);
return ret;
}
#ifdef CONFIG_SOC_S5E8825
static int mifpmuman_send_msg_blocking(struct mifpmuman *pmu, u32 msg)
{
struct scsc_mif_abs *mif;
int ret;
char cmd_string[20];
mifpmu_cmd_fh_to_string(msg, cmd_string, 20);
SCSC_TAG_INFO(MXMAN, "Send PMU cmd [%s] 0x%x\n", cmd_string, msg);
if (!pmu->in_use) {
SCSC_TAG_ERR(MXMAN, "PMU not initialized\n");
return -ENODEV;
}
/* Get abs implementation */
mif = pmu->mif;
ret = mif->set_mbox_pmu(mif, msg);
if (wait_for_completion_timeout(&pmu->msg_ack, pmu_cmd_timeout * PMU_MSG_TIMEOUT) == 0) {
SCSC_TAG_ERR(MXMAN, "Timeout waiting for msg ACK\n");
return -ETIMEDOUT;
}
/* reinit so completion can be re-used */
reinit_completion(&pmu->msg_ack);
return ret;
}
#else
int mifpmuman_send_msg(struct mifpmuman *pmu, enum pmu_msg msg)
{
struct scsc_mif_abs *mif;
int ret;
SCSC_TAG_INFO(MXMAN, "Send PMU message %s\n", pmu_msg_string[msg]);
mutex_lock(&pmu->lock);
if (!pmu->in_use) {
mutex_unlock(&pmu->lock);
return -ENODEV;
}
/* Get abs implementation */
mif = pmu->mif;
ret = mif->set_mbox_pmu(mif, msg);
if (wait_for_completion_timeout(&pmu->msg_ack, pmu_cmd_timeout * PMU_MSG_TIMEOUT) == 0) {
SCSC_TAG_ERR(MXMAN, "Timeout waiting for msg ACK\n");
mutex_unlock(&pmu->lock);
return -ETIMEDOUT;
}
SCSC_TAG_INFO(MXMAN, "Received PMU message %s\n",
pmu_msg_string[pmu->last_msg]);
/* reinit so completion can be re-used */
reinit_completion(&pmu->msg_ack);
mutex_unlock(&pmu->lock);
return ret;
}
#endif
#ifdef CONFIG_SOC_S5E8825
int mifpmuman_start_subsystem(struct mifpmuman *pmu, enum scsc_subsystem sub)
{
u32 msg;
int r;
mutex_lock(&pmu->lock);
switch (sub) {
case SCSC_SUBSYSTEM_WLAN:
SCSC_TAG_INFO(MXMAN, "Booting WLAN subsystem\n");
msg = PMU_AP_MSG_SUBSYS_START_WLAN;
break;
case SCSC_SUBSYSTEM_WPAN:
SCSC_TAG_INFO(MXMAN, "Booting WPAN subsystem\n");
msg = PMU_AP_MSG_SUBSYS_START_WPAN;
break;
case SCSC_SUBSYSTEM_WLAN_WPAN:
SCSC_TAG_INFO(MXMAN, "Booting WLAN and WPAN subsystems\n");
msg = PMU_AP_MSG_SUBSYS_START_WPAN_WLAN;
break;
default:
SCSC_TAG_ERR(MXMAN, "Subsystem %d not found\n", sub);
mutex_unlock(&pmu->lock);
return -EIO;
}
r = mifpmuman_send_msg_blocking(pmu, msg);
mutex_unlock(&pmu->lock);
return r;
}
int mifpmuman_stop_subsystem(struct mifpmuman *pmu, enum scsc_subsystem sub)
{
u32 msg;
int r;
mutex_lock(&pmu->lock);
switch (sub) {
case SCSC_SUBSYSTEM_WLAN:
SCSC_TAG_INFO(MXMAN, "Resetting WLAN subsystem\n");
msg = PMU_AP_MSG_SUBSYS_RESET_WLAN;
break;
case SCSC_SUBSYSTEM_WPAN:
SCSC_TAG_INFO(MXMAN, "Stopping WPAN subsystem\n");
msg = PMU_AP_MSG_SUBSYS_RESET_WPAN;
break;
case SCSC_SUBSYSTEM_WLAN_WPAN:
SCSC_TAG_INFO(MXMAN, "Stopping WLAN and WPAN subsystems\n");
msg = PMU_AP_MSG_SUBSYS_RESET_WPAN_WLAN;
break;
default:
SCSC_TAG_ERR(MXMAN, "Subsystem %d not found\n", sub);
mutex_unlock(&pmu->lock);
return -EIO;
}
r = mifpmuman_send_msg_blocking(pmu, msg);
mutex_unlock(&pmu->lock);
return r;
}
int mifpmuman_force_monitor_mode_subsystem(struct mifpmuman *pmu, enum scsc_subsystem sub)
{
u32 msg;
int r;
mutex_lock(&pmu->lock);
switch (sub) {
case SCSC_SUBSYSTEM_WLAN:
SCSC_TAG_INFO(MXMAN, "Setting WLAN subsystem in monitor mode\n");
msg = PMU_AP_CFG_MONITOR_WLAN;
break;
case SCSC_SUBSYSTEM_WPAN:
SCSC_TAG_INFO(MXMAN, "Setting WPAN subsystem in monitor mode\n");
msg = PMU_AP_CFG_MONITOR_WPAN;
break;
case SCSC_SUBSYSTEM_WLAN_WPAN:
SCSC_TAG_INFO(MXMAN, "Setting WLAN and WPAN subsystems in monitor mode\n");
msg = PMU_AP_CFG_MONITOR_WPAN_WLAN;
break;
default:
SCSC_TAG_ERR(MXMAN, "Subsystem %d not found\n", sub);
mutex_unlock(&pmu->lock);
return -EIO;
}
r = mifpmuman_send_msg_blocking(pmu, msg);
mutex_unlock(&pmu->lock);
return r;
}
#endif
int mifpmuman_deinit(struct mifpmuman *pmu)
{
mutex_lock(&pmu->lock);
if (!pmu->in_use) {
mutex_unlock(&pmu->lock);
return -ENODEV;
}
pmu->in_use = false;
mutex_unlock(&pmu->lock);
return 0;
}