blob: 747b585f1f8361e9e235c655eca9a0bf189aa1ee [file] [log] [blame]
Stephen Boyd2f0a8472012-01-11 19:24:58 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Stephen Boyde44ec392011-08-29 12:03:24 -07002 *
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
Stephen Boydef5d1c42011-12-15 20:47:14 -080013#define pr_fmt(fmt) "scm-pas: " fmt
14
Stephen Boyde44ec392011-08-29 12:03:24 -070015#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/string.h>
Stephen Boydef5d1c42011-12-15 20:47:14 -080018#include <linux/clk.h>
Stephen Boyde44ec392011-08-29 12:03:24 -070019
20#include <mach/scm.h>
21#include <mach/socinfo.h>
Stephen Boydef5d1c42011-12-15 20:47:14 -080022#include <mach/msm_bus.h>
23#include <mach/msm_bus_board.h>
Stephen Boyde44ec392011-08-29 12:03:24 -070024#include "scm-pas.h"
25
26#define PAS_INIT_IMAGE_CMD 1
27#define PAS_AUTH_AND_RESET_CMD 5
28#define PAS_SHUTDOWN_CMD 6
29#define PAS_IS_SUPPORTED_CMD 7
30
31int pas_init_image(enum pas_id id, const u8 *metadata, size_t size)
32{
33 int ret;
34 struct pas_init_image_req {
35 u32 proc;
36 u32 image_addr;
37 } request;
38 u32 scm_ret = 0;
39 /* Make memory physically contiguous */
40 void *mdata_buf = kmemdup(metadata, size, GFP_KERNEL);
41
42 if (!mdata_buf)
43 return -ENOMEM;
44
45 request.proc = id;
46 request.image_addr = virt_to_phys(mdata_buf);
47
48 ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request,
49 sizeof(request), &scm_ret, sizeof(scm_ret));
50 kfree(mdata_buf);
51
52 if (ret)
53 return ret;
54 return scm_ret;
55}
56EXPORT_SYMBOL(pas_init_image);
57
Stephen Boydef5d1c42011-12-15 20:47:14 -080058static struct msm_bus_paths scm_pas_bw_tbl[] = {
59 {
60 .vectors = (struct msm_bus_vectors[]){
61 {
62 .src = MSM_BUS_MASTER_SPS,
63 .dst = MSM_BUS_SLAVE_EBI_CH0,
64 },
65 },
66 .num_paths = 1,
67 },
68 {
69 .vectors = (struct msm_bus_vectors[]){
70 {
71 .src = MSM_BUS_MASTER_SPS,
72 .dst = MSM_BUS_SLAVE_EBI_CH0,
73 .ib = 492 * 8 * 1000000UL,
74 .ab = 492 * 8 * 100000UL,
75 },
76 },
77 .num_paths = 1,
78 },
79};
80
81static struct msm_bus_scale_pdata scm_pas_bus_pdata = {
82 .usecase = scm_pas_bw_tbl,
83 .num_usecases = ARRAY_SIZE(scm_pas_bw_tbl),
84 .name = "scm_pas",
85};
86
87static uint32_t scm_perf_client;
88static struct clk *scm_bus_clk;
89
90static DEFINE_MUTEX(scm_pas_bw_mutex);
91static int scm_pas_bw_count;
92
93static int scm_pas_enable_bw(void)
94{
95 int ret = 0;
96
Stephen Boyd2f0a8472012-01-11 19:24:58 -080097 if (!scm_perf_client || !scm_bus_clk)
Stephen Boydef5d1c42011-12-15 20:47:14 -080098 return -EINVAL;
99
100 mutex_lock(&scm_pas_bw_mutex);
101 if (!scm_pas_bw_count) {
102 ret = msm_bus_scale_client_update_request(scm_perf_client, 1);
103 if (ret) {
104 pr_err("bandwidth request failed (%d)\n", ret);
105 } else {
106 ret = clk_enable(scm_bus_clk);
107 if (ret)
108 pr_err("clock enable failed\n");
109 }
110 }
111 if (ret)
112 msm_bus_scale_client_update_request(scm_perf_client, 0);
113 else
114 scm_pas_bw_count++;
115 mutex_unlock(&scm_pas_bw_mutex);
116 return ret;
117}
118
119static void scm_pas_disable_bw(void)
120{
121 mutex_lock(&scm_pas_bw_mutex);
122 if (scm_pas_bw_count-- == 1) {
123 msm_bus_scale_client_update_request(scm_perf_client, 0);
124 clk_disable(scm_bus_clk);
125 }
126 mutex_unlock(&scm_pas_bw_mutex);
127}
128
Stephen Boyde44ec392011-08-29 12:03:24 -0700129int pas_auth_and_reset(enum pas_id id)
130{
Stephen Boydef5d1c42011-12-15 20:47:14 -0800131 int ret, bus_ret;
Stephen Boyde44ec392011-08-29 12:03:24 -0700132 u32 proc = id, scm_ret = 0;
133
Stephen Boydef5d1c42011-12-15 20:47:14 -0800134 bus_ret = scm_pas_enable_bw();
Stephen Boyde44ec392011-08-29 12:03:24 -0700135 ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc,
136 sizeof(proc), &scm_ret, sizeof(scm_ret));
137 if (ret)
Stephen Boydef5d1c42011-12-15 20:47:14 -0800138 scm_ret = ret;
139 if (!bus_ret)
140 scm_pas_disable_bw();
Stephen Boyde44ec392011-08-29 12:03:24 -0700141
142 return scm_ret;
143}
144EXPORT_SYMBOL(pas_auth_and_reset);
145
146int pas_shutdown(enum pas_id id)
147{
148 int ret;
149 u32 proc = id, scm_ret = 0;
150
151 ret = scm_call(SCM_SVC_PIL, PAS_SHUTDOWN_CMD, &proc, sizeof(proc),
152 &scm_ret, sizeof(scm_ret));
153 if (ret)
154 return ret;
155
156 return scm_ret;
157}
158EXPORT_SYMBOL(pas_shutdown);
159
160static bool secure_pil = true;
161module_param(secure_pil, bool, S_IRUGO);
162MODULE_PARM_DESC(secure_pil, "Use secure PIL");
163
164int pas_supported(enum pas_id id)
165{
166 int ret;
167 u32 periph = id, ret_val = 0;
168
169 if (!secure_pil)
170 return 0;
171
172 /*
173 * 8660 SCM doesn't support querying secure PIL support so just return
174 * true if not overridden on the command line.
175 */
176 if (cpu_is_msm8x60())
177 return 1;
178
179 if (scm_is_call_available(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD) <= 0)
180 return 0;
181
182 ret = scm_call(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD, &periph,
183 sizeof(periph), &ret_val, sizeof(ret_val));
184 if (ret)
185 return ret;
186
187 return ret_val;
188}
189EXPORT_SYMBOL(pas_supported);
Stephen Boydef5d1c42011-12-15 20:47:14 -0800190
191static int __init scm_pas_init(void)
192{
Stephen Boyd737b1e62011-12-28 13:48:28 -0800193 /* TODO: Remove once bus scaling driver is in place */
194 if (!cpu_is_apq8064())
195 scm_perf_client = msm_bus_scale_register_client(
196 &scm_pas_bus_pdata);
Stephen Boydef5d1c42011-12-15 20:47:14 -0800197 if (!scm_perf_client)
198 pr_warn("unable to register bus client\n");
199 scm_bus_clk = clk_get_sys("scm", "bus_clk");
200 if (!IS_ERR(scm_bus_clk)) {
201 clk_set_rate(scm_bus_clk, 64000000);
202 } else {
203 scm_bus_clk = NULL;
204 pr_warn("unable to get bus clock\n");
205 }
206 return 0;
207}
208module_init(scm_pas_init);