@@ -590,6 +590,13 @@ struct arm_cmn_hw_event {
590590 s8 dtc_idx [CMN_MAX_DTCS ];
591591 u8 num_dns ;
592592 u8 dtm_offset ;
593+
594+ /*
595+ * WP config registers are divided to UP and DOWN events. We need to
596+ * keep to track only one of them.
597+ */
598+ DECLARE_BITMAP (wp_idx , CMN_MAX_XPS );
599+
593600 bool wide_sel ;
594601 enum cmn_filter_select filter_sel ;
595602};
@@ -617,6 +624,17 @@ static unsigned int arm_cmn_get_index(u64 x[], unsigned int pos)
617624 return (x [pos / 32 ] >> ((pos % 32 ) * 2 )) & 3 ;
618625}
619626
627+ static void arm_cmn_set_wp_idx (unsigned long * wp_idx , unsigned int pos , bool val )
628+ {
629+ if (val )
630+ set_bit (pos , wp_idx );
631+ }
632+
633+ static unsigned int arm_cmn_get_wp_idx (unsigned long * wp_idx , unsigned int pos )
634+ {
635+ return test_bit (pos , wp_idx );
636+ }
637+
620638struct arm_cmn_event_attr {
621639 struct device_attribute attr ;
622640 enum cmn_model model ;
@@ -1336,9 +1354,34 @@ static const struct attribute_group *arm_cmn_attr_groups[] = {
13361354 NULL
13371355};
13381356
1339- static int arm_cmn_wp_idx (struct perf_event * event )
1357+ static int arm_cmn_find_free_wp_idx (struct arm_cmn_dtm * dtm ,
1358+ struct perf_event * event )
1359+ {
1360+ int wp_idx = CMN_EVENT_EVENTID (event );
1361+
1362+ if (dtm -> wp_event [wp_idx ] >= 0 )
1363+ if (dtm -> wp_event [++ wp_idx ] >= 0 )
1364+ return - ENOSPC ;
1365+
1366+ return wp_idx ;
1367+ }
1368+
1369+ static int arm_cmn_get_assigned_wp_idx (struct perf_event * event ,
1370+ struct arm_cmn_hw_event * hw ,
1371+ unsigned int pos )
13401372{
1341- return CMN_EVENT_EVENTID (event ) + CMN_EVENT_WP_GRP (event );
1373+ return CMN_EVENT_EVENTID (event ) + arm_cmn_get_wp_idx (hw -> wp_idx , pos );
1374+ }
1375+
1376+ static void arm_cmn_claim_wp_idx (struct arm_cmn_dtm * dtm ,
1377+ struct perf_event * event ,
1378+ unsigned int dtc , int wp_idx ,
1379+ unsigned int pos )
1380+ {
1381+ struct arm_cmn_hw_event * hw = to_cmn_hw (event );
1382+
1383+ dtm -> wp_event [wp_idx ] = hw -> dtc_idx [dtc ];
1384+ arm_cmn_set_wp_idx (hw -> wp_idx , pos , wp_idx - CMN_EVENT_EVENTID (event ));
13421385}
13431386
13441387static u32 arm_cmn_wp_config (struct perf_event * event )
@@ -1520,12 +1563,12 @@ static void arm_cmn_event_start(struct perf_event *event, int flags)
15201563 writeq_relaxed (CMN_CC_INIT , cmn -> dtc [i ].base + CMN_DT_PMCCNTR );
15211564 cmn -> dtc [i ].cc_active = true;
15221565 } else if (type == CMN_TYPE_WP ) {
1523- int wp_idx = arm_cmn_wp_idx (event );
15241566 u64 val = CMN_EVENT_WP_VAL (event );
15251567 u64 mask = CMN_EVENT_WP_MASK (event );
15261568
15271569 for_each_hw_dn (hw , dn , i ) {
15281570 void __iomem * base = dn -> pmu_base + CMN_DTM_OFFSET (hw -> dtm_offset );
1571+ int wp_idx = arm_cmn_get_assigned_wp_idx (event , hw , i );
15291572
15301573 writeq_relaxed (val , base + CMN_DTM_WPn_VAL (wp_idx ));
15311574 writeq_relaxed (mask , base + CMN_DTM_WPn_MASK (wp_idx ));
@@ -1550,10 +1593,9 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
15501593 i = hw -> dtc_idx [0 ];
15511594 cmn -> dtc [i ].cc_active = false;
15521595 } else if (type == CMN_TYPE_WP ) {
1553- int wp_idx = arm_cmn_wp_idx (event );
1554-
15551596 for_each_hw_dn (hw , dn , i ) {
15561597 void __iomem * base = dn -> pmu_base + CMN_DTM_OFFSET (hw -> dtm_offset );
1598+ int wp_idx = arm_cmn_get_assigned_wp_idx (event , hw , i );
15571599
15581600 writeq_relaxed (0 , base + CMN_DTM_WPn_MASK (wp_idx ));
15591601 writeq_relaxed (~0ULL , base + CMN_DTM_WPn_VAL (wp_idx ));
@@ -1571,10 +1613,23 @@ struct arm_cmn_val {
15711613 u8 dtm_count [CMN_MAX_DTMS ];
15721614 u8 occupid [CMN_MAX_DTMS ][SEL_MAX ];
15731615 u8 wp [CMN_MAX_DTMS ][4 ];
1616+ u8 wp_combine [CMN_MAX_DTMS ][2 ];
15741617 int dtc_count [CMN_MAX_DTCS ];
15751618 bool cycles ;
15761619};
15771620
1621+ static int arm_cmn_val_find_free_wp_config (struct perf_event * event ,
1622+ struct arm_cmn_val * val , int dtm )
1623+ {
1624+ int wp_idx = CMN_EVENT_EVENTID (event );
1625+
1626+ if (val -> wp [dtm ][wp_idx ])
1627+ if (val -> wp [dtm ][++ wp_idx ])
1628+ return - ENOSPC ;
1629+
1630+ return wp_idx ;
1631+ }
1632+
15781633static void arm_cmn_val_add_event (struct arm_cmn * cmn , struct arm_cmn_val * val ,
15791634 struct perf_event * event )
15801635{
@@ -1606,8 +1661,9 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
16061661 if (type != CMN_TYPE_WP )
16071662 continue ;
16081663
1609- wp_idx = arm_cmn_wp_idx (event );
1610- val -> wp [dtm ][wp_idx ] = CMN_EVENT_WP_COMBINE (event ) + 1 ;
1664+ wp_idx = arm_cmn_val_find_free_wp_config (event , val , dtm );
1665+ val -> wp [dtm ][wp_idx ] = 1 ;
1666+ val -> wp_combine [dtm ][wp_idx >> 1 ] += !!CMN_EVENT_WP_COMBINE (event );
16111667 }
16121668}
16131669
@@ -1631,6 +1687,7 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
16311687 return - ENOMEM ;
16321688
16331689 arm_cmn_val_add_event (cmn , val , leader );
1690+
16341691 for_each_sibling_event (sibling , leader )
16351692 arm_cmn_val_add_event (cmn , val , sibling );
16361693
@@ -1645,7 +1702,7 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
16451702 goto done ;
16461703
16471704 for_each_hw_dn (hw , dn , i ) {
1648- int wp_idx , wp_cmb , dtm = dn -> dtm , sel = hw -> filter_sel ;
1705+ int wp_idx , dtm = dn -> dtm , sel = hw -> filter_sel ;
16491706
16501707 if (val -> dtm_count [dtm ] == CMN_DTM_NUM_COUNTERS )
16511708 goto done ;
@@ -1657,12 +1714,12 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
16571714 if (type != CMN_TYPE_WP )
16581715 continue ;
16591716
1660- wp_idx = arm_cmn_wp_idx (event );
1661- if (val -> wp [ dtm ][ wp_idx ] )
1717+ wp_idx = arm_cmn_val_find_free_wp_config (event , val , dtm );
1718+ if (wp_idx < 0 )
16621719 goto done ;
16631720
1664- wp_cmb = val -> wp [ dtm ][ wp_idx ^ 1 ];
1665- if ( wp_cmb && wp_cmb != CMN_EVENT_WP_COMBINE (event ) + 1 )
1721+ if ( wp_idx & 1 &&
1722+ val -> wp_combine [ dtm ][ wp_idx >> 1 ] != !! CMN_EVENT_WP_COMBINE (event ))
16661723 goto done ;
16671724 }
16681725
@@ -1773,8 +1830,11 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
17731830 struct arm_cmn_dtm * dtm = & cmn -> dtms [hw -> dn [i ].dtm ] + hw -> dtm_offset ;
17741831 unsigned int dtm_idx = arm_cmn_get_index (hw -> dtm_idx , i );
17751832
1776- if (type == CMN_TYPE_WP )
1777- dtm -> wp_event [arm_cmn_wp_idx (event )] = -1 ;
1833+ if (type == CMN_TYPE_WP ) {
1834+ int wp_idx = arm_cmn_get_assigned_wp_idx (event , hw , i );
1835+
1836+ dtm -> wp_event [wp_idx ] = -1 ;
1837+ }
17781838
17791839 if (hw -> filter_sel > SEL_NONE )
17801840 hw -> dn [i ].occupid [hw -> filter_sel ].count -- ;
@@ -1783,6 +1843,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
17831843 writel_relaxed (dtm -> pmu_config_low , dtm -> base + CMN_DTM_PMU_CONFIG );
17841844 }
17851845 memset (hw -> dtm_idx , 0 , sizeof (hw -> dtm_idx ));
1846+ memset (hw -> wp_idx , 0 , sizeof (hw -> wp_idx ));
17861847
17871848 for_each_hw_dtc_idx (hw , j , idx )
17881849 cmn -> dtc [j ].counters [idx ] = NULL ;
@@ -1836,10 +1897,11 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
18361897 if (type == CMN_TYPE_XP ) {
18371898 input_sel = CMN__PMEVCNT0_INPUT_SEL_XP + dtm_idx ;
18381899 } else if (type == CMN_TYPE_WP ) {
1839- int tmp , wp_idx = arm_cmn_wp_idx ( event ) ;
1900+ int tmp , wp_idx ;
18401901 u32 cfg = arm_cmn_wp_config (event );
18411902
1842- if (dtm -> wp_event [wp_idx ] >= 0 )
1903+ wp_idx = arm_cmn_find_free_wp_idx (dtm , event );
1904+ if (wp_idx < 0 )
18431905 goto free_dtms ;
18441906
18451907 tmp = dtm -> wp_event [wp_idx ^ 1 ];
@@ -1848,7 +1910,8 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
18481910 goto free_dtms ;
18491911
18501912 input_sel = CMN__PMEVCNT0_INPUT_SEL_WP + wp_idx ;
1851- dtm -> wp_event [wp_idx ] = hw -> dtc_idx [d ];
1913+
1914+ arm_cmn_claim_wp_idx (dtm , event , d , wp_idx , i );
18521915 writel_relaxed (cfg , dtm -> base + CMN_DTM_WPn_CONFIG (wp_idx ));
18531916 } else {
18541917 struct arm_cmn_nodeid nid = arm_cmn_nid (cmn , dn -> id );
0 commit comments