diff options
Diffstat (limited to 'drivers/gpu/nvgpu/volt/volt_pmu.c')
-rw-r--r-- | drivers/gpu/nvgpu/volt/volt_pmu.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/volt/volt_pmu.c b/drivers/gpu/nvgpu/volt/volt_pmu.c new file mode 100644 index 00000000..915db9a7 --- /dev/null +++ b/drivers/gpu/nvgpu/volt/volt_pmu.c | |||
@@ -0,0 +1,286 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | * DEALINGS IN THE SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include <nvgpu/pmu.h> | ||
24 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
25 | |||
26 | #include "gk20a/gk20a.h" | ||
27 | #include "boardobj/boardobjgrp.h" | ||
28 | #include "boardobj/boardobjgrp_e32.h" | ||
29 | #include "gp106/bios_gp106.h" | ||
30 | #include "ctrl/ctrlvolt.h" | ||
31 | #include "ctrl/ctrlperf.h" | ||
32 | |||
33 | #include "volt.h" | ||
34 | |||
35 | #define RAIL_COUNT 2 | ||
36 | |||
37 | struct volt_rpc_pmucmdhandler_params { | ||
38 | struct nv_pmu_volt_rpc *prpc_call; | ||
39 | u32 success; | ||
40 | }; | ||
41 | |||
42 | static void volt_rpc_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, | ||
43 | void *param, u32 handle, u32 status) | ||
44 | { | ||
45 | struct volt_rpc_pmucmdhandler_params *phandlerparams = | ||
46 | (struct volt_rpc_pmucmdhandler_params *)param; | ||
47 | |||
48 | gk20a_dbg_info(""); | ||
49 | |||
50 | if (msg->msg.volt.msg_type != NV_PMU_VOLT_MSG_ID_RPC) { | ||
51 | nvgpu_err(g, "unsupported msg for VOLT RPC %x", | ||
52 | msg->msg.volt.msg_type); | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | if (phandlerparams->prpc_call->b_supported) | ||
57 | phandlerparams->success = 1; | ||
58 | } | ||
59 | |||
60 | |||
61 | static u32 volt_pmu_rpc_execute(struct gk20a *g, | ||
62 | struct nv_pmu_volt_rpc *prpc_call) | ||
63 | { | ||
64 | struct pmu_cmd cmd; | ||
65 | struct pmu_msg msg; | ||
66 | struct pmu_payload payload; | ||
67 | u32 status = 0; | ||
68 | u32 seqdesc; | ||
69 | struct volt_rpc_pmucmdhandler_params handler; | ||
70 | |||
71 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
72 | memset(&cmd, 0, sizeof(struct pmu_cmd)); | ||
73 | memset(&msg, 0, sizeof(struct pmu_msg)); | ||
74 | memset(&handler, 0, sizeof(struct volt_rpc_pmucmdhandler_params)); | ||
75 | |||
76 | cmd.hdr.unit_id = PMU_UNIT_VOLT; | ||
77 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_volt_cmd) + | ||
78 | (u32)sizeof(struct pmu_hdr); | ||
79 | cmd.cmd.volt.cmd_type = NV_PMU_VOLT_CMD_ID_RPC; | ||
80 | msg.hdr.size = sizeof(struct pmu_msg); | ||
81 | |||
82 | payload.in.buf = (u8 *)prpc_call; | ||
83 | payload.in.size = (u32)sizeof(struct nv_pmu_volt_rpc); | ||
84 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
85 | payload.in.offset = NV_PMU_VOLT_CMD_RPC_ALLOC_OFFSET; | ||
86 | |||
87 | payload.out.buf = (u8 *)prpc_call; | ||
88 | payload.out.size = (u32)sizeof(struct nv_pmu_volt_rpc); | ||
89 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
90 | payload.out.offset = NV_PMU_VOLT_MSG_RPC_ALLOC_OFFSET; | ||
91 | |||
92 | handler.prpc_call = prpc_call; | ||
93 | handler.success = 0; | ||
94 | |||
95 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
96 | PMU_COMMAND_QUEUE_LPQ, | ||
97 | volt_rpc_pmucmdhandler, (void *)&handler, | ||
98 | &seqdesc, ~0); | ||
99 | if (status) { | ||
100 | nvgpu_err(g, "unable to post volt RPC cmd %x", | ||
101 | cmd.cmd.volt.cmd_type); | ||
102 | goto volt_pmu_rpc_execute; | ||
103 | } | ||
104 | |||
105 | pmu_wait_message_cond(&g->pmu, | ||
106 | gk20a_get_gr_idle_timeout(g), | ||
107 | &handler.success, 1); | ||
108 | |||
109 | if (handler.success == 0) { | ||
110 | status = -EINVAL; | ||
111 | nvgpu_err(g, "rpc call to volt failed"); | ||
112 | } | ||
113 | |||
114 | volt_pmu_rpc_execute: | ||
115 | return status; | ||
116 | } | ||
117 | |||
118 | u32 volt_pmu_send_load_cmd_to_pmu(struct gk20a *g) | ||
119 | { | ||
120 | struct nv_pmu_volt_rpc rpc_call = { 0 }; | ||
121 | u32 status = 0; | ||
122 | |||
123 | rpc_call.function = NV_PMU_VOLT_RPC_ID_LOAD; | ||
124 | |||
125 | status = volt_pmu_rpc_execute(g, &rpc_call); | ||
126 | if (status) | ||
127 | nvgpu_err(g, | ||
128 | "Error while executing LOAD RPC: status = 0x%08x.", | ||
129 | status); | ||
130 | |||
131 | return status; | ||
132 | } | ||
133 | |||
134 | static u32 volt_rail_get_voltage(struct gk20a *g, | ||
135 | u8 volt_domain, u32 *pvoltage_uv) | ||
136 | { | ||
137 | struct nv_pmu_volt_rpc rpc_call = { 0 }; | ||
138 | u32 status = 0; | ||
139 | u8 rail_idx; | ||
140 | |||
141 | rail_idx = volt_rail_volt_domain_convert_to_idx(g, volt_domain); | ||
142 | if ((rail_idx == CTRL_VOLT_RAIL_INDEX_INVALID) || | ||
143 | (!VOLT_RAIL_INDEX_IS_VALID(&g->perf_pmu.volt, rail_idx))) { | ||
144 | nvgpu_err(g, | ||
145 | "failed: volt_domain = %d, voltage rail table = %d.", | ||
146 | volt_domain, rail_idx); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | /* Set RPC parameters. */ | ||
151 | rpc_call.function = NV_PMU_VOLT_RPC_ID_VOLT_RAIL_GET_VOLTAGE; | ||
152 | rpc_call.params.volt_rail_get_voltage.rail_idx = rail_idx; | ||
153 | |||
154 | /* Execute the voltage get request via PMU RPC. */ | ||
155 | status = volt_pmu_rpc_execute(g, &rpc_call); | ||
156 | if (status) { | ||
157 | nvgpu_err(g, | ||
158 | "Error while executing volt_rail_get_voltage rpc"); | ||
159 | return status; | ||
160 | } | ||
161 | |||
162 | /* Copy out the current voltage. */ | ||
163 | *pvoltage_uv = rpc_call.params.volt_rail_get_voltage.voltage_uv; | ||
164 | |||
165 | return status; | ||
166 | } | ||
167 | |||
168 | |||
169 | static u32 volt_policy_set_voltage(struct gk20a *g, u8 client_id, | ||
170 | struct ctrl_perf_volt_rail_list *prail_list) | ||
171 | { | ||
172 | struct nv_pmu_volt_rpc rpc_call = { 0 }; | ||
173 | struct obj_volt *pvolt = &g->perf_pmu.volt; | ||
174 | u32 status = 0; | ||
175 | u8 policy_idx = CTRL_VOLT_POLICY_INDEX_INVALID; | ||
176 | u8 i = 0; | ||
177 | |||
178 | /* Sanity check input rail list. */ | ||
179 | for (i = 0; i < prail_list->num_rails; i++) { | ||
180 | if ((prail_list->rails[i].volt_domain == | ||
181 | CTRL_VOLT_DOMAIN_INVALID) || | ||
182 | (prail_list->rails[i].voltage_uv == | ||
183 | NV_PMU_VOLT_VALUE_0V_IN_UV)) { | ||
184 | nvgpu_err(g, "Invalid voltage domain or target"); | ||
185 | nvgpu_err(g, " client_id = %d, listEntry = %d", | ||
186 | client_id, i); | ||
187 | nvgpu_err(g, " volt_domain = %d, voltage_uv = %d uV.", | ||
188 | prail_list->rails[i].volt_domain, | ||
189 | prail_list->rails[i].voltage_uv); | ||
190 | status = -EINVAL; | ||
191 | goto exit; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | /* Convert the client ID to index. */ | ||
196 | if (client_id == CTRL_VOLT_POLICY_CLIENT_PERF_CORE_VF_SEQ) | ||
197 | policy_idx = | ||
198 | pvolt->volt_policy_metadata.perf_core_vf_seq_policy_idx; | ||
199 | else { | ||
200 | status = -EINVAL; | ||
201 | goto exit; | ||
202 | } | ||
203 | |||
204 | /* Set RPC parameters. */ | ||
205 | rpc_call.function = NV_PMU_VOLT_RPC_ID_VOLT_POLICY_SET_VOLTAGE; | ||
206 | rpc_call.params.volt_policy_voltage_data.policy_idx = policy_idx; | ||
207 | memcpy(&rpc_call.params.volt_policy_voltage_data.rail_list, prail_list, | ||
208 | (sizeof(struct ctrl_perf_volt_rail_list))); | ||
209 | |||
210 | /* Execute the voltage change request via PMU RPC. */ | ||
211 | status = volt_pmu_rpc_execute(g, &rpc_call); | ||
212 | if (status) | ||
213 | nvgpu_err(g, | ||
214 | "Error while executing VOLT_POLICY_SET_VOLTAGE RPC"); | ||
215 | |||
216 | exit: | ||
217 | return status; | ||
218 | } | ||
219 | |||
220 | u32 volt_set_voltage(struct gk20a *g, u32 logic_voltage_uv, u32 sram_voltage_uv) | ||
221 | { | ||
222 | u32 status = 0; | ||
223 | struct ctrl_perf_volt_rail_list rail_list = { 0 }; | ||
224 | |||
225 | rail_list.num_rails = RAIL_COUNT; | ||
226 | rail_list.rails[0].volt_domain = CTRL_VOLT_DOMAIN_LOGIC; | ||
227 | rail_list.rails[0].voltage_uv = logic_voltage_uv; | ||
228 | rail_list.rails[0].voltage_min_noise_unaware_uv = logic_voltage_uv; | ||
229 | rail_list.rails[1].volt_domain = CTRL_VOLT_DOMAIN_SRAM; | ||
230 | rail_list.rails[1].voltage_uv = sram_voltage_uv; | ||
231 | rail_list.rails[1].voltage_min_noise_unaware_uv = sram_voltage_uv; | ||
232 | |||
233 | status = volt_policy_set_voltage(g, | ||
234 | CTRL_VOLT_POLICY_CLIENT_PERF_CORE_VF_SEQ, &rail_list); | ||
235 | |||
236 | return status; | ||
237 | |||
238 | } | ||
239 | |||
240 | u32 volt_get_voltage(struct gk20a *g, u32 volt_domain, u32 *voltage_uv) | ||
241 | { | ||
242 | return volt_rail_get_voltage(g, volt_domain, voltage_uv); | ||
243 | } | ||
244 | |||
245 | static int volt_policy_set_noiseaware_vmin(struct gk20a *g, | ||
246 | struct ctrl_volt_volt_rail_list *prail_list) | ||
247 | { | ||
248 | struct nv_pmu_volt_rpc rpc_call = { 0 }; | ||
249 | u32 status = 0; | ||
250 | |||
251 | /* Set RPC parameters. */ | ||
252 | rpc_call.function = NV_PMU_VOLT_RPC_ID_VOLT_RAIL_SET_NOISE_UNAWARE_VMIN; | ||
253 | rpc_call.params.volt_rail_set_noise_unaware_vmin.num_rails = | ||
254 | prail_list->num_rails; | ||
255 | memcpy(&rpc_call.params.volt_rail_set_noise_unaware_vmin.rail_list, | ||
256 | prail_list, (sizeof(struct ctrl_volt_volt_rail_list))); | ||
257 | |||
258 | /* Execute the voltage change request via PMU RPC. */ | ||
259 | status = volt_pmu_rpc_execute(g, &rpc_call); | ||
260 | if (status) { | ||
261 | nvgpu_err(g, | ||
262 | "Error while executing VOLT_POLICY_SET_VOLTAGE RPC"); | ||
263 | return -EINVAL; | ||
264 | } | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | int volt_set_noiseaware_vmin(struct gk20a *g, u32 logic_voltage_uv, | ||
270 | u32 sram_voltage_uv) | ||
271 | { | ||
272 | int status = 0; | ||
273 | struct ctrl_volt_volt_rail_list rail_list = { 0 }; | ||
274 | |||
275 | rail_list.num_rails = RAIL_COUNT; | ||
276 | rail_list.rails[0].rail_idx = 0; | ||
277 | rail_list.rails[0].voltage_uv = logic_voltage_uv; | ||
278 | rail_list.rails[1].rail_idx = 1; | ||
279 | rail_list.rails[1].voltage_uv = sram_voltage_uv; | ||
280 | |||
281 | status = volt_policy_set_noiseaware_vmin(g, &rail_list); | ||
282 | |||
283 | return status; | ||
284 | |||
285 | } | ||
286 | |||