Commit 9835f1b70bb3890d38308b9be4fb9d7451ba67f1

Authored by Linus Walleij
Committed by Lee Jones
1 parent 0d3a7cce3e

mfd: qcom_rpm: Fix offset error for msm8660

The RPM in MSM8660/APQ8060 has different offsets to the selector
ACK and request context ACK registers. Make all these register
offsets part of the per-SoC data and assign the right values.

The bug was found by verifying backwards to the vendor tree in
the out-of-tree files <mach/rpm-[8660|8064|8960]>: all were using
offsets 3,11,15,23 and a select size of 4, except the MSM8660/APQ8060
which was using offsets 3,11,19,27 and a select size of 7.

All other platforms apart from msm8660 were affected by reading
excess registers, since 7 was hardcoded as the number of select
words, this patch makes also this part dynamic so we only write/read
as many select words as the platform actually use.

Symptoms of this bug when using msm8660: the first RPM transaction
would work, but the next would stall or raise an error since the
previous transaction was not properly ACKed as the ACK words were
read at the wrong offset.

Cc: stable@vger.kernel.org
Fixes: 58e214382bdd ("mfd: qcom-rpm: Driver for the Qualcomm RPM")
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Björn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

Showing 1 changed file with 36 additions and 14 deletions Side-by-side Diff

drivers/mfd/qcom_rpm.c
... ... @@ -34,7 +34,12 @@
34 34 struct qcom_rpm_data {
35 35 u32 version;
36 36 const struct qcom_rpm_resource *resource_table;
37   - unsigned n_resources;
  37 + unsigned int n_resources;
  38 + unsigned int req_ctx_off;
  39 + unsigned int req_sel_off;
  40 + unsigned int ack_ctx_off;
  41 + unsigned int ack_sel_off;
  42 + unsigned int sel_size;
38 43 };
39 44  
40 45 struct qcom_rpm {
... ... @@ -61,11 +66,7 @@
61 66  
62 67 #define RPM_REQUEST_TIMEOUT (5 * HZ)
63 68  
64   -#define RPM_REQUEST_CONTEXT 3
65   -#define RPM_REQ_SELECT 11
66   -#define RPM_ACK_CONTEXT 15
67   -#define RPM_ACK_SELECTOR 23
68   -#define RPM_SELECT_SIZE 7
  69 +#define RPM_MAX_SEL_SIZE 7
69 70  
70 71 #define RPM_NOTIFICATION BIT(30)
71 72 #define RPM_REJECTED BIT(31)
... ... @@ -157,6 +158,11 @@
157 158 .version = 3,
158 159 .resource_table = apq8064_rpm_resource_table,
159 160 .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table),
  161 + .req_ctx_off = 3,
  162 + .req_sel_off = 11,
  163 + .ack_ctx_off = 15,
  164 + .ack_sel_off = 23,
  165 + .sel_size = 4,
160 166 };
161 167  
162 168 static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = {
... ... @@ -240,6 +246,11 @@
240 246 .version = 2,
241 247 .resource_table = msm8660_rpm_resource_table,
242 248 .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table),
  249 + .req_ctx_off = 3,
  250 + .req_sel_off = 11,
  251 + .ack_ctx_off = 19,
  252 + .ack_sel_off = 27,
  253 + .sel_size = 7,
243 254 };
244 255  
245 256 static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = {
... ... @@ -322,6 +333,11 @@
322 333 .version = 3,
323 334 .resource_table = msm8960_rpm_resource_table,
324 335 .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
  336 + .req_ctx_off = 3,
  337 + .req_sel_off = 11,
  338 + .ack_ctx_off = 15,
  339 + .ack_sel_off = 23,
  340 + .sel_size = 4,
325 341 };
326 342  
327 343 static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = {
... ... @@ -362,6 +378,11 @@
362 378 .version = 3,
363 379 .resource_table = ipq806x_rpm_resource_table,
364 380 .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table),
  381 + .req_ctx_off = 3,
  382 + .req_sel_off = 11,
  383 + .ack_ctx_off = 15,
  384 + .ack_sel_off = 23,
  385 + .sel_size = 4,
365 386 };
366 387  
367 388 static const struct of_device_id qcom_rpm_of_match[] = {
... ... @@ -380,7 +401,7 @@
380 401 {
381 402 const struct qcom_rpm_resource *res;
382 403 const struct qcom_rpm_data *data = rpm->data;
383   - u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
  404 + u32 sel_mask[RPM_MAX_SEL_SIZE] = { 0 };
384 405 int left;
385 406 int ret = 0;
386 407 int i;
387 408  
388 409  
... ... @@ -398,12 +419,12 @@
398 419 writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i));
399 420  
400 421 bitmap_set((unsigned long *)sel_mask, res->select_id, 1);
401   - for (i = 0; i < ARRAY_SIZE(sel_mask); i++) {
  422 + for (i = 0; i < rpm->data->sel_size; i++) {
402 423 writel_relaxed(sel_mask[i],
403   - RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i));
  424 + RPM_CTRL_REG(rpm, rpm->data->req_sel_off + i));
404 425 }
405 426  
406   - writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));
  427 + writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, rpm->data->req_ctx_off));
407 428  
408 429 reinit_completion(&rpm->ack);
409 430 regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
... ... @@ -426,10 +447,11 @@
426 447 u32 ack;
427 448 int i;
428 449  
429   - ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
430   - for (i = 0; i < RPM_SELECT_SIZE; i++)
431   - writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i));
432   - writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
  450 + ack = readl_relaxed(RPM_CTRL_REG(rpm, rpm->data->ack_ctx_off));
  451 + for (i = 0; i < rpm->data->sel_size; i++)
  452 + writel_relaxed(0,
  453 + RPM_CTRL_REG(rpm, rpm->data->ack_sel_off + i));
  454 + writel(0, RPM_CTRL_REG(rpm, rpm->data->ack_ctx_off));
433 455  
434 456 if (ack & RPM_NOTIFICATION) {
435 457 dev_warn(rpm->dev, "ignoring notification!\n");