Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 1 | /* |
Martin Roth | 86953226 | 2017-02-09 17:23:55 -0800 | [diff] [blame] | 2 | * This file is part of the coreboot project. |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 3 | * |
| 4 | * Copyright (C) 2014 Alexandru Gagniuc <mr.nuke.me@gmail.com> |
Martin Roth | 86953226 | 2017-02-09 17:23:55 -0800 | [diff] [blame] | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | */ |
| 16 | |
| 17 | /* |
Paul Menzel | 1d6002a | 2017-10-14 13:24:06 +0200 | [diff] [blame] | 18 | * udelay() implementation for SMI handlers |
Martin Roth | 86953226 | 2017-02-09 17:23:55 -0800 | [diff] [blame] | 19 | * This is neat in that it never writes to hardware registers, and thus does |
| 20 | * not modify the state of the hardware while servicing SMIs. |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 21 | */ |
| 22 | |
| 23 | #include <cpu/x86/msr.h> |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame^] | 24 | #include <cpu/amd/msr.h> |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 25 | #include <cpu/x86/tsc.h> |
| 26 | #include <delay.h> |
| 27 | #include <stdint.h> |
| 28 | |
| 29 | void udelay(uint32_t us) |
| 30 | { |
| 31 | uint8_t fid, did, pstate_idx; |
| 32 | uint64_t tsc_clock, tsc_start, tsc_now, tsc_wait_ticks; |
| 33 | msr_t msr; |
| 34 | const uint64_t tsc_base = 100000000; |
| 35 | |
| 36 | /* Get initial timestamp before we do the math */ |
| 37 | tsc_start = rdtscll(); |
| 38 | |
| 39 | /* Get the P-state. This determines which MSR to read */ |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame^] | 40 | msr = rdmsr(PS_STS_REG); |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 41 | pstate_idx = msr.lo & 0x07; |
| 42 | |
| 43 | /* Get FID and VID for current P-State */ |
Elyes HAOUAS | 400ce55 | 2018-10-12 10:54:30 +0200 | [diff] [blame^] | 44 | msr = rdmsr(PSTATE_0_MSR + pstate_idx); |
Alexandru Gagniuc | 065b7da | 2014-04-15 15:41:38 -0500 | [diff] [blame] | 45 | |
| 46 | /* Extract the FID and VID values */ |
| 47 | fid = msr.lo & 0x3f; |
| 48 | did = (msr.lo >> 6) & 0x7; |
| 49 | |
| 50 | /* Calculate the CPU clock (from base freq of 100MHz) */ |
| 51 | tsc_clock = tsc_base * (fid + 0x10) / (1 << did); |
| 52 | |
| 53 | /* Now go on and wait */ |
| 54 | tsc_wait_ticks = (tsc_clock / 1000000) * us; |
| 55 | |
| 56 | do { |
| 57 | tsc_now = rdtscll(); |
| 58 | } while (tsc_now - tsc_wait_ticks < tsc_start); |
| 59 | } |