.. | .. |
---|
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 | |
---|