~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/arm/mach-msm/scm.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
 
2
 *
 
3
 * This program is free software; you can redistribute it and/or modify
 
4
 * it under the terms of the GNU General Public License version 2 and
 
5
 * only version 2 as published by the Free Software Foundation.
 
6
 *
 
7
 * This program is distributed in the hope that it will be useful,
 
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
 * GNU General Public License for more details.
 
11
 *
 
12
 * You should have received a copy of the GNU General Public License
 
13
 * along with this program; if not, write to the Free Software
 
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
15
 * 02110-1301, USA.
 
16
 */
 
17
 
 
18
#include <linux/slab.h>
 
19
#include <linux/io.h>
 
20
#include <linux/module.h>
 
21
#include <linux/mutex.h>
 
22
#include <linux/errno.h>
 
23
#include <linux/err.h>
 
24
 
 
25
#include <asm/cacheflush.h>
 
26
 
 
27
#include "scm.h"
 
28
 
 
29
/* Cache line size for msm8x60 */
 
30
#define CACHELINESIZE 32
 
31
 
 
32
#define SCM_ENOMEM              -5
 
33
#define SCM_EOPNOTSUPP          -4
 
34
#define SCM_EINVAL_ADDR         -3
 
35
#define SCM_EINVAL_ARG          -2
 
36
#define SCM_ERROR               -1
 
37
#define SCM_INTERRUPTED         1
 
38
 
 
39
static DEFINE_MUTEX(scm_lock);
 
40
 
 
41
/**
 
42
 * struct scm_command - one SCM command buffer
 
43
 * @len: total available memory for command and response
 
44
 * @buf_offset: start of command buffer
 
45
 * @resp_hdr_offset: start of response buffer
 
46
 * @id: command to be executed
 
47
 * @buf: buffer returned from scm_get_command_buffer()
 
48
 *
 
49
 * An SCM command is laid out in memory as follows:
 
50
 *
 
51
 *      ------------------- <--- struct scm_command
 
52
 *      | command header  |
 
53
 *      ------------------- <--- scm_get_command_buffer()
 
54
 *      | command buffer  |
 
55
 *      ------------------- <--- struct scm_response and
 
56
 *      | response header |      scm_command_to_response()
 
57
 *      ------------------- <--- scm_get_response_buffer()
 
58
 *      | response buffer |
 
59
 *      -------------------
 
60
 *
 
61
 * There can be arbitrary padding between the headers and buffers so
 
62
 * you should always use the appropriate scm_get_*_buffer() routines
 
63
 * to access the buffers in a safe manner.
 
64
 */
 
65
struct scm_command {
 
66
        u32     len;
 
67
        u32     buf_offset;
 
68
        u32     resp_hdr_offset;
 
69
        u32     id;
 
70
        u32     buf[0];
 
71
};
 
72
 
 
73
/**
 
74
 * struct scm_response - one SCM response buffer
 
75
 * @len: total available memory for response
 
76
 * @buf_offset: start of response data relative to start of scm_response
 
77
 * @is_complete: indicates if the command has finished processing
 
78
 */
 
79
struct scm_response {
 
80
        u32     len;
 
81
        u32     buf_offset;
 
82
        u32     is_complete;
 
83
};
 
84
 
 
85
/**
 
86
 * alloc_scm_command() - Allocate an SCM command
 
87
 * @cmd_size: size of the command buffer
 
88
 * @resp_size: size of the response buffer
 
89
 *
 
90
 * Allocate an SCM command, including enough room for the command
 
91
 * and response headers as well as the command and response buffers.
 
92
 *
 
93
 * Returns a valid &scm_command on success or %NULL if the allocation fails.
 
94
 */
 
95
static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
 
96
{
 
97
        struct scm_command *cmd;
 
98
        size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
 
99
                resp_size;
 
100
 
 
101
        cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
 
102
        if (cmd) {
 
103
                cmd->len = len;
 
104
                cmd->buf_offset = offsetof(struct scm_command, buf);
 
105
                cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
 
106
        }
 
107
        return cmd;
 
108
}
 
109
 
 
110
/**
 
111
 * free_scm_command() - Free an SCM command
 
112
 * @cmd: command to free
 
113
 *
 
114
 * Free an SCM command.
 
115
 */
 
116
static inline void free_scm_command(struct scm_command *cmd)
 
117
{
 
118
        kfree(cmd);
 
119
}
 
120
 
 
121
/**
 
122
 * scm_command_to_response() - Get a pointer to a scm_response
 
123
 * @cmd: command
 
124
 *
 
125
 * Returns a pointer to a response for a command.
 
126
 */
 
127
static inline struct scm_response *scm_command_to_response(
 
128
                const struct scm_command *cmd)
 
129
{
 
130
        return (void *)cmd + cmd->resp_hdr_offset;
 
131
}
 
132
 
 
133
/**
 
134
 * scm_get_command_buffer() - Get a pointer to a command buffer
 
135
 * @cmd: command
 
136
 *
 
137
 * Returns a pointer to the command buffer of a command.
 
138
 */
 
139
static inline void *scm_get_command_buffer(const struct scm_command *cmd)
 
140
{
 
141
        return (void *)cmd->buf;
 
142
}
 
143
 
 
144
/**
 
145
 * scm_get_response_buffer() - Get a pointer to a response buffer
 
146
 * @rsp: response
 
147
 *
 
148
 * Returns a pointer to a response buffer of a response.
 
149
 */
 
150
static inline void *scm_get_response_buffer(const struct scm_response *rsp)
 
151
{
 
152
        return (void *)rsp + rsp->buf_offset;
 
153
}
 
154
 
 
155
static int scm_remap_error(int err)
 
156
{
 
157
        switch (err) {
 
158
        case SCM_ERROR:
 
159
                return -EIO;
 
160
        case SCM_EINVAL_ADDR:
 
161
        case SCM_EINVAL_ARG:
 
162
                return -EINVAL;
 
163
        case SCM_EOPNOTSUPP:
 
164
                return -EOPNOTSUPP;
 
165
        case SCM_ENOMEM:
 
166
                return -ENOMEM;
 
167
        }
 
168
        return -EINVAL;
 
169
}
 
170
 
 
171
static u32 smc(u32 cmd_addr)
 
172
{
 
173
        int context_id;
 
174
        register u32 r0 asm("r0") = 1;
 
175
        register u32 r1 asm("r1") = (u32)&context_id;
 
176
        register u32 r2 asm("r2") = cmd_addr;
 
177
        do {
 
178
                asm volatile(
 
179
                        __asmeq("%0", "r0")
 
180
                        __asmeq("%1", "r0")
 
181
                        __asmeq("%2", "r1")
 
182
                        __asmeq("%3", "r2")
 
183
#ifdef REQUIRES_SEC
 
184
                        ".arch_extension sec\n"
 
185
#endif
 
186
                        "smc    #0      @ switch to secure world\n"
 
187
                        : "=r" (r0)
 
188
                        : "r" (r0), "r" (r1), "r" (r2)
 
189
                        : "r3");
 
190
        } while (r0 == SCM_INTERRUPTED);
 
191
 
 
192
        return r0;
 
193
}
 
194
 
 
195
static int __scm_call(const struct scm_command *cmd)
 
196
{
 
197
        int ret;
 
198
        u32 cmd_addr = virt_to_phys(cmd);
 
199
 
 
200
        /*
 
201
         * Flush the entire cache here so callers don't have to remember
 
202
         * to flush the cache when passing physical addresses to the secure
 
203
         * side in the buffer.
 
204
         */
 
205
        flush_cache_all();
 
206
        ret = smc(cmd_addr);
 
207
        if (ret < 0)
 
208
                ret = scm_remap_error(ret);
 
209
 
 
210
        return ret;
 
211
}
 
212
 
 
213
/**
 
214
 * scm_call() - Send an SCM command
 
215
 * @svc_id: service identifier
 
216
 * @cmd_id: command identifier
 
217
 * @cmd_buf: command buffer
 
218
 * @cmd_len: length of the command buffer
 
219
 * @resp_buf: response buffer
 
220
 * @resp_len: length of the response buffer
 
221
 *
 
222
 * Sends a command to the SCM and waits for the command to finish processing.
 
223
 */
 
224
int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
 
225
                void *resp_buf, size_t resp_len)
 
226
{
 
227
        int ret;
 
228
        struct scm_command *cmd;
 
229
        struct scm_response *rsp;
 
230
 
 
231
        cmd = alloc_scm_command(cmd_len, resp_len);
 
232
        if (!cmd)
 
233
                return -ENOMEM;
 
234
 
 
235
        cmd->id = (svc_id << 10) | cmd_id;
 
236
        if (cmd_buf)
 
237
                memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
 
238
 
 
239
        mutex_lock(&scm_lock);
 
240
        ret = __scm_call(cmd);
 
241
        mutex_unlock(&scm_lock);
 
242
        if (ret)
 
243
                goto out;
 
244
 
 
245
        rsp = scm_command_to_response(cmd);
 
246
        do {
 
247
                u32 start = (u32)rsp;
 
248
                u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
 
249
                start &= ~(CACHELINESIZE - 1);
 
250
                while (start < end) {
 
251
                        asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
 
252
                             : "memory");
 
253
                        start += CACHELINESIZE;
 
254
                }
 
255
        } while (!rsp->is_complete);
 
256
 
 
257
        if (resp_buf)
 
258
                memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
 
259
out:
 
260
        free_scm_command(cmd);
 
261
        return ret;
 
262
}
 
263
EXPORT_SYMBOL(scm_call);
 
264
 
 
265
u32 scm_get_version(void)
 
266
{
 
267
        int context_id;
 
268
        static u32 version = -1;
 
269
        register u32 r0 asm("r0");
 
270
        register u32 r1 asm("r1");
 
271
 
 
272
        if (version != -1)
 
273
                return version;
 
274
 
 
275
        mutex_lock(&scm_lock);
 
276
 
 
277
        r0 = 0x1 << 8;
 
278
        r1 = (u32)&context_id;
 
279
        do {
 
280
                asm volatile(
 
281
                        __asmeq("%0", "r0")
 
282
                        __asmeq("%1", "r1")
 
283
                        __asmeq("%2", "r0")
 
284
                        __asmeq("%3", "r1")
 
285
                        "smc    #0      @ switch to secure world\n"
 
286
                        : "=r" (r0), "=r" (r1)
 
287
                        : "r" (r0), "r" (r1)
 
288
                        : "r2", "r3");
 
289
        } while (r0 == SCM_INTERRUPTED);
 
290
 
 
291
        version = r1;
 
292
        mutex_unlock(&scm_lock);
 
293
 
 
294
        return version;
 
295
}
 
296
EXPORT_SYMBOL(scm_get_version);