summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom/rpmh.c
blob: b55e23c4417d3b68d19d310ef3276436d031cbd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 */

#include <dm/device.h>
#include <dm/device_compat.h>
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/types.h>

#include <soc/qcom/rpmh.h>

#include "rpmh-internal.h"

#define RPMH_TIMEOUT_MS			msecs_to_jiffies(10000)

#define DEFINE_RPMH_MSG_ONSTACK(device, s, name)	\
	struct rpmh_request name = {			\
		.msg = {				\
			.state = s,			\
			.cmds = name.cmd,		\
			.num_cmds = 0,			\
			.wait_for_compl = true		\
		},					\
		.cmd = { { 0 } },			\
		.dev = device,				\
		.needs_free = false,			\
	}

#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client)

static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct udevice *dev)
{
	struct rsc_drv *drv = (struct rsc_drv *)dev_get_priv(dev->parent);

	if (!drv) {
		log_err("BUG: no RPMh driver for %s (parent %s)\n", dev->name, dev->parent->name);
		BUG();
	}

	return &drv->client;
}

/**
 * __rpmh_write: Cache and send the RPMH request
 *
 * @dev: The device making the request
 * @state: Active/Sleep request type
 * @rpm_msg: The data that needs to be sent (cmds).
 *
 * Cache the RPMH request and send if the state is ACTIVE_ONLY.
 * SLEEP/WAKE_ONLY requests are not sent to the controller at
 * this time. Use rpmh_flush() to send them to the controller.
 */
static int __rpmh_write(const struct udevice *dev, enum rpmh_state state,
			struct rpmh_request *rpm_msg)
{
	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);

	if (state != RPMH_ACTIVE_ONLY_STATE) {
		log_debug("WARN: Only ACTIVE_ONLY state supported\n");
		return 0;
	}

	return rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg);
}

static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
			   const struct tcs_cmd *cmd, u32 n, bool is_read)
{
	if (!cmd || !n || n > MAX_RPMH_PAYLOAD)
		return -EINVAL;

	memcpy(req->cmd, cmd, n * sizeof(*cmd));

	req->msg.state = state;
	req->msg.cmds = req->cmd;
	req->msg.num_cmds = n;
	req->msg.is_read = is_read;

	debug("rpmh_msg: %d, %d cmds [first %#x/%#x]\n", state, n, cmd->addr, cmd->data);

	return 0;
}

/**
 * rpmh_read: Read a resource value
 *
 * @dev: The device making the request
 * @cmd: The payload having address of resource to read
 *
 * Reads the value for the resource address given in tcs_cmd->addr
 * and returns the tcs_cmd->data filled with same.
 *
 * May sleep. Do not call from atomic contexts.
 *
 * Return: 0 on success, negative errno on failure
 */
int rpmh_read(const struct udevice *dev, enum rpmh_state state, struct tcs_cmd *cmd)
{
	DEFINE_RPMH_MSG_ONSTACK(dev, state, rpm_msg);
	int ret;

	ret = __fill_rpmh_msg(&rpm_msg, state, cmd, 1, true);
	if (ret)
		return ret;

	ret = __rpmh_write(dev, state, &rpm_msg);
	if (ret)
		return ret;

	/* Read back the response into the cmd data structure */
	cmd->data = rpm_msg.cmd[0].data;

	return (ret > 0) ? 0 : ret;
}

/**
 * rpmh_write: Write a set of RPMH commands and block until response
 *
 * @dev: The device making the request
 * @state: Active/sleep set
 * @cmd: The payload data
 * @n: The number of elements in @cmd
 *
 * May sleep. Do not call from atomic contexts.
 */
int rpmh_write(const struct udevice *dev, enum rpmh_state state,
	       const struct tcs_cmd *cmd, u32 n)
{
	DEFINE_RPMH_MSG_ONSTACK(dev, state, rpm_msg);
	int ret;

	ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n, false);
	if (ret)
		return ret;

	ret = __rpmh_write(dev, state, &rpm_msg);

	return ret;
}
EXPORT_SYMBOL_GPL(rpmh_write);