| /* | |
| * (C) Copyright 2002 | |
| * Daniel Engström, Omicron Ceti AB, daniel@omicron.se. | |
| * | |
| * 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 <asm/io.h> | |
| #include <asm/i8254.h> | |
| #include <asm/ibmpc.h> | |
| static volatile unsigned long system_ticks; | |
| static int timer_init_done =0; | |
| static void timer_isr(void *unused) | |
| { | |
| system_ticks++; | |
| } | |
| unsigned long get_system_ticks(void) | |
| { | |
| return system_ticks; | |
| } | |
| #define TIMER0_VALUE 0x04aa /* 1kHz 1.9318MHz / 1000 */ | |
| #define TIMER2_VALUE 0x0a8e /* 440Hz */ | |
| int timer_init(void) | |
| { | |
| system_ticks = 0; | |
| irq_install_handler(0, timer_isr, NULL); | |
| /* initialize timer 0 and 2 | |
| * | |
| * Timer 0 is used to increment system_tick 1000 times/sec | |
| * Timer 1 was used for DRAM refresh in early PC's | |
| * Timer 2 is used to drive the speaker | |
| * (to stasrt a beep: write 3 to port 0x61, | |
| * to stop it again: write 0) | |
| */ | |
| outb(PIT_CMD_CTR0|PIT_CMD_BOTH|PIT_CMD_MODE2, PIT_BASE + PIT_COMMAND); | |
| outb(TIMER0_VALUE&0xff, PIT_BASE + PIT_T0); | |
| outb(TIMER0_VALUE>>8, PIT_BASE + PIT_T0); | |
| outb(PIT_CMD_CTR2|PIT_CMD_BOTH|PIT_CMD_MODE3, PIT_BASE + PIT_COMMAND); | |
| outb(TIMER2_VALUE&0xff, PIT_BASE + PIT_T2); | |
| outb(TIMER2_VALUE>>8, PIT_BASE + PIT_T2); | |
| timer_init_done = 1; | |
| return 0; | |
| } | |
| #ifdef CFG_TIMER_GENERIC | |
| /* the unit for these is CFG_HZ */ | |
| /* FixMe: implement these */ | |
| void reset_timer (void) | |
| { | |
| system_ticks = 0; | |
| } | |
| ulong get_timer (ulong base) | |
| { | |
| return (system_ticks - base); | |
| } | |
| void set_timer (ulong t) | |
| { | |
| system_ticks = t; | |
| } | |
| static u16 read_pit(void) | |
| { | |
| u8 low; | |
| outb(PIT_CMD_LATCH, PIT_BASE + PIT_COMMAND); | |
| low = inb(PIT_BASE + PIT_T0); | |
| return ((inb(PIT_BASE + PIT_T0) << 8) | low); | |
| } | |
| /* this is not very exact */ | |
| void udelay (unsigned long usec) | |
| { | |
| int counter; | |
| int wraps; | |
| if (!timer_init_done) { | |
| return; | |
| } | |
| counter = read_pit(); | |
| wraps = usec/1000; | |
| usec = usec%1000; | |
| usec*=1194; | |
| usec/=1000; | |
| usec+=counter; | |
| if (usec > 1194) { | |
| usec-=1194; | |
| wraps++; | |
| } | |
| while (1) { | |
| int new_count = read_pit(); | |
| if (((new_count < usec) && !wraps) || wraps < 0) { | |
| break; | |
| } | |
| if (new_count > counter) { | |
| wraps--; | |
| } | |
| counter = new_count; | |
| } | |
| } | |
| #if 0 | |
| /* this is a version with debug output */ | |
| void _udelay (unsigned long usec) | |
| { | |
| int counter; | |
| int wraps; | |
| int usec1, usec2, usec3; | |
| int wraps1, wraps2, wraps3, wraps4; | |
| int ctr1, ctr2, ctr3, nct1, nct2; | |
| int i; | |
| usec1=usec; | |
| if (!timer_init_done) { | |
| return; | |
| } | |
| counter = read_pit(); | |
| ctr1 = counter; | |
| wraps = usec/1000; | |
| usec = usec%1000; | |
| usec2 = usec; | |
| wraps1 = wraps; | |
| usec*=1194; | |
| usec/=1000; | |
| usec+=counter; | |
| if (usec > 1194) { | |
| usec-=1194; | |
| wraps++; | |
| } | |
| usec3 = usec; | |
| wraps2 = wraps; | |
| ctr2 = wraps3 = nct1 = 4711; | |
| ctr3 = wraps4 = nct2 = 4711; | |
| i=0; | |
| while (1) { | |
| int new_count = read_pit(); | |
| i++; | |
| if ((new_count < usec && !wraps) || wraps < 0) { | |
| break; | |
| } | |
| if (new_count > counter) { | |
| wraps--; | |
| } | |
| if (ctr2==4711) { | |
| ctr2 = counter; | |
| wraps3 = wraps; | |
| nct1 = new_count; | |
| } else { | |
| ctr3 = counter; | |
| wraps4 = wraps; | |
| nct2 = new_count; | |
| } | |
| counter = new_count; | |
| } | |
| printf("udelay(%d)\n", usec1); | |
| printf("counter %d\n", ctr1); | |
| printf("1: wraps %d, usec %d\n", wraps1, usec2); | |
| printf("2: wraps %d, usec %d\n", wraps2, usec3); | |
| printf("new_count[0] %d counter %d wraps %d\n", nct1, ctr2, wraps3); | |
| printf("new_count[%d] %d counter %d wraps %d\n", i, nct2, ctr3, wraps4); | |
| printf("%d %d %d %d %d\n", | |
| read_pit(), read_pit(), read_pit(), | |
| read_pit(), read_pit()); | |
| } | |
| #endif | |
| #endif |