Browse Source

[timer] Rewrite the 8254 Programmable Interval Timer support

The 8254 timer code (used to implement udelay()) has an unknown
provenance.  Rewrite this code to avoid potential licensing
uncertainty.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
08189df4e0

+ 3
- 3
src/arch/i386/core/rdtsc_timer.c View File

@@ -27,7 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
27 27
 
28 28
 #include <assert.h>
29 29
 #include <ipxe/timer.h>
30
-#include <ipxe/timer2.h>
30
+#include <ipxe/pit8254.h>
31 31
 
32 32
 /**
33 33
  * Number of TSC ticks per microsecond
@@ -56,10 +56,10 @@ static void rdtsc_udelay ( unsigned long usecs ) {
56 56
 			elapsed = ( currticks() - start );
57 57
 		} while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) );
58 58
 	} else {
59
-		/* Not yet calibrated; use timer2 and calibrate
59
+		/* Not yet calibrated; use 8254 PIT and calibrate
60 60
 		 * based on result.
61 61
 		 */
62
-		timer2_udelay ( usecs );
62
+		pit8254_udelay ( usecs );
63 63
 		elapsed = ( currticks() - start );
64 64
 		rdtsc_ticks_per_usec = ( elapsed / usecs );
65 65
 		DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs "

+ 0
- 87
src/arch/i386/core/timer2.c View File

@@ -1,87 +0,0 @@
1
-/*
2
- * arch/i386/core/i386_timer.c
3
- *
4
- * Use the "System Timer 2" to implement the udelay callback in
5
- * the BIOS timer driver. Also used to calibrate the clock rate
6
- * in the RTDSC timer driver.
7
- * 
8
- * This program is free software; you can redistribute it and/or
9
- * modify it under the terms of the GNU General Public License as
10
- * published by the Free Software Foundation; either version 2, or (at
11
- * your option) any later version.
12
- */
13
-
14
-FILE_LICENCE ( GPL2_OR_LATER );
15
-
16
-#include <stddef.h>
17
-#include <ipxe/timer2.h>
18
-#include <ipxe/io.h>
19
-
20
-/* Timers tick over at this rate */
21
-#define TIMER2_TICKS_PER_SEC	1193180U
22
-
23
-/* Parallel Peripheral Controller Port B */
24
-#define	PPC_PORTB	0x61
25
-
26
-/* Meaning of the port bits */
27
-#define	PPCB_T2OUT	0x20	/* Bit 5 */
28
-#define	PPCB_SPKR	0x02	/* Bit 1 */
29
-#define	PPCB_T2GATE	0x01	/* Bit 0 */
30
-
31
-/* Ports for the 8254 timer chip */
32
-#define	TIMER2_PORT	0x42
33
-#define	TIMER_MODE_PORT	0x43
34
-
35
-/* Meaning of the mode bits */
36
-#define	TIMER0_SEL	0x00
37
-#define	TIMER1_SEL	0x40
38
-#define	TIMER2_SEL	0x80
39
-#define	READBACK_SEL	0xC0
40
-
41
-#define	LATCH_COUNT	0x00
42
-#define	LOBYTE_ACCESS	0x10
43
-#define	HIBYTE_ACCESS	0x20
44
-#define	WORD_ACCESS	0x30
45
-
46
-#define	MODE0		0x00
47
-#define	MODE1		0x02
48
-#define	MODE2		0x04
49
-#define	MODE3		0x06
50
-#define	MODE4		0x08
51
-#define	MODE5		0x0A
52
-
53
-#define	BINARY_COUNT	0x00
54
-#define	BCD_COUNT	0x01
55
-
56
-static void load_timer2 ( unsigned int ticks ) {
57
-	/*
58
-	 * Now let's take care of PPC channel 2
59
-	 *
60
-	 * Set the Gate high, program PPC channel 2 for mode 0,
61
-	 * (interrupt on terminal count mode), binary count,
62
-	 * load 5 * LATCH count, (LSB and MSB) to begin countdown.
63
-	 *
64
-	 * Note some implementations have a bug where the high bits byte
65
-	 * of channel 2 is ignored.
66
-	 */
67
-	/* Set up the timer gate, turn off the speaker */
68
-	/* Set the Gate high, disable speaker */
69
-	outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
70
-	/* binary, mode 0, LSB/MSB, Ch 2 */
71
-	outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
72
-	/* LSB of ticks */
73
-	outb(ticks & 0xFF, TIMER2_PORT);
74
-	/* MSB of ticks */
75
-	outb(ticks >> 8, TIMER2_PORT);
76
-}
77
-
78
-static int timer2_running ( void ) {
79
-	return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
80
-}
81
-
82
-void timer2_udelay ( unsigned long usecs ) {
83
-	load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) );
84
-	while (timer2_running()) {
85
-		/* Do nothing */
86
-	}
87
-}

+ 3
- 3
src/arch/i386/include/ipxe/bios_timer.h View File

@@ -15,7 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
15 15
 #define TIMER_PREFIX_pcbios __pcbios_
16 16
 #endif
17 17
 
18
-#include <ipxe/timer2.h>
18
+#include <ipxe/pit8254.h>
19 19
 
20 20
 /**
21 21
  * Delay for a fixed number of microseconds
@@ -25,9 +25,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
25 25
 static inline __always_inline void
26 26
 TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) {
27 27
 	/* BIOS timer is not high-resolution enough for udelay(), so
28
-	 * we use timer2
28
+	 * we use the 8254 Programmable Interval Timer.
29 29
 	 */
30
-	timer2_udelay ( usecs );
30
+	pit8254_udelay ( usecs );
31 31
 }
32 32
 
33 33
 /**

+ 0
- 14
src/arch/i386/include/ipxe/timer2.h View File

@@ -1,14 +0,0 @@
1
-#ifndef _IPXE_TIMER2_H
2
-#define _IPXE_TIMER2_H
3
-
4
-/** @file
5
- *
6
- * Timer chip control
7
- *
8
- */
9
-
10
-FILE_LICENCE ( GPL2_OR_LATER );
11
-
12
-extern void timer2_udelay ( unsigned long usecs );
13
-
14
-#endif /* _IPXE_TIMER2_H */

+ 66
- 0
src/arch/x86/core/pit8254.c View File

@@ -0,0 +1,66 @@
1
+/*
2
+ * Copyright (C) 2015 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 (at your option) 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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <assert.h>
23
+#include <ipxe/io.h>
24
+#include <ipxe/pit8254.h>
25
+
26
+/** @file
27
+ *
28
+ * 8254 Programmable Interval Timer
29
+ *
30
+ */
31
+
32
+/**
33
+ * Delay for a fixed number of timer ticks using the speaker channel
34
+ *
35
+ * @v ticks		Number of timer ticks for which to delay
36
+ */
37
+void pit8254_speaker_delay ( unsigned int ticks ) {
38
+	uint8_t spkr;
39
+	uint8_t cmd;
40
+	uint8_t low;
41
+	uint8_t high;
42
+
43
+	/* Sanity check */
44
+	assert ( ticks <= 0xffff );
45
+
46
+	/* Disable speaker, set speaker channel gate input high */
47
+	spkr = inb ( PIT8254_SPKR );
48
+	spkr &= ~PIT8254_SPKR_ENABLE;
49
+	spkr |= PIT8254_SPKR_GATE;
50
+	outb ( spkr, PIT8254_SPKR );
51
+
52
+	/* Program speaker channel to "interrupt" on terminal count */
53
+	cmd = ( PIT8254_CMD_CHANNEL ( PIT8254_CH_SPKR ) |
54
+		PIT8254_CMD_ACCESS_LOHI | PIT8254_CMD_OP_TERMINAL |
55
+		PIT8254_CMD_BINARY );
56
+	low = ( ( ticks >> 0 ) & 0xff );
57
+	high = ( ( ticks >> 8 ) & 0xff );
58
+	outb ( cmd, PIT8254_CMD );
59
+	outb ( low, PIT8254_DATA ( PIT8254_CH_SPKR ) );
60
+	outb ( high, PIT8254_DATA ( PIT8254_CH_SPKR ) );
61
+
62
+	/* Wait for channel to "interrupt" */
63
+	do {
64
+		spkr = inb ( PIT8254_SPKR );
65
+	} while ( ! ( spkr & PIT8254_SPKR_OUT ) );
66
+}

+ 81
- 0
src/arch/x86/include/ipxe/pit8254.h View File

@@ -0,0 +1,81 @@
1
+#ifndef _IPXE_PIT8254_H
2
+#define _IPXE_PIT8254_H
3
+
4
+/** @file
5
+ *
6
+ * 8254 Programmable Interval Timer
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+/** IRQ0 channel */
13
+#define PIT8254_CH_IRQ0 0
14
+
15
+/** PC speaker channel */
16
+#define PIT8254_CH_SPKR 2
17
+
18
+/** Timer frequency (1.193182MHz) */
19
+#define PIT8254_HZ 1193182UL
20
+
21
+/** Data port */
22
+#define PIT8254_DATA(channel) ( 0x40 + (channel) )
23
+
24
+/** Mode/command register */
25
+#define PIT8254_CMD 0x43
26
+
27
+/** Select channel */
28
+#define PIT8254_CMD_CHANNEL(channel) ( (channel) << 6 )
29
+
30
+/** Access modes */
31
+#define PIT8254_CMD_ACCESS_LATCH 0x00	/**< Latch count value command */
32
+#define PIT8254_CMD_ACCESS_LO	0x10	/**< Low byte only */
33
+#define PIT8254_CMD_ACCESS_HI	0x20	/**< High byte only */
34
+#define PIT8254_CMD_ACCESS_LOHI	0x30	/**< Low-byte, high-byte pair */
35
+
36
+/* Operating modes */
37
+#define PIT8254_CMD_OP_TERMINAL	0x00	/**< Interrupt on terminal count */
38
+#define PIT8254_CMD_OP_ONESHOT	0x02	/**< Hardware re-triggerable one-shot */
39
+#define PIT8254_CMD_OP_RATE	0x04	/**< Rate generator */
40
+#define PIT8254_CMD_OP_SQUARE	0x06	/**< Square wave generator */
41
+#define PIT8254_CMD_OP_SWSTROBE	0x08	/**< Software triggered strobe */
42
+#define PIT8254_CMD_OP_HWSTROBE	0x0a	/**< Hardware triggered strobe */
43
+#define PIT8254_CMD_OP_RATE2	0x0c	/**< Rate generator (duplicate) */
44
+#define PIT8254_CMD_OP_SQUARE2	0x0e	/**< Square wave generator (duplicate)*/
45
+
46
+/** Binary mode */
47
+#define PIT8254_CMD_BINARY 0x00
48
+
49
+/** BCD mode */
50
+#define PIT8254_CMD_BCD 0x01
51
+
52
+/** PC speaker control register */
53
+#define PIT8254_SPKR 0x61
54
+
55
+/** PC speaker channel gate */
56
+#define PIT8254_SPKR_GATE 0x01
57
+
58
+/** PC speaker enabled */
59
+#define PIT8254_SPKR_ENABLE 0x02
60
+
61
+/** PC speaker channel output */
62
+#define PIT8254_SPKR_OUT 0x20
63
+
64
+extern void pit8254_speaker_delay ( unsigned int ticks );
65
+
66
+/**
67
+ * Delay for a fixed number of microseconds
68
+ *
69
+ * @v usecs		Number of microseconds for which to delay
70
+ */
71
+static inline __attribute__ (( always_inline )) void
72
+pit8254_udelay ( unsigned long usecs ) {
73
+
74
+	/* Delays are invariably compile-time constants; force the
75
+	 * multiplication and division to take place at compilation
76
+	 * time rather than runtime.
77
+	 */
78
+	pit8254_speaker_delay ( ( usecs * PIT8254_HZ ) / 1000000 );
79
+}
80
+
81
+#endif /* _IPXE_PIT8254_H */

Loading…
Cancel
Save