Browse Source

[time] Add support for the ACPI power management timer

Allow the ACPI power management timer to be used if enabled via
TIMER_ACPI in config/timer.h.  This provides an alternative timer on
systems where the standard 8254 PIT is unavailable or unreliable.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 6 years ago
parent
commit
3ec2079ce2

+ 1
- 0
src/arch/x86/include/bits/errfile.h View File

@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
27 27
 #define ERRFILE_acpipwr		( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 )
28 28
 #define ERRFILE_cpuid		( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 )
29 29
 #define ERRFILE_rdtsc_timer	( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 )
30
+#define ERRFILE_acpi_timer	( ERRFILE_ARCH | ERRFILE_CORE | 0x00130000 )
30 31
 
31 32
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
32 33
 #define ERRFILE_bzimage	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )

+ 136
- 0
src/arch/x86/interface/pcbios/acpi_timer.c View File

@@ -0,0 +1,136 @@
1
+/*
2
+ * Copyright (C) 2018 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <unistd.h>
27
+#include <errno.h>
28
+#include <assert.h>
29
+#include <byteswap.h>
30
+#include <ipxe/io.h>
31
+#include <ipxe/acpi.h>
32
+
33
+/** @file
34
+ *
35
+ * ACPI power management timer
36
+ *
37
+ */
38
+
39
+/** ACPI timer frequency (fixed 3.579545MHz) */
40
+#define ACPI_TIMER_HZ 3579545
41
+
42
+/** ACPI timer mask
43
+ *
44
+ * Timers may be implemented as either 24-bit or 32-bit counters.  We
45
+ * simplify the code by pessimistically assuming that the timer has
46
+ * only 24 bits.
47
+ */
48
+#define ACPI_TIMER_MASK 0x00ffffffUL
49
+
50
+/** Power management timer register address */
51
+static unsigned int pm_tmr;
52
+
53
+struct timer acpi_timer __timer ( TIMER_PREFERRED );
54
+
55
+/**
56
+ * Get current system time in ticks
57
+ *
58
+ * @ret ticks		Current time, in ticks
59
+ */
60
+static unsigned long acpi_currticks ( void ) {
61
+	static unsigned long offset;
62
+	static uint32_t prev;
63
+	uint32_t now;
64
+
65
+	/* Read timer and account for wraparound */
66
+	now = ( inl ( pm_tmr ) & ACPI_TIMER_MASK );
67
+	if ( now < prev ) {
68
+		offset += ( ( ACPI_TIMER_MASK + 1 ) /
69
+			    ( ACPI_TIMER_HZ / TICKS_PER_SEC ) );
70
+	}
71
+	prev = now;
72
+
73
+	/* Convert to timer ticks */
74
+	return ( offset + ( now / ( ACPI_TIMER_HZ / TICKS_PER_SEC ) ) );
75
+}
76
+
77
+/**
78
+ * Delay for a fixed number of microseconds
79
+ *
80
+ * @v usecs		Number of microseconds for which to delay
81
+ */
82
+static void acpi_udelay ( unsigned long usecs ) {
83
+	uint32_t start;
84
+	uint32_t elapsed;
85
+	uint32_t threshold;
86
+
87
+	/* Delay until a suitable number of ticks have elapsed.  We do
88
+	 * not need to allow for multiple wraparound, since the
89
+	 * wraparound period for a 24-bit timer at 3.579545MHz is
90
+	 * around 4700000us.
91
+	 */
92
+	start = inl ( pm_tmr );
93
+	threshold = ( ( usecs * ACPI_TIMER_HZ ) / 1000000 );
94
+	do {
95
+		elapsed = ( ( inl ( pm_tmr ) - start ) & ACPI_TIMER_MASK );
96
+	} while ( elapsed < threshold );
97
+}
98
+
99
+/**
100
+ * Probe ACPI power management timer
101
+ *
102
+ * @ret rc		Return status code
103
+ */
104
+static int acpi_timer_probe ( void ) {
105
+	struct acpi_fadt fadtab;
106
+	userptr_t fadt;
107
+	unsigned int pm_tmr_blk;
108
+
109
+	/* Locate FADT */
110
+	fadt = acpi_find ( FADT_SIGNATURE, 0 );
111
+	if ( ! fadt ) {
112
+		DBGC ( &acpi_timer, "ACPI could not find FADT\n" );
113
+		return -ENOENT;
114
+	}
115
+
116
+	/* Read FADT */
117
+	copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
118
+	pm_tmr_blk = le32_to_cpu ( fadtab.pm_tmr_blk );
119
+	if ( ! pm_tmr_blk ) {
120
+		DBGC ( &acpi_timer, "ACPI has no timer\n" );
121
+		return -ENOENT;
122
+	}
123
+
124
+	/* Record power management timer register address */
125
+	pm_tmr = ( pm_tmr_blk + ACPI_PM_TMR );
126
+
127
+	return 0;
128
+}
129
+
130
+/** ACPI timer */
131
+struct timer acpi_timer __timer ( TIMER_PREFERRED ) = {
132
+	.name = "acpi",
133
+	.probe = acpi_timer_probe,
134
+	.currticks = acpi_currticks,
135
+	.udelay = acpi_udelay,
136
+};

+ 3
- 0
src/config/config_timer.c View File

@@ -46,3 +46,6 @@ REQUIRE_OBJECT ( efi_timer );
46 46
 #ifdef TIMER_LINUX
47 47
 REQUIRE_OBJECT ( linux_timer );
48 48
 #endif
49
+#ifdef TIMER_ACPI
50
+REQUIRE_OBJECT ( acpi_timer );
51
+#endif

+ 7
- 0
src/include/ipxe/acpi.h View File

@@ -119,6 +119,10 @@ struct acpi_fadt {
119 119
 	uint32_t pm1a_cnt_blk;
120 120
 	/** PM1b Control Register Block */
121 121
 	uint32_t pm1b_cnt_blk;
122
+	/** PM2 Control Register Block */
123
+	uint32_t pm2_cnt_blk;
124
+	/** PM Timer Control Register Block */
125
+	uint32_t pm_tmr_blk;
122 126
 } __attribute__ (( packed ));
123 127
 
124 128
 /** ACPI PM1 Control Register (within PM1a_CNT_BLK or PM1A_CNT_BLK) */
@@ -126,6 +130,9 @@ struct acpi_fadt {
126 130
 #define ACPI_PM1_CNT_SLP_TYP(x) ( (x) << 10 )	/**< Sleep type */
127 131
 #define ACPI_PM1_CNT_SLP_EN ( 1 << 13 )		/**< Sleep enable */
128 132
 
133
+/** ACPI PM Timer Register (within PM_TMR_BLK) */
134
+#define ACPI_PM_TMR 0
135
+
129 136
 /** Differentiated System Description Table (DSDT) signature */
130 137
 #define DSDT_SIGNATURE ACPI_SIGNATURE ( 'D', 'S', 'D', 'T' )
131 138
 

Loading…
Cancel
Save