sparc: Support HW cache events.
[safe/jmp/linux-2.6] / arch / sparc / kernel / perf_event.c
index 2d6a1b1..48375f6 100644 (file)
@@ -68,8 +68,19 @@ struct perf_event_map {
 #define PIC_LOWER      0x02
 };
 
+#define C(x) PERF_COUNT_HW_CACHE_##x
+
+#define CACHE_OP_UNSUPPORTED   0xfffe
+#define CACHE_OP_NONSENSE      0xffff
+
+typedef struct perf_event_map cache_map_t
+                               [PERF_COUNT_HW_CACHE_MAX]
+                               [PERF_COUNT_HW_CACHE_OP_MAX]
+                               [PERF_COUNT_HW_CACHE_RESULT_MAX];
+
 struct sparc_pmu {
        const struct perf_event_map     *(*event_map)(int);
+       const cache_map_t               *cache_map;
        int                             max_events;
        int                             upper_shift;
        int                             lower_shift;
@@ -92,8 +103,96 @@ static const struct perf_event_map *ultra3i_event_map(int event_id)
        return &ultra3i_perfmon_event_map[event_id];
 }
 
+static const cache_map_t ultra3i_cache_map = {
+[C(L1D)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { 0x0a, PIC_LOWER },
+               [C(RESULT_MISS)] = { 0x0a, PIC_UPPER },
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+               [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+       },
+},
+[C(L1I)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
+       },
+       [ 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)] = { 0x0c, PIC_LOWER, },
+               [C(RESULT_MISS)] = { 0x0c, PIC_UPPER, },
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = { 0x0c, PIC_LOWER },
+               [C(RESULT_MISS)] = { 0x0c, PIC_UPPER },
+       },
+       [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)] = { 0x12, PIC_UPPER, },
+       },
+       [ 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)] = { 0x11, PIC_UPPER, },
+       },
+       [ 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 ultra3i_pmu = {
        .event_map      = ultra3i_event_map,
+       .cache_map      = &ultra3i_cache_map,
        .max_events     = ARRAY_SIZE(ultra3i_perfmon_event_map),
        .upper_shift    = 11,
        .lower_shift    = 4,
@@ -375,6 +474,37 @@ void perf_event_release_pmc(void)
        }
 }
 
+static const struct perf_event_map *sparc_map_cache_event(u64 config)
+{
+       unsigned int cache_type, cache_op, cache_result;
+       const struct perf_event_map *pmap;
+
+       if (!sparc_pmu->cache_map)
+               return ERR_PTR(-ENOENT);
+
+       cache_type = (config >>  0) & 0xff;
+       if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+               return ERR_PTR(-EINVAL);
+
+       cache_op = (config >>  8) & 0xff;
+       if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+               return ERR_PTR(-EINVAL);
+
+       cache_result = (config >> 16) & 0xff;
+       if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+               return ERR_PTR(-EINVAL);
+
+       pmap = &((*sparc_pmu->cache_map)[cache_type][cache_op][cache_result]);
+
+       if (pmap->encoding == CACHE_OP_UNSUPPORTED)
+               return ERR_PTR(-ENOENT);
+
+       if (pmap->encoding == CACHE_OP_NONSENSE)
+               return ERR_PTR(-EINVAL);
+
+       return pmap;
+}
+
 static void hw_perf_event_destroy(struct perf_event *event)
 {
        perf_event_release_pmc();
@@ -390,12 +520,17 @@ static int __hw_perf_event_init(struct perf_event *event)
        if (atomic_read(&nmi_active) < 0)
                return -ENODEV;
 
-       if (attr->type != PERF_TYPE_HARDWARE)
+       if (attr->type == PERF_TYPE_HARDWARE) {
+               if (attr->config >= sparc_pmu->max_events)
+                       return -EINVAL;
+               pmap = sparc_pmu->event_map(attr->config);
+       } else if (attr->type == PERF_TYPE_HW_CACHE) {
+               pmap = sparc_map_cache_event(attr->config);
+               if (IS_ERR(pmap))
+                       return PTR_ERR(pmap);
+       } else
                return -EOPNOTSUPP;
 
-       if (attr->config >= sparc_pmu->max_events)
-               return -EINVAL;
-
        perf_event_grab_pmc();
        event->destroy = hw_perf_event_destroy;
 
@@ -417,8 +552,6 @@ static int __hw_perf_event_init(struct perf_event *event)
                atomic64_set(&hwc->period_left, hwc->sample_period);
        }
 
-       pmap = sparc_pmu->event_map(attr->config);
-
        enc = pmap->encoding;
        if (pmap->pic_mask & PIC_UPPER) {
                hwc->idx = PIC_UPPER_INDEX;