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