[PATCH v3 5/5] misc: fastrpc: Add wakelock management support

From: Ekansh Gupta
Date: Tue Nov 21 2023 - 07:42:42 EST


CPU can go into suspend mode at anytime. Users might have a
requirement to keep the CPU awake until they get a response for
their remote call to DSP. Wakelock management can be used to
achieve this requirement.

Co-developed-by: Anandu Krishnan E <quic_anane@xxxxxxxxxxx>
Signed-off-by: Anandu Krishnan E <quic_anane@xxxxxxxxxxx>
Signed-off-by: Ekansh Gupta <quic_ekangupt@xxxxxxxxxxx>
---
drivers/misc/fastrpc.c | 104 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)

diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 97895fce1228..dcda287cd899 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -120,6 +120,11 @@
/* CPU feature information to DSP */
#define FASTRPC_CPUINFO_DEFAULT (0)
#define FASTRPC_CPUINFO_EARLY_WAKEUP (1)
+
+/* Maximum PM timeout that can be voted through fastrpc */
+#define FASTRPC_MAX_PM_TIMEOUT_MS 50
+#define FASTRPC_NON_SECURE_WAKE_SOURCE_CLIENT_NAME "fastrpc-non_secure"
+#define FASTRPC_SECURE_WAKE_SOURCE_CLIENT_NAME "fastrpc-secure"
/* Process status notifications from DSP will be sent with this unique context */
#define FASTRPC_NOTIF_CTX_RESERVED 0xABCDABCD

@@ -387,6 +392,10 @@ struct fastrpc_channel_ctx {
struct fastrpc_device *fdevice;
struct fastrpc_buf *remote_heap;
struct list_head invoke_interrupted_mmaps;
+ /* Secure subsystems like ADSP/SLPI will use secure client */
+ struct wakeup_source *wake_source_secure;
+ /* Non-secure subsystem like CDSP will use regular client */
+ struct wakeup_source *wake_source;
bool secure;
bool unsigned_support;
bool cpuinfo_status;
@@ -417,6 +426,8 @@ struct fastrpc_user {
u32 profile;
/* Threads poll for specified timeout and fall back to glink wait */
u32 poll_timeout;
+ u32 ws_timeout;
+ u32 wake_enable;
int tgid;
int pd;
bool is_secure_dev;
@@ -438,11 +449,21 @@ struct fastrpc_ctrl_smmu {
u32 sharedcb; /* Set to SMMU share context bank */
};

+struct fastrpc_ctrl_wakelock {
+ u32 enable; /* wakelock control enable */
+};
+
+struct fastrpc_ctrl_pm {
+ u32 timeout; /* timeout(in ms) for PM to keep system awake */
+};
+
struct fastrpc_internal_control {
u32 req;
union {
struct fastrpc_ctrl_latency lp;
struct fastrpc_ctrl_smmu smmu;
+ struct fastrpc_ctrl_wakelock wp;
+ struct fastrpc_ctrl_pm pm;
};
};

@@ -980,6 +1001,43 @@ static void fastrpc_session_free(struct fastrpc_channel_ctx *cctx,
spin_unlock_irqrestore(&cctx->lock, flags);
}

+static void fastrpc_pm_awake(struct fastrpc_user *fl,
+ u32 is_secure_channel)
+{
+ struct fastrpc_channel_ctx *cctx = fl->cctx;
+ struct wakeup_source *wake_source = NULL;
+
+ /*
+ * Vote with PM to abort any suspend in progress and
+ * keep system awake for specified timeout
+ */
+ if (is_secure_channel)
+ wake_source = cctx->wake_source_secure;
+ else
+ wake_source = cctx->wake_source;
+
+ if (wake_source)
+ pm_wakeup_ws_event(wake_source, fl->ws_timeout, true);
+}
+
+static void fastrpc_pm_relax(struct fastrpc_user *fl,
+ u32 is_secure_channel)
+{
+ struct fastrpc_channel_ctx *cctx = fl->cctx;
+ struct wakeup_source *wake_source = NULL;
+
+ if (!fl->wake_enable)
+ return;
+
+ if (is_secure_channel)
+ wake_source = cctx->wake_source_secure;
+ else
+ wake_source = cctx->wake_source;
+
+ if (wake_source)
+ __pm_relax(wake_source);
+}
+
static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
u64 len, u32 attr, struct fastrpc_map **ppmap)
{
@@ -1971,6 +2029,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
fastrpc_buf_free(buf);
}

+ fastrpc_pm_relax(fl, cctx->secure);
fastrpc_session_free(cctx, fl->sctx);
fastrpc_channel_ctx_put(cctx);

@@ -2251,6 +2310,24 @@ static int fastrpc_internal_control(struct fastrpc_user *fl,
case FASTRPC_CONTROL_SMMU:
fl->sharedcb = cp->smmu.sharedcb;
break;
+ case FASTRPC_CONTROL_WAKELOCK:
+ if (!fl->is_secure_dev) {
+ dev_err(&fl->cctx->rpdev->dev,
+ "PM voting not allowed for non-secure device node");
+ err = -EPERM;
+ return err;
+ }
+ fl->wake_enable = cp->wp.enable;
+ break;
+ case FASTRPC_CONTROL_PM:
+ if (!fl->wake_enable)
+ return -EACCES;
+ if (cp->pm.timeout > FASTRPC_MAX_PM_TIMEOUT_MS)
+ fl->ws_timeout = FASTRPC_MAX_PM_TIMEOUT_MS;
+ else
+ fl->ws_timeout = cp->pm.timeout;
+ fastrpc_pm_awake(fl, fl->cctx->secure);
+ break;
case FASTRPC_CONTROL_RPC_POLL:
err = fastrpc_manage_poll_mode(fl, cp->lp.enable, cp->lp.latency);
break;
@@ -2942,6 +3019,21 @@ static int fastrpc_device_register(struct device *dev, struct fastrpc_channel_ct
return err;
}

+static void fastrpc_register_wakeup_source(struct device *dev,
+ const char *client_name, struct wakeup_source **device_wake_source)
+{
+ struct wakeup_source *wake_source = NULL;
+
+ wake_source = wakeup_source_register(dev, client_name);
+ if (IS_ERR_OR_NULL(wake_source)) {
+ dev_err(dev, "wakeup_source_register failed for dev %s, client %s with err %ld\n",
+ dev_name(dev), client_name, PTR_ERR(wake_source));
+ return;
+ }
+
+ *device_wake_source = wake_source;
+}
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
@@ -3023,6 +3115,13 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
goto fdev_error;
}

+ if (data->fdevice)
+ fastrpc_register_wakeup_source(data->fdevice->miscdev.this_device,
+ FASTRPC_NON_SECURE_WAKE_SOURCE_CLIENT_NAME, &data->wake_source);
+ if (data->secure_fdevice)
+ fastrpc_register_wakeup_source(data->secure_fdevice->miscdev.this_device,
+ FASTRPC_SECURE_WAKE_SOURCE_CLIENT_NAME, &data->wake_source_secure);
+
kref_init(&data->refcount);

dev_set_drvdata(&rpdev->dev, data);
@@ -3098,6 +3197,11 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
if (cctx->remote_heap)
fastrpc_buf_free(cctx->remote_heap);

+ if (cctx->wake_source)
+ wakeup_source_unregister(cctx->wake_source);
+ if (cctx->wake_source_secure)
+ wakeup_source_unregister(cctx->wake_source_secure);
+
of_platform_depopulate(&rpdev->dev);

fastrpc_channel_ctx_put(cctx);
--
2.17.1