static u64
 armpmu_event_update(struct perf_event *event,
                    struct hw_perf_event *hwc,
-                   int idx)
+                   int idx, int overflow)
 {
-       int shift = 64 - 32;
-       s64 prev_raw_count, new_raw_count;
-       u64 delta;
+       u64 delta, prev_raw_count, new_raw_count;
 
 again:
        prev_raw_count = local64_read(&hwc->prev_count);
                             new_raw_count) != prev_raw_count)
                goto again;
 
-       delta = (new_raw_count << shift) - (prev_raw_count << shift);
-       delta >>= shift;
+       new_raw_count &= armpmu->max_period;
+       prev_raw_count &= armpmu->max_period;
+
+       if (overflow)
+               delta = armpmu->max_period - prev_raw_count + new_raw_count;
+       else
+               delta = new_raw_count - prev_raw_count;
 
        local64_add(delta, &event->count);
        local64_sub(delta, &hwc->period_left);
        if (hwc->idx < 0)
                return;
 
-       armpmu_event_update(event, hwc, hwc->idx);
+       armpmu_event_update(event, hwc, hwc->idx, 0);
 }
 
 static void
        if (!(hwc->state & PERF_HES_STOPPED)) {
                armpmu->disable(hwc, hwc->idx);
                barrier(); /* why? */
-               armpmu_event_update(event, hwc, hwc->idx);
+               armpmu_event_update(event, hwc, hwc->idx, 0);
                hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
        }
 }
 
                        continue;
 
                hwc = &event->hw;
-               armpmu_event_update(event, hwc, idx);
+               armpmu_event_update(event, hwc, idx, 1);
                data.period = event->hw.last_period;
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
                        continue;
 
                hwc = &event->hw;
-               armpmu_event_update(event, hwc, idx);
+               armpmu_event_update(event, hwc, idx, 1);
                data.period = event->hw.last_period;
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
 
                        continue;
 
                hwc = &event->hw;
-               armpmu_event_update(event, hwc, idx);
+               armpmu_event_update(event, hwc, idx, 1);
                data.period = event->hw.last_period;
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;
                        continue;
 
                hwc = &event->hw;
-               armpmu_event_update(event, hwc, idx);
+               armpmu_event_update(event, hwc, idx, 1);
                data.period = event->hw.last_period;
                if (!armpmu_event_set_period(event, hwc, idx))
                        continue;