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