Patrick Georgi | ac95903 | 2020-05-05 22:49:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
Martin Roth | 86953226 | 2017-02-09 17:23:55 -0800 | [diff] [blame] | 2 | |
| 3 | /* |
Paul Menzel | 1d6002a | 2017-10-14 13:24:06 +0200 | [diff] [blame] | 4 | * udelay() implementation for SMI handlers |
Martin Roth | 86953226 | 2017-02-09 17:23:55 -0800 | [diff] [blame] | 5 | * This is neat in that it never writes to hardware registers, and thus does |
| 6 | * not modify the state of the hardware while servicing SMIs. |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 7 | */ |
| 8 | |
| 9 | #include <cpu/x86/msr.h> |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame] | 10 | #include <cpu/amd/msr.h> |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 11 | #include <cpu/x86/tsc.h> |
| 12 | #include <delay.h> |
| 13 | #include <stdint.h> |
| 14 | |
| 15 | void udelay(uint32_t us) |
| 16 | { |
| 17 | uint8_t fid, did, pstate_idx; |
| 18 | uint64_t tsc_clock, tsc_start, tsc_now, tsc_wait_ticks; |
| 19 | msr_t msr; |
| 20 | const uint64_t tsc_base = 100000000; |
| 21 | |
| 22 | /* Get initial timestamp before we do the math */ |
| 23 | tsc_start = rdtscll(); |
| 24 | |
| 25 | /* Get the P-state. This determines which MSR to read */ |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame] | 26 | msr = rdmsr(PS_STS_REG); |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 27 | pstate_idx = msr.lo & 0x07; |
| 28 | |
| 29 | /* Get FID and VID for current P-State */ |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame] | 30 | msr = rdmsr(PSTATE_0_MSR + pstate_idx); |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 31 | |
| 32 | /* Extract the FID and VID values */ |
| 33 | fid = msr.lo & 0x3f; |
| 34 | did = (msr.lo >> 6) & 0x7; |
| 35 | |
| 36 | /* Calculate the CPU clock (from base freq of 100MHz) */ |
| 37 | tsc_clock = tsc_base * (fid + 0x10) / (1 << did); |
| 38 | |
| 39 | /* Now go on and wait */ |
| 40 | tsc_wait_ticks = (tsc_clock / 1000000) * us; |
| 41 | |
| 42 | do { |
| 43 | tsc_now = rdtscll(); |
| 44 | } while (tsc_now - tsc_wait_ticks < tsc_start); |
| 45 | } |