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