// 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 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");