diff options
-rw-r--r-- | Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt | 14 | ||||
-rw-r--r-- | drivers/clk/msm/Kconfig | 11 | ||||
-rw-r--r-- | drivers/clk/msm/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/msm/virtclk-front-8996.c | 407 | ||||
-rw-r--r-- | drivers/clk/msm/virtclk-front.c | 460 | ||||
-rw-r--r-- | include/linux/clk/msm-clock-generic.h | 14 |
6 files changed, 908 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt b/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt new file mode 100644 index 000000000000..a863c802120a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt @@ -0,0 +1,14 @@ +QTI Virtual Clock Frontend Binding +------------------------------------------------ + +Required properties : +- compatible : shall contain: + "qcom,virtclk-frontend-8996" + +- #clock-cells : shall contain 1 + +Example: + virtclk-frontend@0 { + compatible = "qcom,virtclk-frontend-8996"; + #clock-cells = <1>; + }; diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig index 3829f6aec124..a7501f5d446f 100644 --- a/drivers/clk/msm/Kconfig +++ b/drivers/clk/msm/Kconfig @@ -16,4 +16,15 @@ config MSM_CLK_CONTROLLER_V2 Generate clock data structures from definitions found in device tree. +config MSM_VIRTCLK_FRONTEND + bool + +config MSM_VIRTCLK_FRONTEND_8996 + tristate "QTI msm8996 virtual clock frontend driver" + depends on COMMON_CLK_MSM && MSM_HAB + select MSM_VIRTCLK_FRONTEND + ---help--- + This is the virtual clock frontend driver for the QTI msm8996 + virtual platform. + source "drivers/clk/msm/mdss/Kconfig" diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile index 27e07eb12205..5f50890704da 100644 --- a/drivers/clk/msm/Makefile +++ b/drivers/clk/msm/Makefile @@ -29,3 +29,6 @@ endif obj-$(CONFIG_COMMON_CLK_MSM) += gdsc.o obj-$(CONFIG_COMMON_CLK_MSM) += mdss/ + +obj-$(CONFIG_MSM_VIRTCLK_FRONTEND) += virtclk-front.o +obj-$(CONFIG_MSM_VIRTCLK_FRONTEND_8996) += virtclk-front-8996.o diff --git a/drivers/clk/msm/virtclk-front-8996.c b/drivers/clk/msm/virtclk-front-8996.c new file mode 100644 index 000000000000..84cd55bd85bf --- /dev/null +++ b/drivers/clk/msm/virtclk-front-8996.c @@ -0,0 +1,407 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk/msm-clock-generic.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <dt-bindings/clock/msm-clocks-8996.h> + +static struct virtclk_front gcc_blsp1_ahb_clk = { + .c = { + .dbg_name = "gcc_blsp1_ahb_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_ahb_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup1_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup1_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup1_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup1_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart1_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart1_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart1_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup2_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup2_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup2_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup2_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart2_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart2_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart2_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup3_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup3_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup3_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup3_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart3_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart3_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart3_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup4_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup4_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup4_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup4_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart4_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart4_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart4_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup5_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup5_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup5_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup5_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart5_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart5_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart5_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup6_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup6_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_qup6_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_qup6_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp1_uart6_apps_clk = { + .c = { + .dbg_name = "gcc_blsp1_uart6_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp1_uart6_apps_clk.c), + }, +}; + + +static struct virtclk_front gcc_blsp2_ahb_clk = { + .c = { + .dbg_name = "gcc_blsp2_ahb_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_ahb_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup1_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup1_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup1_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup1_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup1_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup1_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart1_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart1_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart1_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup2_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup2_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup2_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup2_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup2_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup2_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart2_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart2_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart2_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup3_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup3_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup3_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup3_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup3_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup3_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart3_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart3_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart3_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup4_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup4_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup4_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup4_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup4_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup4_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart4_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart4_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart4_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup5_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup5_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup5_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup5_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup5_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup5_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart5_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart5_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart5_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup6_spi_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup6_spi_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup6_spi_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_qup6_i2c_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_qup6_i2c_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_qup6_i2c_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_blsp2_uart6_apps_clk = { + .c = { + .dbg_name = "gcc_blsp2_uart6_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_blsp2_uart6_apps_clk.c), + }, +}; + +static struct virtclk_front gcc_sdcc2_ahb_clk = { + .c = { + .dbg_name = "gcc_sdcc2_ahb_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_sdcc2_ahb_clk.c), + }, +}; + +static struct virtclk_front gcc_sdcc2_apps_clk = { + .c = { + .dbg_name = "gcc_sdcc2_apps_clk", + .ops = &virtclk_front_ops, + CLK_INIT(gcc_sdcc2_apps_clk.c), + }, +}; + +static struct clk_lookup msm_clocks_8996[] = { + CLK_LIST(gcc_blsp1_ahb_clk), + CLK_LIST(gcc_blsp1_qup1_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup1_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart1_apps_clk), + CLK_LIST(gcc_blsp1_qup2_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup2_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart2_apps_clk), + CLK_LIST(gcc_blsp1_qup3_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup3_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart3_apps_clk), + CLK_LIST(gcc_blsp1_qup4_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup4_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart4_apps_clk), + CLK_LIST(gcc_blsp1_qup5_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup5_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart5_apps_clk), + CLK_LIST(gcc_blsp1_qup6_spi_apps_clk), + CLK_LIST(gcc_blsp1_qup6_i2c_apps_clk), + CLK_LIST(gcc_blsp1_uart6_apps_clk), + CLK_LIST(gcc_blsp2_ahb_clk), + CLK_LIST(gcc_blsp2_qup1_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup1_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart1_apps_clk), + CLK_LIST(gcc_blsp2_qup2_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup2_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart2_apps_clk), + CLK_LIST(gcc_blsp2_qup3_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup3_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart3_apps_clk), + CLK_LIST(gcc_blsp2_qup4_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup4_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart4_apps_clk), + CLK_LIST(gcc_blsp2_qup5_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup5_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart5_apps_clk), + CLK_LIST(gcc_blsp2_qup6_spi_apps_clk), + CLK_LIST(gcc_blsp2_qup6_i2c_apps_clk), + CLK_LIST(gcc_blsp2_uart6_apps_clk), + CLK_LIST(gcc_sdcc2_ahb_clk), + CLK_LIST(gcc_sdcc2_apps_clk), +}; + +static const struct of_device_id msm8996_virtclk_front_match_table[] = { + { .compatible = "qcom,virtclk-frontend-8996" }, + {} +}; + +static int msm8996_virtclk_front_probe(struct platform_device *pdev) +{ + return msm_virtclk_front_probe(pdev, msm_clocks_8996, + ARRAY_SIZE(msm_clocks_8996)); +} + +static struct platform_driver msm8996_virtclk_front_driver = { + .probe = msm8996_virtclk_front_probe, + .driver = { + .name = "virtclk-front-8996", + .of_match_table = msm8996_virtclk_front_match_table, + .owner = THIS_MODULE, + }, +}; + +int __init msm8996_virtclk_front_init(void) +{ + return platform_driver_register(&msm8996_virtclk_front_driver); +} +arch_initcall(msm8996_virtclk_front_init); diff --git a/drivers/clk/msm/virtclk-front.c b/drivers/clk/msm/virtclk-front.c new file mode 100644 index 000000000000..08c7e5aaa7f4 --- /dev/null +++ b/drivers/clk/msm/virtclk-front.c @@ -0,0 +1,460 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <linux/clk/msm-clock-generic.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/habmm.h> +#include <soc/qcom/msm-clock-controller.h> + +struct virtclk_front_data { + int handle; + struct rt_mutex lock; +}; + +enum virtclk_cmd { + CLK_MSG_GETID = 1, + CLK_MSG_ENABLE, + CLK_MSG_DISABLE, + CLK_MSG_RESET, + CLK_MSG_SETFREQ, + CLK_MSG_GETFREQ, + CLK_MSG_MAX +}; + +struct clk_msg_header { + u32 cmd; + u32 len; + u32 clk_id; +} __packed; + +struct clk_msg_rsp { + struct clk_msg_header header; + u32 rsp; +} __packed; + +struct clk_msg_setfreq { + struct clk_msg_header header; + u32 freq; +} __packed; + +struct clk_msg_getid { + struct clk_msg_header header; + char name[32]; +} __packed; + +struct clk_msg_getfreq { + struct clk_msg_rsp rsp; + u32 freq; +} __packed; + +static struct virtclk_front_data virtclk_front_ctx; + +static inline struct virtclk_front *to_virtclk_front(struct clk *clk) +{ + return container_of(clk, struct virtclk_front, c); +} + +static int virtclk_front_init_iface(void) +{ + int ret = 0; + int handle; + + rt_mutex_lock(&virtclk_front_ctx.lock); + + if (virtclk_front_ctx.handle) + goto out; + + ret = habmm_socket_open(&handle, MM_CLK_VM1, 0, 0); + if (ret) { + pr_err("open habmm socket failed (%d)\n", ret); + goto out; + } + + virtclk_front_ctx.handle = handle; + +out: + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static int virtclk_front_get_id(struct clk *clk) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_getid msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + if (v->id) + return ret; + + msg.header.cmd = CLK_MSG_GETID; + msg.header.len = sizeof(msg); + strlcpy(msg.name, clk->dbg_name, sizeof(msg.name)); + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, + UINT_MAX, 0); + if (ret) { + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s: error response (%d)\n", clk->dbg_name, rsp.rsp); + ret = -EIO; + } else + v->id = rsp.header.clk_id; + + rt_mutex_unlock(&virtclk_front_ctx.lock); + + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static int virtclk_front_prepare(struct clk *clk) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_header msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + ret = virtclk_front_init_iface(); + if (ret) + return ret; + + ret = virtclk_front_get_id(clk); + if (ret) + return ret; + + msg.clk_id = v->id; + msg.cmd = CLK_MSG_ENABLE; + msg.len = sizeof(struct clk_msg_header); + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, UINT_MAX, 0); + if (ret) { + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s: error response (%d)\n", clk->dbg_name, rsp.rsp); + ret = -EIO; + } + + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static void virtclk_front_unprepare(struct clk *clk) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_header msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + ret = virtclk_front_init_iface(); + if (ret) + return; + + ret = virtclk_front_get_id(clk); + if (ret) + return; + + msg.clk_id = v->id; + msg.cmd = CLK_MSG_DISABLE; + msg.len = sizeof(struct clk_msg_header); + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, UINT_MAX, 0); + if (ret) { + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp) + pr_err("%s: error response (%d)\n", clk->dbg_name, rsp.rsp); + + rt_mutex_unlock(&virtclk_front_ctx.lock); + return; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); +} + +static int virtclk_front_reset(struct clk *clk, enum clk_reset_action action) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_header msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + ret = virtclk_front_init_iface(); + if (ret) + return ret; + + ret = virtclk_front_get_id(clk); + if (ret) + return ret; + + msg.clk_id = v->id; + msg.cmd = CLK_MSG_RESET; + msg.len = sizeof(struct clk_msg_header); + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, UINT_MAX, 0); + if (ret) { + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s: error response (%d)\n", clk->dbg_name, rsp.rsp); + ret = -EIO; + } + + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static int virtclk_front_set_rate(struct clk *clk, unsigned long rate) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_setfreq msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + ret = virtclk_front_init_iface(); + if (ret) + return ret; + + ret = virtclk_front_get_id(clk); + if (ret) + return ret; + + msg.header.clk_id = v->id; + msg.header.cmd = CLK_MSG_SETFREQ; + msg.header.len = sizeof(msg); + msg.freq = (u32)rate; + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, UINT_MAX, 0); + if (ret) { + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s (%luHz): error response (%d)\n", clk->dbg_name, + rate, rsp.rsp); + ret = -EIO; + } + + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static int virtclk_front_set_max_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static int virtclk_front_is_enabled(struct clk *clk) +{ + struct virtclk_front *v = to_virtclk_front(clk); + + return !!v->c.prepare_count; +} + +static int virtclk_front_set_flags(struct clk *clk, unsigned flags) +{ + return 0; +} + +static unsigned long virtclk_front_get_rate(struct clk *clk) +{ + struct virtclk_front *v = to_virtclk_front(clk); + struct clk_msg_header msg; + struct clk_msg_getfreq rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + ret = virtclk_front_init_iface(); + if (ret) + return 0; + + ret = virtclk_front_get_id(clk); + if (ret) + return 0; + + msg.clk_id = v->id; + msg.cmd = CLK_MSG_GETFREQ; + msg.len = sizeof(msg); + + rt_mutex_lock(&virtclk_front_ctx.lock); + + handle = virtclk_front_ctx.handle; + ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); + if (ret) { + ret = 0; + pr_err("%s: habmm socket send failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + ret = habmm_socket_recv(handle, &rsp, &rsp_size, UINT_MAX, 0); + if (ret) { + ret = 0; + pr_err("%s: habmm socket receive failed (%d)\n", clk->dbg_name, + ret); + goto err_out; + } + + if (rsp.rsp.rsp) { + pr_err("%s: error response (%d)\n", clk->dbg_name, rsp.rsp.rsp); + ret = 0; + } else + ret = rsp.freq; + + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static long virtclk_front_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +struct clk_ops virtclk_front_ops = { + .prepare = virtclk_front_prepare, + .unprepare = virtclk_front_unprepare, + .reset = virtclk_front_reset, + .set_rate = virtclk_front_set_rate, + .set_max_rate = virtclk_front_set_max_rate, + .is_enabled = virtclk_front_is_enabled, + .set_flags = virtclk_front_set_flags, + .get_rate = virtclk_front_get_rate, + .round_rate = virtclk_front_round_rate, +}; + +int msm_virtclk_front_probe(struct platform_device *pdev, + struct clk_lookup *table, + size_t size) +{ + int ret; + + ret = of_msm_clock_register(pdev->dev.of_node, table, size); + if (ret) + return ret; + + rt_mutex_init(&virtclk_front_ctx.lock); + + dev_info(&pdev->dev, "Registered virtual clock provider.\n"); + + return ret; +} +EXPORT_SYMBOL(msm_virtclk_front_probe); diff --git a/include/linux/clk/msm-clock-generic.h b/include/linux/clk/msm-clock-generic.h index d7186a363a3f..fe019d366d0b 100644 --- a/include/linux/clk/msm-clock-generic.h +++ b/include/linux/clk/msm-clock-generic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -307,4 +307,16 @@ static inline struct mux_div_clk *to_mux_div_clk(struct clk *clk) extern struct clk_ops clk_ops_mux_div_clk; +/* ==================== Virtual clock ==================== */ +struct virtclk_front { + int id; + struct clk c; +}; + +extern struct clk_ops virtclk_front_ops; + +int msm_virtclk_front_probe(struct platform_device *pdev, + struct clk_lookup *table, + size_t size); + #endif |