| /* | 
 |  * (C) Copyright 2002 | 
 |  * Keith Outwater, keith_outwater@mvis.com | 
 |  * | 
 |  * See file CREDITS for list of people who contributed to this | 
 |  * project. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License as | 
 |  * published by the Free Software Foundation; either version 2 of | 
 |  * the License, or (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
 |  * MA 02111-1307 USA | 
 |  */ | 
 |  | 
 | #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 *)CFG_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 *)CFG_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 *)CFG_IMMR; | 
 |  | 
 | 	immap->im_cpmtimer.cpmt_tgcr &= ~TGCR_STP1; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Turn the beeper off | 
 |  */ | 
 | void | 
 | beeper_off(void) | 
 | { | 
 |     volatile immap_t *immap  = (immap_t *)CFG_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 *)CFG_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; | 
 | } | 
 |  | 
 | /* vim: set ts=4 sw=4 tw=78: */ |