[coreboot] Use the constant TSC for AMD Family 10h–15h processors?
Paul Menzel
paulepanter at users.sourceforge.net
Tue May 14 11:03:25 CEST 2013
Am Montag, den 13.05.2013, 09:04 +0200 schrieb Paul Menzel:
> Am Sonntag, den 12.05.2013, 15:40 +0200 schrieb Peter Stuge:
> > Paul Menzel wrote:
> > > do you know if the timer mentioned in the BIOS and Kernel Developer’s
> > > Guide (BKGD) for the AMD Family 14h processors [1]
> > >
> > > 2.11.4 BIOS Timer
> > >
> > > The root complex implements a 32-bit microsecond timer (see
> > > D0F0xE4_x0130_80F0 and D0F0xE4_x0130_80F1) that the BIOS can use
> > > to accurately time wait operations between initialization steps.
> > > To ensure that BIOS waits a minimum number of microseconds
> > > between steps BIOS should always wait for one microsecond more
> > > than the required minimum wait time.
> > >
> > > could be used for implementing `tsc_freq_mhz()` as done for Intel
> > > Haswell processors?
> >
> > Isn't that quite clear from the text that you quoted?
>
> Probably, yes. As this area is new to me, I prefer to ask and get
> confirmation to be sure.
It looks like it is not the same. The BIOS Timer above is more to fit
Aaron’s commit.
commit c46cc6f149c42653344d6e9f3656a4212fc46cef
Author: Aaron Durbin <adurbin at chromium.org>
Date: Mon Apr 29 16:57:10 2013 -0500
haswell: 24MHz monotonic time implementation
Haswell ULT devices have a 24MHz package-level counter. Use
this counter to provide a timer_monotonic_get()
implementation.
Reviewed-on: http://review.coreboot.org/3153
But the problem is, that the register is part of the PCI config space(?)
and the Root Complex/PCI stuff is not yet available in ramstage?
+#include <stdint.h>
+#include <pci_ops.h>
+#include <timer.h>
+
+static struct monotonic_counter {
+ int initialized;
+ struct mono_time time;
+ uint32_t last_value;
+} mono_counter;
+
+static inline uint32_t read_counter_msr(void)
+{
+ /* D0F0xE4_x0130_80F0 BIOS Timer
+ *
+ * This field increments once every microsecond when the timer is
+ * enabled. The counter rolls over and continues counting when it
+ * reaches FFFF_FFFFh. A write to this register causes the counter
+ * to reset and begin counting from the value written. */
+ pci_write_config32(CI_DEV(0, 0, 0), 0xe4, 0x013080F0);
+
+ return pci_read_config32(PCI_DEV(0, 0, 0), 0xe4);
+}
+
+void timer_monotonic_get(struct mono_time *mt)
+{
+ uint32_t current_tick;
+ uint32_t usecs_elapsed;
+
+ if (!mono_counter.initialized) {
+ mono_counter.last_value = read_counter_msr();
+ mono_counter.initialized = 1;
+ }
+
+ current_tick = read_counter_msr();
+ usecs_elapsed = current_tick - mono_counter.last_value;
+
+ /* Update current time and tick values only if a full tick occurred. */
+ if (usecs_elapsed) {
+ mono_time_add_usecs(&mono_counter.time, usecs_elapsed);
+ mono_counter.last_value = current_tick;
+ }
+
+ /* Save result. */
+ *mt = mono_counter.time;
+}
This results in the following hang.
00.000: <00>
00.456:
00.456:
00.456: coreboot-4.0-4160-g41f456b-dirty Tue May 14 00:28:53 CEST 2013 starting...
00.456: BSP Family_Model: 00500f10
00.457: cpu_init_detectedx = 00000000
00.457: agesawrapper_amdinitmmio passed.
00.458: agesawrapper_amdinitreset passed.
00.461: agesawrapper_amdinitearly BSP Family_Model: 00500f10
00.486: cpu_init_detectedx = 00000001
00.486: agesawrapper_amdinitmmio passed.
00.487: agesawrapper_amdinitreset passed.
00.491: agesawrapper_amdinitearly passed.
00.627: agesawrapper_amdinitpost passed.
00.753: agesawrapper_amdinitenv passed.
00.767: Loading image.
00.768: CBFS: loading stage fallback/coreboot_ram @ 0x200000 (1343544 bytes), entry @ 0x200000
00.873: Jumping to image.
00.874: coreboot-4.0-4160-g41f456b-dirty Tue May 14 00:28:53 CEST 2013 booting...
00.874: get_pbus: dev is NULL!
> > > Suggestions, if this should be shared and how the files should be
> > > named are appreciated.
> >
> > Yes and no. We can do this for coreboot's own code for AMD platforms,
> > but it obviously does not make much sense to hack this into AGESA if
> > there are not already provisions for it.
>
> I totally forgot about that. There is coreboot code for the K8 and
> Family 10h processors, if I am not mistaken.
>
> AGESA is there for Family 10h to 15h processors.
>
> > Since AGESA is the only thing relevant going forward the question
> > is what AGESA needs, timing-wise. Have you checked?
>
> Only a little. Having an ASRock E350M1, I am looking into the Family 14h
> family. There seems to be no TSC stuff in `src/cpu/amd`. But the AMD
> vendor code seems to have it.
>
> $ git grep -i tsc src/vendorcode/amd/agesa/f14/
>
> `AGESA.h` has a struct `MEM_DATA_STRUCT` where the frequency is put
> into.
>
> $ nl -ba src/vendorcode/amd/agesa/f14/AGESA.h | grep -B 40 -A 5
> TSC
> […]
> 1700 ///
> 1701 /// Contains all data relevant to Memory Initialization.
> 1702 ///
> 1703 typedef struct _MEM_DATA_STRUCT {
> 1704 IN AMD_CONFIG_PARAMS StdHeader; ///< Standard configuration header
> 1705
> 1706 IN MEM_PARAMETER_STRUCT *ParameterListPtr; ///< List of input Parameters
> 1707
> 1708 OUT MEM_FUNCTION_STRUCT FunctionList; ///< List of function Pointers
> 1709
> 1710 IN OUT AGESA_STATUS (*GetPlatformCfg[MAX_PLATFORM_TYPES]) (struct _MEM_DATA_STRUCT *MemData, UINT8 SocketID, CH_DEF_STRUCT *CurrentChannel); ///< look-up platform info
> 1711
> 1712 IN OUT BOOLEAN (*ErrorHandling)(struct _DIE_STRUCT *MCTPtr, UINT8 DCT, UINT16 ChipSelMask, AMD_CONFIG_PARAMS *StdHeader); ///< Error Handling
> 1713
> 1714
> 1715 OUT MEM_SOCKET_STRUCT SocketList[MAX_SOCKETS_SUPPORTED]; ///< Socket list for memory code.
> 1716 ///< SocketList is a shortcut for IBVs to retrieve training
> 1717 ///< and timing data for each channel indexed by socket/channel,
> 1718 ///< eliminating their need to parse die/dct/channel etc.
> 1719 ///< It contains pointers to the populated data structures for
> 1720 ///< each channel and skips the channel structures that are
> 1721 ///< unpopulated. In the case of channels sharing the same DCT,
> 1722 ///< the pTimings pointers will point to the same DCT Timing data.
> 1723
> 1724 OUT DIE_STRUCT *DiesPerSystem; ///< Pointed to an array of DIE_STRUCTs
> 1725 OUT UINT8 DieCount; ///< Number of MCTs in the system.
> 1726
> 1727 IN SPD_DEF_STRUCT *SpdDataStructure; ///< Pointer to SPD Data structure
> 1728
> 1729 IN OUT struct _PLATFORM_CONFIGURATION *PlatFormConfig; ///< Platform profile/build option config structure
> 1730
> 1731 IN OUT BOOLEAN IsFlowControlSupported; ///< Indicates if flow control is supported
> 1732
> 1733 OUT UINT32 TscRate; ///< The rate at which the TSC increments in megahertz.
> 1734
> 1735 } MEM_DATA_STRUCT;
> […]
>
> With
>
> $ git grep -i tsc src/vendorcode/amd/agesa/f14/ | grep -i rate
> […]
> src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuF14Utilities.c: * @CpuServiceMethod{::F_CPU_GET_TSC_RATE}.
> src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuF14Utilities.c:F14GetTscRate (
> src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuF14Utilities.h:F14GetTscRate (
> […]
>
> I found `F14GetTscRate`.
>
> $ nl -ba src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuF14Utilities.c
> […]
> 213 /*---------------------------------------------------------------------------------------*/
> 214 /**
> 215 * Determines the rate at which the executing core's time stamp counter is
> 216 * incrementing.
> 217 *
> 218 * @CpuServiceMethod{::F_CPU_GET_TSC_RATE}.
> 219 *
> 220 * @param[in] FamilySpecificServices The current Family Specific Services.
> 221 * @param[out] FrequencyInMHz TSC actual frequency.
> 222 * @param[in] StdHeader Header for library and services.
> 223 *
> 224 * @return The most severe status of all called services
> 225 */
> 226 AGESA_STATUS
> 227 F14GetTscRate (
> 228 IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
> 229 OUT UINT32 *FrequencyInMHz,
> 230 IN AMD_CONFIG_PARAMS *StdHeader
> 231 )
> 232 {
> 233 UINT64 MsrReg;
> 234 PSTATE_CPU_FAMILY_SERVICES *FamilyServices;
> 235
> 236 FamilyServices = NULL;
> 237 GetFeatureServicesOfCurrentCore (&PstateFamilyServiceTable, (const VOID **)&FamilyServices, StdHeader);
> 238 ASSERT (FamilyServices != NULL);
> 239
> 240 LibAmdMsrRead (0xC0010015, &MsrReg, StdHeader);
> 241 if ((MsrReg & 0x01000000) != 0) {
> 242 return (FamilyServices->GetPstateFrequency (FamilyServices, 0, FrequencyInMHz, StdHeader));
> 243 } else {
> 244 return (FamilySpecificServices->GetCurrentNbFrequency (FamilySpecificServices, FrequencyInMHz, StdHeader));
> 245 }
> 246 }
> […]
>
> So there is the infrastructure already. The only problem is how to hook
> this up into coreboot. Create `src/cpu/amd/agesa/tsc_delay.c` and
> somehow call the AGESA `F14GetTscRate()` from it?
There are also other timers in the processor cores.
2.4.5 Timers
Each core includes the following timers. These timers do not
vary in frequency regardless of the current P-state or C-state.
• MSR0000_0010 [Time Stamp Counter (TSC)]; the TSC increments at
the rate specified by MSRC001_0015[TscFreqSel].
• The APIC timer (APIC380 and APIC390), which decrements at a
rate of 2x CLKIN.
It looks like that is what AGESA uses and which might be there from the
“beginning”.
Thanks,
Paul
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <http://www.coreboot.org/pipermail/coreboot/attachments/20130514/8b30ac5b/attachment.sig>
More information about the coreboot
mailing list