Files
2025-08-12 22:16:57 +02:00

623 lines
15 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(msg) "slatersb: %s: " msg, __func__
#include "slatersb.h"
#include <linux/remoteproc/qcom_rproc.h>
struct slatersb_priv {
void *handle;
struct mutex glink_mutex;
struct mutex rsb_state_mutex;
enum slatersb_state slatersb_current_state;
void *lhndl;
char rx_buf[SLATERSB_GLINK_INTENT_SIZE];
struct work_struct slate_up_work;
struct work_struct slate_down_work;
struct work_struct rsb_up_work;
struct work_struct rsb_down_work;
struct work_struct rsb_calibration_work;
struct work_struct bttn_configr_work;
struct workqueue_struct *slatersb_wq;
void *slate_subsys_handle;
struct completion wrk_cmplt;
struct completion slate_lnikup_cmplt;
struct completion tx_done;
struct device *ldev;
struct wakeup_source *slatersb_ws;
wait_queue_head_t link_state_wait;
uint32_t calbrtion_intrvl;
uint32_t calbrtion_cpi;
uint8_t bttn_configs;
bool slate_resp_cmplt;
bool rsb_rpmsg;
bool is_in_twm;
bool calibration_needed;
bool is_calibrd;
bool is_cnfgrd;
bool blk_rsb_cmnds;
bool pending_enable;
};
static void *slatersb_drv;
static int slatersb_enable(struct slatersb_priv *dev, bool enable);
static void slatersb_slatedown_work(struct work_struct *work)
{
struct slatersb_priv *dev = container_of(work, struct slatersb_priv,
slate_down_work);
mutex_lock(&dev->rsb_state_mutex);
if (dev->slatersb_current_state == SLATERSB_STATE_RSB_ENABLED)
dev->slatersb_current_state = SLATERSB_STATE_RSB_CONFIGURED;
if (dev->slatersb_current_state == SLATERSB_STATE_RSB_CONFIGURED)
dev->slatersb_current_state = SLATERSB_STATE_INIT;
dev->is_cnfgrd = false;
dev->blk_rsb_cmnds = false;
pr_debug("RSB current state is : %d\n", dev->slatersb_current_state);
if (dev->slatersb_current_state == SLATERSB_STATE_INIT) {
if (dev->is_calibrd)
dev->calibration_needed = true;
}
mutex_unlock(&dev->rsb_state_mutex);
}
static int slatersb_tx_msg(struct slatersb_priv *dev, void *msg, size_t len)
{
int rc = 0;
uint8_t resp = 0;
mutex_lock(&dev->glink_mutex);
__pm_stay_awake(dev->slatersb_ws);
if (!dev->rsb_rpmsg) {
pr_err("slatersb-rpmsg is not probed yet, waiting for it to be probed\n");
goto err_ret;
}
rc = slatersb_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->slatersb_ws);
mutex_unlock(&dev->glink_mutex);
return rc;
}
static int slatersb_enable(struct slatersb_priv *dev, bool enable)
{
struct slatersb_msg req = {0};
req.cmd_id = SLATERSB_ENABLE;
req.data = enable ? 0x01 : 0x00;
pr_debug("req.data = %d, req.cmd_id = %d\n", req.data, req.cmd_id);
return slatersb_tx_msg(dev, &req, SLATERSB_MSG_SIZE);
}
static int slatersb_configr_rsb(struct slatersb_priv *dev, bool enable)
{
struct slatersb_msg req = {0};
req.cmd_id = SLATERSB_CONFIGR_RSB;
req.data = enable ? 0x01 : 0x00;
pr_debug("req.data = %d, req.cmd_id = %d\n", req.data, req.cmd_id);
return slatersb_tx_msg(dev, &req, SLATERSB_MSG_SIZE);
}
void slatersb_notify_glink_channel_state(bool state)
{
struct slatersb_priv *dev =
container_of(slatersb_drv, struct slatersb_priv, lhndl);
pr_debug("%s: RSB-CTRL channel state: %d\n", __func__, state);
dev->rsb_rpmsg = state;
}
void slatersb_rx_msg(void *data, int len)
{
struct slatersb_priv *dev =
container_of(slatersb_drv, struct slatersb_priv, lhndl);
if (len > SLATERSB_GLINK_INTENT_SIZE) {
pr_err("Invalid slatersb glink intent size\n");
return;
}
dev->slate_resp_cmplt = true;
wake_up(&dev->link_state_wait);
memcpy(dev->rx_buf, data, len);
}
static void slatersb_slateup_work(struct work_struct *work)
{
int ret = 0;
struct slatersb_priv *dev =
container_of(work, struct slatersb_priv, slate_up_work);
mutex_lock(&dev->rsb_state_mutex);
if (!dev->rsb_rpmsg)
pr_err("slatersb-rpmsg is not probed yet\n");
ret = wait_event_timeout(dev->link_state_wait,
dev->rsb_rpmsg, msecs_to_jiffies(TIMEOUT_MS_GLINK_OPEN));
if (ret == 0) {
pr_err("channel connection time out %d\n",
ret);
goto unlock;
}
pr_debug("slatersb-rpmsg is probed\n");
ret = slatersb_configr_rsb(dev, true);
if (ret != 0) {
pr_err("SLATE failed to configure RSB %d\n", ret);
dev->slatersb_current_state = SLATERSB_STATE_INIT;
goto unlock;
}
dev->is_cnfgrd = true;
dev->slatersb_current_state = SLATERSB_STATE_RSB_CONFIGURED;
pr_debug("RSB Cofigured\n");
if (dev->pending_enable)
queue_work(dev->slatersb_wq, &dev->rsb_up_work);
unlock:
mutex_unlock(&dev->rsb_state_mutex);
}
/**
* ssr_slate_cb(): callback function is called.
* @arg1: a notifier_block.
* @arg2: opcode that defines the event.
* @arg3: void pointer.
*
* by ssr framework when SLATE goes down, up and during
* ramdump collection. It handles SLATE shutdown and
* power up events.
*
* Return: NOTIFY_DONE.
*/
static int ssr_slatersb_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
struct slatersb_priv *dev = container_of(slatersb_drv,
struct slatersb_priv, lhndl);
switch (opcode) {
case QCOM_SSR_BEFORE_SHUTDOWN:
if (dev->slatersb_current_state == SLATERSB_STATE_RSB_ENABLED)
dev->pending_enable = true;
queue_work(dev->slatersb_wq, &dev->slate_down_work);
break;
case QCOM_SSR_AFTER_POWERUP:
if (dev->slatersb_current_state == SLATERSB_STATE_INIT)
queue_work(dev->slatersb_wq, &dev->slate_up_work);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block ssr_slate_nb = {
.notifier_call = ssr_slatersb_cb,
.priority = 0,
};
/**
* slatersb_ssr_register(): callback function is called.
* @arg1: pointer to slatersb_priv structure.
*
* ssr_register checks that domain id should be in range
* and register SSR framework for value at domain id.
*
* Return: 0 for success and -ENODEV otherwise.
*/
static int slatersb_ssr_register(struct slatersb_priv *dev)
{
struct notifier_block *nb;
if (!dev)
return -ENODEV;
nb = &ssr_slate_nb;
dev->slate_subsys_handle =
qcom_register_ssr_notifier(SLATERSB_SLATE_SUBSYS, nb);
if (!dev->slate_subsys_handle) {
dev->slate_subsys_handle = NULL;
return -ENODEV;
}
return 0;
}
static void slatersb_enable_rsb(struct work_struct *work)
{
int rc = 0;
struct slatersb_priv *dev =
container_of(work, struct slatersb_priv, rsb_up_work);
mutex_lock(&dev->rsb_state_mutex);
if (dev->slatersb_current_state == SLATERSB_STATE_RSB_ENABLED) {
pr_debug("RSB is already enabled\n");
goto unlock;
}
if (dev->slatersb_current_state != SLATERSB_STATE_RSB_CONFIGURED) {
pr_err("SLATE is not yet configured for RSB\n");
dev->pending_enable = true;
goto unlock;
}
rc = slatersb_enable(dev, true);
if (rc != 0) {
pr_err("Failed to send enable command to SLATE%d\n", rc);
dev->slatersb_current_state = SLATERSB_STATE_RSB_CONFIGURED;
goto unlock;
}
dev->slatersb_current_state = SLATERSB_STATE_RSB_ENABLED;
dev->pending_enable = false;
pr_debug("RSB Enabled\n");
if (dev->calibration_needed) {
dev->calibration_needed = false;
queue_work(dev->slatersb_wq, &dev->rsb_calibration_work);
}
pr_debug("RSB Enabled\n");
unlock:
mutex_unlock(&dev->rsb_state_mutex);
}
static void slatersb_disable_rsb(struct work_struct *work)
{
int rc = 0;
struct slatersb_priv *dev = container_of(work, struct slatersb_priv,
rsb_down_work);
mutex_lock(&dev->rsb_state_mutex);
dev->pending_enable = false;
if (dev->slatersb_current_state == SLATERSB_STATE_RSB_ENABLED) {
rc = slatersb_enable(dev, false);
if (rc != 0) {
pr_err("Failed to send disable command to SLATE\n");
goto unlock;
}
dev->slatersb_current_state = SLATERSB_STATE_RSB_CONFIGURED;
pr_debug("RSB Disabled\n");
}
unlock:
mutex_unlock(&dev->rsb_state_mutex);
}
static void slatersb_calibration(struct work_struct *work)
{
int rc = 0;
struct slatersb_msg req = {0};
struct slatersb_priv *dev =
container_of(work, struct slatersb_priv,
rsb_calibration_work);
mutex_lock(&dev->rsb_state_mutex);
if (!dev->is_cnfgrd) {
pr_err("RSB is not configured\n");
goto unlock;
}
req.cmd_id = SLATERSB_CALIBRATION_RESOLUTION;
req.data = dev->calbrtion_cpi;
rc = slatersb_tx_msg(dev, &req, 5);
if (rc != 0) {
pr_err("Failed to send resolution value to SLATE %d\n", rc);
goto unlock;
}
req.cmd_id = SLATERSB_CALIBRATION_INTERVAL;
req.data = dev->calbrtion_intrvl;
rc = slatersb_tx_msg(dev, &req, 5);
if (rc != 0) {
pr_err("Failed to send interval value to SLATE %d\n", rc);
goto unlock;
}
dev->is_calibrd = true;
pr_debug("RSB Calibrated\n");
unlock:
mutex_unlock(&dev->rsb_state_mutex);
}
static void slatersb_buttn_configration(struct work_struct *work)
{
int rc = 0;
struct slatersb_msg req = {0};
struct slatersb_priv *dev =
container_of(work, struct slatersb_priv,
bttn_configr_work);
mutex_lock(&dev->rsb_state_mutex);
if (!dev->is_cnfgrd) {
pr_err("RSB is not configured\n");
goto unlock;
}
req.cmd_id = SLATERSB_BUTTN_CONFIGRATION;
req.data = dev->bttn_configs;
rc = slatersb_tx_msg(dev, &req, 5);
if (rc != 0) {
pr_err("configuration cmnd failed %d\n",
rc);
goto unlock;
}
dev->bttn_configs = 0;
pr_debug("RSB Button configured\n");
unlock:
mutex_unlock(&dev->rsb_state_mutex);
}
static int slatersb_handle_cmd_in_ssr(struct slatersb_priv *dev, char *str)
{
long val;
int ret = 0;
char *tmp;
tmp = strsep(&str, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &val);
if (ret < 0)
return ret;
if (val == SLATERSB_POWER_ENABLE)
dev->pending_enable = true;
else if (val == SLATERSB_POWER_DISABLE)
dev->pending_enable = false;
return 0;
}
static int split_slate_work(struct slatersb_priv *dev, char *str)
{
long val;
int ret = 0;
char *tmp;
tmp = strsep(&str, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &val);
if (ret < 0)
return ret;
switch (val) {
case SLATERSB_IN_TWM:
dev->is_in_twm = true;
queue_work(dev->slatersb_wq, &dev->rsb_down_work);
break;
case SLATERSB_POWER_DISABLE:
queue_work(dev->slatersb_wq, &dev->rsb_down_work);
break;
case SLATERSB_OUT_TWM:
dev->is_in_twm = false;
queue_work(dev->slatersb_wq, &dev->rsb_up_work);
break;
case SLATERSB_POWER_ENABLE:
queue_work(dev->slatersb_wq, &dev->rsb_up_work);
break;
case SLATERSB_POWER_CALIBRATION:
tmp = strsep(&str, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &val);
if (ret < 0)
return ret;
dev->calbrtion_intrvl = (uint32_t)val;
tmp = strsep(&str, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &val);
if (ret < 0)
return ret;
dev->calbrtion_cpi = (uint32_t)val;
queue_work(dev->slatersb_wq, &dev->rsb_calibration_work);
break;
case SLATERSB_BTTN_CONFIGURE:
tmp = strsep(&str, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &val);
if (ret < 0)
return ret;
dev->bttn_configs = (uint8_t)val;
queue_work(dev->slatersb_wq, &dev->bttn_configr_work);
break;
}
return 0;
}
static ssize_t store_enable(struct device *pdev, struct device_attribute *attr,
const char *buff, size_t count)
{
int rc;
struct slatersb_priv *dev = dev_get_drvdata(pdev);
char *arr;
if (dev->blk_rsb_cmnds) {
pr_err("Device is in TWM state\n");
return count;
}
arr = kstrdup(buff, GFP_KERNEL);
if (!arr)
return -ENOMEM;
rc = split_slate_work(dev, arr);
if (!dev->is_cnfgrd) {
slatersb_handle_cmd_in_ssr(dev, arr);
kfree(arr);
return -ENOMEDIUM;
}
if (rc != 0)
pr_err("Not able to process request\n");
kfree(arr);
return count;
}
static ssize_t show_enable(struct device *dev, struct device_attribute *attr,
char *buff)
{
return 0;
}
static struct device_attribute dev_attr_rsb = {
.attr = {
.name = "enable",
.mode = 00660,
},
.show = show_enable,
.store = store_enable,
};
static int slatersb_init(struct slatersb_priv *dev)
{
slatersb_drv = &dev->lhndl;
mutex_init(&dev->glink_mutex);
mutex_init(&dev->rsb_state_mutex);
dev->slatersb_wq =
create_singlethread_workqueue("slate-work-queue");
if (!dev->slatersb_wq) {
pr_err("Failed to init SLATE-RSB work-queue\n");
return -ENOMEM;
}
init_waitqueue_head(&dev->link_state_wait);
/* set default slatersb state */
dev->slatersb_current_state = SLATERSB_STATE_INIT;
/* Init all works */
INIT_WORK(&dev->slate_up_work, slatersb_slateup_work);
INIT_WORK(&dev->slate_down_work, slatersb_slatedown_work);
INIT_WORK(&dev->rsb_up_work, slatersb_enable_rsb);
INIT_WORK(&dev->rsb_down_work, slatersb_disable_rsb);
INIT_WORK(&dev->rsb_calibration_work, slatersb_calibration);
INIT_WORK(&dev->bttn_configr_work, slatersb_buttn_configration);
slatersb_channel_init(&slatersb_notify_glink_channel_state, &slatersb_rx_msg);
return 0;
}
static int slate_rsb_probe(struct platform_device *pdev)
{
struct slatersb_priv *dev;
struct device_node *node;
int rc = 0;
node = pdev->dev.of_node;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* Add wake lock for PM suspend */
dev->slatersb_ws = wakeup_source_register(&pdev->dev, "slate_rsb");
dev->slatersb_current_state = SLATERSB_STATE_UNKNOWN;
rc = slatersb_init(dev);
if (rc)
pr_err("init failed\n");
/* register device for slate ssr */
rc = slatersb_ssr_register(dev);
if (rc)
pr_err("Failed to register for slate ssr\n");
rc = device_create_file(&pdev->dev, &dev_attr_rsb);
if (rc)
pr_err("Not able to create the file slate-rsb/enable\n");
dev_set_drvdata(&pdev->dev, dev);
pr_debug("RSB probe successfully\n");
return 0;
}
static int slate_rsb_remove(struct platform_device *pdev)
{
struct slatersb_priv *dev = platform_get_drvdata(pdev);
destroy_workqueue(dev->slatersb_wq);
wakeup_source_unregister(dev->slatersb_ws);
return 0;
}
static int slate_rsb_resume(struct device *pldev)
{
return 0;
}
static int slate_rsb_suspend(struct device *pldev)
{
return 0;
}
static const struct of_device_id slate_rsb_of_match[] = {
{ .compatible = "qcom,slate-rsb" },
{ },
};
static const struct dev_pm_ops pm_rsb = {
.resume = slate_rsb_resume,
.suspend = slate_rsb_suspend,
};
static struct platform_driver slate_rsb_driver = {
.driver = {
.name = "slate-rsb",
.of_match_table = slate_rsb_of_match,
.pm = &pm_rsb,
},
.probe = slate_rsb_probe,
.remove = slate_rsb_remove,
};
module_platform_driver(slate_rsb_driver);
MODULE_DESCRIPTION("SoC SLATE RSB driver");
MODULE_LICENSE("GPL");