diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index b59779d910c7..2f1cc3f757a6 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -14,7 +14,6 @@ msm_kgsl-y = \ kgsl_pool.o \ kgsl_pwrctrl.o \ kgsl_pwrscale.o \ - kgsl_rgmu.o \ kgsl_sharedmem.o \ kgsl_snapshot.o \ kgsl_trace.o \ diff --git a/drivers/gpu/msm/adreno_a6xx_rgmu.c b/drivers/gpu/msm/adreno_a6xx_rgmu.c index af229d91f81d..74cd52cbb126 100644 --- a/drivers/gpu/msm/adreno_a6xx_rgmu.c +++ b/drivers/gpu/msm/adreno_a6xx_rgmu.c @@ -3,14 +3,23 @@ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ +#include +#include +#include #include +#include +#include +#include #include #include "adreno.h" #include "adreno_a6xx.h" +#include "adreno_a6xx_rgmu.h" #include "adreno_snapshot.h" -#include "kgsl_rgmu.h" #include "kgsl_trace.h" +#include "kgsl_util.h" + +#define RGMU_CLK_FREQ 200000000 /* RGMU timeouts */ #define RGMU_IDLE_TIMEOUT 100 /* ms */ @@ -31,10 +40,10 @@ static const unsigned int a6xx_rgmu_registers[] = { 0x26000, 0x26002, }; -irqreturn_t rgmu_irq_handler(int irq, void *data) +static irqreturn_t a6xx_rgmu_irq_handler(int irq, void *data) { struct kgsl_device *device = data; - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); unsigned int status = 0; gmu_core_regread(device, A6XX_GMU_AO_HOST_INTERRUPT_STATUS, &status); @@ -59,10 +68,10 @@ irqreturn_t rgmu_irq_handler(int irq, void *data) return IRQ_HANDLED; } -irqreturn_t oob_irq_handler(int irq, void *data) +static irqreturn_t a6xx_oob_irq_handler(int irq, void *data) { struct kgsl_device *device = data; - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); unsigned int status = 0; @@ -101,7 +110,7 @@ static const char *oob_to_str(enum oob_request req) static int a6xx_rgmu_oob_set(struct kgsl_device *device, enum oob_request req) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); int ret, set, check; set = BIT(req + 16); @@ -144,7 +153,7 @@ static void a6xx_rgmu_oob_clear(struct kgsl_device *device, static void a6xx_rgmu_bcl_config(struct kgsl_device *device, bool on) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); if (on) { /* Enable BCL CRC HW i/f */ @@ -171,7 +180,7 @@ static void a6xx_rgmu_bcl_config(struct kgsl_device *device, bool on) static void a6xx_rgmu_irq_enable(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); /* Clear pending IRQs and Unmask needed IRQs */ adreno_gmu_clear_and_unmask_irqs(ADRENO_DEVICE(device)); @@ -183,7 +192,7 @@ static void a6xx_rgmu_irq_enable(struct kgsl_device *device) static void a6xx_rgmu_irq_disable(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); /* Disable all IRQs on host */ disable_irq(rgmu->rgmu_interrupt_num); @@ -197,7 +206,7 @@ static int a6xx_rgmu_ifpc_store(struct kgsl_device *device, unsigned int val) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); unsigned int requested_idle_level; if (!ADRENO_FEATURE(adreno_dev, ADRENO_IFPC)) @@ -225,7 +234,7 @@ static int a6xx_rgmu_ifpc_store(struct kgsl_device *device, static unsigned int a6xx_rgmu_ifpc_show(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); return rgmu->idle_level == GPU_HW_IFPC; } @@ -256,7 +265,7 @@ static bool a6xx_rgmu_gx_is_on(struct kgsl_device *device) static int a6xx_rgmu_wait_for_lowest_idle(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); unsigned int reg[10] = {0}; unsigned long t; uint64_t ts1, ts2, ts3; @@ -344,7 +353,7 @@ static int a6xx_rgmu_wait_for_lowest_idle(struct kgsl_device *device) static int a6xx_rgmu_fw_start(struct kgsl_device *device, unsigned int boot_state) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); unsigned int status; int i; @@ -420,9 +429,9 @@ static int a6xx_rgmu_fw_start(struct kgsl_device *device, return 0; } -static int a6xx_rgmu_suspend(struct kgsl_device *device) +static int a6xx_rgmu_suspend_device(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); int ret = 0; /* Check GX GDSC is status */ @@ -467,7 +476,7 @@ static int a6xx_rgmu_gpu_pwrctrl(struct kgsl_device *device, ret = a6xx_rgmu_fw_start(device, arg1); break; case GMU_SUSPEND: - ret = a6xx_rgmu_suspend(device); + ret = a6xx_rgmu_suspend_device(device); break; case GMU_NOTIFY_SLUMBER: @@ -487,6 +496,178 @@ static int a6xx_rgmu_gpu_pwrctrl(struct kgsl_device *device, return ret; } +static void a6xx_rgmu_disable_clks(struct kgsl_device *device) +{ + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + int ret; + + /* Check GX GDSC is status */ + if (a6xx_rgmu_gx_is_on(device)) { + + if (IS_ERR_OR_NULL(rgmu->gx_gdsc)) + return; + + /* + * Switch gx gdsc control from RGMU to CPU. Force non-zero + * reference count in clk driver so next disable call will + * turn off the GDSC. + */ + ret = regulator_enable(rgmu->gx_gdsc); + if (ret) + dev_err(&rgmu->pdev->dev, + "Fail to enable gx gdsc:%d\n", ret); + + ret = regulator_disable(rgmu->gx_gdsc); + if (ret) + dev_err(&rgmu->pdev->dev, + "Fail to disable gx gdsc:%d\n", ret); + + if (a6xx_rgmu_gx_is_on(device)) + dev_err(&rgmu->pdev->dev, "gx is stuck on\n"); + } + + clk_bulk_disable_unprepare(rgmu->num_clks, rgmu->clks); + + clear_bit(GMU_CLK_ON, &device->gmu_core.flags); +} + +static int a6xx_rgmu_disable_gdsc(struct kgsl_device *device) +{ + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + + /* Wait up to 5 seconds for the regulator to go off */ + if (kgsl_regulator_disable_wait(rgmu->cx_gdsc, 5000)) + return 0; + + dev_err(&rgmu->pdev->dev, "RGMU CX gdsc off timeout\n"); + return -ETIMEDOUT; +} + + +static void a6xx_rgmu_halt_execution(struct kgsl_device *device); + +static void a6xx_rgmu_snapshot(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + + /* Mask so there's no interrupt caused by NMI */ + adreno_write_gmureg(adreno_dev, + ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF); + + /* Make sure the interrupt is masked */ + wmb(); + + /* + * Halt RGMU execution so that GX will not + * be collapsed while dumping snapshot. + */ + a6xx_rgmu_halt_execution(device); + + kgsl_device_snapshot(device, NULL, true); + + gmu_core_regwrite(device, A6XX_GMU_GMU2HOST_INTR_CLR, 0xffffffff); + gmu_core_regwrite(device, A6XX_GMU_GMU2HOST_INTR_MASK, + RGMU_OOB_IRQ_MASK); + + rgmu->fault_count++; +} + +static int a6xx_rgmu_suspend(struct kgsl_device *device) +{ + if (!test_bit(GMU_CLK_ON, &device->gmu_core.flags)) + return 0; + + a6xx_rgmu_irq_disable(device); + + if (a6xx_rgmu_gpu_pwrctrl(device, GMU_SUSPEND, 0, 0)) + return -EINVAL; + + a6xx_rgmu_disable_clks(device); + a6xx_rgmu_disable_gdsc(device); + return 0; +} + +static int a6xx_rgmu_enable_clks(struct kgsl_device *device) +{ + int ret; + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + ret = clk_set_rate(rgmu->rgmu_clk, RGMU_CLK_FREQ); + if (ret) { + dev_err(&rgmu->pdev->dev, "Couldn't set the RGMU clock\n"); + return ret; + } + + ret = clk_set_rate(rgmu->gpu_clk, + pwr->pwrlevels[pwr->default_pwrlevel].gpu_freq); + if (ret) { + dev_err(&rgmu->pdev->dev, "Couldn't set the GPU clock\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(rgmu->num_clks, rgmu->clks); + if (ret) { + dev_err(&rgmu->pdev->dev, "Failed to enable RGMU clocks\n"); + return ret; + } + + set_bit(GMU_CLK_ON, &device->gmu_core.flags); + return 0; +} + +static int a6xx_rgmu_enable_gdsc(struct a6xx_rgmu_device *rgmu) +{ + int ret; + + if (IS_ERR_OR_NULL(rgmu->cx_gdsc)) + return 0; + + ret = regulator_enable(rgmu->cx_gdsc); + if (ret) + dev_err(&rgmu->pdev->dev, + "Fail to enable CX gdsc:%d\n", ret); + + return ret; +} + +/* To be called to power on both GPU and RGMU */ +static int a6xx_rgmu_start(struct kgsl_device *device) +{ + int ret = 0; + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + + switch (device->state) { + case KGSL_STATE_RESET: + ret = a6xx_rgmu_suspend(device); + if (ret) + goto error_rgmu; + /* Fall-through */ + case KGSL_STATE_INIT: + case KGSL_STATE_SUSPEND: + case KGSL_STATE_SLUMBER: + a6xx_rgmu_enable_gdsc(rgmu); + a6xx_rgmu_enable_clks(device); + a6xx_rgmu_irq_enable(device); + ret = a6xx_rgmu_gpu_pwrctrl(device, + GMU_FW_START, GMU_COLD_BOOT, 0); + if (ret) + goto error_rgmu; + break; + } + /* Request default DCVS level */ + kgsl_pwrctrl_set_default_gpu_pwrlevel(device); + return 0; + +error_rgmu: + set_bit(GMU_FAULT, &device->gmu_core.flags); + a6xx_rgmu_snapshot(device); + return ret; +} + + + /* * a6xx_rgmu_load_firmware() - Load the ucode into the RGMU TCM * @device: Pointer to KGSL device @@ -495,7 +676,7 @@ static int a6xx_rgmu_load_firmware(struct kgsl_device *device) { const struct firmware *fw = NULL; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev); int ret; @@ -520,10 +701,15 @@ static int a6xx_rgmu_load_firmware(struct kgsl_device *device) return rgmu->fw_hostptr ? 0 : -ENOMEM; } +static int a6xx_rgmu_init(struct kgsl_device *device) +{ + return a6xx_rgmu_load_firmware(device); +} + /* Halt RGMU execution */ static void a6xx_rgmu_halt_execution(struct kgsl_device *device) { - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); unsigned int index, status, fence; gmu_core_regread(device, A6XX_RGMU_CX_PCC_DEBUG, &index); @@ -556,7 +742,7 @@ static void a6xx_rgmu_halt_execution(struct kgsl_device *device) * This is where all of the A6XX GMU specific bits and pieces are grabbed * into the snapshot memory */ -static void a6xx_rgmu_snapshot(struct kgsl_device *device, +static void a6xx_rgmu_device_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot) { @@ -569,7 +755,64 @@ static u64 a6xx_rgmu_read_alwayson(struct kgsl_device *device) return a6xx_read_alwayson(ADRENO_DEVICE(device)); } -struct gmu_dev_ops adreno_a6xx_rgmudev = { +/* Caller shall ensure GPU is ready for SLUMBER */ +static void a6xx_rgmu_stop(struct kgsl_device *device) +{ + if (!test_bit(GMU_CLK_ON, &device->gmu_core.flags)) + return; + + /* Wait for the lowest idle level we requested */ + if (a6xx_rgmu_wait_for_lowest_idle(device)) + goto error; + + a6xx_rgmu_gpu_pwrctrl(device, + GMU_NOTIFY_SLUMBER, 0, 0); + + a6xx_rgmu_irq_disable(device); + a6xx_rgmu_disable_clks(device); + a6xx_rgmu_disable_gdsc(device); + return; + +error: + + /* + * The power controller will change state to SLUMBER anyway + * Set GMU_FAULT flag to indicate to power contrller + * that hang recovery is needed to power on GPU + */ + set_bit(GMU_FAULT, &device->gmu_core.flags); + a6xx_rgmu_snapshot(device); +} + +/* + * a6xx_rgmu_dcvs_set() - Change GPU frequency and/or bandwidth. + * @rgmu: Pointer to RGMU device + * @pwrlevel: index to GPU DCVS table used by KGSL + * @bus_level: index to GPU bus table used by KGSL + * + * The function converts GPU power level and bus level index used by KGSL + * to index being used by GMU/RPMh. + */ +static int a6xx_rgmu_dcvs_set(struct kgsl_device *device, + int pwrlevel, int bus_level) +{ + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + int ret; + unsigned long rate; + + if (pwrlevel == INVALID_DCVS_IDX) + return -EINVAL; + + rate = device->pwrctrl.pwrlevels[pwrlevel].gpu_freq; + + ret = clk_set_rate(rgmu->gpu_clk, rate); + if (ret) + dev_err(&rgmu->pdev->dev, "Couldn't set the GPU clock\n"); + + return ret; +} + +static struct gmu_dev_ops a6xx_rgmudev = { .load_firmware = a6xx_rgmu_load_firmware, .oob_set = a6xx_rgmu_oob_set, .oob_clear = a6xx_rgmu_oob_clear, @@ -581,9 +824,207 @@ struct gmu_dev_ops adreno_a6xx_rgmudev = { .wait_for_lowest_idle = a6xx_rgmu_wait_for_lowest_idle, .ifpc_store = a6xx_rgmu_ifpc_store, .ifpc_show = a6xx_rgmu_ifpc_show, - .snapshot = a6xx_rgmu_snapshot, + .snapshot = a6xx_rgmu_device_snapshot, .halt_execution = a6xx_rgmu_halt_execution, .read_alwayson = a6xx_rgmu_read_alwayson, .gmu2host_intr_mask = RGMU_OOB_IRQ_MASK, .gmu_ao_intr_mask = RGMU_AO_IRQ_MASK, }; + +static struct gmu_core_ops a6xx_rgmu_ops = { + .init = a6xx_rgmu_init, + .start = a6xx_rgmu_start, + .stop = a6xx_rgmu_stop, + .dcvs_set = a6xx_rgmu_dcvs_set, + .snapshot = a6xx_rgmu_snapshot, + .suspend = a6xx_rgmu_suspend, +}; + +static int a6xx_rgmu_irq_probe(struct kgsl_device *device) +{ + struct a6xx_rgmu_device *rgmu = A6XX_RGMU_DEVICE(device); + int ret; + + ret = kgsl_request_irq(rgmu->pdev, "kgsl_oob", + a6xx_oob_irq_handler, device); + if (ret < 0) + return ret; + + rgmu->oob_interrupt_num = ret; + + ret = kgsl_request_irq(rgmu->pdev, + "kgsl_rgmu", a6xx_rgmu_irq_handler, device); + if (ret < 0) + return ret; + + rgmu->rgmu_interrupt_num = ret; + return 0; +} + +static int a6xx_rgmu_regulators_probe(struct a6xx_rgmu_device *rgmu) +{ + int ret = 0; + + rgmu->cx_gdsc = devm_regulator_get(&rgmu->pdev->dev, "vddcx"); + if (IS_ERR(rgmu->cx_gdsc)) { + ret = PTR_ERR(rgmu->cx_gdsc); + if (ret != -EPROBE_DEFER) + dev_err(&rgmu->pdev->dev, + "Couldn't get CX gdsc error:%d\n", ret); + return ret; + } + + rgmu->gx_gdsc = devm_regulator_get(&rgmu->pdev->dev, "vdd"); + if (IS_ERR(rgmu->gx_gdsc)) { + ret = PTR_ERR(rgmu->gx_gdsc); + if (ret != -EPROBE_DEFER) + dev_err(&rgmu->pdev->dev, + "Couldn't get GX gdsc error:%d\n", ret); + } + + return ret; +} + +static int a6xx_rgmu_clocks_probe(struct a6xx_rgmu_device *rgmu, + struct device_node *node) +{ + int ret; + + ret = devm_clk_bulk_get_all(&rgmu->pdev->dev, &rgmu->clks); + if (ret < 0) + return ret; + + rgmu->num_clks = ret; + + rgmu->gpu_clk = kgsl_of_clk_by_name(rgmu->clks, ret, "core"); + if (!rgmu->gpu_clk) { + dev_err(&rgmu->pdev->dev, "The GPU clock isn't defined\n"); + return -ENODEV; + } + + rgmu->rgmu_clk = kgsl_of_clk_by_name(rgmu->clks, ret, "gmu"); + if (!rgmu->rgmu_clk) { + dev_err(&rgmu->pdev->dev, "The RGMU clock isn't defined\n"); + return -ENODEV; + } + + return 0; +} + +/* Do not access any RGMU registers in RGMU probe function */ +static int a6xx_rgmu_probe(struct kgsl_device *device, + struct platform_device *pdev) +{ + struct a6xx_rgmu_device *rgmu; + struct resource *res; + int ret = -ENXIO; + + rgmu = devm_kzalloc(&pdev->dev, sizeof(*rgmu), GFP_KERNEL); + + if (rgmu == NULL) + return -ENOMEM; + + rgmu->pdev = pdev; + + /* Set up RGMU regulators */ + ret = a6xx_rgmu_regulators_probe(rgmu); + if (ret) + return ret; + + /* Set up RGMU clocks */ + ret = a6xx_rgmu_clocks_probe(rgmu, pdev->dev.of_node); + if (ret) + return ret; + + /* Map and reserve RGMU CSRs registers */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "kgsl_rgmu"); + if (!res) { + dev_err(&pdev->dev, "The RGMU register region isn't defined\n"); + return -ENODEV; + } + + device->gmu_core.gmu2gpu_offset = (res->start - device->reg_phys) >> 2; + device->gmu_core.reg_len = resource_size(res); + device->gmu_core.reg_virt = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(device->gmu_core.reg_virt)) { + dev_err(&pdev->dev, "Unable to map the RGMU registers\n"); + return PTR_ERR(device->gmu_core.reg_virt); + } + + device->gmu_core.ptr = (void *)rgmu; + + /* Initialize OOB and RGMU interrupts */ + ret = a6xx_rgmu_irq_probe(device); + if (ret) + return ret; + + /* Don't enable RGMU interrupts until RGMU started */ + /* We cannot use rgmu_irq_disable because it writes registers */ + disable_irq(rgmu->rgmu_interrupt_num); + disable_irq(rgmu->oob_interrupt_num); + + /* Set up RGMU idle states */ + if (ADRENO_FEATURE(ADRENO_DEVICE(device), ADRENO_IFPC)) + rgmu->idle_level = GPU_HW_IFPC; + else + rgmu->idle_level = GPU_HW_ACTIVE; + + set_bit(GMU_ENABLED, &device->gmu_core.flags); + device->gmu_core.core_ops = &a6xx_rgmu_ops; + device->gmu_core.dev_ops = &a6xx_rgmudev; + + return 0; +} + +static void a6xx_rgmu_remove(struct kgsl_device *device) +{ + a6xx_rgmu_stop(device); + + memset(&device->gmu_core, 0, sizeof(device->gmu_core)); +} + +static int a6xx_rgmu_bind(struct device *dev, struct device *master, void *data) +{ + struct kgsl_device *device = dev_get_drvdata(master); + + return a6xx_rgmu_probe(device, to_platform_device(dev)); +} + +static void a6xx_rgmu_unbind(struct device *dev, struct device *master, + void *data) +{ + struct kgsl_device *device = dev_get_drvdata(master); + + a6xx_rgmu_remove(device); +} + +static const struct component_ops a6xx_rgmu_component_ops = { + .bind = a6xx_rgmu_bind, + .unbind = a6xx_rgmu_unbind, +}; + +static int a6xx_rgmu_probe_dev(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &a6xx_rgmu_component_ops); +} + +static int a6xx_rgmu_remove_dev(struct platform_device *pdev) +{ + component_del(&pdev->dev, &a6xx_rgmu_component_ops); + return 0; +} + +static const struct of_device_id a6xx_rgmu_match_table[] = { + { .compatible = "qcom,gpu-rgmu" }, + { }, +}; + +struct platform_driver a6xx_rgmu_driver = { + .probe = a6xx_rgmu_probe_dev, + .remove = a6xx_rgmu_remove_dev, + .driver = { + .name = "kgsl-rgmu", + .of_match_table = a6xx_rgmu_match_table, + }, +}; diff --git a/drivers/gpu/msm/kgsl_rgmu.h b/drivers/gpu/msm/adreno_a6xx_rgmu.h similarity index 79% rename from drivers/gpu/msm/kgsl_rgmu.h rename to drivers/gpu/msm/adreno_a6xx_rgmu.h index 112f6e52c6ef..02839b16001e 100644 --- a/drivers/gpu/msm/kgsl_rgmu.h +++ b/drivers/gpu/msm/adreno_a6xx_rgmu.h @@ -2,8 +2,8 @@ /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ -#ifndef __KGSL_RGMU_H -#define __KGSL_RGMU_H +#ifndef __ADRENO_A6XX_RGMU_H +#define __ADRENO_A6XX_RGMU_H #define RGMU_AO_IRQ_FENCE_ERR BIT(3) #define RGMU_AO_IRQ_MASK RGMU_AO_IRQ_FENCE_ERR @@ -16,7 +16,7 @@ #define MAX_RGMU_CLKS 8 /** - * struct rgmu_device - rGMU device structure + * struct a6xx_rgmu_device - rGMU device structure * @ver: RGMU firmware version * @rgmu_interrupt_num: RGMU interrupt number * @oob_interrupt_num: number of RGMU asserted OOB interrupt @@ -31,7 +31,7 @@ * @idle_level: Minimal GPU idle power level * @fault_count: RGMU fault count */ -struct rgmu_device { +struct a6xx_rgmu_device { u32 ver; struct platform_device *pdev; unsigned int rgmu_interrupt_num; @@ -49,9 +49,6 @@ struct rgmu_device { unsigned int fault_count; }; -extern struct gmu_dev_ops adreno_a6xx_rgmudev; -#define KGSL_RGMU_DEVICE(_a) ((struct rgmu_device *)((_a)->gmu_core.ptr)) +#define A6XX_RGMU_DEVICE(_a) ((struct a6xx_rgmu_device *)((_a)->gmu_core.ptr)) -irqreturn_t rgmu_irq_handler(int irq, void *data); -irqreturn_t oob_irq_handler(int irq, void *data); -#endif /* __KGSL_RGMU_H */ +#endif /* __ADRENO_A6XX_RGMU_H */ diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 43b5a4c5c54a..8ffbf0b07b8b 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2008-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. */ #ifndef __KGSL_H #define __KGSL_H diff --git a/drivers/gpu/msm/kgsl_gmu_core.c b/drivers/gpu/msm/kgsl_gmu_core.c index aded346fa01e..be1f81cd91d9 100644 --- a/drivers/gpu/msm/kgsl_gmu_core.c +++ b/drivers/gpu/msm/kgsl_gmu_core.c @@ -12,7 +12,7 @@ static const struct of_device_id gmu_match_table[] = { { .compatible = "qcom,gpu-gmu", .data = &a6xx_gmu_driver }, - { .compatible = "qcom,gpu-rgmu", .data = &kgsl_rgmu_driver }, + { .compatible = "qcom,gpu-rgmu", .data = &a6xx_rgmu_driver }, {}, }; diff --git a/drivers/gpu/msm/kgsl_gmu_core.h b/drivers/gpu/msm/kgsl_gmu_core.h index ff82d5619469..43c180f5033c 100644 --- a/drivers/gpu/msm/kgsl_gmu_core.h +++ b/drivers/gpu/msm/kgsl_gmu_core.h @@ -162,7 +162,7 @@ struct gmu_core_device { }; extern struct platform_driver a6xx_gmu_driver; -extern struct platform_driver kgsl_rgmu_driver; +extern struct platform_driver a6xx_rgmu_driver; /* GMU core functions */ diff --git a/drivers/gpu/msm/kgsl_rgmu.c b/drivers/gpu/msm/kgsl_rgmu.c deleted file mode 100644 index f8610129aef9..000000000000 --- a/drivers/gpu/msm/kgsl_rgmu.c +++ /dev/null @@ -1,461 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "adreno.h" -#include "kgsl_device.h" -#include "kgsl_rgmu.h" -#include "kgsl_util.h" - -#define RGMU_CLK_FREQ 200000000 - -static int rgmu_irq_probe(struct kgsl_device *device) -{ - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - int ret; - - ret = kgsl_request_irq(rgmu->pdev, "kgsl_oob", oob_irq_handler, device); - if (ret < 0) - return ret; - - rgmu->oob_interrupt_num = ret; - - ret = kgsl_request_irq(rgmu->pdev, - "kgsl_rgmu", rgmu_irq_handler, device); - if (ret < 0) - return ret; - - rgmu->rgmu_interrupt_num = ret; - return 0; -} - -static int rgmu_regulators_probe(struct rgmu_device *rgmu) -{ - int ret = 0; - - rgmu->cx_gdsc = devm_regulator_get(&rgmu->pdev->dev, "vddcx"); - if (IS_ERR(rgmu->cx_gdsc)) { - ret = PTR_ERR(rgmu->cx_gdsc); - if (ret != -EPROBE_DEFER) - dev_err(&rgmu->pdev->dev, - "Couldn't get CX gdsc error:%d\n", ret); - return ret; - } - - rgmu->gx_gdsc = devm_regulator_get(&rgmu->pdev->dev, "vdd"); - if (IS_ERR(rgmu->gx_gdsc)) { - ret = PTR_ERR(rgmu->gx_gdsc); - if (ret != -EPROBE_DEFER) - dev_err(&rgmu->pdev->dev, - "Couldn't get GX gdsc error:%d\n", ret); - } - - return ret; -} - -static int rgmu_clocks_probe(struct rgmu_device *rgmu, struct device_node *node) -{ - int ret; - - ret = devm_clk_bulk_get_all(&rgmu->pdev->dev, &rgmu->clks); - if (ret < 0) - return ret; - - rgmu->num_clks = ret; - - rgmu->gpu_clk = kgsl_of_clk_by_name(rgmu->clks, ret, "core"); - if (!rgmu->gpu_clk) { - dev_err(&rgmu->pdev->dev, "The GPU clock isn't defined\n"); - return -ENODEV; - } - - rgmu->rgmu_clk = kgsl_of_clk_by_name(rgmu->clks, ret, "gmu"); - if (!rgmu->rgmu_clk) { - dev_err(&rgmu->pdev->dev, "The RGMU clock isn't defined\n"); - return -ENODEV; - } - - return 0; -} - -static void rgmu_disable_clks(struct kgsl_device *device) -{ - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); - int ret; - - /* Check GX GDSC is status */ - if (gmu_dev_ops->gx_is_on(device)) { - - if (IS_ERR_OR_NULL(rgmu->gx_gdsc)) - return; - - /* - * Switch gx gdsc control from RGMU to CPU. Force non-zero - * reference count in clk driver so next disable call will - * turn off the GDSC. - */ - ret = regulator_enable(rgmu->gx_gdsc); - if (ret) - dev_err(&rgmu->pdev->dev, - "Fail to enable gx gdsc:%d\n", ret); - - ret = regulator_disable(rgmu->gx_gdsc); - if (ret) - dev_err(&rgmu->pdev->dev, - "Fail to disable gx gdsc:%d\n", ret); - - if (gmu_dev_ops->gx_is_on(device)) - dev_err(&rgmu->pdev->dev, "gx is stuck on\n"); - } - - clk_bulk_disable_unprepare(rgmu->num_clks, rgmu->clks); - - clear_bit(GMU_CLK_ON, &device->gmu_core.flags); -} - -static int rgmu_enable_clks(struct kgsl_device *device) -{ - int ret; - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - - ret = clk_set_rate(rgmu->rgmu_clk, RGMU_CLK_FREQ); - if (ret) { - dev_err(&rgmu->pdev->dev, "Couldn't set the RGMU clock\n"); - return ret; - } - - ret = clk_set_rate(rgmu->gpu_clk, - pwr->pwrlevels[pwr->default_pwrlevel].gpu_freq); - if (ret) { - dev_err(&rgmu->pdev->dev, "Couldn't set the GPU clock\n"); - return ret; - } - - ret = clk_bulk_prepare_enable(rgmu->num_clks, rgmu->clks); - if (ret) { - dev_err(&rgmu->pdev->dev, "Failed to enable RGMU clocks\n"); - return ret; - } - - set_bit(GMU_CLK_ON, &device->gmu_core.flags); - return 0; -} - -static int rgmu_disable_gdsc(struct kgsl_device *device) -{ - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - - /* Wait up to 5 seconds for the regulator to go off */ - if (kgsl_regulator_disable_wait(rgmu->cx_gdsc, 5000)) - return 0; - - dev_err(&rgmu->pdev->dev, "RGMU CX gdsc off timeout\n"); - return -ETIMEDOUT; -} - -static int rgmu_enable_gdsc(struct rgmu_device *rgmu) -{ - int ret; - - if (IS_ERR_OR_NULL(rgmu->cx_gdsc)) - return 0; - - ret = regulator_enable(rgmu->cx_gdsc); - if (ret) - dev_err(&rgmu->pdev->dev, - "Fail to enable CX gdsc:%d\n", ret); - - return ret; -} - -static void rgmu_snapshot(struct kgsl_device *device) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - - /* Mask so there's no interrupt caused by NMI */ - adreno_write_gmureg(adreno_dev, - ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF); - - /* Make sure the interrupt is masked */ - wmb(); - - /* - * Halt RGMU execution so that GX will not - * be collapsed while dumping snapshot. - */ - gmu_dev_ops->halt_execution(device); - - kgsl_device_snapshot(device, NULL, true); - - adreno_write_gmureg(adreno_dev, - ADRENO_REG_GMU_GMU2HOST_INTR_CLR, 0xFFFFFFFF); - adreno_write_gmureg(adreno_dev, - ADRENO_REG_GMU_GMU2HOST_INTR_MASK, - ~(gmu_dev_ops->gmu2host_intr_mask)); - - rgmu->fault_count++; -} - -/* Caller shall ensure GPU is ready for SLUMBER */ -static void rgmu_stop(struct kgsl_device *device) -{ - struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); - - if (!test_bit(GMU_CLK_ON, &device->gmu_core.flags)) - return; - - /* Wait for the lowest idle level we requested */ - if (gmu_dev_ops->wait_for_lowest_idle(device)) - goto error; - - gmu_dev_ops->rpmh_gpu_pwrctrl(device, - GMU_NOTIFY_SLUMBER, 0, 0); - - gmu_dev_ops->irq_disable(device); - rgmu_disable_clks(device); - rgmu_disable_gdsc(device); - return; - -error: - - /* - * The power controller will change state to SLUMBER anyway - * Set GMU_FAULT flag to indicate to power contrller - * that hang recovery is needed to power on GPU - */ - set_bit(GMU_FAULT, &device->gmu_core.flags); - rgmu_snapshot(device); -} - -static struct gmu_core_ops rgmu_ops; - -/* Do not access any RGMU registers in RGMU probe function */ -static int rgmu_probe(struct kgsl_device *device, struct platform_device *pdev) -{ - struct rgmu_device *rgmu; - struct resource *res; - int ret = -ENXIO; - - rgmu = devm_kzalloc(&pdev->dev, sizeof(*rgmu), GFP_KERNEL); - - if (rgmu == NULL) - return -ENOMEM; - - rgmu->pdev = pdev; - - /* Set up RGMU regulators */ - ret = rgmu_regulators_probe(rgmu); - if (ret) - return ret; - - /* Set up RGMU clocks */ - ret = rgmu_clocks_probe(rgmu, pdev->dev.of_node); - if (ret) - return ret; - - /* Map and reserve RGMU CSRs registers */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "kgsl_rgmu"); - if (!res) { - dev_err(&pdev->dev, "The RGMU register region isn't defined\n"); - return -ENODEV; - } - - device->gmu_core.gmu2gpu_offset = (res->start - device->reg_phys) >> 2; - device->gmu_core.reg_len = resource_size(res); - device->gmu_core.reg_virt = devm_ioremap_resource(&pdev->dev, res); - - if (IS_ERR(device->gmu_core.reg_virt)) { - dev_err(&pdev->dev, "Unable to map the RGMU registers\n"); - return PTR_ERR(device->gmu_core.reg_virt); - } - - device->gmu_core.ptr = (void *)rgmu; - - /* Initialize OOB and RGMU interrupts */ - ret = rgmu_irq_probe(device); - if (ret) - return ret; - - /* Don't enable RGMU interrupts until RGMU started */ - /* We cannot use rgmu_irq_disable because it writes registers */ - disable_irq(rgmu->rgmu_interrupt_num); - disable_irq(rgmu->oob_interrupt_num); - - /* Set up RGMU idle states */ - if (ADRENO_FEATURE(ADRENO_DEVICE(device), ADRENO_IFPC)) - rgmu->idle_level = GPU_HW_IFPC; - else - rgmu->idle_level = GPU_HW_ACTIVE; - - set_bit(GMU_ENABLED, &device->gmu_core.flags); - device->gmu_core.core_ops = &rgmu_ops; - device->gmu_core.dev_ops = &adreno_a6xx_rgmudev; - - return 0; -} - -static int rgmu_suspend(struct kgsl_device *device) -{ - struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); - - if (!test_bit(GMU_CLK_ON, &device->gmu_core.flags)) - return 0; - - gmu_dev_ops->irq_disable(device); - - if (gmu_dev_ops->rpmh_gpu_pwrctrl(device, GMU_SUSPEND, 0, 0)) - return -EINVAL; - - rgmu_disable_clks(device); - rgmu_disable_gdsc(device); - return 0; -} - -static int rgmu_init(struct kgsl_device *device) -{ - struct gmu_dev_ops *ops = GMU_DEVICE_OPS(device); - int ret; - - ret = ops->load_firmware(device); - if (ret) - return ret; - - return 0; -} - -/* To be called to power on both GPU and RGMU */ -static int rgmu_start(struct kgsl_device *device) -{ - int ret = 0; - struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - - switch (device->state) { - case KGSL_STATE_RESET: - ret = rgmu_suspend(device); - if (ret) - goto error_rgmu; - /* Fall-thru */ - case KGSL_STATE_INIT: - case KGSL_STATE_SUSPEND: - case KGSL_STATE_SLUMBER: - rgmu_enable_gdsc(rgmu); - rgmu_enable_clks(device); - gmu_dev_ops->irq_enable(device); - ret = gmu_dev_ops->rpmh_gpu_pwrctrl(device, - GMU_FW_START, GMU_COLD_BOOT, 0); - if (ret) - goto error_rgmu; - break; - } - /* Request default DCVS level */ - kgsl_pwrctrl_set_default_gpu_pwrlevel(device); - return 0; - -error_rgmu: - set_bit(GMU_FAULT, &device->gmu_core.flags); - rgmu_snapshot(device); - return ret; -} - -/* - * rgmu_dcvs_set() - Change GPU frequency and/or bandwidth. - * @rgmu: Pointer to RGMU device - * @pwrlevel: index to GPU DCVS table used by KGSL - * @bus_level: index to GPU bus table used by KGSL - * - * The function converts GPU power level and bus level index used by KGSL - * to index being used by GMU/RPMh. - */ -static int rgmu_dcvs_set(struct kgsl_device *device, - int pwrlevel, int bus_level) -{ - struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - int ret; - unsigned long rate; - - if (pwrlevel == INVALID_DCVS_IDX) - return -EINVAL; - - rate = device->pwrctrl.pwrlevels[pwrlevel].gpu_freq; - - ret = clk_set_rate(rgmu->gpu_clk, rate); - if (ret) - dev_err(&rgmu->pdev->dev, "Couldn't set the GPU clock\n"); - - return ret; -} - -static void rgmu_remove(struct kgsl_device *device) -{ - rgmu_stop(device); - - memset(&device->gmu_core, 0, sizeof(device->gmu_core)); -} - -static struct gmu_core_ops rgmu_ops = { - .init = rgmu_init, - .start = rgmu_start, - .stop = rgmu_stop, - .dcvs_set = rgmu_dcvs_set, - .snapshot = rgmu_snapshot, - .suspend = rgmu_suspend, -}; - -static int kgsl_rgmu_bind(struct device *dev, struct device *master, void *data) -{ - struct kgsl_device *device = dev_get_drvdata(master); - - return rgmu_probe(device, to_platform_device(dev)); -} - -static void kgsl_rgmu_unbind(struct device *dev, struct device *master, - void *data) -{ - struct kgsl_device *device = dev_get_drvdata(master); - - rgmu_remove(device); -} - -static const struct component_ops kgsl_rgmu_ops = { - .bind = kgsl_rgmu_bind, - .unbind = kgsl_rgmu_unbind, -}; - -static int kgsl_rgmu_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &kgsl_rgmu_ops); -} - -static int kgsl_rgmu_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &kgsl_rgmu_ops); - return 0; -} - -static const struct of_device_id kgsl_rgmu_match_table[] = { - { .compatible = "qcom,gpu-rgmu" }, - { }, -}; - -struct platform_driver kgsl_rgmu_driver = { - .probe = kgsl_rgmu_probe, - .remove = kgsl_rgmu_remove, - .driver = { - .name = "kgsl-rgmu", - .of_match_table = kgsl_rgmu_match_table, - }, -};