xref: /rk3399_ARM-atf/drivers/arm/css/scmi/scmi_common.c (revision 14928b88ab9f16aebd492f4d71779fd6f5ac91b2)
1*14928b88SAntonio Nino Diaz /*
2*14928b88SAntonio Nino Diaz  * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3*14928b88SAntonio Nino Diaz  *
4*14928b88SAntonio Nino Diaz  * SPDX-License-Identifier: BSD-3-Clause
5*14928b88SAntonio Nino Diaz  */
6*14928b88SAntonio Nino Diaz 
7*14928b88SAntonio Nino Diaz #include <assert.h>
8*14928b88SAntonio Nino Diaz 
9*14928b88SAntonio Nino Diaz #include <arch_helpers.h>
10*14928b88SAntonio Nino Diaz #include <common/debug.h>
11*14928b88SAntonio Nino Diaz #include <drivers/arm/css/scmi.h>
12*14928b88SAntonio Nino Diaz 
13*14928b88SAntonio Nino Diaz #include "scmi_private.h"
14*14928b88SAntonio Nino Diaz 
15*14928b88SAntonio Nino Diaz #if HW_ASSISTED_COHERENCY
16*14928b88SAntonio Nino Diaz #define scmi_lock_init(lock)
17*14928b88SAntonio Nino Diaz #define scmi_lock_get(lock)		spin_lock(lock)
18*14928b88SAntonio Nino Diaz #define scmi_lock_release(lock)		spin_unlock(lock)
19*14928b88SAntonio Nino Diaz #else
20*14928b88SAntonio Nino Diaz #define scmi_lock_init(lock)		bakery_lock_init(lock)
21*14928b88SAntonio Nino Diaz #define scmi_lock_get(lock)		bakery_lock_get(lock)
22*14928b88SAntonio Nino Diaz #define scmi_lock_release(lock)		bakery_lock_release(lock)
23*14928b88SAntonio Nino Diaz #endif
24*14928b88SAntonio Nino Diaz 
25*14928b88SAntonio Nino Diaz 
26*14928b88SAntonio Nino Diaz /*
27*14928b88SAntonio Nino Diaz  * Private helper function to get exclusive access to SCMI channel.
28*14928b88SAntonio Nino Diaz  */
29*14928b88SAntonio Nino Diaz void scmi_get_channel(scmi_channel_t *ch)
30*14928b88SAntonio Nino Diaz {
31*14928b88SAntonio Nino Diaz 	assert(ch->lock);
32*14928b88SAntonio Nino Diaz 	scmi_lock_get(ch->lock);
33*14928b88SAntonio Nino Diaz 
34*14928b88SAntonio Nino Diaz 	/* Make sure any previous command has finished */
35*14928b88SAntonio Nino Diaz 	assert(SCMI_IS_CHANNEL_FREE(
36*14928b88SAntonio Nino Diaz 			((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
37*14928b88SAntonio Nino Diaz }
38*14928b88SAntonio Nino Diaz 
39*14928b88SAntonio Nino Diaz /*
40*14928b88SAntonio Nino Diaz  * Private helper function to transfer ownership of channel from AP to SCP.
41*14928b88SAntonio Nino Diaz  */
42*14928b88SAntonio Nino Diaz void scmi_send_sync_command(scmi_channel_t *ch)
43*14928b88SAntonio Nino Diaz {
44*14928b88SAntonio Nino Diaz 	mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
45*14928b88SAntonio Nino Diaz 
46*14928b88SAntonio Nino Diaz 	SCMI_MARK_CHANNEL_BUSY(mbx_mem->status);
47*14928b88SAntonio Nino Diaz 
48*14928b88SAntonio Nino Diaz 	/*
49*14928b88SAntonio Nino Diaz 	 * Ensure that any write to the SCMI payload area is seen by SCP before
50*14928b88SAntonio Nino Diaz 	 * we write to the doorbell register. If these 2 writes were reordered
51*14928b88SAntonio Nino Diaz 	 * by the CPU then SCP would read stale payload data
52*14928b88SAntonio Nino Diaz 	 */
53*14928b88SAntonio Nino Diaz 	dmbst();
54*14928b88SAntonio Nino Diaz 
55*14928b88SAntonio Nino Diaz 	ch->info->ring_doorbell(ch->info);
56*14928b88SAntonio Nino Diaz 	/*
57*14928b88SAntonio Nino Diaz 	 * Ensure that the write to the doorbell register is ordered prior to
58*14928b88SAntonio Nino Diaz 	 * checking whether the channel is free.
59*14928b88SAntonio Nino Diaz 	 */
60*14928b88SAntonio Nino Diaz 	dmbsy();
61*14928b88SAntonio Nino Diaz 
62*14928b88SAntonio Nino Diaz 	/* Wait for channel to be free */
63*14928b88SAntonio Nino Diaz 	while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status))
64*14928b88SAntonio Nino Diaz 		;
65*14928b88SAntonio Nino Diaz 
66*14928b88SAntonio Nino Diaz 	/*
67*14928b88SAntonio Nino Diaz 	 * Ensure that any read to the SCMI payload area is done after reading
68*14928b88SAntonio Nino Diaz 	 * mailbox status. If these 2 reads were reordered then the CPU would
69*14928b88SAntonio Nino Diaz 	 * read invalid payload data
70*14928b88SAntonio Nino Diaz 	 */
71*14928b88SAntonio Nino Diaz 	dmbld();
72*14928b88SAntonio Nino Diaz }
73*14928b88SAntonio Nino Diaz 
74*14928b88SAntonio Nino Diaz /*
75*14928b88SAntonio Nino Diaz  * Private helper function to release exclusive access to SCMI channel.
76*14928b88SAntonio Nino Diaz  */
77*14928b88SAntonio Nino Diaz void scmi_put_channel(scmi_channel_t *ch)
78*14928b88SAntonio Nino Diaz {
79*14928b88SAntonio Nino Diaz 	/* Make sure any previous command has finished */
80*14928b88SAntonio Nino Diaz 	assert(SCMI_IS_CHANNEL_FREE(
81*14928b88SAntonio Nino Diaz 			((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
82*14928b88SAntonio Nino Diaz 
83*14928b88SAntonio Nino Diaz 	assert(ch->lock);
84*14928b88SAntonio Nino Diaz 	scmi_lock_release(ch->lock);
85*14928b88SAntonio Nino Diaz }
86*14928b88SAntonio Nino Diaz 
87*14928b88SAntonio Nino Diaz /*
88*14928b88SAntonio Nino Diaz  * API to query the SCMI protocol version.
89*14928b88SAntonio Nino Diaz  */
90*14928b88SAntonio Nino Diaz int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version)
91*14928b88SAntonio Nino Diaz {
92*14928b88SAntonio Nino Diaz 	mailbox_mem_t *mbx_mem;
93*14928b88SAntonio Nino Diaz 	int token = 0, ret;
94*14928b88SAntonio Nino Diaz 	scmi_channel_t *ch = (scmi_channel_t *)p;
95*14928b88SAntonio Nino Diaz 
96*14928b88SAntonio Nino Diaz 	validate_scmi_channel(ch);
97*14928b88SAntonio Nino Diaz 
98*14928b88SAntonio Nino Diaz 	scmi_get_channel(ch);
99*14928b88SAntonio Nino Diaz 
100*14928b88SAntonio Nino Diaz 	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
101*14928b88SAntonio Nino Diaz 	mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG,
102*14928b88SAntonio Nino Diaz 							token);
103*14928b88SAntonio Nino Diaz 	mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN;
104*14928b88SAntonio Nino Diaz 	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
105*14928b88SAntonio Nino Diaz 
106*14928b88SAntonio Nino Diaz 	scmi_send_sync_command(ch);
107*14928b88SAntonio Nino Diaz 
108*14928b88SAntonio Nino Diaz 	/* Get the return values */
109*14928b88SAntonio Nino Diaz 	SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version);
110*14928b88SAntonio Nino Diaz 	assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN);
111*14928b88SAntonio Nino Diaz 	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
112*14928b88SAntonio Nino Diaz 
113*14928b88SAntonio Nino Diaz 	scmi_put_channel(ch);
114*14928b88SAntonio Nino Diaz 
115*14928b88SAntonio Nino Diaz 	return ret;
116*14928b88SAntonio Nino Diaz }
117*14928b88SAntonio Nino Diaz 
118*14928b88SAntonio Nino Diaz /*
119*14928b88SAntonio Nino Diaz  * API to query the protocol message attributes for a SCMI protocol.
120*14928b88SAntonio Nino Diaz  */
121*14928b88SAntonio Nino Diaz int scmi_proto_msg_attr(void *p, uint32_t proto_id,
122*14928b88SAntonio Nino Diaz 		uint32_t command_id, uint32_t *attr)
123*14928b88SAntonio Nino Diaz {
124*14928b88SAntonio Nino Diaz 	mailbox_mem_t *mbx_mem;
125*14928b88SAntonio Nino Diaz 	int token = 0, ret;
126*14928b88SAntonio Nino Diaz 	scmi_channel_t *ch = (scmi_channel_t *)p;
127*14928b88SAntonio Nino Diaz 
128*14928b88SAntonio Nino Diaz 	validate_scmi_channel(ch);
129*14928b88SAntonio Nino Diaz 
130*14928b88SAntonio Nino Diaz 	scmi_get_channel(ch);
131*14928b88SAntonio Nino Diaz 
132*14928b88SAntonio Nino Diaz 	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
133*14928b88SAntonio Nino Diaz 	mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id,
134*14928b88SAntonio Nino Diaz 				SCMI_PROTO_MSG_ATTR_MSG, token);
135*14928b88SAntonio Nino Diaz 	mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN;
136*14928b88SAntonio Nino Diaz 	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
137*14928b88SAntonio Nino Diaz 	SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id);
138*14928b88SAntonio Nino Diaz 
139*14928b88SAntonio Nino Diaz 	scmi_send_sync_command(ch);
140*14928b88SAntonio Nino Diaz 
141*14928b88SAntonio Nino Diaz 	/* Get the return values */
142*14928b88SAntonio Nino Diaz 	SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr);
143*14928b88SAntonio Nino Diaz 	assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN);
144*14928b88SAntonio Nino Diaz 	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
145*14928b88SAntonio Nino Diaz 
146*14928b88SAntonio Nino Diaz 	scmi_put_channel(ch);
147*14928b88SAntonio Nino Diaz 
148*14928b88SAntonio Nino Diaz 	return ret;
149*14928b88SAntonio Nino Diaz }
150*14928b88SAntonio Nino Diaz 
151*14928b88SAntonio Nino Diaz /*
152*14928b88SAntonio Nino Diaz  * SCMI Driver initialization API. Returns initialized channel on success
153*14928b88SAntonio Nino Diaz  * or NULL on error. The return type is an opaque void pointer.
154*14928b88SAntonio Nino Diaz  */
155*14928b88SAntonio Nino Diaz void *scmi_init(scmi_channel_t *ch)
156*14928b88SAntonio Nino Diaz {
157*14928b88SAntonio Nino Diaz 	uint32_t version;
158*14928b88SAntonio Nino Diaz 	int ret;
159*14928b88SAntonio Nino Diaz 
160*14928b88SAntonio Nino Diaz 	assert(ch && ch->info);
161*14928b88SAntonio Nino Diaz 	assert(ch->info->db_reg_addr);
162*14928b88SAntonio Nino Diaz 	assert(ch->info->db_modify_mask);
163*14928b88SAntonio Nino Diaz 	assert(ch->info->db_preserve_mask);
164*14928b88SAntonio Nino Diaz 	assert(ch->info->ring_doorbell != NULL);
165*14928b88SAntonio Nino Diaz 
166*14928b88SAntonio Nino Diaz 	assert(ch->lock);
167*14928b88SAntonio Nino Diaz 
168*14928b88SAntonio Nino Diaz 	scmi_lock_init(ch->lock);
169*14928b88SAntonio Nino Diaz 
170*14928b88SAntonio Nino Diaz 	ch->is_initialized = 1;
171*14928b88SAntonio Nino Diaz 
172*14928b88SAntonio Nino Diaz 	ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version);
173*14928b88SAntonio Nino Diaz 	if (ret != SCMI_E_SUCCESS) {
174*14928b88SAntonio Nino Diaz 		WARN("SCMI power domain protocol version message failed");
175*14928b88SAntonio Nino Diaz 		goto error;
176*14928b88SAntonio Nino Diaz 	}
177*14928b88SAntonio Nino Diaz 
178*14928b88SAntonio Nino Diaz 	if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) {
179*14928b88SAntonio Nino Diaz 		WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x",
180*14928b88SAntonio Nino Diaz 			version, SCMI_PWR_DMN_PROTO_VER);
181*14928b88SAntonio Nino Diaz 		goto error;
182*14928b88SAntonio Nino Diaz 	}
183*14928b88SAntonio Nino Diaz 
184*14928b88SAntonio Nino Diaz 	VERBOSE("SCMI power domain protocol version 0x%x detected\n", version);
185*14928b88SAntonio Nino Diaz 
186*14928b88SAntonio Nino Diaz 	ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version);
187*14928b88SAntonio Nino Diaz 	if ((ret != SCMI_E_SUCCESS)) {
188*14928b88SAntonio Nino Diaz 		WARN("SCMI system power protocol version message failed");
189*14928b88SAntonio Nino Diaz 		goto error;
190*14928b88SAntonio Nino Diaz 	}
191*14928b88SAntonio Nino Diaz 
192*14928b88SAntonio Nino Diaz 	if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) {
193*14928b88SAntonio Nino Diaz 		WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x",
194*14928b88SAntonio Nino Diaz 			version, SCMI_SYS_PWR_PROTO_VER);
195*14928b88SAntonio Nino Diaz 		goto error;
196*14928b88SAntonio Nino Diaz 	}
197*14928b88SAntonio Nino Diaz 
198*14928b88SAntonio Nino Diaz 	VERBOSE("SCMI system power management protocol version 0x%x detected\n",
199*14928b88SAntonio Nino Diaz 						version);
200*14928b88SAntonio Nino Diaz 
201*14928b88SAntonio Nino Diaz 	INFO("SCMI driver initialized\n");
202*14928b88SAntonio Nino Diaz 
203*14928b88SAntonio Nino Diaz 	return (void *)ch;
204*14928b88SAntonio Nino Diaz 
205*14928b88SAntonio Nino Diaz error:
206*14928b88SAntonio Nino Diaz 	ch->is_initialized = 0;
207*14928b88SAntonio Nino Diaz 	return NULL;
208*14928b88SAntonio Nino Diaz }
209