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

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

msm: kgsl: Generate TCS votes to send to the GMU



With the demise of msm_bus_scale we lost the API to generate TCS
commands so we need to do it ourselves.

Change-Id: Ic0dedbadad9bead0508febcb942e5bb1bd6cb465
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent d50bb36c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ static int interconnect_bus_set(struct kgsl_device *device, int level,
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;

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

	return 0;
}
+229 −99
Original line number Diff line number Diff line
@@ -14,8 +14,10 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <soc/qcom/cmd-db.h>
#include <soc/qcom/tcs.h>

#include "adreno.h"
#include "kgsl_bus.h"
#include "kgsl_device.h"
#include "kgsl_gmu.h"
#include "kgsl_util.h"
@@ -699,7 +701,7 @@ static int rpmh_arc_votes_init(struct kgsl_device *device,

	num_freqs = gmu->num_gpupwrlevels;

	if (num_freqs > pri_rail->num) {
	if (num_freqs > pri_rail->num || num_freqs > ARRAY_SIZE(vlvl_tbl)) {
		dev_err(&gmu->pdev->dev,
			"Defined more GPU DCVS levels than RPMh can support\n");
		return -EINVAL;
@@ -713,132 +715,260 @@ static int rpmh_arc_votes_init(struct kgsl_device *device,
						sec_rail, vlvl_tbl, num_freqs);
}

struct bcm {
	const char *name;
	u32 buswidth;
	u32 channels;
	u32 unit;
	u16 width;
	u8 vcd;
	bool fixed;
};

/*
 * build_rpmh_bw_votes() - build TCS commands to vote for bandwidth.
 * Each command sets frequency of a node along path to DDR or CNOC.
 * @rpmh_vote: Pointer to RPMh vote needed by GMU to set BW via RPMh
 * @num_usecases: Number of BW use cases (or BW levels)
 * @handle: Provided by bus driver. It contains TCS command sets for
 * all BW use cases of a bus client.
 * List of Bus Control Modules (BCMs) that need to be configured for the GPU
 * to access DDR. For each bus level we will generate a vote each BC
 */
static void build_rpmh_bw_votes(struct gmu_bw_votes *rpmh_vote,
		unsigned int num_usecases, struct msm_bus_tcs_handle handle)
static struct bcm a660_ddr_bcms[] = {
	{ .name = "SH0", .buswidth = 16 },
	{ .name = "MC0", .buswidth = 4 },
	{ .name = "ACV", .fixed = true },
};

/* Same as above, but for the CNOC BCMs */
static struct bcm a660_cnoc_bcms[] = {
	{ .name = "CN0", .buswidth = 4 },
};

/* Generate a set of bandwidth votes for the list of BCMs */
static void tcs_cmd_data(struct bcm *bcms, int count, u32 ab, u32 ib,
		u32 *data)
{
	struct msm_bus_tcs_usecase *tmp;
	int i, j;
	int i;

	for (i = 0; i < count; i++) {
		bool valid = true;
		bool commit = false;
		u64 avg, peak, x, y;

		if (i == count - 1 || bcms[i].vcd != bcms[i + 1].vcd)
			commit = true;

	for (i = 0; i < num_usecases; i++) {
		tmp = &handle.usecases[i];
		for (j = 0; j < tmp->num_cmds; j++) {
			if (!i) {
		/*
			 * Wait bitmask and TCS command addresses are
			 * same for all bw use cases. To save data volume
			 * exchanged between driver and GMU, only
			 * transfer bitmasks and TCS command addresses
			 * of first set of bw use case
		 * On a660, the "ACV" y vote should be 0x08 if there is a valid
		 * vote and 0x00 if not. This is kind of hacky and a660 specific
		 * but we can clean it up when we add a new target
		 */
				rpmh_vote->cmds_per_bw_vote = tmp->num_cmds;
				rpmh_vote->cmds_wait_bitmask =
						tmp->cmds[j].wait ?
						rpmh_vote->cmds_wait_bitmask
						| BIT(i)
						: rpmh_vote->cmds_wait_bitmask
						& (~BIT(i));
				rpmh_vote->cmd_addrs[j] = tmp->cmds[j].addr;
		if (bcms[i].fixed) {
			if (!ab && !ib)
				data[i] = BCM_TCS_CMD(commit, false, 0x0, 0x0);
			else
				data[i] = BCM_TCS_CMD(commit, true, 0x0, 0x8);
			continue;
		}
			rpmh_vote->cmd_data[i][j] = tmp->cmds[j].data;

		/* Multiple the bandwidth by the width of the connection */
		avg = ((u64) ab) * bcms[i].width;

		/* And then divide by the total width across channels */
		do_div(avg, bcms[i].buswidth * bcms[i].channels);

		peak = ((u64) ib) * bcms[i].width;
		do_div(peak, bcms[i].buswidth);

		/* Input bandwidth value is in KBps */
		x = avg * 1000ULL;
		do_div(x, bcms[i].unit);

		/* Input bandwidth value is in KBps */
		y = peak * 1000ULL;
		do_div(y, bcms[i].unit);

		/*
		 * If a bandwidth value was specified but the calculation ends
		 * rounding down to zero, set a minimum level
		 */
		if (ab && x == 0)
			x = 1;

		if (ib && y == 0)
			y = 1;

		x = min_t(u64, x, BCM_TCS_CMD_VOTE_MASK);
		y = min_t(u64, y, BCM_TCS_CMD_VOTE_MASK);

		if (!x && !y)
			valid = false;

		data[i] = BCM_TCS_CMD(commit, valid, x, y);
	}
}

struct bcm_data {
	__le32 unit;
	__le16 width;
	u8 vcd;
	u8 reserved;
};

struct rpmh_bw_votes {
	u32 wait_bitmask;
	u32 num_cmds;
	u32 *addrs;
	u32 num_levels;
	u32 **cmds;
};

static void free_rpmh_bw_votes(struct rpmh_bw_votes *votes)
{
	int i;

	if (!votes)
		return;

	for (i = 0; votes->cmds && i < votes->num_levels; i++)
		kfree(votes->cmds[i]);

	kfree(votes->cmds);
	kfree(votes->addrs);
	kfree(votes);
}

static void build_bwtable_cmd_cache(struct gmu_device *gmu)
/* Build the votes table from the specified bandwidth levels */
static struct rpmh_bw_votes *build_rpmh_bw_votes(struct bcm *bcms,
		int bcm_count, u32 *levels, int levels_count)
{
	struct hfi_bwtable_cmd *cmd = &gmu->hfi.bwtbl_cmd;
	struct rpmh_votes_t *votes = &gmu->rpmh_votes;
	unsigned int i, j;
	struct rpmh_bw_votes *votes;
	int i;

	cmd->hdr = 0xFFFFFFFF;
	cmd->bw_level_num = gmu->num_bwlevels;
	cmd->cnoc_cmds_num = votes->cnoc_votes.cmds_per_bw_vote;
	cmd->cnoc_wait_bitmask = votes->cnoc_votes.cmds_wait_bitmask;
	cmd->ddr_cmds_num = votes->ddr_votes.cmds_per_bw_vote;
	cmd->ddr_wait_bitmask = votes->ddr_votes.cmds_wait_bitmask;
	votes = kzalloc(sizeof(*votes), GFP_KERNEL);
	if (!votes)
		return ERR_PTR(-ENOMEM);

	for (i = 0; i < cmd->ddr_cmds_num; i++)
		cmd->ddr_cmd_addrs[i] = votes->ddr_votes.cmd_addrs[i];
	votes->addrs = kcalloc(bcm_count, sizeof(*votes->cmds), GFP_KERNEL);
	if (!votes->addrs) {
		free_rpmh_bw_votes(votes);
		return ERR_PTR(-ENOMEM);
	}

	for (i = 0; i < cmd->bw_level_num; i++)
		for (j = 0; j < cmd->ddr_cmds_num; j++)
			cmd->ddr_cmd_data[i][j] =
				votes->ddr_votes.cmd_data[i][j];
	votes->cmds = kcalloc(levels_count, sizeof(*votes->cmds), GFP_KERNEL);
	if (!votes->cmds) {
		free_rpmh_bw_votes(votes);
		return ERR_PTR(-ENOMEM);
	}

	for (i = 0; i < cmd->cnoc_cmds_num; i++)
		cmd->cnoc_cmd_addrs[i] =
			votes->cnoc_votes.cmd_addrs[i];
	votes->num_cmds = bcm_count;
	votes->num_levels = levels_count;

	for (i = 0; i < MAX_CNOC_LEVELS; i++)
		for (j = 0; j < cmd->cnoc_cmds_num; j++)
			cmd->cnoc_cmd_data[i][j] =
				votes->cnoc_votes.cmd_data[i][j];
	/* Get the cmd-db information for each BCM */
	for (i = 0; i < bcm_count; i++) {
		size_t l;
		const struct bcm_data *data;

		data = cmd_db_read_aux_data(bcms[i].name, &l);

		votes->addrs[i] = cmd_db_read_addr(bcms[i].name);

		bcms[i].unit = le32_to_cpu(data->unit);
		bcms[i].width = le16_to_cpu(data->width);
		bcms[i].vcd = data->vcd;
	}

/*
 * gmu_bus_vote_init - initialized RPMh votes needed for bw scaling by GMU.
 * @gmu: Pointer to GMU device
 * @pwr: Pointer to KGSL power controller
 */
static int gmu_bus_vote_init(struct gmu_device *gmu, struct kgsl_pwrctrl *pwr)
	for (i = 0; i < bcm_count; i++) {
		if (i == (bcm_count - 1) || bcms[i].vcd != bcms[i + 1].vcd)
			votes->wait_bitmask |= (1 << i);
	}

	for (i = 0; i < levels_count; i++) {
		votes->cmds[i] = kcalloc(bcm_count, sizeof(u32), GFP_KERNEL);
		if (!votes->cmds[i]) {
			free_rpmh_bw_votes(votes);
			return ERR_PTR(-ENOMEM);
		}

		tcs_cmd_data(bcms, bcm_count, 0, levels[i], votes->cmds[i]);
	}

	return votes;
}

static void build_bwtable_cmd_cache(struct hfi_bwtable_cmd *cmd,
		struct rpmh_bw_votes *ddr, struct rpmh_bw_votes *cnoc)
{
	struct msm_bus_tcs_usecase *usecases;
	struct msm_bus_tcs_handle hdl;
	struct rpmh_votes_t *votes = &gmu->rpmh_votes;
	int ret;
	unsigned int i, j;

	if (!gmu->num_bwlevels)
		return 0;
	cmd->hdr = 0xFFFFFFFF;
	cmd->bw_level_num = ddr->num_levels;
	cmd->ddr_cmds_num = ddr->num_cmds;
	cmd->ddr_wait_bitmask = ddr->wait_bitmask;

	usecases  = kcalloc(gmu->num_bwlevels, sizeof(*usecases), GFP_KERNEL);
	if (!usecases)
		return -ENOMEM;
	for (i = 0; i < ddr->num_cmds; i++)
		cmd->ddr_cmd_addrs[i] = ddr->addrs[i];

	hdl.num_usecases = gmu->num_bwlevels;
	hdl.usecases = usecases;
	for (i = 0; i < ddr->num_levels; i++)
		for (j = 0; j < ddr->num_cmds; j++)
			cmd->ddr_cmd_data[i][j] = (u32) ddr->cmds[i][j];

	/*
	 * Query TCS command set for each use case defined in GPU b/w table
	 */
	ret = msm_bus_scale_query_tcs_cmd_all(&hdl, gmu->pcl);
	if (ret)
		goto out;
	if (!cnoc)
		return;

	build_rpmh_bw_votes(&votes->ddr_votes, gmu->num_bwlevels, hdl);
	cmd->cnoc_cmds_num = cnoc->num_cmds;
	cmd->cnoc_wait_bitmask = cnoc->wait_bitmask;

	/*
	 *Query CNOC TCS command set for each use case defined in cnoc bw table
	 */
	ret = msm_bus_scale_query_tcs_cmd_all(&hdl, gmu->ccl);
	if (ret)
		goto out;
	for (i = 0; i < cnoc->num_cmds; i++)
		cmd->cnoc_cmd_addrs[i] = cnoc->addrs[i];

	build_rpmh_bw_votes(&votes->cnoc_votes, gmu->num_cnocbwlevels, hdl);
	for (i = 0; i < cnoc->num_levels; i++)
		for (j = 0; j < cnoc->num_cmds; j++)
			cmd->cnoc_cmd_data[i][j] = (u32) cnoc->cmds[i][j];
}

	build_bwtable_cmd_cache(gmu);
static int gmu_bus_vote_init(struct kgsl_device *device)
{
	struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	struct rpmh_bw_votes *ddr, *cnoc = NULL;
	u32 *cnoc_table;
	u32 count;

out:
	kfree(usecases);
	/* Build the DDR votes */
	ddr = build_rpmh_bw_votes(a660_ddr_bcms, ARRAY_SIZE(a660_ddr_bcms),
		pwr->ddr_table, pwr->ddr_table_count);
	if (IS_ERR(ddr))
		return PTR_ERR(ddr);

	return ret;
	/* Get the CNOC table */
	cnoc_table = kgsl_bus_get_table(device->pdev, "qcom,bus-table-cnoc",
		&count);

	/* And build the votes for that, if it exists */
	if (count > 0)
		cnoc = build_rpmh_bw_votes(a660_cnoc_bcms,
			ARRAY_SIZE(a660_cnoc_bcms), cnoc_table, count);
	kfree(cnoc_table);

	if (IS_ERR(cnoc)) {
		free_rpmh_bw_votes(ddr);
		return PTR_ERR(cnoc);
	}

	/* Build the HFI command once */
	build_bwtable_cmd_cache(&gmu->hfi.bwtbl_cmd, ddr, cnoc);

	free_rpmh_bw_votes(ddr);
	free_rpmh_bw_votes(cnoc);

	return 0;
}

static int gmu_rpmh_init(struct kgsl_device *device,
		struct gmu_device *gmu, struct kgsl_pwrctrl *pwr)
		struct gmu_device *gmu)
{
	struct rpmh_arc_vals gfx_arc, cx_arc, mx_arc;
	int ret;

	/* Initialize BW tables */
	ret = gmu_bus_vote_init(gmu, pwr);
	ret = gmu_bus_vote_init(device);
	if (ret)
		return ret;

@@ -1259,7 +1389,7 @@ static int gmu_probe(struct kgsl_device *device, struct device_node *node)
	gmu->icc_path = of_icc_get(&gmu->pdev->dev, NULL);

	/* Populates RPMh configurations */
	ret = gmu_rpmh_init(device, gmu, pwr);
	ret = gmu_rpmh_init(device, gmu);
	if (ret)
		goto error;

@@ -1422,7 +1552,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;
	int level;

	switch (device->state) {
	case KGSL_STATE_INIT:
+0 −9
Original line number Diff line number Diff line
@@ -118,18 +118,9 @@ struct gmu_memdesc {
	enum gmu_context_index ctx_idx;
};

struct gmu_bw_votes {
	uint32_t cmds_wait_bitmask;
	uint32_t cmds_per_bw_vote;
	uint32_t cmd_addrs[MAX_BW_CMDS];
	uint32_t cmd_data[MAX_GX_LEVELS][MAX_BW_CMDS];
};

struct rpmh_votes_t {
	uint32_t gx_votes[MAX_GX_LEVELS];
	uint32_t cx_votes[MAX_CX_LEVELS];
	struct gmu_bw_votes ddr_votes;
	struct gmu_bw_votes cnoc_votes;
};

enum gmu_load_mode {