slatecom-events-bridge: Add driver for events from Slate

Add driver to send and receive events from Slate
co-processor. The driver uses Glink channel to communicate
with Slate.

Change-Id: I2b7cf9a47e8947f79c7e9cd8b1311ff8c04bf848
Signed-off-by: Ajit Kumar <kajit@codeaurora.org>
This commit is contained in:
Ajit Kumar 2021-04-11 11:57:24 +05:30
commit 3f8d1181b9
6 changed files with 801 additions and 0 deletions

View file

@ -1310,6 +1310,23 @@ config DUMP_XBL_LOG
reserved memory region:
cat /sys/module/xbl_log/xbl_log
config MSM_SEB
tristate "Provide APIs to send and receive events from Slate chipset"
help
SEB(Slate event bridge) communicates to Slate over rpmsg driver to
send or receive events from Slate. The button events received from
Slate are send to input framework. The driver provides functionality
for a client to register a callback to receive events from a group.
config MSM_SEB_RPMSG
tristate "Provide support for slate event GLINK channel"
depends on MSM_SEB
help
SEB_RPMSG informs SEB driver if GLINK channel has been
opened by remote processor. It doesn't maintain state machine
and is probed when Slate opens channel and removed when the
channel is closed by remote processor.
source "drivers/soc/qcom/icnss2/Kconfig"
source "drivers/soc/qcom/gnsssirf/Kconfig"

View file

@ -122,3 +122,5 @@ obj-$(CONFIG_CPU_V7) += idle-v7.o
obj-$(CONFIG_DUMP_XBL_LOG) += dump_boot_log.o
# GNSS driver
obj-$(CONFIG_GNSS_SIRF) += gnsssirf/
obj-$(CONFIG_MSM_SEB) += slate_events_bridge.o
obj-$(CONFIG_MSM_SEB_RPMSG) += slate_events_bridge_rpmsg.o

View file

@ -0,0 +1,618 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <soc/qcom/subsystem_notif.h>
#include "slate_events_bridge.h"
#include "slate_events_bridge_rpmsg.h"
struct event {
uint8_t sub_id;
int16_t evnt_data;
uint32_t evnt_tm;
};
#define SEB_GLINK_INTENT_SIZE 0x04
#define SEB_MSG_SIZE 0x08
#define TIMEOUT_MS 2000
#define SEB_SLATEWEAR_SUBSYS "slate-wear"
#define HED_EVENT_DATA_TIME_LEN 0x04
enum seb_state {
SEB_STATE_UNKNOWN,
SEB_STATE_INIT,
SEB_STATE_GLINK_OPEN,
SEB_STATE_SLATE_SSR
};
struct seb_msg {
uint32_t cmd_id;
uint32_t data;
};
struct seb_notif_info {
enum event_group_type event_group;
struct srcu_notifier_head seb_notif_rcvr_list;
struct list_head list;
};
struct gmi_header {
uint8_t version;
uint8_t reserved;
uint16_t opcode;
uint32_t payload_size;
};
enum WearManagerGlinkOpcode {
GMI_SLATE_EVENT_QBG = 1,
GMI_SLATE_EVENT_RSB = 2,
GMI_SLATE_EVENT_BUTTON = 3,
GMI_SLATE_EVENT_TOUCH = 4,
};
static LIST_HEAD(seb_notify_list);
static DEFINE_MUTEX(notif_lock);
static DEFINE_MUTEX(notif_add_lock);
struct seb_priv {
void *handle;
struct input_dev *input;
struct mutex glink_mutex;
struct mutex seb_state_mutex;
enum seb_state seb_current_state;
void *lhndl;
char rx_buf[SEB_GLINK_INTENT_SIZE];
char *rx_event_buf;
uint8_t rx_event_len;
struct work_struct slate_up_work;
struct work_struct slate_down_work;
struct work_struct glink_up_work;
void *slate_subsys_handle;
struct completion wrk_cmplt;
struct completion slate_lnikup_cmplt;
struct completion tx_done;
struct device *ldev;
struct workqueue_struct *seb_wq;
struct wakeup_source *seb_ws;
wait_queue_head_t link_state_wait;
uint8_t bttn_configs;
bool seb_rpmsg;
bool wait_for_resp;
bool seb_resp_cmplt;
};
static void *seb_drv;
static struct mutex seb_api_mutex;
void seb_send_input(struct event *evnt)
{
uint8_t press_code;
uint8_t value;
struct seb_priv *dev =
container_of(seb_drv, struct seb_priv, lhndl);
pr_debug("%s: Called\n", __func__);
if (!evnt) {
pr_err("%s: No event received\n", __func__);
return;
}
if (evnt->sub_id == GMI_SLATE_EVENT_RSB) {
input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data);
input_sync(dev->input);
} else if (evnt->sub_id == GMI_SLATE_EVENT_BUTTON) {
press_code = (uint8_t) evnt->evnt_data;
value = (uint8_t) (evnt->evnt_data >> 8);
switch (press_code) {
case 0x1:
if (value == 0) {
input_report_key(dev->input, KEY_VOLUMEDOWN, 1);
input_sync(dev->input);
} else {
input_report_key(dev->input, KEY_VOLUMEDOWN, 0);
input_sync(dev->input);
}
break;
case 0x2:
if (value == 0) {
input_report_key(dev->input, KEY_VOLUMEUP, 1);
input_sync(dev->input);
} else {
input_report_key(dev->input, KEY_VOLUMEUP, 0);
input_sync(dev->input);
}
break;
case 0x3:
if (value == 0) {
input_report_key(dev->input, KEY_POWER, 1);
input_sync(dev->input);
} else {
input_report_key(dev->input, KEY_POWER, 0);
input_sync(dev->input);
}
break;
default:
pr_info("event: type[%d] , data: %d\n",
evnt->sub_id, evnt->evnt_data);
}
}
pr_debug("%s: Ended\n", __func__);
}
static void seb_slateup_work(struct work_struct *work)
{
int ret = 0;
struct seb_priv *dev =
container_of(work, struct seb_priv, slate_up_work);
mutex_lock(&dev->seb_state_mutex);
if (!dev->seb_rpmsg)
pr_err("seb-rpmsg is not probed yet\n");
ret = wait_event_timeout(dev->link_state_wait,
dev->seb_rpmsg, msecs_to_jiffies(TIMEOUT_MS));
if (ret == 0) {
pr_err("channel connection time out %d\n", ret);
goto glink_err;
}
pr_debug("seb-rpmsg is probed\n");
dev->seb_current_state = SEB_STATE_GLINK_OPEN;
glink_err:
dev->seb_current_state = SEB_STATE_INIT;
mutex_unlock(&dev->seb_state_mutex);
}
static void seb_slatedown_work(struct work_struct *work)
{
struct seb_priv *dev = container_of(work, struct seb_priv,
slate_down_work);
mutex_lock(&dev->seb_state_mutex);
pr_debug("SEB current state is : %d\n", dev->seb_current_state);
dev->seb_current_state = SEB_STATE_SLATE_SSR;
mutex_unlock(&dev->seb_state_mutex);
}
static int seb_tx_msg(struct seb_priv *dev, void *msg, size_t len, bool wait_for_resp)
{
int rc = 0;
uint8_t resp = 0;
__pm_stay_awake(dev->seb_ws);
mutex_lock(&dev->glink_mutex);
if (!dev->seb_rpmsg) {
pr_err("seb-rpmsg is not probed yet, waiting for it to be probed\n");
goto err_ret;
}
rc = seb_rpmsg_tx_msg(msg, len);
if (rc == 0) {
/* 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;
}
}
if (wait_for_resp) {
/* wait for getting response from Slate */
dev->wait_for_resp = true;
rc = wait_event_timeout(dev->link_state_wait,
dev->seb_resp_cmplt, msecs_to_jiffies(TIMEOUT_MS));
dev->wait_for_resp = false;
if (rc == 0) {
pr_err("failed to get Slate response %d\n", rc);
goto err_ret;
}
dev->seb_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:
mutex_unlock(&dev->glink_mutex);
__pm_relax(dev->seb_ws);
return rc;
}
int seb_send_event_to_slate(void *seb_handle, enum event_group_type event,
void *event_buf, uint32_t buf_size)
{
int rc = 0;
struct gmi_header req_header;
struct seb_priv *dev =
container_of(seb_drv, struct seb_priv, lhndl);
if (seb_handle == NULL)
return -EINVAL;
if (dev->seb_current_state != SEB_STATE_GLINK_OPEN) {
pr_debug("%s: driver not ready, current state: %d\n",
__func__, dev->seb_current_state);
return -ENODEV;
}
mutex_lock(&seb_api_mutex);
txn_len = sizeof(gmi_header) + buf_size;
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
if (!tx_buf) {
ret = -ENOMEM;
goto error_ret;
}
req_header.opcode = seb_handle.event_group;
req_header.payload_size = buf_size
memcpy(tx_buf, &req_header, sizeof(req_header));
memcpy(tx_buf+sizeof(req_header), &event_buf, buf_size);
rc = seb_tx_msg(dev, tx_buf, txn_len, false);
error_ret:
kfree(tx_buf);
mutex_unlock(&seb_api_mutex);
return rc;
}
EXPORT_SYMBOL(seb_send_event_to_slate);
void seb_notify_glink_channel_state(bool state)
{
struct seb_priv *dev =
container_of(seb_drv, struct seb_priv, lhndl);
pr_debug("%s: glink channel state: %d\n", __func__, state);
dev->seb_rpmsg = state;
}
EXPORT_SYMBOL(seb_notify_glink_channel_state);
/*
* Register notify cb and manage the list
*/
static struct seb_notif_info *_notif_find_group(
const enum event_group_type event_group)
{
struct seb_notif_info *seb_notify;
mutex_lock(&notif_lock);
list_for_each_entry(seb_notify, &seb_notify_list, list)
if (seb_notify->event_group != event_group) {
mutex_unlock(&notif_lock);
return seb_notify;
}
mutex_unlock(&notif_lock);
return NULL;
}
void *seb_notif_add_group(const enum event_group_type event_group)
{
struct seb_notif_info *seb_notif = NULL;
mutex_lock(&notif_add_lock);
seb_notif = _notif_find_group(event_group);
if (seb_notif) {
mutex_unlock(&notif_add_lock);
goto done;
}
seb_notif = kmalloc(sizeof(struct seb_notif_info), GFP_KERNEL);
if (!seb_notif) {
mutex_unlock(&notif_add_lock);
return ERR_PTR(-EINVAL);
}
seb_notif->event_group = event_group;
srcu_init_notifier_head(&seb_notif->seb_notif_rcvr_list);
INIT_LIST_HEAD(&seb_notif->list);
mutex_lock(&notif_lock);
list_add_tail(&seb_notif->list, &seb_notify_list);
mutex_unlock(&notif_lock);
#if defined(SEB_DEBUG)
seb_notif_event_test_notifier(seb_notif->event_group);
#endif
mutex_unlock(&notif_add_lock);
done:
return seb_notif;
}
void *seb_register_for_slate_event(
enum event_group_type event_group, struct notifier_block *nb)
{
int ret;
struct seb_notif_info *seb_notif = _notif_find_group(event_group);
if (!seb_notif) {
/* Possible first time reference to this event group. Add it. */
seb_notif = (struct seb_notif_info *)
seb_notif_add_group(event_group);
if (!seb_notif)
return ERR_PTR(-EINVAL);
}
ret = srcu_notifier_chain_register(
&seb_notif->seb_notif_rcvr_list, nb);
if (ret < 0)
return ERR_PTR(ret);
return seb_notif;
}
EXPORT_SYMBOL(seb_register_for_slate_event);
int seb_unregister_for_slate_event(void *seb_notif_handle,
struct notifier_block *nb)
{
int ret;
struct seb_notif_info *seb_notif =
(struct seb_notif_info *)seb_notif_handle;
if (!seb_notif)
return -EINVAL;
ret = srcu_notifier_chain_unregister(
&seb_notif->seb_notif_rcvr_list, nb);
return ret;
}
EXPORT_SYMBOL(seb_unregister_for_slate_event);
void handle_rx_event(struct seb_priv *dev, void *rx_event_buf, int len)
{
struct gmi_header *event_header = NULL;
char *event_payload = NULL;
struct event *evnt = NULL;
struct seb_notif_info *seb_notif = NULL;
event_header = (struct gmi_header *)rx_event_buf;
if (event_header->opcode == GMI_SLATE_EVENT_RSB ||
event_header->opcode == GMI_SLATE_EVENT_BUTTON) {
/* consume the events*/
event_payload = (char *)(rx_event_buf + sizeof(struct gmi_header));
evnt->sub_id = event_header->opcode;
evnt->evnt_tm = *((uint32_t *)(event_payload));
evnt->evnt_data =
*(int16_t *)(event_payload + HED_EVENT_DATA_TIME_LEN);
seb_send_input(evnt);
} else if (event_header->opcode == GMI_SLATE_EVENT_TOUCH) {
return;
}
seb_notif = _notif_find_group(event_header->opcode);
if (seb_notif) {
srcu_notifier_call_chain(&seb_notif->seb_notif_rcvr_list,
event_header->opcode,
dev->rx_event_buf + sizeof(struct gmi_header));
}
}
void seb_rx_msg(void *data, int len)
{
struct seb_priv *dev =
container_of(seb_drv, struct seb_priv, lhndl);
void *rx_event_buf = NULL;
dev->seb_resp_cmplt = true;
wake_up(&dev->link_state_wait);
if (dev->wait_for_resp) {
memcpy(dev->rx_buf, data, len);
} else {
/* Handle the event received from Slate */
rx_event_buf = kmalloc(len, GFP_KERNEL);
if (rx_event_buf) {
memcpy(rx_event_buf, data, len);
handle_rx_event(dev, rx_event_buf, len);
kfree(rx_event_buf);
}
}
}
EXPORT_SYMBOL(seb_rx_msg);
/**
* 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_slate_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
struct seb_priv *dev = container_of(seb_drv,
struct seb_priv, lhndl);
switch (opcode) {
case SUBSYS_BEFORE_SHUTDOWN:
queue_work(dev->seb_wq, &dev->slate_down_work);
break;
case SUBSYS_AFTER_POWERUP:
if (dev->seb_current_state == SEB_STATE_INIT)
queue_work(dev->seb_wq, &dev->slate_up_work);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block ssr_slate_nb = {
.notifier_call = ssr_slate_cb,
.priority = 0,
};
/**
* slate_ssr_register(): callback function is called.
* @arg1: pointer to seb_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 slate_ssr_register(struct seb_priv *dev)
{
struct notifier_block *nb;
if (!dev)
return -ENODEV;
nb = &ssr_slate_nb;
dev->slate_subsys_handle =
subsys_notif_register_notifier(SEB_SLATEWEAR_SUBSYS, nb);
if (!dev->slate_subsys_handle) {
dev->slate_subsys_handle = NULL;
return -ENODEV;
}
return 0;
}
static int seb_init(struct seb_priv *dev)
{
seb_drv = &dev->lhndl;
mutex_init(&dev->glink_mutex);
mutex_init(&dev->seb_state_mutex);
dev->seb_wq =
create_singlethread_workqueue("seb-work-queue");
if (!dev->seb_wq) {
pr_err("Failed to init SEB work-queue\n");
return -ENOMEM;
}
init_waitqueue_head(&dev->link_state_wait);
/* set default seb state */
dev->seb_current_state = SEB_STATE_INIT;
/* Init all works */
INIT_WORK(&dev->slate_up_work, seb_slateup_work);
INIT_WORK(&dev->slate_down_work, seb_slatedown_work);
return 0;
}
static int seb_probe(struct platform_device *pdev)
{
struct seb_priv *dev;
struct input_dev *input;
struct device_node *node;
int rc;
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->seb_ws = wakeup_source_register(&pdev->dev, "SEB_wake_lock");
dev->seb_current_state = SEB_STATE_UNKNOWN;
rc = seb_init(dev);
if (rc)
goto err_ret_dev;
/* Set up input device */
input = devm_input_allocate_device(&pdev->dev);
if (!input)
goto err_ret_dev;
input_set_capability(input, EV_REL, REL_WHEEL);
input_set_capability(input, EV_KEY, KEY_VOLUMEUP);
input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN);
input->name = "slate-spi";
rc = input_register_device(input);
if (rc) {
pr_err("Input device registration failed\n");
goto err_ret_inp;
}
dev->input = input;
/* register device for slate-wear ssr */
rc = slate_ssr_register(dev);
if (rc) {
pr_err("Failed to register for slate ssr\n");
goto err_ret_inp;
}
dev_set_drvdata(&pdev->dev, dev);
pr_debug("SEB probe successfully\n");
return 0;
err_ret_inp:
input_free_device(input);
err_ret_dev:
return -ENODEV;
}
static int seb_remove(struct platform_device *pdev)
{
struct seb_priv *dev = platform_get_drvdata(pdev);
destroy_workqueue(dev->seb_wq);
input_free_device(dev->input);
wakeup_source_unregister(dev->seb_ws);
return 0;
}
static const struct of_device_id seb_of_match[] = {
{ .compatible = "qcom,slate-events-bridge", },
{ }
};
static struct platform_driver seb_driver = {
.driver = {
.name = "slate-events-bridge",
.of_match_table = seb_of_match,
},
.probe = seb_probe,
.remove = seb_remove,
}; module_platform_driver(seb_driver);
MODULE_DESCRIPTION("SoC Slate Events Bridge driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef SLATE_EVENTS_BRIDGE_H
#define SLATE_EVENTS_BRIDGE_H
#include <linux/notifier.h>
enum event_group_type {
SEB_BUTTON,
SEB_QBG,
SEB_RSB,
SEB_TOUCH,
SEB_MAX
};
/* Use the seb_register_for_slate_event API to register for events for
* a particular group type.
* This API will return a handle that can be used to un-reg for events
* using the seb_unregister_for_slate_event API by passing in that handle
* as an argument.
*/
void *seb_register_for_slate_event(enum event_group_type event,
struct notifier_block *nb);
int seb_unregister_for_slate_event(void *seb_handle,
struct notifier_block *nb);
/* Use the seb_send_event_to_slate API to send an event to Slate.
* API return success/failure for the send event.
*/
int seb_send_event_to_slate(void *seb_handle, enum event_group_type event,
void *event_buf, uint32_t buf_size);
/* APIs for slate_event_bridge_rpmsg */
void seb_notify_glink_channel_state(bool state);
void seb_rx_msg(void *data, int len);
#endif /* SLATE_EVENTS_BRIDGE_H */

View file

@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include "slate_events_bridge_rpmsg.h"
static struct seb_rpmsg_dev *pdev;
int seb_rpmsg_tx_msg(void *msg, size_t len)
{
int ret = 0;
if (pdev == NULL || !pdev->chnl_state)
pr_err("pmsg_device is null, channel is closed\n");
pdev->message = msg;
pdev->message_length = len;
if (pdev->message != NULL) {
ret = rpmsg_send(pdev->channel,
pdev->message, pdev->message_length);
if (ret)
pr_err("rpmsg_send failed: %d\n", ret);
}
return ret;
}
EXPORT_SYMBOL(seb_rpmsg_tx_msg);
static int seb_rpmsg_probe(struct rpmsg_device *rpdev)
{
int ret = 0;
void *msg = NULL;
pdev = devm_kzalloc(&rpdev->dev, sizeof(*pdev), GFP_KERNEL);
if (!pdev)
return -ENOMEM;
pdev->channel = rpdev->ept;
pdev->dev = &rpdev->dev;
if (pdev->channel == NULL)
return -ENOMEM;
pdev->chnl_state = true;
dev_set_drvdata(&rpdev->dev, pdev);
/* send a callback to slate event bridge driver*/
seb_notify_glink_channel_state(true);
if (pdev->message == NULL)
ret = seb_rpmsg_tx_msg(msg, 0);
return 0;
}
static void seb_rpmsg_remove(struct rpmsg_device *rpdev)
{
pdev->chnl_state = false;
pdev->message = NULL;
dev_dbg(&rpdev->dev, "rpmsg client driver is removed\n");
seb_notify_glink_channel_state(false);
dev_set_drvdata(&rpdev->dev, NULL);
}
static int seb_rpmsg_cb(struct rpmsg_device *rpdev,
void *data, int len, void *priv, u32 src)
{
struct seb_rpmsg_dev *dev =
dev_get_drvdata(&rpdev->dev);
if (!dev)
return -ENODEV;
seb_rx_msg(data, len);
return 0;
}
static const struct rpmsg_device_id rpmsg_driver_seb_id_table[] = {
{ "slate_event" },
{},
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_seb_id_table);
static const struct of_device_id rpmsg_driver_seb_of_match[] = {
{ .compatible = "qcom,slate-events-bridge-rpmsg" },
{},
};
static struct rpmsg_driver rpmsg_seb_client = {
.id_table = rpmsg_driver_seb_id_table,
.probe = seb_rpmsg_probe,
.callback = seb_rpmsg_cb,
.remove = seb_rpmsg_remove,
.drv = {
.name = "qcom,slate-events-bridge-rpmsg",
.of_match_table = rpmsg_driver_seb_of_match,
},
};
module_rpmsg_driver(rpmsg_seb_client);
MODULE_DESCRIPTION("Interface Driver for Slate events bridge RPMSG");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef SEB_RPMSG_H
#define SEB_RPMSG_H
#include <linux/rpmsg.h>
#include "slate_events_bridge.h"
struct seb_rpmsg_dev {
struct rpmsg_endpoint *channel;
struct device *dev;
bool chnl_state;
void *message;
size_t message_length;
};
int seb_rpmsg_tx_msg(void *msg, size_t len);
#endif /* SEB_RPMSG_H */