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

Commit 249a0230 authored by Zhiqiang Tu's avatar Zhiqiang Tu
Browse files

clk: msm: Add reset support in virtual clock frontend driver



To support reset function in virtual clock driver, we register
a reset controller in virtual clock front driver. It is a virtual
reset controller which talks with host backend via virtual clock
message.

Change-Id: I13d48180534110260d66e42f3a02b63c2afcaeca
Signed-off-by: default avatarZhiqiang Tu <ztu@codeaurora.org>
parent de111ce2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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>;
	};
+1 −0
Original line number Diff line number Diff line
@@ -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
+192 −0
Original line number Diff line number Diff line
/*
 * 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);
+37 −0
Original line number Diff line number Diff line
/*
 * 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
+21 −2
Original line number Diff line number Diff line
/* 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 = {
Loading