Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 292371d2 authored by Zhiqiang Tu's avatar Zhiqiang Tu
Browse files

clk: msm: Add virtual clock frontend driver



It is virtual clock frontend driver which communicates
with host backend clock service/driver over HABMM. It
only supports msm8996 virtual platform currently.

Change-Id: Icfee28c301fa4a583b45e5d364432535643eb9b7
Signed-off-by: default avatarZhiqiang Tu <ztu@codeaurora.org>
parent 7099c46a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
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>;
	};
+11 −0
Original line number Diff line number Diff line
@@ -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"
+3 −0
Original line number Diff line number Diff line
@@ -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
+407 −0
Original line number Diff line number Diff line
/* 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);
+460 −0
Original line number Diff line number Diff line
/* 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);
Loading