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

Commit fc14a5b0 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: msm_bus: Introduce support for RPMH" into msm-4.8

parents f99d5811 04ce4205
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -211,6 +211,15 @@ config QCOM_BUS_SCALING
	for the active devices needs without keeping the clocks at max
	frequency when a slower speed is sufficient.

config  QCOM_BUS_CONFIG_RPMH
	bool "RPMH Bus scaling driver"
	depends on QCOM_BUS_SCALING
	help
	  This option enables bus scaling using QCOM specific hardware
	  accelerators. It enables the translation of bandwidth requests
	  from logical nodes to hardware nodes controlled by the BCM (Bus
	  Clock Manager)

config QCOM_SECURE_BUFFER
	bool "Helper functions for securing buffers through TZ"
	help
+1 −0
Original line number Diff line number Diff line
@@ -29,3 +29,4 @@ obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
+11 −4
Original line number Diff line number Diff line
@@ -4,10 +4,17 @@
obj-y +=  msm_bus_core.o msm_bus_client_api.o
obj-$(CONFIG_OF) += msm_bus_of.o
obj-$(CONFIG_MSM_RPM_SMD) += msm_bus_rpm_smd.o
obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o msm_bus_rules.o \

ifdef CONFIG_QCOM_BUS_CONFIG_RPMH
	obj-y += msm_bus_fabric_rpmh.o msm_bus_arb_rpmh.o msm_bus_rules.o \
		msm_bus_bimc_rpmh.o msm_bus_noc_rpmh.o
	obj-$(CONFIG_OF) += msm_bus_of_rpmh.o
else
	obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o \
		msm_bus_bimc_adhoc.o msm_bus_noc_adhoc.o
	obj-$(CONFIG_OF) += msm_bus_of_adhoc.o
endif

obj-$(CONFIG_DEBUG_BUS_VOTER) += msm_bus_dbg_voter.o
obj-$(CONFIG_CORESIGHT) +=  msm_buspm_coresight_adhoc.o

obj-$(CONFIG_DEBUG_FS) += msm_bus_dbg.o
+1451 −0

File added.

Preview size limit exceeded, changes collapsed.

+606 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define pr_fmt(fmt) "AXI: BIMC: %s(): " fmt, __func__

#include <linux/slab.h>
#include <linux/io.h>
#include <linux/msm-bus-board.h>
#include "msm_bus_core.h"
#include "msm_bus_bimc.h"
#include "msm_bus_adhoc.h"

/* M_Generic */

enum bke_sw {
	BKE_OFF = 0,
	BKE_ON = 1,
};

#define M_REG_BASE(b)		((b) + 0x00008000)

#define M_MODE_ADDR(b, n) \
		(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000210)
enum bimc_m_mode {
	M_MODE_RMSK				= 0xf0000011,
	M_MODE_WR_GATHER_BEATS_BMSK		= 0xf0000000,
	M_MODE_WR_GATHER_BEATS_SHFT		= 0x1c,
	M_MODE_NARROW_WR_BMSK			= 0x10,
	M_MODE_NARROW_WR_SHFT			= 0x4,
	M_MODE_ORDERING_MODEL_BMSK		= 0x1,
	M_MODE_ORDERING_MODEL_SHFT		= 0x0,
};

#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
enum bimc_m_priolvl_override {
	M_PRIOLVL_OVERRIDE_RMSK			= 0x301,
	M_PRIOLVL_OVERRIDE_BMSK			= 0x300,
	M_PRIOLVL_OVERRIDE_SHFT			= 0x8,
	M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK	= 0x1,
	M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT	= 0x0,
};

#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
enum bimc_m_read_command_override {
	M_RD_CMD_OVERRIDE_RMSK			= 0x3071f7f,
	M_RD_CMD_OVERRIDE_AREQPRIO_BMSK		= 0x3000000,
	M_RD_CMD_OVERRIDE_AREQPRIO_SHFT		= 0x18,
	M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK		= 0x70000,
	M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT		= 0x10,
	M_RD_CMD_OVERRIDE_ATRANSIENT_BMSK		= 0x1000,
	M_RD_CMD_OVERRIDE_ATRANSIENT_SHFT		= 0xc,
	M_RD_CMD_OVERRIDE_ASHARED_BMSK		= 0x800,
	M_RD_CMD_OVERRIDE_ASHARED_SHFT		= 0xb,
	M_RD_CMD_OVERRIDE_AREDIRECT_BMSK		= 0x400,
	M_RD_CMD_OVERRIDE_AREDIRECT_SHFT		= 0xa,
	M_RD_CMD_OVERRIDE_AOOO_BMSK			= 0x200,
	M_RD_CMD_OVERRIDE_AOOO_SHFT			= 0x9,
	M_RD_CMD_OVERRIDE_AINNERSHARED_BMSK		= 0x100,
	M_RD_CMD_OVERRIDE_AINNERSHARED_SHFT		= 0x8,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK	= 0x40,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT	= 0x6,
	M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK	= 0x20,
	M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT	= 0x5,
	M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK	= 0x10,
	M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT	= 0x4,
	M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK	= 0x8,
	M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT	= 0x3,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK	= 0x4,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT	= 0x2,
	M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK		= 0x2,
	M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT		= 0x1,
	M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK	= 0x1,
	M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT	= 0x0,
};

#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250)
enum bimc_m_write_command_override {
	M_WR_CMD_OVERRIDE_RMSK			= 0x3071f7f,
	M_WR_CMD_OVERRIDE_AREQPRIO_BMSK		= 0x3000000,
	M_WR_CMD_OVERRIDE_AREQPRIO_SHFT		= 0x18,
	M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK		= 0x70000,
	M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT		= 0x10,
	M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK	= 0x1000,
	M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT	= 0xc,
	M_WR_CMD_OVERRIDE_ASHARED_BMSK		= 0x800,
	M_WR_CMD_OVERRIDE_ASHARED_SHFT		= 0xb,
	M_WR_CMD_OVERRIDE_AREDIRECT_BMSK		= 0x400,
	M_WR_CMD_OVERRIDE_AREDIRECT_SHFT		= 0xa,
	M_WR_CMD_OVERRIDE_AOOO_BMSK			= 0x200,
	M_WR_CMD_OVERRIDE_AOOO_SHFT			= 0x9,
	M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK		= 0x100,
	M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT		= 0x8,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK	= 0x40,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT	= 0x6,
	M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK	= 0x20,
	M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT	= 0x5,
	M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK	= 0x10,
	M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT	= 0x4,
	M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK	= 0x8,
	M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT	= 0x3,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK	= 0x4,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT	= 0x2,
	M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK	= 0x2,
	M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT	= 0x1,
	M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK	= 0x1,
	M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT	= 0x0,
};

#define M_BKE_EN_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000300)
enum bimc_m_bke_en {
	M_BKE_EN_RMSK			= 0x1,
	M_BKE_EN_EN_BMSK		= 0x1,
	M_BKE_EN_EN_SHFT		= 0x0,
};

/* Grant Period registers */
#define M_BKE_GP_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000304)
enum bimc_m_bke_grant_period {
	M_BKE_GP_RMSK		= 0x3ff,
	M_BKE_GP_GP_BMSK	= 0x3ff,
	M_BKE_GP_GP_SHFT	= 0x0,
};

/* Grant count register.
 * The Grant count register represents a signed 16 bit
 * value, range 0-0x7fff
 */
#define M_BKE_GC_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000308)
enum bimc_m_bke_grant_count {
	M_BKE_GC_RMSK			= 0xffff,
	M_BKE_GC_GC_BMSK		= 0xffff,
	M_BKE_GC_GC_SHFT		= 0x0,
};

/* Threshold High Registers */
#define M_BKE_THH_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000320)
enum bimc_m_bke_thresh_high {
	M_BKE_THH_RMSK		= 0xffff,
	M_BKE_THH_THRESH_BMSK	= 0xffff,
	M_BKE_THH_THRESH_SHFT	= 0x0,
};

/* Threshold Medium Registers */
#define M_BKE_THM_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000324)
enum bimc_m_bke_thresh_medium {
	M_BKE_THM_RMSK		= 0xffff,
	M_BKE_THM_THRESH_BMSK	= 0xffff,
	M_BKE_THM_THRESH_SHFT	= 0x0,
};

/* Threshold Low Registers */
#define M_BKE_THL_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000328)
enum bimc_m_bke_thresh_low {
	M_BKE_THL_RMSK			= 0xffff,
	M_BKE_THL_THRESH_BMSK		= 0xffff,
	M_BKE_THL_THRESH_SHFT		= 0x0,
};

#define NUM_HEALTH_LEVEL	(4)
#define M_BKE_HEALTH_0_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000340)
enum bimc_m_bke_health_0 {
	M_BKE_HEALTH_0_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_1_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000344)
enum bimc_m_bke_health_1 {
	M_BKE_HEALTH_1_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_1_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_1_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_1_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_1_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_2_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000348)
enum bimc_m_bke_health_2 {
	M_BKE_HEALTH_2_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_2_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_2_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_2_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_2_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_3_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x0000034c)
enum bimc_m_bke_health_3 {
	M_BKE_HEALTH_3_CONFIG_RMSK			= 0x303,
	M_BKE_HEALTH_3_CONFIG_AREQPRIO_BMSK	= 0x300,
	M_BKE_HEALTH_3_CONFIG_AREQPRIO_SHFT	= 0x8,
	M_BKE_HEALTH_3_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_3_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define BKE_HEALTH_MASK \
	(M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK |\
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK |\
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK)

#define BKE_HEALTH_VAL(limit, areq, plvl) \
	((((limit) << M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK) | \
	(((areq) << M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK) | \
	(((plvl) << M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK))

#define MAX_GRANT_PERIOD \
	(M_BKE_GP_GP_BMSK >> \
	M_BKE_GP_GP_SHFT)

#define MAX_GC \
	(M_BKE_GC_GC_BMSK >> \
	(M_BKE_GC_GC_SHFT + 1))

static int bimc_div(int64_t *a, uint32_t b)
{
	if ((*a > 0) && (*a < b)) {
		*a = 0;
		return 1;
	} else {
		return do_div(*a, b);
	}
}

static void set_bke_en(void __iomem *addr, uint32_t index,
		bool req)
{
	uint32_t old_val, new_val;

	old_val = readl_relaxed(M_BKE_EN_ADDR(addr, index));
	new_val = req << M_BKE_EN_EN_SHFT;
	if ((old_val & M_BKE_EN_RMSK) == (new_val))
		return;
	writel_relaxed(((old_val & ~(M_BKE_EN_EN_BMSK)) | (new_val &
				M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(addr, index));
	/* Ensure that BKE register is programmed set before returning */
	wmb();
}

static void set_health_reg(void __iomem *addr, uint32_t rmsk,
	uint8_t index, struct msm_bus_bimc_qos_mode *qmode)
{
	uint32_t reg_val, val0, val;

	/* Note, addr is already passed with right mas_index */
	reg_val = readl_relaxed(addr) & rmsk;
	val0 = BKE_HEALTH_VAL(qmode->rl.qhealth[index].limit_commands,
		qmode->rl.qhealth[index].areq_prio,
		qmode->rl.qhealth[index].prio_level);
	val = ((reg_val & (~(BKE_HEALTH_MASK))) | (val0 & BKE_HEALTH_MASK));
	writel_relaxed(val, addr);
	/*
	 * Ensure that priority for regulator/limiter modes are
	 * set before returning
	 */
	wmb();
}

static void msm_bus_bimc_set_qos_prio(void __iomem *base,
	uint32_t mas_index, uint8_t qmode_sel,
	struct msm_bus_bimc_qos_mode *qmode)
{

	switch (qmode_sel) {
	case BIMC_QOS_MODE_FIXED:
	case BIMC_QOS_MODE_REGULATOR:
	case BIMC_QOS_MODE_LIMITER:
		set_health_reg(M_BKE_HEALTH_3_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_3_CONFIG_RMSK, 3, qmode);
		set_health_reg(M_BKE_HEALTH_2_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_2_CONFIG_RMSK, 2, qmode);
		set_health_reg(M_BKE_HEALTH_1_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_1_CONFIG_RMSK, 1, qmode);
		set_health_reg(M_BKE_HEALTH_0_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_0_CONFIG_RMSK, 0, qmode);
		set_bke_en(base, mas_index, true);
		break;
	case BIMC_QOS_MODE_BYPASS:
		set_bke_en(base, mas_index, false);
		break;
	default:
		break;
	}
}

static void set_qos_bw_regs(void __iomem *baddr, uint32_t mas_index,
	int32_t th, int32_t tm, int32_t tl, uint32_t gp,
	uint32_t gc)
{
	int32_t reg_val, val;
	int32_t bke_reg_val;
	int16_t val2;

	/* Disable BKE before writing to registers as per spec */
	bke_reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index));
	writel_relaxed((bke_reg_val & ~(M_BKE_EN_EN_BMSK)),
		M_BKE_EN_ADDR(baddr, mas_index));

	/* Write values of registers calculated */
	reg_val = readl_relaxed(M_BKE_GP_ADDR(baddr, mas_index))
		& M_BKE_GP_RMSK;
	val =  gp << M_BKE_GP_GP_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_GP_GP_BMSK)) | (val &
		M_BKE_GP_GP_BMSK)), M_BKE_GP_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_GC_ADDR(baddr, mas_index)) &
		M_BKE_GC_RMSK;
	val =  gc << M_BKE_GC_GC_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_GC_GC_BMSK)) | (val &
		M_BKE_GC_GC_BMSK)), M_BKE_GC_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THH_ADDR(baddr, mas_index)) &
		M_BKE_THH_RMSK;
	val =  th << M_BKE_THH_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THH_THRESH_BMSK)) | (val &
		M_BKE_THH_THRESH_BMSK)), M_BKE_THH_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THM_ADDR(baddr, mas_index)) &
		M_BKE_THM_RMSK;
	val2 =	tm << M_BKE_THM_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val2 &
		M_BKE_THM_THRESH_BMSK)), M_BKE_THM_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THL_ADDR(baddr, mas_index)) &
		M_BKE_THL_RMSK;
	val2 =	tl << M_BKE_THL_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THL_THRESH_BMSK)) |
		(val2 & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr,
		mas_index));

	/* Ensure that all bandwidth register writes have completed
	 * before returning
	 */
	wmb();
}

static void bimc_set_static_qos_bw(void __iomem *base, unsigned int qos_freq,
	int mport, struct msm_bus_bimc_qos_bw *qbw)
{
	int32_t bw_mbps, thh = 0, thm, thl, gc;
	int32_t gp;
	u64 temp;

	if (qos_freq == 0) {
		MSM_BUS_DBG("No QoS Frequency.\n");
		return;
	}

	if (!(qbw->bw && qbw->gp)) {
		MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
		return;
	}

	/* Convert bandwidth to MBPS */
	temp = qbw->bw;
	bimc_div(&temp, 1000000);
	bw_mbps = temp;

	/* Grant period in clock cycles
	 * Grant period from bandwidth structure
	 * is in nano seconds, QoS freq is in KHz.
	 * Divide by 1000 to get clock cycles.
	 */
	gp = (qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);

	/* Grant count = BW in MBps * Grant period
	 * in micro seconds
	 */
	gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
	gc = min(gc, MAX_GC);

	/* Medium threshold = -((Medium Threshold percentage *
	 * Grant count) / 100)
	 */
	thm = -((qbw->thmp * gc) / 100);
	qbw->thm = thm;

	/* Low threshold = -(Grant count) */
	thl = -gc;
	qbw->thl = thl;

	MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
			__func__, gp, gc, thm, thl, thh);

	set_qos_bw_regs(base, mport, thh, thm, thl, gp, gc);
}

static int msm_bus_bimc_limit_mport(struct msm_bus_node_device_type *info,
				void __iomem *qos_base, uint32_t qos_off,
				uint32_t qos_delta, uint32_t qos_freq,
				int enable_lim, u64 lim_bw)
{
	int mode;
	int i;
	struct msm_bus_bimc_qos_mode qmode = {0};

	if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
		MSM_BUS_DBG("No QoS Ports to limit\n");
		return 0;
	}

	if ((enable_lim == THROTTLE_ON) && lim_bw) {
		mode =  BIMC_QOS_MODE_LIMITER;

		qmode.rl.qhealth[0].limit_commands = 1;
		qmode.rl.qhealth[1].limit_commands = 0;
		qmode.rl.qhealth[2].limit_commands = 0;
		qmode.rl.qhealth[3].limit_commands = 0;
		for (i = 0; i < NUM_HEALTH_LEVEL; i++) {
			qmode.rl.qhealth[i].prio_level =
					info->node_info->qos_params.prio_lvl;
			qmode.rl.qhealth[i].areq_prio =
					info->node_info->qos_params.prio_rd;
		}

		for (i = 0; i < info->node_info->num_qports; i++) {
			struct msm_bus_bimc_qos_bw qbw;
			/* If not in fixed mode, update bandwidth */
			if (info->node_info->lim_bw != lim_bw) {
				qbw.ws = info->node_info->qos_params.ws;
				qbw.bw = lim_bw;
				qbw.gp = info->node_info->qos_params.gp;
				qbw.thmp = info->node_info->qos_params.thmp;
				bimc_set_static_qos_bw(qos_base, qos_freq,
					info->node_info->qport[i], &qbw);
			}
		}
		info->node_info->lim_bw = lim_bw;
	} else {
		mode = info->node_info->qos_params.mode;
		if (mode != BIMC_QOS_MODE_BYPASS) {
			for (i = 0; i < NUM_HEALTH_LEVEL; i++) {
				qmode.rl.qhealth[i].prio_level =
					info->node_info->qos_params.prio_lvl;
				qmode.rl.qhealth[i].areq_prio =
					info->node_info->qos_params.prio_rd;
			}
		}
	}

	for (i = 0; i < info->node_info->num_qports; i++)
		msm_bus_bimc_set_qos_prio(qos_base, info->node_info->qport[i],
				mode, &qmode);
	return 0;
}

static bool msm_bus_bimc_update_bw_reg(int mode)
{
	bool ret = false;

	if ((mode == BIMC_QOS_MODE_LIMITER)
		|| (mode == BIMC_QOS_MODE_REGULATOR))
		ret = true;

	return ret;
}

static int msm_bus_bimc_qos_init(struct msm_bus_node_device_type *info,
				void __iomem *qos_base,
				uint32_t qos_off, uint32_t qos_delta,
				uint32_t qos_freq)
{
	int i;
	struct msm_bus_bimc_qos_mode qmode = {0};

	if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
		MSM_BUS_DBG("No QoS Ports to init\n");
		return 0;
	}

	switch (info->node_info->qos_params.mode) {
		/* For now Fixed and regulator are handled the same way. */
	case BIMC_QOS_MODE_FIXED:
	case BIMC_QOS_MODE_REGULATOR:
		for (i = 0; i < NUM_HEALTH_LEVEL; i++) {
			qmode.rl.qhealth[i].prio_level =
				info->node_info->qos_params.prio_lvl;
			qmode.rl.qhealth[i].areq_prio =
				info->node_info->qos_params.prio_rd;
		}
		break;
	case BIMC_QOS_MODE_LIMITER:
		qmode.rl.qhealth[0].limit_commands = 1;
		qmode.rl.qhealth[1].limit_commands = 0;
		qmode.rl.qhealth[2].limit_commands = 0;
		qmode.rl.qhealth[3].limit_commands = 0;
		for (i = 0; i < NUM_HEALTH_LEVEL; i++) {
			qmode.rl.qhealth[i].prio_level =
				info->node_info->qos_params.prio_lvl;
			qmode.rl.qhealth[i].areq_prio =
				info->node_info->qos_params.prio_rd;
		}
		break;
	default:
		break;
	}


	for (i = 0; i < info->node_info->num_qports; i++)
		msm_bus_bimc_set_qos_prio(qos_base, info->node_info->qport[i],
			info->node_info->qos_params.mode, &qmode);

	return 0;
}

static int msm_bus_bimc_set_bw(struct msm_bus_node_device_type *dev,
				void __iomem *qos_base, uint32_t qos_off,
				uint32_t qos_delta, uint32_t qos_freq)
{
	struct msm_bus_bimc_qos_bw qbw;
	struct msm_bus_bimc_qos_mode qmode = {0};
	int i;
	int64_t bw = 0;
	int ret = 0;
	struct msm_bus_node_info_type *info = dev->node_info;
	int mode;

	if (info && info->num_qports &&
		((info->qos_params.mode == BIMC_QOS_MODE_LIMITER))) {
		bw = msm_bus_div64(info->num_qports,
				dev->node_bw[ACTIVE_CTX].sum_ab);

		MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %llu\n",
				info->id, bw);

		if (!info->qport) {
			MSM_BUS_DBG("No qos ports to update!\n");
			goto exit_set_bw;
		}

		qbw.bw = bw + info->qos_params.bw_buffer;

		/* Default to gp of 5us */
		qbw.gp = (info->qos_params.gp ?
				info->qos_params.gp : 5000);
		/* Default to thmp of 50% */
		qbw.thmp = (info->qos_params.thmp ?
				info->qos_params.thmp : 50);
		/*
		 * If the BW vote is 0 then set the QoS mode to
		 * Fixed/0/0.
		 */
		if (bw) {
			qmode.rl.qhealth[0].limit_commands = 1;
			qmode.rl.qhealth[1].limit_commands = 0;
			qmode.rl.qhealth[2].limit_commands = 0;
			qmode.rl.qhealth[3].limit_commands = 0;
			mode = info->qos_params.mode;
		} else {
			mode =	BIMC_QOS_MODE_FIXED;
		}

		for (i = 0; i < info->num_qports; i++) {
			msm_bus_bimc_set_qos_prio(qos_base,
				info->qport[i], mode, &qmode);
			if (bw)
				bimc_set_static_qos_bw(qos_base, qos_freq,
					info->qport[i], &qbw);
		}
	}
exit_set_bw:
	return ret;
}

int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev)
{
	if (!bus_dev)
		return -ENODEV;
	bus_dev->fabdev->noc_ops.qos_init = msm_bus_bimc_qos_init;
	bus_dev->fabdev->noc_ops.set_bw = msm_bus_bimc_set_bw;
	bus_dev->fabdev->noc_ops.limit_mport = msm_bus_bimc_limit_mport;
	bus_dev->fabdev->noc_ops.update_bw_reg =
					msm_bus_bimc_update_bw_reg;
	return 0;
}
EXPORT_SYMBOL(msm_bus_bimc_set_ops);
Loading