Merge branch 'topic/x86-lds-nits' of git://git.kernel.org/pub/scm/linux/kernel/git...
[safe/jmp/linux-2.6] / arch / sparc / kernel / perf_event.c
index 8abdc4d..fa5936e 100644 (file)
@@ -56,7 +56,8 @@ struct cpu_hw_events {
        struct perf_event       *events[MAX_HWEVENTS];
        unsigned long           used_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
        unsigned long           active_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
-       int enabled;
+       u64                     pcr;
+       int                     enabled;
 };
 DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { .enabled = 1, };
 
@@ -68,6 +69,17 @@ struct perf_event_map {
 #define PIC_LOWER      0x02
 };
 
+static unsigned long perf_event_encode(const struct perf_event_map *pmap)
+{
+       return ((unsigned long) pmap->encoding << 16) | pmap->pic_mask;
+}
+
+static void perf_event_decode(unsigned long val, u16 *enc, u8 *msk)
+{
+       *msk = val & 0xff;
+       *enc = val >> 16;
+}
+
 #define C(x) PERF_COUNT_HW_CACHE_##x
 
 #define CACHE_OP_UNSUPPORTED   0xfffe
@@ -201,6 +213,121 @@ static const struct sparc_pmu ultra3_pmu = {
        .lower_nop      = 0x14,
 };
 
+/* Niagara1 is very limited.  The upper PIC is hard-locked to count
+ * only instructions, so it is free running which creates all kinds of
+ * problems.  Some hardware designs make one wonder if the creator
+ * even looked at how this stuff gets used by software.
+ */
+static const struct perf_event_map niagara1_perfmon_event_map[] = {
+       [PERF_COUNT_HW_CPU_CYCLES] = { 0x00, PIC_UPPER },
+       [PERF_COUNT_HW_INSTRUCTIONS] = { 0x00, PIC_UPPER },
+       [PERF_COUNT_HW_CACHE_REFERENCES] = { 0, PIC_NONE },
+       [PERF_COUNT_HW_CACHE_MISSES] = { 0x03, PIC_LOWER },
+};
+
+static const struct perf_event_map *niagara1_event_map(int event_id)
+{
+       return &niagara1_perfmon_event_map[event_id];
+}
+
+static const cache_map_t niagara1_cache_map = {
+[C(L1D)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x03, PIC_LOWER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x03, PIC_LOWER, },
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(L1I)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x00, PIC_UPPER },
+               [C(RESULT_MISS)] = { 0x02, PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_NONSENSE },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(LL)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x07, PIC_LOWER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x07, PIC_LOWER, },
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(DTLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x05, PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(ITLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x04, PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(BPU)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+};
+
+static const struct sparc_pmu niagara1_pmu = {
+       .event_map      = niagara1_event_map,
+       .cache_map      = &niagara1_cache_map,
+       .max_events     = ARRAY_SIZE(niagara1_perfmon_event_map),
+       .upper_shift    = 0,
+       .lower_shift    = 4,
+       .event_mask     = 0x7,
+       .upper_nop      = 0x0,
+       .lower_nop      = 0x0,
+};
+
 static const struct perf_event_map niagara2_perfmon_event_map[] = {
        [PERF_COUNT_HW_CPU_CYCLES] = { 0x02ff, PIC_UPPER | PIC_LOWER },
        [PERF_COUNT_HW_INSTRUCTIONS] = { 0x02ff, PIC_UPPER | PIC_LOWER },
@@ -215,14 +342,102 @@ static const struct perf_event_map *niagara2_event_map(int event_id)
        return &niagara2_perfmon_event_map[event_id];
 }
 
+static const cache_map_t niagara2_cache_map = {
+[C(L1D)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x0208, PIC_UPPER | PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0302, PIC_UPPER | PIC_LOWER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { 0x0210, PIC_UPPER | PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0302, PIC_UPPER | PIC_LOWER, },
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(L1I)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x02ff, PIC_UPPER | PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0301, PIC_UPPER | PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_NONSENSE },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(LL)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x0208, PIC_UPPER | PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0330, PIC_UPPER | PIC_LOWER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { 0x0210, PIC_UPPER | PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0320, PIC_UPPER | PIC_LOWER, },
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(DTLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0x0b08, PIC_UPPER | PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(ITLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { 0xb04, PIC_UPPER | PIC_LOWER, },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(BPU)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_WRITE) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+       [ C(OP_PREFETCH) ] = {
+               [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+               [ C(RESULT_MISS)   ] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+};
+
 static const struct sparc_pmu niagara2_pmu = {
        .event_map      = niagara2_event_map,
+       .cache_map      = &niagara2_cache_map,
        .max_events     = ARRAY_SIZE(niagara2_perfmon_event_map),
        .upper_shift    = 19,
        .lower_shift    = 6,
        .event_mask     = 0xfff,
        .hv_bit         = 0x8,
-       .irq_bit        = 0x03,
+       .irq_bit        = 0x30,
        .upper_nop      = 0x220,
        .lower_nop      = 0x220,
 };
@@ -250,23 +465,30 @@ static u64 nop_for_index(int idx)
                              sparc_pmu->lower_nop, idx);
 }
 
-static inline void sparc_pmu_enable_event(struct hw_perf_event *hwc,
-                                           int idx)
+static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
 {
        u64 val, mask = mask_for_index(idx);
 
-       val = pcr_ops->read();
-       pcr_ops->write((val & ~mask) | hwc->config);
+       val = cpuc->pcr;
+       val &= ~mask;
+       val |= hwc->config;
+       cpuc->pcr = val;
+
+       pcr_ops->write(cpuc->pcr);
 }
 
-static inline void sparc_pmu_disable_event(struct hw_perf_event *hwc,
-                                            int idx)
+static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
 {
        u64 mask = mask_for_index(idx);
        u64 nop = nop_for_index(idx);
-       u64 val = pcr_ops->read();
+       u64 val;
+
+       val = cpuc->pcr;
+       val &= ~mask;
+       val |= nop;
+       cpuc->pcr = val;
 
-       pcr_ops->write((val & ~mask) | nop);
+       pcr_ops->write(cpuc->pcr);
 }
 
 void hw_perf_enable(void)
@@ -281,7 +503,7 @@ void hw_perf_enable(void)
        cpuc->enabled = 1;
        barrier();
 
-       val = pcr_ops->read();
+       val = cpuc->pcr;
 
        for (i = 0; i < MAX_HWEVENTS; i++) {
                struct perf_event *cp = cpuc->events[i];
@@ -293,7 +515,9 @@ void hw_perf_enable(void)
                val |= hwc->config_base;
        }
 
-       pcr_ops->write(val);
+       cpuc->pcr = val;
+
+       pcr_ops->write(cpuc->pcr);
 }
 
 void hw_perf_disable(void)
@@ -306,10 +530,12 @@ void hw_perf_disable(void)
 
        cpuc->enabled = 0;
 
-       val = pcr_ops->read();
+       val = cpuc->pcr;
        val &= ~(PCR_UTRACE | PCR_STRACE |
                 sparc_pmu->hv_bit | sparc_pmu->irq_bit);
-       pcr_ops->write(val);
+       cpuc->pcr = val;
+
+       pcr_ops->write(cpuc->pcr);
 }
 
 static u32 read_pmc(int idx)
@@ -341,7 +567,7 @@ static void write_pmc(int idx, u64 val)
 }
 
 static int sparc_perf_event_set_period(struct perf_event *event,
-                                        struct hw_perf_event *hwc, int idx)
+                                      struct hw_perf_event *hwc, int idx)
 {
        s64 left = atomic64_read(&hwc->period_left);
        s64 period = hwc->sample_period;
@@ -381,19 +607,19 @@ static int sparc_pmu_enable(struct perf_event *event)
        if (test_and_set_bit(idx, cpuc->used_mask))
                return -EAGAIN;
 
-       sparc_pmu_disable_event(hwc, idx);
+       sparc_pmu_disable_event(cpuc, hwc, idx);
 
        cpuc->events[idx] = event;
        set_bit(idx, cpuc->active_mask);
 
        sparc_perf_event_set_period(event, hwc, idx);
-       sparc_pmu_enable_event(hwc, idx);
+       sparc_pmu_enable_event(cpuc, hwc, idx);
        perf_event_update_userpage(event);
        return 0;
 }
 
 static u64 sparc_perf_event_update(struct perf_event *event,
-                                    struct hw_perf_event *hwc, int idx)
+                                  struct hw_perf_event *hwc, int idx)
 {
        int shift = 64 - 32;
        u64 prev_raw_count, new_raw_count;
@@ -423,7 +649,7 @@ static void sparc_pmu_disable(struct perf_event *event)
        int idx = hwc->idx;
 
        clear_bit(idx, cpuc->active_mask);
-       sparc_pmu_disable_event(hwc, idx);
+       sparc_pmu_disable_event(cpuc, hwc, idx);
 
        barrier();
 
@@ -437,18 +663,29 @@ static void sparc_pmu_disable(struct perf_event *event)
 static void sparc_pmu_read(struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
+
        sparc_perf_event_update(event, hwc, hwc->idx);
 }
 
 static void sparc_pmu_unthrottle(struct perf_event *event)
 {
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
        struct hw_perf_event *hwc = &event->hw;
-       sparc_pmu_enable_event(hwc, hwc->idx);
+
+       sparc_pmu_enable_event(cpuc, hwc, hwc->idx);
 }
 
 static atomic_t active_events = ATOMIC_INIT(0);
 static DEFINE_MUTEX(pmc_grab_mutex);
 
+static void perf_stop_nmi_watchdog(void *unused)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+       stop_nmi_watchdog(NULL);
+       cpuc->pcr = pcr_ops->read();
+}
+
 void perf_event_grab_pmc(void)
 {
        if (atomic_inc_not_zero(&active_events))
@@ -457,7 +694,7 @@ void perf_event_grab_pmc(void)
        mutex_lock(&pmc_grab_mutex);
        if (atomic_read(&active_events) == 0) {
                if (atomic_read(&nmi_active) > 0) {
-                       on_each_cpu(stop_nmi_watchdog, NULL, 1);
+                       on_each_cpu(perf_stop_nmi_watchdog, NULL, 1);
                        BUG_ON(atomic_read(&nmi_active) != 0);
                }
                atomic_inc(&active_events);
@@ -510,12 +747,109 @@ static void hw_perf_event_destroy(struct perf_event *event)
        perf_event_release_pmc();
 }
 
+/* Make sure all events can be scheduled into the hardware at
+ * the same time.  This is simplified by the fact that we only
+ * need to support 2 simultaneous HW events.
+ */
+static int sparc_check_constraints(unsigned long *events, int n_ev)
+{
+       if (n_ev <= perf_max_events) {
+               u8 msk1, msk2;
+               u16 dummy;
+
+               if (n_ev == 1)
+                       return 0;
+               BUG_ON(n_ev != 2);
+               perf_event_decode(events[0], &dummy, &msk1);
+               perf_event_decode(events[1], &dummy, &msk2);
+
+               /* If both events can go on any counter, OK.  */
+               if (msk1 == (PIC_UPPER | PIC_LOWER) &&
+                   msk2 == (PIC_UPPER | PIC_LOWER))
+                       return 0;
+
+               /* If one event is limited to a specific counter,
+                * and the other can go on both, OK.
+                */
+               if ((msk1 == PIC_UPPER || msk1 == PIC_LOWER) &&
+                   msk2 == (PIC_UPPER | PIC_LOWER))
+                       return 0;
+               if ((msk2 == PIC_UPPER || msk2 == PIC_LOWER) &&
+                   msk1 == (PIC_UPPER | PIC_LOWER))
+                       return 0;
+
+               /* If the events are fixed to different counters, OK.  */
+               if ((msk1 == PIC_UPPER && msk2 == PIC_LOWER) ||
+                   (msk1 == PIC_LOWER && msk2 == PIC_UPPER))
+                       return 0;
+
+               /* Otherwise, there is a conflict.  */
+       }
+
+       return -1;
+}
+
+static int check_excludes(struct perf_event **evts, int n_prev, int n_new)
+{
+       int eu = 0, ek = 0, eh = 0;
+       struct perf_event *event;
+       int i, n, first;
+
+       n = n_prev + n_new;
+       if (n <= 1)
+               return 0;
+
+       first = 1;
+       for (i = 0; i < n; i++) {
+               event = evts[i];
+               if (first) {
+                       eu = event->attr.exclude_user;
+                       ek = event->attr.exclude_kernel;
+                       eh = event->attr.exclude_hv;
+                       first = 0;
+               } else if (event->attr.exclude_user != eu ||
+                          event->attr.exclude_kernel != ek ||
+                          event->attr.exclude_hv != eh) {
+                       return -EAGAIN;
+               }
+       }
+
+       return 0;
+}
+
+static int collect_events(struct perf_event *group, int max_count,
+                         struct perf_event *evts[], unsigned long *events)
+{
+       struct perf_event *event;
+       int n = 0;
+
+       if (!is_software_event(group)) {
+               if (n >= max_count)
+                       return -1;
+               evts[n] = group;
+               events[n++] = group->hw.event_base;
+       }
+       list_for_each_entry(event, &group->sibling_list, group_entry) {
+               if (!is_software_event(event) &&
+                   event->state != PERF_EVENT_STATE_OFF) {
+                       if (n >= max_count)
+                               return -1;
+                       evts[n] = event;
+                       events[n++] = event->hw.event_base;
+               }
+       }
+       return n;
+}
+
 static int __hw_perf_event_init(struct perf_event *event)
 {
        struct perf_event_attr *attr = &event->attr;
+       struct perf_event *evts[MAX_HWEVENTS];
        struct hw_perf_event *hwc = &event->hw;
+       unsigned long events[MAX_HWEVENTS];
        const struct perf_event_map *pmap;
        u64 enc;
+       int n;
 
        if (atomic_read(&nmi_active) < 0)
                return -ENODEV;
@@ -531,9 +865,6 @@ static int __hw_perf_event_init(struct perf_event *event)
        } else
                return -EOPNOTSUPP;
 
-       perf_event_grab_pmc();
-       event->destroy = hw_perf_event_destroy;
-
        /* We save the enable bits in the config_base.  So to
         * turn off sampling just write 'config', and to enable
         * things write 'config | config_base'.
@@ -546,13 +877,39 @@ static int __hw_perf_event_init(struct perf_event *event)
        if (!attr->exclude_hv)
                hwc->config_base |= sparc_pmu->hv_bit;
 
+       hwc->event_base = perf_event_encode(pmap);
+
+       enc = pmap->encoding;
+
+       n = 0;
+       if (event->group_leader != event) {
+               n = collect_events(event->group_leader,
+                                  perf_max_events - 1,
+                                  evts, events);
+               if (n < 0)
+                       return -EINVAL;
+       }
+       events[n] = hwc->event_base;
+       evts[n] = event;
+
+       if (check_excludes(evts, n, 1))
+               return -EINVAL;
+
+       if (sparc_check_constraints(events, n + 1))
+               return -EINVAL;
+
+       /* Try to do all error checking before this point, as unwinding
+        * state after grabbing the PMC is difficult.
+        */
+       perf_event_grab_pmc();
+       event->destroy = hw_perf_event_destroy;
+
        if (!hwc->sample_period) {
                hwc->sample_period = MAX_PERIOD;
                hwc->last_period = hwc->sample_period;
                atomic64_set(&hwc->period_left, hwc->sample_period);
        }
 
-       enc = pmap->encoding;
        if (pmap->pic_mask & PIC_UPPER) {
                hwc->idx = PIC_UPPER_INDEX;
                enc <<= sparc_pmu->upper_shift;
@@ -605,7 +962,7 @@ void perf_event_print_debug(void)
 }
 
 static int __kprobes perf_event_nmi_handler(struct notifier_block *self,
-                                             unsigned long cmd, void *__args)
+                                           unsigned long cmd, void *__args)
 {
        struct die_args *args = __args;
        struct perf_sample_data data;
@@ -646,7 +1003,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self,
                        continue;
 
                if (perf_event_overflow(event, 1, &data, regs))
-                       sparc_pmu_disable_event(hwc, idx);
+                       sparc_pmu_disable_event(cpuc, hwc, idx);
        }
 
        return NOTIFY_STOP;
@@ -665,6 +1022,10 @@ static bool __init supported_pmu(void)
                sparc_pmu = &ultra3_pmu;
                return true;
        }
+       if (!strcmp(sparc_pmu_type, "niagara")) {
+               sparc_pmu = &niagara1_pmu;
+               return true;
+       }
        if (!strcmp(sparc_pmu_type, "niagara2")) {
                sparc_pmu = &niagara2_pmu;
                return true;