diff options
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 112 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 3 |
4 files changed, 56 insertions, 65 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index f804cfe7407c..b3bc2a5c28e6 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c | |||
| @@ -10,6 +10,24 @@ | |||
| 10 | #include "a6xx_gpu.h" | 10 | #include "a6xx_gpu.h" |
| 11 | #include "a6xx_gmu.xml.h" | 11 | #include "a6xx_gmu.xml.h" |
| 12 | 12 | ||
| 13 | static void a6xx_gmu_fault(struct a6xx_gmu *gmu) | ||
| 14 | { | ||
| 15 | struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); | ||
| 16 | struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; | ||
| 17 | struct msm_gpu *gpu = &adreno_gpu->base; | ||
| 18 | struct drm_device *dev = gpu->dev; | ||
| 19 | struct msm_drm_private *priv = dev->dev_private; | ||
| 20 | |||
| 21 | /* FIXME: add a banner here */ | ||
| 22 | gmu->hung = true; | ||
| 23 | |||
| 24 | /* Turn off the hangcheck timer while we are resetting */ | ||
| 25 | del_timer(&gpu->hangcheck_timer); | ||
| 26 | |||
| 27 | /* Queue the GPU handler because we need to treat this as a recovery */ | ||
| 28 | queue_work(priv->wq, &gpu->recover_work); | ||
| 29 | } | ||
| 30 | |||
| 13 | static irqreturn_t a6xx_gmu_irq(int irq, void *data) | 31 | static irqreturn_t a6xx_gmu_irq(int irq, void *data) |
| 14 | { | 32 | { |
| 15 | struct a6xx_gmu *gmu = data; | 33 | struct a6xx_gmu *gmu = data; |
| @@ -21,8 +39,7 @@ static irqreturn_t a6xx_gmu_irq(int irq, void *data) | |||
| 21 | if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) { | 39 | if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) { |
| 22 | dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n"); | 40 | dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n"); |
| 23 | 41 | ||
| 24 | /* Temporary until we can recover safely */ | 42 | a6xx_gmu_fault(gmu); |
| 25 | BUG(); | ||
| 26 | } | 43 | } |
| 27 | 44 | ||
| 28 | if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR) | 45 | if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR) |
| @@ -46,8 +63,7 @@ static irqreturn_t a6xx_hfi_irq(int irq, void *data) | |||
| 46 | if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) { | 63 | if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) { |
| 47 | dev_err_ratelimited(gmu->dev, "GMU firmware fault\n"); | 64 | dev_err_ratelimited(gmu->dev, "GMU firmware fault\n"); |
| 48 | 65 | ||
| 49 | /* Temporary until we can recover safely */ | 66 | a6xx_gmu_fault(gmu); |
| 50 | BUG(); | ||
| 51 | } | 67 | } |
| 52 | 68 | ||
| 53 | return IRQ_HANDLED; | 69 | return IRQ_HANDLED; |
| @@ -166,10 +182,8 @@ static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu) | |||
| 166 | } | 182 | } |
| 167 | 183 | ||
| 168 | /* Wait for the GMU to get to its most idle state */ | 184 | /* Wait for the GMU to get to its most idle state */ |
| 169 | int a6xx_gmu_wait_for_idle(struct a6xx_gpu *a6xx_gpu) | 185 | int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu) |
| 170 | { | 186 | { |
| 171 | struct a6xx_gmu *gmu = &a6xx_gpu->gmu; | ||
| 172 | |||
| 173 | return spin_until(a6xx_gmu_check_idle_level(gmu)); | 187 | return spin_until(a6xx_gmu_check_idle_level(gmu)); |
| 174 | } | 188 | } |
| 175 | 189 | ||
| @@ -568,7 +582,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) | |||
| 568 | if (!rpmh_init) { | 582 | if (!rpmh_init) { |
| 569 | a6xx_gmu_rpmh_init(gmu); | 583 | a6xx_gmu_rpmh_init(gmu); |
| 570 | rpmh_init = true; | 584 | rpmh_init = true; |
| 571 | } else if (state != GMU_RESET) { | 585 | } else { |
| 572 | ret = a6xx_rpmh_start(gmu); | 586 | ret = a6xx_rpmh_start(gmu); |
| 573 | if (ret) | 587 | if (ret) |
| 574 | return ret; | 588 | return ret; |
| @@ -657,10 +671,9 @@ static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) | |||
| 657 | gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0); | 671 | gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0); |
| 658 | } | 672 | } |
| 659 | 673 | ||
| 660 | int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu) | 674 | /* Force the GMU off in case it isn't responsive */ |
| 675 | static void a6xx_gmu_force_off(struct a6xx_gmu *gmu) | ||
| 661 | { | 676 | { |
| 662 | struct a6xx_gmu *gmu = &a6xx_gpu->gmu; | ||
| 663 | int ret; | ||
| 664 | u32 val; | 677 | u32 val; |
| 665 | 678 | ||
| 666 | /* Flush all the queues */ | 679 | /* Flush all the queues */ |
| @@ -681,44 +694,6 @@ int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu) | |||
| 681 | (val & 1), 100, 10000); | 694 | (val & 1), 100, 10000); |
| 682 | gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val, | 695 | gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val, |
| 683 | (val & 1), 100, 1000); | 696 | (val & 1), 100, 1000); |
| 684 | |||
| 685 | /* | ||
| 686 | * Depending on the state of the GMU at this point the GX domain might | ||
| 687 | * have been left on. Hardware sequencing rules state that the GX has to | ||
| 688 | * be turned off before the CX domain so this is that one time that | ||
| 689 | * that calling pm_runtime_put_sync() is expected to do something useful | ||
| 690 | * (turn off the headswitch) | ||
| 691 | */ | ||
| 692 | if (!IS_ERR(gmu->gxpd)) | ||
| 693 | pm_runtime_put_sync(gmu->gxpd); | ||
| 694 | |||
| 695 | /* Disable the resources */ | ||
| 696 | clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); | ||
| 697 | pm_runtime_put_sync(gmu->dev); | ||
| 698 | |||
| 699 | /* Re-enable the resources */ | ||
| 700 | pm_runtime_get_sync(gmu->dev); | ||
| 701 | |||
| 702 | /* Use a known rate to bring up the GMU */ | ||
| 703 | clk_set_rate(gmu->core_clk, 200000000); | ||
| 704 | ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); | ||
| 705 | if (ret) | ||
| 706 | goto out; | ||
| 707 | |||
| 708 | a6xx_gmu_irq_enable(gmu); | ||
| 709 | |||
| 710 | ret = a6xx_gmu_fw_start(gmu, GMU_RESET); | ||
| 711 | if (!ret) | ||
| 712 | ret = a6xx_hfi_start(gmu, GMU_COLD_BOOT); | ||
| 713 | |||
| 714 | /* Set the GPU back to the highest power frequency */ | ||
| 715 | __a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1); | ||
| 716 | |||
| 717 | out: | ||
| 718 | if (ret) | ||
| 719 | a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER); | ||
| 720 | |||
| 721 | return ret; | ||
| 722 | } | 697 | } |
| 723 | 698 | ||
| 724 | int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | 699 | int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) |
| @@ -731,6 +706,8 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | |||
| 731 | if (WARN(!gmu->mmio, "The GMU is not set up yet\n")) | 706 | if (WARN(!gmu->mmio, "The GMU is not set up yet\n")) |
| 732 | return 0; | 707 | return 0; |
| 733 | 708 | ||
| 709 | gmu->hung = false; | ||
| 710 | |||
| 734 | /* Turn on the resources */ | 711 | /* Turn on the resources */ |
| 735 | pm_runtime_get_sync(gmu->dev); | 712 | pm_runtime_get_sync(gmu->dev); |
| 736 | 713 | ||
| @@ -789,11 +766,9 @@ bool a6xx_gmu_isidle(struct a6xx_gmu *gmu) | |||
| 789 | return true; | 766 | return true; |
| 790 | } | 767 | } |
| 791 | 768 | ||
| 792 | int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) | 769 | /* Gracefully try to shut down the GMU and by extension the GPU */ |
| 770 | static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) | ||
| 793 | { | 771 | { |
| 794 | struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; | ||
| 795 | struct msm_gpu *gpu = &adreno_gpu->base; | ||
| 796 | struct a6xx_gmu *gmu = &a6xx_gpu->gmu; | ||
| 797 | u32 val; | 772 | u32 val; |
| 798 | 773 | ||
| 799 | /* | 774 | /* |
| @@ -803,10 +778,13 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) | |||
| 803 | val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); | 778 | val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); |
| 804 | 779 | ||
| 805 | if (val != 0xf) { | 780 | if (val != 0xf) { |
| 806 | int ret = a6xx_gmu_wait_for_idle(a6xx_gpu); | 781 | int ret = a6xx_gmu_wait_for_idle(gmu); |
| 807 | 782 | ||
| 808 | /* Temporary until we can recover safely */ | 783 | /* If the GMU isn't responding assume it is hung */ |
| 809 | BUG_ON(ret); | 784 | if (ret) { |
| 785 | a6xx_gmu_force_off(gmu); | ||
| 786 | return; | ||
| 787 | } | ||
| 810 | 788 | ||
| 811 | /* tell the GMU we want to slumber */ | 789 | /* tell the GMU we want to slumber */ |
| 812 | a6xx_gmu_notify_slumber(gmu); | 790 | a6xx_gmu_notify_slumber(gmu); |
| @@ -838,14 +816,30 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) | |||
| 838 | 816 | ||
| 839 | /* Tell RPMh to power off the GPU */ | 817 | /* Tell RPMh to power off the GPU */ |
| 840 | a6xx_rpmh_stop(gmu); | 818 | a6xx_rpmh_stop(gmu); |
| 819 | } | ||
| 820 | |||
| 821 | |||
| 822 | int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) | ||
| 823 | { | ||
| 824 | struct a6xx_gmu *gmu = &a6xx_gpu->gmu; | ||
| 825 | struct msm_gpu *gpu = &a6xx_gpu->base.base; | ||
| 826 | |||
| 827 | /* | ||
| 828 | * Force the GMU off if we detected a hang, otherwise try to shut it | ||
| 829 | * down gracefully | ||
| 830 | */ | ||
| 831 | if (gmu->hung) | ||
| 832 | a6xx_gmu_force_off(gmu); | ||
| 833 | else | ||
| 834 | a6xx_gmu_shutdown(gmu); | ||
| 841 | 835 | ||
| 842 | /* Remove the bus vote */ | 836 | /* Remove the bus vote */ |
| 843 | icc_set_bw(gpu->icc_path, 0, 0); | 837 | icc_set_bw(gpu->icc_path, 0, 0); |
| 844 | 838 | ||
| 845 | /* | 839 | /* |
| 846 | * Mark the GPU power domain as off. During the shutdown process the GMU | 840 | * Make sure the GX domain is off before turning off the GMU (CX) |
| 847 | * should actually turn off the power so this is really just a | 841 | * domain. Usually the GMU does this but only if the shutdown sequence |
| 848 | * houskeeping step | 842 | * was successful |
| 849 | */ | 843 | */ |
| 850 | if (!IS_ERR(gmu->gxpd)) | 844 | if (!IS_ERR(gmu->gxpd)) |
| 851 | pm_runtime_put_sync(gmu->gxpd); | 845 | pm_runtime_put_sync(gmu->gxpd); |
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h index 078d418c8179..c5b1887f259f 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h | |||
| @@ -27,9 +27,6 @@ struct a6xx_gmu_bo { | |||
| 27 | /* the GMU is coming up for the first time or back from a power collapse */ | 27 | /* the GMU is coming up for the first time or back from a power collapse */ |
| 28 | #define GMU_COLD_BOOT 1 | 28 | #define GMU_COLD_BOOT 1 |
| 29 | 29 | ||
| 30 | /* The GMU is being soft reset after a fault */ | ||
| 31 | #define GMU_RESET 2 | ||
| 32 | |||
| 33 | /* | 30 | /* |
| 34 | * These define the level of control that the GMU has - the higher the number | 31 | * These define the level of control that the GMU has - the higher the number |
| 35 | * the more things that the GMU hardware controls on its own. | 32 | * the more things that the GMU hardware controls on its own. |
| @@ -79,6 +76,7 @@ struct a6xx_gmu { | |||
| 79 | struct a6xx_hfi_queue queues[2]; | 76 | struct a6xx_hfi_queue queues[2]; |
| 80 | 77 | ||
| 81 | struct tasklet_struct hfi_tasklet; | 78 | struct tasklet_struct hfi_tasklet; |
| 79 | bool hung; | ||
| 82 | }; | 80 | }; |
| 83 | 81 | ||
| 84 | static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset) | 82 | static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset) |
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index fefe773c989e..f76d8cd06f93 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c | |||
| @@ -698,7 +698,7 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu) | |||
| 698 | * Make sure the GMU is idle before continuing (because some transitions | 698 | * Make sure the GMU is idle before continuing (because some transitions |
| 699 | * may use VBIF | 699 | * may use VBIF |
| 700 | */ | 700 | */ |
| 701 | a6xx_gmu_wait_for_idle(a6xx_gpu); | 701 | a6xx_gmu_wait_for_idle(&a6xx_gpu->gmu); |
| 702 | 702 | ||
| 703 | /* Clear the VBIF pipe before shutting down */ | 703 | /* Clear the VBIF pipe before shutting down */ |
| 704 | /* FIXME: This accesses the GPU - do we need to make sure it is on? */ | 704 | /* FIXME: This accesses the GPU - do we need to make sure it is on? */ |
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index 528a4cfe07cd..b46279eb18c5 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h | |||
| @@ -46,9 +46,8 @@ struct a6xx_gpu { | |||
| 46 | int a6xx_gmu_resume(struct a6xx_gpu *gpu); | 46 | int a6xx_gmu_resume(struct a6xx_gpu *gpu); |
| 47 | int a6xx_gmu_stop(struct a6xx_gpu *gpu); | 47 | int a6xx_gmu_stop(struct a6xx_gpu *gpu); |
| 48 | 48 | ||
| 49 | int a6xx_gmu_wait_for_idle(struct a6xx_gpu *gpu); | 49 | int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu); |
| 50 | 50 | ||
| 51 | int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu); | ||
| 52 | bool a6xx_gmu_isidle(struct a6xx_gmu *gmu); | 51 | bool a6xx_gmu_isidle(struct a6xx_gmu *gmu); |
| 53 | 52 | ||
| 54 | int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state); | 53 | int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state); |
