msm: msm_bus: Add NoC limiter and regulator mode for adhoc driver

Add the ability to switch NoC masters to be in limiter and regulator mode
for the adhoc bus driver. These modes offer differing degrees of
throttling the io traffic from NoC master ports if needed.

Change-Id: If2f868430ebccff1a11aad7d90fa5b352ea2c876
Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
This commit is contained in:
Girish Mahadevan 2014-12-17 13:31:45 -07:00
parent ce52cb4930
commit 855f769218
10 changed files with 225 additions and 41 deletions

View File

@ -85,6 +85,11 @@ qcom,prio-rd: Read priority for a BIMC bus master (Can be 0/1/2)
qcom,prio-wr: Write priority for a BIMC bus master (Can be 0/1/2)
qcom,prio0: Priority low signal for a NoC bus master
(Can be 0/1/2).
qcom,reg-prio1: Regulator mode Priority high signal for a NoC bus master if the master port is in
regulator QoS mode
qcom,reg-prio0: Regulator Priority low signal for a NoC bus master if the master port is in
regulator Qos mode.
(Can be 0/1/2).
qcom,prio1: Priority high signal for a NoC bus master
qcom,bw_buffer: Optional parameter in KBytes used to specify a buffer value that should be added to
the voted bandwidth value to figure out the limiting bandwidth for a master port.

View File

@ -40,7 +40,7 @@ struct msm_bus_noc_ops {
uint32_t qos_delta, uint32_t qos_freq);
int (*limit_mport)(struct msm_bus_node_device_type *dev,
void __iomem *qos_base, uint32_t qos_off,
uint32_t qos_delta, uint32_t qos_freq, bool enable_lim,
uint32_t qos_delta, uint32_t qos_freq, int enable_lim,
uint64_t lim_bw);
bool (*update_bw_reg)(int mode);
};
@ -71,6 +71,8 @@ struct qos_params_type {
unsigned int prio_wr;
unsigned int prio1;
unsigned int prio0;
unsigned int reg_prio1;
unsigned int reg_prio0;
unsigned int gp;
unsigned int thmp;
unsigned int ws;
@ -119,7 +121,7 @@ struct msm_bus_node_device_type {
};
int msm_bus_enable_limiter(struct msm_bus_node_device_type *nodedev,
bool throttle_en, uint64_t lim_bw);
int throttle_en, uint64_t lim_bw);
int msm_bus_update_clks(struct msm_bus_node_device_type *nodedev,
int ctx, int **dirty_nodes, int *num_dirty);
int msm_bus_commit_data(int *dirty_nodes, int ctx, int num_dirty);

View File

@ -473,7 +473,6 @@ static int msm_bus_apply_rules(struct list_head *list, bool after_clk_commit)
struct device *dev = NULL;
struct msm_bus_node_device_type *dev_info = NULL;
int ret = 0;
bool throttle_en = false;
list_for_each_entry(rule, list, link) {
if (!rule)
@ -492,12 +491,11 @@ static int msm_bus_apply_rules(struct list_head *list, bool after_clk_commit)
}
dev_info = dev->platform_data;
throttle_en = ((rule->throttle == THROTTLE_ON) ? true : false);
ret = msm_bus_enable_limiter(dev_info, throttle_en,
ret = msm_bus_enable_limiter(dev_info, rule->throttle,
rule->lim_bw);
if (ret)
MSM_BUS_ERR("Failed to set limiter for %d", rule->id);
trace_bus_rules_apply(rule->id, rule->lim_bw, throttle_en);
trace_bus_rules_apply(rule->id, rule->lim_bw, rule->throttle);
}
return ret;

View File

@ -1906,7 +1906,7 @@ static int msm_bus_bimc_port_unhalt(uint32_t haltid, uint8_t mport)
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,
bool enable_lim, u64 lim_bw)
int enable_lim, u64 lim_bw)
{
int mode;
int i;
@ -1916,7 +1916,7 @@ static int msm_bus_bimc_limit_mport(struct msm_bus_node_device_type *info,
return 0;
}
if (enable_lim && lim_bw) {
if ((enable_lim == THROTTLE_ON) && lim_bw) {
mode = BIMC_QOS_MODE_LIMITER;
if (!info->node_info->lim_bw) {

View File

@ -663,7 +663,7 @@ exit_enable_qos_clk:
}
int msm_bus_enable_limiter(struct msm_bus_node_device_type *node_dev,
bool enable, uint64_t lim_bw)
int enable, uint64_t lim_bw)
{
int ret = 0;
struct msm_bus_node_device_type *bus_node_dev;
@ -886,6 +886,8 @@ static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata,
node_info->qos_params.mode = pdata_node_info->qos_params.mode;
node_info->qos_params.prio1 = pdata_node_info->qos_params.prio1;
node_info->qos_params.prio0 = pdata_node_info->qos_params.prio0;
node_info->qos_params.reg_prio1 = pdata_node_info->qos_params.reg_prio1;
node_info->qos_params.reg_prio0 = pdata_node_info->qos_params.reg_prio0;
node_info->qos_params.prio_lvl = pdata_node_info->qos_params.prio_lvl;
node_info->qos_params.prio_rd = pdata_node_info->qos_params.prio_rd;
node_info->qos_params.prio_wr = pdata_node_info->qos_params.prio_wr;

View File

@ -28,6 +28,8 @@
#define QOS_DEFAULT_DELTA 0x80
#define MAX_BW_FIELD (NOC_QOS_BWn_BW_BMSK >> NOC_QOS_BWn_BW_SHFT)
#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 NOC_QOS_REG_BASE(b, o) ((b) + (o))
@ -94,10 +96,12 @@ enum noc_qos_id_saturationn {
static int noc_div(uint64_t *a, uint32_t b)
{
if ((*a > 0) && (*a < b))
if ((*a > 0) && (*a < b)) {
*a = 0;
return 1;
else
} else {
return do_div(*a, b);
}
}
/**
@ -116,9 +120,12 @@ static uint64_t noc_bw(uint32_t bw_field, uint32_t qos_freq)
return res * 1000000ULL;
}
static uint32_t noc_bw_ceil(long int bw_field, uint32_t qos_freq)
/**
* Calculate the max BW in Bytes/s for a given time-base.
*/
static uint32_t noc_bw_ceil(long int bw_field, uint32_t qos_freq_khz)
{
uint64_t bw_temp = 2 * qos_freq * bw_field;
uint64_t bw_temp = 2 * qos_freq_khz * bw_field;
uint32_t scale = 1000 * BW_SCALE;
noc_div(&bw_temp, scale);
return bw_temp * 1000000;
@ -145,18 +152,21 @@ static uint32_t noc_ws(uint64_t bw, uint32_t sat, uint32_t qos_freq)
#define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase))
/* Calculate bandwidth field value for requested bandwidth */
static uint32_t noc_bw_field(uint64_t bw, uint32_t qos_freq)
static uint32_t noc_bw_field(uint64_t bw_bps, uint32_t qos_freq_khz)
{
uint32_t bw_field = 0;
if (bw) {
if (bw_bps) {
uint32_t rem;
uint64_t bw_capped = min_t(uint64_t, bw, MAX_BW(qos_freq));
uint64_t bw_capped = min_t(uint64_t, bw_bps,
MAX_BW(qos_freq_khz));
uint64_t bwc = bw_capped * BW_SCALE;
uint64_t qf = 2 * qos_freq * 1000;
uint64_t qf = 2 * qos_freq_khz * 1000;
rem = noc_div(&bwc, qf);
bw_field = (uint32_t)min_t(uint64_t, bwc, MAX_BW_FIELD);
bw_field = (uint32_t)max_t(unsigned long, bwc, MIN_BW_FIELD);
bw_field = (uint32_t)min_t(unsigned long, bw_field,
MAX_BW_FIELD);
}
MSM_BUS_DBG("NOC: bw_field: %u\n", bw_field);
@ -165,7 +175,7 @@ static uint32_t noc_bw_field(uint64_t bw, uint32_t qos_freq)
static uint32_t noc_sat_field(uint64_t bw, uint32_t ws, uint32_t qos_freq)
{
uint32_t sat_field = 0, win;
uint32_t sat_field = 0;
if (bw) {
/* Limit to max bw and scale bw to 100 KB increments */
@ -174,19 +184,16 @@ static uint32_t noc_sat_field(uint64_t bw, uint32_t ws, uint32_t qos_freq)
uint32_t rem = noc_div(&bw_scaled, 100000);
/**
* Calculate saturation from windows size.
* WS must be at least one arb period.
* Saturation must not exceed max field size
*
* Bandwidth is in 100KB increments
* Window size is in ns
* qos_freq is in KHz
SATURATION =
(BW [MBps] * integration window [us] *
time base frequency [MHz]) / (256 * 16)
*/
win = max(ws, 1000000 / qos_freq);
tbw = bw_scaled * win * qos_freq;
tscale = 10000000ULL * BW_SCALE * SAT_SCALE;
tbw = bw_scaled * ws * qos_freq;
tscale = BW_SCALE * SAT_SCALE * 1000000LL;
rem = noc_div(&tbw, tscale);
sat_field = (uint32_t)min_t(uint64_t, tbw, MAX_SAT_FIELD);
sat_field = (uint32_t)max_t(unsigned long, tbw, MIN_SAT_FIELD);
sat_field = (uint32_t)min_t(unsigned long, sat_field,
MAX_SAT_FIELD);
}
MSM_BUS_DBG("NOC: sat_field: %d\n", sat_field);
@ -247,7 +254,6 @@ static void msm_bus_noc_set_qos_bw(void __iomem *base, uint32_t qos_off,
return;
}
/* If Limiter or Regulator modes are not supported, bw not available*/
if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER |
NOC_QOS_PERM_MODE_REGULATOR)) {
@ -729,13 +735,163 @@ static int msm_bus_noc_set_bw(struct msm_bus_node_device_type *dev,
qos_bw.ws = info->qos_params.ws;
msm_bus_noc_set_qos_bw(qos_base, qos_off, qos_freq,
info->qport[i], qos_delta,
info->qos_params.mode, &qos_bw);
(1 << info->qos_params.mode), &qos_bw);
MSM_BUS_DBG("NOC: QoS: Update mas_bw: ws: %u\n",
qos_bw.ws);
}
}
return ret;
}
static int msm_bus_noc_set_lim_mode(struct msm_bus_node_device_type *info,
void __iomem *qos_base, uint32_t qos_off,
uint32_t qos_delta, uint32_t qos_freq,
u64 lim_bw)
{
int i;
if (info && info->node_info->num_qports) {
struct msm_bus_noc_qos_bw qos_bw;
if (lim_bw != info->node_info->lim_bw) {
for (i = 0; i < info->node_info->num_qports; i++) {
qos_bw.bw = lim_bw;
qos_bw.ws = info->node_info->qos_params.ws;
msm_bus_noc_set_qos_bw(qos_base,
qos_off, qos_freq,
info->node_info->qport[i], qos_delta,
(1 << NOC_QOS_MODE_LIMITER), &qos_bw);
}
info->node_info->lim_bw = lim_bw;
}
for (i = 0; i < info->node_info->num_qports; i++) {
noc_set_qos_mode(qos_base, qos_off,
info->node_info->qport[i],
qos_delta,
NOC_QOS_MODE_LIMITER,
(1 << NOC_QOS_MODE_LIMITER));
}
}
return 0;
}
static int msm_bus_noc_set_reg_mode(struct msm_bus_node_device_type *info,
void __iomem *qos_base, uint32_t qos_off,
uint32_t qos_delta, uint32_t qos_freq,
u64 lim_bw)
{
int i;
if (info && info->node_info->num_qports) {
struct msm_bus_noc_qos_priority prio;
struct msm_bus_noc_qos_bw qos_bw;
for (i = 0; i < info->node_info->num_qports; i++) {
prio.p1 =
info->node_info->qos_params.reg_prio1;
prio.p0 =
info->node_info->qos_params.reg_prio0;
noc_set_qos_priority(qos_base, qos_off,
info->node_info->qport[i],
qos_delta,
&prio);
}
if (lim_bw != info->node_info->lim_bw) {
for (i = 0; i < info->node_info->num_qports; i++) {
qos_bw.bw = lim_bw;
qos_bw.ws = info->node_info->qos_params.ws;
msm_bus_noc_set_qos_bw(qos_base, qos_off,
qos_freq,
info->node_info->qport[i], qos_delta,
(1 << NOC_QOS_MODE_REGULATOR), &qos_bw);
}
info->node_info->lim_bw = lim_bw;
}
for (i = 0; i < info->node_info->num_qports; i++) {
noc_set_qos_mode(qos_base, qos_off,
info->node_info->qport[i],
qos_delta,
NOC_QOS_MODE_REGULATOR,
(1 << NOC_QOS_MODE_REGULATOR));
}
}
return 0;
}
static int msm_bus_noc_set_def_mode(struct msm_bus_node_device_type *info,
void __iomem *qos_base, uint32_t qos_off,
uint32_t qos_delta, uint32_t qos_freq,
u64 lim_bw)
{
int i;
for (i = 0; i < info->node_info->num_qports; i++) {
if (info->node_info->qos_params.mode ==
NOC_QOS_MODE_FIXED) {
struct msm_bus_noc_qos_priority prio;
prio.p1 =
info->node_info->qos_params.prio1;
prio.p0 =
info->node_info->qos_params.prio0;
noc_set_qos_priority(qos_base, qos_off,
info->node_info->qport[i],
qos_delta, &prio);
}
noc_set_qos_mode(qos_base, qos_off,
info->node_info->qport[i],
qos_delta,
info->node_info->qos_params.mode,
(1 << info->node_info->qos_params.mode));
}
return 0;
}
static int msm_bus_noc_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 ret = 0;
if (!(info && info->node_info->num_qports)) {
MSM_BUS_ERR("Invalid Node info or no Qports to program");
ret = -ENXIO;
goto exit_limit_mport;
}
if (lim_bw) {
switch (enable_lim) {
case THROTTLE_REG:
{
msm_bus_noc_set_reg_mode(info, qos_base, qos_off,
qos_delta, qos_freq, lim_bw);
break;
}
case THROTTLE_ON:
{
msm_bus_noc_set_lim_mode(info, qos_base, qos_off,
qos_delta, qos_freq, lim_bw);
break;
}
default:
{
msm_bus_noc_set_def_mode(info, qos_base, qos_off,
qos_delta, qos_freq, lim_bw);
break;
}
}
} else
msm_bus_noc_set_def_mode(info, qos_base, qos_off,
qos_delta, qos_freq, lim_bw);
exit_limit_mport:
return ret;
}
int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata,
struct msm_bus_hw_algorithm *hw_algo)
{
@ -763,7 +919,7 @@ int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev)
else {
bus_dev->fabdev->noc_ops.qos_init = msm_bus_noc_qos_init;
bus_dev->fabdev->noc_ops.set_bw = msm_bus_noc_set_bw;
bus_dev->fabdev->noc_ops.limit_mport = NULL;
bus_dev->fabdev->noc_ops.limit_mport = msm_bus_noc_limit_mport;
bus_dev->fabdev->noc_ops.update_bw_reg =
msm_bus_noc_update_bw_reg;
}

View File

@ -198,6 +198,12 @@ static void get_qos_params(
of_property_read_u32(dev_node, "qcom,prio0",
&node_info->qos_params.prio0);
of_property_read_u32(dev_node, "qcom,reg-prio1",
&node_info->qos_params.reg_prio1);
of_property_read_u32(dev_node, "qcom,reg-prio0",
&node_info->qos_params.reg_prio0);
of_property_read_u32(dev_node, "qcom,prio-rd",
&node_info->qos_params.prio_rd);

View File

@ -189,10 +189,7 @@ static bool check_rule(struct rules_def *rule,
case OP_GE:
{
u64 src_field = get_field(rule, inp->id);
if (!src_field)
ret = false;
else
ret = do_compare_op(src_field, rule->rule_ops.thresh,
ret = do_compare_op(src_field, rule->rule_ops.thresh,
rule->rule_ops.op);
break;
}
@ -303,6 +300,16 @@ static bool ops_equal(int op1, int op2)
return ret;
}
static bool is_throttle_rule(int mode)
{
bool ret = true;
if (mode == THROTTLE_OFF)
ret = false;
return ret;
}
static int node_rules_compare(void *priv, struct list_head *a,
struct list_head *b)
{
@ -333,10 +340,16 @@ static int node_rules_compare(void *priv, struct list_head *a,
}
} else
ret = ra->rule_ops.op - rb->rule_ops.op;
} else if (is_throttle_rule(ra->rule_ops.mode) &&
is_throttle_rule(rb->rule_ops.mode)) {
if (ra->rule_ops.mode == THROTTLE_ON)
ret = -1;
else
ret = 1;
} else if ((ra->rule_ops.mode == THROTTLE_OFF) &&
(rb->rule_ops.mode == THROTTLE_ON)) {
is_throttle_rule(rb->rule_ops.mode)) {
ret = 1;
} else if ((ra->rule_ops.mode == THROTTLE_ON) &&
} else if (is_throttle_rule(ra->rule_ops.mode) &&
(rb->rule_ops.mode == THROTTLE_OFF)) {
ret = -1;
}

View File

@ -28,5 +28,7 @@
#define THROTTLE_ON 0
#define THROTTLE_OFF 1
#define THROTTLE_REG 2
#endif

View File

@ -263,14 +263,14 @@ TRACE_EVENT(bus_agg_clk,
TRACE_EVENT(bus_rules_apply,
TP_PROTO(int rule_id, unsigned long long limit_bw, bool throttle_en),
TP_PROTO(int rule_id, unsigned long long limit_bw, int throttle_en),
TP_ARGS(rule_id, limit_bw, throttle_en),
TP_STRUCT__entry(
__field(int, rule_id)
__field(u64, limit_bw)
__field(bool, throttle_en)
__field(int, throttle_en)
),
TP_fast_assign(