sparc64: add hot-patched and inlined get_tick()
authorPavel Tatashin <pasha.tatashin@oracle.com>
Mon, 12 Jun 2017 20:41:47 +0000 (16:41 -0400)
committerDavid S. Miller <davem@davemloft.net>
Mon, 12 Jun 2017 22:44:03 +0000 (15:44 -0700)
Add the new get_tick() function that is hot-patched during boot based on
processor we are booting on.

Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
Reviewed-by: Steven Sistare <steven.sistare@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/include/asm/timer_64.h
arch/sparc/kernel/time_64.c
arch/sparc/kernel/vmlinux.lds.S

index 61d07251792c9f09eb3c9ac2fc1d7f5c83dde954..51bc3bc54bfe60014f5a59f7ed439b8a7da26f0b 100644 (file)
@@ -34,4 +34,64 @@ unsigned long sparc64_get_clock_tick(unsigned int cpu);
 void setup_sparc64_timer(void);
 void __init time_init(void);
 
+#define TICK_PRIV_BIT          BIT(63)
+#define TICKCMP_IRQ_BIT                BIT(63)
+
+#define HBIRD_STICKCMP_ADDR    0x1fe0000f060UL
+#define HBIRD_STICK_ADDR       0x1fe0000f070UL
+
+#define GET_TICK_NINSTR                13
+struct get_tick_patch {
+       unsigned int    addr;
+       unsigned int    tick[GET_TICK_NINSTR];
+       unsigned int    stick[GET_TICK_NINSTR];
+};
+
+extern struct get_tick_patch __get_tick_patch;
+extern struct get_tick_patch __get_tick_patch_end;
+
+static inline unsigned long get_tick(void)
+{
+       unsigned long tick, tmp1, tmp2;
+
+       __asm__ __volatile__(
+       /* read hbtick 13 instructions */
+       "661:\n"
+       "       mov     0x1fe, %1\n"
+       "       sllx    %1, 0x20, %1\n"
+       "       sethi   %%hi(0xf000), %2\n"
+       "       or      %2, 0x70, %2\n"
+       "       or      %1, %2, %1\n"   /* %1 = HBIRD_STICK_ADDR */
+       "       add     %1, 8, %2\n"
+       "       ldxa    [%2]%3, %0\n"
+       "       ldxa    [%1]%3, %1\n"
+       "       ldxa    [%2]%3, %2\n"
+       "       sub     %2, %0, %0\n"   /* don't modify %xcc */
+       "       brnz,pn %0, 661b\n"     /* restart to save one register */
+       "        sllx   %2, 32, %2\n"
+       "       or      %2, %1, %0\n"
+       /* Common/not patched code */
+       "       sllx    %0, 1, %0\n"
+       "       srlx    %0, 1, %0\n"    /* Clear TICK_PRIV_BIT */
+       /* Beginning of patch section */
+       "       .section .get_tick_patch, \"ax\"\n"
+       "       .word   661b\n"
+       /* read tick 2 instructions and 11 skipped */
+       "       ba      1f\n"
+       "        rd     %%tick, %0\n"
+       "       .skip   4 * (%4 - 2)\n"
+       "1:\n"
+       /* read stick 2 instructions and 11 skipped */
+       "       ba      1f\n"
+       "        rd     %%asr24, %0\n"
+       "       .skip   4 * (%4 - 2)\n"
+       "1:\n"
+       /* End of patch section */
+       "       .previous\n"
+       : "=&r" (tick), "=&r" (tmp1), "=&r" (tmp2)
+       : "i" (ASI_PHYS_BYPASS_EC_E), "i" (GET_TICK_NINSTR));
+
+       return tick;
+}
+
 #endif /* _SPARC64_TIMER_H */
index d149276ddd80f1024302fb6581a2a100f352f2ae..ca27415c393a4f23a57edcedfb57c18e393bf9b3 100644 (file)
 #include <asm/cpudata.h>
 #include <linux/uaccess.h>
 #include <asm/irq_regs.h>
+#include <asm/cacheflush.h>
 
 #include "entry.h"
 #include "kernel.h"
 
 DEFINE_SPINLOCK(rtc_lock);
 
-#define TICK_PRIV_BIT  (1UL << 63)
-#define TICKCMP_IRQ_BIT        (1UL << 63)
-
 #ifdef CONFIG_SMP
 unsigned long profile_pc(struct pt_regs *regs)
 {
@@ -290,9 +288,6 @@ static struct sparc64_tick_ops stick_operations __read_mostly = {
  * 2) write high
  * 3) write low
  */
-#define HBIRD_STICKCMP_ADDR    0x1fe0000f060UL
-#define HBIRD_STICK_ADDR       0x1fe0000f070UL
-
 static unsigned long __hbird_read_stick(void)
 {
        unsigned long ret, tmp1, tmp2, tmp3;
@@ -777,6 +772,26 @@ static u64 clocksource_tick_read(struct clocksource *cs)
        return tick_operations.get_tick();
 }
 
+static void __init get_tick_patch(void)
+{
+       unsigned int *addr, *instr, i;
+       struct get_tick_patch *p;
+
+       if (tlb_type == spitfire && is_hummingbird())
+               return;
+
+       for (p = &__get_tick_patch; p < &__get_tick_patch_end; p++) {
+               instr = (tlb_type == spitfire) ? p->tick : p->stick;
+               addr = (unsigned int *)(unsigned long)p->addr;
+               for (i = 0; i < GET_TICK_NINSTR; i++) {
+                       addr[i] = instr[i];
+                       /* ensure that address is modified before flush */
+                       wmb();
+                       flushi(&addr[i]);
+               }
+       }
+}
+
 static void init_tick_ops(struct sparc64_tick_ops *ops)
 {
        unsigned long freq, quotient, tick;
@@ -789,6 +804,7 @@ static void init_tick_ops(struct sparc64_tick_ops *ops)
        ops->ticks_per_nsec_quotient = quotient;
        ops->frequency = freq;
        tick_operations = *ops;
+       get_tick_patch();
 }
 
 void __init time_init_early(void)
index 572db686f845830a69f0815f7d274eae4927483c..03b3d65d1266d3e3c0de536406d4ae0c9f959c92 100644 (file)
@@ -149,6 +149,11 @@ SECTIONS
                *(.sun_m7_2insn_patch)
                __sun_m7_2insn_patch_end = .;
        }
+       .get_tick_patch : {
+               __get_tick_patch = .;
+               *(.get_tick_patch)
+               __get_tick_patch_end = .;
+       }
        PERCPU_SECTION(SMP_CACHE_BYTES)
 
 #ifdef CONFIG_JUMP_LABEL