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