return res;
 }
 
+/**
+ * calculate_min_delta() - Calculate a good minimum delta for mips_next_event().
+ *
+ * Running under virtualisation can introduce overhead into mips_next_event() in
+ * the form of hypervisor emulation of CP0_Count/CP0_Compare registers,
+ * potentially with an unnatural frequency, which makes a fixed min_delta_ns
+ * value inappropriate as it may be too small.
+ *
+ * It can also introduce occasional latency from the guest being descheduled.
+ *
+ * This function calculates a good minimum delta based roughly on the 75th
+ * percentile of the time taken to do the mips_next_event() sequence, in order
+ * to handle potentially higher overhead while also eliminating outliers due to
+ * unpredictable hypervisor latency (which can be handled by retries).
+ *
+ * Return:     An appropriate minimum delta for the clock event device.
+ */
+static unsigned int calculate_min_delta(void)
+{
+       unsigned int cnt, i, j, k, l;
+       unsigned int buf1[4], buf2[3];
+       unsigned int min_delta;
+
+       /*
+        * Calculate the median of 5 75th percentiles of 5 samples of how long
+        * it takes to set CP0_Compare = CP0_Count + delta.
+        */
+       for (i = 0; i < 5; ++i) {
+               for (j = 0; j < 5; ++j) {
+                       /*
+                        * This is like the code in mips_next_event(), and
+                        * directly measures the borderline "safe" delta.
+                        */
+                       cnt = read_c0_count();
+                       write_c0_compare(cnt);
+                       cnt = read_c0_count() - cnt;
+
+                       /* Sorted insert into buf1 */
+                       for (k = 0; k < j; ++k) {
+                               if (cnt < buf1[k]) {
+                                       l = min_t(unsigned int,
+                                                 j, ARRAY_SIZE(buf1) - 1);
+                                       for (; l > k; --l)
+                                               buf1[l] = buf1[l - 1];
+                                       break;
+                               }
+                       }
+                       if (k < ARRAY_SIZE(buf1))
+                               buf1[k] = cnt;
+               }
+
+               /* Sorted insert of 75th percentile into buf2 */
+               for (k = 0; k < i; ++k) {
+                       if (buf1[ARRAY_SIZE(buf1) - 1] < buf2[k]) {
+                               l = min_t(unsigned int,
+                                         i, ARRAY_SIZE(buf2) - 1);
+                               for (; l > k; --l)
+                                       buf2[l] = buf2[l - 1];
+                               break;
+                       }
+               }
+               if (k < ARRAY_SIZE(buf2))
+                       buf2[k] = buf1[ARRAY_SIZE(buf1) - 1];
+       }
+
+       /* Use 2 * median of 75th percentiles */
+       min_delta = buf2[ARRAY_SIZE(buf2) - 1] * 2;
+
+       /* Don't go too low */
+       if (min_delta < 0x300)
+               min_delta = 0x300;
+
+       pr_debug("%s: median 75th percentile=%#x, min_delta=%#x\n",
+                __func__, buf2[ARRAY_SIZE(buf2) - 1], min_delta);
+       return min_delta;
+}
+
 DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
 int cp0_timer_irq_installed;
 
 {
        unsigned int cpu = smp_processor_id();
        struct clock_event_device *cd;
-       unsigned int irq;
+       unsigned int irq, min_delta;
 
        if (!cpu_has_counter || !mips_hpt_frequency)
                return -ENXIO;
 
        /* Calculate the min / max delta */
        cd->max_delta_ns        = clockevent_delta2ns(0x7fffffff, cd);
-       cd->min_delta_ns        = clockevent_delta2ns(0x300, cd);
+       min_delta               = calculate_min_delta();
+       cd->min_delta_ns        = clockevent_delta2ns(min_delta, cd);
 
        cd->rating              = 300;
        cd->irq                 = irq;