Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d50bb36c authored by Jordan Crouse's avatar Jordan Crouse
Browse files

msm: kgsl: Modernize bus scaling



Replace all of the legacy bus scaling code with the new interconnect API.
This includes removing all msm_bus support as well as the devbw interface
which is not needed since icc can set both the IB and the AB on our behalf.

As part of this, move most of the bus specific code into its own file,
add support for parsing a table of our own from the device tree and handle
both the GPU and GMU bus setting paths properly.

Change-Id: Ic0dedbadfbb3c48ec414ec60e3032b2204b4e724
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent ada604f0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ obj-$(CONFIG_QCOM_KGSL) += msm_kgsl.o

msm_kgsl-y = \
	kgsl.o \
	kgsl_bus.o \
	kgsl_drawobj.o \
	kgsl_events.o \
	kgsl_ioctl.o \
+7 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "adreno_compat.h"
#include "adreno_iommu.h"
#include "adreno_trace.h"
#include "kgsl_bus.h"
#include "kgsl_trace.h"
#include "kgsl_util.h"

@@ -1450,6 +1451,12 @@ static int adreno_probe(struct platform_device *pdev)
		return status;
	}

	status = kgsl_bus_init(device, pdev);
	if (status) {
		device->pdev = NULL;
		return status;
	}

	/*
	 * Probe/init GMU after initial gpu power probe
	 * Another part of GPU power probe in platform_probe
+185 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 */

#include <linux/interconnect.h>
#include <linux/of.h>

#include "kgsl_bus.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"

static int gmu_bus_set(struct kgsl_device *device, int buslevel,
		u32 ab)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int ret;

	ret = gmu_core_dcvs_set(device, INVALID_DCVS_IDX, buslevel);

	if (!ret)
		icc_set_bw(pwr->icc_path, MBps_to_icc(ab), 0);

	return ret;
}

static int interconnect_bus_set(struct kgsl_device *device, int level,
		u32 ab)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;

	icc_set_bw(pwr->icc_path, MBps_to_icc(ab),
		KBps_to_icc(pwr->ddr_table[level]));

	return 0;
}

static u32 _ab_buslevel_update(struct kgsl_pwrctrl *pwr,
		u32 ib)
{
	if (!ib)
		return 0;

	/* In the absence of any other settings, make ab 25% of ib */
	if ((!pwr->bus_percent_ab) && (!pwr->bus_ab_mbytes))
		return 25 * ib / 100;

	if (pwr->bus_width)
		return pwr->bus_ab_mbytes;

	return (pwr->bus_percent_ab * pwr->bus_max) / 100;
}


void kgsl_bus_update(struct kgsl_device *device, bool on)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	/* FIXME: this might be wrong? */
	int cur = pwr->pwrlevels[pwr->active_pwrlevel].bus_freq;
	int buslevel = 0;
	u32 ab;

	/* the bus should be ON to update the active frequency */
	if (on && !(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)))
		return;
	/*
	 * If the bus should remain on calculate our request and submit it,
	 * otherwise request bus level 0, off.
	 */
	if (on) {
		buslevel = min_t(int, pwr->pwrlevels[0].bus_max,
				cur + pwr->bus_mod);
		buslevel = max_t(int, buslevel, 1);
	} else {
		/* If the bus is being turned off, reset to default level */
		pwr->bus_mod = 0;
		pwr->bus_percent_ab = 0;
		pwr->bus_ab_mbytes = 0;
	}
	trace_kgsl_buslevel(device, pwr->active_pwrlevel, buslevel);
	pwr->cur_buslevel = buslevel;

	/* buslevel is the IB vote, update the AB */
	ab = _ab_buslevel_update(pwr, pwr->ddr_table[buslevel]);

	pwr->bus_set(device, buslevel, ab);
}

static void validate_pwrlevels(struct kgsl_device *device, u32 *ibs,
		int count)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int i;

	for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
		struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[i];

		if (pwrlevel->bus_freq >= count) {
			dev_err(device->dev, "Bus setting for GPU freq %d is out of bounds\n",
				pwrlevel->gpu_freq);
			pwrlevel->bus_freq = count - 1;
		}

		if (pwrlevel->bus_max >= count) {
			dev_err(device->dev, "Bus max for GPU freq %d is out of bounds\n",
				pwrlevel->gpu_freq);
			pwrlevel->bus_max = count - 1;
		}

		if (pwrlevel->bus_min >= count) {
			dev_err(device->dev, "Bus min for GPU freq %d is out of bounds\n",
				pwrlevel->gpu_freq);
			pwrlevel->bus_min = count - 1;
		}

		if (pwrlevel->bus_min > pwrlevel->bus_max) {
			dev_err(device->dev, "Bus min is bigger than bus max for GPU freq %d\n",
				pwrlevel->gpu_freq);
			pwrlevel->bus_min = pwrlevel->bus_max;
		}
	}
}

u32 *kgsl_bus_get_table(struct platform_device *pdev,
		const char *name, int *count)
{
	u32 *levels;
	int i, num = of_property_count_elems_of_size(pdev->dev.of_node,
		name, sizeof(u32));

	/* If the bus wasn't specified, then build a static table */
	if (num <= 0)
		return ERR_PTR(-EINVAL);

	levels = kcalloc(num, sizeof(*levels), GFP_KERNEL);
	if (!levels)
		return ERR_PTR(-ENOMEM);

	for (i = 0; i < num; i++)
		of_property_read_u32_index(pdev->dev.of_node,
			name, i, &levels[i]);

	*count = num;
	return levels;
}

int kgsl_bus_init(struct kgsl_device *device, struct platform_device *pdev)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int ret, count;

	pwr->ddr_table = kgsl_bus_get_table(pdev, "qcom,bus-table-ddr", &count);
	if (IS_ERR(pwr->ddr_table)) {
		ret = PTR_ERR(pwr->ddr_table);
		pwr->ddr_table = NULL;
		return ret;
	}

	pwr->ddr_table_count = count;

	validate_pwrlevels(device, pwr->ddr_table, pwr->ddr_table_count);

	pwr->icc_path = of_icc_get(&pdev->dev, NULL);
	if (IS_ERR(pwr->icc_path) && !gmu_core_scales_bandwidth(device)) {
		WARN(1, "The CPU has no way to set the GPU bus levels\n");
		return PTR_ERR(pwr->icc_path);
	}

	if (gmu_core_scales_bandwidth(device))
		pwr->bus_set = gmu_bus_set;
	else
		pwr->bus_set = interconnect_bus_set;

	return 0;
}

void kgsl_bus_close(struct kgsl_device *device)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;

	kfree(pwr->ddr_table);

	/* FIXME: Make sure icc put can handle NULL or IS_ERR */
	icc_put(pwr->icc_path);
}
+19 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 */

#ifndef _KGSL_BUS_H
#define _KGSL_BUS_H

struct kgsl_device;
struct platform_device;

int kgsl_bus_init(struct kgsl_device *device, struct platform_device *pdev);
void kgsl_bus_close(struct kgsl_device *device);
void kgsl_bus_update(struct kgsl_device *device, bool on);

u32 *kgsl_bus_get_table(struct platform_device *pdev,
		const char *name, int *count);

#endif
+10 −5
Original line number Diff line number Diff line
@@ -7,9 +7,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interconnect.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/msm-bus.h>
#include <linux/of_platform.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -490,6 +490,7 @@ static int gmu_dcvs_set(struct kgsl_device *device,
		int gpu_pwrlevel, int bus_level)
{
	int ret = 0;
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device);
@@ -515,7 +516,7 @@ static int gmu_dcvs_set(struct kgsl_device *device,
	if (gpu_pwrlevel < gmu->num_gpupwrlevels - 1)
		req.freq = gmu->num_gpupwrlevels - gpu_pwrlevel - 1;

	if (bus_level < gmu->num_bwlevels && bus_level > 0)
	if (bus_level < pwr->ddr_table_count && bus_level > 0)
		req.bw = bus_level;

	/* GMU will vote for slumber levels through the sleep sequence */
@@ -794,6 +795,9 @@ static int gmu_bus_vote_init(struct gmu_device *gmu, struct kgsl_pwrctrl *pwr)
	struct rpmh_votes_t *votes = &gmu->rpmh_votes;
	int ret;

	if (!gmu->num_bwlevels)
		return 0;

	usecases  = kcalloc(gmu->num_bwlevels, sizeof(*usecases), GFP_KERNEL);
	if (!usecases)
		return -ENOMEM;
@@ -1418,6 +1422,7 @@ static int gmu_start(struct kgsl_device *device)
	struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device);
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
	unsigned long ib;

	switch (device->state) {
	case KGSL_STATE_INIT:
@@ -1432,7 +1437,9 @@ static int gmu_start(struct kgsl_device *device)
		gmu_dev_ops->irq_enable(device);

		/* Vote for minimal DDR BW for GMU to init */
		icc_set_bw(gmu->icc_path, 0, MBps_to_icc(1171));
		level = pwr->pwrlevels[pwr->default_pwrlevel].bus_min;
		icc_set_bw(gmu->icc_path, 0,
			MBps_to_icc(pwr->ddr_table[level]));

		ret = gmu_dev_ops->rpmh_gpu_pwrctrl(device, GMU_FW_START,
				GMU_COLD_BOOT, 0);
@@ -1447,8 +1454,6 @@ static int gmu_start(struct kgsl_device *device)
		ret = kgsl_pwrctrl_set_default_gpu_pwrlevel(device);
		if (ret)
			goto error_gmu;

		icc_set_bw(gmu->icc_path, 0, 0);
		break;

	case KGSL_STATE_SLUMBER:
Loading