--- /dev/null
+/*
+ * Meta performance counter support.
+ *  Copyright (C) 2012 Imagination Technologies Ltd
+ *
+ * This code is based on the sh pmu code:
+ *  Copyright (C) 2009 Paul Mundt
+ *
+ * and on the arm pmu code:
+ *  Copyright (C) 2009 picoChip Designs, Ltd., James Iles
+ *  Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/irqchip/metag.h>
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+
+#include <asm/core_reg.h>
+#include <asm/hwthread.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "perf_event.h"
+
+static int _hw_perf_event_init(struct perf_event *);
+static void _hw_perf_event_destroy(struct perf_event *);
+
+/* Determines which core type we are */
+static struct metag_pmu *metag_pmu __read_mostly;
+
+/* Processor specific data */
+static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+
+/* PMU admin */
+const char *perf_pmu_name(void)
+{
+       if (metag_pmu)
+               return metag_pmu->pmu.name;
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(perf_pmu_name);
+
+int perf_num_counters(void)
+{
+       if (metag_pmu)
+               return metag_pmu->max_events;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(perf_num_counters);
+
+static inline int metag_pmu_initialised(void)
+{
+       return !!metag_pmu;
+}
+
+static void release_pmu_hardware(void)
+{
+       int irq;
+       unsigned int version = (metag_pmu->version &
+                       (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS)) >>
+                       METAC_ID_REV_S;
+
+       /* Early cores don't have overflow interrupts */
+       if (version < 0x0104)
+               return;
+
+       irq = internal_irq_map(17);
+       if (irq >= 0)
+               free_irq(irq, (void *)1);
+
+       irq = internal_irq_map(16);
+       if (irq >= 0)
+               free_irq(irq, (void *)0);
+}
+
+static int reserve_pmu_hardware(void)
+{
+       int err = 0, irq[2];
+       unsigned int version = (metag_pmu->version &
+                       (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS)) >>
+                       METAC_ID_REV_S;
+
+       /* Early cores don't have overflow interrupts */
+       if (version < 0x0104)
+               goto out;
+
+       /*
+        * Bit 16 on HWSTATMETA is the interrupt for performance counter 0;
+        * similarly, 17 is the interrupt for performance counter 1.
+        * We can't (yet) interrupt on the cycle counter, because it's a
+        * register, however it holds a 32-bit value as opposed to 24-bit.
+        */
+       irq[0] = internal_irq_map(16);
+       if (irq[0] < 0) {
+               pr_err("unable to map internal IRQ %d\n", 16);
+               goto out;
+       }
+       err = request_irq(irq[0], metag_pmu->handle_irq, IRQF_NOBALANCING,
+                       "metagpmu0", (void *)0);
+       if (err) {
+               pr_err("unable to request IRQ%d for metag PMU counters\n",
+                               irq[0]);
+               goto out;
+       }
+
+       irq[1] = internal_irq_map(17);
+       if (irq[1] < 0) {
+               pr_err("unable to map internal IRQ %d\n", 17);
+               goto out_irq1;
+       }
+       err = request_irq(irq[1], metag_pmu->handle_irq, IRQF_NOBALANCING,
+                       "metagpmu1", (void *)1);
+       if (err) {
+               pr_err("unable to request IRQ%d for metag PMU counters\n",
+                               irq[1]);
+               goto out_irq1;
+       }
+
+       return 0;
+
+out_irq1:
+       free_irq(irq[0], (void *)0);
+out:
+       return err;
+}
+
+/* PMU operations */
+static void metag_pmu_enable(struct pmu *pmu)
+{
+}
+
+static void metag_pmu_disable(struct pmu *pmu)
+{
+}
+
+static int metag_pmu_event_init(struct perf_event *event)
+{
+       int err = 0;
+       atomic_t *active_events = &metag_pmu->active_events;
+
+       if (!metag_pmu_initialised()) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       if (has_branch_stack(event))
+               return -EOPNOTSUPP;
+
+       event->destroy = _hw_perf_event_destroy;
+
+       if (!atomic_inc_not_zero(active_events)) {
+               mutex_lock(&metag_pmu->reserve_mutex);
+               if (atomic_read(active_events) == 0)
+                       err = reserve_pmu_hardware();
+
+               if (!err)
+                       atomic_inc(active_events);
+
+               mutex_unlock(&metag_pmu->reserve_mutex);
+       }
+
+       /* Hardware and caches counters */
+       switch (event->attr.type) {
+       case PERF_TYPE_HARDWARE:
+       case PERF_TYPE_HW_CACHE:
+               err = _hw_perf_event_init(event);
+               break;
+
+       default:
+               return -ENOENT;
+       }
+
+       if (err)
+               event->destroy(event);
+
+out:
+       return err;
+}
+
+void metag_pmu_event_update(struct perf_event *event,
+               struct hw_perf_event *hwc, int idx)
+{
+       u64 prev_raw_count, new_raw_count;
+       s64 delta;
+
+       /*
+        * If this counter is chained, it may be that the previous counter
+        * value has been changed beneath us.
+        *
+        * To get around this, we read and exchange the new raw count, then
+        * add the delta (new - prev) to the generic counter atomically.
+        *
+        * Without interrupts, this is the simplest approach.
+        */
+again:
+       prev_raw_count = local64_read(&hwc->prev_count);
+       new_raw_count = metag_pmu->read(idx);
+
+       if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                       new_raw_count) != prev_raw_count)
+               goto again;
+
+       /*
+        * Calculate the delta and add it to the counter.
+        */
+       delta = new_raw_count - prev_raw_count;
+
+       local64_add(delta, &event->count);
+}
+
+int metag_pmu_event_set_period(struct perf_event *event,
+               struct hw_perf_event *hwc, int idx)
+{
+       s64 left = local64_read(&hwc->period_left);
+       s64 period = hwc->sample_period;
+       int ret = 0;
+
+       if (unlikely(left <= -period)) {
+               left = period;
+               local64_set(&hwc->period_left, left);
+               hwc->last_period = period;
+               ret = 1;
+       }
+
+       if (unlikely(left <= 0)) {
+               left += period;
+               local64_set(&hwc->period_left, left);
+               hwc->last_period = period;
+               ret = 1;
+       }
+
+       if (left > (s64)metag_pmu->max_period)
+               left = metag_pmu->max_period;
+
+       if (metag_pmu->write)
+               metag_pmu->write(idx, (u64)(-left) & MAX_PERIOD);
+
+       perf_event_update_userpage(event);
+
+       return ret;
+}
+
+static void metag_pmu_start(struct perf_event *event, int flags)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       if (WARN_ON_ONCE(idx == -1))
+               return;
+
+       /*
+        * We always have to reprogram the period, so ignore PERF_EF_RELOAD.
+        */
+       if (flags & PERF_EF_RELOAD)
+               WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+       hwc->state = 0;
+
+       /*
+        * Reset the period.
+        * Some counters can't be stopped (i.e. are core global), so when the
+        * counter was 'stopped' we merely disabled the IRQ. If we don't reset
+        * the period, then we'll either: a) get an overflow too soon;
+        * or b) too late if the overflow happened since disabling.
+        * Obviously, this has little bearing on cores without the overflow
+        * interrupt, as the performance counter resets to zero on write
+        * anyway.
+        */
+       if (metag_pmu->max_period)
+               metag_pmu_event_set_period(event, hwc, hwc->idx);
+       cpuc->events[idx] = event;
+       metag_pmu->enable(hwc, idx);
+}
+
+static void metag_pmu_stop(struct perf_event *event, int flags)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       /*
+        * We should always update the counter on stop; see comment above
+        * why.
+        */
+       if (!(hwc->state & PERF_HES_STOPPED)) {
+               metag_pmu_event_update(event, hwc, hwc->idx);
+               metag_pmu->disable(hwc, hwc->idx);
+               hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+       }
+}
+
+static int metag_pmu_add(struct perf_event *event, int flags)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = 0, ret = 0;
+
+       perf_pmu_disable(event->pmu);
+
+       /* check whether we're counting instructions */
+       if (hwc->config == 0x100) {
+               if (__test_and_set_bit(METAG_INST_COUNTER,
+                               cpuc->used_mask)) {
+                       ret = -EAGAIN;
+                       goto out;
+               }
+               idx = METAG_INST_COUNTER;
+       } else {
+               /* Check whether we have a spare counter */
+               idx = find_first_zero_bit(cpuc->used_mask,
+                               atomic_read(&metag_pmu->active_events));
+               if (idx >= METAG_INST_COUNTER) {
+                       ret = -EAGAIN;
+                       goto out;
+               }
+
+               __set_bit(idx, cpuc->used_mask);
+       }
+       hwc->idx = idx;
+
+       /* Make sure the counter is disabled */
+       metag_pmu->disable(hwc, idx);
+
+       hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+       if (flags & PERF_EF_START)
+               metag_pmu_start(event, PERF_EF_RELOAD);
+
+       perf_event_update_userpage(event);
+out:
+       perf_pmu_enable(event->pmu);
+       return ret;
+}
+
+static void metag_pmu_del(struct perf_event *event, int flags)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       WARN_ON(idx < 0);
+       metag_pmu_stop(event, PERF_EF_UPDATE);
+       cpuc->events[idx] = NULL;
+       __clear_bit(idx, cpuc->used_mask);
+
+       perf_event_update_userpage(event);
+}
+
+static void metag_pmu_read(struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       /* Don't read disabled counters! */
+       if (hwc->idx < 0)
+               return;
+
+       metag_pmu_event_update(event, hwc, hwc->idx);
+}
+
+static struct pmu pmu = {
+       .pmu_enable     = metag_pmu_enable,
+       .pmu_disable    = metag_pmu_disable,
+
+       .event_init     = metag_pmu_event_init,
+
+       .add            = metag_pmu_add,
+       .del            = metag_pmu_del,
+       .start          = metag_pmu_start,
+       .stop           = metag_pmu_stop,
+       .read           = metag_pmu_read,
+};
+
+/* Core counter specific functions */
+static const int metag_general_events[] = {
+       [PERF_COUNT_HW_CPU_CYCLES] = 0x03,
+       [PERF_COUNT_HW_INSTRUCTIONS] = 0x100,
+       [PERF_COUNT_HW_CACHE_REFERENCES] = -1,
+       [PERF_COUNT_HW_CACHE_MISSES] = -1,
+       [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = -1,
+       [PERF_COUNT_HW_BRANCH_MISSES] = -1,
+       [PERF_COUNT_HW_BUS_CYCLES] = -1,
+       [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = -1,
+       [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = -1,
+       [PERF_COUNT_HW_REF_CPU_CYCLES] = -1,
+};
+
+static const int metag_pmu_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
+       [C(L1D)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)] = 0x08,
+                       [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,
+               },
+       },
+       [C(L1I)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)] = 0x09,
+                       [C(RESULT_MISS)] = 0x0a,
+               },
+               [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(LL)] = {
+               [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,
+               },
+       },
+       [C(DTLB)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)] = 0xd0,
+                       [C(RESULT_MISS)] = 0xd2,
+               },
+               [C(OP_WRITE)] = {
+                       [C(RESULT_ACCESS)] = 0xd4,
+                       [C(RESULT_MISS)] = 0xd5,
+               },
+               [C(OP_PREFETCH)] = {
+                       [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+                       [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+               },
+       },
+       [C(ITLB)] = {
+               [C(OP_READ)] = {
+                       [C(RESULT_ACCESS)] = 0xd1,
+                       [C(RESULT_MISS)] = 0xd3,
+               },
+               [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,
+               },
+       },
+       [C(NODE)] = {
+               [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 void _hw_perf_event_destroy(struct perf_event *event)
+{
+       atomic_t *active_events = &metag_pmu->active_events;
+       struct mutex *pmu_mutex = &metag_pmu->reserve_mutex;
+
+       if (atomic_dec_and_mutex_lock(active_events, pmu_mutex)) {
+               release_pmu_hardware();
+               mutex_unlock(pmu_mutex);
+       }
+}
+
+static int _hw_perf_cache_event(int config, int *evp)
+{
+       unsigned long type, op, result;
+       int ev;
+
+       if (!metag_pmu->cache_events)
+               return -EINVAL;
+
+       /* Unpack config */
+       type = config & 0xff;
+       op = (config >> 8) & 0xff;
+       result = (config >> 16) & 0xff;
+
+       if (type >= PERF_COUNT_HW_CACHE_MAX ||
+                       op >= PERF_COUNT_HW_CACHE_OP_MAX ||
+                       result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+               return -EINVAL;
+
+       ev = (*metag_pmu->cache_events)[type][op][result];
+       if (ev == 0)
+               return -EOPNOTSUPP;
+       if (ev == -1)
+               return -EINVAL;
+       *evp = ev;
+       return 0;
+}
+
+static int _hw_perf_event_init(struct perf_event *event)
+{
+       struct perf_event_attr *attr = &event->attr;
+       struct hw_perf_event *hwc = &event->hw;
+       int mapping = 0, err;
+
+       switch (attr->type) {
+       case PERF_TYPE_HARDWARE:
+               if (attr->config >= PERF_COUNT_HW_MAX)
+                       return -EINVAL;
+
+               mapping = metag_pmu->event_map(attr->config);
+               break;
+
+       case PERF_TYPE_HW_CACHE:
+               err = _hw_perf_cache_event(attr->config, &mapping);
+               if (err)
+                       return err;
+               break;
+       }
+
+       /* Return early if the event is unsupported */
+       if (mapping == -1)
+               return -EINVAL;
+
+       /*
+        * Early cores have "limited" counters - they have no overflow
+        * interrupts - and so are unable to do sampling without extra work
+        * and timer assistance.
+        */
+       if (metag_pmu->max_period == 0) {
+               if (hwc->sample_period)
+                       return -EINVAL;
+       }
+
+       /*
+        * Don't assign an index until the event is placed into the hardware.
+        * -1 signifies that we're still deciding where to put it. On SMP
+        * systems each core has its own set of counters, so we can't do any
+        * constraint checking yet.
+        */
+       hwc->idx = -1;
+
+       /* Store the event encoding */
+       hwc->config |= (unsigned long)mapping;
+
+       /*
+        * For non-sampling runs, limit the sample_period to half of the
+        * counter width. This way, the new counter value should be less
+        * likely to overtake the previous one (unless there are IRQ latency
+        * issues...)
+        */
+       if (metag_pmu->max_period) {
+               if (!hwc->sample_period) {
+                       hwc->sample_period = metag_pmu->max_period >> 1;
+                       hwc->last_period = hwc->sample_period;
+                       local64_set(&hwc->period_left, hwc->sample_period);
+               }
+       }
+
+       return 0;
+}
+
+static void metag_pmu_enable_counter(struct hw_perf_event *event, int idx)
+{
+       struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events);
+       unsigned int config = event->config;
+       unsigned int tmp = config & 0xf0;
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+       /*
+        * Check if we're enabling the instruction counter (index of
+        * MAX_HWEVENTS - 1)
+        */
+       if (METAG_INST_COUNTER == idx) {
+               WARN_ONCE((config != 0x100),
+                       "invalid configuration (%d) for counter (%d)\n",
+                       config, idx);
+
+               /* Reset the cycle count */
+               __core_reg_set(TXTACTCYC, 0);
+               goto unlock;
+       }
+
+       /* Check for a core internal or performance channel event. */
+       if (tmp) {
+               void *perf_addr = (void *)PERF_COUNT(idx);
+
+               /*
+                * Anything other than a cycle count will write the low-
+                * nibble to the correct counter register.
+                */
+               switch (tmp) {
+               case 0xd0:
+                       perf_addr = (void *)PERF_ICORE(idx);
+                       break;
+
+               case 0xf0:
+                       perf_addr = (void *)PERF_CHAN(idx);
+                       break;
+               }
+
+               metag_out32((tmp & 0x0f), perf_addr);
+
+               /*
+                * Now we use the high nibble as the performance event to
+                * to count.
+                */
+               config = tmp >> 4;
+       }
+
+       /*
+        * Enabled counters start from 0. Early cores clear the count on
+        * write but newer cores don't, so we make sure that the count is
+        * set to 0.
+        */
+       tmp = ((config & 0xf) << 28) |
+                       ((1 << 24) << cpu_2_hwthread_id[get_cpu()]);
+       metag_out32(tmp, PERF_COUNT(idx));
+unlock:
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void metag_pmu_disable_counter(struct hw_perf_event *event, int idx)
+{
+       struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events);
+       unsigned int tmp = 0;
+       unsigned long flags;
+
+       /*
+        * The cycle counter can't be disabled per se, as it's a hardware
+        * thread register which is always counting. We merely return if this
+        * is the counter we're attempting to disable.
+        */
+       if (METAG_INST_COUNTER == idx)
+               return;
+
+       /*
+        * The counter value _should_ have been read prior to disabling,
+        * as if we're running on an early core then the value gets reset to
+        * 0, and any read after that would be useless. On the newer cores,
+        * however, it's better to read-modify-update this for purposes of
+        * the overflow interrupt.
+        * Here we remove the thread id AND the event nibble (there are at
+        * least two events that count events that are core global and ignore
+        * the thread id mask). This only works because we don't mix thread
+        * performance counts, and event 0x00 requires a thread id mask!
+        */
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+       tmp = metag_in32(PERF_COUNT(idx));
+       tmp &= 0x00ffffff;
+       metag_out32(tmp, PERF_COUNT(idx));
+
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static u64 metag_pmu_read_counter(int idx)
+{
+       u32 tmp = 0;
+
+       /* The act of reading the cycle counter also clears it */
+       if (METAG_INST_COUNTER == idx) {
+               __core_reg_swap(TXTACTCYC, tmp);
+               goto out;
+       }
+
+       tmp = metag_in32(PERF_COUNT(idx)) & 0x00ffffff;
+out:
+       return tmp;
+}
+
+static void metag_pmu_write_counter(int idx, u32 val)
+{
+       struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events);
+       u32 tmp = 0;
+       unsigned long flags;
+
+       /*
+        * This _shouldn't_ happen, but if it does, then we can just
+        * ignore the write, as the register is read-only and clear-on-write.
+        */
+       if (METAG_INST_COUNTER == idx)
+               return;
+
+       /*
+        * We'll keep the thread mask and event id, and just update the
+        * counter itself. Also , we should bound the value to 24-bits.
+        */
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+       val &= 0x00ffffff;
+       tmp = metag_in32(PERF_COUNT(idx)) & 0xff000000;
+       val |= tmp;
+       metag_out32(val, PERF_COUNT(idx));
+
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static int metag_pmu_event_map(int idx)
+{
+       return metag_general_events[idx];
+}
+
+static irqreturn_t metag_pmu_counter_overflow(int irq, void *dev)
+{
+       int idx = (int)dev;
+       struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+       struct perf_event *event = cpuhw->events[idx];
+       struct hw_perf_event *hwc = &event->hw;
+       struct pt_regs *regs = get_irq_regs();
+       struct perf_sample_data sampledata;
+       unsigned long flags;
+       u32 counter = 0;
+
+       /*
+        * We need to stop the core temporarily from generating another
+        * interrupt while we disable this counter. However, we don't want
+        * to flag the counter as free
+        */
+       __global_lock2(flags);
+       counter = metag_in32(PERF_COUNT(idx));
+       metag_out32((counter & 0x00ffffff), PERF_COUNT(idx));
+       __global_unlock2(flags);
+
+       /* Update the counts and reset the sample period */
+       metag_pmu_event_update(event, hwc, idx);
+       perf_sample_data_init(&sampledata, 0, hwc->last_period);
+       metag_pmu_event_set_period(event, hwc, idx);
+
+       /*
+        * Enable the counter again once core overflow processing has
+        * completed.
+        */
+       if (!perf_event_overflow(event, &sampledata, regs))
+               metag_out32(counter, PERF_COUNT(idx));
+
+       return IRQ_HANDLED;
+}
+
+static struct metag_pmu _metag_pmu = {
+       .handle_irq     = metag_pmu_counter_overflow,
+       .enable         = metag_pmu_enable_counter,
+       .disable        = metag_pmu_disable_counter,
+       .read           = metag_pmu_read_counter,
+       .write          = metag_pmu_write_counter,
+       .event_map      = metag_pmu_event_map,
+       .cache_events   = &metag_pmu_cache_events,
+       .max_period     = MAX_PERIOD,
+       .max_events     = MAX_HWEVENTS,
+};
+
+/* PMU CPU hotplug notifier */
+static int __cpuinit metag_pmu_cpu_notify(struct notifier_block *b,
+               unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned int)hcpu;
+       struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+
+       if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
+               return NOTIFY_DONE;
+
+       memset(cpuc, 0, sizeof(struct cpu_hw_events));
+       raw_spin_lock_init(&cpuc->pmu_lock);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata metag_pmu_notifier = {
+       .notifier_call = metag_pmu_cpu_notify,
+};
+
+/* PMU Initialisation */
+static int __init init_hw_perf_events(void)
+{
+       int ret = 0, cpu;
+       u32 version = *(u32 *)METAC_ID;
+       int major = (version & METAC_ID_MAJOR_BITS) >> METAC_ID_MAJOR_S;
+       int min_rev = (version & (METAC_ID_MINOR_BITS | METAC_ID_REV_BITS))
+                       >> METAC_ID_REV_S;
+
+       /* Not a Meta 2 core, then not supported */
+       if (0x02 > major) {
+               pr_info("no hardware counter support available\n");
+               goto out;
+       } else if (0x02 == major) {
+               metag_pmu = &_metag_pmu;
+
+               if (min_rev < 0x0104) {
+                       /*
+                        * A core without overflow interrupts, and clear-on-
+                        * write counters.
+                        */
+                       metag_pmu->handle_irq = NULL;
+                       metag_pmu->write = NULL;
+                       metag_pmu->max_period = 0;
+               }
+
+               metag_pmu->name = "Meta 2";
+               metag_pmu->version = version;
+               metag_pmu->pmu = pmu;
+       }
+
+       pr_info("enabled with %s PMU driver, %d counters available\n",
+                       metag_pmu->name, metag_pmu->max_events);
+
+       /* Initialise the active events and reservation mutex */
+       atomic_set(&metag_pmu->active_events, 0);
+       mutex_init(&metag_pmu->reserve_mutex);
+
+       /* Clear the counters */
+       metag_out32(0, PERF_COUNT(0));
+       metag_out32(0, PERF_COUNT(1));
+
+       for_each_possible_cpu(cpu) {
+               struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+
+               memset(cpuc, 0, sizeof(struct cpu_hw_events));
+               raw_spin_lock_init(&cpuc->pmu_lock);
+       }
+
+       register_cpu_notifier(&metag_pmu_notifier);
+       ret = perf_pmu_register(&pmu, (char *)metag_pmu->name, PERF_TYPE_RAW);
+out:
+       return ret;
+}
+early_initcall(init_hw_perf_events);