diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2014-07-26 09:49:05 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2014-07-26 09:49:05 -0700 |
commit | 073b22a0e1d44f6f4e2ebc9801d556369e7fe738 (patch) | |
tree | 40c4e8448a07d5974dbdb69fd69c580e61cc7612 | |
parent | ea43cb4aa302993af3947af04b494c708d512d1a (diff) | |
parent | 5a398a5b7cbeee984c3aca2022db43ce036c5851 (diff) |
Merge "msm: msm_bus: Rules engine for adhoc driver"
-rw-r--r-- | Documentation/devicetree/bindings/arm/msm/msm_bus_rules.txt | 62 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_adhoc.h | 35 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c | 90 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_bimc.c | 112 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_core.h | 1 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_dbg.c | 46 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_fabric_adhoc.c | 123 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_noc.c | 34 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_of.c | 1 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_of_adhoc.c | 200 | ||||
-rw-r--r-- | drivers/platform/msm/msm_bus/msm_bus_rules.c | 625 | ||||
-rw-r--r-- | include/dt-bindings/msm/msm-bus-rule-ops.h | 32 | ||||
-rw-r--r-- | include/linux/msm_bus_rules.h | 77 |
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 */ |