#define        ARMV8_IDX_TO_COUNTER(x) \
        (((x) - ARMV8_IDX_COUNTER0) & ARMV8_PMU_COUNTER_MASK)
 
+/*
+ * This code is really good
+ */
+
+#define PMEVN_CASE(n, case_macro) \
+       case n: case_macro(n); break
+
+#define PMEVN_SWITCH(x, case_macro)                            \
+       do {                                                    \
+               switch (x) {                                    \
+               PMEVN_CASE(0,  case_macro);                     \
+               PMEVN_CASE(1,  case_macro);                     \
+               PMEVN_CASE(2,  case_macro);                     \
+               PMEVN_CASE(3,  case_macro);                     \
+               PMEVN_CASE(4,  case_macro);                     \
+               PMEVN_CASE(5,  case_macro);                     \
+               PMEVN_CASE(6,  case_macro);                     \
+               PMEVN_CASE(7,  case_macro);                     \
+               PMEVN_CASE(8,  case_macro);                     \
+               PMEVN_CASE(9,  case_macro);                     \
+               PMEVN_CASE(10, case_macro);                     \
+               PMEVN_CASE(11, case_macro);                     \
+               PMEVN_CASE(12, case_macro);                     \
+               PMEVN_CASE(13, case_macro);                     \
+               PMEVN_CASE(14, case_macro);                     \
+               PMEVN_CASE(15, case_macro);                     \
+               PMEVN_CASE(16, case_macro);                     \
+               PMEVN_CASE(17, case_macro);                     \
+               PMEVN_CASE(18, case_macro);                     \
+               PMEVN_CASE(19, case_macro);                     \
+               PMEVN_CASE(20, case_macro);                     \
+               PMEVN_CASE(21, case_macro);                     \
+               PMEVN_CASE(22, case_macro);                     \
+               PMEVN_CASE(23, case_macro);                     \
+               PMEVN_CASE(24, case_macro);                     \
+               PMEVN_CASE(25, case_macro);                     \
+               PMEVN_CASE(26, case_macro);                     \
+               PMEVN_CASE(27, case_macro);                     \
+               PMEVN_CASE(28, case_macro);                     \
+               PMEVN_CASE(29, case_macro);                     \
+               PMEVN_CASE(30, case_macro);                     \
+               default: WARN(1, "Invalid PMEV* index\n");      \
+               }                                               \
+       } while (0)
+
+#define RETURN_READ_PMEVCNTRN(n) \
+       return read_sysreg(pmevcntr##n##_el0)
+static unsigned long read_pmevcntrn(int n)
+{
+       PMEVN_SWITCH(n, RETURN_READ_PMEVCNTRN);
+       return 0;
+}
+
+#define WRITE_PMEVCNTRN(n) \
+       write_sysreg(val, pmevcntr##n##_el0)
+static void write_pmevcntrn(int n, unsigned long val)
+{
+       PMEVN_SWITCH(n, WRITE_PMEVCNTRN);
+}
+
+#define WRITE_PMEVTYPERN(n) \
+       write_sysreg(val, pmevtyper##n##_el0)
+static void write_pmevtypern(int n, unsigned long val)
+{
+       PMEVN_SWITCH(n, WRITE_PMEVTYPERN);
+}
+
 static inline u32 armv8pmu_pmcr_read(void)
 {
        return read_sysreg(pmcr_el0);
        return pmnc & BIT(ARMV8_IDX_TO_COUNTER(idx));
 }
 
-static inline void armv8pmu_select_counter(int idx)
+static inline u32 armv8pmu_read_evcntr(int idx)
 {
        u32 counter = ARMV8_IDX_TO_COUNTER(idx);
-       write_sysreg(counter, pmselr_el0);
-       isb();
-}
 
-static inline u64 armv8pmu_read_evcntr(int idx)
-{
-       armv8pmu_select_counter(idx);
-       return read_sysreg(pmxevcntr_el0);
+       return read_pmevcntrn(counter);
 }
 
 static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
 
 static inline void armv8pmu_write_evcntr(int idx, u64 value)
 {
-       armv8pmu_select_counter(idx);
-       write_sysreg(value, pmxevcntr_el0);
+       u32 counter = ARMV8_IDX_TO_COUNTER(idx);
+
+       write_pmevcntrn(counter, value);
 }
 
 static inline void armv8pmu_write_hw_counter(struct perf_event *event,
 
 static inline void armv8pmu_write_evtype(int idx, u32 val)
 {
-       armv8pmu_select_counter(idx);
+       u32 counter = ARMV8_IDX_TO_COUNTER(idx);
+
        val &= ARMV8_PMU_EVTYPE_MASK;
-       write_sysreg(val, pmxevtyper_el0);
+       write_pmevtypern(counter, val);
 }
 
 static inline void armv8pmu_write_event_type(struct perf_event *event)
                armv8pmu_write_evtype(idx - 1, hwc->config_base);
                armv8pmu_write_evtype(idx, chain_evt);
        } else {
-               armv8pmu_write_evtype(idx, hwc->config_base);
+               if (idx == ARMV8_IDX_CYCLE_COUNTER)
+                       write_sysreg(hwc->config_base, pmccfiltr_el0);
+               else
+                       armv8pmu_write_evtype(idx, hwc->config_base);
        }
 }
 
 static inline void armv8pmu_disable_counter(u32 mask)
 {
        write_sysreg(mask, pmcntenclr_el0);
+       /*
+        * Make sure the effects of disabling the counter are visible before we
+        * start configuring the event.
+        */
+       isb();
 }
 
 static inline void armv8pmu_disable_event_counter(struct perf_event *event)
        armv8pmu_disable_event_counter(event);
 
        /*
-        * Set event (if destined for PMNx counters).
+        * Set event.
         */
        armv8pmu_write_event_type(event);