| .. | .. |
|---|
| 42 | 42 | */ |
|---|
| 43 | 43 | |
|---|
| 44 | 44 | #include <linux/cpufreq.h> |
|---|
| 45 | +#include <linux/delay.h> |
|---|
| 45 | 46 | #include <linux/interrupt.h> |
|---|
| 46 | 47 | #include <linux/io.h> |
|---|
| 47 | 48 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 178 | 179 | struct completion done; |
|---|
| 179 | 180 | struct semaphore sem; |
|---|
| 180 | 181 | struct pmap pmap; |
|---|
| 182 | + int host_irq; |
|---|
| 181 | 183 | }; |
|---|
| 182 | 184 | |
|---|
| 183 | 185 | static void __iomem *__map_region(const char *name) |
|---|
| .. | .. |
|---|
| 195 | 197 | return ptr; |
|---|
| 196 | 198 | } |
|---|
| 197 | 199 | |
|---|
| 198 | | -static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, |
|---|
| 200 | +static unsigned long wait_for_avs_command(struct private_data *priv, |
|---|
| 201 | + unsigned long timeout) |
|---|
| 202 | +{ |
|---|
| 203 | + unsigned long time_left = 0; |
|---|
| 204 | + u32 val; |
|---|
| 205 | + |
|---|
| 206 | + /* Event driven, wait for the command interrupt */ |
|---|
| 207 | + if (priv->host_irq >= 0) |
|---|
| 208 | + return wait_for_completion_timeout(&priv->done, |
|---|
| 209 | + msecs_to_jiffies(timeout)); |
|---|
| 210 | + |
|---|
| 211 | + /* Polling for command completion */ |
|---|
| 212 | + do { |
|---|
| 213 | + time_left = timeout; |
|---|
| 214 | + val = readl(priv->base + AVS_MBOX_STATUS); |
|---|
| 215 | + if (val) |
|---|
| 216 | + break; |
|---|
| 217 | + |
|---|
| 218 | + usleep_range(1000, 2000); |
|---|
| 219 | + } while (--timeout); |
|---|
| 220 | + |
|---|
| 221 | + return time_left; |
|---|
| 222 | +} |
|---|
| 223 | + |
|---|
| 224 | +static int __issue_avs_command(struct private_data *priv, unsigned int cmd, |
|---|
| 225 | + unsigned int num_in, unsigned int num_out, |
|---|
| 199 | 226 | u32 args[]) |
|---|
| 200 | 227 | { |
|---|
| 201 | | - unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT); |
|---|
| 202 | 228 | void __iomem *base = priv->base; |
|---|
| 229 | + unsigned long time_left; |
|---|
| 203 | 230 | unsigned int i; |
|---|
| 204 | 231 | int ret; |
|---|
| 205 | 232 | u32 val; |
|---|
| .. | .. |
|---|
| 225 | 252 | /* Clear status before we begin. */ |
|---|
| 226 | 253 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); |
|---|
| 227 | 254 | |
|---|
| 228 | | - /* We need to send arguments for this command. */ |
|---|
| 229 | | - if (args && is_send) { |
|---|
| 230 | | - for (i = 0; i < AVS_MAX_CMD_ARGS; i++) |
|---|
| 231 | | - writel(args[i], base + AVS_MBOX_PARAM(i)); |
|---|
| 232 | | - } |
|---|
| 255 | + /* Provide input parameters */ |
|---|
| 256 | + for (i = 0; i < num_in; i++) |
|---|
| 257 | + writel(args[i], base + AVS_MBOX_PARAM(i)); |
|---|
| 233 | 258 | |
|---|
| 234 | 259 | /* Protect from spurious interrupts. */ |
|---|
| 235 | 260 | reinit_completion(&priv->done); |
|---|
| .. | .. |
|---|
| 239 | 264 | writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0); |
|---|
| 240 | 265 | |
|---|
| 241 | 266 | /* Wait for AVS co-processor to finish processing the command. */ |
|---|
| 242 | | - time_left = wait_for_completion_timeout(&priv->done, time_left); |
|---|
| 267 | + time_left = wait_for_avs_command(priv, AVS_TIMEOUT); |
|---|
| 243 | 268 | |
|---|
| 244 | 269 | /* |
|---|
| 245 | 270 | * If the AVS status is not in the expected range, it means AVS didn't |
|---|
| .. | .. |
|---|
| 256 | 281 | goto out; |
|---|
| 257 | 282 | } |
|---|
| 258 | 283 | |
|---|
| 259 | | - /* This command returned arguments, so we read them back. */ |
|---|
| 260 | | - if (args && !is_send) { |
|---|
| 261 | | - for (i = 0; i < AVS_MAX_CMD_ARGS; i++) |
|---|
| 262 | | - args[i] = readl(base + AVS_MBOX_PARAM(i)); |
|---|
| 263 | | - } |
|---|
| 284 | + /* Process returned values */ |
|---|
| 285 | + for (i = 0; i < num_out; i++) |
|---|
| 286 | + args[i] = readl(base + AVS_MBOX_PARAM(i)); |
|---|
| 264 | 287 | |
|---|
| 265 | 288 | /* Clear status to tell AVS co-processor we are done. */ |
|---|
| 266 | 289 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); |
|---|
| .. | .. |
|---|
| 338 | 361 | u32 args[AVS_MAX_CMD_ARGS]; |
|---|
| 339 | 362 | int ret; |
|---|
| 340 | 363 | |
|---|
| 341 | | - ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, false, args); |
|---|
| 364 | + ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args); |
|---|
| 342 | 365 | if (ret || !pmap) |
|---|
| 343 | 366 | return ret; |
|---|
| 344 | 367 | |
|---|
| .. | .. |
|---|
| 359 | 382 | args[2] = pmap->p2; |
|---|
| 360 | 383 | args[3] = pmap->state; |
|---|
| 361 | 384 | |
|---|
| 362 | | - return __issue_avs_command(priv, AVS_CMD_SET_PMAP, true, args); |
|---|
| 385 | + return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args); |
|---|
| 363 | 386 | } |
|---|
| 364 | 387 | |
|---|
| 365 | 388 | static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) |
|---|
| .. | .. |
|---|
| 367 | 390 | u32 args[AVS_MAX_CMD_ARGS]; |
|---|
| 368 | 391 | int ret; |
|---|
| 369 | 392 | |
|---|
| 370 | | - ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, false, args); |
|---|
| 393 | + ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args); |
|---|
| 371 | 394 | if (ret) |
|---|
| 372 | 395 | return ret; |
|---|
| 373 | 396 | *pstate = args[0]; |
|---|
| .. | .. |
|---|
| 381 | 404 | |
|---|
| 382 | 405 | args[0] = pstate; |
|---|
| 383 | 406 | |
|---|
| 384 | | - return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, true, args); |
|---|
| 407 | + return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args); |
|---|
| 408 | + |
|---|
| 385 | 409 | } |
|---|
| 386 | 410 | |
|---|
| 387 | 411 | static u32 brcm_avs_get_voltage(void __iomem *base) |
|---|
| .. | .. |
|---|
| 410 | 434 | if (ret) |
|---|
| 411 | 435 | return ERR_PTR(ret); |
|---|
| 412 | 436 | |
|---|
| 413 | | - table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1, sizeof(*table), |
|---|
| 437 | + /* |
|---|
| 438 | + * We allocate space for the 5 different P-STATES AVS, |
|---|
| 439 | + * plus extra space for a terminating element. |
|---|
| 440 | + */ |
|---|
| 441 | + table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1 + 1, sizeof(*table), |
|---|
| 414 | 442 | GFP_KERNEL); |
|---|
| 415 | 443 | if (!table) |
|---|
| 416 | 444 | return ERR_PTR(-ENOMEM); |
|---|
| .. | .. |
|---|
| 455 | 483 | struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); |
|---|
| 456 | 484 | struct private_data *priv = policy->driver_data; |
|---|
| 457 | 485 | |
|---|
| 486 | + cpufreq_cpu_put(policy); |
|---|
| 487 | + |
|---|
| 458 | 488 | return brcm_avs_get_frequency(priv->base); |
|---|
| 459 | 489 | } |
|---|
| 460 | 490 | |
|---|
| .. | .. |
|---|
| 480 | 510 | * AVS co-processor, not necessarily the P-state we are running at now. |
|---|
| 481 | 511 | * So, we get the current P-state explicitly. |
|---|
| 482 | 512 | */ |
|---|
| 483 | | - return brcm_avs_get_pstate(priv, &priv->pmap.state); |
|---|
| 513 | + ret = brcm_avs_get_pstate(priv, &priv->pmap.state); |
|---|
| 514 | + if (ret) |
|---|
| 515 | + return ret; |
|---|
| 516 | + |
|---|
| 517 | + /* This is best effort. Nothing to do if it fails. */ |
|---|
| 518 | + (void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL); |
|---|
| 519 | + |
|---|
| 520 | + return 0; |
|---|
| 484 | 521 | } |
|---|
| 485 | 522 | |
|---|
| 486 | 523 | static int brcm_avs_resume(struct cpufreq_policy *policy) |
|---|
| 487 | 524 | { |
|---|
| 488 | 525 | struct private_data *priv = policy->driver_data; |
|---|
| 489 | 526 | int ret; |
|---|
| 527 | + |
|---|
| 528 | + /* This is best effort. Nothing to do if it fails. */ |
|---|
| 529 | + (void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL); |
|---|
| 490 | 530 | |
|---|
| 491 | 531 | ret = brcm_avs_set_pmap(priv, &priv->pmap); |
|---|
| 492 | 532 | if (ret == -EEXIST) { |
|---|
| .. | .. |
|---|
| 509 | 549 | { |
|---|
| 510 | 550 | struct private_data *priv; |
|---|
| 511 | 551 | struct device *dev; |
|---|
| 512 | | - int host_irq, ret; |
|---|
| 552 | + int ret; |
|---|
| 513 | 553 | |
|---|
| 514 | 554 | dev = &pdev->dev; |
|---|
| 515 | 555 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 536 | 576 | goto unmap_base; |
|---|
| 537 | 577 | } |
|---|
| 538 | 578 | |
|---|
| 539 | | - host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); |
|---|
| 540 | | - if (host_irq < 0) { |
|---|
| 541 | | - dev_err(dev, "Couldn't find interrupt %s -- %d\n", |
|---|
| 542 | | - BRCM_AVS_HOST_INTR, host_irq); |
|---|
| 543 | | - ret = host_irq; |
|---|
| 544 | | - goto unmap_intr_base; |
|---|
| 545 | | - } |
|---|
| 579 | + priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); |
|---|
| 546 | 580 | |
|---|
| 547 | | - ret = devm_request_irq(dev, host_irq, irq_handler, IRQF_TRIGGER_RISING, |
|---|
| 581 | + ret = devm_request_irq(dev, priv->host_irq, irq_handler, |
|---|
| 582 | + IRQF_TRIGGER_RISING, |
|---|
| 548 | 583 | BRCM_AVS_HOST_INTR, priv); |
|---|
| 549 | | - if (ret) { |
|---|
| 584 | + if (ret && priv->host_irq >= 0) { |
|---|
| 550 | 585 | dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", |
|---|
| 551 | | - BRCM_AVS_HOST_INTR, host_irq, ret); |
|---|
| 586 | + BRCM_AVS_HOST_INTR, priv->host_irq, ret); |
|---|
| 552 | 587 | goto unmap_intr_base; |
|---|
| 553 | 588 | } |
|---|
| 554 | 589 | |
|---|
| .. | .. |
|---|
| 601 | 636 | /* All cores share the same clock and thus the same policy. */ |
|---|
| 602 | 637 | cpumask_setall(policy->cpus); |
|---|
| 603 | 638 | |
|---|
| 604 | | - ret = __issue_avs_command(priv, AVS_CMD_ENABLE, false, NULL); |
|---|
| 639 | + ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL); |
|---|
| 605 | 640 | if (!ret) { |
|---|
| 606 | 641 | unsigned int pstate; |
|---|
| 607 | 642 | |
|---|