diff options
| -rw-r--r-- | Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt | 2 | ||||
| -rw-r--r-- | drivers/clk/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/msm/virt-reset-front.c | 192 | ||||
| -rw-r--r-- | drivers/clk/msm/virt-reset-front.h | 37 | ||||
| -rw-r--r-- | drivers/clk/msm/virtclk-front-8996.c | 23 | ||||
| -rw-r--r-- | drivers/clk/msm/virtclk-front.c | 58 | ||||
| -rw-r--r-- | drivers/clk/msm/virtclk-front.h | 65 |
7 files changed, 328 insertions, 50 deletions
diff --git a/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt b/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt index a863c802120a..b5d52635b2b2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt +++ b/Documentation/devicetree/bindings/clock/qcom,virtclk-front.txt @@ -6,9 +6,11 @@ Required properties : "qcom,virtclk-frontend-8996" - #clock-cells : shall contain 1 +- #reset-cells : shall contain 1 Example: virtclk-frontend@0 { compatible = "qcom,virtclk-frontend-8996"; #clock-cells = <1>; + #reset-cells = <1>; }; diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile index 5f50890704da..671d2dc041eb 100644 --- a/drivers/clk/msm/Makefile +++ b/drivers/clk/msm/Makefile @@ -31,4 +31,5 @@ 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) += virt-reset-front.o obj-$(CONFIG_MSM_VIRTCLK_FRONTEND_8996) += virtclk-front-8996.o diff --git a/drivers/clk/msm/virt-reset-front.c b/drivers/clk/msm/virt-reset-front.c new file mode 100644 index 000000000000..548e98cf0951 --- /dev/null +++ b/drivers/clk/msm/virt-reset-front.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018, 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/device.h> +#include <linux/delay.h> +#include <linux/reset-controller.h> +#include <linux/clk/msm-clk.h> +#include <linux/habmm.h> +#include "virt-reset-front.h" +#include "virtclk-front.h" + +static int virtrc_front_get_clk_id(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct virtrc_front *rst; + struct virt_reset_map *map; + struct clk_msg_getid msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + rst = to_virtrc_front(rcdev); + map = &rst->reset_map[id]; + msg.header.cmd = CLK_MSG_GETID; + msg.header.len = sizeof(msg); + strlcpy(msg.name, map->clk_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", map->clk_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", map->clk_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s: error response (%d)\n", map->clk_name, rsp.rsp); + ret = -EIO; + } else + map->clk_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 __virtrc_front_reset(struct reset_controller_dev *rcdev, + unsigned long id, enum clk_reset_action action) +{ + struct virtrc_front *rst; + struct virt_reset_map *map; + struct clk_msg_reset msg; + struct clk_msg_rsp rsp; + u32 rsp_size = sizeof(rsp); + int handle; + int ret = 0; + + rst = to_virtrc_front(rcdev); + map = &rst->reset_map[id]; + + ret = virtclk_front_init_iface(); + if (ret) + return ret; + + ret = virtrc_front_get_clk_id(rcdev, id); + if (ret) + return ret; + + msg.header.clk_id = map->clk_id; + msg.header.cmd = CLK_MSG_RESET; + msg.header.len = sizeof(struct clk_msg_header); + msg.reset = action; + + 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", map->clk_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", map->clk_name, + ret); + goto err_out; + } + + if (rsp.rsp) { + pr_err("%s: error response (%d)\n", map->clk_name, rsp.rsp); + ret = -EIO; + } + + rt_mutex_unlock(&virtclk_front_ctx.lock); + + pr_debug("%s(%lu): do %s\n", map->clk_name, id, + action == CLK_RESET_ASSERT ? "assert" : "deassert"); + + return ret; + +err_out: + habmm_socket_close(handle); + virtclk_front_ctx.handle = 0; + rt_mutex_unlock(&virtclk_front_ctx.lock); + return ret; +} + +static int virtrc_front_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret = 0; + + ret = __virtrc_front_reset(rcdev, id, CLK_RESET_ASSERT); + if (ret) + return ret; + + udelay(1); + + return __virtrc_front_reset(rcdev, id, CLK_RESET_DEASSERT); +} + +static int virtrc_front_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return __virtrc_front_reset(rcdev, id, CLK_RESET_ASSERT); +} + +static int virtrc_front_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return __virtrc_front_reset(rcdev, id, CLK_RESET_DEASSERT); +} + +struct reset_control_ops virtrc_front_ops = { + .reset = virtrc_front_reset, + .assert = virtrc_front_reset_assert, + .deassert = virtrc_front_reset_deassert, +}; + +int msm_virtrc_front_register(struct platform_device *pdev, + struct virt_reset_map *map, unsigned int num_resets) +{ + struct virtrc_front *reset; + int ret = 0; + + reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.ops = &virtrc_front_ops; + reset->rcdev.owner = pdev->dev.driver->owner; + reset->rcdev.nr_resets = num_resets; + reset->reset_map = map; + + ret = reset_controller_register(&reset->rcdev); + if (ret) + dev_err(&pdev->dev, "Failed to register with reset controller\n"); + + return ret; +} +EXPORT_SYMBOL(msm_virtrc_front_register); diff --git a/drivers/clk/msm/virt-reset-front.h b/drivers/clk/msm/virt-reset-front.h new file mode 100644 index 000000000000..0c2863a078a1 --- /dev/null +++ b/drivers/clk/msm/virt-reset-front.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018, 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. + */ + +#ifndef __VIRT_RESET_FRONT_H +#define __VIRT_RESET_FRONT_H + +#include <linux/platform_device.h> +#include <linux/reset-controller.h> + +struct virt_reset_map { + const char *clk_name; + int clk_id; +}; + +struct virtrc_front { + struct virt_reset_map *reset_map; + struct reset_controller_dev rcdev; +}; + +#define to_virtrc_front(r) \ + container_of(r, struct virtrc_front, rcdev) + +extern struct reset_control_ops virtrc_front_ops; + +int msm_virtrc_front_register(struct platform_device *pdev, + struct virt_reset_map *map, unsigned int nr_resets); +#endif diff --git a/drivers/clk/msm/virtclk-front-8996.c b/drivers/clk/msm/virtclk-front-8996.c index 2e978cd3a456..28f3dccf5f72 100644 --- a/drivers/clk/msm/virtclk-front-8996.c +++ b/drivers/clk/msm/virtclk-front-8996.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <dt-bindings/clock/msm-clocks-8996.h> +#include "virt-reset-front.h" static struct virtclk_front gcc_blsp1_ahb_clk = { .c = { @@ -524,6 +525,15 @@ static struct clk_lookup msm_clocks_8996[] = { CLK_LIST(gcc_mss_mnoc_bimc_axi_clk), }; +static struct virt_reset_map msm_resets_8996[] = { + [QUSB2PHY_PRIM_BCR] = { "gcc_qusb2phy_prim_clk" }, + [QUSB2PHY_SEC_BCR] = { "gcc_qusb2phy_sec_clk" }, + [USB_20_BCR] = { "gcc_usb20_master_clk" }, + [USB_30_BCR] = { "gcc_usb3_phy_pipe_clk" }, + [USB3_PHY_BCR] = { "gcc_usb3_phy_clk" }, + [USB3PHY_PHY_BCR] = { "gcc_usb3phy_phy_clk" }, +}; + static const struct of_device_id msm8996_virtclk_front_match_table[] = { { .compatible = "qcom,virtclk-frontend-8996" }, {} @@ -531,8 +541,17 @@ static const struct of_device_id msm8996_virtclk_front_match_table[] = { static int msm8996_virtclk_front_probe(struct platform_device *pdev) { - return msm_virtclk_front_probe(pdev, msm_clocks_8996, + int ret = 0; + + ret = msm_virtclk_front_probe(pdev, msm_clocks_8996, ARRAY_SIZE(msm_clocks_8996)); + if (ret) + return ret; + + ret = msm_virtrc_front_register(pdev, msm_resets_8996, + ARRAY_SIZE(msm_resets_8996)); + + return ret; } static struct platform_driver msm8996_virtclk_front_driver = { diff --git a/drivers/clk/msm/virtclk-front.c b/drivers/clk/msm/virtclk-front.c index 08c7e5aaa7f4..4018c4922574 100644 --- a/drivers/clk/msm/virtclk-front.c +++ b/drivers/clk/msm/virtclk-front.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -17,56 +17,16 @@ #include <linux/of.h> #include <linux/habmm.h> #include <soc/qcom/msm-clock-controller.h> +#include "virtclk-front.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; +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 virtclk_front_init_iface(void) { int ret = 0; int handle; @@ -88,6 +48,7 @@ out: rt_mutex_unlock(&virtclk_front_ctx.lock); return ret; } +EXPORT_SYMBOL(virtclk_front_init_iface); static int virtclk_front_get_id(struct clk *clk) { @@ -246,7 +207,7 @@ err_out: 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_reset msg; struct clk_msg_rsp rsp; u32 rsp_size = sizeof(rsp); int handle; @@ -260,9 +221,10 @@ static int virtclk_front_reset(struct clk *clk, enum clk_reset_action action) if (ret) return ret; - msg.clk_id = v->id; - msg.cmd = CLK_MSG_RESET; - msg.len = sizeof(struct clk_msg_header); + msg.header.clk_id = v->id; + msg.header.cmd = CLK_MSG_RESET; + msg.header.len = sizeof(struct clk_msg_header); + msg.reset = action; rt_mutex_lock(&virtclk_front_ctx.lock); diff --git a/drivers/clk/msm/virtclk-front.h b/drivers/clk/msm/virtclk-front.h new file mode 100644 index 000000000000..60650f8d1ed1 --- /dev/null +++ b/drivers/clk/msm/virtclk-front.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2018, 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. + */ + +#ifndef __VIRTCLK_FRONT_H +#define __VIRTCLK_FRONT_H + +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_reset { + struct clk_msg_header header; + u32 reset; +} __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; + +struct virtclk_front_data { + int handle; + struct rt_mutex lock; +}; + +extern struct virtclk_front_data virtclk_front_ctx; +int virtclk_front_init_iface(void); + +#endif |
