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

Commit d60d4256 authored by David Dai's avatar David Dai
Browse files

msm: msm_bus: Add SBM disconnect mechanism



In order to support MM CX power collapse for sm8150,
manually configure SBM to disable traffic before power
collapse to prevent traffic during shutdown.

Change-Id: I77e8c97d78385f47acf0bbc5086cd7672a22ba00
Signed-off-by: default avatarDavid Dai <daidavid1@codeaurora.org>
parent ed1e6248
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
	((vote_y & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT))

static int msm_bus_dev_init_qos(struct device *dev, void *data);
static int msm_bus_dev_sbm_config(struct device *dev, bool enable);

static struct list_head bcm_query_list_inorder[VCD_MAX_CNT];
static struct msm_bus_node_device_type *cur_rsc;
@@ -562,6 +563,7 @@ int msm_bus_commit_data(struct list_head *clist)

	list_for_each_entry_safe(node, node_tmp, clist, link) {
		bcm_clist_add(node);
		msm_bus_dev_sbm_config(&node->dev, false);
	}

	if (!cur_rsc) {
@@ -654,6 +656,7 @@ int msm_bus_commit_data(struct list_head *clist)
	list_for_each_entry_safe(node, node_tmp, clist, link) {
		if (unlikely(node->node_info->defer_qos))
			msm_bus_dev_init_qos(&node->dev, NULL);
		msm_bus_dev_sbm_config(&node->dev, true);
	}

exit_msm_bus_commit_data:
@@ -1005,6 +1008,47 @@ static int msm_bus_dev_init_qos(struct device *dev, void *data)
	return ret;
}

static int msm_bus_dev_sbm_config(struct device *dev, bool enable)
{
	int ret = 0;
	struct msm_bus_node_device_type *node_dev = NULL;
	struct msm_bus_node_device_type *fab_dev = NULL;

	node_dev = to_msm_bus_node(dev);
	if (!node_dev) {
		MSM_BUS_ERR("%s: Unable to get node device info", __func__);
		return -ENXIO;
	}

	if (!node_dev->node_info->num_disable_ports)
		return 0;

	if ((node_dev->node_bw[DUAL_CTX].sum_ab ||
		node_dev->node_bw[DUAL_CTX].max_ib) && !enable)
		return 0;
	else if ((!node_dev->node_bw[DUAL_CTX].sum_ab &&
		!node_dev->node_bw[DUAL_CTX].max_ib) && enable)
		return 0;

	fab_dev = to_msm_bus_node(node_dev->node_info->bus_device);
	if (!fab_dev) {
		MSM_BUS_ERR("%s: Unable to get bus device info for %d",
			__func__,
			node_dev->node_info->id);
		return -ENXIO;
	}

	if (fab_dev->fabdev &&
			fab_dev->fabdev->noc_ops.sbm_config) {
		ret = fab_dev->fabdev->noc_ops.sbm_config(
			node_dev,
			fab_dev->fabdev->qos_base,
			fab_dev->fabdev->sbm_offset,
			enable);
	}
	return ret;
}

static int msm_bus_fabric_init(struct device *dev,
			struct msm_bus_node_device_type *pdata)
{
@@ -1041,6 +1085,7 @@ static int msm_bus_fabric_init(struct device *dev,
	fabdev->qos_freq = pdata->fabdev->qos_freq;
	fabdev->bus_type = pdata->fabdev->bus_type;
	fabdev->bypass_qos_prg = pdata->fabdev->bypass_qos_prg;
	fabdev->sbm_offset = pdata->fabdev->sbm_offset;
	msm_bus_fab_init_noc_ops(node_dev);

	fabdev->qos_base = devm_ioremap(dev,
@@ -1293,6 +1338,8 @@ static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata,
	node_info->num_bcm_devs = pdata_node_info->num_bcm_devs;
	node_info->num_rsc_devs = pdata_node_info->num_rsc_devs;
	node_info->num_qports = pdata_node_info->num_qports;
	node_info->num_disable_ports = pdata_node_info->num_disable_ports;
	node_info->disable_ports = pdata_node_info->disable_ports;
	node_info->virt_dev = pdata_node_info->virt_dev;
	node_info->is_fab_dev = pdata_node_info->is_fab_dev;
	node_info->is_bcm_dev = pdata_node_info->is_bcm_dev;
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, 2018, 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
@@ -47,6 +47,7 @@ struct msm_bus_noc_info {
	uint32_t qos_baseoffset;
	uint32_t qos_delta;
	uint32_t *mas_modes;
	uint32_t sbm_offset;
	struct msm_bus_noc_commit cdata[NUM_CTX];
};

+93 −1
Original line number Diff line number Diff line
/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2018, 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
@@ -12,6 +12,9 @@

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

#include <linux/bitops.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/msm-bus-board.h>
@@ -29,9 +32,23 @@
#define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT)
#define MIN_SAT_FIELD	1
#define MIN_BW_FIELD	1
#define READ_TIMEOUT_MS	msecs_to_jiffies(1)
#define READ_DELAY_US	10

#define NOC_QOS_REG_BASE(b, o)		((b) + (o))

/*Sideband Manager Disable Macros*/
#define DISABLE_SBM_FLAGOUTCLR0_LOW_OFF		0x80
#define DISABLE_SBM_FLAGOUTCLR0_HIGH_OFF	0x84
#define DISABLE_SBM_FLAGOUTSET0_LOW_OFF		0x88
#define DISABLE_SBM_FLAGOUTSET0_HIGH_OFF	0x8C
#define DISABLE_SBM_FLAGOUTSTATUS0_LOW_OFF	0x90
#define DISABLE_SBM_FLAGOUTSTATUS0_HIGH_OFF	0x94
#define DISABLE_SBM_SENSEIN0_LOW_OFF		0x100
#define DISABLE_SBM_SENSEIN0_HIGH_OFF		0x104

#define DISABLE_SBM_REG_BASE(b, o, d)	((b) + (o) + (d))

#define NOC_QOS_MAINCTL_LOWn_ADDR(b, o, n, d)	\
	(NOC_QOS_REG_BASE(b, o) + 0x8 + (d) * (n))
enum noc_qos_id_mainctl_lown {
@@ -361,12 +378,87 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info,
	return ret;
}

static int msm_bus_noc_sbm_config(struct msm_bus_node_device_type *node_dev,
				void __iomem *noc_base, uint32_t sbm_offset,
				bool enable)
{
	int ret = 0, idx;
	unsigned long j, j_timeout;
	uint32_t flagset_offset, flagclr_offset, sense_offset;

	for (idx = 0; idx < node_dev->node_info->num_disable_ports; idx++) {
		uint32_t disable_port = node_dev->node_info->disable_ports[idx];
		uint32_t reg_val = 0;

		if (disable_port >= 64) {
			return -EINVAL;
		} else if (disable_port < 32) {
			flagset_offset = DISABLE_SBM_FLAGOUTSET0_LOW_OFF;
			flagclr_offset = DISABLE_SBM_FLAGOUTCLR0_LOW_OFF;
			sense_offset = DISABLE_SBM_SENSEIN0_LOW_OFF;
		} else {
			flagset_offset = DISABLE_SBM_FLAGOUTSET0_HIGH_OFF;
			flagclr_offset = DISABLE_SBM_FLAGOUTCLR0_HIGH_OFF;
			sense_offset = DISABLE_SBM_SENSEIN0_HIGH_OFF;
			disable_port = disable_port - 32;
		}

		if (enable) {
			reg_val |= 0x1 << disable_port;
			writel_relaxed(reg_val, DISABLE_SBM_REG_BASE(noc_base,
					sbm_offset, flagclr_offset));
			/* Ensure SBM reconnect took place */
			wmb();

			j = jiffies;
			j_timeout = j + READ_TIMEOUT_MS;
			while (((0x1 << disable_port) &
				readl_relaxed(DISABLE_SBM_REG_BASE(noc_base,
				sbm_offset, sense_offset)))) {
				udelay(READ_DELAY_US);
				j = jiffies;
				if (time_after(j, j_timeout)) {
					MSM_BUS_ERR("%s: SBM enable timeout.\n",
								 __func__);
					goto sbm_timeout;
				}
			}
		} else {
			reg_val |= 0x1 << disable_port;
			writel_relaxed(reg_val, DISABLE_SBM_REG_BASE(noc_base,
					sbm_offset, flagset_offset));
			/* Ensure SBM disconnect took place */
			wmb();

			j = jiffies;
			j_timeout = j + READ_TIMEOUT_MS;
			while (!((0x1 << disable_port) &
				readl_relaxed(DISABLE_SBM_REG_BASE(noc_base,
				sbm_offset, sense_offset)))) {
				udelay(READ_DELAY_US);
				j = jiffies;
				if (time_after(j, j_timeout)) {
					MSM_BUS_ERR("%s: SBM disable timeout.\n"
								, __func__);
					goto sbm_timeout;
				}
			}
		}
	}
	return ret;

sbm_timeout:
	return -ETIME;

}

int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev)
{
	if (!bus_dev)
		return -ENODEV;

	bus_dev->fabdev->noc_ops.qos_init = msm_bus_noc_qos_init;
	bus_dev->fabdev->noc_ops.sbm_config = msm_bus_noc_sbm_config;

	return 0;
}
+25 −1
Original line number Diff line number Diff line
/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2018, 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
@@ -167,6 +167,11 @@ static struct msm_bus_fab_device_type *get_fab_device_info(
	if (ret)
		dev_dbg(&pdev->dev, "Bus base offset is missing\n");

	ret = of_property_read_u32(dev_node, "qcom,sbm-offset",
			&fab_dev->sbm_offset);
	if (ret)
		dev_dbg(&pdev->dev, "sbm disable offset is missing\n");

	ret = of_property_read_u32(dev_node, "qcom,qos-off",
			&fab_dev->qos_off);
	if (ret)
@@ -375,6 +380,25 @@ static struct msm_bus_node_info_type *get_node_info_data(
	node_info->qport = get_arr(pdev, dev_node, "qcom,qport",
			&node_info->num_qports);

	node_info->num_disable_ports = of_property_count_elems_of_size(dev_node,
			 "qcom,disable-ports", sizeof(uint32_t));

	if (node_info->num_disable_ports < 0) {
		node_info->num_disable_ports = 0;
		dev_dbg(&pdev->dev, "no disable ports\n");
	}

	if (node_info->num_disable_ports) {
		node_info->disable_ports = devm_kcalloc(&pdev->dev,
			node_info->num_disable_ports, sizeof(uint32_t),
							GFP_KERNEL);
		if (!node_info->disable_ports)
			return NULL;
		ret = of_property_read_u32_array(dev_node, "qcom,disable-ports",
					node_info->disable_ports,
					node_info->num_disable_ports);
	}

	if (of_get_property(dev_node, "qcom,connections", &size)) {
		node_info->num_connections = size / sizeof(int);
		node_info->connections = devm_kzalloc(&pdev->dev, size,
+6 −0
Original line number Diff line number Diff line
@@ -52,6 +52,9 @@ struct msm_bus_noc_ops {
			uint32_t qos_delta, uint32_t qos_freq, int enable_lim,
			uint64_t lim_bw);
	bool (*update_bw_reg)(int mode);
	int (*sbm_config)(struct msm_bus_node_device_type *node_dev,
			void __iomem *noc_base, uint32_t sbm_offset,
			bool enable);
};

struct nodebw {
@@ -111,6 +114,7 @@ struct msm_bus_fab_device_type {
	uint32_t base_offset;
	uint32_t qos_freq;
	uint32_t qos_off;
	uint32_t sbm_offset;
	struct msm_bus_noc_ops noc_ops;
	enum msm_bus_hw_sel bus_type;
	bool bypass_qos_prg;
@@ -189,6 +193,8 @@ struct msm_bus_node_info_type {
	struct rule_update_path_info rule;
	uint64_t lim_bw;
	bool defer_qos;
	uint32_t *disable_ports;
	int num_disable_ports;
	struct node_agg_params_type agg_params;
};