1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #include <arch/io.h>
4 #include <console/spkmodem.h>
5
6 #define SPEAKER_PIT_FREQUENCY 0x1234dd
7
8 enum {
9 PIT_COUNTER_0 = 0x40,
10 PIT_COUNTER_1 = 0x41,
11 PIT_COUNTER_2 = 0x42,
12 PIT_CTRL = 0x43,
13 PIT_SPEAKER_PORT = 0x61,
14 };
15
16 enum {
17 PIT_SPK_TMR2 = 0x01,
18 PIT_SPK_DATA = 0x02,
19 PIT_SPK_TMR2_LATCH = 0x20
20 };
21
22 enum {
23 PIT_CTRL_SELECT_MASK = 0xc0,
24 PIT_CTRL_SELECT_0 = 0x00,
25 PIT_CTRL_SELECT_1 = 0x40,
26 PIT_CTRL_SELECT_2 = 0x80,
27
28 PIT_CTRL_READLOAD_MASK = 0x30,
29 PIT_CTRL_COUNTER_LATCH = 0x00,
30 PIT_CTRL_READLOAD_LSB = 0x10,
31 PIT_CTRL_READLOAD_MSB = 0x20,
32 PIT_CTRL_READLOAD_WORD = 0x30,
33
34 PIT_CTRL_MODE_MASK = 0x0e,
35 PIT_CTRL_INTR_ON_TERM = 0x00,
36 PIT_CTRL_PROGR_ONE_SHOT = 0x02,
37
38 PIT_CTRL_RATE_GEN = 0x04,
39
40 PIT_CTRL_SQUAREWAVE_GEN = 0x06,
41 PIT_CTRL_SOFTSTROBE = 0x08,
42
43 PIT_CTRL_HARDSTROBE = 0x0a,
44
45 PIT_CTRL_COUNT_MASK = 0x01,
46 PIT_CTRL_COUNT_BINARY = 0x00,
47 PIT_CTRL_COUNT_BCD = 0x01
48 };
49
50 static void
make_tone(uint16_t freq_count,unsigned int duration)51 make_tone(uint16_t freq_count, unsigned int duration)
52 {
53 outb(PIT_CTRL_SELECT_2
54 | PIT_CTRL_READLOAD_WORD
55 | PIT_CTRL_SQUAREWAVE_GEN
56 | PIT_CTRL_COUNT_BINARY, PIT_CTRL);
57
58 outb(freq_count & 0xff, PIT_COUNTER_2);
59
60 outb((freq_count >> 8) & 0xff, PIT_COUNTER_2);
61
62 outb(inb(PIT_SPEAKER_PORT)
63 | PIT_SPK_TMR2 | PIT_SPK_DATA,
64 PIT_SPEAKER_PORT);
65
66 for (; duration; duration--) {
67 unsigned short counter, previous_counter = 0xffff;
68
69 while (1) {
70 counter = inb(PIT_COUNTER_2);
71 counter |= ((uint16_t)inb(PIT_COUNTER_2)) << 8;
72 if (counter > previous_counter) {
73 previous_counter = counter;
74 break;
75 }
76 previous_counter = counter;
77 }
78 }
79 }
80
spkmodem_tx_byte(unsigned char c)81 void spkmodem_tx_byte(unsigned char c)
82 {
83 int i;
84
85 make_tone(SPEAKER_PIT_FREQUENCY / 200, 4);
86 for (i = 7; i >= 0; i--) {
87 if ((c >> i) & 1)
88 make_tone(SPEAKER_PIT_FREQUENCY / 2000, 20);
89 else
90 make_tone(SPEAKER_PIT_FREQUENCY / 4000, 40);
91 make_tone(SPEAKER_PIT_FREQUENCY / 1000, 10);
92 }
93 make_tone(SPEAKER_PIT_FREQUENCY / 200, 0);
94 }
95
spkmodem_init(void)96 void spkmodem_init(void)
97 {
98 /* Some cards need time to come online.
99 * Output some message to get it started.
100 */
101 spkmodem_tx_byte('S');
102 spkmodem_tx_byte('P');
103 spkmodem_tx_byte('K');
104 spkmodem_tx_byte('\r');
105 spkmodem_tx_byte('\n');
106 }
107