aboutsummaryrefslogtreecommitdiff
path: root/board/gen860t/beeper.c
blob: 0bebca98b1e61d176a503b289f1ffc7489e1761a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
 * (C) Copyright 2002
 * Keith Outwater, keith_outwater@mvis.com
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <mpc8xx.h>
#include <asm/8xx_immap.h>
#include <linux/ctype.h>

/*
 * Basic beeper support for the GEN860T board.  The GEN860T includes
 * an audio sounder driven by a Phillips TDA8551 amplifier.  The
 * TDA8551 features a digital volume control which uses a "trinary"
 * input (high/high-Z/low) to set volume.  The 860's SPKROUT pin
 * drives the amplifier input.
 */

/*
 * Initialize beeper-related hardware. Initialize timer 1 for use with
 * the beeper. Use 66 MHz internal clock with prescale of 33 to get
 * 1 uS period per count.
 * FIXME: we should really compute the prescale based on the reported
 * core clock frequency.
 */
void init_beeper (void)
{
	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;

	immap->im_cpmtimer.cpmt_tgcr &= ~TGCR_RST1 | TGCR_STP1;
	immap->im_cpmtimer.cpmt_tmr1 = ((33 << TMR_PS_SHIFT) & TMR_PS_MSK)
		| TMR_OM | TMR_FRR | TMR_ICLK_IN_GEN;
	immap->im_cpmtimer.cpmt_tcn1 = 0;
	immap->im_cpmtimer.cpmt_ter1 = 0xffff;
	immap->im_cpmtimer.cpmt_tgcr |= TGCR_RST1;
}

/*
 * Set beeper frequency.  Max allowed frequency is 2.5 KHz.  This limit
 * is mostly arbitrary, but the beeper isn't really much good beyond this
 * frequency.
 */
void set_beeper_frequency (uint frequency)
{
#define FREQ_LIMIT	2500

	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;

	/*
	 * Compute timer ticks given desired frequency.  The timer is set up
	 * to count 0.5 uS per tick and it takes two ticks per cycle (Hz).
	 */
	if (frequency > FREQ_LIMIT)
		frequency = FREQ_LIMIT;
	frequency = 1000000 / frequency;
	immap->im_cpmtimer.cpmt_trr1 = (ushort) frequency;
}

/*
 * Turn the beeper on
 */
void beeper_on (void)
{
	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;

	immap->im_cpmtimer.cpmt_tgcr &= ~TGCR_STP1;
}

/*
 * Turn the beeper off
 */
void beeper_off (void)
{
	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;

	immap->im_cpmtimer.cpmt_tgcr |= TGCR_STP1;
}

/*
 * Increase or decrease the beeper volume.  Volume can be set
 * from off to full in 64 steps.  To increase volume, the output
 * pin is actively driven high, then returned to tristate.
 * To decrease volume, output a low on the port pin (no need to
 * change pin mode to tristate) then output a high to go back to
 * tristate.
 */
void set_beeper_volume (int steps)
{
	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
	int i;

	if (steps >= 0) {
		for (i = 0; i < (steps >= 64 ? 64 : steps); i++) {
			immap->im_cpm.cp_pbodr &= ~(0x80000000 >> 19);
			udelay (1);
			immap->im_cpm.cp_pbodr |= (0x80000000 >> 19);
			udelay (1);
		}
	} else {
		for (i = 0; i > (steps <= -64 ? -64 : steps); i--) {
			immap->im_cpm.cp_pbdat &= ~(0x80000000 >> 19);
			udelay (1);
			immap->im_cpm.cp_pbdat |= (0x80000000 >> 19);
			udelay (1);
		}
	}
}

/*
 * Check the environment to see if the beeper needs beeping.
 * Controlled by a sequence of the form:
 * freq/delta volume/on time/off time;... where:
 * freq			= frequency in Hz (0 - 2500)
 * delta volume = volume steps up or down (-64 <= vol <= 64)
 * on time		= time in mS
 * off time		= time in mS
 *
 * Return 1 on success, 0 on failure
 */
int do_beeper (char *sequence)
{
#define DELIMITER	';'

	int args[4];
	int i;
	int val;
	char *p = sequence;
	char *tp;

	/*
	 * Parse the control sequence.  This is a really simple parser
	 * without any real error checking.  You can probably blow it
	 * up really easily.
	 */
	if (*p == '\0' || !isdigit (*p)) {
		printf ("%s:%d: null or invalid string (%s)\n",
			__FILE__, __LINE__, p);
		return 0;
	}

	i = 0;
	while (*p != '\0') {
		while (*p != DELIMITER) {
			if (i > 3)
				i = 0;
			val = (int) simple_strtol (p, &tp, 0);
			if (tp == p) {
				printf ("%s:%d: no digits or bad format\n",
					__FILE__, __LINE__);
				return 0;
			} else {
				args[i] = val;
			}

			i++;
			if (*tp == DELIMITER)
				p = tp;
			else
				p = ++tp;
		}
		p++;

		/*
		 * Well, we got something that has a chance of being correct
		 */
#if 0
		for (i = 0; i < 4; i++) {
			printf ("%s:%d:arg %d = %d\n", __FILE__, __LINE__, i,
				args[i]);
		}
		printf ("\n");
#endif
		set_beeper_frequency (args[0]);
		set_beeper_volume (args[1]);
		beeper_on ();
		udelay (1000 * args[2]);
		beeper_off ();
		udelay (1000 * args[3]);
	}
	return 1;
}