1634 lines
40 KiB
C
Executable File
1634 lines
40 KiB
C
Executable File
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
#define pr_fmt(msg) "slatecom_dev:" msg
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/version.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/device.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <asm/dma.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/remoteproc.h>
|
|
#include <linux/remoteproc/qcom_rproc.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/qseecom_kernel.h>
|
|
#include <linux/soc/qcom/slatecom_interface.h>
|
|
#include <linux/soc/qcom/slate_events_bridge_intf.h>
|
|
|
|
#include <uapi/linux/slatecom_interface.h>
|
|
|
|
#include "slatecom.h"
|
|
#include "slatecom_rpmsg.h"
|
|
|
|
|
|
#define SLATECOM "slate_com_dev"
|
|
#define SLATEDAEMON_LDO09_LPM_VTG 0
|
|
#define SLATEDAEMON_LDO09_NPM_VTG 10000
|
|
#define SLATEDAEMON_LDO03_LPM_VTG 0
|
|
#define SLATEDAEMON_LDO03_NPM_VTG 10000
|
|
#define MPPS_DOWN_EVENT_TO_SLATE_TIMEOUT 3000
|
|
#define ADSP_DOWN_EVENT_TO_SLATE_TIMEOUT 3000
|
|
#define MAX_APP_NAME_SIZE 100
|
|
#define COMPAT_PTR(val) ((void *)((uint64_t)val & 0xffffffffUL))
|
|
#define __QAPI_VERSION_MAJOR_SHIFT (24)
|
|
#define __QAPI_VERSION_MINOR_SHIFT (16)
|
|
#define __QAPI_VERSION_NIT_SHIFT (0)
|
|
#define __QAPI_VERSION_MAJOR_MASK (0xff000000)
|
|
#define __QAPI_VERSION_MINOR_MASK (0x00ff0000)
|
|
#define __QAPI_VERSION_NIT_MASK (0x0000ffff)
|
|
#define SCOM_GLINK_INTENT_SIZE 308
|
|
|
|
/*pil_slate_intf.h*/
|
|
#define RESULT_SUCCESS 0
|
|
#define RESULT_FAILURE -1
|
|
|
|
#define SLATECOM_INTF_N_FILES 2
|
|
#define BUF_SIZE 10
|
|
#define SECURE_APP "slateapp"
|
|
|
|
static char btss_state[BUF_SIZE] = "offline";
|
|
static char dspss_state[BUF_SIZE] = "offline";
|
|
static void ssr_register(void);
|
|
static int setup_pmic_gpio15(void);
|
|
static unsigned int pmic_gpio15 = -1;
|
|
static int slate_boot_status;
|
|
struct rproc *slatecom_rproc;
|
|
|
|
/* tzapp command list.*/
|
|
enum slate_tz_commands {
|
|
SLATE_RPROC_RAMDUMP,
|
|
SLATE_RPROC_IMAGE_LOAD,
|
|
SLATE_RPROC_AUTH_MDT,
|
|
SLATE_RPROC_DLOAD_CONT,
|
|
SLATE_RPROC_GET_SLATE_VERSION,
|
|
SLATE_RPROC_SHUTDOWN,
|
|
SLATE_RPROC_DUMPINFO,
|
|
SLATE_RPROC_UP_INFO,
|
|
SLATE_RPROC_RESET,
|
|
};
|
|
|
|
/* tzapp slate request.*/
|
|
struct tzapp_slate_req {
|
|
uint64_t address_fw;
|
|
uint32_t size_fw;
|
|
uint8_t tzapp_slate_cmd;
|
|
} __packed;
|
|
|
|
/* tzapp slate response.*/
|
|
struct tzapp_slate_rsp {
|
|
uint32_t tzapp_slate_cmd;
|
|
uint32_t slate_info_len;
|
|
int32_t status;
|
|
uint32_t slate_info[100];
|
|
} __packed;
|
|
|
|
enum {
|
|
SSR_DOMAIN_SLATE,
|
|
SSR_DOMAIN_MODEM,
|
|
SSR_DOMAIN_ADSP,
|
|
SSR_DOMAIN_MAX,
|
|
};
|
|
|
|
enum slatecom_state {
|
|
SLATECOM_STATE_UNKNOWN,
|
|
SLATECOM_STATE_INIT,
|
|
SLATECOM_STATE_GLINK_OPEN,
|
|
SLATECOM_STATE_SLATE_SSR
|
|
};
|
|
|
|
struct rf_power_clk_data {
|
|
struct clk *clk; /* clock regulator handle */
|
|
const char *name; /* clock name */
|
|
bool is_enabled; /* is this clock enabled? */
|
|
};
|
|
|
|
struct slatedaemon_priv {
|
|
void *pil_h;
|
|
int app_status;
|
|
unsigned long attrs;
|
|
u32 cmd_status;
|
|
struct device *platform_dev;
|
|
bool slatecom_rpmsg;
|
|
bool slate_resp_cmplt;
|
|
bool slate_unload;
|
|
void *lhndl;
|
|
wait_queue_head_t link_state_wait;
|
|
char rx_buf[SCOM_GLINK_INTENT_SIZE];
|
|
struct work_struct slatecom_up_work;
|
|
struct work_struct slatecom_down_work;
|
|
struct mutex glink_mutex;
|
|
struct mutex slatecom_state_mutex;
|
|
struct mutex cmdsync_lock;
|
|
enum slatecom_state slatecom_current_state;
|
|
struct workqueue_struct *slatecom_wq;
|
|
struct wakeup_source slatecom_ws;
|
|
struct qseecom_handle *qseecom_handle;
|
|
struct rf_power_clk_data *rf_clk_2; /* rf clock */
|
|
};
|
|
|
|
static void *slatecom_intf_drv;
|
|
|
|
struct slate_event {
|
|
enum slate_event_type e_type;
|
|
};
|
|
|
|
struct service_info {
|
|
const char name[32];
|
|
const char ssr_domains[32];
|
|
int domain_id;
|
|
void *handle;
|
|
struct notifier_block *nb;
|
|
};
|
|
|
|
static struct slatedaemon_priv *dev;
|
|
static DEFINE_MUTEX(slate_char_mutex);
|
|
static struct cdev slate_cdev;
|
|
static struct class *slate_class;
|
|
struct device *dev_ret;
|
|
static dev_t slate_dev;
|
|
static int device_open;
|
|
static void *handle;
|
|
static bool twm_exit;
|
|
static bool slate_app_running;
|
|
static bool slate_dsp_error;
|
|
static bool slate_bt_error;
|
|
static struct slatecom_open_config_type config_type;
|
|
static DECLARE_COMPLETION(slate_modem_down_wait);
|
|
static DECLARE_COMPLETION(slate_adsp_down_wait);
|
|
static struct srcu_notifier_head slatecom_notifier_chain;
|
|
static struct platform_device *slate_pdev;
|
|
struct kobject *kobj_ref;
|
|
|
|
static ssize_t slate_bt_state_sysfs_read
|
|
(struct kobject *class, struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return scnprintf(buf, BUF_SIZE, btss_state);
|
|
}
|
|
|
|
static ssize_t slate_dsp_state_sysfs_read
|
|
(struct kobject *class, struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return scnprintf(buf, BUF_SIZE, dspss_state);
|
|
}
|
|
|
|
struct kobj_attribute slatecom_attr[] = {
|
|
{
|
|
.attr = {
|
|
.name = "slate_bt_state",
|
|
.mode = 0644
|
|
},
|
|
.show = slate_bt_state_sysfs_read,
|
|
},
|
|
{
|
|
.attr = {
|
|
.name = "slate_dsp_state",
|
|
.mode = 0644
|
|
},
|
|
.show = slate_dsp_state_sysfs_read,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* send_uevent(): send events to user space
|
|
* pce : ssr event handle value
|
|
* Return: 0 on success, standard Linux error code on error
|
|
*
|
|
* It adds pce value to event and broadcasts to user space.
|
|
*/
|
|
static int send_uevent(struct slate_event *pce)
|
|
{
|
|
char event_string[32];
|
|
char *envp[2] = { event_string, NULL };
|
|
|
|
snprintf(event_string, ARRAY_SIZE(event_string),
|
|
"SLATE_EVENT=%d", pce->e_type);
|
|
return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp);
|
|
}
|
|
|
|
void slatecom_intf_notify_glink_channel_state(bool state)
|
|
{
|
|
struct slatedaemon_priv *dev =
|
|
container_of(slatecom_intf_drv, struct slatedaemon_priv, lhndl);
|
|
|
|
pr_debug("%s: slate_ctrl channel state: %d\n", __func__, state);
|
|
dev->slatecom_rpmsg = state;
|
|
wake_up(&dev->link_state_wait);
|
|
}
|
|
|
|
void slatecom_rx_msg(void *data, int len)
|
|
{
|
|
struct slatedaemon_priv *dev =
|
|
container_of(slatecom_intf_drv, struct slatedaemon_priv, lhndl);
|
|
|
|
if (len > SCOM_GLINK_INTENT_SIZE) {
|
|
pr_err("Invalid slatecom_intf glink intent size\n");
|
|
return;
|
|
}
|
|
dev->slate_resp_cmplt = true;
|
|
wake_up(&dev->link_state_wait);
|
|
memcpy(dev->rx_buf, data, len);
|
|
}
|
|
|
|
static int slatecom_tx_msg(struct slatedaemon_priv *dev, void *msg, size_t len)
|
|
{
|
|
int rc = 0;
|
|
uint8_t resp = 0;
|
|
|
|
mutex_lock(&dev->glink_mutex);
|
|
__pm_stay_awake(&dev->slatecom_ws);
|
|
if (!dev->slatecom_rpmsg) {
|
|
pr_err("slatecom-rpmsg is not probed yet, waiting for it to be probed\n");
|
|
goto err_ret;
|
|
}
|
|
rc = slatecom_rpmsg_tx_msg(msg, len);
|
|
|
|
/* wait for sending command to SLATE */
|
|
rc = wait_event_timeout(dev->link_state_wait,
|
|
(rc == 0), msecs_to_jiffies(TIMEOUT_MS));
|
|
if (rc == 0) {
|
|
pr_err("failed to send command to SLATE %d\n", rc);
|
|
goto err_ret;
|
|
}
|
|
|
|
/* wait for getting response from SLATE */
|
|
rc = wait_event_timeout(dev->link_state_wait,
|
|
dev->slate_resp_cmplt,
|
|
msecs_to_jiffies(TIMEOUT_MS));
|
|
if (rc == 0) {
|
|
pr_err("failed to get SLATE response %d\n", rc);
|
|
goto err_ret;
|
|
}
|
|
dev->slate_resp_cmplt = false;
|
|
/* check SLATE response */
|
|
resp = *(uint8_t *)dev->rx_buf;
|
|
if (resp == 0x01) {
|
|
pr_err("Bad SLATE response\n");
|
|
rc = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
rc = 0;
|
|
|
|
err_ret:
|
|
__pm_relax(&dev->slatecom_ws);
|
|
mutex_unlock(&dev->glink_mutex);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* send_state_change_cmd send state transition event to Slate
|
|
* and wait for the response.
|
|
* The response is returned to the caller.
|
|
*/
|
|
static int send_state_change_cmd(uint64_t state)
|
|
{
|
|
int ret = 0;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
struct slatedaemon_priv *dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
|
|
switch (state) {
|
|
case STATE_TWM_ENTER:
|
|
msg_header.opcode = GMI_MGR_ENTER_TWM;
|
|
break;
|
|
case STATE_DS_ENTER:
|
|
msg_header.opcode = GMI_MGR_ENTER_TRACKER_DS;
|
|
break;
|
|
case STATE_DS_EXIT:
|
|
msg_header.opcode = GMI_MGR_EXIT_TRACKER_DS;
|
|
break;
|
|
case STATE_S2D_ENTER:
|
|
msg_header.opcode = GMI_MGR_ENTER_TRACKER_DS;
|
|
break;
|
|
case STATE_S2D_EXIT:
|
|
msg_header.opcode = GMI_MGR_EXIT_TRACKER_DS;
|
|
break;
|
|
default:
|
|
pr_err("Invalid MSM State transtion cmd\n");
|
|
break;
|
|
}
|
|
ret = slatecom_tx_msg(dev, &msg_header.opcode, sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("MSM State transtion event:%llu cmd failed\n", state);
|
|
return ret;
|
|
}
|
|
|
|
static int slatecom_char_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&slate_char_mutex);
|
|
if (device_open == 1) {
|
|
pr_err("device is already open\n");
|
|
mutex_unlock(&slate_char_mutex);
|
|
return -EBUSY;
|
|
}
|
|
device_open++;
|
|
handle = slatecom_open(&config_type);
|
|
mutex_unlock(&slate_char_mutex);
|
|
if (IS_ERR(handle)) {
|
|
device_open = 0;
|
|
ret = PTR_ERR(handle);
|
|
handle = NULL;
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int slatechar_read_cmd(struct slate_ui_data *fui_obj_msg,
|
|
unsigned int type)
|
|
{
|
|
void *read_buf;
|
|
int ret = 0;
|
|
void __user *result = (void *)
|
|
(uintptr_t)fui_obj_msg->result;
|
|
|
|
read_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t),
|
|
GFP_KERNEL);
|
|
if (read_buf == NULL)
|
|
return -ENOMEM;
|
|
switch (type) {
|
|
case SLATECOM_REG_READ:
|
|
ret = slatecom_reg_read(handle, fui_obj_msg->cmd,
|
|
fui_obj_msg->num_of_words,
|
|
read_buf);
|
|
break;
|
|
case SLATECOM_AHB_READ:
|
|
ret = slatecom_ahb_read(handle,
|
|
fui_obj_msg->slate_address,
|
|
fui_obj_msg->num_of_words,
|
|
read_buf);
|
|
break;
|
|
}
|
|
if (!ret && copy_to_user(result, read_buf,
|
|
fui_obj_msg->num_of_words * sizeof(uint32_t))) {
|
|
pr_err("copy to user failed\n");
|
|
ret = -EFAULT;
|
|
}
|
|
if (ret < 0)
|
|
pr_err("%s failed\n", __func__);
|
|
kfree(read_buf);
|
|
return ret;
|
|
}
|
|
|
|
static int slatechar_write_cmd(struct slate_ui_data *fui_obj_msg, unsigned int type)
|
|
{
|
|
void *write_buf;
|
|
int ret = -EINVAL;
|
|
void __user *write = (void *)
|
|
(uintptr_t)fui_obj_msg->write;
|
|
|
|
write_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t),
|
|
GFP_KERNEL);
|
|
if (write_buf == NULL)
|
|
return -ENOMEM;
|
|
write_buf = memdup_user(write,
|
|
fui_obj_msg->num_of_words * sizeof(uint32_t));
|
|
if (IS_ERR(write_buf)) {
|
|
ret = PTR_ERR(write_buf);
|
|
kfree(write_buf);
|
|
return ret;
|
|
}
|
|
switch (type) {
|
|
case SLATECOM_REG_WRITE:
|
|
ret = slatecom_reg_write(handle, fui_obj_msg->cmd,
|
|
fui_obj_msg->num_of_words,
|
|
write_buf);
|
|
break;
|
|
case SLATECOM_AHB_WRITE:
|
|
ret = slatecom_ahb_write(handle,
|
|
fui_obj_msg->slate_address,
|
|
fui_obj_msg->num_of_words,
|
|
write_buf);
|
|
break;
|
|
}
|
|
if (ret < 0)
|
|
pr_err("%s failed\n", __func__);
|
|
kfree(write_buf);
|
|
return ret;
|
|
}
|
|
|
|
static int slatecom_get_rproc_handle(struct slatedaemon_priv *priv)
|
|
{
|
|
struct platform_device *pdev = NULL;
|
|
int ret;
|
|
const char *firmware_name = NULL;
|
|
phandle rproc_phandle;
|
|
|
|
pdev = slate_pdev;
|
|
|
|
if (!pdev) {
|
|
pr_err("%s: Platform device null\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
if (!pdev->dev.of_node) {
|
|
pr_err("%s: Device tree information missing\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"qcom,firmware-name", &firmware_name);
|
|
if (ret < 0) {
|
|
pr_err("can't get fw name.\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (!priv->pil_h) {
|
|
if (of_property_read_u32(pdev->dev.of_node, "qcom,rproc-handle",
|
|
&rproc_phandle)) {
|
|
pr_err("error reading rproc phandle\n");
|
|
goto fail;
|
|
}
|
|
|
|
priv->pil_h = rproc_get_by_phandle(rproc_phandle);
|
|
if (!priv->pil_h) {
|
|
pr_err("rproc not found\n");
|
|
goto fail;
|
|
}
|
|
slatecom_rproc = (struct rproc *)priv->pil_h;
|
|
return 0;
|
|
}
|
|
|
|
fail:
|
|
pr_err("%s: SLATE get handle failed\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
|
|
static int dt_parse_clk_info(struct device *dev,
|
|
struct rf_power_clk_data **clk_data)
|
|
{
|
|
int ret = -EINVAL;
|
|
struct rf_power_clk_data *clk = NULL;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
*clk_data = NULL;
|
|
if (of_parse_phandle(np, "clocks", 0)) {
|
|
clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
|
|
if (!clk) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
/* Allocated 20 bytes size buffer for clock name string */
|
|
clk->name = devm_kzalloc(dev, 20, GFP_KERNEL);
|
|
|
|
/* Parse clock name from node */
|
|
ret = of_property_read_string_index(np, "clock-names", 0,
|
|
&(clk->name));
|
|
if (ret < 0) {
|
|
pr_err("%s: reading \"clock-names\" failed\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
|
|
clk->clk = devm_clk_get(dev, clk->name);
|
|
if (IS_ERR(clk->clk)) {
|
|
ret = PTR_ERR(clk->clk);
|
|
pr_err("%s: failed to get %s, ret (%d)\n",
|
|
__func__, clk->name, ret);
|
|
clk->clk = NULL;
|
|
return ret;
|
|
}
|
|
*clk_data = clk;
|
|
} else {
|
|
pr_err("%s: clocks is not provided in device tree\n", __func__);
|
|
}
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
static int rf_clk_enable(struct rf_power_clk_data *clk)
|
|
{
|
|
int rc = 0;
|
|
|
|
pr_debug("%s: %s\n", __func__, clk->name);
|
|
|
|
/* Get the clock handle for vreg */
|
|
if (!clk->clk || clk->is_enabled) {
|
|
pr_err("%s: error - node: %p, clk->is_enabled:%d\n",
|
|
__func__, clk->clk, clk->is_enabled);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = clk_prepare_enable(clk->clk);
|
|
if (rc) {
|
|
pr_err("%s: failed to enable %s, rc(%d)\n",
|
|
__func__, clk->name, rc);
|
|
return rc;
|
|
}
|
|
|
|
clk->is_enabled = true;
|
|
return rc;
|
|
}
|
|
|
|
static int rf_clk_disable(struct rf_power_clk_data *clk)
|
|
{
|
|
int rc = 0;
|
|
|
|
pr_debug("%s: %s\n", __func__, clk->name);
|
|
|
|
/* Get the clock handle for vreg */
|
|
if (!clk->clk || !clk->is_enabled) {
|
|
pr_err("%s: error - node: %p, clk->is_enabled:%d\n",
|
|
__func__, clk->clk, clk->is_enabled);
|
|
return -EINVAL;
|
|
}
|
|
clk_disable_unprepare(clk->clk);
|
|
|
|
clk->is_enabled = false;
|
|
return rc;
|
|
}
|
|
|
|
static int slatecom_fw_load(struct slatedaemon_priv *priv)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (dev->rf_clk_2)
|
|
rf_clk_enable(dev->rf_clk_2);
|
|
|
|
if (!priv->pil_h) {
|
|
pr_err("%s: Getting rproc handle\n", __func__);
|
|
ret = slatecom_get_rproc_handle(priv);
|
|
}
|
|
if (ret == 0) {
|
|
ret = rproc_boot(priv->pil_h);
|
|
if (ret) {
|
|
pr_err("%s: rproc boot failed, err: %d\n",
|
|
__func__, ret);
|
|
priv->pil_h = NULL;
|
|
slate_boot_status = 0;
|
|
goto fail;
|
|
}
|
|
slate_boot_status = 1;
|
|
dev->slate_unload = false;
|
|
pr_info("%s: SLATE image is loaded\n", __func__);
|
|
return 0;
|
|
}
|
|
fail:
|
|
pr_err("%s: SLATE image loading failed\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
|
|
static void slatecom_fw_unload(struct slatedaemon_priv *priv)
|
|
{
|
|
if (!priv) {
|
|
pr_err("%s: handle not found\n", __func__);
|
|
return;
|
|
}
|
|
if (priv->pil_h) {
|
|
pr_err("%s: calling subsystem put\n", __func__);
|
|
priv->slate_unload = true;
|
|
rproc_shutdown(priv->pil_h);
|
|
priv->slate_unload = false;
|
|
priv->pil_h = NULL;
|
|
slate_boot_status = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* send_slate_boot_status send state boot status event to clients
|
|
*
|
|
*/
|
|
static int send_slate_boot_status(enum boot_status event)
|
|
{
|
|
int rc;
|
|
char *event_buf;
|
|
unsigned int event_buf_size;
|
|
|
|
if (event == SLATE_UPDATE_START)
|
|
set_slate_bt_state(false);
|
|
else if (event == SLATE_UPDATE_DONE)
|
|
set_slate_bt_state(true);
|
|
|
|
event_buf_size = sizeof(enum boot_status);
|
|
|
|
event_buf = kmemdup((char *)&event, event_buf_size, GFP_KERNEL);
|
|
if (!event_buf)
|
|
return -ENOMEM;
|
|
|
|
rc = seb_send_event(SLATE_STATUS, event_buf,
|
|
event_buf_size);
|
|
if (rc < 0) {
|
|
pr_err("Failed to send SLATE_STATUS event, rc=%d\n",
|
|
rc);
|
|
goto free_event_buf;
|
|
}
|
|
|
|
pr_info("Send SLATE_STATUS event successful\n");
|
|
|
|
free_event_buf:
|
|
kfree(event_buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* load_slate_tzapp() - Called to load TZ app.
|
|
* @pbd: struct containing private SLATE data.
|
|
*
|
|
* Return: 0 on success. Error code on failure.
|
|
*/
|
|
|
|
static int load_slate_tzapp(struct slatedaemon_priv *pbd)
|
|
{
|
|
int rc;
|
|
|
|
/* return success if already loaded */
|
|
if (pbd->qseecom_handle && !pbd->app_status)
|
|
return 0;
|
|
/* Load the APP */
|
|
rc = qseecom_start_app(&pbd->qseecom_handle, SECURE_APP, SZ_4K);
|
|
if (rc < 0) {
|
|
dev_err(pbd->platform_dev, "SLATE TZ app load failure\n");
|
|
pbd->app_status = RESULT_FAILURE;
|
|
return -EIO;
|
|
}
|
|
pbd->app_status = RESULT_SUCCESS;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
|
|
* aligns buffer lengths
|
|
* @hdl: index of qseecom_handle
|
|
* @cmd: req buffer - set to qseecom_handle.sbuf
|
|
* @cmd_len: ptr to req buffer len
|
|
* @rsp: rsp buffer - set to qseecom_handle.sbuf + offset
|
|
* @rsp_len: ptr to rsp buffer len
|
|
*
|
|
* Return: Success always .
|
|
*/
|
|
static int get_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd,
|
|
uint32_t *cmd_len, void **rsp, uint32_t *rsp_len)
|
|
{
|
|
*cmd = handle->sbuf;
|
|
if (*cmd_len & QSEECOM_ALIGN_MASK)
|
|
*cmd_len = QSEECOM_ALIGN(*cmd_len);
|
|
*rsp = handle->sbuf + *cmd_len;
|
|
if (*rsp_len & QSEECOM_ALIGN_MASK)
|
|
*rsp_len = QSEECOM_ALIGN(*rsp_len);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* slate_tzapp_comm() - Function called to communicate with TZ APP.
|
|
* @pbd: struct containing private SLATE data.
|
|
* @req: struct containing command and parameters.
|
|
*
|
|
* Return: 0 on success. Error code on failure.
|
|
*/
|
|
|
|
static long slate_tzapp_comm(struct slatedaemon_priv *pbd,
|
|
struct tzapp_slate_req *req)
|
|
{
|
|
struct tzapp_slate_req *slate_tz_req;
|
|
struct tzapp_slate_rsp *slate_tz_rsp;
|
|
int rc, req_len, rsp_len;
|
|
|
|
/* Fill command structure */
|
|
req_len = sizeof(struct tzapp_slate_req);
|
|
rsp_len = sizeof(struct tzapp_slate_rsp);
|
|
|
|
mutex_lock(&pbd->cmdsync_lock);
|
|
rc = get_cmd_rsp_buffers(pbd->qseecom_handle,
|
|
(void **)&slate_tz_req, &req_len,
|
|
(void **)&slate_tz_rsp, &rsp_len);
|
|
if (rc)
|
|
goto end;
|
|
|
|
slate_tz_req->tzapp_slate_cmd = req->tzapp_slate_cmd;
|
|
slate_tz_req->address_fw = req->address_fw;
|
|
slate_tz_req->size_fw = req->size_fw;
|
|
rc = qseecom_send_command(pbd->qseecom_handle,
|
|
(void *)slate_tz_req, req_len, (void *)slate_tz_rsp, rsp_len);
|
|
|
|
mutex_unlock(&pbd->cmdsync_lock);
|
|
pr_debug("SLATE PIL qseecom returned with value 0x%x and status 0x%x\n",
|
|
rc, slate_tz_rsp->status);
|
|
if (rc || slate_tz_rsp->status)
|
|
pbd->cmd_status = slate_tz_rsp->status;
|
|
else
|
|
pbd->cmd_status = 0;
|
|
return rc;
|
|
end:
|
|
mutex_unlock(&pbd->cmdsync_lock);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int slate_soft_reset(void)
|
|
{
|
|
struct tzapp_slate_req slate_tz_req;
|
|
struct slatedaemon_priv *tzapp = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
int ret;
|
|
|
|
ret = load_slate_tzapp(tzapp);
|
|
if (ret) {
|
|
dev_err(&slate_pdev->dev, "%s: SLATE TZ app load failure\n", __func__);
|
|
return ret;
|
|
}
|
|
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RESET;
|
|
slate_tz_req.address_fw = 0;
|
|
slate_tz_req.size_fw = 0;
|
|
ret = slate_tzapp_comm(tzapp, &slate_tz_req);
|
|
if (ret || tzapp->cmd_status)
|
|
dev_err(&slate_pdev->dev,
|
|
"%s: Failed to send reset signal to tzapp\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slate_soft_reset);
|
|
|
|
int send_wlan_state(enum WMSlateCtrlChnlOpcode type)
|
|
{
|
|
int ret = 0;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
struct slatedaemon_priv *dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
|
|
switch (type) {
|
|
case GMI_MGR_WLAN_BOOT_INIT:
|
|
msg_header.opcode = GMI_MGR_WLAN_BOOT_INIT;
|
|
break;
|
|
case GMI_MGR_WLAN_BOOT_COMPLETE:
|
|
msg_header.opcode = GMI_MGR_WLAN_BOOT_COMPLETE;
|
|
break;
|
|
case GMI_WLAN_5G_CONNECT:
|
|
msg_header.opcode = GMI_WLAN_5G_CONNECT;
|
|
break;
|
|
case GMI_WLAN_5G_DISCONNECT:
|
|
msg_header.opcode = GMI_WLAN_5G_DISCONNECT;
|
|
break;
|
|
default:
|
|
pr_err("Invalid WLAN State transtion cmd = %d\n", type);
|
|
break;
|
|
}
|
|
|
|
ret = slatecom_tx_msg(dev, &msg_header.opcode, sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("WLAN State transtion event cmd failed with = %d\n", ret);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(send_wlan_state);
|
|
|
|
static int modem_down2_slate(void)
|
|
{
|
|
complete(&slate_modem_down_wait);
|
|
return 0;
|
|
}
|
|
|
|
static int adsp_down2_slate(void)
|
|
{
|
|
complete(&slate_adsp_down_wait);
|
|
return 0;
|
|
}
|
|
|
|
static int send_time_sync(struct slate_ui_data *tui_obj_msg)
|
|
{
|
|
int ret = 0;
|
|
void *write_buf;
|
|
size_t len;
|
|
|
|
len = tui_obj_msg->num_of_words * sizeof(uint32_t);
|
|
write_buf = memdup_user((COMPAT_PTR(tui_obj_msg->buffer)), len);
|
|
if (IS_ERR(write_buf)) {
|
|
ret = PTR_ERR(write_buf);
|
|
kfree(write_buf);
|
|
return ret;
|
|
}
|
|
ret = slatecom_tx_msg(dev, write_buf, tui_obj_msg->num_of_words*4);
|
|
if (ret < 0)
|
|
pr_err("send_time_data cmd failed\n");
|
|
else
|
|
pr_info("send_time_data cmd success\n");
|
|
kfree(write_buf);
|
|
return ret;
|
|
}
|
|
|
|
static int send_debug_config(uint64_t config)
|
|
{
|
|
int ret = 0;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
struct slatedaemon_priv *dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
|
|
switch (config) {
|
|
case ENABLE_PMIC_RTC:
|
|
msg_header.opcode = GMI_MGR_ENABLE_PMIC_RTC;
|
|
break;
|
|
case DISABLE_PMIC_RTC:
|
|
msg_header.opcode = GMI_MGR_DISABLE_PMIC_RTC;
|
|
break;
|
|
case ENABLE_QCLI:
|
|
msg_header.opcode = GMI_MGR_ENABLE_QCLI;
|
|
break;
|
|
case DISABLE_QCLI:
|
|
msg_header.opcode = GMI_MGR_DISABLE_QCLI;
|
|
break;
|
|
default:
|
|
pr_err("Invalid debug config cmd:%llu\n", config);
|
|
return -EINVAL;
|
|
}
|
|
ret = slatecom_tx_msg(dev, &msg_header.opcode, sizeof(msg_header.opcode));
|
|
|
|
if (ret < 0)
|
|
pr_err("failed to send debug config cmd:%llu\n", config);
|
|
return ret;
|
|
}
|
|
|
|
int set_slate_boot_mode(uint32_t mode)
|
|
{
|
|
gpio_direction_output(pmic_gpio15, 0);
|
|
gpio_set_value(pmic_gpio15, mode);
|
|
/*get back pmic gpio*/
|
|
if (gpio_get_value(pmic_gpio15) == mode)
|
|
return RESULT_SUCCESS;
|
|
return RESULT_FAILURE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(set_slate_boot_mode);
|
|
|
|
int get_slate_boot_mode(void)
|
|
{
|
|
int mode = -1;
|
|
|
|
if (gpio_direction_input(pmic_gpio15) < 0)
|
|
return RESULT_FAILURE;
|
|
mode = gpio_get_value(pmic_gpio15);
|
|
|
|
return mode;
|
|
}
|
|
EXPORT_SYMBOL_GPL(get_slate_boot_mode);
|
|
|
|
bool is_slate_unload_only(void)
|
|
{
|
|
struct slatedaemon_priv *dev =
|
|
container_of(slatecom_intf_drv, struct slatedaemon_priv, lhndl);
|
|
|
|
return dev->slate_unload;
|
|
}
|
|
EXPORT_SYMBOL_GPL(is_slate_unload_only);
|
|
|
|
static int send_get_fw_version(struct slate_ui_data *ui_obj_msg)
|
|
{
|
|
int ret = 0;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
struct wear_firmware_info *fw_info = NULL;
|
|
void __user *read_buf = COMPAT_PTR(ui_obj_msg->buffer);
|
|
struct slatedaemon_priv *dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
|
|
msg_header.opcode = GMI_WEAR_MGR_GET_FIRMWARE_DETAILS;
|
|
ret = slatecom_tx_msg(dev, &msg_header.opcode, sizeof(msg_header.opcode));
|
|
fw_info = (struct wear_firmware_info *)dev->rx_buf;
|
|
if (!ret && copy_to_user(read_buf, fw_info, sizeof(struct wear_firmware_info))) {
|
|
pr_err("copy to user failed\n");
|
|
ret = -EFAULT;
|
|
}
|
|
return ret;
|
|
}
|
|
static int send_ipc_cmd_to_slate(struct slate_ui_data *ui_obj_msg)
|
|
{
|
|
int ret = 0;
|
|
uint32_t cmd = ui_obj_msg->cmd;
|
|
|
|
switch (cmd) {
|
|
case STATE_TRANSITION:
|
|
ret = send_state_change_cmd(ui_obj_msg->write);
|
|
break;
|
|
case TIME_SYNC:
|
|
ret = send_time_sync(ui_obj_msg);
|
|
break;
|
|
case DEBUG_CONFIG:
|
|
ret = send_debug_config(ui_obj_msg->write);
|
|
break;
|
|
case GET_VERSION:
|
|
ret = send_get_fw_version(ui_obj_msg);
|
|
break;
|
|
default:
|
|
pr_err("Invalid ipc cmd:%d\n", cmd);
|
|
return -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int send_boot_cmd_to_slate(struct slate_ui_data *ui_obj_msg)
|
|
{
|
|
int ret = 0;
|
|
uint32_t cmd = ui_obj_msg->cmd;
|
|
|
|
switch (cmd) {
|
|
case SOFT_RESET:
|
|
slate_soft_reset();
|
|
break;
|
|
case TWM_EXIT:
|
|
twm_exit = true;
|
|
dev->slate_unload = true;
|
|
break;
|
|
case AON_APP_RUNNING:
|
|
slate_app_running = true;
|
|
break;
|
|
case LOAD:
|
|
if (!slate_boot_status)
|
|
ret = slatecom_fw_load(dev);
|
|
else {
|
|
pr_info("slate is already loaded\n");
|
|
dev->slate_unload = false;
|
|
ret = -EFAULT;
|
|
}
|
|
break;
|
|
case UNLOAD:
|
|
slatecom_fw_unload(dev);
|
|
break;
|
|
case GET_BOOT_MODE:
|
|
ret = get_slate_boot_mode();
|
|
break;
|
|
case SET_BOOT_MODE:
|
|
ret = set_slate_boot_mode(ui_obj_msg->write);
|
|
break;
|
|
case CMD_SAVE_AON_DUMP:
|
|
if (!dev->pil_h)
|
|
ret = slatecom_get_rproc_handle(dev);
|
|
if (ret == 0)
|
|
slatecom_rproc->ops->coredump(slatecom_rproc);
|
|
else
|
|
pr_err("failed to get rproc, skip coredump collection\n");
|
|
break;
|
|
case BOOT_STATUS:
|
|
ret = send_slate_boot_status(ui_obj_msg->write);
|
|
break;
|
|
default:
|
|
pr_err("Invalid boot cmd:%d\n", cmd);
|
|
return -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static long slate_com_ioctl(struct file *filp,
|
|
unsigned int ui_slatecom_cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct slate_ui_data ui_obj_msg = {0, 0, 0, 0, 0, NULL};
|
|
|
|
if (!dev || (dev->slatecom_current_state == SLATECOM_STATE_UNKNOWN) || (filp == NULL)) {
|
|
pr_err("slatecom driver not initialized\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (arg != 0) {
|
|
if (copy_from_user(&ui_obj_msg, (void __user *) arg,
|
|
sizeof(ui_obj_msg))) {
|
|
pr_err("The copy from user failed\n");
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
switch (ui_slatecom_cmd) {
|
|
case SLATECOM_REG_READ:
|
|
case SLATECOM_AHB_READ:
|
|
ret = slatechar_read_cmd(&ui_obj_msg,
|
|
ui_slatecom_cmd);
|
|
break;
|
|
case SLATECOM_AHB_WRITE:
|
|
case SLATECOM_REG_WRITE:
|
|
ret = slatechar_write_cmd(&ui_obj_msg, ui_slatecom_cmd);
|
|
break;
|
|
case SLATECOM_SET_SPI_FREE:
|
|
ret = slatecom_set_spi_state(SLATECOM_SPI_FREE);
|
|
break;
|
|
case SLATECOM_SET_SPI_BUSY:
|
|
ret = slatecom_set_spi_state(SLATECOM_SPI_BUSY);
|
|
break;
|
|
case SLATECOM_MODEM_DOWN2_SLATE:
|
|
ret = modem_down2_slate();
|
|
break;
|
|
case SLATECOM_ADSP_DOWN2_SLATE:
|
|
ret = adsp_down2_slate();
|
|
break;
|
|
case SLATECOM_SEND_IPC_CMD:
|
|
if (dev->slatecom_current_state != SLATECOM_STATE_GLINK_OPEN) {
|
|
pr_err("driver not ready, glink is not open\n");
|
|
return -ENODEV;
|
|
}
|
|
ret = send_ipc_cmd_to_slate(&ui_obj_msg);
|
|
break;
|
|
case SLATECOM_SEND_BOOT_CMD:
|
|
ret = send_boot_cmd_to_slate(&ui_obj_msg);
|
|
break;
|
|
default:
|
|
ret = -ENOIOCTLCMD;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
long compat_slate_com_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
unsigned int nr = _IOC_NR(cmd);
|
|
|
|
return (long)slate_com_ioctl(file, nr, arg);
|
|
}
|
|
|
|
static ssize_t slatecom_char_write(struct file *f, const char __user *buf,
|
|
size_t count, loff_t *off)
|
|
{
|
|
unsigned char qcli_cmnd;
|
|
uint32_t opcode;
|
|
int ret = 0;
|
|
struct slatedaemon_priv *dev = NULL;
|
|
|
|
if (!slatecom_intf_drv) {
|
|
pr_err("Invalid use-case, slatecom driver is not ready\n");
|
|
return -EINVAL;
|
|
}
|
|
dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv,
|
|
lhndl);
|
|
if (copy_from_user(&qcli_cmnd, buf, sizeof(unsigned char)))
|
|
return -EFAULT;
|
|
|
|
pr_debug("%s: QCLI command arg = %c\n", __func__, qcli_cmnd);
|
|
|
|
switch (qcli_cmnd) {
|
|
case '0':
|
|
opcode = GMI_MGR_DISABLE_QCLI;
|
|
ret = slatecom_tx_msg(dev, &opcode, sizeof(opcode));
|
|
if (ret < 0)
|
|
pr_err("MSM QCLI Disable cmd failed\n");
|
|
break;
|
|
case '1':
|
|
opcode = GMI_MGR_ENABLE_QCLI;
|
|
ret = slatecom_tx_msg(dev, &opcode, sizeof(opcode));
|
|
if (ret < 0)
|
|
pr_err("MSM QCLI Enable cmd failed\n");
|
|
break;
|
|
case '2':
|
|
pr_err("subsystem start notify, cmd depricated\n");
|
|
break;
|
|
case '3':
|
|
pr_err("subsystem stop notify, cmd depricated\n");
|
|
break;
|
|
case '4':
|
|
opcode = GMI_MGR_ENABLE_PMIC_RTC;
|
|
ret = slatecom_tx_msg(dev, &opcode, sizeof(opcode));
|
|
if (ret < 0)
|
|
pr_err("MSM RTC Enable cmd failed\n");
|
|
break;
|
|
case '5':
|
|
opcode = GMI_MGR_DISABLE_PMIC_RTC;
|
|
ret = slatecom_tx_msg(dev, &opcode, sizeof(opcode));
|
|
if (ret < 0)
|
|
pr_err("MSM RTC Disable cmd failed\n");
|
|
break;
|
|
case 'c':
|
|
if (dev->slatecom_current_state == SLATECOM_STATE_GLINK_OPEN) {
|
|
opcode = GMI_MGR_FORCE_CRASH;
|
|
ret = slatecom_tx_msg(dev, &opcode, sizeof(opcode));
|
|
if (ret < 0)
|
|
pr_err("AON force crash cmd failed\n");
|
|
} else
|
|
pr_debug("glink is not open, skip AON force crash\n");
|
|
break;
|
|
|
|
default:
|
|
pr_err("MSM QCLI Invalid Option\n");
|
|
break;
|
|
}
|
|
|
|
*off += count;
|
|
return count;
|
|
}
|
|
|
|
static int slatecom_char_close(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&slate_char_mutex);
|
|
ret = slatecom_close(&handle);
|
|
device_open = 0;
|
|
mutex_unlock(&slate_char_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static void slatecom_slateup_work(struct work_struct *work)
|
|
{
|
|
int ret = 0;
|
|
struct slatedaemon_priv *dev =
|
|
container_of(work, struct slatedaemon_priv, slatecom_up_work);
|
|
|
|
mutex_lock(&dev->slatecom_state_mutex);
|
|
if (!dev->slatecom_rpmsg)
|
|
pr_err("slatecom-rpmsg is not probed yet\n");
|
|
ret = wait_event_timeout(dev->link_state_wait,
|
|
dev->slatecom_rpmsg, msecs_to_jiffies(TIMEOUT_MS_GLINK_OPEN));
|
|
if (ret == 0) {
|
|
pr_err("channel connection time out %d\n", ret);
|
|
goto glink_err;
|
|
}
|
|
dev->slatecom_current_state = SLATECOM_STATE_GLINK_OPEN;
|
|
goto unlock;
|
|
|
|
glink_err:
|
|
dev->slatecom_current_state = SLATECOM_STATE_INIT;
|
|
unlock:
|
|
mutex_unlock(&dev->slatecom_state_mutex);
|
|
}
|
|
|
|
|
|
static void slatecom_slatedown_work(struct work_struct *work)
|
|
{
|
|
struct slatedaemon_priv *dev = container_of(work, struct slatedaemon_priv,
|
|
slatecom_down_work);
|
|
|
|
mutex_lock(&dev->slatecom_state_mutex);
|
|
|
|
pr_debug("Slatecom current state is : %d\n", dev->slatecom_current_state);
|
|
|
|
dev->slatecom_current_state = SLATECOM_STATE_SLATE_SSR;
|
|
|
|
mutex_unlock(&dev->slatecom_state_mutex);
|
|
}
|
|
|
|
static int slatecom_rpmsg_init(struct slatedaemon_priv *dev)
|
|
{
|
|
slatecom_intf_drv = &dev->lhndl;
|
|
mutex_init(&dev->glink_mutex);
|
|
mutex_init(&dev->slatecom_state_mutex);
|
|
|
|
dev->slatecom_wq =
|
|
create_singlethread_workqueue("slatecom-work-queue");
|
|
if (!dev->slatecom_wq) {
|
|
pr_err("Failed to init Slatecom work-queue\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
init_waitqueue_head(&dev->link_state_wait);
|
|
|
|
/* set default slatecom state */
|
|
dev->slatecom_current_state = SLATECOM_STATE_INIT;
|
|
|
|
/* Init all works */
|
|
INIT_WORK(&dev->slatecom_up_work, slatecom_slateup_work);
|
|
INIT_WORK(&dev->slatecom_down_work, slatecom_slatedown_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int slate_daemon_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *node;
|
|
int rc = 0;
|
|
|
|
node = pdev->dev.of_node;
|
|
|
|
dev = kzalloc(sizeof(struct slatedaemon_priv), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
/* Add wake lock for PM suspend */
|
|
wakeup_source_add(&dev->slatecom_ws);
|
|
dev->slatecom_current_state = SLATECOM_STATE_UNKNOWN;
|
|
rc = slatecom_rpmsg_init(dev);
|
|
if (rc)
|
|
return -ENODEV;
|
|
dev->platform_dev = &pdev->dev;
|
|
pr_info("%s success\n", __func__);
|
|
slate_pdev = pdev;
|
|
setup_pmic_gpio15();
|
|
ssr_register();
|
|
dt_parse_clk_info(&pdev->dev,
|
|
&dev->rf_clk_2);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id slate_daemon_of_match[] = {
|
|
{ .compatible = "qcom,slate-daemon", },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, slate_daemon_of_match);
|
|
|
|
static struct platform_driver slate_daemon_driver = {
|
|
.probe = slate_daemon_probe,
|
|
.driver = {
|
|
.name = "slate-daemon",
|
|
.of_match_table = slate_daemon_of_match,
|
|
},
|
|
};
|
|
|
|
static const struct file_operations fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = slatecom_char_open,
|
|
.write = slatecom_char_write,
|
|
.release = slatecom_char_close,
|
|
.unlocked_ioctl = slate_com_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = compat_slate_com_ioctl,
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
*ssr_slate_cb(): callback function is called
|
|
*by ssr framework when SLATE goes down, up and during ramdump
|
|
*collection. It handles SLATE shutdown and power up events.
|
|
*/
|
|
static int ssr_slate_cb(struct notifier_block *this,
|
|
unsigned long opcode, void *data)
|
|
{
|
|
struct slate_event slatee;
|
|
struct slatedaemon_priv *dev = container_of(slatecom_intf_drv,
|
|
struct slatedaemon_priv, lhndl);
|
|
|
|
switch (opcode) {
|
|
case QCOM_SSR_BEFORE_SHUTDOWN:
|
|
pr_debug("Slate before shutdown\n");
|
|
slatee.e_type = SLATE_BEFORE_POWER_DOWN;
|
|
slatecom_set_spi_state(SLATECOM_SPI_BUSY);
|
|
send_uevent(&slatee);
|
|
queue_work(dev->slatecom_wq, &dev->slatecom_down_work);
|
|
break;
|
|
case QCOM_SSR_AFTER_SHUTDOWN:
|
|
pr_debug("Slate after shutdown\n");
|
|
slatee.e_type = SLATE_AFTER_POWER_DOWN;
|
|
slatecom_slatedown_handler();
|
|
send_uevent(&slatee);
|
|
set_slate_bt_state(false);
|
|
set_slate_dsp_state(false);
|
|
if (dev->rf_clk_2)
|
|
rf_clk_enable(dev->rf_clk_2);
|
|
break;
|
|
case QCOM_SSR_BEFORE_POWERUP:
|
|
pr_debug("Slate before powerup\n");
|
|
slatee.e_type = SLATE_BEFORE_POWER_UP;
|
|
send_uevent(&slatee);
|
|
break;
|
|
case QCOM_SSR_AFTER_POWERUP:
|
|
pr_debug("Slate after powerup\n");
|
|
twm_exit = false;
|
|
slatee.e_type = SLATE_AFTER_POWER_UP;
|
|
slatecom_set_spi_state(SLATECOM_SPI_FREE);
|
|
if (dev->slatecom_current_state == SLATECOM_STATE_INIT ||
|
|
dev->slatecom_current_state == SLATECOM_STATE_SLATE_SSR)
|
|
queue_work(dev->slatecom_wq, &dev->slatecom_up_work);
|
|
send_uevent(&slatee);
|
|
if (dev->rf_clk_2)
|
|
rf_clk_disable(dev->rf_clk_2);
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
/**
|
|
*ssr_modem_cb(): callback function is called
|
|
*by ssr framework when modem goes down, up and during ramdump
|
|
*collection. It handles modem shutdown and power up events.
|
|
*/
|
|
static int ssr_modem_cb(struct notifier_block *this,
|
|
unsigned long opcode, void *data)
|
|
{
|
|
struct slate_event modeme;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
int ret = 0;
|
|
|
|
switch (opcode) {
|
|
case QCOM_SSR_BEFORE_SHUTDOWN:
|
|
modeme.e_type = MODEM_BEFORE_POWER_DOWN;
|
|
reinit_completion(&slate_modem_down_wait);
|
|
send_uevent(&modeme);
|
|
msg_header.opcode = GMI_MGR_SSR_MPSS_DOWN_PRE_NOTIFICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send mdsp down pre-event to slate\n");
|
|
break;
|
|
case QCOM_SSR_AFTER_SHUTDOWN:
|
|
modeme.e_type = MODEM_AFTER_POWER_DOWN;
|
|
reinit_completion(&slate_modem_down_wait);
|
|
send_uevent(&modeme);
|
|
msg_header.opcode = GMI_MGR_SSR_MPSS_DOWN_NOTIFICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send mdsp down event to slate\n");
|
|
break;
|
|
case QCOM_SSR_BEFORE_POWERUP:
|
|
modeme.e_type = MODEM_BEFORE_POWER_UP;
|
|
send_uevent(&modeme);
|
|
msg_header.opcode = GMI_MGR_SSR_MPSS_UP_PRE_NOTIFICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send mdsp up pre-event to slate\n");
|
|
break;
|
|
case QCOM_SSR_AFTER_POWERUP:
|
|
modeme.e_type = MODEM_AFTER_POWER_UP;
|
|
send_uevent(&modeme);
|
|
msg_header.opcode = GMI_MGR_SSR_MPSS_UP_NOTIFICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send mdsp up event to slate\n");
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static int ssr_adsp_cb(struct notifier_block *this,
|
|
unsigned long opcode, void *data)
|
|
{
|
|
struct slate_event adspe;
|
|
struct msg_header_t msg_header = {0, 0};
|
|
int ret = 0;
|
|
|
|
switch (opcode) {
|
|
case QCOM_SSR_BEFORE_SHUTDOWN:
|
|
adspe.e_type = ADSP_BEFORE_POWER_DOWN;
|
|
reinit_completion(&slate_adsp_down_wait);
|
|
send_uevent(&adspe);
|
|
msg_header.opcode = GMI_MGR_SSR_ADSP_DOWN_PRE_INDICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send adsp down pre-event to slate\n");
|
|
break;
|
|
case QCOM_SSR_AFTER_SHUTDOWN:
|
|
adspe.e_type = ADSP_AFTER_POWER_DOWN;
|
|
reinit_completion(&slate_adsp_down_wait);
|
|
send_uevent(&adspe);
|
|
msg_header.opcode = GMI_MGR_SSR_ADSP_DOWN_INDICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send adsp down event to slate\n");
|
|
break;
|
|
case QCOM_SSR_BEFORE_POWERUP:
|
|
adspe.e_type = ADSP_BEFORE_POWER_UP;
|
|
send_uevent(&adspe);
|
|
msg_header.opcode = GMI_MGR_SSR_ADSP_UP_PRE_INDICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send adsp up pre-event to slate\n");
|
|
break;
|
|
case QCOM_SSR_AFTER_POWERUP:
|
|
adspe.e_type = ADSP_AFTER_POWER_UP;
|
|
send_uevent(&adspe);
|
|
msg_header.opcode = GMI_MGR_SSR_ADSP_UP_INDICATION;
|
|
ret = slatecom_tx_msg(dev, &(msg_header.opcode), sizeof(msg_header.opcode));
|
|
if (ret < 0)
|
|
pr_err("failed to send adsp up event to slate\n");
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
bool is_twm_exit(void)
|
|
{
|
|
if (twm_exit)
|
|
return true;
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL_GPL(is_twm_exit);
|
|
|
|
bool is_slate_running(void)
|
|
{
|
|
if (slate_app_running) {
|
|
slate_app_running = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL_GPL(is_slate_running);
|
|
|
|
void set_slate_dsp_state(bool status)
|
|
{
|
|
struct slate_event statee;
|
|
|
|
slate_dsp_error = status;
|
|
if (!status) {
|
|
statee.e_type = SLATE_DSP_ERROR;
|
|
strscpy(dspss_state, "error", BUF_SIZE);
|
|
srcu_notifier_call_chain(&slatecom_notifier_chain,
|
|
DSP_ERROR, NULL);
|
|
} else {
|
|
statee.e_type = SLATE_DSP_READY;
|
|
strscpy(dspss_state, "ready", BUF_SIZE);
|
|
srcu_notifier_call_chain(&slatecom_notifier_chain,
|
|
DSP_READY, NULL);
|
|
}
|
|
send_uevent(&statee);
|
|
}
|
|
|
|
void set_slate_bt_state(bool status)
|
|
{
|
|
struct slate_event statee;
|
|
|
|
slate_bt_error = status;
|
|
if (!status) {
|
|
statee.e_type = SLATE_BT_ERROR;
|
|
strscpy(btss_state, "error", BUF_SIZE);
|
|
srcu_notifier_call_chain(&slatecom_notifier_chain,
|
|
BT_ERROR, NULL);
|
|
} else {
|
|
statee.e_type = SLATE_BT_READY;
|
|
strscpy(btss_state, "ready", BUF_SIZE);
|
|
srcu_notifier_call_chain(&slatecom_notifier_chain,
|
|
BT_READY, NULL);
|
|
}
|
|
send_uevent(&statee);
|
|
}
|
|
|
|
void *slatecom_register_notifier(struct notifier_block *nb)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = srcu_notifier_chain_register(&slatecom_notifier_chain, nb);
|
|
if (ret < 0)
|
|
return ERR_PTR(ret);
|
|
|
|
return &slatecom_notifier_chain;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_register_notifier);
|
|
|
|
int slatecom_unregister_notifier(void *notify, struct notifier_block *nb)
|
|
{
|
|
return srcu_notifier_chain_unregister(notify, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_unregister_notifier);
|
|
|
|
static struct notifier_block ssr_modem_nb = {
|
|
.notifier_call = ssr_modem_cb,
|
|
.priority = 0,
|
|
};
|
|
|
|
static struct notifier_block ssr_adsp_nb = {
|
|
.notifier_call = ssr_adsp_cb,
|
|
.priority = 0,
|
|
};
|
|
|
|
static struct notifier_block ssr_slate_nb = {
|
|
.notifier_call = ssr_slate_cb,
|
|
.priority = 0,
|
|
};
|
|
|
|
static struct service_info service_data[3] = {
|
|
{
|
|
.name = "SSR_SLATE",
|
|
.ssr_domains = "slatefw",
|
|
.domain_id = SSR_DOMAIN_SLATE,
|
|
.nb = &ssr_slate_nb,
|
|
.handle = NULL,
|
|
},
|
|
{
|
|
.name = "SSR_MODEM",
|
|
.ssr_domains = "mpss",
|
|
.domain_id = SSR_DOMAIN_MODEM,
|
|
.nb = &ssr_modem_nb,
|
|
.handle = NULL,
|
|
},
|
|
{
|
|
.name = "SSR_ADSP",
|
|
.ssr_domains = "lpass",
|
|
.domain_id = SSR_DOMAIN_ADSP,
|
|
.nb = &ssr_adsp_nb,
|
|
.handle = NULL,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* ssr_register checks that domain id should be in range and register
|
|
* SSR framework for value at domain id.
|
|
*/
|
|
static void ssr_register(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(service_data); i++) {
|
|
if ((service_data[i].domain_id < 0) ||
|
|
(service_data[i].domain_id >= SSR_DOMAIN_MAX)) {
|
|
pr_err("Invalid service ID = %d\n",
|
|
service_data[i].domain_id);
|
|
} else {
|
|
service_data[i].handle =
|
|
qcom_register_ssr_notifier(
|
|
service_data[i].ssr_domains,
|
|
service_data[i].nb);
|
|
pr_info("subsys registration for id = %d, ssr domain = %s\n",
|
|
service_data[i].domain_id,
|
|
service_data[i].ssr_domains);
|
|
if (IS_ERR_OR_NULL(service_data[i].handle)) {
|
|
pr_err("subsys register failed for id = %d, ssr domain = %s\n",
|
|
service_data[i].domain_id,
|
|
service_data[i].ssr_domains);
|
|
service_data[i].handle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int setup_pmic_gpio15(void)
|
|
{
|
|
int val = 0;
|
|
|
|
val = of_get_named_gpio(slate_pdev->dev.of_node, "qcom,platform-reset-gpio", 0);
|
|
if (val < 0) {
|
|
pr_err("pmic gpio is not found, error=%d\n", val);
|
|
return -EINVAL;
|
|
}
|
|
pmic_gpio15 = val;
|
|
if (gpio_request(pmic_gpio15, "SLATE_EFLASH_STATUS")) {
|
|
dev_err(&slate_pdev->dev,
|
|
"%s Failed to configure SLATE_EFLASH_STATUS gpio\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
static int __init init_slate_com_dev(void)
|
|
{
|
|
int ret, i = 0;
|
|
|
|
ret = alloc_chrdev_region(&slate_dev, 0, 1, SLATECOM);
|
|
if (ret < 0) {
|
|
pr_err("failed with error %d\n", ret);
|
|
return ret;
|
|
}
|
|
cdev_init(&slate_cdev, &fops);
|
|
|
|
ret = cdev_add(&slate_cdev, slate_dev, 1);
|
|
if (ret < 0) {
|
|
unregister_chrdev_region(slate_dev, 1);
|
|
pr_err("device registration failed\n");
|
|
return ret;
|
|
}
|
|
slate_class = class_create(SLATECOM);
|
|
if (IS_ERR_OR_NULL(slate_class)) {
|
|
cdev_del(&slate_cdev);
|
|
unregister_chrdev_region(slate_dev, 1);
|
|
pr_err("class creation failed\n");
|
|
return PTR_ERR(slate_class);
|
|
}
|
|
|
|
dev_ret = device_create(slate_class, NULL, slate_dev, NULL, SLATECOM);
|
|
if (IS_ERR_OR_NULL(dev_ret)) {
|
|
class_destroy(slate_class);
|
|
cdev_del(&slate_cdev);
|
|
unregister_chrdev_region(slate_dev, 1);
|
|
pr_err("device create failed\n");
|
|
return PTR_ERR(dev_ret);
|
|
}
|
|
|
|
for (i = 0; i < SLATECOM_INTF_N_FILES; i++) {
|
|
kobj_ref = kobject_create_and_add(slatecom_attr[i].attr.name, kernel_kobj);
|
|
/*Creating sysfs file for power_state*/
|
|
if (sysfs_create_file(kobj_ref, &slatecom_attr[i].attr)) {
|
|
pr_err("%s: failed to create slate-bt/dsp entry\n", __func__);
|
|
kobject_put(kobj_ref);
|
|
sysfs_remove_file(kernel_kobj, &slatecom_attr[i].attr);
|
|
}
|
|
}
|
|
|
|
if (platform_driver_register(&slate_daemon_driver))
|
|
pr_err("%s: failed to register slate-daemon register\n", __func__);
|
|
|
|
srcu_init_notifier_head(&slatecom_notifier_chain);
|
|
slatecom_state_init(&set_slate_dsp_state, &set_slate_bt_state);
|
|
slatecom_ctrl_channel_init(&slatecom_intf_notify_glink_channel_state, &slatecom_rx_msg);
|
|
return 0;
|
|
}
|
|
|
|
static void __exit exit_slate_com_dev(void)
|
|
{
|
|
int i = 0;
|
|
|
|
kobject_put(kobj_ref);
|
|
for (i = 0; i < SLATECOM_INTF_N_FILES; i++)
|
|
sysfs_remove_file(kernel_kobj, &slatecom_attr[i].attr);
|
|
|
|
device_destroy(slate_class, slate_dev);
|
|
class_destroy(slate_class);
|
|
cdev_del(&slate_cdev);
|
|
unregister_chrdev_region(slate_dev, 1);
|
|
platform_driver_unregister(&slate_daemon_driver);
|
|
gpio_free(pmic_gpio15);
|
|
}
|
|
|
|
module_init(init_slate_com_dev);
|
|
module_exit(exit_slate_com_dev);
|
|
MODULE_LICENSE("GPL");
|