Implement SC520 timers

Signed-off-by: Graeme Russ <graeme.russ at gmail.com>
This commit is contained in:
Graeme Russ 2009-02-24 21:14:45 +11:00 committed by Wolfgang Denk
parent 6d7f610b09
commit 8c63d47651
7 changed files with 259 additions and 243 deletions

View File

@ -29,7 +29,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(CPU).a
START = start.o start16.o resetvec.o
COBJS = serial.o interrupts.o exceptions.o cpu.o timer.o
COBJS = serial.o interrupts.o exceptions.o cpu.o
SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))

View File

@ -27,47 +27,56 @@
#include <asm/interrupt.h>
#include <asm/ic/sc520.h>
void reset_timer(void)
void sc520_timer_isr(void)
{
write_mmcr_word(SC520_GPTMR0CNT, 0);
write_mmcr_word(SC520_GPTMR0CTL, 0x6001);
/* Ack the GP Timer Interrupt */
write_mmcr_byte (SC520_GPTMRSTA, 0x02);
}
ulong get_timer(ulong base)
int timer_init(void)
{
/* fixme: 30 or 33 */
return read_mmcr_word(SC520_GPTMR0CNT) / 33;
}
/* Map GP Timer 1 to Master PIC IR0 */
write_mmcr_byte (SC520_GPTMR1MAP, 0x01);
void set_timer(ulong t)
{
/* FixMe: use two cascade coupled timers */
write_mmcr_word(SC520_GPTMR0CTL, 0x4001);
write_mmcr_word(SC520_GPTMR0CNT, t*33);
write_mmcr_word(SC520_GPTMR0CTL, 0x6001);
}
/* Disable GP Timers 1 & 2 - Allow configuration writes */
write_mmcr_word (SC520_GPTMR1CTL, 0x4000);
write_mmcr_word (SC520_GPTMR2CTL, 0x4000);
/* Reset GP Timers 1 & 2 */
write_mmcr_word (SC520_GPTMR1CNT, 0x0000);
write_mmcr_word (SC520_GPTMR2CNT, 0x0000);
/* Setup GP Timer 2 as a 100kHz (10us) prescaler */
write_mmcr_word (SC520_GPTMR2MAXCMPA, 83);
write_mmcr_word (SC520_GPTMR2CTL, 0xc001);
/* Setup GP Timer 1 as a 1000 Hz (1ms) interrupt generator */
write_mmcr_word (SC520_GPTMR1MAXCMPA, 100);
write_mmcr_word (SC520_GPTMR1CTL, 0xe009);
/* Clear the GP Timers status register */
write_mmcr_byte (SC520_GPTMRSTA, 0x07);
/* Register the SC520 specific timer interrupt handler */
register_timer_isr (sc520_timer_isr);
/* Install interrupt handler for GP Timer 1 */
irq_install_handler (0, timer_isr, NULL);
unmask_irq (0);
return 0;
}
void udelay(unsigned long usec)
{
int m=0;
int m = 0;
long u;
read_mmcr_word(SC520_SWTMRMILLI);
read_mmcr_word(SC520_SWTMRMICRO);
read_mmcr_word (SC520_SWTMRMILLI);
read_mmcr_word (SC520_SWTMRMICRO);
#if 0
/* do not enable this line, udelay is used in the serial driver -> recursion */
printf("udelay: %ld m.u %d.%d tm.tu %d.%d\n", usec, m, u, tm, tu);
#endif
while (1) {
m += read_mmcr_word(SC520_SWTMRMILLI);
u = read_mmcr_word(SC520_SWTMRMICRO) + (m * 1000);
if (usec <= u) {
break;
}
}
do {
m += read_mmcr_word (SC520_SWTMRMILLI);
u = read_mmcr_word (SC520_SWTMRMICRO) + (m * 1000);
} while (u < usec);
}

View File

@ -1,211 +0,0 @@
/*
* (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 CONFIG_SYS_GENERIC_TIMER
/* the unit for these is CONFIG_SYS_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

View File

@ -43,6 +43,13 @@ extern ulong i386boot_bios_size; /* size of BIOS emulation code */
/* cpu/.../cpu.c */
int cpu_init(void);
/* cpu/.../timer.c */
void timer_isr(void *);
typedef void (timer_fnc_t) (void);
int register_timer_isr (timer_fnc_t *isr_func);
/* Architecture specific - can be in cpu/i386/, lib_i386/, or $(BOARD)/ */
int timer_init(void);
/* cpu/.../interrupts.c */

View File

@ -39,7 +39,9 @@ COBJS-y += video_bios.o
COBJS-y += video.o
COBJS-y += zimage.o
COBJS-y += interrupts.o
COBJS-y += timer.o
COBJS-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
COBJS-$(CONFIG_SYS_GENERIC_TIMER) += pcat_timer.o
SRCS := $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))

102
lib_i386/pcat_timer.c Normal file
View File

@ -0,0 +1,102 @@
/*
* (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>
#define TIMER0_VALUE 0x04aa /* 1kHz 1.9318MHz / 1000 */
#define TIMER2_VALUE 0x0a8e /* 440Hz */
int timer_init(void)
{
/* 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);
irq_install_handler (0, timer_isr, NULL);
unmask_irq (0);
return 0;
}
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)
{
counter = read_pit ();
wraps = usec / 1000;
usec = usec % 1000;
usec *= 1194;
usec /= 1000;
usec += counter;
while (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;
}
}
}

107
lib_i386/timer.c Normal file
View File

@ -0,0 +1,107 @@
/*
* (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 <malloc.h>
#include <asm/io.h>
#include <asm/i8254.h>
#include <asm/ibmpc.h>
struct timer_isr_function {
struct timer_isr_function *next;
timer_fnc_t *isr_func;
};
static struct timer_isr_function *first_timer_isr = NULL;
static volatile unsigned long system_ticks = 0;
/*
* register_timer_isr() allows multiple architecture and board specific
* functions to be called every millisecond. Keep the execution time of
* each function as low as possible
*/
int register_timer_isr (timer_fnc_t *isr_func)
{
struct timer_isr_function *new_func;
struct timer_isr_function *temp;
int flag;
new_func = malloc(sizeof(struct timer_isr_function));
if (new_func == NULL)
return 1;
new_func->isr_func = isr_func;
new_func->next = NULL;
/*
* Don't allow timer interrupts while the
* linked list is being modified
*/
flag = disable_interrupts ();
if (first_timer_isr == NULL) {
first_timer_isr = new_func;
} else {
temp = first_timer_isr;
while (temp->next != NULL)
temp = temp->next;
temp->next = new_func;
}
if (flag)
enable_interrupts ();
return 0;
}
/*
* timer_isr() MUST be the registered interrupt handler for
*/
void timer_isr(void *unused)
{
struct timer_isr_function *temp = first_timer_isr;
system_ticks++;
/* Execute each registered function */
while (temp != NULL) {
temp->isr_func ();
temp = temp->next;
}
}
void reset_timer (void)
{
system_ticks = 0;
}
ulong get_timer (ulong base)
{
return (system_ticks - base);
}
void set_timer (ulong t)
{
system_ticks = t;
}