15b3b1688SDavid Daney /* 25b3b1688SDavid Daney * This file is subject to the terms and conditions of the GNU General Public 35b3b1688SDavid Daney * License. See the file "COPYING" in the main directory of this archive 45b3b1688SDavid Daney * for more details. 55b3b1688SDavid Daney * 65b3b1688SDavid Daney * Copyright (C) 2007 by Ralf Baechle 754954a6dSDavid Daney * Copyright (C) 2009, 2010 Cavium Networks, Inc. 85b3b1688SDavid Daney */ 95b3b1688SDavid Daney #include <linux/clocksource.h> 10cae39d13SPaul Gortmaker #include <linux/export.h> 115b3b1688SDavid Daney #include <linux/init.h> 1254954a6dSDavid Daney #include <linux/smp.h> 135b3b1688SDavid Daney 1454954a6dSDavid Daney #include <asm/cpu-info.h> 155b3b1688SDavid Daney #include <asm/time.h> 165b3b1688SDavid Daney 175b3b1688SDavid Daney #include <asm/octeon/octeon.h> 185b3b1688SDavid Daney #include <asm/octeon/cvmx-ipd-defs.h> 1954954a6dSDavid Daney #include <asm/octeon/cvmx-mio-defs.h> 205b3b1688SDavid Daney 215b3b1688SDavid Daney /* 225b3b1688SDavid Daney * Set the current core's cvmcount counter to the value of the 235b3b1688SDavid Daney * IPD_CLK_COUNT. We do this on all cores as they are brought 245b3b1688SDavid Daney * on-line. This allows for a read from a local cpu register to 255b3b1688SDavid Daney * access a synchronized counter. 265b3b1688SDavid Daney * 2754954a6dSDavid Daney * On CPU_CAVIUM_OCTEON2 the IPD_CLK_COUNT is scaled by rdiv/sdiv. 285b3b1688SDavid Daney */ 295b3b1688SDavid Daney void octeon_init_cvmcount(void) 305b3b1688SDavid Daney { 315b3b1688SDavid Daney unsigned long flags; 325b3b1688SDavid Daney unsigned loops = 2; 3354954a6dSDavid Daney u64 f = 0; 3454954a6dSDavid Daney u64 rdiv = 0; 3554954a6dSDavid Daney u64 sdiv = 0; 3654954a6dSDavid Daney if (current_cpu_type() == CPU_CAVIUM_OCTEON2) { 3754954a6dSDavid Daney union cvmx_mio_rst_boot rst_boot; 3854954a6dSDavid Daney rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT); 3954954a6dSDavid Daney rdiv = rst_boot.s.c_mul; /* CPU clock */ 4054954a6dSDavid Daney sdiv = rst_boot.s.pnr_mul; /* I/O clock */ 4154954a6dSDavid Daney f = (0x8000000000000000ull / sdiv) * 2; 4254954a6dSDavid Daney } 4354954a6dSDavid Daney 445b3b1688SDavid Daney 455b3b1688SDavid Daney /* Clobber loops so GCC will not unroll the following while loop. */ 465b3b1688SDavid Daney asm("" : "+r" (loops)); 475b3b1688SDavid Daney 485b3b1688SDavid Daney local_irq_save(flags); 495b3b1688SDavid Daney /* 505b3b1688SDavid Daney * Loop several times so we are executing from the cache, 515b3b1688SDavid Daney * which should give more deterministic timing. 525b3b1688SDavid Daney */ 5354954a6dSDavid Daney while (loops--) { 5454954a6dSDavid Daney u64 ipd_clk_count = cvmx_read_csr(CVMX_IPD_CLK_COUNT); 5554954a6dSDavid Daney if (rdiv != 0) { 5654954a6dSDavid Daney ipd_clk_count *= rdiv; 5754954a6dSDavid Daney if (f != 0) { 5854954a6dSDavid Daney asm("dmultu\t%[cnt],%[f]\n\t" 5954954a6dSDavid Daney "mfhi\t%[cnt]" 6054954a6dSDavid Daney : [cnt] "+r" (ipd_clk_count), 6154954a6dSDavid Daney [f] "=r" (f) 6254954a6dSDavid Daney : : "hi", "lo"); 6354954a6dSDavid Daney } 6454954a6dSDavid Daney } 6554954a6dSDavid Daney write_c0_cvmcount(ipd_clk_count); 6654954a6dSDavid Daney } 675b3b1688SDavid Daney local_irq_restore(flags); 685b3b1688SDavid Daney } 695b3b1688SDavid Daney 70d0ce9a5aSColy Li static cycle_t octeon_cvmcount_read(struct clocksource *cs) 715b3b1688SDavid Daney { 725b3b1688SDavid Daney return read_c0_cvmcount(); 735b3b1688SDavid Daney } 745b3b1688SDavid Daney 755b3b1688SDavid Daney static struct clocksource clocksource_mips = { 765b3b1688SDavid Daney .name = "OCTEON_CVMCOUNT", 775b3b1688SDavid Daney .read = octeon_cvmcount_read, 785b3b1688SDavid Daney .mask = CLOCKSOURCE_MASK(64), 795b3b1688SDavid Daney .flags = CLOCK_SOURCE_IS_CONTINUOUS, 805b3b1688SDavid Daney }; 815b3b1688SDavid Daney 82c6a3c851SDavid Daney unsigned long long notrace sched_clock(void) 83c6a3c851SDavid Daney { 840e8a1d82SDavid Daney /* 64-bit arithmatic can overflow, so use 128-bit. */ 850e8a1d82SDavid Daney u64 t1, t2, t3; 860e8a1d82SDavid Daney unsigned long long rv; 870e8a1d82SDavid Daney u64 mult = clocksource_mips.mult; 880e8a1d82SDavid Daney u64 shift = clocksource_mips.shift; 890e8a1d82SDavid Daney u64 cnt = read_c0_cvmcount(); 900e8a1d82SDavid Daney 910e8a1d82SDavid Daney asm ( 920e8a1d82SDavid Daney "dmultu\t%[cnt],%[mult]\n\t" 930e8a1d82SDavid Daney "nor\t%[t1],$0,%[shift]\n\t" 940e8a1d82SDavid Daney "mfhi\t%[t2]\n\t" 950e8a1d82SDavid Daney "mflo\t%[t3]\n\t" 960e8a1d82SDavid Daney "dsll\t%[t2],%[t2],1\n\t" 970e8a1d82SDavid Daney "dsrlv\t%[rv],%[t3],%[shift]\n\t" 980e8a1d82SDavid Daney "dsllv\t%[t1],%[t2],%[t1]\n\t" 990e8a1d82SDavid Daney "or\t%[rv],%[t1],%[rv]\n\t" 1000e8a1d82SDavid Daney : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3) 1010e8a1d82SDavid Daney : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift) 1020e8a1d82SDavid Daney : "hi", "lo"); 1030e8a1d82SDavid Daney return rv; 104c6a3c851SDavid Daney } 105c6a3c851SDavid Daney 1065b3b1688SDavid Daney void __init plat_time_init(void) 1075b3b1688SDavid Daney { 1085b3b1688SDavid Daney clocksource_mips.rating = 300; 10975c4fd8cSJohn Stultz clocksource_register_hz(&clocksource_mips, octeon_get_clock_rate()); 1105b3b1688SDavid Daney } 111ca148125SDavid Daney 112ca148125SDavid Daney static u64 octeon_udelay_factor; 113ca148125SDavid Daney static u64 octeon_ndelay_factor; 114ca148125SDavid Daney 115ca148125SDavid Daney void __init octeon_setup_delays(void) 116ca148125SDavid Daney { 117ca148125SDavid Daney octeon_udelay_factor = octeon_get_clock_rate() / 1000000; 118ca148125SDavid Daney /* 119ca148125SDavid Daney * For __ndelay we divide by 2^16, so the factor is multiplied 120ca148125SDavid Daney * by the same amount. 121ca148125SDavid Daney */ 122ca148125SDavid Daney octeon_ndelay_factor = (octeon_udelay_factor * 0x10000ull) / 1000ull; 123ca148125SDavid Daney 124ca148125SDavid Daney preset_lpj = octeon_get_clock_rate() / HZ; 125ca148125SDavid Daney } 126ca148125SDavid Daney 127ca148125SDavid Daney void __udelay(unsigned long us) 128ca148125SDavid Daney { 129ca148125SDavid Daney u64 cur, end, inc; 130ca148125SDavid Daney 131ca148125SDavid Daney cur = read_c0_cvmcount(); 132ca148125SDavid Daney 133ca148125SDavid Daney inc = us * octeon_udelay_factor; 134ca148125SDavid Daney end = cur + inc; 135ca148125SDavid Daney 136ca148125SDavid Daney while (end > cur) 137ca148125SDavid Daney cur = read_c0_cvmcount(); 138ca148125SDavid Daney } 139ca148125SDavid Daney EXPORT_SYMBOL(__udelay); 140ca148125SDavid Daney 141ca148125SDavid Daney void __ndelay(unsigned long ns) 142ca148125SDavid Daney { 143ca148125SDavid Daney u64 cur, end, inc; 144ca148125SDavid Daney 145ca148125SDavid Daney cur = read_c0_cvmcount(); 146ca148125SDavid Daney 147ca148125SDavid Daney inc = ((ns * octeon_ndelay_factor) >> 16); 148ca148125SDavid Daney end = cur + inc; 149ca148125SDavid Daney 150ca148125SDavid Daney while (end > cur) 151ca148125SDavid Daney cur = read_c0_cvmcount(); 152ca148125SDavid Daney } 153ca148125SDavid Daney EXPORT_SYMBOL(__ndelay); 154ca148125SDavid Daney 155ca148125SDavid Daney void __delay(unsigned long loops) 156ca148125SDavid Daney { 157ca148125SDavid Daney u64 cur, end; 158ca148125SDavid Daney 159ca148125SDavid Daney cur = read_c0_cvmcount(); 160ca148125SDavid Daney end = cur + loops; 161ca148125SDavid Daney 162ca148125SDavid Daney while (end > cur) 163ca148125SDavid Daney cur = read_c0_cvmcount(); 164ca148125SDavid Daney } 165ca148125SDavid Daney EXPORT_SYMBOL(__delay); 166