2019-02-17 07:16:41

by xiang xiao

[permalink] [raw]
Subject: [PATCH] rpmsg: add clock API redirection driver

From: Yanlin Zhu <[email protected]>

which could redirect clk API from remote to the kernel

Signed-off-by: Yanlin Zhu <[email protected]>
---
drivers/rpmsg/Kconfig | 10 ++
drivers/rpmsg/Makefile | 1 +
drivers/rpmsg/rpmsg_clk.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 295 insertions(+)
create mode 100644 drivers/rpmsg/rpmsg_clk.c

diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 13ead55..ed04cb9 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -15,6 +15,16 @@ config RPMSG_CHAR
in /dev. They make it possible for user-space programs to send and
receive rpmsg packets.

+config RPMSG_CLK
+ tristate "RPMSG clock API redirection"
+ depends on COMMON_CLK
+ depends on RPMSG
+ help
+ Say Y here to redirect clock API from the remote processor.
+ With this driver, the remote processor could:
+ 1.Reuse the clock driver in the kernel side, or
+ 2.Form a hybrid(kernel plus RTOS) clock tree.
+
config RPMSG_SYSLOG
tristate "RPMSG syslog redirection"
depends on RPMSG
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index bfd22df..0d777b1 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RPMSG) += rpmsg_core.o
obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
+obj-$(CONFIG_RPMSG_CLK) += rpmsg_clk.o
obj-$(CONFIG_RPMSG_SYSLOG) += rpmsg_syslog.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
diff --git a/drivers/rpmsg/rpmsg_clk.c b/drivers/rpmsg/rpmsg_clk.c
new file mode 100644
index 0000000..0ec0241
--- /dev/null
+++ b/drivers/rpmsg/rpmsg_clk.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Pinecone Inc.
+ *
+ * redirect clk API from remote to the kernel.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+
+#define RPMSG_CLK_ENABLE 0
+#define RPMSG_CLK_DISABLE 1
+#define RPMSG_CLK_SETRATE 2
+#define RPMSG_CLK_SETPHASE 3
+#define RPMSG_CLK_GETPHASE 4
+#define RPMSG_CLK_GETRATE 5
+#define RPMSG_CLK_ROUNDRATE 6
+#define RPMSG_CLK_ISENABLED 7
+
+struct rpmsg_clk_header {
+ u32 command;
+ u32 response;
+ s64 result;
+ u64 cookie;
+} __packed;
+
+struct rpmsg_clk_enable {
+ struct rpmsg_clk_header header;
+ char name[0];
+} __packed;
+
+#define rpmsg_clk_disable rpmsg_clk_enable
+#define rpmsg_clk_isenabled rpmsg_clk_enable
+
+struct rpmsg_clk_setrate {
+ struct rpmsg_clk_header header;
+ u64 rate;
+ char name[0];
+} __packed;
+
+#define rpmsg_clk_getrate rpmsg_clk_enable
+#define rpmsg_clk_roundrate rpmsg_clk_setrate
+
+struct rpmsg_clk_setphase {
+ struct rpmsg_clk_header header;
+ u32 degrees;
+ char name[0];
+} __packed;
+
+#define rpmsg_clk_getphase rpmsg_clk_enable
+
+struct rpmsg_clk_res {
+ struct clk *clk;
+ atomic_t count;
+};
+
+static void rpmsg_clk_release(struct device *dev, void *res)
+{
+ struct rpmsg_clk_res *clkres = res;
+ int count = atomic_read(&clkres->count);
+
+ while (count-- > 0)
+ clk_disable_unprepare(clkres->clk);
+
+ clk_put(clkres->clk);
+}
+
+static int rpmsg_clk_match(struct device *dev, void *res, void *data)
+{
+ struct rpmsg_clk_res *clkres = res;
+
+ return !strcmp(__clk_get_name(clkres->clk), data);
+}
+
+static struct rpmsg_clk_res *
+rpmsg_clk_get_res(struct rpmsg_device *rpdev, const char *name)
+{
+ struct rpmsg_clk_res *clkres;
+ struct clk *clk;
+
+ clkres = devres_find(&rpdev->dev, rpmsg_clk_release,
+ rpmsg_clk_match, (void *)name);
+ if (clkres)
+ return clkres;
+
+ clkres = devres_alloc(rpmsg_clk_release, sizeof(*clkres), GFP_KERNEL);
+ if (!clkres)
+ return ERR_PTR(-ENOMEM);
+
+ clk = clk_get(&rpdev->dev, name);
+ if (IS_ERR(clk)) {
+ devres_free(clkres);
+ return ERR_CAST(clk);
+ }
+
+ clkres->clk = clk;
+ devres_add(&rpdev->dev, clkres);
+
+ return clkres;
+}
+
+static int rpmsg_clk_enable_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_enable *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres)) {
+ msg->header.result = clk_prepare_enable(clkres->clk);
+ if (msg->header.result >= 0)
+ atomic_inc(&clkres->count);
+ } else {
+ msg->header.result = PTR_ERR(clkres);
+ }
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_disable_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_disable *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres)) {
+ msg->header.result = 0;
+ if (atomic_dec_return(&clkres->count) >= 0)
+ clk_disable_unprepare(clkres->clk);
+ else
+ atomic_inc(&clkres->count);
+ } else {
+ msg->header.result = PTR_ERR(clkres);
+ }
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_getrate_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_getrate *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = clk_get_rate(clkres->clk);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_roundrate_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_roundrate *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = clk_round_rate(clkres->clk, msg->rate);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_setrate_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_setrate *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = clk_set_rate(clkres->clk, msg->rate);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_setphase_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_setphase *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = clk_set_phase(clkres->clk, msg->degrees);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_getphase_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_getphase *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = clk_get_phase(clkres->clk);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_clk_isenabled_handler(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_isenabled *msg = data;
+ struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
+
+ if (!IS_ERR(clkres))
+ msg->header.result = __clk_is_enabled(clkres->clk);
+ else
+ msg->header.result = PTR_ERR(clkres);
+
+ return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static const rpmsg_rx_cb_t rpmsg_clk_handler[] = {
+ [RPMSG_CLK_ENABLE] = rpmsg_clk_enable_handler,
+ [RPMSG_CLK_DISABLE] = rpmsg_clk_disable_handler,
+ [RPMSG_CLK_SETRATE] = rpmsg_clk_setrate_handler,
+ [RPMSG_CLK_SETPHASE] = rpmsg_clk_setphase_handler,
+ [RPMSG_CLK_GETPHASE] = rpmsg_clk_getphase_handler,
+ [RPMSG_CLK_GETRATE] = rpmsg_clk_getrate_handler,
+ [RPMSG_CLK_ROUNDRATE] = rpmsg_clk_roundrate_handler,
+ [RPMSG_CLK_ISENABLED] = rpmsg_clk_isenabled_handler,
+};
+
+static int rpmsg_clk_callback(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct rpmsg_clk_header *hdr = data;
+ u32 cmd = hdr->command;
+ int ret = -EINVAL;
+
+ if (cmd < ARRAY_SIZE(rpmsg_clk_handler)) {
+ hdr->response = 1;
+ ret = rpmsg_clk_handler[cmd](rpdev, data, len, priv, src);
+ } else {
+ dev_err(&rpdev->dev, "invalid command %u\n", cmd);
+ }
+
+ return ret;
+}
+
+static int rpmsg_clk_probe(struct rpmsg_device *rpdev)
+{
+ return 0;
+}
+
+static void rpmsg_clk_remove(struct rpmsg_device *rpdev)
+{
+}
+
+static const struct rpmsg_device_id rpmsg_clk_id_table[] = {
+ { .name = "rpmsg-clk" },
+ { }
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_clk_id_table);
+
+static struct rpmsg_driver rpmsg_clk_driver = {
+ .drv = {
+ .name = "rpmsg_clk",
+ .owner = THIS_MODULE,
+ },
+
+ .id_table = rpmsg_clk_id_table,
+ .probe = rpmsg_clk_probe,
+ .callback = rpmsg_clk_callback,
+ .remove = rpmsg_clk_remove,
+};
+
+module_rpmsg_driver(rpmsg_clk_driver);
+
+MODULE_ALIAS("rpmsg:rpmsg_clk");
+MODULE_AUTHOR("Yanlin Zhu <[email protected]>");
+MODULE_DESCRIPTION("rpmsg clock API redirection driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4



2019-02-22 08:20:29

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH] rpmsg: add clock API redirection driver

Hello Xiang

On 2/16/19 8:37 PM, Xiang Xiao wrote:
> From: Yanlin Zhu <[email protected]>
>
> which could redirect clk API from remote to the kernel
>
> Signed-off-by: Yanlin Zhu <[email protected]>
> ---
> drivers/rpmsg/Kconfig | 10 ++
> drivers/rpmsg/Makefile | 1 +
> drivers/rpmsg/rpmsg_clk.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 295 insertions(+)
> create mode 100644 drivers/rpmsg/rpmsg_clk.c
>
> diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
> index 13ead55..ed04cb9 100644
> --- a/drivers/rpmsg/Kconfig
> +++ b/drivers/rpmsg/Kconfig
> @@ -15,6 +15,16 @@ config RPMSG_CHAR
> in /dev. They make it possible for user-space programs to send and
> receive rpmsg packets.
>
> +config RPMSG_CLK
> + tristate "RPMSG clock API redirection"
> + depends on COMMON_CLK
> + depends on RPMSG
> + help
> + Say Y here to redirect clock API from the remote processor.
> + With this driver, the remote processor could:
> + 1.Reuse the clock driver in the kernel side, or
> + 2.Form a hybrid(kernel plus RTOS) clock tree.
> +
> config RPMSG_SYSLOG
> tristate "RPMSG syslog redirection"
> depends on RPMSG
> diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
> index bfd22df..0d777b1 100644
> --- a/drivers/rpmsg/Makefile
> +++ b/drivers/rpmsg/Makefile
> @@ -1,6 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_RPMSG) += rpmsg_core.o
> obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
> +obj-$(CONFIG_RPMSG_CLK) += rpmsg_clk.o
> obj-$(CONFIG_RPMSG_SYSLOG) += rpmsg_syslog.o
> obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
> obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
> diff --git a/drivers/rpmsg/rpmsg_clk.c b/drivers/rpmsg/rpmsg_clk.c
> new file mode 100644
> index 0000000..0ec0241
> --- /dev/null
> +++ b/drivers/rpmsg/rpmsg_clk.c
> @@ -0,0 +1,284 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2016 Pinecone Inc.
> + *
> + * redirect clk API from remote to the kernel.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/rpmsg.h>
> +
> +#define RPMSG_CLK_ENABLE 0
> +#define RPMSG_CLK_DISABLE 1
> +#define RPMSG_CLK_SETRATE 2
> +#define RPMSG_CLK_SETPHASE 3
> +#define RPMSG_CLK_GETPHASE 4
> +#define RPMSG_CLK_GETRATE 5
> +#define RPMSG_CLK_ROUNDRATE 6
> +#define RPMSG_CLK_ISENABLED 7
> +
> +struct rpmsg_clk_header {
> + u32 command;
> + u32 response;
> + s64 result;
> + u64 cookie;
> +} __packed;
> +
> +struct rpmsg_clk_enable {
> + struct rpmsg_clk_header header;
> + char name[0];
> +} __packed;
> +
> +#define rpmsg_clk_disable rpmsg_clk_enable
> +#define rpmsg_clk_isenabled rpmsg_clk_enable
> +
> +struct rpmsg_clk_setrate {
> + struct rpmsg_clk_header header;
> + u64 rate;
> + char name[0];
> +} __packed;
> +
> +#define rpmsg_clk_getrate rpmsg_clk_enable
> +#define rpmsg_clk_roundrate rpmsg_clk_setrate
> +
> +struct rpmsg_clk_setphase {
> + struct rpmsg_clk_header header;
> + u32 degrees;
> + char name[0];
> +} __packed;
> +
> +#define rpmsg_clk_getphase rpmsg_clk_enable
> +
> +struct rpmsg_clk_res {
> + struct clk *clk;
> + atomic_t count;
> +};
> +
> +static void rpmsg_clk_release(struct device *dev, void *res)
> +{
> + struct rpmsg_clk_res *clkres = res;
> + int count = atomic_read(&clkres->count);
> +
> + while (count-- > 0)
> + clk_disable_unprepare(clkres->clk);
> +
> + clk_put(clkres->clk);
> +}
> +
> +static int rpmsg_clk_match(struct device *dev, void *res, void *data)
> +{
> + struct rpmsg_clk_res *clkres = res;
> +
> + return !strcmp(__clk_get_name(clkres->clk), data);
> +}
> +
> +static struct rpmsg_clk_res *
> +rpmsg_clk_get_res(struct rpmsg_device *rpdev, const char *name)
> +{
> + struct rpmsg_clk_res *clkres;
> + struct clk *clk;
> +
> + clkres = devres_find(&rpdev->dev, rpmsg_clk_release,
> + rpmsg_clk_match, (void *)name);
> + if (clkres)
> + return clkres;
> +
> + clkres = devres_alloc(rpmsg_clk_release, sizeof(*clkres), GFP_KERNEL);
> + if (!clkres)
> + return ERR_PTR(-ENOMEM);
> +
> + clk = clk_get(&rpdev->dev, name);
> + if (IS_ERR(clk)) {
> + devres_free(clkres);
> + return ERR_CAST(clk);
> + }
> +
> + clkres->clk = clk;
> + devres_add(&rpdev->dev, clkres);
> +
> + return clkres;
> +}
> +
> +static int rpmsg_clk_enable_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_enable *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres)) {
> + msg->header.result = clk_prepare_enable(clkres->clk);
> + if (msg->header.result >= 0)
> + atomic_inc(&clkres->count);
> + } else {
> + msg->header.result = PTR_ERR(clkres);
> + }
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_disable_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_disable *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres)) {
> + msg->header.result = 0;
> + if (atomic_dec_return(&clkres->count) >= 0)
> + clk_disable_unprepare(clkres->clk);
> + else
> + atomic_inc(&clkres->count);
> + } else {
> + msg->header.result = PTR_ERR(clkres);
> + }
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_getrate_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_getrate *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = clk_get_rate(clkres->clk);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_roundrate_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_roundrate *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = clk_round_rate(clkres->clk, msg->rate);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_setrate_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_setrate *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = clk_set_rate(clkres->clk, msg->rate);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_setphase_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_setphase *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = clk_set_phase(clkres->clk, msg->degrees);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_getphase_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_getphase *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = clk_get_phase(clkres->clk);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static int rpmsg_clk_isenabled_handler(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_isenabled *msg = data;
> + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> +
> + if (!IS_ERR(clkres))
> + msg->header.result = __clk_is_enabled(clkres->clk);
> + else
> + msg->header.result = PTR_ERR(clkres);
> +
> + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> +}
> +
> +static const rpmsg_rx_cb_t rpmsg_clk_handler[] = {
> + [RPMSG_CLK_ENABLE] = rpmsg_clk_enable_handler,
> + [RPMSG_CLK_DISABLE] = rpmsg_clk_disable_handler,
> + [RPMSG_CLK_SETRATE] = rpmsg_clk_setrate_handler,
> + [RPMSG_CLK_SETPHASE] = rpmsg_clk_setphase_handler,
> + [RPMSG_CLK_GETPHASE] = rpmsg_clk_getphase_handler,
> + [RPMSG_CLK_GETRATE] = rpmsg_clk_getrate_handler,
> + [RPMSG_CLK_ROUNDRATE] = rpmsg_clk_roundrate_handler,
> + [RPMSG_CLK_ISENABLED] = rpmsg_clk_isenabled_handler,
> +};
> +
> +static int rpmsg_clk_callback(struct rpmsg_device *rpdev,
> + void *data, int len, void *priv, u32 src)
> +{
> + struct rpmsg_clk_header *hdr = data;
> + u32 cmd = hdr->command;
> + int ret = -EINVAL;
> +
> + if (cmd < ARRAY_SIZE(rpmsg_clk_handler)) {
> + hdr->response = 1;
> + ret = rpmsg_clk_handler[cmd](rpdev, data, len, priv, src);
> + } else {
> + dev_err(&rpdev->dev, "invalid command %u\n", cmd);
> + }
> +
> + return ret;
> +}
> +
> +static int rpmsg_clk_probe(struct rpmsg_device *rpdev)
> +{
> + return 0;
> +}
> +
> +static void rpmsg_clk_remove(struct rpmsg_device *rpdev)
> +{
> +}
> +
> +static const struct rpmsg_device_id rpmsg_clk_id_table[] = {
> + { .name = "rpmsg-clk" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(rpmsg, rpmsg_clk_id_table);
> +
> +static struct rpmsg_driver rpmsg_clk_driver = {
> + .drv = {
> + .name = "rpmsg_clk",
> + .owner = THIS_MODULE,
> + },
> +
> + .id_table = rpmsg_clk_id_table,
> + .probe = rpmsg_clk_probe,
> + .callback = rpmsg_clk_callback,
> + .remove = rpmsg_clk_remove,
> +};
> +
> +module_rpmsg_driver(rpmsg_clk_driver);
> +
> +MODULE_ALIAS("rpmsg:rpmsg_clk");
> +MODULE_AUTHOR("Yanlin Zhu <[email protected]>");
> +MODULE_DESCRIPTION("rpmsg clock API redirection driver");
> +MODULE_LICENSE("GPL v2");
>

This is similar to the resource manager we proposed several months ago.
we proposed a generic solution for the clock but also the other
resources managed by Linux for the remote processor (regulators, GPIO,...)

Please have a look here
patch:
https://patchwork.kernel.org/project/linux-remoteproc/list/?submitter=92421
overview document:
http://openamp.github.io/docs/mca/remoteproc-resource-manager-overview.pdf


Regards
Arnaud

2019-02-22 08:37:32

by xiang xiao

[permalink] [raw]
Subject: Re: [PATCH] rpmsg: add clock API redirection driver

On Fri, Feb 22, 2019 at 4:19 PM Arnaud Pouliquen
<[email protected]> wrote:
>
> Hello Xiang
>
> On 2/16/19 8:37 PM, Xiang Xiao wrote:
> > From: Yanlin Zhu <[email protected]>
> >
> > which could redirect clk API from remote to the kernel
> >
> > Signed-off-by: Yanlin Zhu <[email protected]>
> > ---
> > drivers/rpmsg/Kconfig | 10 ++
> > drivers/rpmsg/Makefile | 1 +
> > drivers/rpmsg/rpmsg_clk.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 295 insertions(+)
> > create mode 100644 drivers/rpmsg/rpmsg_clk.c
> >
> > diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
> > index 13ead55..ed04cb9 100644
> > --- a/drivers/rpmsg/Kconfig
> > +++ b/drivers/rpmsg/Kconfig
> > @@ -15,6 +15,16 @@ config RPMSG_CHAR
> > in /dev. They make it possible for user-space programs to send and
> > receive rpmsg packets.
> >
> > +config RPMSG_CLK
> > + tristate "RPMSG clock API redirection"
> > + depends on COMMON_CLK
> > + depends on RPMSG
> > + help
> > + Say Y here to redirect clock API from the remote processor.
> > + With this driver, the remote processor could:
> > + 1.Reuse the clock driver in the kernel side, or
> > + 2.Form a hybrid(kernel plus RTOS) clock tree.
> > +
> > config RPMSG_SYSLOG
> > tristate "RPMSG syslog redirection"
> > depends on RPMSG
> > diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
> > index bfd22df..0d777b1 100644
> > --- a/drivers/rpmsg/Makefile
> > +++ b/drivers/rpmsg/Makefile
> > @@ -1,6 +1,7 @@
> > # SPDX-License-Identifier: GPL-2.0
> > obj-$(CONFIG_RPMSG) += rpmsg_core.o
> > obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
> > +obj-$(CONFIG_RPMSG_CLK) += rpmsg_clk.o
> > obj-$(CONFIG_RPMSG_SYSLOG) += rpmsg_syslog.o
> > obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
> > obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
> > diff --git a/drivers/rpmsg/rpmsg_clk.c b/drivers/rpmsg/rpmsg_clk.c
> > new file mode 100644
> > index 0000000..0ec0241
> > --- /dev/null
> > +++ b/drivers/rpmsg/rpmsg_clk.c
> > @@ -0,0 +1,284 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2016 Pinecone Inc.
> > + *
> > + * redirect clk API from remote to the kernel.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/module.h>
> > +#include <linux/rpmsg.h>
> > +
> > +#define RPMSG_CLK_ENABLE 0
> > +#define RPMSG_CLK_DISABLE 1
> > +#define RPMSG_CLK_SETRATE 2
> > +#define RPMSG_CLK_SETPHASE 3
> > +#define RPMSG_CLK_GETPHASE 4
> > +#define RPMSG_CLK_GETRATE 5
> > +#define RPMSG_CLK_ROUNDRATE 6
> > +#define RPMSG_CLK_ISENABLED 7
> > +
> > +struct rpmsg_clk_header {
> > + u32 command;
> > + u32 response;
> > + s64 result;
> > + u64 cookie;
> > +} __packed;
> > +
> > +struct rpmsg_clk_enable {
> > + struct rpmsg_clk_header header;
> > + char name[0];
> > +} __packed;
> > +
> > +#define rpmsg_clk_disable rpmsg_clk_enable
> > +#define rpmsg_clk_isenabled rpmsg_clk_enable
> > +
> > +struct rpmsg_clk_setrate {
> > + struct rpmsg_clk_header header;
> > + u64 rate;
> > + char name[0];
> > +} __packed;
> > +
> > +#define rpmsg_clk_getrate rpmsg_clk_enable
> > +#define rpmsg_clk_roundrate rpmsg_clk_setrate
> > +
> > +struct rpmsg_clk_setphase {
> > + struct rpmsg_clk_header header;
> > + u32 degrees;
> > + char name[0];
> > +} __packed;
> > +
> > +#define rpmsg_clk_getphase rpmsg_clk_enable
> > +
> > +struct rpmsg_clk_res {
> > + struct clk *clk;
> > + atomic_t count;
> > +};
> > +
> > +static void rpmsg_clk_release(struct device *dev, void *res)
> > +{
> > + struct rpmsg_clk_res *clkres = res;
> > + int count = atomic_read(&clkres->count);
> > +
> > + while (count-- > 0)
> > + clk_disable_unprepare(clkres->clk);
> > +
> > + clk_put(clkres->clk);
> > +}
> > +
> > +static int rpmsg_clk_match(struct device *dev, void *res, void *data)
> > +{
> > + struct rpmsg_clk_res *clkres = res;
> > +
> > + return !strcmp(__clk_get_name(clkres->clk), data);
> > +}
> > +
> > +static struct rpmsg_clk_res *
> > +rpmsg_clk_get_res(struct rpmsg_device *rpdev, const char *name)
> > +{
> > + struct rpmsg_clk_res *clkres;
> > + struct clk *clk;
> > +
> > + clkres = devres_find(&rpdev->dev, rpmsg_clk_release,
> > + rpmsg_clk_match, (void *)name);
> > + if (clkres)
> > + return clkres;
> > +
> > + clkres = devres_alloc(rpmsg_clk_release, sizeof(*clkres), GFP_KERNEL);
> > + if (!clkres)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + clk = clk_get(&rpdev->dev, name);
> > + if (IS_ERR(clk)) {
> > + devres_free(clkres);
> > + return ERR_CAST(clk);
> > + }
> > +
> > + clkres->clk = clk;
> > + devres_add(&rpdev->dev, clkres);
> > +
> > + return clkres;
> > +}
> > +
> > +static int rpmsg_clk_enable_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_enable *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres)) {
> > + msg->header.result = clk_prepare_enable(clkres->clk);
> > + if (msg->header.result >= 0)
> > + atomic_inc(&clkres->count);
> > + } else {
> > + msg->header.result = PTR_ERR(clkres);
> > + }
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_disable_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_disable *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres)) {
> > + msg->header.result = 0;
> > + if (atomic_dec_return(&clkres->count) >= 0)
> > + clk_disable_unprepare(clkres->clk);
> > + else
> > + atomic_inc(&clkres->count);
> > + } else {
> > + msg->header.result = PTR_ERR(clkres);
> > + }
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_getrate_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_getrate *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = clk_get_rate(clkres->clk);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_roundrate_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_roundrate *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = clk_round_rate(clkres->clk, msg->rate);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_setrate_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_setrate *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = clk_set_rate(clkres->clk, msg->rate);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_setphase_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_setphase *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = clk_set_phase(clkres->clk, msg->degrees);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_getphase_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_getphase *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = clk_get_phase(clkres->clk);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static int rpmsg_clk_isenabled_handler(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_isenabled *msg = data;
> > + struct rpmsg_clk_res *clkres = rpmsg_clk_get_res(rpdev, msg->name);
> > +
> > + if (!IS_ERR(clkres))
> > + msg->header.result = __clk_is_enabled(clkres->clk);
> > + else
> > + msg->header.result = PTR_ERR(clkres);
> > +
> > + return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
> > +}
> > +
> > +static const rpmsg_rx_cb_t rpmsg_clk_handler[] = {
> > + [RPMSG_CLK_ENABLE] = rpmsg_clk_enable_handler,
> > + [RPMSG_CLK_DISABLE] = rpmsg_clk_disable_handler,
> > + [RPMSG_CLK_SETRATE] = rpmsg_clk_setrate_handler,
> > + [RPMSG_CLK_SETPHASE] = rpmsg_clk_setphase_handler,
> > + [RPMSG_CLK_GETPHASE] = rpmsg_clk_getphase_handler,
> > + [RPMSG_CLK_GETRATE] = rpmsg_clk_getrate_handler,
> > + [RPMSG_CLK_ROUNDRATE] = rpmsg_clk_roundrate_handler,
> > + [RPMSG_CLK_ISENABLED] = rpmsg_clk_isenabled_handler,
> > +};
> > +
> > +static int rpmsg_clk_callback(struct rpmsg_device *rpdev,
> > + void *data, int len, void *priv, u32 src)
> > +{
> > + struct rpmsg_clk_header *hdr = data;
> > + u32 cmd = hdr->command;
> > + int ret = -EINVAL;
> > +
> > + if (cmd < ARRAY_SIZE(rpmsg_clk_handler)) {
> > + hdr->response = 1;
> > + ret = rpmsg_clk_handler[cmd](rpdev, data, len, priv, src);
> > + } else {
> > + dev_err(&rpdev->dev, "invalid command %u\n", cmd);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int rpmsg_clk_probe(struct rpmsg_device *rpdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static void rpmsg_clk_remove(struct rpmsg_device *rpdev)
> > +{
> > +}
> > +
> > +static const struct rpmsg_device_id rpmsg_clk_id_table[] = {
> > + { .name = "rpmsg-clk" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(rpmsg, rpmsg_clk_id_table);
> > +
> > +static struct rpmsg_driver rpmsg_clk_driver = {
> > + .drv = {
> > + .name = "rpmsg_clk",
> > + .owner = THIS_MODULE,
> > + },
> > +
> > + .id_table = rpmsg_clk_id_table,
> > + .probe = rpmsg_clk_probe,
> > + .callback = rpmsg_clk_callback,
> > + .remove = rpmsg_clk_remove,
> > +};
> > +
> > +module_rpmsg_driver(rpmsg_clk_driver);
> > +
> > +MODULE_ALIAS("rpmsg:rpmsg_clk");
> > +MODULE_AUTHOR("Yanlin Zhu <[email protected]>");
> > +MODULE_DESCRIPTION("rpmsg clock API redirection driver");
> > +MODULE_LICENSE("GPL v2");
> >
>
> This is similar to the resource manager we proposed several months ago.
> we proposed a generic solution for the clock but also the other
> resources managed by Linux for the remote processor (regulators, GPIO,...)
>
> Please have a look here
> patch:
> https://patchwork.kernel.org/project/linux-remoteproc/list/?submitter=92421
> overview document:
> http://openamp.github.io/docs/mca/remoteproc-resource-manager-overview.pdf
>
>

Great, I will take a look at in this weekend and send the feedback.

> Regards
> Arnaud