summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2014-07-26 09:49:05 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2014-07-26 09:49:05 -0700
commit073b22a0e1d44f6f4e2ebc9801d556369e7fe738 (patch)
tree40c4e8448a07d5974dbdb69fd69c580e61cc7612
parentea43cb4aa302993af3947af04b494c708d512d1a (diff)
parent5a398a5b7cbeee984c3aca2022db43ce036c5851 (diff)
Merge "msm: msm_bus: Rules engine for adhoc driver"
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm_bus_rules.txt62
-rw-r--r--drivers/platform/msm/msm_bus/Makefile2
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_adhoc.h35
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c90
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_bimc.c112
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_core.h1
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_dbg.c46
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c123
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_noc.c34
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_of.c1
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c200
-rw-r--r--drivers/platform/msm/msm_bus/msm_bus_rules.c625
-rw-r--r--include/dt-bindings/msm/msm-bus-rule-ops.h32
-rw-r--r--include/linux/msm_bus_rules.h77
14 files changed, 1371 insertions, 69 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus_rules.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus_rules.txt
new file mode 100644
index 000000000000..b68284c8970d
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus_rules.txt
@@ -0,0 +1,62 @@
+MSM Bus static bandwidth rules for adhoc bus topologies
+
+Buses are the interconnects between various devices. The devices are
+connected in different topologies. The static bandwidth rules allow
+setting up SOC specific rules to monitor certain bandwidth requests
+at different bus nodes. When the conditions of the rule are met
+the bus driver will be given a list of actions to be take on specific
+bus master ports (throttle on/off, what bandwidth to throttle to etc).
+
+The mandatory properties for bus driver are:
+
+compatible: "qcom,msm-bus-static-bw-rules"
+
+The static_rules node can have numerous rules for the different bandwidth voting
+conditions to be monitored. The mandatory properties for the rules are
+
+- qcom,src-nodes: An array of phandles denoting the source nodes
+ whose bandwidth votes need to be monitored.
+- qcom,src-field: This field represents the voted field of the
+ source node to be monitored. Possible values
+ are FLD_IB/FLD_AB/FLD_CLK
+- qcom,src-op: The operand to be used when evaluating a node's
+ bandwidth vote with a threshold.Possible values
+ are OP_LE/OP_LT/OP_GT/OP_GE.
+- qcom,thresh: The threshold in Kbytes/s to be used in vote
+ evaluation.
+- qcom,mode: The QoS mode to be applied when this rule's
+ criterion are satisfied. Possible values are
+ THROTTLE_ON/THROTTLE_OFF
+- qcom,dest-node: An array of phandles representing the nodes to
+ which the QoS mode is to be applied.
+
+The optional properties for the rule node are:
+- qcom,dest-bw: The destination bandwidth value in Kbytes/s to
+ be used toward the QoS mode for the destination
+ node.
+
+Example:
+ static-rules {
+ compatible = "qcom,msm-bus-static-bw-rules";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rule@0 {
+ qcom,src-nodes = <&mas_apss>;
+ qcom,src-field = <FLD_IB>;
+ qcom,src-op = <OP_LE>;
+ qcom,thresh = <1599078>;
+ qcom,mode = <THROTTLE_ON>;
+ qcom,dest-node = <&mas_apss>;
+ qcom,dest-bw = <1599078>;
+ };
+
+ rule@1 {
+ qcom,src-nodes = <&mas_apss>;
+ qcom,src-field = <FLD_IB>;
+ qcom,src-op = <OP_GT>;
+ qcom,thresh = <1599078>;
+ qcom,mode = <THROTTLE_OFF>;
+ qcom,dest-node = <&mas_apss>;
+ };
+ };
diff --git a/drivers/platform/msm/msm_bus/Makefile b/drivers/platform/msm/msm_bus/Makefile
index 83270390b393..5b56a2ad7f03 100644
--- a/drivers/platform/msm/msm_bus/Makefile
+++ b/drivers/platform/msm/msm_bus/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_OF) += msm_bus_of.o
obj-$(CONFIG_MSM_RPM_SMD) += msm_bus_rpm_smd.o
ifdef CONFIG_BUS_TOPOLOGY_ADHOC
- obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o
+ obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o msm_bus_rules.o
obj-$(CONFIG_OF) += msm_bus_of_adhoc.o
obj-$(CONFIG_CORESIGHT) += msm_buspm_coresight_adhoc.o
else
diff --git a/drivers/platform/msm/msm_bus/msm_bus_adhoc.h b/drivers/platform/msm/msm_bus/msm_bus_adhoc.h
index f310fba0aeae..a0673b5268ab 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_adhoc.h
+++ b/drivers/platform/msm/msm_bus/msm_bus_adhoc.h
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/msm-bus-board.h>
#include <linux/msm-bus.h>
+#include <linux/msm_bus_rules.h>
#include "msm_bus_core.h"
struct msm_bus_node_device_type;
@@ -37,6 +38,10 @@ struct msm_bus_noc_ops {
int (*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);
+ 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,
+ uint64_t lim_bw);
};
struct nodebw {
@@ -56,6 +61,19 @@ struct msm_bus_fab_device_type {
bool bypass_qos_prg;
};
+struct qos_params_type {
+ int mode;
+ unsigned int prio_lvl;
+ unsigned int prio_rd;
+ unsigned int prio_wr;
+ unsigned int prio1;
+ unsigned int prio0;
+ unsigned int gp;
+ unsigned int thmp;
+ unsigned int ws;
+ int cur_mode;
+};
+
struct msm_bus_node_info_type {
const char *name;
unsigned int id;
@@ -64,6 +82,7 @@ struct msm_bus_node_info_type {
int num_ports;
int num_qports;
int *qport;
+ struct qos_params_type qos_params;
unsigned int num_connections;
bool is_fab_dev;
bool virt_dev;
@@ -72,13 +91,8 @@ struct msm_bus_node_info_type {
unsigned int bus_device_id;
struct device *bus_device;
unsigned int buswidth;
- int ws;
- int mode;
- unsigned int prio_lvl;
- unsigned int prio_rd;
- unsigned int prio_wr;
- unsigned int prio1;
- unsigned int prio0;
+ struct rule_update_path_info rule;
+ uint64_t lim_bw;
};
struct msm_bus_node_device_type {
@@ -94,6 +108,8 @@ struct msm_bus_node_device_type {
struct nodeclk qos_clk;
};
+int msm_bus_enable_limiter(struct msm_bus_node_device_type *nodedev,
+ bool 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);
@@ -107,4 +123,9 @@ extern struct msm_bus_device_node_registration
extern void msm_bus_arb_setops_adhoc(struct msm_bus_arb_ops *arb_ops);
extern int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev);
extern int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev);
+extern int msm_bus_of_get_static_rules(struct platform_device *pdev,
+ struct bus_rule_type **static_rule);
+extern int msm_rules_update_path(struct list_head *input_list,
+ struct list_head *output_list);
+extern void print_all_rules(void);
#endif /* _ARCH_ARM_MACH_MSM_BUS_ADHOC_H */
diff --git a/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c b/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c
index 1fc1a7c5df29..7b475f278a2d 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c
@@ -34,6 +34,8 @@ struct handle_type {
};
static struct handle_type handle_list;
+struct list_head input_list;
+struct list_head apply_list;
DEFINE_MUTEX(msm_bus_adhoc_lock);
@@ -350,6 +352,70 @@ static uint64_t arbitrate_bus_req(struct msm_bus_node_device_type *bus_dev,
return bw_max_hz;
}
+static void del_inp_list(struct list_head *list)
+{
+ struct rule_update_path_info *rule_node;
+ struct rule_update_path_info *rule_node_tmp;
+
+ list_for_each_entry_safe(rule_node, rule_node_tmp, list, link)
+ list_del(&rule_node->link);
+}
+
+static void del_op_list(struct list_head *list)
+{
+ struct rule_apply_rcm_info *rule;
+ struct rule_apply_rcm_info *rule_tmp;
+
+ list_for_each_entry_safe(rule, rule_tmp, list, link)
+ list_del(&rule->link);
+}
+
+static int msm_bus_apply_rules(struct list_head *list, bool after_clk_commit)
+{
+ struct rule_apply_rcm_info *rule;
+ 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 && (rule->after_clk_commit != after_clk_commit))
+ continue;
+
+ dev = bus_find_device(&msm_bus_type, NULL,
+ (void *) &rule->id,
+ msm_bus_device_match_adhoc);
+
+ if (!dev) {
+ MSM_BUS_ERR("Can't find dev node for %d", rule->id);
+ continue;
+ }
+ dev_info = dev->platform_data;
+
+ throttle_en = ((rule->throttle == THROTTLE_ON) ? true : false);
+ ret = msm_bus_enable_limiter(dev_info, throttle_en,
+ rule->lim_bw);
+ if (ret)
+ MSM_BUS_ERR("Failed to set limiter for %d", rule->id);
+ }
+
+ return ret;
+}
+
+static uint64_t get_node_ib(struct msm_bus_node_device_type *bus_dev)
+{
+ int i;
+ int ctx;
+ uint64_t max_ib = 0;
+
+ for (ctx = 0; ctx < NUM_CTX; ctx++) {
+ for (i = 0; i < bus_dev->num_lnodes; i++)
+ max_ib = max(max_ib,
+ bus_dev->lnode_list[i].lnode_ib[ctx]);
+ }
+ return max_ib;
+}
+
static int update_path(int src, int dest, uint64_t req_ib, uint64_t req_bw,
uint64_t cur_ib, uint64_t cur_bw, int src_idx, int ctx)
{
@@ -361,6 +427,8 @@ static int update_path(int src, int dest, uint64_t req_ib, uint64_t req_bw,
int ret = 0;
int *dirty_nodes = NULL;
int num_dirty = 0;
+ struct rule_update_path_info *rule_node;
+ bool rules_registered = msm_rule_are_rules_registered();
src_dev = bus_find_device(&msm_bus_type, NULL,
(void *) &src,
@@ -381,6 +449,9 @@ static int update_path(int src, int dest, uint64_t req_ib, uint64_t req_bw,
}
curr_idx = src_idx;
+ INIT_LIST_HEAD(&input_list);
+ INIT_LIST_HEAD(&apply_list);
+
while (next_dev) {
dev_info = next_dev->platform_data;
@@ -419,11 +490,30 @@ static int update_path(int src, int dest, uint64_t req_ib, uint64_t req_bw,
goto exit_update_path;
}
+ if (rules_registered) {
+ rule_node = &dev_info->node_info->rule;
+ rule_node->id = dev_info->node_info->id;
+ rule_node->ib = get_node_ib(dev_info);
+ rule_node->ab = dev_info->node_ab.ab[ACTIVE_CTX];
+ list_add_tail(&rule_node->link, &input_list);
+ }
+
next_dev = lnode->next_dev;
curr_idx = lnode->next;
}
+ if (rules_registered) {
+ msm_rules_update_path(&input_list, &apply_list);
+ msm_bus_apply_rules(&apply_list, false);
+ }
+
msm_bus_commit_data(dirty_nodes, ctx, num_dirty);
+
+ if (rules_registered) {
+ msm_bus_apply_rules(&apply_list, true);
+ del_inp_list(&input_list);
+ del_op_list(&apply_list);
+ }
exit_update_path:
return ret;
}
diff --git a/drivers/platform/msm/msm_bus/msm_bus_bimc.c b/drivers/platform/msm/msm_bus/msm_bus_bimc.c
index 138041b47028..45c25dbe0ccb 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_bimc.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_bimc.c
@@ -1490,15 +1490,15 @@ static void bke_switch(
set_qos_mode(baddr, mas_index, 0, 0, 0);
}
-static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
+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 (binfo->qos_freq == 0) {
- MSM_BUS_DBG("Zero QoS Frequency\n");
+ if (qos_freq == 0) {
+ MSM_BUS_DBG("No QoS Frequency.\n");
return;
}
@@ -1517,7 +1517,7 @@ static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
* is in nano seconds, QoS freq is in KHz.
* Divide by 1000 to get clock cycles.
*/
- gp = (binfo->qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
+ gp = (qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
/* Grant count = BW in MBps * Grant period
* in micro seconds
@@ -1538,7 +1538,7 @@ static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
__func__, gp, gc, thm, thl, thh);
trace_bus_bke_params(gc, gp, thl, thm, thl);
- set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp, gc);
+ set_qos_bw_regs(base, mport, thh, thm, thl, gp, gc);
}
static void msm_bus_bimc_config_master(
@@ -1587,7 +1587,8 @@ static void msm_bus_bimc_config_master(
qbw.bw = bw;
qbw.gp = info->node_info->bimc_gp;
qbw.thmp = info->node_info->bimc_thmp;
- bimc_set_static_qos_bw(binfo,
+ bimc_set_static_qos_bw(binfo->base,
+ binfo->qos_freq,
info->node_info->qport[i], &qbw);
info->node_info->cur_lim_bw = bw;
MSM_BUS_DBG("%s: Qos is %d reqclk %llu bw %llu",
@@ -1744,7 +1745,8 @@ static void msm_bus_bimc_config_limiter(
qbw.bw = info->cur_lim_bw;
qbw.gp = info->node_info->bimc_gp;
qbw.thmp = info->node_info->bimc_thmp;
- bimc_set_static_qos_bw(binfo,
+ bimc_set_static_qos_bw(binfo->base,
+ binfo->qos_freq,
info->node_info->qport[i], &qbw);
bke_switch(binfo->base,
info->node_info->qport[i],
@@ -1799,7 +1801,8 @@ static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo,
qbw.bw = info->node_info->bimc_bw[0];
qbw.gp = info->node_info->bimc_gp;
qbw.thmp = info->node_info->bimc_thmp;
- bimc_set_static_qos_bw(binfo,
+ bimc_set_static_qos_bw(binfo->base,
+ binfo->qos_freq,
info->node_info->qport[i], &qbw);
}
}
@@ -1888,6 +1891,64 @@ static int msm_bus_bimc_port_unhalt(uint32_t haltid, uint8_t mport)
return 0;
}
+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 mode;
+ int i;
+
+ if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
+ MSM_BUS_DBG("No QoS Ports to limit\n");
+ return 0;
+ }
+
+ if (enable_lim && lim_bw) {
+ mode = BIMC_QOS_MODE_LIMITER;
+
+ if (!info->node_info->lim_bw) {
+ struct msm_bus_bimc_qos_mode qmode;
+ 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 < info->node_info->num_qports; i++) {
+ /* If not in bypass mode, update priority */
+ if (mode != BIMC_QOS_MODE_BYPASS)
+ msm_bus_bimc_set_qos_prio(qos_base,
+ info->node_info->qport[i], mode,
+ &qmode);
+ }
+ }
+
+ 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);
+ }
+ bke_switch(qos_base, info->node_info->qport[i],
+ BKE_ON, mode);
+ }
+ info->node_info->lim_bw = lim_bw;
+ } else {
+ mode = info->node_info->qos_params.mode;
+ for (i = 0; i < info->node_info->num_qports; i++) {
+ bke_switch(qos_base, info->node_info->qport[i],
+ BKE_OFF, mode);
+ }
+ }
+ info->node_info->qos_params.cur_mode = mode;
+ return 0;
+}
+
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,
@@ -1896,31 +1957,39 @@ static int msm_bus_bimc_qos_init(struct msm_bus_node_device_type *info,
int i;
struct msm_bus_bimc_qos_mode qmode;
- switch (info->node_info->mode) {
+ switch (info->node_info->qos_params.mode) {
case BIMC_QOS_MODE_FIXED:
- qmode.fixed.prio_level = info->node_info->prio_lvl;
- qmode.fixed.areq_prio_rd = info->node_info->prio_rd;
- qmode.fixed.areq_prio_wr = info->node_info->prio_wr;
+ qmode.fixed.prio_level = info->node_info->qos_params.prio_lvl;
+ qmode.fixed.areq_prio_rd = info->node_info->qos_params.prio_rd;
+ qmode.fixed.areq_prio_wr = info->node_info->qos_params.prio_wr;
+ 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;
break;
default:
break;
}
- if (!info->node_info->qport) {
+ if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
MSM_BUS_DBG("No QoS Ports to init\n");
return 0;
}
for (i = 0; i < info->node_info->num_qports; i++) {
/* If not in bypass mode, update priority */
- if (info->node_info->mode != BIMC_QOS_MODE_BYPASS) {
+ if (info->node_info->qos_params.mode != BIMC_QOS_MODE_BYPASS) {
msm_bus_bimc_set_qos_prio(qos_base, info->node_info->
- qport[i], info->node_info->mode, &qmode);
+ qport[i], info->node_info->qos_params.mode,
+ &qmode);
/* If not in fixed mode, update bandwidth */
- if (info->node_info->mode != BIMC_QOS_MODE_FIXED) {
+ if (info->node_info->qos_params.mode
+ != BIMC_QOS_MODE_FIXED) {
struct msm_bus_bimc_qos_bw qbw;
- qbw.ws = info->node_info->ws;
+ qbw.ws = info->node_info->qos_params.ws;
msm_bus_bimc_set_qos_bw(qos_base, qos_freq,
info->node_info->qport[i], &qbw);
}
@@ -1928,7 +1997,7 @@ static int msm_bus_bimc_qos_init(struct msm_bus_node_device_type *info,
/* set mode */
msm_bus_bimc_set_qos_mode(qos_base, info->node_info->qport[i],
- info->node_info->mode);
+ info->node_info->qos_params.mode);
}
return 0;
@@ -1944,7 +2013,9 @@ static int msm_bus_bimc_set_bw(struct msm_bus_node_device_type *dev,
int ret = 0;
struct msm_bus_node_info_type *info = dev->node_info;
- if (info && info->num_qports) {
+ if (info && info->num_qports &&
+ ((info->qos_params.mode == BIMC_QOS_MODE_LIMITER) ||
+ (info->qos_params.mode == BIMC_QOS_MODE_REGULATOR))) {
bw = msm_bus_div64(info->num_qports,
dev->node_ab.ab[DUAL_CTX]);
@@ -1958,7 +2029,7 @@ static int msm_bus_bimc_set_bw(struct msm_bus_node_device_type *dev,
}
qbw.bw = bw;
- qbw.ws = info->ws;
+ qbw.ws = info->qos_params.ws;
/* Threshold low = 90% of bw */
qbw.thl = div_s64((90 * bw), 100);
/* Threshold medium = bw */
@@ -2001,6 +2072,7 @@ int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev)
else {
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;
}
return 0;
}
diff --git a/drivers/platform/msm/msm_bus/msm_bus_core.h b/drivers/platform/msm/msm_bus/msm_bus_core.h
index 5e4fd74d0414..00b8f035aaed 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_core.h
+++ b/drivers/platform/msm/msm_bus/msm_bus_core.h
@@ -40,6 +40,7 @@
? -msm_bus_div64((ports), (-bw)) : msm_bus_div64((ports), (bw))) : (bw))
#define INTERLEAVED_VAL(fab_pdata, n) \
((fab_pdata->il_flag) ? (n) : 1)
+#define KBTOB(a) (a * 1000ULL)
enum msm_bus_dbg_op_type {
MSM_BUS_DBG_UNREGISTER = -2,
diff --git a/drivers/platform/msm/msm_bus/msm_bus_dbg.c b/drivers/platform/msm/msm_bus/msm_bus_dbg.c
index 67c0eecd95fd..77c4efb3a417 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_dbg.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_dbg.c
@@ -24,7 +24,9 @@
#include <linux/hrtimer.h>
#include <linux/msm-bus-board.h>
#include <linux/msm-bus.h>
+#include <linux/msm_bus_rules.h>
#include "msm_bus_core.h"
+#include "msm_bus_adhoc.h"
#define CREATE_TRACE_POINTS
#include <trace/events/trace_msm_bus.h>
@@ -59,6 +61,8 @@ struct msm_bus_fab_list {
char buffer[MAX_BUFF_SIZE];
};
+static char *rules_buf;
+
LIST_HEAD(fabdata_list);
LIST_HEAD(cl_list);
@@ -517,6 +521,28 @@ static const struct file_operations fabric_data_fops = {
.read = fabric_data_read,
};
+static ssize_t rules_dbg_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ memset(rules_buf, 0, MAX_BUFF_SIZE);
+ print_rules_buf(rules_buf, MAX_BUFF_SIZE);
+ ret = simple_read_from_buffer(buf, count, ppos,
+ rules_buf, MAX_BUFF_SIZE);
+ return ret;
+}
+
+static int rules_dbg_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations rules_dbg_fops = {
+ .open = rules_dbg_open,
+ .read = rules_dbg_read,
+};
+
static int msm_bus_dbg_record_fabric(const char *fabname, struct dentry *file)
{
struct msm_bus_fab_list *fablist;
@@ -657,7 +683,7 @@ EXPORT_SYMBOL(msm_bus_dbg_commit_data);
static int __init msm_bus_debugfs_init(void)
{
- struct dentry *commit, *shell_client;
+ struct dentry *commit, *shell_client, *rules_dbg;
struct msm_bus_fab_list *fablist;
struct msm_bus_cldata *cldata = NULL;
uint64_t val = 0;
@@ -686,6 +712,16 @@ static int __init msm_bus_debugfs_init(void)
goto err;
}
+ rules_dbg = debugfs_create_dir("rules-dbg", dir);
+ if ((!rules_dbg) || IS_ERR(rules_dbg)) {
+ MSM_BUS_ERR("Couldn't create rules-dbg\n");
+ goto err;
+ }
+
+ if (debugfs_create_file("print_rules", S_IRUGO | S_IWUSR,
+ rules_dbg, &val, &rules_dbg_fops) == NULL)
+ goto err;
+
if (debugfs_create_file("update_request", S_IRUGO | S_IWUSR,
shell_client, &val, &shell_client_en_fops) == NULL)
goto err;
@@ -705,6 +741,12 @@ static int __init msm_bus_debugfs_init(void)
clients, NULL, &msm_bus_dbg_update_request_fops) == NULL)
goto err;
+ rules_buf = kzalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+ if (!rules_buf) {
+ MSM_BUS_ERR("Failed to alloc rules_buf");
+ goto err;
+ }
+
list_for_each_entry(cldata, &cl_list, list) {
if (cldata->pdata->name == NULL) {
MSM_BUS_DBG("Client name not found\n");
@@ -720,6 +762,7 @@ static int __init msm_bus_debugfs_init(void)
commit, (void *)fablist->name, &fabric_data_fops);
if (fablist->file == NULL) {
MSM_BUS_DBG("Cannot create files for commit data\n");
+ kfree(rules_buf);
goto err;
}
}
@@ -748,6 +791,7 @@ static void __exit msm_bus_dbg_teardown(void)
list_del(&fablist->list);
kfree(fablist);
}
+ kfree(rules_buf);
mutex_unlock(&msm_bus_dbg_fablist_lock);
}
module_exit(msm_bus_dbg_teardown);
diff --git a/drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c b/drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c
index 1dd367ed0854..bac77aab2c29 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c
@@ -23,6 +23,13 @@
#include "msm_bus_noc.h"
#include "msm_bus_bimc.h"
+struct static_rules_type {
+ int num_rules;
+ struct bus_rule_type *rules;
+};
+
+static struct static_rules_type static_rules;
+
static int enable_nodeclk(struct nodeclk *nclk)
{
int ret = 0;
@@ -587,6 +594,49 @@ exit_enable_qos_clk:
return ret;
}
+int msm_bus_enable_limiter(struct msm_bus_node_device_type *node_dev,
+ bool enable, uint64_t lim_bw)
+{
+ int ret = 0;
+ struct msm_bus_node_device_type *bus_node_dev;
+
+ if (!node_dev) {
+ MSM_BUS_ERR("No device specified");
+ ret = -ENXIO;
+ goto exit_enable_limiter;
+ }
+
+ if (!node_dev->ap_owned) {
+ MSM_BUS_ERR("Device is not AP owned %d.",
+ node_dev->node_info->id);
+ ret = -ENXIO;
+ goto exit_enable_limiter;
+ }
+
+ bus_node_dev = node_dev->node_info->bus_device->platform_data;
+ if (!bus_node_dev) {
+ MSM_BUS_ERR("Unable to get bus device infofor %d",
+ node_dev->node_info->id);
+ ret = -ENXIO;
+ goto exit_enable_limiter;
+ }
+ if (bus_node_dev->fabdev &&
+ bus_node_dev->fabdev->noc_ops.limit_mport) {
+ msm_bus_qos_enable_clk(node_dev);
+ bus_node_dev->fabdev->noc_ops.limit_mport(
+ node_dev,
+ bus_node_dev->fabdev->qos_base,
+ bus_node_dev->fabdev->base_offset,
+ bus_node_dev->fabdev->qos_off,
+ bus_node_dev->fabdev->qos_freq,
+ enable, lim_bw);
+ msm_bus_qos_disable_clk(node_dev);
+ }
+
+exit_enable_limiter:
+ return ret;
+}
+
static int msm_bus_dev_init_qos(struct device *dev, void *data)
{
int ret = 0;
@@ -619,7 +669,7 @@ static int msm_bus_dev_init_qos(struct device *dev, void *data)
bus_node_info->fabdev->noc_ops.qos_init) {
if (node_dev->ap_owned &&
- (node_dev->node_info->mode) != -1) {
+ (node_dev->node_info->qos_params.mode) != -1) {
if (bus_node_info->fabdev->bypass_qos_prg)
goto exit_init_qos;
@@ -674,6 +724,7 @@ static int msm_bus_fabric_init(struct device *dev,
fabdev->qos_range = pdata->fabdev->qos_range;
fabdev->base_offset = pdata->fabdev->base_offset;
fabdev->qos_off = pdata->fabdev->qos_off;
+ fabdev->qos_freq = pdata->fabdev->qos_freq;
fabdev->bus_type = pdata->fabdev->bus_type;
fabdev->bypass_qos_prg = pdata->fabdev->bypass_qos_prg;
msm_bus_fab_init_noc_ops(node_dev);
@@ -751,12 +802,15 @@ static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata,
node_info->buswidth = pdata_node_info->buswidth;
node_info->virt_dev = pdata_node_info->virt_dev;
node_info->is_fab_dev = pdata_node_info->is_fab_dev;
- node_info->mode = pdata_node_info->mode;
- node_info->prio1 = pdata_node_info->prio1;
- node_info->prio0 = pdata_node_info->prio0;
- node_info->prio_lvl = pdata_node_info->prio_lvl;
- node_info->prio_rd = pdata_node_info->prio_rd;
- node_info->prio_wr = pdata_node_info->prio_wr;
+ 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.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;
+ node_info->qos_params.gp = pdata_node_info->qos_params.gp;
+ node_info->qos_params.thmp = pdata_node_info->qos_params.thmp;
+ node_info->qos_params.ws = pdata_node_info->qos_params.ws;
node_info->dev_connections = devm_kzalloc(bus_dev,
sizeof(struct device *) *
@@ -1015,6 +1069,36 @@ exit_device_probe:
return ret;
}
+static int msm_bus_device_rules_probe(struct platform_device *pdev)
+{
+ struct bus_rule_type *rule_data = NULL;
+ int num_rules = 0;
+
+ num_rules = msm_bus_of_get_static_rules(pdev, &rule_data);
+
+ if (!rule_data)
+ goto exit_rules_probe;
+
+ msm_rule_register(num_rules, rule_data, NULL);
+ static_rules.num_rules = num_rules;
+ static_rules.rules = rule_data;
+ pdev->dev.platform_data = &static_rules;
+
+exit_rules_probe:
+ return 0;
+}
+
+int msm_bus_device_rules_remove(struct platform_device *pdev)
+{
+ struct static_rules_type *static_rules = NULL;
+
+ static_rules = pdev->dev.platform_data;
+ if (static_rules)
+ msm_rule_unregister(static_rules->num_rules,
+ static_rules->rules, NULL);
+ return 0;
+}
+
static int msm_bus_free_dev(struct device *dev, void *data)
{
struct msm_bus_node_device_type *bus_node = NULL;
@@ -1034,6 +1118,21 @@ int msm_bus_device_remove(struct platform_device *pdev)
return 0;
}
+static struct of_device_id rules_match[] = {
+ {.compatible = "qcom,msm-bus-static-bw-rules"},
+ {}
+};
+
+static struct platform_driver msm_bus_rules_driver = {
+ .probe = msm_bus_device_rules_probe,
+ .remove = msm_bus_device_rules_remove,
+ .driver = {
+ .name = "msm_bus_rules_device",
+ .owner = THIS_MODULE,
+ .of_match_table = rules_match,
+ },
+};
+
static struct of_device_id fabric_match[] = {
{.compatible = "qcom,msm-bus-device"},
{}
@@ -1051,7 +1150,15 @@ static struct platform_driver msm_bus_device_driver = {
int __init msm_bus_device_init_driver(void)
{
+ int rc;
+
MSM_BUS_ERR("msm_bus_fabric_init_driver\n");
- return platform_driver_register(&msm_bus_device_driver);
+ rc = platform_driver_register(&msm_bus_device_driver);
+
+ if (rc) {
+ MSM_BUS_ERR("Failed to register bus device driver");
+ return rc;
+ }
+ return platform_driver_register(&msm_bus_rules_driver);
}
subsys_initcall(msm_bus_device_init_driver);
diff --git a/drivers/platform/msm/msm_bus/msm_bus_noc.c b/drivers/platform/msm/msm_bus/msm_bus_noc.c
index 1124b04f7b8c..cea72ad39dbf 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_noc.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_noc.c
@@ -642,8 +642,8 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info,
int ret = 0;
int i;
- prio.p1 = info->node_info->prio1;
- prio.p0 = info->node_info->prio0;
+ prio.p1 = info->node_info->qos_params.prio1;
+ prio.p0 = info->node_info->qos_params.prio0;
if (!info->node_info->qport) {
MSM_BUS_DBG("No QoS Ports to init\n");
@@ -652,26 +652,28 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info,
}
for (i = 0; i < info->node_info->num_qports; i++) {
- if (info->node_info->mode != NOC_QOS_MODE_BYPASS) {
+ if (info->node_info->qos_params.mode != NOC_QOS_MODE_BYPASS) {
noc_set_qos_priority(qos_base, qos_off,
info->node_info->qport[i], qos_delta,
&prio);
- if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
+ if (info->node_info->qos_params.mode !=
+ NOC_QOS_MODE_FIXED) {
struct msm_bus_noc_qos_bw qbw;
- qbw.ws = info->node_info->ws;
+ qbw.ws = info->node_info->qos_params.ws;
qbw.bw = 0;
msm_bus_noc_set_qos_bw(qos_base, qos_off,
- qos_freq,
- info->node_info->qport[i],
- qos_delta,
- info->node_info->mode, &qbw);
+ qos_freq,
+ info->node_info->qport[i],
+ qos_delta,
+ info->node_info->qos_params.mode,
+ &qbw);
}
}
noc_set_qos_mode(qos_base, qos_off, info->node_info->qport[i],
- qos_delta, info->node_info->mode,
- (1 << info->node_info->mode));
+ qos_delta, info->node_info->qos_params.mode,
+ (1 << info->node_info->qos_params.mode));
}
err_qos_init:
return ret;
@@ -687,7 +689,10 @@ static int msm_bus_noc_set_bw(struct msm_bus_node_device_type *dev,
int i;
struct msm_bus_node_info_type *info = dev->node_info;
- if (info && info->num_qports) {
+ if (info && info->num_qports &&
+ ((info->qos_params.mode == NOC_QOS_MODE_REGULATOR) ||
+ (info->qos_params.mode ==
+ NOC_QOS_MODE_LIMITER))) {
struct msm_bus_noc_qos_bw qos_bw;
bw = msm_bus_div64(info->num_qports,
@@ -700,10 +705,10 @@ static int msm_bus_noc_set_bw(struct msm_bus_node_device_type *dev,
}
qos_bw.bw = bw;
- qos_bw.ws = info->ws;
+ 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->mode, &qos_bw);
+ info->qos_params.mode, &qos_bw);
MSM_BUS_DBG("NOC: QoS: Update mas_bw: ws: %u\n",
qos_bw.ws);
}
@@ -736,6 +741,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;
}
return 0;
}
diff --git a/drivers/platform/msm/msm_bus/msm_bus_of.c b/drivers/platform/msm/msm_bus/msm_bus_of.c
index b7a190ec8f26..937fe6399be0 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_of.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_of.c
@@ -22,7 +22,6 @@
#include <linux/msm-bus-board.h>
#include "msm_bus_core.h"
-#define KBTOB(a) (a * 1000ULL)
static const char * const hw_sel_name[] = {"RPM", "NoC", "BIMC", NULL};
static const char * const mode_sel_name[] = {"Fixed", "Limiter", "Bypass",
"Regulator", NULL};
diff --git a/drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c b/drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c
index 4b22d260f45b..1b2a1e778a94 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
+#include <linux/msm_bus_rules.h>
#include "msm_bus_core.h"
#include "msm_bus_adhoc.h"
@@ -153,6 +154,48 @@ fab_dev_err:
return NULL;
}
+static void get_qos_params(
+ struct device_node * const dev_node,
+ struct platform_device * const pdev,
+ struct msm_bus_node_info_type *node_info)
+{
+ const char *qos_mode = NULL;
+ unsigned int ret;
+
+ ret = of_property_read_string(dev_node, "qcom,qos-mode", &qos_mode);
+
+ if (ret)
+ node_info->qos_params.mode = -1;
+ else
+ node_info->qos_params.mode = get_qos_mode(pdev, dev_node,
+ qos_mode);
+
+ of_property_read_u32(dev_node, "qcom,prio-lvl",
+ &node_info->qos_params.prio_lvl);
+
+ of_property_read_u32(dev_node, "qcom,prio1",
+ &node_info->qos_params.prio1);
+
+ of_property_read_u32(dev_node, "qcom,prio0",
+ &node_info->qos_params.prio0);
+
+ of_property_read_u32(dev_node, "qcom,prio-rd",
+ &node_info->qos_params.prio_rd);
+
+ of_property_read_u32(dev_node, "qcom,prio-wr",
+ &node_info->qos_params.prio_wr);
+
+ of_property_read_u32(dev_node, "qcom,gp",
+ &node_info->qos_params.gp);
+
+ of_property_read_u32(dev_node, "qcom,thmp",
+ &node_info->qos_params.thmp);
+
+ of_property_read_u32(dev_node, "qcom,ws",
+ &node_info->qos_params.ws);
+}
+
+
static struct msm_bus_node_info_type *get_node_info_data(
struct device_node * const dev_node,
struct platform_device * const pdev)
@@ -163,7 +206,6 @@ static struct msm_bus_node_info_type *get_node_info_data(
int i;
struct device_node *con_node;
struct device_node *bus_dev;
- const char *qos_mode = NULL;
node_info = devm_kzalloc(&pdev->dev,
sizeof(struct msm_bus_node_info_type),
@@ -244,22 +286,7 @@ static struct msm_bus_node_info_type *get_node_info_data(
dev_dbg(&pdev->dev, "slv rpm id is missing\n");
node_info->slv_rpm_id = -1;
}
-
- ret = of_property_read_string(dev_node, "qcom,qos-mode", &qos_mode);
-
- if (ret)
- node_info->mode = -1;
- else
- node_info->mode = get_qos_mode(pdev, dev_node, qos_mode);
-
- ret = of_property_read_u32(dev_node, "qcom,prio-lvl",
- &node_info->prio_lvl);
- ret = of_property_read_u32(dev_node, "qcom,prio1", &node_info->prio1);
- ret = of_property_read_u32(dev_node, "qcom,prio0", &node_info->prio0);
- ret = of_property_read_u32(dev_node, "qcom,prio-rd",
- &node_info->prio_rd);
- ret = of_property_read_u32(dev_node, "qcom,prio-wr",
- &node_info->prio_wr);
+ get_qos_params(dev_node, pdev, node_info);
return node_info;
@@ -395,3 +422,142 @@ node_reg_err:
pdata = NULL;
return NULL;
}
+
+static int msm_bus_of_get_ids(struct platform_device *pdev,
+ struct device_node *dev_node, int **dev_ids,
+ int *num_ids, char *prop_name)
+{
+ int ret = 0;
+ int size, i;
+ struct device_node *rule_node;
+ int *ids = NULL;
+
+ if (of_get_property(dev_node, prop_name, &size)) {
+ *num_ids = size / sizeof(int);
+ ids = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ } else {
+ dev_err(&pdev->dev, "No rule nodes, skipping node");
+ ret = -ENXIO;
+ goto exit_get_ids;
+ }
+
+ *dev_ids = ids;
+ for (i = 0; i < *num_ids; i++) {
+ rule_node = of_parse_phandle(dev_node, prop_name, i);
+ if (IS_ERR_OR_NULL(rule_node)) {
+ dev_err(&pdev->dev, "Can't get rule node id");
+ ret = -ENXIO;
+ goto err_get_ids;
+ }
+
+ if (of_property_read_u32(rule_node, "cell-id",
+ &ids[i])) {
+ dev_err(&pdev->dev, "Can't get rule node id");
+ ret = -ENXIO;
+ goto err_get_ids;
+ }
+ of_node_put(rule_node);
+ }
+exit_get_ids:
+ return ret;
+err_get_ids:
+ devm_kfree(&pdev->dev, ids);
+ of_node_put(rule_node);
+ ids = NULL;
+ return ret;
+}
+
+int msm_bus_of_get_static_rules(struct platform_device *pdev,
+ struct bus_rule_type **static_rules)
+{
+ int ret = 0;
+ struct device_node *of_node, *child_node;
+ int num_rules = 0;
+ int rule_idx = 0;
+ int bw_fld = 0;
+ int i;
+ struct bus_rule_type *static_rule = NULL;
+
+ of_node = pdev->dev.of_node;
+ num_rules = of_get_child_count(of_node);
+ static_rule = devm_kzalloc(&pdev->dev,
+ sizeof(struct bus_rule_type) * num_rules,
+ GFP_KERNEL);
+
+ if (IS_ERR_OR_NULL(static_rule)) {
+ ret = -ENOMEM;
+ goto exit_static_rules;
+ }
+
+ *static_rules = static_rule;
+ for_each_child_of_node(of_node, child_node) {
+ ret = msm_bus_of_get_ids(pdev, child_node,
+ &static_rule[rule_idx].src_id,
+ &static_rule[rule_idx].num_src,
+ "qcom,src-nodes");
+
+ ret = msm_bus_of_get_ids(pdev, child_node,
+ &static_rule[rule_idx].dst_node,
+ &static_rule[rule_idx].num_dst,
+ "qcom,dest-node");
+
+ ret = of_property_read_u32(child_node, "qcom,src-field",
+ &static_rule[rule_idx].src_field);
+ if (ret) {
+ dev_err(&pdev->dev, "src-field missing");
+ ret = -ENXIO;
+ goto err_static_rules;
+ }
+
+ ret = of_property_read_u32(child_node, "qcom,src-op",
+ &static_rule[rule_idx].op);
+ if (ret) {
+ dev_err(&pdev->dev, "src-op missing");
+ ret = -ENXIO;
+ goto err_static_rules;
+ }
+
+ ret = of_property_read_u32(child_node, "qcom,mode",
+ &static_rule[rule_idx].mode);
+ if (ret) {
+ dev_err(&pdev->dev, "mode missing");
+ ret = -ENXIO;
+ goto err_static_rules;
+ }
+
+ ret = of_property_read_u32(child_node, "qcom,thresh", &bw_fld);
+ if (ret) {
+ dev_err(&pdev->dev, "thresh missing");
+ ret = -ENXIO;
+ goto err_static_rules;
+ } else
+ static_rule[rule_idx].thresh = KBTOB(bw_fld);
+
+ ret = of_property_read_u32(child_node, "qcom,dest-bw",
+ &bw_fld);
+ if (ret)
+ static_rule[rule_idx].dst_bw = 0;
+ else
+ static_rule[rule_idx].dst_bw = KBTOB(bw_fld);
+
+ rule_idx++;
+ }
+ ret = rule_idx;
+exit_static_rules:
+ return ret;
+err_static_rules:
+ for (i = 0; i < num_rules; i++) {
+ if (!IS_ERR_OR_NULL(static_rule)) {
+ if (!IS_ERR_OR_NULL(static_rule[i].src_id))
+ devm_kfree(&pdev->dev,
+ static_rule[i].src_id);
+ if (!IS_ERR_OR_NULL(static_rule[i].dst_node))
+ devm_kfree(&pdev->dev,
+ static_rule[i].dst_node);
+ devm_kfree(&pdev->dev, static_rule);
+ }
+ }
+ devm_kfree(&pdev->dev, *static_rules);
+ static_rules = NULL;
+ return ret;
+}
diff --git a/drivers/platform/msm/msm_bus/msm_bus_rules.c b/drivers/platform/msm/msm_bus/msm_bus_rules.c
new file mode 100644
index 000000000000..cb3b2561d7bb
--- /dev/null
+++ b/drivers/platform/msm/msm_bus/msm_bus_rules.c
@@ -0,0 +1,625 @@
+/* Copyright (c) 2014, 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.
+ */
+
+#include <linux/list_sort.h>
+#include <linux/msm-bus-board.h>
+#include <linux/msm_bus_rules.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct node_vote_info {
+ int id;
+ u64 ib;
+ u64 ab;
+ u64 clk;
+};
+
+struct rules_def {
+ int rule_id;
+ int num_src;
+ int state;
+ struct node_vote_info *src_info;
+ struct bus_rule_type rule_ops;
+ bool state_change;
+ struct list_head link;
+};
+
+struct rule_node_info {
+ int id;
+ void *data;
+ struct raw_notifier_head rule_notify_list;
+ int cur_rule;
+ int num_rules;
+ struct list_head node_rules;
+ struct list_head link;
+ struct rule_apply_rcm_info apply;
+};
+
+DEFINE_MUTEX(msm_bus_rules_lock);
+static LIST_HEAD(node_list);
+static struct rule_node_info *get_node(u32 id, void *data);
+
+#define LE(op1, op2) (op1 <= op2)
+#define LT(op1, op2) (op1 < op2)
+#define GE(op1, op2) (op1 >= op2)
+#define GT(op1, op2) (op1 > op2)
+#define NB_ID (0x201)
+
+static struct rule_node_info *get_node(u32 id, void *data)
+{
+ struct rule_node_info *node_it = NULL;
+ struct rule_node_info *node_match = NULL;
+
+ list_for_each_entry(node_it, &node_list, link) {
+ if (node_it->id == id) {
+ if ((id == NB_ID)) {
+ if ((node_it->data == data)) {
+ node_match = node_it;
+ break;
+ }
+ } else {
+ node_match = node_it;
+ break;
+ }
+ }
+ }
+ return node_match;
+}
+
+static struct rule_node_info *gen_node(u32 id, void *data)
+{
+ struct rule_node_info *node_it = NULL;
+ struct rule_node_info *node_match = NULL;
+
+ list_for_each_entry(node_it, &node_list, link) {
+ if (node_it->id == id) {
+ node_match = node_it;
+ break;
+ }
+ }
+
+ if (!node_match) {
+ node_match = kzalloc(sizeof(struct rule_node_info), GFP_KERNEL);
+ node_match->id = id;
+ node_match->cur_rule = -1;
+ node_match->num_rules = 0;
+ node_match->data = data;
+ list_add_tail(&node_match->link, &node_list);
+ INIT_LIST_HEAD(&node_match->node_rules);
+ RAW_INIT_NOTIFIER_HEAD(&node_match->rule_notify_list);
+ pr_debug("Added new node %d to list\n", id);
+ }
+ return node_match;
+}
+
+static bool do_compare_op(u64 op1, u64 op2, int op)
+{
+ bool ret = false;
+
+ switch (op) {
+ case OP_LE:
+ ret = LE(op1, op2);
+ break;
+ case OP_LT:
+ ret = LT(op1, op2);
+ break;
+ case OP_GT:
+ ret = GT(op1, op2);
+ break;
+ case OP_GE:
+ ret = GE(op1, op2);
+ break;
+ case OP_NOOP:
+ ret = true;
+ break;
+ default:
+ pr_info("Invalid OP %d", op);
+ break;
+ }
+ return ret;
+}
+
+static void update_src_id_vote(struct rule_update_path_info *inp_node,
+ struct rule_node_info *rule_node)
+{
+ struct rules_def *rule;
+ int i;
+
+ list_for_each_entry(rule, &rule_node->node_rules, link) {
+ for (i = 0; i < rule->num_src; i++) {
+ if (rule->src_info[i].id == inp_node->id) {
+ rule->src_info[i].ib = inp_node->ib;
+ rule->src_info[i].ab = inp_node->ab;
+ rule->src_info[i].clk = inp_node->clk;
+ }
+ }
+ }
+}
+
+static u64 get_field(struct rules_def *rule, int src_id)
+{
+ u64 field = 0;
+ int i;
+
+ for (i = 0; i < rule->num_src; i++) {
+ switch (rule->rule_ops.src_field) {
+ case FLD_IB:
+ field += rule->src_info[i].ib;
+ break;
+ case FLD_AB:
+ field += rule->src_info[i].ab;
+ break;
+ case FLD_CLK:
+ field += rule->src_info[i].clk;
+ break;
+ }
+ }
+
+ return field;
+}
+
+static bool check_rule(struct rules_def *rule,
+ struct rule_update_path_info *inp)
+{
+ bool ret = false;
+
+ if (!rule)
+ return ret;
+
+ switch (rule->rule_ops.op) {
+ case OP_LE:
+ case OP_LT:
+ case OP_GT:
+ 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,
+ rule->rule_ops.op);
+ break;
+ }
+ default:
+ pr_err("Unsupported op %d", rule->rule_ops.op);
+ break;
+ }
+ return ret;
+}
+
+static void match_rule(struct rule_update_path_info *inp_node,
+ struct rule_node_info *node)
+{
+ struct rules_def *rule;
+ int i;
+ bool match_found = false;
+ bool relevant_trans = false;
+
+ list_for_each_entry(rule, &node->node_rules, link) {
+ for (i = 0; i < rule->num_src; i++) {
+ if (rule->src_info[i].id == inp_node->id) {
+ relevant_trans = true;
+ if (check_rule(rule, inp_node)) {
+ node->cur_rule = rule->rule_id;
+ if (rule->state ==
+ RULE_STATE_NOT_APPLIED) {
+ rule->state =
+ RULE_STATE_APPLIED;
+ rule->state_change = true;
+ match_found = true;
+ }
+ break;
+ }
+ }
+ }
+ if (match_found)
+ break;
+ }
+
+ if (!relevant_trans)
+ return;
+
+ if (!match_found)
+ node->cur_rule = -1;
+
+ list_for_each_entry(rule, &node->node_rules, link) {
+ if (rule->rule_id != node->cur_rule) {
+ if (rule->state == RULE_STATE_APPLIED) {
+ rule->state = RULE_STATE_NOT_APPLIED;
+ rule->state_change = true;
+ }
+ }
+ }
+}
+
+static void apply_rule(struct rule_node_info *node,
+ struct list_head *output_list)
+{
+ struct rules_def *rule;
+
+ list_for_each_entry(rule, &node->node_rules, link) {
+ if (node->id == NB_ID) {
+ if (rule->state_change) {
+ rule->state_change = false;
+ raw_notifier_call_chain(&node->rule_notify_list,
+ rule->state, (void *)&rule->rule_ops);
+ }
+ } else {
+ rule->state_change = false;
+ if ((rule->state == RULE_STATE_APPLIED)) {
+ node->apply.id = rule->rule_ops.dst_node[0];
+ node->apply.throttle = rule->rule_ops.mode;
+ node->apply.lim_bw = rule->rule_ops.dst_bw;
+ list_add_tail(&node->apply.link, output_list);
+ }
+ }
+ }
+
+}
+
+int msm_rules_update_path(struct list_head *input_list,
+ struct list_head *output_list)
+{
+ int ret = 0;
+ struct rule_update_path_info *inp_node;
+ struct rule_node_info *node_it = NULL;
+
+ mutex_lock(&msm_bus_rules_lock);
+ list_for_each_entry(inp_node, input_list, link) {
+ list_for_each_entry(node_it, &node_list, link) {
+ update_src_id_vote(inp_node, node_it);
+ match_rule(inp_node, node_it);
+ }
+ }
+
+ list_for_each_entry(node_it, &node_list, link)
+ apply_rule(node_it, output_list);
+
+ mutex_unlock(&msm_bus_rules_lock);
+ return ret;
+}
+
+static bool ops_equal(int op1, int op2)
+{
+ bool ret = false;
+
+ switch (op1) {
+ case OP_GT:
+ case OP_GE:
+ case OP_LT:
+ case OP_LE:
+ if (abs(op1 - op2) <= 1)
+ ret = true;
+ break;
+ default:
+ ret = (op1 == op2);
+ }
+
+ return ret;
+}
+
+static int node_rules_compare(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ struct rules_def *ra = container_of(a, struct rules_def, link);
+ struct rules_def *rb = container_of(b, struct rules_def, link);
+ int ret = -1;
+ int64_t th_diff = 0;
+
+
+ if (ra->rule_ops.mode == rb->rule_ops.mode) {
+ if (ops_equal(ra->rule_ops.op, rb->rule_ops.op)) {
+ if ((ra->rule_ops.op == OP_LT) ||
+ (ra->rule_ops.op == OP_LE)) {
+ th_diff = ra->rule_ops.thresh -
+ rb->rule_ops.thresh;
+ if (th_diff > 0)
+ ret = 1;
+ else
+ ret = -1;
+ } else if ((ra->rule_ops.op == OP_GT) ||
+ (ra->rule_ops.op == OP_GE)) {
+ th_diff = rb->rule_ops.thresh -
+ ra->rule_ops.thresh;
+ if (th_diff > 0)
+ ret = 1;
+ else
+ ret = -1;
+ }
+ } else
+ ret = ra->rule_ops.op - rb->rule_ops.op;
+ } else if ((ra->rule_ops.mode == THROTTLE_OFF) &&
+ (rb->rule_ops.mode == THROTTLE_ON)) {
+ ret = 1;
+ } else if ((ra->rule_ops.mode == THROTTLE_ON) &&
+ (rb->rule_ops.mode == THROTTLE_OFF)) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void print_rules(struct rule_node_info *node_it)
+{
+ struct rules_def *node_rule = NULL;
+ int i;
+
+ if (!node_it) {
+ pr_err("%s: no node for found", __func__);
+ return;
+ }
+
+ pr_info("\n Now printing rules for Node %d cur rule %d\n",
+ node_it->id, node_it->cur_rule);
+ list_for_each_entry(node_rule, &node_it->node_rules, link) {
+ pr_info("\n num Rules %d rule Id %d\n",
+ node_it->num_rules, node_rule->rule_id);
+ pr_info("Rule: src_field %d\n", node_rule->rule_ops.src_field);
+ for (i = 0; i < node_rule->rule_ops.num_src; i++)
+ pr_info("Rule: src %d\n",
+ node_rule->rule_ops.src_id[i]);
+ for (i = 0; i < node_rule->rule_ops.num_dst; i++)
+ pr_info("Rule: dst %d dst_bw %llu\n",
+ node_rule->rule_ops.dst_node[i],
+ node_rule->rule_ops.dst_bw);
+ pr_info("Rule: thresh %llu op %d mode %d State %d\n",
+ node_rule->rule_ops.thresh,
+ node_rule->rule_ops.op,
+ node_rule->rule_ops.mode,
+ node_rule->state);
+ }
+}
+
+void print_all_rules(void)
+{
+ struct rule_node_info *node_it = NULL;
+
+ list_for_each_entry(node_it, &node_list, link)
+ print_rules(node_it);
+}
+
+void print_rules_buf(char *buf, int max_buf)
+{
+ struct rule_node_info *node_it = NULL;
+ struct rules_def *node_rule = NULL;
+ int i;
+ int cnt = 0;
+
+ list_for_each_entry(node_it, &node_list, link) {
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "\n Now printing rules for Node %d cur_rule %d\n",
+ node_it->id, node_it->cur_rule);
+ list_for_each_entry(node_rule, &node_it->node_rules, link) {
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "\nNum Rules:%d ruleId %d STATE:%d change:%d\n",
+ node_it->num_rules, node_rule->rule_id,
+ node_rule->state, node_rule->state_change);
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "Src_field %d\n",
+ node_rule->rule_ops.src_field);
+ for (i = 0; i < node_rule->rule_ops.num_src; i++)
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "Src %d Cur Ib %llu Ab %llu\n",
+ node_rule->rule_ops.src_id[i],
+ node_rule->src_info[i].ib,
+ node_rule->src_info[i].ab);
+ for (i = 0; i < node_rule->rule_ops.num_dst; i++)
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "Dst %d dst_bw %llu\n",
+ node_rule->rule_ops.dst_node[0],
+ node_rule->rule_ops.dst_bw);
+ cnt += scnprintf(buf + cnt, max_buf - cnt,
+ "Thresh %llu op %d mode %d\n",
+ node_rule->rule_ops.thresh,
+ node_rule->rule_ops.op,
+ node_rule->rule_ops.mode);
+ }
+ }
+}
+
+static int copy_rule(struct bus_rule_type *src, struct rules_def *node_rule,
+ struct notifier_block *nb)
+{
+ int i;
+ int ret = 0;
+
+ memcpy(&node_rule->rule_ops, src,
+ sizeof(struct bus_rule_type));
+ node_rule->rule_ops.src_id = kzalloc(
+ (sizeof(int) * node_rule->rule_ops.num_src),
+ GFP_KERNEL);
+ if (!node_rule->rule_ops.src_id) {
+ pr_err("%s:Failed to allocate for src_id",
+ __func__);
+ return -ENOMEM;
+ }
+ memcpy(node_rule->rule_ops.src_id, src->src_id,
+ sizeof(int) * src->num_src);
+
+
+ if (!nb) {
+ node_rule->rule_ops.dst_node = kzalloc(
+ (sizeof(int) * node_rule->rule_ops.num_dst),
+ GFP_KERNEL);
+ if (!node_rule->rule_ops.dst_node) {
+ pr_err("%s:Failed to allocate for src_id",
+ __func__);
+ return -ENOMEM;
+ }
+ memcpy(node_rule->rule_ops.dst_node, src->dst_node,
+ sizeof(int) * src->num_dst);
+ }
+
+ node_rule->num_src = src->num_src;
+ node_rule->src_info = kzalloc(
+ (sizeof(struct node_vote_info) * node_rule->rule_ops.num_src),
+ GFP_KERNEL);
+ if (!node_rule->src_info) {
+ pr_err("%s:Failed to allocate for src_id",
+ __func__);
+ return -ENOMEM;
+ }
+ for (i = 0; i < src->num_src; i++)
+ node_rule->src_info[i].id = src->src_id[i];
+
+ return ret;
+}
+
+void msm_rule_register(int num_rules, struct bus_rule_type *rule,
+ struct notifier_block *nb)
+{
+ struct rule_node_info *node = NULL;
+ int i, j;
+ struct rules_def *node_rule = NULL;
+ int num_dst = 0;
+
+ if (!rule)
+ return;
+
+ mutex_lock(&msm_bus_rules_lock);
+ for (i = 0; i < num_rules; i++) {
+ if (nb)
+ num_dst = 1;
+ else
+ num_dst = rule[i].num_dst;
+
+ for (j = 0; j < num_dst; j++) {
+ int id = 0;
+
+ if (nb)
+ id = NB_ID;
+ else
+ id = rule[i].dst_node[j];
+
+ node = gen_node(id, nb);
+ if (!node) {
+ pr_info("Error getting rule");
+ goto exit_rule_register;
+ }
+ node_rule = kzalloc(sizeof(struct rules_def),
+ GFP_KERNEL);
+ if (!node_rule) {
+ pr_err("%s: Failed to allocate for rule",
+ __func__);
+ goto exit_rule_register;
+ }
+
+ if (copy_rule(&rule[i], node_rule, nb)) {
+ pr_err("Error copying rule");
+ goto exit_rule_register;
+ }
+
+ node_rule->rule_id = node->num_rules++;
+ if (nb)
+ node->data = nb;
+
+ list_add_tail(&node_rule->link, &node->node_rules);
+ }
+ }
+ list_sort(NULL, &node->node_rules, node_rules_compare);
+
+ if (nb)
+ raw_notifier_chain_register(&node->rule_notify_list, nb);
+exit_rule_register:
+ mutex_unlock(&msm_bus_rules_lock);
+ return;
+}
+
+static int comp_rules(struct bus_rule_type *rulea, struct bus_rule_type *ruleb)
+{
+ int ret = 1;
+
+ if (rulea->num_src == ruleb->num_src)
+ ret = memcmp(rulea->src_id, ruleb->src_id,
+ (sizeof(int) * rulea->num_src));
+ if (!ret && (rulea->num_dst == ruleb->num_dst))
+ ret = memcmp(rulea->dst_node, ruleb->dst_node,
+ (sizeof(int) * rulea->num_dst));
+ if (!ret && (rulea->dst_bw == ruleb->dst_bw) &&
+ (rulea->op == ruleb->op) && (rulea->thresh == ruleb->thresh))
+ ret = 0;
+
+ return ret;
+}
+
+void msm_rule_unregister(int num_rules, struct bus_rule_type *rule,
+ struct notifier_block *nb)
+{
+ int i;
+ struct rule_node_info *node = NULL;
+ struct rule_node_info *node_tmp = NULL;
+ struct rules_def *node_rule;
+ struct rules_def *node_rule_tmp;
+ bool match_found = false;
+
+ if (!rule)
+ return;
+
+ mutex_lock(&msm_bus_rules_lock);
+ if (nb) {
+ node = get_node(NB_ID, nb);
+ if (!node)
+ pr_err("%s: Can't find node", __func__);
+
+ list_for_each_entry_safe(node_rule, node_rule_tmp,
+ &node->node_rules, link) {
+ list_del(&node_rule->link);
+ kfree(node_rule);
+ node->num_rules--;
+ }
+ raw_notifier_chain_unregister(&node->rule_notify_list, nb);
+ } else {
+ for (i = 0; i < num_rules; i++) {
+ match_found = false;
+
+ list_for_each_entry(node, &node_list, link) {
+ list_for_each_entry_safe(node_rule,
+ node_rule_tmp, &node->node_rules, link) {
+ if (comp_rules(&node_rule->rule_ops,
+ &rule[i]) == 0) {
+ list_del(&node_rule->link);
+ kfree(node_rule);
+ match_found = true;
+ node->num_rules--;
+ list_sort(NULL,
+ &node->node_rules,
+ node_rules_compare);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ list_for_each_entry_safe(node, node_tmp,
+ &node_list, link) {
+ if (!node->num_rules) {
+ pr_debug("Deleting Rule node %d", node->id);
+ list_del(&node->link);
+ kfree(node);
+ }
+ }
+ mutex_unlock(&msm_bus_rules_lock);
+}
+
+bool msm_rule_are_rules_registered(void)
+{
+ bool ret = false;
+
+ if (list_empty(&node_list))
+ ret = false;
+ else
+ ret = true;
+
+ return ret;
+}
+
diff --git a/include/dt-bindings/msm/msm-bus-rule-ops.h b/include/dt-bindings/msm/msm-bus-rule-ops.h
new file mode 100644
index 000000000000..5ef9c6d77ff1
--- /dev/null
+++ b/include/dt-bindings/msm/msm-bus-rule-ops.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2014, 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.
+ */
+
+#ifndef __MSM_BUS_RULE_OPS_H
+#define __MSM_BUS_RULE_OPS_H
+
+#define FLD_IB 0
+#define FLD_AB 1
+#define FLD_CLK 2
+
+#define OP_LE 0
+#define OP_LT 1
+#define OP_GE 2
+#define OP_GT 3
+#define OP_NOOP 4
+
+#define RULE_STATE_NOT_APPLIED 0
+#define RULE_STATE_APPLIED 1
+
+#define THROTTLE_ON 0
+#define THROTTLE_OFF 1
+
+#endif
diff --git a/include/linux/msm_bus_rules.h b/include/linux/msm_bus_rules.h
new file mode 100644
index 000000000000..1b384566f0c5
--- /dev/null
+++ b/include/linux/msm_bus_rules.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2014, 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.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_BUS_RULES_H
+#define _ARCH_ARM_MACH_MSM_BUS_RULES_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <dt-bindings/msm/msm-bus-rule-ops.h>
+
+#define MAX_NODES (5)
+
+struct rule_update_path_info {
+ u32 id;
+ u64 ab;
+ u64 ib;
+ u64 clk;
+ struct list_head link;
+};
+
+struct rule_apply_rcm_info {
+ u32 id;
+ u64 lim_bw;
+ int throttle;
+ bool after_clk_commit;
+ struct list_head link;
+};
+
+struct bus_rule_type {
+ int num_src;
+ int *src_id;
+ int src_field;
+ int op;
+ u64 thresh;
+ int num_dst;
+ int *dst_node;
+ u64 dst_bw;
+ int mode;
+ void *client_data;
+};
+
+#if (defined(CONFIG_BUS_TOPOLOGY_ADHOC))
+void msm_rule_register(int num_rules, struct bus_rule_type *rule,
+ struct notifier_block *nb);
+void msm_rule_unregister(int num_rules, struct bus_rule_type *rule,
+ struct notifier_block *nb);
+void print_rules_buf(char *buf, int count);
+bool msm_rule_are_rules_registered(void);
+#else
+static inline void msm_rule_register(int num_rules, struct bus_rule_type *rule,
+ struct notifier_block *nb)
+{
+}
+static inline void msm_rule_unregister(int num_rules,
+ struct bus_rule_type *rule,
+ struct notifier_block *nb)
+{
+}
+static inline void print_rules_buf(char *buf, int count)
+{
+}
+static inline bool msm_rule_are_rules_registered(void)
+{
+ return false;
+}
+#endif /* defined(CONFIG_BUS_TOPOLOGY_ADHOC) */
+#endif /* _ARCH_ARM_MACH_MSM_BUS_RULES_H */