diff options
Diffstat (limited to 'cpu/i386')
-rw-r--r-- | cpu/i386/Makefile | 43 | ||||
-rw-r--r-- | cpu/i386/config.mk | 26 | ||||
-rw-r--r-- | cpu/i386/cpu.c | 62 | ||||
-rw-r--r-- | cpu/i386/interrupts.c | 583 | ||||
-rw-r--r-- | cpu/i386/reset.S | 38 | ||||
-rw-r--r-- | cpu/i386/serial.c | 460 | ||||
-rw-r--r-- | cpu/i386/start.S | 198 | ||||
-rw-r--r-- | cpu/i386/start16.S | 112 | ||||
-rw-r--r-- | cpu/i386/timer.c | 211 |
9 files changed, 1733 insertions, 0 deletions
diff --git a/cpu/i386/Makefile b/cpu/i386/Makefile new file mode 100644 index 00000000000..1482398efe3 --- /dev/null +++ b/cpu/i386/Makefile @@ -0,0 +1,43 @@ +# +# (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 $(TOPDIR)/config.mk + +LIB = lib$(CPU).a + +START = start.o start16.o reset.o +OBJS = serial.o interrupts.o cpu.o timer.o + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(OBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/i386/config.mk b/cpu/i386/config.mk new file mode 100644 index 00000000000..70caeb270aa --- /dev/null +++ b/cpu/i386/config.mk @@ -0,0 +1,26 @@ +# +# (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 +# + +PLATFORM_RELFLAGS += # -pipe -mpreferred-stack-boundary=2 -fno-builtin -nostdinc -nostdlib + +PLATFORM_CPPFLAGS += -march=i386 diff --git a/cpu/i386/cpu.c b/cpu/i386/cpu.c new file mode 100644 index 00000000000..669823f946e --- /dev/null +++ b/cpu/i386/cpu.c @@ -0,0 +1,62 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * 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 + */ + +/* + * CPU specific code + */ + +#include <common.h> +#include <command.h> + +int cpu_init(void) +{ + return 0; +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + extern void reset_cpu(ulong addr); + + printf ("resetting ...\n"); + udelay(50000); /* wait 50 ms */ + disable_interrupts(); + reset_cpu(0); + + /*NOTREACHED*/ + return 0; +} + +void flush_cache (unsigned long dummy1, unsigned long dummy2) +{ + asm("wbinvd\n"); + return; +} + diff --git a/cpu/i386/interrupts.c b/cpu/i386/interrupts.c new file mode 100644 index 00000000000..81d6881857c --- /dev/null +++ b/cpu/i386/interrupts.c @@ -0,0 +1,583 @@ +/* + * (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 <syscall.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/i8259.h> +#include <asm/ibmpc.h> + + +#if 0 +/* done */ +int interrupt_init (void); +void irq_install_handler(int, interrupt_handler_t *, void *); +void irq_free_handler (int); +void enable_interrupts (void); +int disable_interrupts (void); + +/* todo */ +void timer_interrupt (struct pt_regs *); +void external_interrupt (struct pt_regs *); +void reset_timer (void); +ulong get_timer (ulong base); +void set_timer (ulong t); + +#endif + +struct idt_entry { + u16 base_low; + u16 selector; + u8 res; + u8 access; + u16 base_high; +} __attribute__ ((packed)); + + +struct idt_entry idt[256]; + + +#define MAX_IRQ 16 + +typedef struct irq_handler { + struct irq_handler *next; + interrupt_handler_t* isr_func; + void *isr_data; +} irq_handler_t; + +#define IRQ_DISABLED 1 + +typedef struct { + irq_handler_t *handler; + unsigned long status; +} irq_desc_t; + +static irq_desc_t irq_table[MAX_IRQ]; + + +/* syscall stuff, very untested + * the current approach which includes copying + * part of the stack will work badly for + * varargs functions. + * if we were to store the top three words on the + * stack (eip, ss, eflags) somwhere and add 14 to + * %esp .... + */ +asm(".globl syscall_entry\n" \ + "syscall_entry:\n" \ + "movl 12(%esp), %eax\n" \ + "movl %esp, %ebx\n" \ + "addl $16, %ebx\n" \ + "pushl %ebx\n" \ + "pushl %eax\n" \ + "call do_syscall\n" \ + "addl $8, %esp\n" \ + "iret\n"); + +void __attribute__ ((regparm(0))) syscall_entry(void); + +int __attribute__ ((regparm(0))) do_syscall(u32 nr, u32 *stack) +{ + if (nr<NR_SYSCALLS) { + /* We copy 8 args of the syscall, + * this will be a problem with the + * printf syscall .... */ + int (*fp)(u32, u32, u32, u32, + u32, u32, u32, u32); + fp = syscall_tbl[nr]; + return fp(stack[0], stack[1], stack[2], stack[3], + stack[4], stack[5], stack[6], stack[7]); + } + + return -1; +} + + +asm ("irq_return:\n" + " addl $4, %esp\n" + " popa\n" + " iret\n"); + +asm ("exp_return:\n" + " addl $12, %esp\n" + " pop %esp\n" + " popa\n" + " iret\n"); + +char exception_stack[4096]; + +#define DECLARE_INTERRUPT(x) \ + asm(".globl irq_"#x"\n" \ + "irq_"#x":\n" \ + "pusha \n" \ + "pushl $"#x"\n" \ + "pushl $irq_return\n" \ + "jmp do_irq\n"); \ + void __attribute__ ((regparm(0))) irq_##x(void) + +#define DECLARE_EXCEPTION(x, f) \ + asm(".globl exp_"#x"\n" \ + "exp_"#x":\n" \ + "pusha \n" \ + "movl %esp, %ebx\n" \ + "movl $exception_stack, %eax\n" \ + "movl %eax, %esp \n" \ + "pushl %ebx\n" \ + "movl 32(%esp), %ebx\n" \ + "xorl %edx, %edx\n" \ + "movw 36(%esp), %dx\n" \ + "pushl %edx\n" \ + "pushl %ebx\n" \ + "pushl $"#x"\n" \ + "pushl $exp_return\n" \ + "jmp "#f"\n"); \ + void __attribute__ ((regparm(0))) exp_##x(void) + +DECLARE_EXCEPTION(0, divide_exception_entry); /* Divide exception */ +DECLARE_EXCEPTION(1, debug_exception_entry); /* Debug exception */ +DECLARE_EXCEPTION(2, nmi_entry); /* NMI */ +DECLARE_EXCEPTION(3, unknown_exception_entry); /* Breakpoint/Coprocessor Error */ +DECLARE_EXCEPTION(4, unknown_exception_entry); /* Overflow */ +DECLARE_EXCEPTION(5, unknown_exception_entry); /* Bounds */ +DECLARE_EXCEPTION(6, invalid_instruction_entry); /* Invalid instruction */ +DECLARE_EXCEPTION(7, unknown_exception_entry); /* Device not present */ +DECLARE_EXCEPTION(8, double_fault_entry); /* Double fault */ +DECLARE_EXCEPTION(9, unknown_exception_entry); /* Co-processor segment overrun */ +DECLARE_EXCEPTION(10, invalid_tss_exception_entry);/* Invalid TSS */ +DECLARE_EXCEPTION(11, seg_fault_entry); /* Segment not present */ +DECLARE_EXCEPTION(12, stack_fault_entry); /* Stack overflow */ +DECLARE_EXCEPTION(13, gpf_entry); /* GPF */ +DECLARE_EXCEPTION(14, page_fault_entry); /* PF */ +DECLARE_EXCEPTION(15, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(16, fp_exception_entry); /* Floating point */ +DECLARE_EXCEPTION(17, alignment_check_entry); /* alignment check */ +DECLARE_EXCEPTION(18, machine_check_entry); /* machine check */ +DECLARE_EXCEPTION(19, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(20, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(21, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(22, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(23, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(24, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(25, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(26, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(27, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(28, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(29, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(30, unknown_exception_entry); /* Reserved */ +DECLARE_EXCEPTION(31, unknown_exception_entry); /* Reserved */ + +DECLARE_INTERRUPT(0); +DECLARE_INTERRUPT(1); +DECLARE_INTERRUPT(3); +DECLARE_INTERRUPT(4); +DECLARE_INTERRUPT(5); +DECLARE_INTERRUPT(6); +DECLARE_INTERRUPT(7); +DECLARE_INTERRUPT(8); +DECLARE_INTERRUPT(9); +DECLARE_INTERRUPT(10); +DECLARE_INTERRUPT(11); +DECLARE_INTERRUPT(12); +DECLARE_INTERRUPT(13); +DECLARE_INTERRUPT(14); +DECLARE_INTERRUPT(15); + +void __attribute__ ((regparm(0))) default_isr(void); +asm ("default_isr: iret\n"); + +void disable_irq(int irq) +{ + if (irq >= MAX_IRQ) { + return; + } + irq_table[irq].status |= IRQ_DISABLED; + +} + +void enable_irq(int irq) +{ + if (irq >= MAX_IRQ) { + return; + } + irq_table[irq].status &= ~IRQ_DISABLED; +} + +/* masks one specific IRQ in the PIC */ +static void unmask_irq(int irq) +{ + int imr_port; + + if (irq >= MAX_IRQ) { + return; + } + if (irq > 7) { + imr_port = SLAVE_PIC + IMR; + } else { + imr_port = MASTER_PIC + IMR; + } + + outb(inb(imr_port)&~(1<<(irq&7)), imr_port); +} + + +/* unmasks one specific IRQ in the PIC */ +static void mask_irq(int irq) +{ + int imr_port; + + if (irq >= MAX_IRQ) { + return; + } + if (irq > 7) { + imr_port = SLAVE_PIC + IMR; + } else { + imr_port = MASTER_PIC + IMR; + } + + outb(inb(imr_port)|(1<<(irq&7)), imr_port); +} + + +/* issue a Specific End Of Interrupt instruciton */ +static void specific_eoi(int irq) +{ + /* If it is on the slave PIC this have to be performed on + * both the master and the slave PICs */ + if (irq > 7) { + outb(OCW2_SEOI|(irq&7), SLAVE_PIC + OCW2); + irq = SEOI_IR2; /* also do IR2 on master */ + } + outb(OCW2_SEOI|irq, MASTER_PIC + OCW2); +} + +void __attribute__ ((regparm(0))) do_irq(int irq) +{ + + mask_irq(irq); + + if (irq_table[irq].status & IRQ_DISABLED) { + unmask_irq(irq); + specific_eoi(irq); + return; + } + + + if (NULL != irq_table[irq].handler) { + irq_handler_t *handler; + for (handler = irq_table[irq].handler; + NULL!= handler; handler = handler->next) { + handler->isr_func(handler->isr_data); + } + } else { + if ((irq & 7) != 7) { + printf("Spurious irq %d\n", irq); + } + } + unmask_irq(irq); + specific_eoi(irq); +} + + +void __attribute__ ((regparm(0))) unknown_exception_entry(int cause, int ip, int seg) +{ + printf("Unknown Exception %d at %04x:%08x\n", cause, seg, ip); +} + +void __attribute__ ((regparm(0))) divide_exception_entry(int cause, int ip, int seg) +{ + printf("Divide Error (Division by zero) at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) debug_exception_entry(int cause, int ip, int seg) +{ + printf("Debug Interrupt (Single step) at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) nmi_entry(int cause, int ip, int seg) +{ + printf("NMI Interrupt at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) invalid_instruction_entry(int cause, int ip, int seg) +{ + printf("Invalid Instruction at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) double_fault_entry(int cause, int ip, int seg) +{ + printf("Double fault at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) invalid_tss_exception_entry(int cause, int ip, int seg) +{ + printf("Invalid TSS at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) seg_fault_entry(int cause, int ip, int seg) +{ + printf("Segmentation fault at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) stack_fault_entry(int cause, int ip, int seg) +{ + printf("Stack fault at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) gpf_entry(int cause, int ip, int seg) +{ + printf("General protection fault at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) page_fault_entry(int cause, int ip, int seg) +{ + printf("Page fault at %04x:%08x\n", seg, ip); + while(1); +} + +void __attribute__ ((regparm(0))) fp_exception_entry(int cause, int ip, int seg) +{ + printf("Floating point exception at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) alignment_check_entry(int cause, int ip, int seg) +{ + printf("Alignment check at %04x:%08x\n", seg, ip); +} + +void __attribute__ ((regparm(0))) machine_check_entry(int cause, int ip, int seg) +{ + printf("Machine check exception at %04x:%08x\n", seg, ip); +} + + +void irq_install_handler(int ino, interrupt_handler_t *func, void *pdata) +{ + int status; + + if (ino>MAX_IRQ) { + return; + } + + if (NULL != irq_table[ino].handler) { + return; + } + + status = disable_interrupts(); + irq_table[ino].handler = malloc(sizeof(irq_handler_t)); + if (NULL == irq_table[ino].handler) { + return; + } + + memset(irq_table[ino].handler, 0, sizeof(irq_handler_t)); + + irq_table[ino].handler->isr_func = func; + irq_table[ino].handler->isr_data = pdata; + if (status) { + enable_interrupts(); + } + + unmask_irq(ino); + + return; +} + +void irq_free_handler(int ino) +{ + int status; + if (ino>MAX_IRQ) { + return; + } + + status = disable_interrupts(); + mask_irq(ino); + if (NULL == irq_table[ino].handler) { + return; + } + free(irq_table[ino].handler); + irq_table[ino].handler=NULL; + if (status) { + enable_interrupts(); + } + return; +} + + +asm ("idt_ptr:\n" + ".word 0x800\n" /* size of the table 8*256 bytes */ + ".long idt\n" /* offset */ + ".word 0x18\n");/* data segment */ + +static void set_vector(int intnum, void *routine) +{ + idt[intnum].base_high = (u16)((u32)(routine)>>16); + idt[intnum].base_low = (u16)((u32)(routine)&0xffff); +} + + +int interrupt_init(void) +{ + int i; + + /* Just in case... */ + disable_interrupts(); + + /* Initialize the IDT and stuff */ + + + memset(irq_table, 0, sizeof(irq_table)); + + /* Setup the IDT */ + for (i=0;i<256;i++) { + idt[i].access = 0x8e; + idt[i].res = 0; + idt[i].selector = 0x10; + set_vector(i, default_isr); + } + + asm ("cs lidt idt_ptr\n"); + + /* Setup exceptions */ + set_vector(0x00, exp_0); + set_vector(0x01, exp_1); + set_vector(0x02, exp_2); + set_vector(0x03, exp_3); + set_vector(0x04, exp_4); + set_vector(0x05, exp_5); + set_vector(0x06, exp_6); + set_vector(0x07, exp_7); + set_vector(0x08, exp_8); + set_vector(0x09, exp_9); + set_vector(0x0a, exp_10); + set_vector(0x0b, exp_11); + set_vector(0x0c, exp_12); + set_vector(0x0d, exp_13); + set_vector(0x0e, exp_14); + set_vector(0x0f, exp_15); + set_vector(0x10, exp_16); + set_vector(0x11, exp_17); + set_vector(0x12, exp_18); + set_vector(0x13, exp_19); + set_vector(0x14, exp_20); + set_vector(0x15, exp_21); + set_vector(0x16, exp_22); + set_vector(0x17, exp_23); + set_vector(0x18, exp_24); + set_vector(0x19, exp_25); + set_vector(0x1a, exp_26); + set_vector(0x1b, exp_27); + set_vector(0x1c, exp_28); + set_vector(0x1d, exp_29); + set_vector(0x1e, exp_30); + set_vector(0x1f, exp_31); + + + /* Setup interrupts */ + set_vector(0x20, irq_0); + set_vector(0x21, irq_1); + set_vector(0x23, irq_3); + set_vector(0x24, irq_4); + set_vector(0x25, irq_5); + set_vector(0x26, irq_6); + set_vector(0x27, irq_7); + set_vector(0x28, irq_8); + set_vector(0x29, irq_9); + set_vector(0x2a, irq_10); + set_vector(0x2b, irq_11); + set_vector(0x2c, irq_12); + set_vector(0x2d, irq_13); + set_vector(0x2e, irq_14); + set_vector(0x2f, irq_15); + /* vectors 0x30-0x3f are reserved for irq 16-31 */ + set_vector(0x40, syscall_entry); + + + /* Mask all interrupts */ + outb(0xff, MASTER_PIC + IMR); + outb(0xff, SLAVE_PIC + IMR); + + /* Master PIC */ + outb(ICW1_SEL|ICW1_EICW4, MASTER_PIC + ICW1); + outb(0x20, MASTER_PIC + ICW2); /* Place master PIC interrupts at INT20 */ + outb(IR2, MASTER_PIC + ICW3); /* ICW3, One slevc PIC is present */ + outb(ICW4_PM, MASTER_PIC + ICW4); + + for (i=0;i<8;i++) { + outb(OCW2_SEOI|i, MASTER_PIC + OCW2); + } + + /* Slave PIC */ + outb(ICW1_SEL|ICW1_EICW4, SLAVE_PIC + ICW1); + outb(0x28, SLAVE_PIC + ICW2); /* Place slave PIC interrupts at INT28 */ + outb(0x02, SLAVE_PIC + ICW3); /* Slave ID */ + outb(ICW4_PM, SLAVE_PIC + ICW4); + + for (i=0;i<8;i++) { + outb(OCW2_SEOI|i, SLAVE_PIC + OCW2); + } + + + /* enable cascade interrerupt */ + outb(0xfb, MASTER_PIC + IMR); + outb(0xff, SLAVE_PIC + IMR); + + /* It is now safe to enable interrupts */ + enable_interrupts(); + + return 0; +} + +void enable_interrupts(void) +{ + asm("sti\n"); +} + +int disable_interrupts(void) +{ + long flags; + + asm volatile ("pushfl ; popl %0 ; cli\n" : "=g" (flags) : ); + + return (flags&0x200); /* IE flags is bit 9 */ +} + + +#ifdef CFG_RESET_GENERIC + +void __attribute__ ((regparm(0))) generate_gpf(void); +asm(".globl generate_gpf\n" + "generate_gpf:\n" + "ljmp $0x70, $0x47114711\n"); /* segment 0x70 is an arbitrary segment which does not + * exist */ +void reset_cpu(ulong addr) +{ + set_vector(13, generate_gpf); /* general protection fault handler */ + set_vector(8, generate_gpf); /* double fault handler */ + generate_gpf(); /* start the show */ +} +#endif diff --git a/cpu/i386/reset.S b/cpu/i386/reset.S new file mode 100644 index 00000000000..57e32a8b7f0 --- /dev/null +++ b/cpu/i386/reset.S @@ -0,0 +1,38 @@ +/* + * U-boot - i386 Startup Code + * + * Copyright (c) 2002 Omicron Ceti AB, Daniel Engström <denaiel@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 + */ + +/* Reset vector, jumps to start16.S */ + +.extern start16 + +.section .reset, "ax" +.code16 +reset_vector: + cli + cld + jmp start16 + + .org 0xf + nop + diff --git a/cpu/i386/serial.c b/cpu/i386/serial.c new file mode 100644 index 00000000000..c0d2e8aa14f --- /dev/null +++ b/cpu/i386/serial.c @@ -0,0 +1,460 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 + */ +/*------------------------------------------------------------------------------+ */ +/* + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + */ +/*------------------------------------------------------------------------------- */ + +#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <asm/ibmpc.h> + +#if CONFIG_SERIAL_SOFTWARE_FIFO +#include <malloc.h> +#endif + +#define UART_RBR 0x00 +#define UART_THR 0x00 +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncLSRDataReady1 0x01 +#define asyncLSROverrunError1 0x02 +#define asyncLSRParityError1 0x04 +#define asyncLSRFramingError1 0x08 +#define asyncLSRBreakInterrupt1 0x10 +#define asyncLSRTxHoldEmpty1 0x20 +#define asyncLSRTxShiftEmpty1 0x40 +#define asyncLSRRxFifoError1 0x80 + + + +#if CONFIG_SERIAL_SOFTWARE_FIFO +/*-----------------------------------------------------------------------------+ + | Fifo + +-----------------------------------------------------------------------------*/ +typedef struct { + char *rx_buffer; + ulong rx_put; + ulong rx_get; +} serial_buffer_t; + +volatile static serial_buffer_t buf_info; +#endif + + +static int serial_div (int baudrate ) +{ + + switch (baudrate) { + case 1200: + return 96; + case 9600: + return 12; + case 19200: + return 6; + case 38400: + return 3; + case 57600: + return 2; + case 115200: + return 1; + } + hang (); +} + + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + + int bdiv = serial_div(gd->baudrate); + + + outb(0x80, UART0_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv, UART0_BASE + UART_DLL); /* set baudrate divisor */ + outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */ + outb(0x03, UART0_BASE + UART_LCR); /* clear DLAB; set 8 bits, no parity */ + outb(0x00, UART0_BASE + UART_FCR); /* disable FIFO */ + outb(0x00, UART0_BASE + UART_MCR); /* no modem control DTR RTS */ + val = inb(UART0_BASE + UART_LSR); /* clear line status */ + val = inb(UART0_BASE + UART_RBR); /* read receive buffer */ + outb(0x00, UART0_BASE + UART_SCR); /* set scratchpad */ + outb(0x00, UART0_BASE + UART_IER); /* set interrupt enable reg */ + + return (0); +} + + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned short bdiv; + + bdiv = serial_div (gd->baudrate); + + outb(0x80, UART0_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv&0xff, UART0_BASE + UART_DLL); /* set baudrate divisor */ + outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */ + outb(0x03, UART0_BASE + UART_LCR); /* clear DLAB; set 8 bits, no parity */ +} + + +void serial_putc (const char c) +{ + int i; + + if (c == '\n') + serial_putc ('\r'); + + /* check THRE bit, wait for transmiter available */ + for (i = 1; i < 3500; i++) { + if ((inb (UART0_BASE + UART_LSR) & 0x20) == 0x20) + break; + udelay (100); + } + outb(c, UART0_BASE + UART_THR); /* put character out */ +} + + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + + +int serial_getc () +{ + unsigned char status = 0; + + while (1) { +#if defined(CONFIG_HW_WATCHDOG) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ +#endif /* CONFIG_HW_WATCHDOG */ + status = inb (UART0_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR); + } + } + return (0x000000ff & (int) inb (UART0_BASE)); +} + + +int serial_tstc () +{ + unsigned char status; + + status = inb (UART0_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + return (1); + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR); + } + return 0; +} + + +#if CONFIG_SERIAL_SOFTWARE_FIFO + +void serial_isr (void *arg) +{ + int space; + int c; + const int rx_get = buf_info.rx_get; + int rx_put = buf_info.rx_put; + + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + while (serial_tstc ()) { + c = serial_getc (); + if (space) { + buf_info.rx_buffer[rx_put++] = c; + space--; + } + if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_put = 0; + if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) { + /* Stop flow by setting RTS inactive */ + outb(inb (UART0_BASE + UART_MCR) & (0xFF ^ 0x02), + UART0_BASE + UART_MCR); + } + } + buf_info.rx_put = rx_put; +} + +void serial_buffered_init (void) +{ + serial_puts ("Switching to interrupt driven serial input mode.\n"); + buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO); + buf_info.rx_put = 0; + buf_info.rx_get = 0; + + if (inb (UART0_BASE + UART_MSR) & 0x10) { + serial_puts ("Check CTS signal present on serial port: OK.\n"); + } else { + serial_puts ("WARNING: CTS signal not present on serial port.\n"); + } + + irq_install_handler ( VECNUM_U0 /*UART0 *//*int vec */ , + serial_isr /*interrupt_handler_t *handler */ , + (void *) &buf_info /*void *arg */ ); + + /* Enable "RX Data Available" Interrupt on UART */ + /* outb(inb(UART0_BASE + UART_IER) |0x01, UART0_BASE + UART_IER); */ + outb(0x01, UART0_BASE + UART_IER); + /* Set DTR active */ + outb(inb (UART0_BASE + UART_MCR) | 0x01, UART0_BASE + UART_MCR); + /* Start flow by setting RTS active */ + outb(inb (UART0_BASE + UART_MCR) | 0x02, UART0_BASE + UART_MCR); + /* Setup UART FIFO: RX trigger level: 4 byte, Enable FIFO */ + outb((1 << 6) | 1, UART0_BASE + UART_FCR); +} + +void serial_buffered_putc (const char c) +{ + /* Wait for CTS */ +#if defined(CONFIG_HW_WATCHDOG) + while (!(inb (UART0_BASE + UART_MSR) & 0x10)) + WATCHDOG_RESET (); +#else + while (!(inb (UART0_BASE + UART_MSR) & 0x10)); +#endif + serial_putc (c); +} + +void serial_buffered_puts (const char *s) +{ + serial_puts (s); +} + +int serial_buffered_getc (void) +{ + int space; + int c; + int rx_get = buf_info.rx_get; + int rx_put; + +#if defined(CONFIG_HW_WATCHDOG) + while (rx_get == buf_info.rx_put) + WATCHDOG_RESET (); +#else + while (rx_get == buf_info.rx_put); +#endif + c = buf_info.rx_buffer[rx_get++]; + if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_get = 0; + buf_info.rx_get = rx_get; + + rx_put = buf_info.rx_put; + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) { + /* Start flow by setting RTS active */ + outb(inb (UART0_BASE + UART_MCR) | 0x02, UART0_BASE + UART_MCR); + } + + return c; +} + +int serial_buffered_tstc (void) +{ + return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0; +} + +#endif /* CONFIG_SERIAL_SOFTWARE_FIFO */ + + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +/* + AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port + number 0 or number 1 + - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 : + configuration has been already done + - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 : + configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE +*/ +#if (CONFIG_KGDB_SER_INDEX & 2) +void kgdb_serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + bdiv = serial_div (CONFIG_KGDB_BAUDRATE); + + /* + * Init onboard 16550 UART + */ + outb(0x80, UART1_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv & 0xff), UART1_BASE + UART_DLL); /* set divisor for 9600 baud */ + outb(bdiv >> 8), UART1_BASE + UART_DLM); /* set divisor for 9600 baud */ + outb(0x03, UART1_BASE + UART_LCR); /* line control 8 bits no parity */ + outb(0x00, UART1_BASE + UART_FCR); /* disable FIFO */ + outb(0x00, UART1_BASE + UART_MCR); /* no modem control DTR RTS */ + val = inb(UART1_BASE + UART_LSR); /* clear line status */ + val = inb(UART1_BASE + UART_RBR); /* read receive buffer */ + outb(0x00, UART1_BASE + UART_SCR); /* set scratchpad */ + outb(0x00, UART1_BASE + UART_IER); /* set interrupt enable reg */ +} + + +void putDebugChar (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + outb(c, UART1_BASE + UART_THR); /* put character out */ + + /* check THRE bit, wait for transfer done */ + while ((inb(UART1_BASE + UART_LSR) & 0x20) != 0x20); +} + + +void putDebugStr (const char *s) +{ + while (*s) { + serial_putc(*s++); + } +} + + +int getDebugChar (void) +{ + unsigned char status = 0; + + while (1) { + status = inb(UART1_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART1_BASE + UART_LSR); + } + } + return (0x000000ff & (int) inb(UART1_BASE)); +} + + +void kgdb_interruptible (int yes) +{ + return; +} + +#else /* ! (CONFIG_KGDB_SER_INDEX & 2) */ + +void kgdb_serial_init (void) +{ + serial_printf ("[on serial] "); +} + +void putDebugChar (int c) +{ + serial_putc (c); +} + +void putDebugStr (const char *str) +{ + serial_puts (str); +} + +int getDebugChar (void) +{ + return serial_getc (); +} + +void kgdb_interruptible (int yes) +{ + return; +} +#endif /* (CONFIG_KGDB_SER_INDEX & 2) */ +#endif /* CFG_CMD_KGDB */ + diff --git a/cpu/i386/start.S b/cpu/i386/start.S new file mode 100644 index 00000000000..025555c0a14 --- /dev/null +++ b/cpu/i386/start.S @@ -0,0 +1,198 @@ +/* + * U-boot - i386 Startup Code + * + * Copyright (c) 2002 Omicron Ceti AB, Daniel Engström <denaiel@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 <config.h> +#include <version.h> + + +.section .text +.code32 +.globl _start +.type _start, @function +.globl _i386boot_start +_i386boot_start: +_start: + movl $0x18,%eax /* Load our segement registes, the + * gdt have already been loaded by start16.S */ + movw %ax,%fs + movw %ax,%ds + movw %ax,%gs + movw %ax,%es + movw %ax,%ss + + /* We call a few functions in the board support package + * since we have no stack yet we'll have to use %ebp + * to store the return address */ + + /* Early platform init (setup gpio, etc ) */ + mov $early_board_init_ret, %ebp + jmp early_board_init +early_board_init_ret: + + /* The __port80 entry-point should be usabe by now */ + /* so we try to indicate progress */ + movw $0x01, %ax + movl $.progress0, %ebp + jmp __show_boot_progress +.progress0: + + /* size memory */ + mov $mem_init_ret, %ebp + jmp mem_init +mem_init_ret: + + /* check ammount of configured memory + * (we need atleast bss start+bss size+stack size) */ + movl $_i386boot_bss_start, %ecx /* BSS start */ + addl $_i386boot_bss_size, %ecx /* BSS size */ + addl $CFG_STACK_SIZE, %ecx + cmpl %ecx, %eax + jae mem_ok + + /* indicate (lack of) progress */ + movw $0x81, %ax + movl $.progress0a, %ebp + jmp __show_boot_progress +.progress0a: + jmp die +mem_ok: + + /* indicate progress */ + movw $0x02, %ax + movl $.progress1, %ebp + jmp __show_boot_progress +.progress1: + + /* create a stack after the bss */ + movl $_i386boot_bss_start, %eax + addl $_i386boot_bss_size, %eax + addl $CFG_STACK_SIZE, %eax + movl %eax, %esp + + pushl $0 + popl %eax + cmpl $0, %eax + jne no_stack + push $0x55aa55aa + popl %ebx + cmpl $0x55aa55aa, %ebx + je stack_ok + +no_stack: + /* indicate (lack of) progress */ + movw $0x82, %ax + movl $.progress1a, %ebp + jmp __show_boot_progress +.progress1a: + jmp die + + +stack_ok: + /* indicate progress */ + movw $0x03, %ax + movl $.progress2, %ebp + jmp __show_boot_progress +.progress2: + + /* copy data section to ram, size must be 4-byte aligned */ + movl $_i386boot_romdata_dest, %edi /* destination address */ + movl $_i386boot_romdata_start, %esi /* source address */ + movl $_i386boot_romdata_size, %ecx /* number of bytes to copy */ + movl %ecx, %eax + andl $3, %eax + jnz data_fail + + shrl $2, %ecx /* copy 4 byte each time */ + cld + cmpl $0, %ecx + je data_ok +data_segment: + movsl + loop data_segment + jmp data_ok +data_fail: + /* indicate (lack of) progress */ + movw $0x83, %ax + movl $.progress2a, %ebp + jmp __show_boot_progress +.progress2a: + jmp die + +data_ok: + + /* indicate progress */ + movw $0x04, %ax + movl $.progress3, %ebp + jmp __show_boot_progress +.progress3: + + /* clear bss section in ram, size must be 4-byte aligned */ + movl $_i386boot_bss_start, %eax /* BSS start */ + movl $_i386boot_bss_size, %ecx /* BSS size */ + movl %ecx, %eax + andl $3, %eax + jnz bss_fail + shrl $2, %ecx /* clear 4 byte each time */ + cld + cmpl $0, %ecx + je bss_ok +bss: + movl $0, (%edi) + add $4, %edi + loop bss + jmp bss_ok + +bss_fail: + /* indicate (lack of) progress */ + movw $0x84, %ax + movl $.progress3a, %ebp + jmp __show_boot_progress +.progress3a: + jmp die + +bss_ok: + + wbinvd + + + /* indicate progress */ + movw $0x05, %ax + movl $.progress4, %ebp + jmp __show_boot_progress +.progress4: + + call start_i386boot /* Enter, U-boot! */ + + /* indicate (lack of) progress */ + movw $0x85, %ax + movl $.progress4a, %ebp + jmp __show_boot_progress +.progress4a: + +die: hlt + jmp die + hlt + + diff --git a/cpu/i386/start16.S b/cpu/i386/start16.S new file mode 100644 index 00000000000..590a5a6c9d7 --- /dev/null +++ b/cpu/i386/start16.S @@ -0,0 +1,112 @@ +/* + * U-boot - i386 Startup Code + * + * Copyright (c) 2002 Omicron Ceti AB, Daniel Engström <denaiel@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 + */ + + +#define BOOT_SEG 0xffff0000 /* linear segment of boot code */ +#define a32 .byte 0x67; +#define o32 .byte 0x66; + +.section .start16, "ax" +.code16 +.globl start16 +start16: + /* First we let the BSP do some early initialization + * this code have to map the flash to its final position + */ + mov $board_init16_ret, %bp + jmp board_init16 +board_init16_ret: + + /* Turn of cache (this might require a 486-class CPU) */ + movl %cr0, %eax + orl $060000000,%eax + movl %eax, %cr0 + wbinvd + + /* load the descriptor tables */ +o32 cs lidt idt_ptr +o32 cs lgdt gdt_ptr + + + /* Now, we enter protected mode */ + movl %cr0, %eax + orl $1,%eax + movl %eax, %cr0 + + /* Flush the prefetch queue */ + jmp ff +ff: + + /* Finally jump to the 32bit initialization code */ + movw $code32start, %ax + movw %ax,%bp +o32 cs ljmp *(%bp) + + /* 48-bit far pointer */ +code32start: + .long _start /* offset */ + .word 0x10 /* segment */ + +idt_ptr: + .word 0 /* limit */ + .long 0 /* base */ + +gdt_ptr: + .word 0x30 /* limit (48 bytes = 6 GDT entries) */ + .long BOOT_SEG + gdt /* base */ + + /* The GDT table ... + * + * Selector Type + * 0x00 NULL + * 0x08 Unused + * 0x10 32bit code + * 0x18 32bit data/stack + * 0x20 16bit code + * 0x28 16bit data/stack + */ + +gdt: + .word 0, 0, 0, 0 /* NULL */ + .word 0, 0, 0, 0 /* unused */ + + .word 0xFFFF /* 4Gb - (0x100000*0x1000 = 4Gb) */ + .word 0 /* base address = 0 */ + .word 0x9B00 /* code read/exec */ + .word 0x00CF /* granularity = 4096, 386 (+5th nibble of limit) */ + + .word 0xFFFF /* 4Gb - (0x100000*0x1000 = 4Gb) */ + .word 0x0 /* base address = 0 */ + .word 0x9300 /* data read/write */ + .word 0x00CF /* granularity = 4096, 386 (+5th nibble of limit) */ + + .word 0xFFFF /* 64kb */ + .word 0 /* base address = 0 */ + .word 0x9b00 /* data read/write */ + .word 0x0010 /* granularity = 1 (+5th nibble of limit) */ + + .word 0xFFFF /* 64kb */ + .word 0 /* base address = 0 */ + .word 0x9300 /* data read/write */ + .word 0x0010 /* granularity = 1 (+5th nibble of limit) */ diff --git a/cpu/i386/timer.c b/cpu/i386/timer.c new file mode 100644 index 00000000000..a23cd6e48bf --- /dev/null +++ b/cpu/i386/timer.c @@ -0,0 +1,211 @@ +/* + * (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 |