From 1bf666cef4a211e672a8facb23caf1e2f9fb9fb5 Mon Sep 17 00:00:00 2001 From: Raghavendra Kakarla Date: Mon, 22 Jul 2019 17:14:42 +0530 Subject: [PATCH] soc: qcom: Add RPM SMD Driver This is a snapshot of the RPM-SMD driver functionality as of 'commit 0453f0e705df8b("move rpm-smd initcall to postcore_initcall_sync")' on msm-4.14 branch. Change-Id: Ie70bca9c28ae2339b45715f7d82839f39e733832 Signed-off-by: Raghavendra Kakarla --- drivers/rpmsg/Kconfig | 9 + drivers/rpmsg/Makefile | 1 + drivers/rpmsg/rpm-smd.c | 1645 ++++++++++++++++++++++++++ drivers/soc/qcom/Makefile | 3 + drivers/soc/qcom/rpm-smd-debug.c | 144 +++ include/soc/qcom/rpm-notifier.h | 56 + include/soc/qcom/rpm-smd.h | 303 +++++ include/trace/events/trace_rpm_smd.h | 104 ++ 8 files changed, 2265 insertions(+) create mode 100644 drivers/rpmsg/rpm-smd.c create mode 100644 drivers/soc/qcom/rpm-smd-debug.c create mode 100644 include/soc/qcom/rpm-notifier.h create mode 100644 include/soc/qcom/rpm-smd.h create mode 100644 include/trace/events/trace_rpm_smd.h diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index c1b34cdc2054..5fccffcfa668 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -77,4 +77,13 @@ config RPMSG_VIRTIO select RPMSG select VIRTIO +config MSM_RPM_SMD + bool "RPM driver using SMD protocol" + help + RPM is the dedicated hardware engine for managing shared SoC + resources. This config adds driver support for using SMD as a + transport layer communication with RPM hardware. It also selects + the MSM_MPM config that programs the MPM module to monitor interrupts + during sleep modes. + endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 1680763bb8b5..8220b8db74cf 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o +obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SPSS) += qcom_glink_spss.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SPI) += qcom_glink_spi.o diff --git a/drivers/rpmsg/rpm-smd.c b/drivers/rpmsg/rpm-smd.c new file mode 100644 index 000000000000..5550068262d0 --- /dev/null +++ b/drivers/rpmsg/rpm-smd.c @@ -0,0 +1,1645 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +#define DEFAULT_BUFFER_SIZE 256 +#define DEBUG_PRINT_BUFFER_SIZE 512 +#define MAX_SLEEP_BUFFER 128 +#define INV_RSC "resource does not exist" +#define ERR "err\0" +#define MAX_ERR_BUFFER_SIZE 128 +#define MAX_WAIT_ON_ACK 24 +#define INIT_ERROR 1 +#define V1_PROTOCOL_VERSION 0x31726576 /* rev1 */ +#define V0_PROTOCOL_VERSION 0 /* rev0 */ +#define RPM_MSG_TYPE_OFFSET 16 +#define RPM_MSG_TYPE_SIZE 8 +#define RPM_SET_TYPE_OFFSET 28 +#define RPM_SET_TYPE_SIZE 4 +#define RPM_REQ_LEN_OFFSET 0 +#define RPM_REQ_LEN_SIZE 16 +#define RPM_MSG_VERSION_OFFSET 24 +#define RPM_MSG_VERSION_SIZE 8 +#define RPM_MSG_VERSION 1 +#define RPM_MSG_SET_OFFSET 28 +#define RPM_MSG_SET_SIZE 4 +#define RPM_RSC_ID_OFFSET 16 +#define RPM_RSC_ID_SIZE 12 +#define RPM_DATA_LEN_OFFSET 0 +#define RPM_DATA_LEN_SIZE 16 +#define RPM_HDR_SIZE ((rpm_msg_fmt_ver == RPM_MSG_V0_FMT) ?\ + sizeof(struct rpm_v0_hdr) : sizeof(struct rpm_v1_hdr)) +#define CLEAR_FIELD(offset, size) (~GENMASK(offset + size - 1, offset)) + +#define for_each_kvp(buf, k) \ + for (k = (struct kvp *)get_first_kvp(buf); \ + ((void *)k - (void *)get_first_kvp(buf)) < \ + get_data_len(buf);\ + k = get_next_kvp(k)) + + +/* Debug Definitions */ +enum { + MSM_RPM_LOG_REQUEST_PRETTY = BIT(0), + MSM_RPM_LOG_REQUEST_RAW = BIT(1), + MSM_RPM_LOG_REQUEST_SHOW_MSG_ID = BIT(2), +}; + +static int msm_rpm_debug_mask; +module_param_named( + debug_mask, msm_rpm_debug_mask, int, 0644 +); + +static uint32_t rpm_msg_fmt_ver; +module_param_named( + rpm_msg_fmt_ver, rpm_msg_fmt_ver, uint, 0444 +); + +struct msm_rpm_driver_data { + const char *ch_name; + uint32_t ch_type; + struct smd_channel *ch_info; + struct work_struct work; + spinlock_t smd_lock_write; + spinlock_t smd_lock_read; + struct completion smd_open; +}; + +struct qcom_smd_rpm { + struct rpmsg_endpoint *rpm_channel; + struct device *dev; + int irq; + struct completion ack; + struct mutex lock; + int ack_status; +}; + +struct qcom_smd_rpm *rpm; +struct qcom_smd_rpm priv_rpm; + +static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier); +static bool standalone; +static int probe_status = -EPROBE_DEFER; +static void msm_rpm_process_ack(uint32_t msg_id, int errno); + +int msm_rpm_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&msm_rpm_sleep_notifier, nb); +} + +int msm_rpm_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&msm_rpm_sleep_notifier, nb); +} + +enum { + MSM_RPM_MSG_REQUEST_TYPE = 0, + MSM_RPM_MSG_TYPE_NR, +}; + +static const uint32_t msm_rpm_request_service_v1[MSM_RPM_MSG_TYPE_NR] = { + 0x716572, /* 'req\0' */ +}; + +enum { + RPM_V1_REQUEST_SERVICE, + RPM_V1_SYSTEMDB_SERVICE, + RPM_V1_COMMAND_SERVICE, + RPM_V1_ACK_SERVICE, + RPM_V1_NACK_SERVICE, +} msm_rpm_request_service_v2; + +struct rpm_v0_hdr { + uint32_t service_type; + uint32_t request_len; +}; + +struct rpm_v1_hdr { + uint32_t request_hdr; +}; + +struct rpm_message_header_v0 { + struct rpm_v0_hdr hdr; + uint32_t msg_id; + enum msm_rpm_set set; + uint32_t resource_type; + uint32_t resource_id; + uint32_t data_len; +}; + +struct rpm_message_header_v1 { + struct rpm_v1_hdr hdr; + uint32_t msg_id; + uint32_t resource_type; + uint32_t request_details; +}; + +struct msm_rpm_ack_msg_v0 { + uint32_t req; + uint32_t req_len; + uint32_t rsc_id; + uint32_t msg_len; + uint32_t id_ack; +}; + +struct msm_rpm_ack_msg_v1 { + uint32_t request_hdr; + uint32_t id_ack; +}; + +struct kvp { + unsigned int k; + unsigned int s; +}; + +struct msm_rpm_kvp_data { + uint32_t key; + uint32_t nbytes; /* number of bytes */ + uint8_t *value; + bool valid; +}; + +struct slp_buf { + struct rb_node node; + char ubuf[MAX_SLEEP_BUFFER]; + char *buf; + bool valid; +}; + +enum rpm_msg_fmts { + RPM_MSG_V0_FMT, + RPM_MSG_V1_FMT +}; + +static struct rb_root tr_root = RB_ROOT; +static int msm_rpm_send_smd_buffer(char *buf, uint32_t size); +static uint32_t msm_rpm_get_next_msg_id(void); + +static inline uint32_t get_offset_value(uint32_t val, uint32_t offset, + uint32_t size) +{ + return (((val) & GENMASK(offset + size - 1, offset)) + >> offset); +} + +static inline void change_offset_value(uint32_t *val, uint32_t offset, + uint32_t size, int32_t val1) +{ + uint32_t member = *val; + uint32_t offset_val = get_offset_value(member, offset, size); + uint32_t mask = (1 << size) - 1; + + offset_val += val1; + *val &= CLEAR_FIELD(offset, size); + *val |= ((offset_val & mask) << offset); +} + +static inline void set_offset_value(uint32_t *val, uint32_t offset, + uint32_t size, uint32_t val1) +{ + uint32_t mask = (1 << size) - 1; + + *val &= CLEAR_FIELD(offset, size); + *val |= ((val1 & mask) << offset); +} + +static uint32_t get_msg_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->msg_id; + + return ((struct rpm_message_header_v1 *)buf)->msg_id; + +} + +static uint32_t get_ack_msg_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->id_ack; + + return ((struct msm_rpm_ack_msg_v1 *)buf)->id_ack; + +} + +static uint32_t get_rsc_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->resource_type; + + return ((struct rpm_message_header_v1 *)buf)->resource_type; + +} + +static uint32_t get_set_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->set; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_SET_TYPE_OFFSET, + RPM_SET_TYPE_SIZE); +} + +static uint32_t get_data_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->data_len; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE); +} + +static uint32_t get_rsc_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->resource_id; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_RSC_ID_OFFSET, + RPM_RSC_ID_SIZE); +} + +static uint32_t get_ack_req_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->req_len; + + return get_offset_value(((struct msm_rpm_ack_msg_v1 *)buf)-> + request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE); +} + +static uint32_t get_ack_msg_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->req; + + return get_offset_value(((struct msm_rpm_ack_msg_v1 *)buf)-> + request_hdr, RPM_MSG_TYPE_OFFSET, + RPM_MSG_TYPE_SIZE); +} + +static uint32_t get_req_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->hdr.request_len; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE); +} + +static void set_msg_ver(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver) { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_VERSION_OFFSET, + RPM_MSG_VERSION_SIZE, val); + } else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_VERSION_OFFSET, + RPM_MSG_VERSION_SIZE, 0); + } +} + +static void set_req_len(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->hdr.request_len = val; + else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE, val); + } +} + +static void change_req_len(char *buf, int32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->hdr.request_len += val; + else { + change_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE, val); + } +} + +static void set_msg_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->hdr.service_type = + msm_rpm_request_service_v1[val]; + else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_TYPE_OFFSET, + RPM_MSG_TYPE_SIZE, RPM_V1_REQUEST_SERVICE); + } +} + +static void set_rsc_id(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->resource_id = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_RSC_ID_OFFSET, + RPM_RSC_ID_SIZE, val); +} + +static void set_data_len(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->data_len = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE, val); +} +static void change_data_len(char *buf, int32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->data_len += val; + else + change_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE, val); +} + +static void set_set_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->set = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_SET_TYPE_OFFSET, + RPM_SET_TYPE_SIZE, val); +} + +static void set_msg_id(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->msg_id = val; + else + ((struct rpm_message_header_v1 *)buf)->msg_id = val; + +} + +static void set_rsc_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->resource_type = val; + else + ((struct rpm_message_header_v1 *)buf)->resource_type = val; +} + +static inline int get_buf_len(char *buf) +{ + return get_req_len(buf) + RPM_HDR_SIZE; +} + +static inline struct kvp *get_first_kvp(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return (struct kvp *)(buf + + sizeof(struct rpm_message_header_v0)); + else + return (struct kvp *)(buf + + sizeof(struct rpm_message_header_v1)); +} + +static inline struct kvp *get_next_kvp(struct kvp *k) +{ + return (struct kvp *)((void *)k + sizeof(*k) + k->s); +} + +static inline void *get_data(struct kvp *k) +{ + return (void *)k + sizeof(*k); +} + +static void delete_kvp(char *buf, struct kvp *d) +{ + struct kvp *n; + int dec; + uint32_t size; + + n = get_next_kvp(d); + dec = (void *)n - (void *)d; + size = get_data_len(buf) - + ((void *)n - (void *)get_first_kvp(buf)); + + memcpy((void *)d, (void *)n, size); + + change_data_len(buf, -dec); + change_req_len(buf, -dec); +} + +static inline void update_kvp_data(struct kvp *dest, struct kvp *src) +{ + memcpy(get_data(dest), get_data(src), src->s); +} + +static void add_kvp(char *buf, struct kvp *n) +{ + int32_t inc = sizeof(*n) + n->s; + + if (get_req_len(buf) + inc > MAX_SLEEP_BUFFER) { + WARN_ON(get_req_len(buf) + inc > MAX_SLEEP_BUFFER); + return; + } + + memcpy(buf + get_buf_len(buf), n, inc); + + change_data_len(buf, inc); + change_req_len(buf, inc); +} + +static struct slp_buf *tr_search(struct rb_root *root, char *slp) +{ + unsigned int type = get_rsc_type(slp); + unsigned int id = get_rsc_id(slp); + struct rb_node *node = root->rb_node; + + while (node) { + struct slp_buf *cur = rb_entry(node, struct slp_buf, node); + unsigned int ctype = get_rsc_type(cur->buf); + unsigned int cid = get_rsc_id(cur->buf); + + if (type < ctype) + node = node->rb_left; + else if (type > ctype) + node = node->rb_right; + else if (id < cid) + node = node->rb_left; + else if (id > cid) + node = node->rb_right; + else + return cur; + } + return NULL; +} + +static int tr_insert(struct rb_root *root, struct slp_buf *slp) +{ + unsigned int type = get_rsc_type(slp->buf); + unsigned int id = get_rsc_id(slp->buf); + struct rb_node **node = &(root->rb_node), *parent = NULL; + + while (*node) { + struct slp_buf *curr = rb_entry(*node, struct slp_buf, node); + unsigned int ctype = get_rsc_type(curr->buf); + unsigned int cid = get_rsc_id(curr->buf); + + parent = *node; + + if (type < ctype) + node = &((*node)->rb_left); + else if (type > ctype) + node = &((*node)->rb_right); + else if (id < cid) + node = &((*node)->rb_left); + else if (id > cid) + node = &((*node)->rb_right); + else + return -EINVAL; + } + + rb_link_node(&slp->node, parent, node); + rb_insert_color(&slp->node, root); + slp->valid = true; + return 0; +} + +static void tr_update(struct slp_buf *s, char *buf) +{ + struct kvp *e, *n; + + for_each_kvp(buf, n) { + bool found = false; + + for_each_kvp(s->buf, e) { + if (n->k == e->k) { + found = true; + if (n->s == e->s) { + void *e_data = get_data(e); + void *n_data = get_data(n); + + if (memcmp(e_data, n_data, n->s)) { + update_kvp_data(e, n); + s->valid = true; + } + } else { + delete_kvp(s->buf, e); + add_kvp(s->buf, n); + s->valid = true; + } + break; + } + + } + if (!found) { + add_kvp(s->buf, n); + s->valid = true; + } + } +} +static atomic_t msm_rpm_msg_id = ATOMIC_INIT(0); + +struct msm_rpm_request { + uint8_t *client_buf; + struct msm_rpm_kvp_data *kvp; + uint32_t num_elements; + uint32_t write_idx; + uint8_t *buf; + uint32_t numbytes; +}; + +/* + * Data related to message acknowledgment + */ + +LIST_HEAD(msm_rpm_wait_list); + +struct msm_rpm_wait_data { + struct list_head list; + uint32_t msg_id; + bool ack_recd; + int errno; + struct completion ack; + bool delete_on_ack; +}; + +DEFINE_SPINLOCK(msm_rpm_list_lock); +LIST_HEAD(msm_rpm_ack_list); + +static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf) +{ + return get_ack_msg_id(buf); +} + +static inline int msm_rpm_get_error_from_ack(uint8_t *buf) +{ + uint8_t *tmp; + uint32_t req_len = get_ack_req_len(buf); + uint32_t msg_type = get_ack_msg_type(buf); + int rc = -ENODEV; + uint32_t err; + uint32_t ack_msg_size = rpm_msg_fmt_ver ? + sizeof(struct msm_rpm_ack_msg_v1) : + sizeof(struct msm_rpm_ack_msg_v0); + + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT && + msg_type == RPM_V1_ACK_SERVICE) { + return 0; + } else if (rpm_msg_fmt_ver && msg_type == RPM_V1_NACK_SERVICE) { + err = *(uint32_t *)(buf + sizeof(struct msm_rpm_ack_msg_v1)); + return err; + } + + req_len -= ack_msg_size; + req_len += 2 * sizeof(uint32_t); + if (!req_len) + return 0; + + pr_err("%s:rpm returned error or nack req_len: %d id_ack: %d\n", + __func__, req_len, get_ack_msg_id(buf)); + + tmp = buf + ack_msg_size; + + if (memcmp(tmp, ERR, sizeof(uint32_t))) { + pr_err("%s rpm returned error\n", __func__); + WARN_ON(1); + } + + tmp += 2 * sizeof(uint32_t); + + if (!(memcmp(tmp, INV_RSC, min_t(uint32_t, req_len, + sizeof(INV_RSC))-1))) { + pr_err("%s(): RPM NACK Unsupported resource\n", __func__); + rc = -EINVAL; + } else { + pr_err("%s(): RPM NACK Invalid header\n", __func__); + } + + return rc; +} + +int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata, + uint32_t size, gfp_t flag) +{ + struct slp_buf *slp; + static DEFINE_SPINLOCK(slp_buffer_lock); + unsigned long flags; + char *buf; + + buf = cdata->buf; + + if (size > MAX_SLEEP_BUFFER) + return -ENOMEM; + + spin_lock_irqsave(&slp_buffer_lock, flags); + slp = tr_search(&tr_root, buf); + + if (!slp) { + slp = kzalloc(sizeof(struct slp_buf), GFP_ATOMIC); + if (!slp) { + spin_unlock_irqrestore(&slp_buffer_lock, flags); + return -ENOMEM; + } + slp->buf = PTR_ALIGN(&slp->ubuf[0], sizeof(u32)); + memcpy(slp->buf, buf, size); + if (tr_insert(&tr_root, slp)) { + pr_err("Error updating sleep request\n"); + kfree(slp); + return -EINVAL; + } + } else { + /* handle unsent requests */ + tr_update(slp, buf); + } + trace_rpm_smd_sleep_set(get_msg_id(cdata->client_buf), + get_rsc_type(cdata->client_buf), + get_req_len(cdata->client_buf)); + + spin_unlock_irqrestore(&slp_buffer_lock, flags); + + return 0; +} + +static struct msm_rpm_driver_data msm_rpm_data = { + .smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open), +}; + +static int msm_rpm_flush_requests(bool print) +{ + struct rb_node *t; + int ret; + int count = 0; + + for (t = rb_first(&tr_root); t; t = rb_next(t)) { + + struct slp_buf *s = rb_entry(t, struct slp_buf, node); + unsigned int type = get_rsc_type(s->buf); + unsigned int id = get_rsc_id(s->buf); + + if (!s->valid) + continue; + + set_msg_id(s->buf, msm_rpm_get_next_msg_id()); + + ret = msm_rpm_send_smd_buffer(s->buf, + get_buf_len(s->buf)); + WARN_ON(ret != 0); + trace_rpm_smd_send_sleep_set(get_msg_id(s->buf), type, id); + + s->valid = false; + count++; + + /* + * RPM acks need to be handled here if we have sent 24 + * messages such that we do not overrun SMD buffer. Since + * we expect only sleep sets at this point (RPM PC would be + * disallowed if we had pending active requests), we need not + * process these sleep set acks. + */ + if (count >= MAX_WAIT_ON_ACK) { + pr_err("Error: more than %d requests are buffered\n", + MAX_WAIT_ON_ACK); + return -ENOSPC; + } + } + return 0; +} + +static void msm_rpm_notify_sleep_chain(char *buf, + struct msm_rpm_kvp_data *kvp) +{ + struct msm_rpm_notifier_data notif; + + notif.rsc_type = get_rsc_type(buf); + notif.rsc_id = get_req_len(buf); + notif.key = kvp->key; + notif.size = kvp->nbytes; + notif.value = kvp->value; + atomic_notifier_call_chain(&msm_rpm_sleep_notifier, 0, ¬if); +} + +static int msm_rpm_add_kvp_data_common(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + uint32_t i; + uint32_t data_size, msg_size; + + if (probe_status) + return probe_status; + + if (!handle || !data) { + pr_err("%s(): Invalid handle/data\n", __func__); + return -EINVAL; + } + + if (size < 0) + return -EINVAL; + + data_size = ALIGN(size, SZ_4); + msg_size = data_size + 8; + + for (i = 0; i < handle->write_idx; i++) { + if (handle->kvp[i].key != key) + continue; + if (handle->kvp[i].nbytes != data_size) { + kfree(handle->kvp[i].value); + handle->kvp[i].value = NULL; + } else { + if (!memcmp(handle->kvp[i].value, data, data_size)) + return 0; + } + break; + } + + if (i >= handle->num_elements) { + pr_err("Number of resources exceeds max allocated\n"); + return -ENOMEM; + } + + if (i == handle->write_idx) + handle->write_idx++; + + if (!handle->kvp[i].value) { + handle->kvp[i].value = kzalloc(data_size, GFP_NOIO); + + if (!handle->kvp[i].value) + return -ENOMEM; + } else { + /* + * We enter the else case, if a key already exists but the + * data doesn't match. In which case, we should zero the data + * out. + */ + memset(handle->kvp[i].value, 0, data_size); + } + + if (!handle->kvp[i].valid) + change_data_len(handle->client_buf, msg_size); + else + change_data_len(handle->client_buf, + (data_size - handle->kvp[i].nbytes)); + + handle->kvp[i].nbytes = data_size; + handle->kvp[i].key = key; + memcpy(handle->kvp[i].value, data, size); + handle->kvp[i].valid = true; + + return 0; + +} + +static struct msm_rpm_request *msm_rpm_create_request_common( + enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id, + int num_elements) +{ + struct msm_rpm_request *cdata; + uint32_t buf_size; + + if (probe_status) + return ERR_PTR(probe_status); + + cdata = kzalloc(sizeof(struct msm_rpm_request), GFP_NOIO); + + if (!cdata) + goto cdata_alloc_fail; + + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + buf_size = sizeof(struct rpm_message_header_v0); + else + buf_size = sizeof(struct rpm_message_header_v1); + + cdata->client_buf = kzalloc(buf_size, GFP_NOIO); + + if (!cdata->client_buf) + goto client_buf_alloc_fail; + + set_set_type(cdata->client_buf, set); + set_rsc_type(cdata->client_buf, rsc_type); + set_rsc_id(cdata->client_buf, rsc_id); + + cdata->num_elements = num_elements; + cdata->write_idx = 0; + + cdata->kvp = kcalloc(num_elements, sizeof(struct msm_rpm_kvp_data), + GFP_NOIO); + + if (!cdata->kvp) { + pr_warn("%s(): Cannot allocate memory for key value data\n", + __func__); + goto kvp_alloc_fail; + } + + cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_NOIO); + + if (!cdata->buf) + goto buf_alloc_fail; + + cdata->numbytes = DEFAULT_BUFFER_SIZE; + return cdata; + +buf_alloc_fail: + kfree(cdata->kvp); +kvp_alloc_fail: + kfree(cdata->client_buf); +client_buf_alloc_fail: + kfree(cdata); +cdata_alloc_fail: + return NULL; + +} + +void msm_rpm_free_request(struct msm_rpm_request *handle) +{ + int i; + + if (!handle) + return; + for (i = 0; i < handle->num_elements; i++) + kfree(handle->kvp[i].value); + kfree(handle->kvp); + kfree(handle->client_buf); + kfree(handle->buf); + kfree(handle); +} +EXPORT_SYMBOL(msm_rpm_free_request); + +struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return msm_rpm_create_request_common(set, rsc_type, rsc_id, + num_elements); +} +EXPORT_SYMBOL(msm_rpm_create_request); + +int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size); + +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data); + +int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size); + +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq); + +bool msm_rpm_waiting_for_ack(void) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + ret = list_empty(&msm_rpm_wait_list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + + return !ret; +} + +static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id) +{ + struct list_head *ptr; + struct msm_rpm_wait_data *elem = NULL; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each(ptr, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem && (elem->msg_id == msg_id)) + break; + elem = NULL; + } + + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + return elem; +} + +static uint32_t msm_rpm_get_next_msg_id(void) +{ + uint32_t id; + + /* + * A message id of 0 is used by the driver to indicate a error + * condition. The RPM driver uses a id of 1 to indicate unsent data + * when the data sent over hasn't been modified. This isn't a error + * scenario and wait for ack returns a success when the message id is 1. + */ + + do { + id = atomic_inc_return(&msm_rpm_msg_id); + } while ((id == 0) || (id == 1) || msm_rpm_get_entry_from_msg_id(id)); + + return id; +} + +static int msm_rpm_add_wait_list(uint32_t msg_id, bool delete_on_ack) +{ + unsigned long flags; + struct msm_rpm_wait_data *data = + kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC); + + if (!data) + return -ENOMEM; + + init_completion(&data->ack); + data->ack_recd = false; + data->msg_id = msg_id; + data->errno = INIT_ERROR; + data->delete_on_ack = delete_on_ack; + spin_lock_irqsave(&msm_rpm_list_lock, flags); + if (delete_on_ack) + list_add_tail(&data->list, &msm_rpm_wait_list); + else + list_add(&data->list, &msm_rpm_wait_list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + + return 0; +} + +static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem) +{ + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + list_del(&elem->list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + kfree(elem); +} + +static void msm_rpm_process_ack(uint32_t msg_id, int errno) +{ + struct list_head *ptr, *next; + struct msm_rpm_wait_data *elem = NULL; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each_safe(ptr, next, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem->msg_id == msg_id) { + elem->errno = errno; + elem->ack_recd = true; + complete(&elem->ack); + if (elem->delete_on_ack) { + list_del(&elem->list); + kfree(elem); + } + break; + } + } + /* + * Special case where the sleep driver doesn't + * wait for ACKs. This would decrease the latency involved with + * entering RPM assisted power collapse. + */ + + if (!elem) + trace_rpm_smd_ack_recvd(0, msg_id, 0xDEADBEEF); + + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); +} + +struct msm_rpm_kvp_packet { + uint32_t id; + uint32_t len; + uint32_t val; +}; + +static void msm_rpm_log_request(struct msm_rpm_request *cdata) +{ + char buf[DEBUG_PRINT_BUFFER_SIZE]; + size_t buflen = DEBUG_PRINT_BUFFER_SIZE; + char name[5]; + u32 value; + uint32_t i; + int j, prev_valid; + int valid_count = 0; + int pos = 0; + uint32_t res_type, rsc_id; + + name[4] = 0; + + for (i = 0; i < cdata->write_idx; i++) + if (cdata->kvp[i].valid) + valid_count++; + + pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO); + if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID) + pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ", + get_msg_id(cdata->client_buf)); + pos += scnprintf(buf + pos, buflen - pos, "s=%s", + (get_set_type(cdata->client_buf) == + MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp")); + + res_type = get_rsc_type(cdata->client_buf); + rsc_id = get_rsc_id(cdata->client_buf); + if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) + && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) { + /* Both pretty and raw formatting */ + memcpy(name, &res_type, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, + ", rsc_type=0x%08X (%s), rsc_id=%u; ", + res_type, name, rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, + "[key=0x%08X (%s), value=%s", + cdata->kvp[i].key, name, + (cdata->kvp[i].nbytes ? "0x" : "null")); + + for (j = 0; j < cdata->kvp[i].nbytes; j++) + pos += scnprintf(buf + pos, buflen - pos, + "%02X ", + cdata->kvp[i].value[j]); + + if (cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, buflen - pos, "("); + + for (j = 0; j < cdata->kvp[i].nbytes; j += 4) { + value = 0; + memcpy(&value, &cdata->kvp[i].value[j], + min_t(uint32_t, sizeof(uint32_t), + cdata->kvp[i].nbytes - j)); + pos += scnprintf(buf + pos, buflen - pos, "%u", + value); + if (j + 4 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + if (cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, buflen - pos, ")"); + pos += scnprintf(buf + pos, buflen - pos, "]"); + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) { + /* Pretty formatting only */ + memcpy(name, &res_type, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name, + rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, "%s=%s", + name, (cdata->kvp[i].nbytes ? "" : "null")); + + for (j = 0; j < cdata->kvp[i].nbytes; j += 4) { + value = 0; + memcpy(&value, &cdata->kvp[i].value[j], + min_t(uint32_t, sizeof(uint32_t), + cdata->kvp[i].nbytes - j)); + pos += scnprintf(buf + pos, buflen - pos, "%u", + value); + + if (j + 4 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } else { + /* Raw formatting only */ + pos += scnprintf(buf + pos, buflen - pos, + ", rsc_type=0x%08X, rsc_id=%u; ", res_type, rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + pos += scnprintf(buf + pos, buflen - pos, + "[key=0x%08X, value=%s", + cdata->kvp[i].key, + (cdata->kvp[i].nbytes ? "0x" : "null")); + for (j = 0; j < cdata->kvp[i].nbytes; j++) { + pos += scnprintf(buf + pos, buflen - pos, + "%02X", + cdata->kvp[i].value[j]); + if (j + 1 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + pos += scnprintf(buf + pos, buflen - pos, "]"); + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } + + pos += scnprintf(buf + pos, buflen - pos, "\n"); + pr_info("request info %s\n", buf); +} + +static int msm_rpm_send_smd_buffer(char *buf, uint32_t size) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags); + ret = rpmsg_send(rpm->rpm_channel, buf, size); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags); + return ret; +} + +static int msm_rpm_send_data(struct msm_rpm_request *cdata, + int msg_type, bool noack) +{ + uint8_t *tmpbuff; + int ret; + uint32_t i; + uint32_t msg_size; + int msg_hdr_sz, req_hdr_sz; + uint32_t data_len = get_data_len(cdata->client_buf); + uint32_t set = get_set_type(cdata->client_buf); + uint32_t msg_id; + + if (probe_status) { + pr_err("probe failed\n"); + return probe_status; + } + if (!data_len) { + pr_err("no data len\n"); + return 1; + } + + msg_hdr_sz = rpm_msg_fmt_ver ? sizeof(struct rpm_message_header_v1) : + sizeof(struct rpm_message_header_v0); + + req_hdr_sz = RPM_HDR_SIZE; + set_msg_type(cdata->client_buf, msg_type); + + set_req_len(cdata->client_buf, data_len + msg_hdr_sz - req_hdr_sz); + msg_size = get_req_len(cdata->client_buf) + req_hdr_sz; + + /* populate data_len */ + if (msg_size > cdata->numbytes) { + kfree(cdata->buf); + cdata->numbytes = msg_size; + cdata->buf = kzalloc(msg_size, GFP_NOIO); + } + + if (!cdata->buf) { + pr_err("Failed malloc\n"); + return 0; + } + + tmpbuff = cdata->buf; + + tmpbuff += msg_hdr_sz; + for (i = 0; (i < cdata->write_idx); i++) { + /* Sanity check */ + WARN_ON((tmpbuff - cdata->buf) > cdata->numbytes); + + if (!cdata->kvp[i].valid) + continue; + + memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes); + tmpbuff += cdata->kvp[i].nbytes; + + if (set == MSM_RPM_CTX_SLEEP_SET) + msm_rpm_notify_sleep_chain(cdata->client_buf, + &cdata->kvp[i]); + + } + + memcpy(cdata->buf, cdata->client_buf, msg_hdr_sz); + if ((set == MSM_RPM_CTX_SLEEP_SET) && + !msm_rpm_smd_buffer_request(cdata, msg_size, GFP_NOIO)) { + return 1; + } + + msg_id = msm_rpm_get_next_msg_id(); + /* Set the version bit for new protocol */ + set_msg_ver(cdata->buf, rpm_msg_fmt_ver); + set_msg_id(cdata->buf, msg_id); + set_msg_id(cdata->client_buf, msg_id); + + if (msm_rpm_debug_mask + & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW)) + msm_rpm_log_request(cdata); + + if (standalone) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + + set_data_len(cdata->client_buf, 0); + ret = msg_id; + return ret; + } + + msm_rpm_add_wait_list(msg_id, noack); + + ret = msm_rpm_send_smd_buffer(&cdata->buf[0], msg_size); + + if (!ret) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + set_data_len(cdata->client_buf, 0); + ret = msg_id; + trace_rpm_smd_send_active_set(msg_id, + get_rsc_type(cdata->client_buf), + get_rsc_id(cdata->client_buf)); + } else if (ret < 0) { + struct msm_rpm_wait_data *rc; + + ret = 0; + pr_err("Failed to write data msg_size:%d ret:%d msg_id:%d\n", + msg_size, ret, msg_id); + rc = msm_rpm_get_entry_from_msg_id(msg_id); + if (rc) + msm_rpm_free_list_entry(rc); + } + return ret; +} + +static int _msm_rpm_send_request(struct msm_rpm_request *handle, bool noack) +{ + int ret; + static DEFINE_MUTEX(send_mtx); + + mutex_lock(&send_mtx); + ret = msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, noack); + mutex_unlock(&send_mtx); + + return ret; +} + +int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) +{ + return _msm_rpm_send_request(handle, false); +} +EXPORT_SYMBOL(msm_rpm_send_request_noirq); + +int msm_rpm_send_request(struct msm_rpm_request *handle) +{ + return _msm_rpm_send_request(handle, false); +} +EXPORT_SYMBOL(msm_rpm_send_request); + +void *msm_rpm_send_request_noack(struct msm_rpm_request *handle) +{ + int ret; + + ret = _msm_rpm_send_request(handle, true); + + return ret < 0 ? ERR_PTR(ret) : NULL; +} +EXPORT_SYMBOL(msm_rpm_send_request_noack); + +int msm_rpm_wait_for_ack(uint32_t msg_id) +{ + struct msm_rpm_wait_data *elem; + int rc = 0; + + if (!msg_id) { + pr_err("Invalid msg id\n"); + return -ENOMEM; + } + + if (msg_id == 1) + return rc; + + if (standalone) + return rc; + + elem = msm_rpm_get_entry_from_msg_id(msg_id); + if (!elem) + return rc; + + wait_for_completion(&elem->ack); + trace_rpm_smd_ack_recvd(0, msg_id, 0xDEADFEED); + + rc = elem->errno; + msm_rpm_free_list_entry(elem); + + return rc; +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack); + +int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) +{ + return msm_rpm_wait_for_ack(msg_id); +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq); + +void *msm_rpm_send_message_noack(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request_common(set, rsc_type, rsc_id, nelems); + + if (IS_ERR(req)) + return req; + + if (!req) + return ERR_PTR(ENOMEM); + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = PTR_ERR(msm_rpm_send_request_noack(req)); +bail: + msm_rpm_free_request(req); + return rc < 0 ? ERR_PTR(rc) : NULL; +} +EXPORT_SYMBOL(msm_rpm_send_message_noack); + +int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request(set, rsc_type, rsc_id, nelems); + + if (IS_ERR(req)) + return PTR_ERR(req); + + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req)); +bail: + msm_rpm_free_request(req); + return rc; +} +EXPORT_SYMBOL(msm_rpm_send_message); + +int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + + return msm_rpm_send_message(set, rsc_type, rsc_id, kvp, nelems); +} +EXPORT_SYMBOL(msm_rpm_send_message_noirq); + +static int smd_mask_receive_interrupt(bool mask, + const struct cpumask *cpumask) +{ + struct irq_chip *irq_chip; + struct irq_data *irq_data; + + irq_data = irq_get_irq_data(rpm->irq); + if (!irq_data) + return -ENODEV; + + irq_chip = irq_data->chip; + if (!irq_chip) + return -ENODEV; + + if (mask) { + irq_chip->irq_mask(irq_data); + if (cpumask) + irq_set_affinity(rpm->irq, cpumask); + } else { + irq_chip->irq_unmask(irq_data); + } + + return 0; +} + +/** + * During power collapse, the rpm driver disables the SMD interrupts to make + * sure that the interrupt doesn't wakes us from sleep. + */ +int msm_rpm_enter_sleep(bool print, const struct cpumask *cpumask) +{ + int ret = 0; + + if (standalone) + return 0; + + + ret = smd_mask_receive_interrupt(true, cpumask); + if (!ret) { + ret = msm_rpm_flush_requests(print); + if (ret) + smd_mask_receive_interrupt(false, NULL); + } + return ret; +} +EXPORT_SYMBOL(msm_rpm_enter_sleep); + +/** + * When the system resumes from power collapse, the SMD interrupt disabled by + * enter function has to reenabled to continue processing SMD message. + */ +void msm_rpm_exit_sleep(void) +{ + + if (standalone) + return; + + smd_mask_receive_interrupt(false, NULL); +} +EXPORT_SYMBOL(msm_rpm_exit_sleep); + +static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, void *ptr, + int size, void *priv, u32 addr) +{ + uint32_t msg_id; + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {0}; + struct msm_rpm_wait_data *elem; + static DEFINE_SPINLOCK(rx_notify_lock); + unsigned long flags; + + if (!size) + return -EINVAL; + + WARN_ON(size > MAX_ERR_BUFFER_SIZE); + + spin_lock_irqsave(&rx_notify_lock, flags); + memcpy(buf, ptr, size); + msg_id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + elem = msm_rpm_get_entry_from_msg_id(msg_id); + + /* + * It is applicable for sleep set requests + * Sleep set requests are not added to the + * wait queue list. Without this check we + * run into NULL pointer deferrence issue. + */ + if (!elem) { + spin_unlock_irqrestore(&rx_notify_lock, flags); + return 0; + } + + msm_rpm_process_ack(msg_id, errno); + spin_unlock_irqrestore(&rx_notify_lock, flags); + + return 0; +} + +static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) +{ + char *key = NULL; + struct device_node *p; + int ret = 0; + int irq; + void __iomem *reg_base; + uint32_t version = V0_PROTOCOL_VERSION; /* set to default v0 format */ + + p = of_find_compatible_node(NULL, NULL, "qcom,rpm-smd"); + if (!p) { + pr_err("Unable to find rpm-smd\n"); + probe_status = -ENODEV; + goto fail; + } + + key = "rpm-standalone"; + standalone = of_property_read_bool(p, key); + if (standalone) { + probe_status = ret; + goto skip_init; + } + + reg_base = of_iomap(p, 0); + if (reg_base) { + version = readq_relaxed(reg_base); + iounmap(reg_base); + } + + if (version == V1_PROTOCOL_VERSION) + rpm_msg_fmt_ver = RPM_MSG_V1_FMT; + + pr_info("RPM-SMD running version %d\n", rpm_msg_fmt_ver); + + irq = of_irq_get(p, 0); + if (!irq) { + pr_err("Unable to get rpm-smd interrupt number\n"); + probe_status = -ENODEV; + goto fail; + } + + rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); + if (!rpm) { + probe_status = -ENOMEM; + goto fail; + } + + rpm->dev = &rpdev->dev; + rpm->rpm_channel = rpdev->ept; + dev_set_drvdata(&rpdev->dev, rpm); + priv_rpm = *rpm; + rpm->irq = irq; + + mutex_init(&rpm->lock); + init_completion(&rpm->ack); + spin_lock_init(&msm_rpm_data.smd_lock_write); + spin_lock_init(&msm_rpm_data.smd_lock_read); + +skip_init: + probe_status = of_platform_populate(p, NULL, NULL, &rpdev->dev); + + if (standalone) + pr_info("RPM running in standalone mode\n"); +fail: + return probe_status; +} + +static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) +{ + of_platform_depopulate(&rpdev->dev); +} + +static struct rpmsg_device_id rpmsg_driver_rpm_id_table[] = { + { .name = "rpm_requests" }, + { }, +}; + +static struct rpmsg_driver qcom_smd_rpm_driver = { + .probe = qcom_smd_rpm_probe, + .remove = qcom_smd_rpm_remove, + .callback = qcom_smd_rpm_callback, + .id_table = rpmsg_driver_rpm_id_table, + .drv = { + .name = "qcom_rpm_smd", + .owner = THIS_MODULE, + }, +}; + +int __init msm_rpm_driver_init(void) +{ + unsigned int ret = 0; + + ret = register_rpmsg_driver(&qcom_smd_rpm_driver); + if (ret) + pr_err("register_rpmsg_driver: failed with err %d\n", ret); + + return ret; +} +postcore_initcall_sync(msm_rpm_driver_init); diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 424f6d67da2a..742bd56d19ec 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -87,6 +87,9 @@ obj-$(CONFIG_QTI_DDR_STATS_LOG) += ddr_stats.o obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o obj-$(CONFIG_QCOM_HYP_CORE_CTL) += hyp_core_ctl.o obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o +ifdef CONFIG_DEBUG_FS +obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o +endif obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o obj-$(CONFIG_ICNSS) += icnss.o obj-$(CONFIG_ICNSS_QMI) += icnss_qmi.o wlan_firmware_service_v01.o diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c new file mode 100644 index 000000000000..baa64782d29d --- /dev/null +++ b/drivers/soc/qcom/rpm-smd-debug.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "rpm-smd-debug: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MSG_BUFFER 350 +#define MAX_KEY_VALUE_PAIRS 20 + +static struct dentry *rpm_debugfs_dir; + +static u32 string_to_uint(const u8 *str) +{ + int i, len; + u32 output = 0; + + len = strnlen(str, sizeof(u32)); + for (i = 0; i < len; i++) + output |= str[i] << (i * 8); + + return output; +} + +static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, + size_t count, loff_t *position) +{ + char buf[MAX_MSG_BUFFER], rsc_type_str[6] = {}, rpm_set[8] = {}, + key_str[6] = {}; + int i, pos = -1, set = -1, nelems = -1; + char *cmp; + uint32_t rsc_type = 0, rsc_id = 0, key = 0, data = 0; + struct msm_rpm_request *req; + + count = min(count, sizeof(buf) - 1); + if (copy_from_user(&buf, user_buffer, count)) + return -EFAULT; + buf[count] = '\0'; + cmp = strstrip(buf); + + if (sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str, + &rsc_id, &nelems, &pos) != 4) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + + if (strlen(rpm_set) > 6 || strlen(rsc_type_str) > 4) { + pr_err("Invalid value of set or resource type\n"); + goto err; + } + + if (!strcmp(rpm_set, "active")) + set = 0; + else if (!strcmp(rpm_set, "sleep")) + set = 1; + + rsc_type = string_to_uint(rsc_type_str); + + if (set < 0 || nelems < 0) { + pr_err("Invalid value of set or nelems\n"); + goto err; + } + if (nelems > MAX_KEY_VALUE_PAIRS) { + pr_err("Exceeded max no of key-value entries\n"); + goto err; + } + + req = msm_rpm_create_request(set, rsc_type, rsc_id, nelems); + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + cmp += pos; + if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err_request; + } + + if (strlen(key_str) > 4) { + pr_err("Key value cannot be more than 4 charecters\n"); + goto err_request; + } + key = string_to_uint(key_str); + if (!key) { + pr_err("Key values entered incorrectly\n"); + goto err_request; + } + + cmp += pos; + if (sscanf(cmp, "%u %n", &data, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err_request; + } + + if (msm_rpm_add_kvp_data(req, key, + (void *)&data, sizeof(data))) + goto err_request; + } + + if (msm_rpm_wait_for_ack(msm_rpm_send_request(req))) + pr_err("Sending the RPM message failed\n"); + +err_request: + msm_rpm_free_request(req); +err: + return count; +} + +static const struct file_operations rsc_ops = { + .write = rsc_ops_write, +}; + +static int __init rpm_smd_debugfs_init(void) +{ + rpm_debugfs_dir = debugfs_create_dir("rpm_send_msg", NULL); + if (!rpm_debugfs_dir) + return -ENOMEM; + + if (!debugfs_create_file("message", 0200, rpm_debugfs_dir, NULL, + &rsc_ops)) + return -ENOMEM; + + return 0; +} +late_initcall(rpm_smd_debugfs_init); + +static void __exit rpm_smd_debugfs_exit(void) +{ + debugfs_remove_recursive(rpm_debugfs_dir); +} +module_exit(rpm_smd_debugfs_exit); + +MODULE_DESCRIPTION("RPM SMD Debug Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/qcom/rpm-notifier.h b/include/soc/qcom/rpm-notifier.h new file mode 100644 index 000000000000..76af11e38235 --- /dev/null +++ b/include/soc/qcom/rpm-notifier.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_NOTIF_H +#define __ARCH_ARM_MACH_MSM_RPM_NOTIF_H + +struct msm_rpm_notifier_data { + uint32_t rsc_type; + uint32_t rsc_id; + uint32_t key; + uint32_t size; + uint8_t *value; +}; +/** + * msm_rpm_register_notifier - Register for sleep set notifications + * + * @nb - notifier block to register + * + * return 0 on success, errno on failure. + */ +int msm_rpm_register_notifier(struct notifier_block *nb); + +/** + * msm_rpm_unregister_notifier - Unregister previously registered notifications + * + * @nb - notifier block to unregister + * + * return 0 on success, errno on failure. + */ +int msm_rpm_unregister_notifier(struct notifier_block *nb); + +/** + * msm_rpm_enter_sleep - Notify RPM driver to prepare for entering sleep + * + * @bool - flag to enable print contents of sleep buffer. + * @cpumask - cpumask of next wakeup cpu + * + * return 0 on success errno on failure. + */ +int msm_rpm_enter_sleep(bool print, const struct cpumask *cpumask); + +/** + * msm_rpm_exit_sleep - Notify RPM driver about resuming from power collapse + */ +void msm_rpm_exit_sleep(void); + +/** + * msm_rpm_waiting_for_ack - Indicate if there is RPM message + * pending acknowledgment. + * returns true for pending messages and false otherwise + */ +bool msm_rpm_waiting_for_ack(void); + +#endif /*__ARCH_ARM_MACH_MSM_RPM_NOTIF_H */ diff --git a/include/soc/qcom/rpm-smd.h b/include/soc/qcom/rpm-smd.h new file mode 100644 index 000000000000..fc77bd4e048e --- /dev/null +++ b/include/soc/qcom/rpm-smd.h @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + */ + +#include + +#ifndef __ARCH_ARM_MACH_MSM_RPM_SMD_H +#define __ARCH_ARM_MACH_MSM_RPM_SMD_H + +/** + * enum msm_rpm_set - RPM enumerations for sleep/active set + * %MSM_RPM_CTX_SET_0: Set resource parameters for active mode. + * %MSM_RPM_CTX_SET_SLEEP: Set resource parameters for sleep. + */ +enum msm_rpm_set { + MSM_RPM_CTX_ACTIVE_SET, + MSM_RPM_CTX_SLEEP_SET, +}; + +struct msm_rpm_request; + +struct msm_rpm_kvp { + uint32_t key; + uint32_t length; + uint8_t *data; +}; +#ifdef CONFIG_MSM_RPM_SMD +/** + * msm_rpm_request() - Creates a parent element to identify the + * resource on the RPM, that stores the KVPs for different fields modified + * for a hardware resource + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @num_elements: number of KVPs pairs associated with the resource + * + * returns pointer to a msm_rpm_request on success, NULL on error + */ +struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements); + +/** + * msm_rpm_request_noirq() - Creates a parent element to identify the + * resource on the RPM, that stores the KVPs for different fields modified + * for a hardware resource. This function is similar to msm_rpm_create_request + * except that it has to be called with interrupts masked. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @num_elements: number of KVPs pairs associated with the resource + * + * returns pointer to a msm_rpm_request on success, NULL on error + */ +struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements); + +/** + * msm_rpm_add_kvp_data() - Adds a Key value pair to a existing RPM resource. + * + * @handle: RPM resource handle to which the data should be appended + * @key: unsigned integer identify the parameter modified + * @data: byte array that contains the value corresponding to key. + * @size: size of data in bytes. + * + * returns 0 on success or errno + */ +int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size); + +/** + * msm_rpm_add_kvp_data_noirq() - Adds a Key value pair to a existing RPM + * resource. This function is similar to msm_rpm_add_kvp_data except that it + * has to be called with interrupts masked. + * + * @handle: RPM resource handle to which the data should be appended + * @key: unsigned integer identify the parameter modified + * @data: byte array that contains the value corresponding to key. + * @size: size of data in bytes. + * + * returns 0 on success or errno + */ +int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size); + +/** msm_rpm_free_request() - clean up the RPM request handle created with + * msm_rpm_create_request + * + * @handle: RPM resource handle to be cleared. + */ + +void msm_rpm_free_request(struct msm_rpm_request *handle); + +/** + * msm_rpm_send_request() - Send the RPM messages using SMD. The function + * assigns a message id before sending the data out to the RPM. RPM hardware + * uses the message id to acknowledge the messages. + * + * @handle: pointer to the msm_rpm_request for the resource being modified. + * + * returns non-zero message id on success and zero on a failed transaction. + * The drivers use message id to wait for ACK from RPM. + */ +int msm_rpm_send_request(struct msm_rpm_request *handle); + +/** + * msm_rpm_send_request_noack() - Send the RPM messages using SMD. The function + * assigns a message id before sending the data out to the RPM. RPM hardware + * uses the message id to acknowledge the messages, but this API does not wait + * on the ACK for this message id and it does not add the message id to the wait + * list. + * + * @handle: pointer to the msm_rpm_request for the resource being modified. + * + * returns NULL on success and PTR_ERR on a failed transaction. + */ +void *msm_rpm_send_request_noack(struct msm_rpm_request *handle); + +/** + * msm_rpm_send_request_noirq() - Send the RPM messages using SMD. The + * function assigns a message id before sending the data out to the RPM. + * RPM hardware uses the message id to acknowledge the messages. This function + * is similar to msm_rpm_send_request except that it has to be called with + * interrupts masked. + * + * @handle: pointer to the msm_rpm_request for the resource being modified. + * + * returns non-zero message id on success and zero on a failed transaction. + * The drivers use message id to wait for ACK from RPM. + */ +int msm_rpm_send_request_noirq(struct msm_rpm_request *handle); + +/** + * msm_rpm_wait_for_ack() - A blocking call that waits for acknowledgment of + * a message from RPM. + * + * @msg_id: the return from msm_rpm_send_requests + * + * returns 0 on success or errno + */ +int msm_rpm_wait_for_ack(uint32_t msg_id); + +/** + * msm_rpm_wait_for_ack_noirq() - A blocking call that waits for acknowledgment + * of a message from RPM. This function is similar to msm_rpm_wait_for_ack + * except that it has to be called with interrupts masked. + * + * @msg_id: the return from msm_rpm_send_request + * + * returns 0 on success or errno + */ +int msm_rpm_wait_for_ack_noirq(uint32_t msg_id); + +/** + * msm_rpm_send_message() -Wrapper function for clients to send data given an + * array of key value pairs. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @kvp: array of KVP data. + * @nelem: number of KVPs pairs associated with the message. + * + * returns 0 on success and errno on failure. + */ +int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); + +/** + * msm_rpm_send_message_noack() -Wrapper function for clients to send data + * given an array of key value pairs without waiting for ack. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @kvp: array of KVP data. + * @nelem: number of KVPs pairs associated with the message. + * + * returns NULL on success and PTR_ERR(errno) on failure. + */ +void *msm_rpm_send_message_noack(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); + +/** + * msm_rpm_send_message_noirq() -Wrapper function for clients to send data + * given an array of key value pairs. This function is similar to the + * msm_rpm_send_message() except that it has to be called with interrupts + * disabled. Clients should choose the irq version when possible for system + * performance. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @kvp: array of KVP data. + * @nelem: number of KVPs pairs associated with the message. + * + * returns 0 on success and errno on failure. + */ +int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); + +/** + * msm_rpm_driver_init() - Initialization function that registers for a + * rpm platform driver. + * + * returns 0 on success. + */ +int __init msm_rpm_driver_init(void); + +#else + +static inline struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return NULL; +} + +static inline struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return NULL; + +} +static inline uint32_t msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int count) +{ + return 0; +} +static inline uint32_t msm_rpm_add_kvp_data_noirq( + struct msm_rpm_request *handle, uint32_t key, + const uint8_t *data, int count) +{ + return 0; +} + +static inline void msm_rpm_free_request(struct msm_rpm_request *handle) +{ +} + +static inline int msm_rpm_send_request(struct msm_rpm_request *handle) +{ + return 0; +} + +static inline int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) +{ + return 0; + +} + +static inline void *msm_rpm_send_request_noack(struct msm_rpm_request *handle) +{ + return NULL; +} + +static inline int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + return 0; +} + +static inline int msm_rpm_send_message_noirq(enum msm_rpm_set set, + uint32_t rsc_type, uint32_t rsc_id, struct msm_rpm_kvp *kvp, + int nelems) +{ + return 0; +} + +static inline void *msm_rpm_send_message_noack(enum msm_rpm_set set, + uint32_t rsc_type, uint32_t rsc_id, struct msm_rpm_kvp *kvp, + int nelems) +{ + return NULL; +} + +static inline int msm_rpm_wait_for_ack(uint32_t msg_id) +{ + return 0; + +} +static inline int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) +{ + return 0; +} + +static inline int __init msm_rpm_driver_init(void) +{ + return 0; +} +#endif + +#endif /*__ARCH_ARM_MACH_MSM_RPM_SMD_H*/ diff --git a/include/trace/events/trace_rpm_smd.h b/include/trace/events/trace_rpm_smd.h new file mode 100644 index 000000000000..242c4fa7d04c --- /dev/null +++ b/include/trace/events/trace_rpm_smd.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rpm_smd + +#if !defined(_TRACE_RPM_SMD_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_RPM_SMD_H + +#include + +TRACE_EVENT(rpm_smd_ack_recvd, + + TP_PROTO(unsigned int irq, unsigned int msg_id, int errno), + + TP_ARGS(irq, msg_id, errno), + + TP_STRUCT__entry( + __field(int, irq) + __field(int, msg_id) + __field(int, errno) + ), + + TP_fast_assign( + __entry->irq = irq; + __entry->msg_id = msg_id; + __entry->errno = errno; + ), + + TP_printk("ctx:%s msg_id:%d errno:%08x", + __entry->irq ? "noslp" : "sleep", + __entry->msg_id, + __entry->errno) +); + +TRACE_EVENT(rpm_smd_interrupt_notify, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy), + + TP_STRUCT__entry( + __field(char *, dummy) + ), + + TP_fast_assign( + __entry->dummy = dummy; + ), + + TP_printk("%s", __entry->dummy) +); + +DECLARE_EVENT_CLASS(rpm_send_msg, + + TP_PROTO(unsigned int msg_id, unsigned int rsc_type, + unsigned int rsc_id), + + TP_ARGS(msg_id, rsc_type, rsc_id), + + TP_STRUCT__entry( + __field(u32, msg_id) + __field(u32, rsc_type) + __field(u32, rsc_id) + __array(char, name, 5) + ), + + TP_fast_assign( + __entry->msg_id = msg_id; + __entry->name[4] = 0; + __entry->rsc_type = rsc_type; + __entry->rsc_id = rsc_id; + memcpy(__entry->name, &rsc_type, sizeof(uint32_t)); + + ), + + TP_printk("msg_id:%d, rsc_type:0x%08x(%s), rsc_id:0x%08x", + __entry->msg_id, + __entry->rsc_type, __entry->name, + __entry->rsc_id) +); + +DEFINE_EVENT(rpm_send_msg, rpm_smd_sleep_set, + TP_PROTO(unsigned int msg_id, unsigned int rsc_type, + unsigned int rsc_id), + TP_ARGS(msg_id, rsc_type, rsc_id) +); + +DEFINE_EVENT(rpm_send_msg, rpm_smd_send_sleep_set, + TP_PROTO(unsigned int msg_id, unsigned int rsc_type, + unsigned int rsc_id), + TP_ARGS(msg_id, rsc_type, rsc_id) +); + +DEFINE_EVENT(rpm_send_msg, rpm_smd_send_active_set, + TP_PROTO(unsigned int msg_id, unsigned int rsc_type, + unsigned int rsc_id), + TP_ARGS(msg_id, rsc_type, rsc_id) +); + +#endif +#define TRACE_INCLUDE_FILE trace_rpm_smd +#include