xref: /linux/arch/x86/realmode/rm/wakemain.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "wakeup.h"
3 #include "boot.h"
4 
5 static void udelay(int loops)
6 {
7 	while (loops--)
8 		io_delay();	/* Approximately 1 us */
9 }
10 
11 static void beep(unsigned int hz)
12 {
13 	u8 enable;
14 
15 	if (!hz) {
16 		enable = 0x00;		/* Turn off speaker */
17 	} else {
18 		u16 div = 1193181/hz;
19 
20 		outb(0xb6, 0x43);	/* Ctr 2, squarewave, load, binary */
21 		io_delay();
22 		outb(div, 0x42);	/* LSB of counter */
23 		io_delay();
24 		outb(div >> 8, 0x42);	/* MSB of counter */
25 		io_delay();
26 
27 		enable = 0x03;		/* Turn on speaker */
28 	}
29 	inb(0x61);		/* Dummy read of System Control Port B */
30 	io_delay();
31 	outb(enable, 0x61);	/* Enable timer 2 output to speaker */
32 	io_delay();
33 }
34 
35 #define DOT_HZ		880
36 #define DASH_HZ		587
37 #define US_PER_DOT	125000
38 
39 /* Okay, this is totally silly, but it's kind of fun. */
40 static void send_morse(const char *pattern)
41 {
42 	char s;
43 
44 	while ((s = *pattern++)) {
45 		switch (s) {
46 		case '.':
47 			beep(DOT_HZ);
48 			udelay(US_PER_DOT);
49 			beep(0);
50 			udelay(US_PER_DOT);
51 			break;
52 		case '-':
53 			beep(DASH_HZ);
54 			udelay(US_PER_DOT * 3);
55 			beep(0);
56 			udelay(US_PER_DOT);
57 			break;
58 		default:	/* Assume it's a space */
59 			udelay(US_PER_DOT * 3);
60 			break;
61 		}
62 	}
63 }
64 
65 struct port_io_ops pio_ops;
66 
67 void main(void)
68 {
69 	init_default_io_ops();
70 
71 	/* Kill machine if structures are wrong */
72 	if (wakeup_header.real_magic != 0x12345678)
73 		while (1)
74 			;
75 
76 	if (wakeup_header.realmode_flags & 4)
77 		send_morse("...-");
78 
79 	if (wakeup_header.realmode_flags & 1)
80 		asm volatile("lcallw   $0xc000,$3");
81 
82 	if (wakeup_header.realmode_flags & 2) {
83 		/* Need to call BIOS */
84 		probe_cards(0);
85 		set_mode(wakeup_header.video_mode);
86 	}
87 }
88