diff options
Diffstat (limited to 'drivers/acpi/cppc_acpi.c')
-rw-r--r-- | drivers/acpi/cppc_acpi.c | 66 |
1 files changed, 31 insertions, 35 deletions
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 80c123fbfdcf..ed58883d35ee 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c | |||
@@ -54,6 +54,7 @@ struct cppc_pcc_data { | |||
54 | unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; | 54 | unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; |
55 | 55 | ||
56 | bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ | 56 | bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ |
57 | bool platform_owns_pcc; /* Ownership of PCC subspace */ | ||
57 | unsigned int pcc_write_cnt; /* Running count of PCC write commands */ | 58 | unsigned int pcc_write_cnt; /* Running count of PCC write commands */ |
58 | 59 | ||
59 | /* | 60 | /* |
@@ -79,6 +80,7 @@ struct cppc_pcc_data { | |||
79 | /* Structure to represent the single PCC channel */ | 80 | /* Structure to represent the single PCC channel */ |
80 | static struct cppc_pcc_data pcc_data = { | 81 | static struct cppc_pcc_data pcc_data = { |
81 | .pcc_subspace_idx = -1, | 82 | .pcc_subspace_idx = -1, |
83 | .platform_owns_pcc = true, | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | /* | 86 | /* |
@@ -181,12 +183,15 @@ static struct kobj_type cppc_ktype = { | |||
181 | .default_attrs = cppc_attrs, | 183 | .default_attrs = cppc_attrs, |
182 | }; | 184 | }; |
183 | 185 | ||
184 | static int check_pcc_chan(void) | 186 | static int check_pcc_chan(bool chk_err_bit) |
185 | { | 187 | { |
186 | int ret = -EIO; | 188 | int ret = -EIO, status = 0; |
187 | struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr; | 189 | struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr; |
188 | ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline); | 190 | ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline); |
189 | 191 | ||
192 | if (!pcc_data.platform_owns_pcc) | ||
193 | return 0; | ||
194 | |||
190 | /* Retry in case the remote processor was too slow to catch up. */ | 195 | /* Retry in case the remote processor was too slow to catch up. */ |
191 | while (!ktime_after(ktime_get(), next_deadline)) { | 196 | while (!ktime_after(ktime_get(), next_deadline)) { |
192 | /* | 197 | /* |
@@ -194,8 +199,11 @@ static int check_pcc_chan(void) | |||
194 | * platform and should have set the command completion bit when | 199 | * platform and should have set the command completion bit when |
195 | * PCC can be used by OSPM | 200 | * PCC can be used by OSPM |
196 | */ | 201 | */ |
197 | if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) { | 202 | status = readw_relaxed(&generic_comm_base->status); |
203 | if (status & PCC_CMD_COMPLETE_MASK) { | ||
198 | ret = 0; | 204 | ret = 0; |
205 | if (chk_err_bit && (status & PCC_ERROR_MASK)) | ||
206 | ret = -EIO; | ||
199 | break; | 207 | break; |
200 | } | 208 | } |
201 | /* | 209 | /* |
@@ -205,6 +213,11 @@ static int check_pcc_chan(void) | |||
205 | udelay(3); | 213 | udelay(3); |
206 | } | 214 | } |
207 | 215 | ||
216 | if (likely(!ret)) | ||
217 | pcc_data.platform_owns_pcc = false; | ||
218 | else | ||
219 | pr_err("PCC check channel failed. Status=%x\n", status); | ||
220 | |||
208 | return ret; | 221 | return ret; |
209 | } | 222 | } |
210 | 223 | ||
@@ -234,7 +247,7 @@ static int send_pcc_cmd(u16 cmd) | |||
234 | if (pcc_data.pending_pcc_write_cmd) | 247 | if (pcc_data.pending_pcc_write_cmd) |
235 | send_pcc_cmd(CMD_WRITE); | 248 | send_pcc_cmd(CMD_WRITE); |
236 | 249 | ||
237 | ret = check_pcc_chan(); | 250 | ret = check_pcc_chan(false); |
238 | if (ret) | 251 | if (ret) |
239 | goto end; | 252 | goto end; |
240 | } else /* CMD_WRITE */ | 253 | } else /* CMD_WRITE */ |
@@ -282,6 +295,8 @@ static int send_pcc_cmd(u16 cmd) | |||
282 | /* Flip CMD COMPLETE bit */ | 295 | /* Flip CMD COMPLETE bit */ |
283 | writew_relaxed(0, &generic_comm_base->status); | 296 | writew_relaxed(0, &generic_comm_base->status); |
284 | 297 | ||
298 | pcc_data.platform_owns_pcc = true; | ||
299 | |||
285 | /* Ring doorbell */ | 300 | /* Ring doorbell */ |
286 | ret = mbox_send_message(pcc_data.pcc_channel, &cmd); | 301 | ret = mbox_send_message(pcc_data.pcc_channel, &cmd); |
287 | if (ret < 0) { | 302 | if (ret < 0) { |
@@ -290,23 +305,11 @@ static int send_pcc_cmd(u16 cmd) | |||
290 | goto end; | 305 | goto end; |
291 | } | 306 | } |
292 | 307 | ||
293 | /* | 308 | /* wait for completion and check for PCC errro bit */ |
294 | * For READs we need to ensure the cmd completed to ensure | 309 | ret = check_pcc_chan(true); |
295 | * the ensuing read()s can proceed. For WRITEs we dont care | 310 | |
296 | * because the actual write()s are done before coming here | 311 | if (pcc_data.pcc_mrtt) |
297 | * and the next READ or WRITE will check if the channel | 312 | last_cmd_cmpl_time = ktime_get(); |
298 | * is busy/free at the entry of this call. | ||
299 | * | ||
300 | * If Minimum Request Turnaround Time is non-zero, we need | ||
301 | * to record the completion time of both READ and WRITE | ||
302 | * command for proper handling of MRTT, so we need to check | ||
303 | * for pcc_mrtt in addition to CMD_READ | ||
304 | */ | ||
305 | if (cmd == CMD_READ || pcc_data.pcc_mrtt) { | ||
306 | ret = check_pcc_chan(); | ||
307 | if (pcc_data.pcc_mrtt) | ||
308 | last_cmd_cmpl_time = ktime_get(); | ||
309 | } | ||
310 | 313 | ||
311 | mbox_client_txdone(pcc_data.pcc_channel, ret); | 314 | mbox_client_txdone(pcc_data.pcc_channel, ret); |
312 | 315 | ||
@@ -1059,25 +1062,18 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) | |||
1059 | */ | 1062 | */ |
1060 | if (CPC_IN_PCC(desired_reg)) { | 1063 | if (CPC_IN_PCC(desired_reg)) { |
1061 | down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */ | 1064 | down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */ |
1062 | /* | 1065 | if (pcc_data.platform_owns_pcc) { |
1063 | * If there are pending write commands i.e pending_pcc_write_cmd | 1066 | ret = check_pcc_chan(false); |
1064 | * is TRUE, then we know OSPM owns the channel as another CPU | ||
1065 | * has already checked for command completion bit and updated | ||
1066 | * the corresponding CPC registers | ||
1067 | */ | ||
1068 | if (!pcc_data.pending_pcc_write_cmd) { | ||
1069 | ret = check_pcc_chan(); | ||
1070 | if (ret) { | 1067 | if (ret) { |
1071 | up_read(&pcc_data.pcc_lock); | 1068 | up_read(&pcc_data.pcc_lock); |
1072 | return ret; | 1069 | return ret; |
1073 | } | 1070 | } |
1074 | /* | ||
1075 | * Update the pending_write to make sure a PCC CMD_READ | ||
1076 | * will not arrive and steal the channel during the | ||
1077 | * transition to write lock | ||
1078 | */ | ||
1079 | pcc_data.pending_pcc_write_cmd = TRUE; | ||
1080 | } | 1071 | } |
1072 | /* | ||
1073 | * Update the pending_write to make sure a PCC CMD_READ will not | ||
1074 | * arrive and steal the channel during the switch to write lock | ||
1075 | */ | ||
1076 | pcc_data.pending_pcc_write_cmd = true; | ||
1081 | cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt; | 1077 | cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt; |
1082 | cpc_desc->write_cmd_status = 0; | 1078 | cpc_desc->write_cmd_status = 0; |
1083 | } | 1079 | } |