diff options
author | David Dai <daidavid1@codeaurora.org> | 2019-08-13 10:53:41 -0400 |
---|---|---|
committer | Georgi Djakov <georgi.djakov@linaro.org> | 2019-08-13 16:02:57 -0400 |
commit | 9e3ce77c116374556d2fb2728bc9e24c67362dd6 (patch) | |
tree | ca7ffc3db077700f50a9af14e1216c780cf2517e | |
parent | cbd5a9c28bb5d2560be34fc6a2b6e60197628433 (diff) |
interconnect: qcom: Add tagging and wake/sleep support for sdm845
Add support for wake and sleep commands by using a tag to indicate
whether or not the aggregate and set requests fall into execution
state specific bucket.
Signed-off-by: David Dai <daidavid1@codeaurora.org>
Reviewed-by: Evan Green <evgreen@chromium.org>
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
-rw-r--r-- | drivers/interconnect/qcom/sdm845.c | 139 |
1 files changed, 108 insertions, 31 deletions
diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index fb526004c82e..93df67345b39 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c | |||
@@ -66,6 +66,22 @@ struct bcm_db { | |||
66 | #define SDM845_MAX_BCM_PER_NODE 2 | 66 | #define SDM845_MAX_BCM_PER_NODE 2 |
67 | #define SDM845_MAX_VCD 10 | 67 | #define SDM845_MAX_VCD 10 |
68 | 68 | ||
69 | /* | ||
70 | * The AMC bucket denotes constraints that are applied to hardware when | ||
71 | * icc_set_bw() completes, whereas the WAKE and SLEEP constraints are applied | ||
72 | * when the execution environment transitions between active and low power mode. | ||
73 | */ | ||
74 | #define QCOM_ICC_BUCKET_AMC 0 | ||
75 | #define QCOM_ICC_BUCKET_WAKE 1 | ||
76 | #define QCOM_ICC_BUCKET_SLEEP 2 | ||
77 | #define QCOM_ICC_NUM_BUCKETS 3 | ||
78 | #define QCOM_ICC_TAG_AMC BIT(QCOM_ICC_BUCKET_AMC) | ||
79 | #define QCOM_ICC_TAG_WAKE BIT(QCOM_ICC_BUCKET_WAKE) | ||
80 | #define QCOM_ICC_TAG_SLEEP BIT(QCOM_ICC_BUCKET_SLEEP) | ||
81 | #define QCOM_ICC_TAG_ACTIVE_ONLY (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE) | ||
82 | #define QCOM_ICC_TAG_ALWAYS (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE |\ | ||
83 | QCOM_ICC_TAG_SLEEP) | ||
84 | |||
69 | /** | 85 | /** |
70 | * struct qcom_icc_node - Qualcomm specific interconnect nodes | 86 | * struct qcom_icc_node - Qualcomm specific interconnect nodes |
71 | * @name: the node name used in debugfs | 87 | * @name: the node name used in debugfs |
@@ -86,8 +102,8 @@ struct qcom_icc_node { | |||
86 | u16 num_links; | 102 | u16 num_links; |
87 | u16 channels; | 103 | u16 channels; |
88 | u16 buswidth; | 104 | u16 buswidth; |
89 | u64 sum_avg; | 105 | u64 sum_avg[QCOM_ICC_NUM_BUCKETS]; |
90 | u64 max_peak; | 106 | u64 max_peak[QCOM_ICC_NUM_BUCKETS]; |
91 | struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE]; | 107 | struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE]; |
92 | size_t num_bcms; | 108 | size_t num_bcms; |
93 | }; | 109 | }; |
@@ -112,8 +128,8 @@ struct qcom_icc_bcm { | |||
112 | const char *name; | 128 | const char *name; |
113 | u32 type; | 129 | u32 type; |
114 | u32 addr; | 130 | u32 addr; |
115 | u64 vote_x; | 131 | u64 vote_x[QCOM_ICC_NUM_BUCKETS]; |
116 | u64 vote_y; | 132 | u64 vote_y[QCOM_ICC_NUM_BUCKETS]; |
117 | bool dirty; | 133 | bool dirty; |
118 | bool keepalive; | 134 | bool keepalive; |
119 | struct bcm_db aux_data; | 135 | struct bcm_db aux_data; |
@@ -555,7 +571,7 @@ inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y, | |||
555 | cmd->wait = true; | 571 | cmd->wait = true; |
556 | } | 572 | } |
557 | 573 | ||
558 | static void tcs_list_gen(struct list_head *bcm_list, | 574 | static void tcs_list_gen(struct list_head *bcm_list, int bucket, |
559 | struct tcs_cmd tcs_list[SDM845_MAX_VCD], | 575 | struct tcs_cmd tcs_list[SDM845_MAX_VCD], |
560 | int n[SDM845_MAX_VCD]) | 576 | int n[SDM845_MAX_VCD]) |
561 | { | 577 | { |
@@ -573,8 +589,8 @@ static void tcs_list_gen(struct list_head *bcm_list, | |||
573 | commit = true; | 589 | commit = true; |
574 | cur_vcd_size = 0; | 590 | cur_vcd_size = 0; |
575 | } | 591 | } |
576 | tcs_cmd_gen(&tcs_list[idx], bcm->vote_x, bcm->vote_y, | 592 | tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket], |
577 | bcm->addr, commit); | 593 | bcm->vote_y[bucket], bcm->addr, commit); |
578 | idx++; | 594 | idx++; |
579 | n[batch]++; | 595 | n[batch]++; |
580 | /* | 596 | /* |
@@ -595,37 +611,55 @@ static void tcs_list_gen(struct list_head *bcm_list, | |||
595 | 611 | ||
596 | static void bcm_aggregate(struct qcom_icc_bcm *bcm) | 612 | static void bcm_aggregate(struct qcom_icc_bcm *bcm) |
597 | { | 613 | { |
598 | size_t i; | 614 | size_t i, bucket; |
599 | u64 agg_avg = 0; | 615 | u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0}; |
600 | u64 agg_peak = 0; | 616 | u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0}; |
601 | u64 temp; | 617 | u64 temp; |
602 | 618 | ||
603 | for (i = 0; i < bcm->num_nodes; i++) { | 619 | for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) { |
604 | temp = bcm->nodes[i]->sum_avg * bcm->aux_data.width; | 620 | for (i = 0; i < bcm->num_nodes; i++) { |
605 | do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels); | 621 | temp = bcm->nodes[i]->sum_avg[bucket] * bcm->aux_data.width; |
606 | agg_avg = max(agg_avg, temp); | 622 | do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels); |
623 | agg_avg[bucket] = max(agg_avg[bucket], temp); | ||
607 | 624 | ||
608 | temp = bcm->nodes[i]->max_peak * bcm->aux_data.width; | 625 | temp = bcm->nodes[i]->max_peak[bucket] * bcm->aux_data.width; |
609 | do_div(temp, bcm->nodes[i]->buswidth); | 626 | do_div(temp, bcm->nodes[i]->buswidth); |
610 | agg_peak = max(agg_peak, temp); | 627 | agg_peak[bucket] = max(agg_peak[bucket], temp); |
611 | } | 628 | } |
612 | 629 | ||
613 | temp = agg_avg * 1000ULL; | 630 | temp = agg_avg[bucket] * 1000ULL; |
614 | do_div(temp, bcm->aux_data.unit); | 631 | do_div(temp, bcm->aux_data.unit); |
615 | bcm->vote_x = temp; | 632 | bcm->vote_x[bucket] = temp; |
616 | 633 | ||
617 | temp = agg_peak * 1000ULL; | 634 | temp = agg_peak[bucket] * 1000ULL; |
618 | do_div(temp, bcm->aux_data.unit); | 635 | do_div(temp, bcm->aux_data.unit); |
619 | bcm->vote_y = temp; | 636 | bcm->vote_y[bucket] = temp; |
637 | } | ||
620 | 638 | ||
621 | if (bcm->keepalive && bcm->vote_x == 0 && bcm->vote_y == 0) { | 639 | if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 && |
622 | bcm->vote_x = 1; | 640 | bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) { |
623 | bcm->vote_y = 1; | 641 | bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1; |
642 | bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1; | ||
643 | bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1; | ||
644 | bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1; | ||
624 | } | 645 | } |
625 | 646 | ||
626 | bcm->dirty = false; | 647 | bcm->dirty = false; |
627 | } | 648 | } |
628 | 649 | ||
650 | static void qcom_icc_pre_aggregate(struct icc_node *node) | ||
651 | { | ||
652 | size_t i; | ||
653 | struct qcom_icc_node *qn; | ||
654 | |||
655 | qn = node->data; | ||
656 | |||
657 | for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { | ||
658 | qn->sum_avg[i] = 0; | ||
659 | qn->max_peak[i] = 0; | ||
660 | } | ||
661 | } | ||
662 | |||
629 | static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, | 663 | static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, |
630 | u32 peak_bw, u32 *agg_avg, u32 *agg_peak) | 664 | u32 peak_bw, u32 *agg_avg, u32 *agg_peak) |
631 | { | 665 | { |
@@ -634,12 +668,19 @@ static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, | |||
634 | 668 | ||
635 | qn = node->data; | 669 | qn = node->data; |
636 | 670 | ||
671 | if (!tag) | ||
672 | tag = QCOM_ICC_TAG_ALWAYS; | ||
673 | |||
674 | for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { | ||
675 | if (tag & BIT(i)) { | ||
676 | qn->sum_avg[i] += avg_bw; | ||
677 | qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw); | ||
678 | } | ||
679 | } | ||
680 | |||
637 | *agg_avg += avg_bw; | 681 | *agg_avg += avg_bw; |
638 | *agg_peak = max_t(u32, *agg_peak, peak_bw); | 682 | *agg_peak = max_t(u32, *agg_peak, peak_bw); |
639 | 683 | ||
640 | qn->sum_avg = *agg_avg; | ||
641 | qn->max_peak = *agg_peak; | ||
642 | |||
643 | for (i = 0; i < qn->num_bcms; i++) | 684 | for (i = 0; i < qn->num_bcms; i++) |
644 | qn->bcms[i]->dirty = true; | 685 | qn->bcms[i]->dirty = true; |
645 | 686 | ||
@@ -675,7 +716,7 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) | |||
675 | * Construct the command list based on a pre ordered list of BCMs | 716 | * Construct the command list based on a pre ordered list of BCMs |
676 | * based on VCD. | 717 | * based on VCD. |
677 | */ | 718 | */ |
678 | tcs_list_gen(&commit_list, cmds, commit_idx); | 719 | tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_AMC, cmds, commit_idx); |
679 | 720 | ||
680 | if (!commit_idx[0]) | 721 | if (!commit_idx[0]) |
681 | return ret; | 722 | return ret; |
@@ -693,6 +734,41 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) | |||
693 | return ret; | 734 | return ret; |
694 | } | 735 | } |
695 | 736 | ||
737 | INIT_LIST_HEAD(&commit_list); | ||
738 | |||
739 | for (i = 0; i < qp->num_bcms; i++) { | ||
740 | /* | ||
741 | * Only generate WAKE and SLEEP commands if a resource's | ||
742 | * requirements change as the execution environment transitions | ||
743 | * between different power states. | ||
744 | */ | ||
745 | if (qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_WAKE] != | ||
746 | qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_SLEEP] || | ||
747 | qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_WAKE] != | ||
748 | qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_SLEEP]) { | ||
749 | list_add_tail(&qp->bcms[i]->list, &commit_list); | ||
750 | } | ||
751 | } | ||
752 | |||
753 | if (list_empty(&commit_list)) | ||
754 | return ret; | ||
755 | |||
756 | tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx); | ||
757 | |||
758 | ret = rpmh_write_batch(qp->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx); | ||
759 | if (ret) { | ||
760 | pr_err("Error sending WAKE RPMH requests (%d)\n", ret); | ||
761 | return ret; | ||
762 | } | ||
763 | |||
764 | tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx); | ||
765 | |||
766 | ret = rpmh_write_batch(qp->dev, RPMH_SLEEP_STATE, cmds, commit_idx); | ||
767 | if (ret) { | ||
768 | pr_err("Error sending SLEEP RPMH requests (%d)\n", ret); | ||
769 | return ret; | ||
770 | } | ||
771 | |||
696 | return ret; | 772 | return ret; |
697 | } | 773 | } |
698 | 774 | ||
@@ -738,6 +814,7 @@ static int qnoc_probe(struct platform_device *pdev) | |||
738 | provider = &qp->provider; | 814 | provider = &qp->provider; |
739 | provider->dev = &pdev->dev; | 815 | provider->dev = &pdev->dev; |
740 | provider->set = qcom_icc_set; | 816 | provider->set = qcom_icc_set; |
817 | provider->pre_aggregate = qcom_icc_pre_aggregate; | ||
741 | provider->aggregate = qcom_icc_aggregate; | 818 | provider->aggregate = qcom_icc_aggregate; |
742 | provider->xlate = of_icc_xlate_onecell; | 819 | provider->xlate = of_icc_xlate_onecell; |
743 | INIT_LIST_HEAD(&provider->nodes); | 820 | INIT_LIST_HEAD(&provider->nodes); |