From af142756744156e7ffd1e7ac64efb8a895096aec Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 4 May 2001 19:51:18 +0000 Subject: Alchemy support. Done by Pete and further hacked up a bit by me ... --- arch/mips/Makefile | 9 + arch/mips/au1000/common/Makefile | 31 + arch/mips/au1000/common/dbg_io.c | 118 ++ arch/mips/au1000/common/int-handler.S | 70 + arch/mips/au1000/common/irq.c | 642 +++++++ arch/mips/au1000/common/prom.c | 121 ++ arch/mips/au1000/common/puts.c | 144 ++ arch/mips/au1000/common/reset.c | 60 + arch/mips/au1000/common/serial.c | 3100 +++++++++++++++++++++++++++++++++ arch/mips/au1000/common/time.c | 286 +++ arch/mips/au1000/pb1000/Makefile | 24 + arch/mips/au1000/pb1000/init.c | 59 + arch/mips/au1000/pb1000/setup.c | 117 ++ arch/mips/config.in | 6 +- arch/mips/defconfig | 1 + arch/mips/defconfig-atlas | 1 + arch/mips/defconfig-ddb5476 | 1 + arch/mips/defconfig-decstation | 1 + arch/mips/defconfig-ev64120 | 1 + arch/mips/defconfig-ev96100 | 1 + arch/mips/defconfig-ip22 | 1 + arch/mips/defconfig-it8172 | 1 + arch/mips/defconfig-malta | 1 + arch/mips/defconfig-nino | 2 + arch/mips/defconfig-ocelot | 1 + arch/mips/defconfig-pb1000 | 445 +++++ arch/mips/defconfig-rm200 | 1 + arch/mips/kernel/proc.c | 7 +- arch/mips/kernel/setup.c | 29 + drivers/char/Config.in | 4 + drivers/char/tty_io.c | 3 + drivers/net/Config.in | 3 + drivers/net/Makefile | 1 + drivers/net/au1000_eth.c | 1266 ++++++++++++++ drivers/net/au1000_eth.h | 223 +++ include/asm-mips/au1000.h | 635 +++++++ include/asm-mips/bootinfo.h | 15 +- include/asm-mips/cpu.h | 2 + include/asm-mips/serial.h | 18 +- 39 files changed, 7445 insertions(+), 6 deletions(-) create mode 100644 arch/mips/au1000/common/Makefile create mode 100644 arch/mips/au1000/common/dbg_io.c create mode 100644 arch/mips/au1000/common/int-handler.S create mode 100644 arch/mips/au1000/common/irq.c create mode 100644 arch/mips/au1000/common/prom.c create mode 100644 arch/mips/au1000/common/puts.c create mode 100644 arch/mips/au1000/common/reset.c create mode 100644 arch/mips/au1000/common/serial.c create mode 100644 arch/mips/au1000/common/time.c create mode 100644 arch/mips/au1000/pb1000/Makefile create mode 100644 arch/mips/au1000/pb1000/init.c create mode 100644 arch/mips/au1000/pb1000/setup.c create mode 100644 arch/mips/defconfig-pb1000 create mode 100644 drivers/net/au1000_eth.c create mode 100644 drivers/net/au1000_eth.h create mode 100644 include/asm-mips/au1000.h diff --git a/arch/mips/Makefile b/arch/mips/Makefile index cbbeca454..7c0a63e6c 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -232,6 +232,15 @@ SUBDIRS += arch/mips/ite-boards/generic arch/mips/ite-boards/ivr LOADADDR += 0x80100000 endif +# +# Au1000 eval board +# +ifdef CONFIG_MIPS_PB1000 +LIBS += arch/mips/au1000/pb1000/pb1000.o arch/mips/au1000/common/au1000.o +SUBDIRS += arch/mips/au1000/pb1000 arch/mips/au1000/common +LOADADDR += 0x80100000 +endif + # # Choosing incompatible machines durings configuration will result in # error messages during linking. Select a default linkscript if diff --git a/arch/mips/au1000/common/Makefile b/arch/mips/au1000/common/Makefile new file mode 100644 index 000000000..5d8c60a93 --- /dev/null +++ b/arch/mips/au1000/common/Makefile @@ -0,0 +1,31 @@ +# +# Copyright 2000 MontaVista Software Inc. +# Author: MontaVista Software, Inc. +# ppopov@mvista.com or support@mvista.com +# +# Makefile for the Alchemy Au1000 CPU, generic files. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: au1000.o + +O_TARGET := au1000.o + +obj-y := prom.o dbg_io.o int-handler.o irq.o puts.o time.o reset.o + +obj-$(CONFIG_AU1000_UART) += serial.o +obj-$(CONFIG_REMOTE_DEBUG) += dbg_io.o +obj-$(CONFIG_BLK_DEV_INITRD) += ramdisk.o + +ramdisk.o: + mkramobj ramdisk ramdisk.o + +include $(TOPDIR)/Rules.make diff --git a/arch/mips/au1000/common/dbg_io.c b/arch/mips/au1000/common/dbg_io.c new file mode 100644 index 000000000..4940db83c --- /dev/null +++ b/arch/mips/au1000/common/dbg_io.c @@ -0,0 +1,118 @@ + +#include +#include +#include + +#ifdef CONFIG_REMOTE_DEBUG + +/* + * FIXME the user should be able to select the + * uart to be used for debugging. + */ +#define DEBUG_BASE UART2_ADDR +/**/ + +/* we need uint32 uint8 */ +/* #include "types.h" */ +typedef unsigned char uint8; +typedef unsigned int uint32; + +#define UART16550_BAUD_2400 2400 +#define UART16550_BAUD_4800 4800 +#define UART16550_BAUD_9600 9600 +#define UART16550_BAUD_19200 19200 +#define UART16550_BAUD_38400 38400 +#define UART16550_BAUD_57600 57600 +#define UART16550_BAUD_115200 115200 + +#define UART16550_PARITY_NONE 0 +#define UART16550_PARITY_ODD 0x08 +#define UART16550_PARITY_EVEN 0x18 +#define UART16550_PARITY_MARK 0x28 +#define UART16550_PARITY_SPACE 0x38 + +#define UART16550_DATA_5BIT 0x0 +#define UART16550_DATA_6BIT 0x1 +#define UART16550_DATA_7BIT 0x2 +#define UART16550_DATA_8BIT 0x3 + +#define UART16550_STOP_1BIT 0x0 +#define UART16550_STOP_2BIT 0x4 + + +#define UART_RX 0 /* Receive buffer */ +#define UART_TX 4 /* Transmit buffer */ +#define UART_IER 8 /* Interrupt Enable Register */ +#define UART_IIR 0xC /* Interrupt ID Register */ +#define UART_FCR 0x10 /* FIFO Control Register */ +#define UART_LCR 0x14 /* Line Control Register */ +#define UART_MCR 0x18 /* Modem Control Register */ +#define UART_LSR 0x1C /* Line Status Register */ +#define UART_MSR 0x20 /* Modem Status Register */ +#define UART_CLK 0x28 /* Baud Rat4e Clock Divider */ +#define UART_MOD_CNTRL 0x100 /* Module Control */ + +/* memory-mapped read/write of the port */ +#define UART16550_READ(y) (inl(DEBUG_BASE + y) & 0xff) +#define UART16550_WRITE(y,z) (outl(z&0xff, DEBUG_BASE + y)) + +void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) +{ + + if (UART16550_READ(UART_MOD_CNTRL) != 0x3) { + UART16550_WRITE(UART_MOD_CNTRL, 3); + } + cal_r4koff(); + + /* disable interrupts */ + UART16550_WRITE(UART_IER, 0); + + /* set up baud rate */ + { + uint32 divisor; + + /* set divisor */ + divisor = get_au1000_uart_baud() / baud; + UART16550_WRITE(UART_CLK, divisor & 0xffff); + } + + /* set data format */ + UART16550_WRITE(UART_LCR, data | parity | stop); +} + +static int remoteDebugInitialized = 0; + +uint8 getDebugChar(void) +{ + if (!remoteDebugInitialized) { + remoteDebugInitialized = 1; + debugInit(UART16550_BAUD_115200, + UART16550_DATA_8BIT, + UART16550_PARITY_NONE, + UART16550_STOP_1BIT); + } + + while((UART16550_READ(UART_LSR) & 0x1) == 0); + return UART16550_READ(UART_RX); +} + + +int putDebugChar(uint8 byte) +{ + int i; + if (!remoteDebugInitialized) { + remoteDebugInitialized = 1; + debugInit(UART16550_BAUD_115200, + UART16550_DATA_8BIT, + UART16550_PARITY_NONE, + UART16550_STOP_1BIT); + } + + while ((UART16550_READ(UART_LSR)&0x40) == 0); + UART16550_WRITE(UART_TX, byte); + //for (i=0;i<0xfff;i++); + + return 1; +} + +#endif diff --git a/arch/mips/au1000/common/int-handler.S b/arch/mips/au1000/common/int-handler.S new file mode 100644 index 000000000..c7c933036 --- /dev/null +++ b/arch/mips/au1000/common/int-handler.S @@ -0,0 +1,70 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: ppopov@mvista.com + * + * Interrupt dispatcher for Au1000 boards. + * + * 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. + */ +#include +#include +#include +#include +#include +#include + + .text + .set macro + .set noat + .align 5 + +NESTED(au1000_IRQ, PT_SIZE, sp) + SAVE_ALL + CLI # Important: mark KERNEL mode ! + + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS # get enabled interrupts + and t0,t1 # isolate allowed ones + + andi t0,0xff00 # isolate pending bits + beqz t0, 3f # spurious interrupt + + andi a0, t0, CAUSEF_IP7 + beq a0, zero, 1f + move a0, sp + jal mips_timer_interrupt + j ret_from_irq + +1: + andi a0, t0, CAUSEF_IP2 # Interrupt Controller 0, Request 0 +/* beq a0, zero, 2f */ + move a0,sp + jal intc0_req0_irqdispatch + j done +2: + andi a0, t0, CAUSEF_IP3 # Interrupt Controller 0, Request 1 + beq a0, zero, 3f + move a0,sp + jal intc0_req1_irqdispatch + j done +3: + andi a0, t0, CAUSEF_IP4 # Interrupt Controller 1, Request 0 + beq a0, zero, 4f + move a0,sp + jal intc1_req1_irqdispatch + j done +4: + andi a0, t0, CAUSEF_IP5 # Interrupt Controller 1, Request 1 + beq a0, zero, 5f + move a0, sp + jal intc1_req1_irqdispatch + +5: + move a0, sp + jal mips_spurious_interrupt +done: + j ret_from_irq +END(au1000_IRQ) diff --git a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c new file mode 100644 index 000000000..7dececa07 --- /dev/null +++ b/arch/mips/au1000/common/irq.c @@ -0,0 +1,642 @@ +/* + * BRIEF MODULE DESCRIPTION + * Au1000 interrupt routines. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + +#undef DEBUG_IRQ +#ifdef DEBUG_IRQ +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define EXT_INTC0_REQ0 2 /* IP 2 */ +#define EXT_INTC0_REQ1 3 /* IP 3 */ +#define EXT_INTC1_REQ0 4 /* IP 4 */ +#define EXT_INTC1_REQ1 5 /* IP 5 */ +#define MIPS_TIMER_IP 7 /* IP 7 */ + +#ifdef CONFIG_REMOTE_DEBUG +extern void breakpoint(void); +#endif + +extern asmlinkage void au1000_IRQ(void); +extern void set_debug_traps(void); +irq_cpustat_t irq_stat [NR_CPUS]; +unsigned int local_bh_count[NR_CPUS]; +unsigned int local_irq_count[NR_CPUS]; +unsigned long spurious_count = 0; +irq_desc_t irq_desc[NR_IRQS]; + +static void setup_au1000_irq(unsigned int irq, int type, int int_req); +static unsigned int startup_au1000_irq(unsigned int irq); +static void enable_au1000_irq(unsigned int irq_nr); +static void disable_au1000_irq(unsigned int irq_nr); +static void end_au1000_irq(unsigned int irq_nr); + +static void ack_level_irq(unsigned int irq_nr); +static void ack_rise_edge_irq(unsigned int irq_nr); +static void ack_fall_edge_irq(unsigned int irq_nr); + +void disable_ack_irq(int irq); + +/* Function for careful CP0 interrupt mask access */ +static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask) +{ + unsigned long status = read_32bit_cp0_register(CP0_STATUS); + status &= ~((clr_mask & 0xFF) << 8); + status |= (set_mask & 0xFF) << 8; + write_32bit_cp0_register(CP0_STATUS, status); +} + +static inline void mask_cpu_irq_input(unsigned int irq_nr) +{ + modify_cp0_intmask(irq_nr, 0); +} + +static inline void unmask_cpu_irq_input(unsigned int irq_nr) +{ + modify_cp0_intmask(0, irq_nr); +} + +static void disable_cpu_irq_input(unsigned int irq_nr) +{ + unsigned long flags; + + save_and_cli(flags); + mask_cpu_irq_input(irq_nr); + restore_flags(flags); +} + +static void enable_cpu_irq_input(unsigned int irq_nr) +{ + unsigned long flags; + + save_and_cli(flags); + unmask_cpu_irq_input(irq_nr); + restore_flags(flags); +} + + +static void setup_au1000_irq(unsigned int irq_nr, int type, int int_req) +{ + /* Config2[n], Config1[n], Config0[n] */ + if (irq_nr > AU1000_LAST_INTC0_INT) { + switch (type) { + case INTC_INT_RISE_EDGE: /* 0:0:1 */ + outl(1< AU1000_LAST_INTC0_INT) { + outl(1< AU1000_LAST_INTC0_INT) { + outl(1< AU1000_LAST_INTC0_INT) { + outl(1< AU1000_LAST_INTC0_INT) { + outl(1< AU1000_LAST_INTC0_INT) { + outl(1< AU1000_LAST_INTC0_INT) { + outl(1<handler ) + continue; + len += sprintf(buf+len, "%3d: ", i); + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); + if ( irq_desc[i].handler ) + len += sprintf(buf+len, " %s ", irq_desc[i].handler->typename ); + else + len += sprintf(buf+len, " None "); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); + } + len += sprintf(buf+len, "\n"); + } + len += sprintf(buf+len, "BAD: %10lu\n", spurious_count); + return len; +} + +asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +{ + struct irqaction *action; + irq_desc_t *desc = irq_desc + irq; + int cpu; + + cpu = smp_processor_id(); + irq_enter(cpu, irq); + + kstat.irqs[cpu][irq]++; + desc->handler->ack(irq); + + action = irq_desc[irq].action; + + if (action && action->handler) + { + //desc->handler->disable(irq); + //if (!(action->flags & SA_INTERRUPT)) __sti(); /* reenable ints */ + do { + action->handler(irq, action->dev_id, regs); + action = action->next; + } while ( action ); + //__cli(); /* disable ints */ + //desc->handler->ack(irq); + //desc->handler->enable(irq); + } + else + { + spurious_count++; + printk("Unhandled interrupt %d, cause %x, disabled\n", + (unsigned)irq, (unsigned)regs->cp0_cause); + desc->handler->disable(irq); + } + irq_exit(cpu, irq); +#if 0 + if (softirq_active(cpu) & softirq_mask(cpu)) + do_softirq(); +#endif +} + +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + struct irqaction *old, **p, *action; + unsigned long flags, shared=0; + irq_desc_t *desc = irq_desc + irq; + unsigned long cp0_status; + + cp0_status = read_32bit_cp0_register(CP0_STATUS); + + if (irq >= NR_IRQS) + return -EINVAL; + + if (!handler) + { + /* Free */ + for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + desc->handler->disable(irq); + desc->handler->ack(irq); + restore_flags(flags); + kfree(action); + return 0; + } + return -ENOENT; + } + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + memset(action, 0, sizeof(struct irqaction)); + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + + p = &irq_desc[irq].action; + + spin_lock_irqsave(&desc->lock,flags); + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & action->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + *p = action; + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + desc->handler->startup(irq); + desc->handler->ack(irq); + desc->handler->enable(irq); + } + else { + printk("irq %d is shared\n", irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + return 0; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + request_irq(irq, NULL, 0, NULL, dev_id); +} + +void enable_cpu_timer(void) +{ + enable_cpu_irq_input(1<handler->disable(irq); + desc->handler->ack(irq); +} + +void mips_spurious_interrupt(struct pt_regs *regs) +{ +#if 0 + return; +#else + unsigned cause; + + cause = read_32bit_cp0_register(CP0_CAUSE); + printk("spurious int (epc %x) (cause %x) (badvaddr %x)\n", + (unsigned)regs->cp0_epc, cause, (unsigned)regs->cp0_badvaddr); +#endif +} + +void intc0_req0_irqdispatch(struct pt_regs *regs) +{ + int irq = 0; + unsigned long int_request; + + int_request = inl(INTC0_REQ0_INT); + + if (!int_request) return; + + for (;;) { + if (!(int_request & 0x1)) { + irq++; + int_request >>= 1; + } + else break; + } + do_IRQ(irq, regs); +} + +void intc0_req1_irqdispatch(struct pt_regs *regs) +{ + int irq = 0; + unsigned long int_request; + + int_request = inl(INTC0_REQ1_INT); + + if (!int_request) return; + + for (;;) { + if (!(int_request & 0x1)) { + irq++; + int_request >>= 1; + } + else break; + } + do_IRQ(irq, regs); +} + +void intc1_req0_irqdispatch(struct pt_regs *regs) +{ + int irq = 0; + unsigned long int_request; + + int_request = inl(INTC1_REQ0_INT); + + if (!int_request) return; + + for (;;) { + if (!(int_request & 0x1)) { + irq++; + int_request >>= 1; + } + else break; + } + do_IRQ(irq, regs); +} + +void intc1_req1_irqdispatch(struct pt_regs *regs) +{ + int irq = 0; + unsigned long int_request; + + int_request = inl(INTC1_REQ1_INT); + + if (!int_request) return; + + for (;;) { + if (!(int_request & 0x1)) { + irq++; + int_request >>= 1; + } + else break; + } + do_IRQ(irq, regs); +} + +void show_pending_irqs(void) +{ +} diff --git a/arch/mips/au1000/common/prom.c b/arch/mips/au1000/common/prom.c new file mode 100644 index 000000000..1c64aa1c4 --- /dev/null +++ b/arch/mips/au1000/common/prom.c @@ -0,0 +1,121 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * PROM library initialisation code, assuming a version of + * pmon is the boot code. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or support@mvista.com + * + * This file was derived from Carsten Langgaard's + * arch/mips/mips-boards/xx files. + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include + +/* #define DEBUG_CMDLINE */ + +char arcs_cmdline[COMMAND_LINE_SIZE]; +int prom_argc; +char **prom_argv, **prom_envp; + +typedef struct +{ + char *name; +/* char *val; */ +}t_env_var; + + +char * __init prom_getcmdline(void) +{ + return &(arcs_cmdline[0]); +} + +void __init prom_init_cmdline(void) +{ + char *cp; + int actr; + + actr = 1; /* Always ignore argv[0] */ + + cp = &(arcs_cmdline[0]); + while(actr < prom_argc) { + strcpy(cp, prom_argv[actr]); + cp += strlen(prom_argv[actr]); + *cp++ = ' '; + actr++; + } + if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ + --cp; + *cp = '\0'; + +} + + +char *prom_getenv(char *envname) +{ + /* + * Return a pointer to the given environment variable. + * Environment variables are stored in the form of "memsize=64". + */ + + t_env_var *env = (t_env_var *)prom_envp; + int i; + + i = strlen(envname); + + while(env->name) { + if(strncmp(envname, env->name, i) == 0) { + return(env->name + strlen(envname) + 1); + } + env++; + } + return(NULL); +} + +static inline unsigned char str2hexnum(unsigned char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'a' && c <= 'f') + return c - 'a' + 10; + return 0; /* foo */ +} + +int __init page_is_ram(unsigned long pagenr) +{ + return 1; +} + +void prom_free_prom_memory (void) +{ +} diff --git a/arch/mips/au1000/common/puts.c b/arch/mips/au1000/common/puts.c new file mode 100644 index 000000000..ab0a966f4 --- /dev/null +++ b/arch/mips/au1000/common/puts.c @@ -0,0 +1,144 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Low level uart routines to directly access a 16550 uart. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or support@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#define SERIAL_BASE 0xB1100000 /* au1000, uart 0 */ +#define SER_CMD 0x7 +#define SER_DATA 0x1 +#define TX_BUSY 0x20 + +#define TIMEOUT 0xffffff +#define SLOW_DOWN + +static const char digits[16] = "0123456789abcdef"; +static volatile unsigned long * const com1 = (unsigned char *)SERIAL_BASE; + + +#ifdef SLOW_DOWN +static inline void slow_down() +{ + int k; + for (k=0; k<10000; k++); +} +#else +#define slow_down() +#endif + +void +putch(const unsigned char c) +{ + unsigned char ch; + int i = 0; + + do { + ch = com1[SER_CMD]; + slow_down(); + i++; + if (i>TIMEOUT) { + break; + } + } while (0 == (ch & TX_BUSY)); + com1[SER_DATA] = c; +} + +void +puts(unsigned char *cp) +{ + unsigned char ch; + int i = 0; + + while (*cp) { + do { + ch = com1[SER_CMD]; + slow_down(); + i++; + if (i>TIMEOUT) { + break; + } + } while (0 == (ch & TX_BUSY)); + com1[SER_DATA] = *cp++; + } + putch('\r'); + putch('\n'); +} + +void +fputs(unsigned char *cp) +{ + unsigned char ch; + int i = 0; + + while (*cp) { + + do { + ch = com1[SER_CMD]; + slow_down(); + i++; + if (i>TIMEOUT) { + break; + } + } while (0 == (ch & TX_BUSY)); + com1[SER_DATA] = *cp++; + } +} + + +void +put64(uint64_t ul) +{ + int cnt; + unsigned ch; + + cnt = 16; /* 16 nibbles in a 64 bit long */ + putch('0'); + putch('x'); + do { + cnt--; + ch = (unsigned char)(ul >> cnt * 4) & 0x0F; + putch(digits[ch]); + } while (cnt > 0); +} + +void +put32(unsigned u) +{ + int cnt; + unsigned ch; + + cnt = 8; /* 8 nibbles in a 32 bit long */ + putch('0'); + putch('x'); + do { + cnt--; + ch = (unsigned char)(u >> cnt * 4) & 0x0F; + putch(digits[ch]); + } while (cnt > 0); +} diff --git a/arch/mips/au1000/common/reset.c b/arch/mips/au1000/common/reset.c new file mode 100644 index 000000000..09e89388e --- /dev/null +++ b/arch/mips/au1000/common/reset.c @@ -0,0 +1,60 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Au1000 reset routines. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +void au1000_restart(char *command) +{ + set_cp0_status(ST0_BEV | ST0_ERL); + set_cp0_config(CONF_CM_UNCACHED); + flush_cache_all(); + write_32bit_cp0_register(CP0_WIRED, 0); + __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000)); +} + +void au1000_halt(void) +{ + printk(KERN_NOTICE "\n** You can safely turn off the power\n"); + while (1) + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0"); +} + +void au1000_power_off(void) +{ + au1000_halt(); +} diff --git a/arch/mips/au1000/common/serial.c b/arch/mips/au1000/common/serial.c new file mode 100644 index 000000000..6737aedff --- /dev/null +++ b/arch/mips/au1000/common/serial.c @@ -0,0 +1,3100 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Au1000 serial port driver. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * Derived almost entirely from drivers/char/serial.c: + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +static char *serial_version = "1.01"; +static char *serial_revdate = "2001-02-08"; + + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +#ifdef MODULE +#undef CONFIG_AU1000_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +/* + * End of serial driver configuration section. + */ + +#ifdef MODVERSIONS +#include +#endif +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AU1000_SERIAL_CONSOLE +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif + +#include +#include +#include +#include + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +extern unsigned long get_au1000_uart_baud(void); + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * IRQ_timeout - How long the timeout should be for each IRQ + * should be after the IRQ has been active. + */ + +static struct async_struct *IRQ_ports[NR_IRQS]; +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_AU1000_SERIAL_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { 0, 0} +}; + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in serial.h */ +}; + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) +#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) +#else +#define DBG_CNT(s) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + + +static inline int serial_paranoia_check(struct async_struct *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return (inl(info->port+offset) & 0xff); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, int value) +{ + outl(value & 0xff, info->port+offset); +} + + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + restore_flags(flags); +} + +static void rs_start(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_start")) + return; + + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + restore_flags(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void rs_sched_event(struct async_struct *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + int ignored = 0; + struct async_icount *icount; + + icount = &info->state->icount; + do { + ch = serial_inp(info, UART_RX); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ +#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (*status & info->ignore_status_mask) { + if (++ignored > 100) + break; + goto ignore_char; + } + *status &= info->read_status_mask; + +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + if (info->line == sercons.index) { + /* Recover the break flag from console xmit */ + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + } + } +#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + *status = serial_inp(info, UART_LSR); + } while (*status & UART_LSR_DR); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + + if (info->x_char) { + serial_outp(info, UART_TX, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + return; + } + + count = info->xmit_fifo_size; + do { + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} + + + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + int pass_counter = 0; + struct async_struct * info; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + + do { + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * This subroutine is called when the RS_TIMER goes off. It is used + * by the serial driver to handle ports that do not have an interrupt + * (irq=0). This doesn't work very well for 16450's, but gives barely + * passable results for a 16550A. (Although at the expense of much + * CPU overhead). + */ +static void rs_timer(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + +#if 0 + if (IRQ_ports[0]) { + save_flags(flags); cli(); + rs_interrupt_single(0, NULL, NULL); + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +#endif +} + +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + +/* + * This routine figures out the correct timeout for a particular IRQ. + * It uses the smallest timeout of all of the serial ports in a + * particular interrupt chain. Now only used for IRQ 0.... + */ +static void figure_IRQ_timeout(int irq) +{ + struct async_struct *info; + int timeout = 60*HZ; /* 60 seconds === a long time :-) */ + + info = IRQ_ports[irq]; + if (!info) { + IRQ_timeout[irq] = 60*HZ; + return; + } + while (info) { + if (info->timeout < timeout) + timeout = info->timeout; + info = info->next_port; + } + if (!irq) + timeout = timeout / 2; + IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; +} + + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + + + if (inl(UART_MOD_CNTRL + state->port) != 0x3) { + outl(3, UART_MOD_CNTRL + state->port); + } +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif + + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(info, UART_LSR); + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_MSR); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("LSR safety check engaged!\n"); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } + + /* + * Allocate the IRQ if necessary + */ +#if 0 + /* au1000, uart0 irq is 0 */ + if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { +#endif + if ((!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + retval = -EBUSY; + goto errout; + } else + handler = rs_interrupt_single; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + /* + * Insert serial port into IRQ chain. + */ + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + /* + * Now, initialize the UART + */ + serial_outp(info, UART_LCR, UART_LCR_WLEN8); + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ + serial_outp(info, UART_MCR, info->MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ + + + /* + * And clear the interrupt registers again for luck. + */ + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up serial timers... + */ + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + /* + * and set the speed of the serial port + */ + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct async_struct * info) +{ + unsigned long flags; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + /* + * Free the IRQ, if necessary + */ +// if (state->irq && (!IRQ_ports[state->irq] || + if ((!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + info->IER = 0; + serial_outp(info, UART_IER, 0x00); /* disable all intrs */ + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ + + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + /* disable FIFO's */ + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + + (void)serial_in(info, UART_RX); /* read data port to reset things */ + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct async_struct *info, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; + unsigned cflag, cval, fcr = 0; + int bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) { + baud = 9600; /* B0 transition handled in rs_set_termios */ + } + baud_base = info->state->baud_base; + //if (baud == 38400 && + if (((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) { + quot = info->state->custom_divisor; + } + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + /* If the quotient is zero refuse the change */ + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + } + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + /* Set up FIFO's */ + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIGGER_1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIGGER_8; + } + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; + save_flags(flags); cli(); + + serial_outp(info, UART_CLK, quot & 0xffff); + serial_outp(info, UART_LCR, cval); + info->LCR = cval; /* Save LCR */ + restore_flags(flags); +} + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + restore_flags(flags); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_send_xchar(tty, START_CHAR(tty)); + } + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + /* Make sure address is not already in use */ + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + /* + * We need to shutdown the serial port at the old + * port/irq combination. + */ + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + change_speed(info, 0); + } + } else { + retval = startup(info); + } + return retval; +} + + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + + +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} + +static int do_autoconfig(struct async_struct * info) +{ + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + restore_flags(flags); +} + + +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct icount; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + save_flags(flags); cli(); + /* note the counters on entry */ + cprev = info->state->icount; + restore_flags(flags); + /* Force modem status interrupts on */ + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + serial_out(info, UART_IER, info->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + set_current_state(TASK_RUNNING); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct async_struct *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; + int ret; + unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->quot = 0; + info->tty = 0; + } + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static char serial_options[] __initdata = + " no serial options enabled\n"; +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct serial_state * state) +{ + struct async_struct *info, scr_info; + unsigned long flags; + + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + if (inl(UART_MOD_CNTRL + state->port) != 0x3) { + outl(3, UART_MOD_CNTRL + state->port); + } + + state->type = PORT_16550; + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + + save_flags(flags); cli(); + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (info->port) { + request_region(info->port,8,"serial(auto)"); + } + + /* + * Reset the UART. + */ + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + (void)serial_in(info, UART_RX); + serial_outp(info, UART_IER, 0); + + restore_flags(flags); +} + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; + } +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + /* + * The interrupt of the serial console port + * can't be shared. + */ + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.put_char = rs_put_char; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->baud_base = get_au1000_uart_baud(); + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) { + continue; + } + + if (state->flags & ASYNC_BOOT_AUTOCONF) { + autoconfig(state); + } + } + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) { + continue; + } + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } + return 0; +} + +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ + +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } + if (i == NR_PORTS) { + for (i = 0; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } + if (i == NR_PORTS) { + restore_flags(flags); + return -1; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} + +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ + +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + /* These will be hidden, because they are devices that will no longer + * be available to the system. (ie, PCMCIA modems, once ejected) + */ + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} + +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + if ((e1 = tty_unregister_driver(&serial_driver))) + printk("serial: failed to unregister serial driver (%d)\n", + e1); + if ((e2 = tty_unregister_driver(&callout_driver))) + printk("serial: failed to unregister callout driver (%d)\n", + e2); + restore_flags(flags); + + for (i = 0; i < NR_PORTS; i++) { + if ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { + release_region(rs_table[i].port, 8); + } + } + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Au1000 serial driver"); + + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 0xffffff; + + do { + status = serial_in(info, UART_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + int ier; + unsigned i; + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, UART_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); + serial_out(info, UART_IER, ier); +} + +/* + * Receive character from the serial port + */ +static int serial_console_wait_key(struct console *co) +{ + static struct async_struct *info; + int ier, c; + + info = &async_sercons; + + /* + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + while ((serial_in(info, UART_LSR) & UART_LSR_DR) == 0); + c = serial_in(info, UART_RX); + + /* + * Restore the interrupts + */ + serial_out(info, UART_IER, ier); + + return c; +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + state->baud_base = get_au1000_uart_baud(); + quot = state->baud_base / baud; + + cval = cflag & (CSIZE | CSTOPB); + cval >>= 4; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + serial_out(info, UART_CLK, quot & 0xffff); + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + + /* + * If we read 0xff from the LSR, there is no UART here. + */ + if (serial_in(info, UART_LSR) == 0xff) + return -1; + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + wait_key: serial_console_wait_key, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init au1000_serial_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c new file mode 100644 index 000000000..b8f58b673 --- /dev/null +++ b/arch/mips/au1000/common/time.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2001 MontaVista Software, ppopov@mvista.com + * Copied and modified Carsten Langgaard's time.c + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * ######################################################################## + * + * Setting up the clock on the MIPS boards. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +extern volatile unsigned long wall_jiffies; +unsigned long missed_heart_beats = 0; +unsigned long uart_baud_base; + +static unsigned long r4k_offset; /* Amount to increment compare reg each time */ +static unsigned long r4k_cur; /* What counter should be at next timer irq */ +extern rwlock_t xtime_lock; + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + +static inline void ack_r4ktimer(unsigned long newval) +{ + write_32bit_cp0_register(CP0_COMPARE, newval); +} + + +/* + * There are a lot of conceptually broken versions of the MIPS timer interrupt + * handler floating around. This one is rather different, but the algorithm + * is provably more robust. + */ +unsigned long wtimer; +void mips_timer_interrupt(struct pt_regs *regs) +{ + int irq = 7; + + if (r4k_offset == 0) + goto null; + + do { + kstat.irqs[0][irq]++; + do_timer(regs); + r4k_cur += r4k_offset; + ack_r4ktimer(r4k_cur); + + } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT) + - r4k_cur) < 0x7fffffff); + + return; + +null: + ack_r4ktimer(0); +} + +/* + * Figure out the r4k offset, the amount to increment the compare + * register for each time tick. + * Use the Programmable Counter 1 to do this. + */ +unsigned long cal_r4koff(void) +{ + unsigned long count; + unsigned long cpu_pll; + unsigned long cpu_speed; + unsigned long start, end; + unsigned long counter; + int i; + int trim_divide = 16; + + counter = inl(PC_COUNTER_CNTRL); + outl(counter | PC_CNTRL_EN1, PC_COUNTER_CNTRL); + + while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S); + outl(trim_divide-1, PC1_TRIM); /* RTC now ticks at 32.768/16 kHz */ + while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S); + + while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S); + outl (0, PC1_COUNTER_WRITE); + while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S); + + start = inl(PC1_COUNTER_READ); + start += 2; + /* wait for the beginning of a new tick */ + while (inl(PC1_COUNTER_READ) < start); + + /* Start r4k counter. */ + write_32bit_cp0_register(CP0_COUNT, 0); + end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */ + + while (end > inl(PC1_COUNTER_READ)); + + count = read_32bit_cp0_register(CP0_COUNT); + cpu_speed = count * 2; + uart_baud_base = (((cpu_speed) / 4) / 16); + return (cpu_speed / HZ); +} + +static unsigned long __init get_mips_time(void) +{ + return inl(PC0_COUNTER_READ); +} + +void __init time_init(void) +{ + unsigned int est_freq, flags; + + printk("calculating r4koff... "); + r4k_offset = cal_r4koff(); + printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); + + //est_freq = 2*r4k_offset*HZ; + est_freq = r4k_offset*HZ; + est_freq += 5000; /* round */ + est_freq -= est_freq%10000; + printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, + (est_freq%1000000)*100/1000000); + r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); + + write_32bit_cp0_register(CP0_COMPARE, r4k_cur); + set_cp0_status(ALLINTS); + + /* Read time from the RTC chipset. */ + write_lock_irqsave (&xtime_lock, flags); + xtime.tv_sec = get_mips_time(); + xtime.tv_usec = 0; + write_unlock_irqrestore(&xtime_lock, flags); +} + +/* This is for machines which generate the exact clock. */ +#define USECS_PER_JIFFY (1000000/HZ) +#define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff) + +/* Cycle counter value at the previous timer interrupt.. */ + +static unsigned int timerhi = 0, timerlo = 0; + +static unsigned long +div64_32(unsigned long v1, unsigned long v2, unsigned long v3) +{ + unsigned long r0; + do_div64_32(r0, v1, v2, v3); + return r0; +} + + +/* + * FIXME: Does playing with the RP bit in c0_status interfere with this code? + */ +static unsigned long do_fast_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + unsigned long r0; + + /* Last jiffy when do_fast_gettimeoffset() was called. */ + static unsigned long last_jiffies=0; + unsigned long quotient; + + /* + * Cached "1/(clocks per usec)*2^32" value. + * It has to be recalculated once each jiffy. + */ + static unsigned long cached_quotient=0; + + tmp = jiffies; + + quotient = cached_quotient; + + if (tmp && last_jiffies != tmp) { + last_jiffies = tmp; + if (last_jiffies != 0) { + r0 = div64_32(timerhi, timerlo, tmp); + quotient = div64_32(USECS_PER_JIFFY, USECS_PER_JIFFY_FRAC, r0); + cached_quotient = quotient; + } + } + + /* Get last timer tick in absolute kernel time */ + count = read_32bit_cp0_register(CP0_COUNT); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu\t%1,%2\n\t" + "mfhi\t%0" + :"=r" (res) + :"r" (count), + "r" (quotient)); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY-1; + + return res; +} + +void do_gettimeofday(struct timeval *tv) +{ + unsigned int flags; + + read_lock_irqsave (&xtime_lock, flags); + *tv = xtime; + tv->tv_usec += do_fast_gettimeoffset(); + + /* + * xtime is atomically updated in timer_bh. jiffies - wall_jiffies + * is nonzero if the timer bottom half hasnt executed yet. + */ + if (jiffies - wall_jiffies) + tv->tv_usec += USECS_PER_JIFFY; + + read_unlock_irqrestore (&xtime_lock, flags); + + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } +} + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq (&xtime_lock); + + /* This is revolting. We need to set the xtime.tv_usec correctly. + * However, the value in this location is is value at the last tick. + * Discover what correction gettimeofday would have done, and then + * undo it! + */ + tv->tv_usec -= do_fast_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + + write_unlock_irq (&xtime_lock); +} + +/* + * The UART baud base is not known at compile time ... if + * we want to be able to use the same code on different + * speed CPUs. + */ +unsigned long get_au1000_uart_baud() +{ + return uart_baud_base; +} diff --git a/arch/mips/au1000/pb1000/Makefile b/arch/mips/au1000/pb1000/Makefile new file mode 100644 index 000000000..c6d318793 --- /dev/null +++ b/arch/mips/au1000/pb1000/Makefile @@ -0,0 +1,24 @@ +# +# Copyright 2000 MontaVista Software Inc. +# Author: MontaVista Software, Inc. +# ppopov@mvista.com or support@mvista.com +# +# Makefile for the Alchemy Semiconductor PB1000 board. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: pb1000.o + +O_TARGET := pb1000.o + +obj-y := init.o setup.o + +include $(TOPDIR)/Rules.make diff --git a/arch/mips/au1000/pb1000/init.c b/arch/mips/au1000/pb1000/init.c new file mode 100644 index 000000000..27cbae887 --- /dev/null +++ b/arch/mips/au1000/pb1000/init.c @@ -0,0 +1,59 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * PB1000 board setup + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or support@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int prom_argc; +char **prom_argv, **prom_envp; +extern void __init prom_init_cmdline(void); + +int __init prom_init(int argc, char **argv, char **envp, int *prom_vec) +{ + prom_argc = argc; + prom_argv = argv; + prom_envp = envp; + + mips_machgroup = MACH_GROUP_ALCHEMY; + mips_machtype = MACH_PB1000; + + prom_init_cmdline(); + + add_memory_region(1, 64 << 20, BOOT_MEM_RAM); + + return 0; +} diff --git a/arch/mips/au1000/pb1000/setup.c b/arch/mips/au1000/pb1000/setup.c new file mode 100644 index 000000000..aaa80694f --- /dev/null +++ b/arch/mips/au1000/pb1000/setup.c @@ -0,0 +1,117 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Au1000-based board setup. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or support@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_AU1000_SERIAL_CONSOLE) +extern void console_setup(char *, int *); +char serial_console[20]; +#endif + +void (*__wbflush) (void); +extern struct rtc_ops no_rtc_ops; +extern char * __init prom_getcmdline(void); +extern void au1000_restart(void); +extern void au1000_halt(void); +extern void au1000_power_off(void); + +struct { + struct resource ram; + struct resource io; + struct resource sram; + struct resource flash; + struct resource boot; + struct resource pcmcia; + struct resource lcd; +} au1000_resources = { + { "RAM", 0, 0x3FFFFFF, IORESOURCE_MEM }, + { "I/O", 0x10000000, 0x119FFFFF }, + { "SRAM", 0x1e000000, 0x1E03FFFF }, + { "System Flash", 0x1F800000, 0x1FBFFFFF }, + { "Boot ROM", 0x1FC00000, 0x1FFFFFFF }, + { "PCMCIA", 0x20000000, 0x27FFFFFF }, + { "LCD", 0x60000000, 0x603FFFFF }, +}; + +void au1000_wbflush(void) +{ + __asm__ volatile ("sync"); +} + +void __init au1000_setup(void) +{ + char *argptr; + + argptr = prom_getcmdline(); + +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + if ((argptr = strstr(argptr, "console=ttyS0")) == NULL) { + argptr = prom_getcmdline(); + strcat(argptr, " console=ttyS0,115200"); + } +#endif + + //set_cp0_status(ST0_FR,0); + rtc_ops = &no_rtc_ops; + __wbflush = au1000_wbflush; + _machine_restart = au1000_restart; + _machine_halt = au1000_halt; + _machine_power_off = au1000_power_off; + + /* + * IO/MEM resources. + */ + mips_io_port_base = KSEG1; + ioport_resource.start = au1000_resources.io.start; + ioport_resource.end = au1000_resources.lcd.end; + +#ifdef CONFIG_BLK_DEV_INITRD + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); +#endif + + outl(PC_CNTRL_E0 | PC_CNTRL_EN0 | PC_CNTRL_EN0, PC_COUNTER_CNTRL); + while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T0S); + outl(0x8000-1, PC0_TRIM); + + printk("Alchemy Semi PB1000 Board\n"); + printk("Au1000/PB1000 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + diff --git a/arch/mips/config.in b/arch/mips/config.in index 9b70f1f2e..5a0a17c57 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -65,6 +65,7 @@ bool 'Support for ITE 8172G board' CONFIG_MIPS_ITE8172 fi bool ' Enable Smart Card Reader 0 Support ' CONFIG_IT8172_SCR0 fi +bool 'Support for Alchemy Semi PB1000 board' CONFIG_MIPS_PB1000 # # Select some configuration options automatically for certain systems. @@ -177,6 +178,9 @@ fi if [ "$CONFIG_MIPS_IVR" = "y" ]; then define_bool CONFIG_PCI y fi +if [ "$CONFIG_MIPS_PB1000" = "y" ]; then + define_bool CONFIG_MIPS_AU1000 y +fi if [ "$CONFIG_NINO" = "y" ]; then define_bool CONFIG_PC_KEYB y fi @@ -480,7 +484,7 @@ bool 'Are you using a crosscompiler' CONFIG_CROSSCOMPILE if [ "$CONFIG_MODULES" = "y" ]; then bool ' Build fp execption handler module' CONFIG_MIPS_FPE_MODULE fi -if [ "$CONFIG_SERIAL" = "y" ]; then +if [ "$CONFIG_SERIAL" = "y" -o "$CONFIG_AU1000_UART" = "y" ]; then bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG fi if [ "$CONFIG_SERIAL" = "y" ]; then diff --git a/arch/mips/defconfig b/arch/mips/defconfig index a9bb345dd..371fa391f 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -30,6 +30,7 @@ CONFIG_SGI_IP22=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_ARC32=y diff --git a/arch/mips/defconfig-atlas b/arch/mips/defconfig-atlas index ba8632bd9..27eaadd48 100644 --- a/arch/mips/defconfig-atlas +++ b/arch/mips/defconfig-atlas @@ -30,6 +30,7 @@ CONFIG_MIPS_ATLAS=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PCI=y diff --git a/arch/mips/defconfig-ddb5476 b/arch/mips/defconfig-ddb5476 index 6a890050c..018f9ac65 100644 --- a/arch/mips/defconfig-ddb5476 +++ b/arch/mips/defconfig-ddb5476 @@ -30,6 +30,7 @@ CONFIG_DDB5476=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_I8259=y diff --git a/arch/mips/defconfig-decstation b/arch/mips/defconfig-decstation index f6ac3193d..640647952 100644 --- a/arch/mips/defconfig-decstation +++ b/arch/mips/defconfig-decstation @@ -30,6 +30,7 @@ CONFIG_DECSTATION=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set # CONFIG_ISA is not set diff --git a/arch/mips/defconfig-ev64120 b/arch/mips/defconfig-ev64120 index 3a0abe35c..d03094048 100644 --- a/arch/mips/defconfig-ev64120 +++ b/arch/mips/defconfig-ev64120 @@ -34,6 +34,7 @@ CONFIG_SYSCLK_100=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PCI=y diff --git a/arch/mips/defconfig-ev96100 b/arch/mips/defconfig-ev96100 index d3300ed4a..ba5484881 100644 --- a/arch/mips/defconfig-ev96100 +++ b/arch/mips/defconfig-ev96100 @@ -30,6 +30,7 @@ CONFIG_MIPS_EV96100=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PCI=y diff --git a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22 index a9bb345dd..371fa391f 100644 --- a/arch/mips/defconfig-ip22 +++ b/arch/mips/defconfig-ip22 @@ -30,6 +30,7 @@ CONFIG_SGI_IP22=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_ARC32=y diff --git a/arch/mips/defconfig-it8172 b/arch/mips/defconfig-it8172 index 3881cd44f..fa9e7d07f 100644 --- a/arch/mips/defconfig-it8172 +++ b/arch/mips/defconfig-it8172 @@ -35,6 +35,7 @@ CONFIG_IT8172_CIR=y # CONFIG_IT8172_SCR0 is not set # CONFIG_IT8172_SCR1 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PCI=y diff --git a/arch/mips/defconfig-malta b/arch/mips/defconfig-malta index 1e02a2837..0786d4a8a 100644 --- a/arch/mips/defconfig-malta +++ b/arch/mips/defconfig-malta @@ -30,6 +30,7 @@ CONFIG_MIPS_MALTA=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_I8259=y diff --git a/arch/mips/defconfig-nino b/arch/mips/defconfig-nino index 23d024cd1..a82abd780 100644 --- a/arch/mips/defconfig-nino +++ b/arch/mips/defconfig-nino @@ -33,6 +33,7 @@ CONFIG_NINO_8MB=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PC_KEYB=y @@ -184,6 +185,7 @@ CONFIG_SERIAL_NONSTANDARD=y # CONFIG_STALDRV is not set CONFIG_SERIAL_TX3912=y CONFIG_SERIAL_TX3912_CONSOLE=y +# CONFIG_AU1000_UART is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 diff --git a/arch/mips/defconfig-ocelot b/arch/mips/defconfig-ocelot index 49a9763b1..15fe6a913 100644 --- a/arch/mips/defconfig-ocelot +++ b/arch/mips/defconfig-ocelot @@ -30,6 +30,7 @@ CONFIG_MOMENCO_OCELOT=y # CONFIG_SNI_RM200_PCI is not set # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_PCI=y diff --git a/arch/mips/defconfig-pb1000 b/arch/mips/defconfig-pb1000 new file mode 100644 index 000000000..df574b8dd --- /dev/null +++ b/arch/mips/defconfig-pb1000 @@ -0,0 +1,445 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_MIPS=y +# CONFIG_SMP is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Machine selection +# +# CONFIG_ACER_PICA_61 is not set +# CONFIG_ALGOR_P4032 is not set +# CONFIG_BAGET_MIPS is not set +# CONFIG_DECSTATION is not set +# CONFIG_DDB5074 is not set +# CONFIG_MIPS_EV96100 is not set +# CONFIG_MIPS_EV64120 is not set +# CONFIG_MIPS_ATLAS is not set +# CONFIG_MIPS_MALTA is not set +# CONFIG_NINO is not set +# CONFIG_MIPS_MAGNUM_4000 is not set +# CONFIG_MOMENCO_OCELOT is not set +# CONFIG_DDB5476 is not set +# CONFIG_OLIVETTI_M700 is not set +# CONFIG_SGI_IP22 is not set +# CONFIG_SNI_RM200_PCI is not set +# CONFIG_MIPS_ITE8172 is not set +# CONFIG_MIPS_IVR is not set +CONFIG_MIPS_PB1000=y +# CONFIG_MCA is not set +# CONFIG_SBUS is not set +CONFIG_MIPS_AU1000=y +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_PCI is not set +# CONFIG_I8259 is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# CPU selection +# +# CONFIG_CPU_R3000 is not set +# CONFIG_CPU_R6000 is not set +# CONFIG_CPU_R4300 is not set +# CONFIG_CPU_R4X00 is not set +# CONFIG_CPU_R5000 is not set +# CONFIG_CPU_R5432 is not set +# CONFIG_CPU_RM7000 is not set +# CONFIG_CPU_NEVADA is not set +# CONFIG_CPU_R10000 is not set +# CONFIG_CPU_SB1 is not set +CONFIG_CPU_MIPS32=y +CONFIG_CPU_ADVANCED=y +CONFIG_CPU_HAS_LLSC=y +CONFIG_CPU_HAS_WB=y + +# +# General setup +# +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_MIPS_FPU_EMULATOR=y +CONFIG_KCORE_ELF=y +CONFIG_ELF_KERNEL=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_NET=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MIPS_AU1000_ENET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_COMPUTONE is not set +# CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set +# CONFIG_DIGIEPCA is not set +# CONFIG_DIGI is not set +# CONFIG_ESPSERIAL is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_RISCOM8 is not set +# CONFIG_SPECIALIX is not set +# CONFIG_SX is not set +# CONFIG_RIO is not set +# CONFIG_STALDRV is not set +# CONFIG_SERIAL_TX3912 is not set +# CONFIG_SERIAL_TX3912_CONSOLE is not set +CONFIG_AU1000_UART=y +CONFIG_AU1000_SERIAL_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Kernel hacking +# +CONFIG_CROSSCOMPILE=y +# CONFIG_REMOTE_DEBUG is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_MIPS_UNCACHED is not set diff --git a/arch/mips/defconfig-rm200 b/arch/mips/defconfig-rm200 index 7959900c2..5cc2d9e5f 100644 --- a/arch/mips/defconfig-rm200 +++ b/arch/mips/defconfig-rm200 @@ -30,6 +30,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SNI_RM200_PCI=y # CONFIG_MIPS_ITE8172 is not set # CONFIG_MIPS_IVR is not set +# CONFIG_MIPS_PB1000 is not set # CONFIG_MCA is not set # CONFIG_SBUS is not set CONFIG_ARC32=y diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 2a066ccaa..773d72c0d 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -44,12 +44,17 @@ int get_cpuinfo(char *buffer) const char *mach_momenco_names[] = GROUP_MOMENCO_NAMES; const char *mach_ite_names[] = GROUP_ITE_NAMES; const char *mach_philips_names[] = GROUP_PHILIPS_NAMES; + const char *mach_globespan_names[] = GROUP_GLOBESPAN_NAMES; + const char *mach_sibyte_names[] = GROUP_SIBYTE_NAMES; + const char *mach_toshiba_names[] = GROUP_TOSHIBA_NAMES; + const char *mach_alchemy_names[] = GROUP_ALCHEMY_NAMES; const char **mach_group_to_name[] = { mach_unknown_names, mach_jazz_names, mach_dec_names, mach_arc_names, mach_sni_rm_names, mach_acn_names, mach_sgi_names, mach_cobalt_names, mach_nec_ddb_names, mach_baget_names, mach_cosine_names, mach_galileo_names, mach_momenco_names, - mach_ite_names, mach_philips_names}; + mach_ite_names, mach_philips_names, mach_globespan_names, + mach_sibyte_names, mach_toshiba_names, mach_alchemy_names}; unsigned int version = read_32bit_cp0_register(CP0_PRID); int len; diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 587adaadd..82d44e33e 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -341,6 +341,30 @@ static inline void cpu_probe(void) } break; #endif + case PRID_COMP_ALCHEMY: + switch (mips_cpu.processor_id & 0xff00) { +#ifdef CONFIG_CPU_MIPS32 + case PRID_IMP_AU1000: + mips_cpu.cputype = CPU_AU1000; + mips_cpu.isa_level = MIPS_CPU_ISA_M32; + mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | + MIPS_CPU_DIVEC | MIPS_CPU_WATCH; + config1 = read_mips32_cp0_config1(); + if (config1 & (1 << 3)) + mips_cpu.options |= MIPS_CPU_WATCH; + if (config1 & (1 << 2)) + mips_cpu.options |= MIPS_CPU_MIPS16; + if (config1 & 1) + mips_cpu.options |= MIPS_CPU_FPU; + mips_cpu.scache.flags = MIPS_CACHE_NOT_PRESENT; + break; +#endif + default: + mips_cpu.cputype = CPU_UNKNOWN; + break; + } + break; case PRID_COMP_SIBYTE: switch (mips_cpu.processor_id & 0xff00) { case PRID_IMP_SB1: @@ -595,6 +619,11 @@ void __init setup_arch(char **cmdline_p) case MACH_GROUP_PHILIPS: nino_setup(); break; +#endif +#ifdef CONFIG_MIPS_PB1000 + case MACH_GROUP_ALCHEMY: + au1000_setup(); + break; #endif default: panic("Unsupported architecture"); diff --git a/drivers/char/Config.in b/drivers/char/Config.in index a7a6933f7..eaff31987 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -61,6 +61,10 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then fi bool ' TMPTX3912/PR31700 serial port support' CONFIG_SERIAL_TX3912 dep_bool ' Console on TMPTX3912/PR31700 serial port' CONFIG_SERIAL_TX3912_CONSOLE $CONFIG_SERIAL_TX3912 + bool ' Enable Au1000 UART Support' CONFIG_AU1000_UART + if [ "$CONFIG_AU1000_UART" = "y" ]; then + bool ' Enable Au1000 serial console' CONFIG_AU1000_SERIAL_CONSOLE + fi fi if [ "$CONFIG_IT8712" = "y" ]; then bool 'Enable Qtronix 990P Keyboard Support' CONFIG_QTRONIX_KEYBOARD diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 218aa7681..0f1685653 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2179,6 +2179,9 @@ void __init console_init(void) #ifdef CONFIG_VT con_init(); #endif +#ifdef CONFIG_AU1000_SERIAL_CONSOLE + au1000_serial_console_init(); +#endif #ifdef CONFIG_SERIAL_CONSOLE #if (defined(CONFIG_8xx) || defined(CONFIG_8260)) console_8xx_init(); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index c0e9bae05..487c21f3f 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -52,6 +52,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_MIPS_GT96100" = "y" ]; then bool ' MIPS GT96100 Ethernet support' CONFIG_MIPS_GT96100ETH fi + if [ "$CONFIG_MIPS_AU1000" = "y" ]; then + bool ' MIPS AU1000 Ethernet support' CONFIG_MIPS_AU1000_ENET + fi if [ "$CONFIG_SGI_IP27" = "y" ]; then bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH fi diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d81d8c51b..b7314c8fd 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -189,6 +189,7 @@ obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o obj-$(CONFIG_EQUALIZER) += eql.o obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o obj-$(CONFIG_MIPS_GT96100ETH) += gt96100eth.o +obj-$(CONFIG_MIPS_AU1000_ENET) += au1000_eth.o obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o obj-$(CONFIG_BAGETLANCE) += bagetlance.o obj-$(CONFIG_DECLANCE) += declance.o diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c new file mode 100644 index 000000000..297c7c6ca --- /dev/null +++ b/drivers/net/au1000_eth.c @@ -0,0 +1,1266 @@ +/* + * + * Alchemy Semi Au1000 ethernet driver + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * ######################################################################## + * + * + */ + +#ifndef __mips__ +#error This driver only works with MIPS architectures! +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "au1000_eth.h" + +#ifdef AU1000_ETH_DEBUG +static int au1000_debug = 10; +#else +static int au1000_debug = 3; +#endif + +// prototypes +static void *dma_alloc(size_t, dma_addr_t *); +static void dma_free(void *, size_t); +static void hard_stop(struct net_device *); +static int __init au1000_probe1(struct net_device *, long, int, int); +static int au1000_init(struct net_device *); +static int au1000_open(struct net_device *); +static int au1000_close(struct net_device *); +static int au1000_tx(struct sk_buff *, struct net_device *); +static int au1000_rx(struct net_device *); +static void au1000_interrupt(int, void *, struct pt_regs *); +static void au1000_tx_timeout(struct net_device *); +static int au1000_set_config(struct net_device *dev, struct ifmap *map); +static void set_rx_mode(struct net_device *); +static struct net_device_stats *au1000_get_stats(struct net_device *); +static inline void update_tx_stats(struct net_device *, u32, u32); +static inline void update_rx_stats(struct net_device *, u32); +static void au1000_timer(unsigned long); +static void cleanup_buffers(struct net_device *); +static int au1000_ioctl(struct net_device *, struct ifreq *, int); +static int mdio_read(struct net_device *, int, int); +static void mdio_write(struct net_device *, int, int, u16); +static inline void sync(void); + +extern void ack_rise_edge_irq(unsigned int); + +static int next_dev; + +/* + * Theory of operation + * + * The Au1000 MACs use a simple rx and tx descriptor ring scheme. + * There are four receive and four transmit descriptors. These + * descriptors are not in memory; rather, they are just a set of + * hardware registers. + * + * Since the Au1000 has a coherent data cache, the receive and + * transmit buffers are allocated from the KSEG0 segment. The + * hardware registers, however, are still mapped at KSEG1 to + * make sure there's no out-of-order writes, and that all writes + * complete immediately. + */ + + +/* + * Base address and interupt of the Au1000 ethernet macs + */ +static struct { + unsigned int port; + int irq; +} au1000_iflist[NUM_INTERFACES] = { + {AU1000_ETH0_BASE, AU1000_ETH0_IRQ}, + {AU1000_ETH1_BASE, AU1000_ETH1_IRQ} +}; + + +static char version[] __devinitdata = + "au1000eth.c:0.1 ppopov@mvista.com\n"; + +// FIX! Need real Ethernet addresses +static unsigned char au1000_mac_addr[2][6] __devinitdata = { + {0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00}, + {0x00, 0x50, 0xc2, 0x0c, 0x40, 0x00} +}; + +#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0)) +#define RUN_AT(x) (jiffies + (x)) + +// For reading/writing 32-bit words from/to DMA memory +#define cpu_to_dma32 cpu_to_be32 +#define dma32_to_cpu be32_to_cpu + +/* CPU pipeline flush */ +static inline void sync(void) +{ + asm volatile ("sync"); +} + +/* FIXME + * All of the PHY code really should be detached from the MAC + * code. + */ + +static char *phy_link[] = + {"unknown", + "10Base2", "10BaseT", + "AUI", + "100BaseT", "100BaseTX", "100BaseFX"}; + +int bcm_5201_init(struct net_device *dev, int phy_addr) +{ + s16 data; + + /* Stop auto-negotiation */ + //printk("bcm_5201_init\n"); + data = mdio_read(dev, phy_addr, MII_CONTROL); + mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO); + + /* Set advertisement to 10/100 and Half/Full duplex + * (full capabilities) */ + data = mdio_read(dev, phy_addr, MII_ANADV); + data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T; + mdio_write(dev, phy_addr, MII_ANADV, data); + + /* Restart auto-negotiation */ + data = mdio_read(dev, phy_addr, MII_CONTROL); + data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO; + mdio_write(dev, phy_addr, MII_CONTROL, data); + //dump_mii(dev, phy_addr); + return 0; +} + +int bcm_5201_reset(struct net_device *dev, int phy_addr) +{ + s16 mii_control, timeout; + + //printk("bcm_5201_reset\n"); + mii_control = mdio_read(dev, phy_addr, MII_CONTROL); + mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET); + mdelay(1); + for (timeout = 100; timeout > 0; --timeout) { + mii_control = mdio_read(dev, phy_addr, MII_CONTROL); + if ((mii_control & MII_CNTL_RESET) == 0) + break; + mdelay(1); + } + if (mii_control & MII_CNTL_RESET) { + printk(KERN_ERR "%s PHY reset timeout !\n", dev->name); + return -1; + } + return 0; +} + +int +bcm_5201_status(struct net_device *dev, int phy_addr, int *link, int *speed) +{ + u16 mii_data; + struct au1000_private *aup; + + if (!dev) { + printk(KERN_ERR "bcm_5201_status error: NULL dev\n"); + return -1; + } + aup = (struct au1000_private *) dev->priv; + + mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS); + if (mii_data & MII_STAT_LINK) { + *link = 1; + mii_data = mdio_read(dev, aup->phy_addr, MII_AUX_CNTRL); + if (mii_data & MII_AUX_100) { + if (mii_data & MII_AUX_FDX) { + *speed = IF_PORT_100BASEFX; + dev->if_port = IF_PORT_100BASEFX; + } + else { + *speed = IF_PORT_100BASETX; + dev->if_port = IF_PORT_100BASETX; + } + } + else { + *speed = IF_PORT_10BASET; + dev->if_port = IF_PORT_10BASET; + } + + } + else { + *link = 0; + *speed = 0; + } + return 0; +} + + +int am79c901_init(struct net_device *dev, int phy_addr) +{ + printk("am79c901_init\n"); + return 0; +} + +int am79c901_reset(struct net_device *dev, int phy_addr) +{ + printk("am79c901_reset\n"); + return 0; +} + +int +am79c901_status(struct net_device *dev, int phy_addr, int *link, int *speed) +{ + return 0; +} + +struct phy_ops bcm_5201_ops = { + bcm_5201_init, + bcm_5201_reset, + bcm_5201_status, +}; + +struct phy_ops am79c901_ops = { + am79c901_init, + am79c901_reset, + am79c901_status, +}; + +static struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; + struct phy_ops *phy_ops; +} mii_chip_table[] = { + {"Broadcom BCM5201 10/100 BaseT PHY", 0x0040, 0x6212, &bcm_5201_ops }, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, &am79c901_ops }, + {0,}, +}; + +static int mdio_read(struct net_device *dev, int phy_id, int reg) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + u32 timedout = 20; + u32 mii_control; + + while (aup->mac->mii_control & MAC_MII_BUSY) { + mdelay(1); + if (--timedout == 0) { + printk(KERN_ERR "%s: read_MII busy timeout!!\n", dev->name); + return -1; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_READ; + + aup->mac->mii_control = mii_control; + + timedout = 20; + while (aup->mac->mii_control & MAC_MII_BUSY) { + mdelay(1); + if (--timedout == 0) { + printk(KERN_ERR "%s: mdio_read busy timeout!!\n", dev->name); + return -1; + } + } + return (int)aup->mac->mii_data; +} + +static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 value) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + u32 timedout = 20; + u32 mii_control; + + while (aup->mac->mii_control & MAC_MII_BUSY) { + mdelay(1); + if (--timedout == 0) { + printk(KERN_ERR "%s: mdio_write busy timeout!!\n", dev->name); + return; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_WRITE; + + aup->mac->mii_data = value; + aup->mac->mii_control = mii_control; +} + + +static void dump_mii(struct net_device *dev, int phy_id) +{ + int i, val; + + for (i = 0; i < 7; i++) { + if ((val = mdio_read(dev, phy_id, i)) >= 0) + printk("%s: MII Reg %d=%x\n", dev->name, i, val); + } + for (i = 16; i < 25; i++) { + if ((val = mdio_read(dev, phy_id, i)) >= 0) + printk("%s: MII Reg %d=%x\n", dev->name, i, val); + } +} + +static int __init mii_probe (struct net_device * dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + int phy_addr; + + aup->mii = NULL; + + /* search for total of 32 possible mii phy addresses */ + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + u16 mii_status; + u16 phy_id0, phy_id1; + int i; + + mii_status = mdio_read(dev, phy_addr, MII_STATUS); + if (mii_status == 0xffff || mii_status == 0x0000) + /* the mii is not accessable, try next one */ + continue; + + phy_id0 = mdio_read(dev, phy_addr, MII_PHY_ID0); + phy_id1 = mdio_read(dev, phy_addr, MII_PHY_ID1); + + /* search our mii table for the current mii */ + for (i = 0; mii_chip_table[i].phy_id1; i++) + if (phy_id0 == mii_chip_table[i].phy_id0 && + phy_id1 == mii_chip_table[i].phy_id1) { + struct mii_phy * mii_phy; + + printk(KERN_INFO "%s: %s found at phy address %d\n", + dev->name, mii_chip_table[i].name, phy_addr); + if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) { + mii_phy->chip_info = mii_chip_table+i; + mii_phy->phy_addr = phy_addr; + //mii_phy->status = mdio_read(dev, phy_addr, MII_STATUS); + mii_phy->next = aup->mii; + aup->phy_ops = mii_chip_table[i].phy_ops; + aup->mii = mii_phy; + } + /* the current mii is on our mii_info_table, + try next address */ + break; + } + } + + if (aup->mii == NULL) { + printk(KERN_ERR "%s: No MII transceivers found!\n", dev->name); + return -1; + } + + /* use last PHY */ + aup->phy_addr = aup->mii->phy_addr; + printk(KERN_INFO "%s: Using %s as default\n", dev->name, aup->mii->chip_info->name); + + return 0; +} + + +/* + * Buffer allocation/deallocation routines. The buffer descriptor returned + * has the virtual and dma address of a buffer suitable for + * both, receive and transmit operations. + */ +static db_dest_t *GetFreeDB(struct au1000_private *aup) +{ + db_dest_t *pDB; + pDB = aup->pDBfree; + + if (pDB) { + aup->pDBfree = pDB->pnext; + } + //printk("GetFreeDB: %x\n", pDB); + return pDB; +} + +void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB) +{ + db_dest_t *pDBfree = aup->pDBfree; + if (pDBfree) + pDBfree->pnext = pDB; + aup->pDBfree = pDB; +} + + +/* + DMA memory allocation, derived from pci_alloc_consistent. + However, the Au1000 data cache is coherent (when programmed + so), therefore we return KSEG0 address, not KSEG1. +*/ +static void *dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC | GFP_DMA; + + ret = (void *) __get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + ret = KSEG0ADDR(ret); + } + return ret; +} + + +static void dma_free(void *vaddr, size_t size) +{ + vaddr = KSEG0ADDR(vaddr); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +static void hard_stop(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + if (au1000_debug > 4) + printk(KERN_INFO "%s: hard stop\n", dev->name); + + aup->mac->control &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE); + sync(); + mdelay(10); +} + + +static void reset_mac(struct net_device *dev) +{ + u32 flags; + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + if (au1000_debug > 4) + printk(KERN_INFO "%s: reset mac, aup %x\n", dev->name, (unsigned)aup); + + spin_lock_irqsave(&aup->lock, flags); + del_timer(&aup->timer); + hard_stop(dev); + *aup->enable |= MAC_DMA_RESET; + sync(); + mdelay(10); + aup->tx_full = 0; + spin_unlock_irqrestore(&aup->lock, flags); +} + +static void cleanup_buffers(struct net_device *dev) +{ + int i; + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + for (i=0; irx_db_inuse[i]) { + ReleaseDB(aup, aup->rx_db_inuse[i]); + aup->rx_db_inuse[i] = 0; + } + } + + for (i=0; itx_db_inuse[i]) { + ReleaseDB(aup, aup->tx_db_inuse[i]); + aup->tx_db_inuse[i] = 0; + } + } +} + + +/* + * Setup the receive and transmit "rings". These pointers are the addresses + * of the rx and tx MAC DMA registers so they are fixed by the hardware -- + * these are not descriptors sitting in memory. + */ +static void +setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base) +{ + int i; + + for (i=0; irx_dma_ring[i] = (volatile rx_dma_t *) ioremap_nocache((unsigned long) + (rx_base + sizeof(rx_dma_t)*i), sizeof(rx_dma_t)); + } + for (i=0; itx_dma_ring[i] = (volatile tx_dma_t *)ioremap_nocache((unsigned long) + (tx_base + sizeof(tx_dma_t)*i), sizeof(tx_dma_t)); + } +} + +/* + * Probe for a AU1000 ethernet controller. + */ +int __init au1000_probe(struct net_device *dev) +{ + int base_addr = au1000_iflist[next_dev].port; + int irq = au1000_iflist[next_dev].irq; + +#ifndef CONFIG_MIPS_AU1000_ENET + return -ENODEV; +#endif + + if (au1000_debug > 4) + printk(KERN_INFO "%s: au1000_probe base_addr %x\n", + dev->name, base_addr); + + if (next_dev >= NUM_INTERFACES) { + return -ENODEV; + } + if (au1000_probe1(dev, base_addr, irq, next_dev) == 0) { + next_dev++; + return 0; + } + next_dev++; + return -ENODEV; +} + + + +static int __init +au1000_probe1(struct net_device *dev, long ioaddr, int irq, int port_num) +{ + static unsigned version_printed = 0; + struct au1000_private *aup = NULL; + int i, retval = 0; + db_dest_t *pDB, *pDBfree; + u16 link, speed; + + if ((ioaddr != AU1000_ETH0_BASE) && (ioaddr != AU1000_ETH1_BASE)) { + return -ENODEV; + } + + if (!request_region(ioaddr, MAC_IOSIZE, "Au1000 ENET")) { + return -ENODEV; + } + + if (version_printed++ == 0) printk(version); + + if (!dev) { + dev = init_etherdev(0, sizeof(struct au1000_private)); + } + if (!dev) { + printk (KERN_ERR "au1000 eth: init_etherdev failed\n"); + return -ENODEV; + } + + printk("%s: Au1000 ethernet found at 0x%lx, irq %d\n", + dev->name, ioaddr, irq); + + + /* Initialize our private structure */ + if (dev->priv == NULL) { + aup = (struct au1000_private *) kmalloc(sizeof(*aup), GFP_KERNEL); + if (aup == NULL) { + retval = -ENOMEM; + goto free_region; + } + dev->priv = aup; + } + + aup = dev->priv; + memset(aup, 0, sizeof(*aup)); + + + /* Allocate the data buffers */ + aup->vaddr = (u32)dma_alloc(MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS), &aup->dma_addr); + if (!aup->vaddr) { + retval = -ENOMEM; + goto free_region; + } + + /* aup->mac is the base address of the MAC's registers */ + aup->mac = (volatile mac_reg_t *)ioremap_nocache((unsigned long)ioaddr, sizeof(*aup->mac)); + /* Setup some variables for quick register address access */ + if (ioaddr == AU1000_ETH0_BASE) { + aup->enable = (volatile u32 *) + ioremap_nocache((unsigned long)MAC0_ENABLE, sizeof(*aup->enable)); + memcpy(dev->dev_addr, au1000_mac_addr[0], sizeof(dev->dev_addr)); + setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR); + } + else if (ioaddr == AU1000_ETH1_BASE) { + aup->enable = (volatile u32 *) + ioremap_nocache((unsigned long)MAC1_ENABLE, sizeof(*aup->enable)); + memcpy(dev->dev_addr, au1000_mac_addr[1], sizeof(dev->dev_addr)); + setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR); + } + else { /* should never happen */ + printk (KERN_ERR "au1000 eth: bad ioaddr %x\n", (unsigned)ioaddr); + retval = -ENODEV; + goto free_region; + } + + aup->phy_addr = PHY_ADDRESS; + /* bring the device out of reset, otherwise probing the mii + * will hang */ + *aup->enable = MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2 | + MAC_EN_CLOCK_ENABLE | MAC_EN_TOSS; + sync(); + mdelay(2); + if (mii_probe(dev) != 0) { + goto free_region; + } + aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed); + if (!link) { + printk(KERN_INFO "%s: link down resetting...\n", dev->name); + aup->phy_ops->phy_reset(dev, aup->phy_addr); + aup->phy_ops->phy_init(dev, aup->phy_addr); + } + else { + printk(KERN_INFO "%s: link up (%s)\n", dev->name, phy_link[speed]); + } + + pDBfree = NULL; + /* setup the data buffer descriptors and attach a buffer to each one */ + pDB = aup->db; + for (i=0; i<(NUM_TX_BUFFS+NUM_RX_BUFFS); i++) { + pDB->pnext = pDBfree; + pDBfree = pDB; + pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i); + pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); + pDB++; + } + aup->pDBfree = pDBfree; + + for (i=0; irx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr; + aup->rx_db_inuse[i] = pDB; + } + for (i=0; itx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr; + aup->tx_dma_ring[i]->len = 0; + aup->tx_db_inuse[i] = pDB; + } + + spin_lock_init(&aup->lock); + dev->base_addr = ioaddr; + dev->irq = irq; + dev->open = au1000_open; + dev->hard_start_xmit = au1000_tx; + dev->stop = au1000_close; + dev->get_stats = au1000_get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &au1000_ioctl; + dev->set_config = &au1000_set_config; + dev->tx_timeout = au1000_tx_timeout; + dev->watchdog_timeo = ETH_TX_TIMEOUT; + + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + /* + * The boot code uses the ethernet controller, so reset it to start fresh. + * au1000_init() expects that the device is in reset state. + */ + reset_mac(dev); + + return 0; + +free_region: + release_region(ioaddr, MAC_IOSIZE); + unregister_netdev(dev); + if (aup->vaddr) + dma_free((void *)aup->vaddr, MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS)); + if (dev->priv != NULL) + kfree(dev->priv); + kfree(dev); + printk(KERN_ERR "%s: au1000_probe1 failed. Returns %d\n", + dev->name, retval); + return retval; +} + + +/* + * Initialize the interface. + * + * When the device powers up, the clocks are disabled and the + * mac is in reset state. When the interface is closed, we + * do the same -- reset the device and disable the clocks to + * conserve power. Thus, whenever au1000_init() is called, + * the device should already be in reset state. + */ +static int au1000_init(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + u32 flags; + int i; + u32 value, control; + + if (au1000_debug > 4) printk("%s: au1000_init", dev->name); + + spin_lock_irqsave(&aup->lock, flags); + + /* bring the device out of reset */ + value = MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2 | + MAC_EN_CLOCK_ENABLE | MAC_EN_TOSS; + *aup->enable = value; + sync(); + mdelay(200); + + aup->mac->control = 0; + aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2; + aup->tx_tail = aup->tx_head; + aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2; + + aup->mac->mac_addr_high = dev->dev_addr[5]<<8 | dev->dev_addr[4]; + aup->mac->mac_addr_low = dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 | + dev->dev_addr[1]<<8 | dev->dev_addr[0]; + + for (i=0; irx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE; + } + + sync(); + control = MAC_DISABLE_RX_OWN | MAC_RX_ENABLE | MAC_TX_ENABLE; +#ifndef CONFIG_CPU_LITTLE_ENDIAN + control |= MAC_BIG_ENDIAN; +#endif + aup->mac->control = control; + + spin_unlock_irqrestore(&aup->lock, flags); + return 0; +} + +static void au1000_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct au1000_private *aup = (struct au1000_private *) dev->priv; + u16 mii_data, link, speed; + + if (!dev) { + /* fatal error, don't restart the timer */ + printk(KERN_ERR "au1000_timer error: NULL dev\n"); + return; + } + if (!(dev->flags & IFF_UP)) { + goto set_timer; + } + + if (aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed) == 0) { + if (link) { + if (!(dev->flags & IFF_RUNNING)) { + netif_carrier_on(dev); + dev->flags |= IFF_RUNNING; + printk(KERN_DEBUG "%s: link up\n", dev->name); + } + } + else { + if (dev->flags & IFF_RUNNING) { + netif_carrier_off(dev); + dev->flags &= ~IFF_RUNNING; + dev->if_port = 0; + printk(KERN_DEBUG "%s: link down\n", dev->name); + } + } + } + +set_timer: + aup->timer.expires = RUN_AT((1*HZ)); + aup->timer.data = (unsigned long)dev; + aup->timer.function = &au1000_timer; /* timer handler */ + add_timer(&aup->timer); + +} + +static int au1000_open(struct net_device *dev) +{ + int retval; + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + MOD_INC_USE_COUNT; + + if (au1000_debug > 4) + printk("%s: open: dev=%p\n", dev->name, dev); + + if ((retval = au1000_init(dev))) { + printk(KERN_ERR "%s: error in au1000_init\n", dev->name); + free_irq(dev->irq, dev); + MOD_DEC_USE_COUNT; + return retval; + } + netif_start_queue(dev); + + if ((retval = request_irq(dev->irq, &au1000_interrupt, 0, dev->name, dev))) { + printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); + MOD_DEC_USE_COUNT; + return retval; + } + + aup->timer.expires = RUN_AT((3*HZ)); + aup->timer.data = (unsigned long)dev; + aup->timer.function = &au1000_timer; /* timer handler */ + add_timer(&aup->timer); + + if (au1000_debug > 4) + printk("%s: open: Initialization done.\n", dev->name); + + return 0; +} + +static int au1000_close(struct net_device *dev) +{ + u32 flags; + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + if (au1000_debug > 4) + printk("%s: close: dev=%p\n", dev->name, dev); + + spin_lock_irqsave(&aup->lock, flags); + + /* stop the device */ + if (netif_device_present(dev)) { + netif_stop_queue(dev); + } + + /* disable the interrupt */ + free_irq(dev->irq, dev); + spin_unlock_irqrestore(&aup->lock, flags); + + reset_mac(dev); + MOD_DEC_USE_COUNT; + return 0; +} + + +static inline void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + struct net_device_stats *ps = &aup->stats; + + ps->tx_packets++; + ps->tx_bytes += pkt_len; + + if (status & TX_FRAME_ABORTED) { + ps->tx_errors++; + ps->tx_aborted_errors++; + if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER)) + ps->tx_carrier_errors++; + } +} + + +/* + * Called from the interrupt service routine to acknowledge + * the TX DONE bits. This is a must if the irq is setup as + * edge triggered. + */ +static void au1000_tx_ack(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + volatile tx_dma_t *ptxd; + + ptxd = aup->tx_dma_ring[aup->tx_tail]; + + while (ptxd->buff_stat & TX_T_DONE) { + update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); + ptxd->buff_stat &= ~TX_T_DONE; + ptxd->len = 0; + sync(); + + aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1); + ptxd = aup->tx_dma_ring[aup->tx_tail]; + + if (aup->tx_full) { + aup->tx_full = 0; + netif_wake_queue(dev); + } + } +} + + +/* + * Au1000 transmit routine. + */ +static int au1000_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + //unsigned long flags; + volatile tx_dma_t *ptxd; + u32 buff_stat; + db_dest_t *pDB; + int i; + + if (au1000_debug > 4) + printk("%s: tx: aup %x len=%d, data=%p, head %d\n", + dev->name, (unsigned)aup, skb->len, skb->data, aup->tx_head); + + /* Prevent interrupts from changing the Tx ring */ + //spin_lock_irqsave(&aup->lock, flags); + + ptxd = aup->tx_dma_ring[aup->tx_head]; + buff_stat = ptxd->buff_stat; + if (buff_stat & TX_DMA_ENABLE) { + /* We've wrapped around and the transmitter is still busy */ + netif_stop_queue(dev); + aup->tx_full = 1; + //spin_unlock_irqrestore(&aup->lock, flags); + return 1; + } + else if (buff_stat & TX_T_DONE) { + update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); + ptxd->len = 0; + } + + if (aup->tx_full) { + aup->tx_full = 0; + netif_wake_queue(dev); + } + + pDB = aup->tx_db_inuse[aup->tx_head]; + memcpy((void *)pDB->vaddr, skb->data, skb->len); + if (skb->len < MAC_MIN_PKT_SIZE) { + for (i=skb->len; ivaddr)[i] = 0; + } + ptxd->len = MAC_MIN_PKT_SIZE; + } + else + ptxd->len = skb->len; + + ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE; + sync(); + dev_kfree_skb(skb); + aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1); + dev->trans_start = jiffies; + //spin_unlock_irqrestore(&aup->lock, flags); + return 0; +} + + +static inline void update_rx_stats(struct net_device *dev, u32 status) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + struct net_device_stats *ps = &aup->stats; + + ps->rx_packets++; + if (status & RX_MCAST_FRAME) + ps->multicast++; + + if (status & RX_ERROR) { + ps->rx_errors++; + if (status & RX_MISSED_FRAME) + ps->rx_missed_errors++; + if (status & (RX_OVERLEN | RX_OVERLEN | RX_LEN_ERROR)) + ps->rx_length_errors++; + if (status & RX_CRC_ERROR) + ps->rx_crc_errors++; + if (status & RX_COLL) + ps->collisions++; + } + else + ps->rx_bytes += status & RX_FRAME_LEN_MASK; + +} + +/* + * Au1000 receive routine. + */ +static int au1000_rx(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + struct sk_buff *skb; + volatile rx_dma_t *prxd; + u32 buff_stat, status; + db_dest_t *pDB; + + if (au1000_debug > 4) + printk("%s: au1000_rx head %d\n", dev->name, aup->rx_head); + + prxd = aup->rx_dma_ring[aup->rx_head]; + buff_stat = prxd->buff_stat; + while (buff_stat & RX_T_DONE) { + status = prxd->status; + pDB = aup->rx_db_inuse[aup->rx_head]; + update_rx_stats(dev, status); + if (!(status & RX_ERROR)) { + + /* good frame */ + skb = dev_alloc_skb((status & RX_FRAME_LEN_MASK) + 2); + if (skb == NULL) { + printk(KERN_ERR + "%s: Memory squeeze, dropping packet.\n", + dev->name); + aup->stats.rx_dropped++; + continue; + } + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte IP header align */ + eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr, + status & RX_FRAME_LEN_MASK, 0); + skb_put(skb, status & RX_FRAME_LEN_MASK); /* Make room */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); /* pass the packet to upper layers */ + } + else { + if (au1000_debug > 4) { + if (status & RX_MISSED_FRAME) + printk("rx miss\n"); + if (status & RX_WDOG_TIMER) + printk("rx wdog\n"); + if (status & RX_RUNT) + printk("rx runt\n"); + if (status & RX_OVERLEN) + printk("rx overlen\n"); + if (status & RX_COLL) + printk("rx coll\n"); + if (status & RX_MII_ERROR) + printk("rx mii error\n"); + if (status & RX_CRC_ERROR) + printk("rx crc error\n"); + if (status & RX_LEN_ERROR) + printk("rx len error\n"); + if (status & RX_U_CNTRL_FRAME) + printk("rx u control frame\n"); + if (status & RX_MISSED_FRAME) + printk("rx miss\n"); + } + } + prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE); + aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1); + sync(); + + /* next descriptor */ + prxd = aup->rx_dma_ring[aup->rx_head]; + buff_stat = prxd->buff_stat; + dev->last_rx = jiffies; + } + return 0; +} + + +/* + * Au1000 interrupt service routine. + */ +void au1000_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + + if (dev == NULL) { + printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name); + return; + } + au1000_rx(dev); + au1000_tx_ack(dev); +} + + +/* + * The Tx ring has been full longer than the watchdog timeout + * value. The transmitter must be hung? + */ +static void au1000_tx_timeout(struct net_device *dev) +{ + printk(KERN_ERR "%s: au1000_tx_timeout: dev=%p\n", dev->name, dev); + reset_mac(dev); + au1000_init(dev); +} + + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + /* fixme */ + if (au1000_debug > 4) + printk("%s: set_multicast: flags=%x\n", dev->name, dev->flags); + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + aup->mac->control |= MAC_PROMISCUOUS; + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + } else if ((dev->flags & IFF_ALLMULTI) || + dev->mc_count > MULTICAST_FILTER_LIMIT) { + aup->mac->control |= MAC_PASS_ALL_MULTI; + aup->mac->control &= ~MAC_PROMISCUOUS; + printk(KERN_INFO "%s: Pass all multicast\n", dev->name); + } else { + int i; + struct dev_mc_list *mclist; + u32 mc_filter[2]; /* Multicast hash filter */ + + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter); + } + aup->mac->multi_hash_high = mc_filter[1]; + aup->mac->multi_hash_low = mc_filter[0]; + aup->mac->control |= MAC_HASH_MODE; + } +} + + +static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + //struct au1000_private *aup = (struct au1000_private *) dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + + /* fixme */ + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = PHY_ADDRESS; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + //data[3] = mdio_read(ioaddr, data[0], data[1]); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + //mdio_write(ioaddr, data[0], data[1], data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + + +static int au1000_set_config(struct net_device *dev, struct ifmap *map) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + u16 control; + + if (au1000_debug > 4) { + printk("%s: set_config called: dev->if_port %d map->port %x\n", + dev->name, dev->if_port, map->port); + } + + switch(map->port){ + case IF_PORT_UNKNOWN: /* use auto here */ + printk("auto\\n"); + dev->if_port = map->port; + /* Link Down: the timer will bring it up */ + netif_carrier_off(dev); + + /* read current control */ + control = mdio_read(dev, aup->phy_addr, MII_CONTROL); + control &= ~(MII_CNTL_FDX | MII_CNTL_F100); + + /* enable auto negotiation and reset the negotiation */ + mdio_write(dev, aup->phy_addr, + MII_CONTROL, control | MII_CNTL_AUTO | MII_CNTL_RST_AUTO); + + break; + + case IF_PORT_10BASET: /* 10BaseT */ + printk("10baseT\n"); + dev->if_port = map->port; + + /* Link Down: the timer will bring it up */ + netif_carrier_off(dev); + + /* set Speed to 10Mbps, Half Duplex */ + control = mdio_read(dev, aup->phy_addr, MII_CONTROL); + printk("read control %x\n", control); + control &= ~(MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_FDX); + + /* disable auto negotiation and force 10M/HD mode*/ + mdio_write(dev, aup->phy_addr, MII_CONTROL, control); + break; + + case IF_PORT_100BASET: /* 100BaseT */ + case IF_PORT_100BASETX: /* 100BaseTx */ + printk("100 base T/TX\n"); + dev->if_port = map->port; + + /* Link Down: the timer will bring it up */ + netif_carrier_off(dev); + + /* set Speed to 100Mbps, Half Duplex */ + /* disable auto negotiation and enable 100MBit Mode */ + control = mdio_read(dev, aup->phy_addr, MII_CONTROL); + printk("read control %x\n", control); + control &= ~(MII_CNTL_AUTO | MII_CNTL_FDX); + control |= MII_CNTL_F100; + mdio_write(dev, aup->phy_addr, MII_CONTROL, control); + break; + + case IF_PORT_100BASEFX: /* 100BaseFx */ + printk("100 Base FX\n"); + dev->if_port = map->port; + + /* Link Down: the timer will bring it up */ + netif_carrier_off(dev); + + /* set Speed to 100Mbps, Full Duplex */ + /* disable auto negotiation and enable 100MBit Mode */ + control = mdio_read(dev, aup->phy_addr, MII_CONTROL); + control &= ~MII_CNTL_AUTO; + control |= MII_CNTL_F100 | MII_CNTL_FDX; + mdio_write(dev, aup->phy_addr, MII_CONTROL, control); + break; + case IF_PORT_10BASE2: /* 10Base2 */ + case IF_PORT_AUI: /* AUI */ + /* These Modes are not supported (are they?)*/ + printk(KERN_INFO "Not supported"); + return -EOPNOTSUPP; + break; + + default: + printk("Invalid"); + return -EINVAL; + } + return 0; +} + +static struct net_device_stats *au1000_get_stats(struct net_device *dev) +{ + struct au1000_private *aup = (struct au1000_private *) dev->priv; + + if (au1000_debug > 4) + printk("%s: au1000_get_stats: dev=%p\n", dev->name, dev); + + if (netif_device_present(dev)) { + return &aup->stats; + } + return 0; +} diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h new file mode 100644 index 000000000..fcd90c397 --- /dev/null +++ b/drivers/net/au1000_eth.h @@ -0,0 +1,223 @@ +/* + * + * Alchemy Semi Au1000 ethernet driver include file + * + * Author: Pete Popov + * + * Copyright 2001 MontaVista Software Inc. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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 NUM_INTERFACES 2 +#define MAC_IOSIZE 0x10000 +#define NUM_RX_DMA 4 /* Au1000 has 4 rx hardware descriptors */ +#define NUM_TX_DMA 4 /* Au1000 has 4 tx hardware descriptors */ + +#define NUM_RX_BUFFS 4 +#define NUM_TX_BUFFS 4 +#define MAX_BUF_SIZE 2048 + +#define ETH_TX_TIMEOUT HZ/4 +#define MAC_MIN_PKT_SIZE 64 + +#ifdef CONFIG_MIPS_PB1000 +#define PHY_ADDRESS 0 +#define PHY_CONTROL_DEFAULT 0x3000 +#define PHY_CONTROL_REG_ADDR 0 +#endif + +#define MULTICAST_FILTER_LIMIT 64 + +/* FIXME + * The PHY defines should be in a separate file. + */ + +/* MII register offsets */ +#define MII_CONTROL 0x0000 +#define MII_STATUS 0x0001 +#define MII_PHY_ID0 0x0002 +#define MII_PHY_ID1 0x0003 +#define MII_ANADV 0x0004 +#define MII_ANLPAR 0x0005 +#define MII_AEXP 0x0006 +#define MII_ANEXT 0x0007 +#define MII_AUX_CNTRL 0x18 + +/* mii registers specific to AMD 79C901 */ +#define MII_STATUS_SUMMARY = 0x0018 + +/* MII Control register bit definitions. */ +#define MII_CNTL_FDX 0x0100 +#define MII_CNTL_RST_AUTO 0x0200 +#define MII_CNTL_ISOLATE 0x0400 +#define MII_CNTL_PWRDWN 0x0800 +#define MII_CNTL_AUTO 0x1000 +#define MII_CNTL_F100 0x2000 +#define MII_CNTL_LPBK 0x4000 +#define MII_CNTL_RESET 0x8000 + +/* MII Status register bit */ +#define MII_STAT_EXT 0x0001 +#define MII_STAT_JAB 0x0002 +#define MII_STAT_LINK 0x0004 +#define MII_STAT_CAN_AUTO 0x0008 +#define MII_STAT_FAULT 0x0010 +#define MII_STAT_AUTO_DONE 0x0020 +#define MII_STAT_CAN_T 0x0800 +#define MII_STAT_CAN_T_FDX 0x1000 +#define MII_STAT_CAN_TX 0x2000 +#define MII_STAT_CAN_TX_FDX 0x4000 +#define MII_STAT_CAN_T4 0x8000 + + +#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */ +#define MII_ID1_MODEL 0x03F0 /* model number */ +#define MII_ID1_REV 0x000F /* model number */ + +/* MII NWAY Register Bits ... + valid for the ANAR (Auto-Negotiation Advertisement) and + ANLPAR (Auto-Negotiation Link Partner) registers */ +#define MII_NWAY_NODE_SEL 0x001f +#define MII_NWAY_CSMA_CD 0x0001 +#define MII_NWAY_T 0x0020 +#define MII_NWAY_T_FDX 0x0040 +#define MII_NWAY_TX 0x0080 +#define MII_NWAY_TX_FDX 0x0100 +#define MII_NWAY_T4 0x0200 +#define MII_NWAY_PAUSE 0x0400 +#define MII_NWAY_RF 0x2000 /* Remote Fault */ +#define MII_NWAY_ACK 0x4000 /* Remote Acknowledge */ +#define MII_NWAY_NP 0x8000 /* Next Page (Enable) */ + +/* mii stsout register bits */ +#define MII_STSOUT_LINK_FAIL 0x4000 +#define MII_STSOUT_SPD 0x0080 +#define MII_STSOUT_DPLX 0x0040 + +/* mii stsics register bits */ +#define MII_STSICS_SPD 0x8000 +#define MII_STSICS_DPLX 0x4000 +#define MII_STSICS_LINKSTS 0x0001 + +/* mii stssum register bits */ +#define MII_STSSUM_LINK 0x0008 +#define MII_STSSUM_DPLX 0x0004 +#define MII_STSSUM_AUTO 0x0002 +#define MII_STSSUM_SPD 0x0001 + +/* Auxilliary Control/Status Register */ +#define MII_AUX_FDX 0x0001 +#define MII_AUX_100 0x0002 +#define MII_AUX_F100 0x0004 +#define MII_AUX_ANEG 0x0008 + +typedef struct mii_phy { + struct mii_phy * next; + struct mii_chip_info * chip_info; + int phy_addr; + u16 status; +} mii_phy_t; + +struct phy_ops { + int (*phy_init) (struct net_device *, int); + int (*phy_reset) (struct net_device *, int); + int (*phy_status) (struct net_device *, int, int *, int *); +}; + +/* + * Data Buffer Descriptor. Data buffers must be aligned on 32 byte + * boundary for both, receive and transmit. + */ +typedef struct db_dest { + struct db_dest *pnext; + volatile u32 *vaddr; + dma_addr_t dma_addr; +} db_dest_t; + +/* + * The transmit and receive descriptors are memory + * mapped registers. + */ +typedef struct tx_dma { + u32 status; + u32 buff_stat; + u32 len; + u32 pad; +} tx_dma_t; + +typedef struct rx_dma { + u32 status; + u32 buff_stat; + u32 pad[2]; +} rx_dma_t; + + +/* + * MAC control registers, memory mapped. + */ +typedef struct mac_reg { + u32 control; + u32 mac_addr_high; + u32 mac_addr_low; + u32 multi_hash_high; + u32 multi_hash_low; + u32 mii_control; + u32 mii_data; + u32 flow_control; + u32 vlan1_tag; + u32 vlan2_tag; +} mac_reg_t; + + +struct au1000_private { + + db_dest_t *pDBfree; + db_dest_t db[NUM_RX_BUFFS+NUM_TX_BUFFS]; + volatile rx_dma_t *rx_dma_ring[NUM_RX_DMA]; + volatile tx_dma_t *tx_dma_ring[NUM_TX_DMA]; + db_dest_t *rx_db_inuse[NUM_RX_DMA]; + db_dest_t *tx_db_inuse[NUM_TX_DMA]; + u32 rx_head; + u32 tx_head; + u32 tx_tail; + u32 tx_full; + + mii_phy_t *mii; + struct phy_ops *phy_ops; + + /* These variables are just for quick access to certain regs addresses. */ + volatile mac_reg_t *mac; /* mac registers */ + volatile u32 *enable; /* address of MAC Enable Register */ + + u32 vaddr; /* virtual address of rx/tx buffers */ + dma_addr_t dma_addr; /* dma address of rx/tx buffers */ + + u8 *hash_table; + u32 hash_mode; + u32 intr_work_done; /* number of Rx and Tx pkts processed in the isr */ + u32 phy_addr; /* PHY address */ + u32 options; /* User-settable misc. driver options. */ + u32 drv_flags; + struct net_device_stats stats; + struct timer_list timer; + spinlock_t lock; /* Serialise access to device */ +}; diff --git a/include/asm-mips/au1000.h b/include/asm-mips/au1000.h new file mode 100644 index 000000000..39cdb61c2 --- /dev/null +++ b/include/asm-mips/au1000.h @@ -0,0 +1,635 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Include file for Alchemy Semiconductor's Au1000 CPU. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or support@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _AU1000_H_ +#define _AU1000_H_ + +/* SDRAM Controller */ +#define CS_MODE_0 0x14000000 +#define CS_MODE_1 0x14000004 +#define CS_MODE_2 0x14000008 + +#define CS_CONFIG_0 0x1400000C +#define CS_CONFIG_1 0x14000010 +#define CS_CONFIG_2 0x14000014 + +#define REFRESH_CONFIG 0x14000018 +#define PRECHARGE_CMD 0x1400001C +#define AUTO_REFRESH_CMD 0x14000020 + +#define WRITE_EXTERN_0 0x14000024 +#define WRITE_EXTERN_1 0x14000028 +#define WRITE_EXTERN_2 0x1400002C + +#define SDRAM_SLEEP 0x14000030 +#define TOGGLE_CKE 0x14000034 + +/* Static Bus Controller */ +#define STATIC_CONFIG_0 0x14001000 +#define STATIC_TIMING_0 0x14001004 +#define STATIC_ADDRESS_0 0x14001008 + +#define STATIC_CONFIG_1 0x14001010 +#define STATIC_TIMING_1 0x14001014 +#define STATIC_ADDRESS_1 0x14001018 + +#define STATIC_CONFIG_2 0x14001020 +#define STATIC_TIMING_2 0x14001024 +#define STATIC_ADDRESS_2 0x14001028 + +#define STATIC_CONFIG_3 0x14001030 +#define STATIC_TIMING_3 0x14001034 +#define STATIC_ADDRESS_3 0x14001038 + +/* DMA Controller 0 */ +#define DMA0_MODE_SET 0x14002000 +#define DMA0_MODE_CLEAR 0x14002004 +#define DMA0_PERIPHERAL_ADDR 0x14002008 +#define DMA0_BUFFER0_START 0x1400200C +#define DMA0_BUFFER0_COUNT 0x14002010 +#define DMA0_BUFFER1_START 0x14002014 +#define DMA0_BUFFER1_COUNT 0x14002018 + +/* DMA Controller 1 */ +#define DMA1_MODE_SET 0x14002100 +#define DMA1_MODE_CLEAR 0x14002104 +#define DMA1_PERIPHERAL_ADDR 0x14002108 +#define DMA1_BUFFER0_START 0x1400210C +#define DMA1_BUFFER0_COUNT 0x14002110 +#define DMA1_BUFFER1_START 0x14002114 +#define DMA1_BUFFER1_COUNT 0x14002118 + +/* DMA Controller 2 */ +#define DMA2_MODE_SET 0x14002200 +#define DMA2_MODE_CLEAR 0x14002204 +#define DMA2_PERIPHERAL_ADDR 0x14002208 +#define DMA2_BUFFER0_START 0x1400220C +#define DMA2_BUFFER0_COUNT 0x14002210 +#define DMA2_BUFFER1_START 0x14002214 +#define DMA2_BUFFER1_COUNT 0x14002218 + +/* DMA Controller 3 */ +#define DMA3_MODE_SET 0x14002300 +#define DMA3_MODE_CLEAR 0x14002304 +#define DMA3_PERIPHERAL_ADDR 0x14002308 +#define DMA3_BUFFER0_START 0x1400230C +#define DMA3_BUFFER0_COUNT 0x14002310 +#define DMA3_BUFFER1_START 0x14002314 +#define DMA3_BUFFER1_COUNT 0x14002318 + +/* DMA Controller 4 */ +#define DMA4_MODE_SET 0x14002400 +#define DMA4_MODE_CLEAR 0x14002404 +#define DMA4_PERIPHERAL_ADDR 0x14002408 +#define DMA4_BUFFER0_START 0x1400240C +#define DMA4_BUFFER0_COUNT 0x14002410 +#define DMA4_BUFFER1_START 0x14002414 +#define DMA4_BUFFER1_COUNT 0x14002418 + +/* DMA Controller 5 */ +#define DMA5_MODE_SET 0x14002500 +#define DMA5_MODE_CLEAR 0x14002504 +#define DMA5_PERIPHERAL_ADDR 0x14002508 +#define DMA5_BUFFER0_START 0x1400250C +#define DMA5_BUFFER0_COUNT 0x14002510 +#define DMA5_BUFFER1_START 0x14002514 +#define DMA5_BUFFER1_COUNT 0x14002518 + +/* DMA Controller 6 */ +#define DMA6_MODE_SET 0x14002600 +#define DMA6_MODE_CLEAR 0x14002604 +#define DMA6_PERIPHERAL_ADDR 0x14002608 +#define DMA6_BUFFER0_START 0x1400260C +#define DMA6_BUFFER0_COUNT 0x14002610 +#define DMA6_BUFFER1_START 0x14002614 +#define DMA6_BUFFER1_COUNT 0x14002618 + +/* DMA Controller 7 */ +#define DMA7_MODE_SET 0x14002700 +#define DMA7_MODE_CLEAR 0x14002704 +#define DMA7_PERIPHERAL_ADDR 0x14002708 +#define DMA7_BUFFER0_START 0x1400270C +#define DMA7_BUFFER0_COUNT 0x14002710 +#define DMA7_BUFFER1_START 0x14002714 +#define DMA7_BUFFER1_COUNT 0x14002718 + +/* Interrupt Controller 0 */ +#define INTC0_CONFIG0_READ 0x10400040 +#define INTC0_CONFIG0_SET 0x10400040 +#define INTC0_CONFIG0_CLEAR 0x10400044 + +#define INTC0_CONFIG1_READ 0x10400048 +#define INTC0_CONFIG1_SET 0x10400048 +#define INTC0_CONFIG1_CLEAR 0x1040004C + +#define INTC0_CONFIG2_READ 0x10400050 +#define INTC0_CONFIG2_SET 0x10400050 +#define INTC0_CONFIG2_CLEAR 0x10400054 + +#define INTC0_REQ0_INT 0x10400054 +#define INTC0_SOURCE_READ 0x10400058 +#define INTC0_SOURCE_SET 0x10400058 +#define INTC0_SOURCE_CLEAR 0x1040005C +#define INTC0_REQ1_INT 0x1040005C + +#define INTC0_ASSIGN_REQ_READ 0x10400060 +#define INTC0_ASSIGN_REQ_SET 0x10400060 +#define INTC0_ASSIGN_REQ_CLEAR 0x10400064 + +#define INTC0_WAKEUP_READ 0x10400068 +#define INTC0_WAKEUP_SET 0x10400068 +#define INTC0_WAKEUP_CLEAR 0x1040006C + +#define INTC0_MASK_READ 0x10400070 +#define INTC0_MASK_SET 0x10400070 +#define INTC0_MASK_CLEAR 0x10400074 + +#define INTC0_R_EDGE_DETECT 0x10400078 +#define INTC0_R_EDGE_DETECT_CLEAR 0x10400078 +#define INTC0_F_EDGE_DETECT_CLEAR 0x1040007C + +#define INTC0_TEST_BIT 0x10400080 + +/* Interrupt Controller 1 */ +#define INTC1_CONFIG0_READ 0x11800040 +#define INTC1_CONFIG0_SET 0x11800040 +#define INTC1_CONFIG0_CLEAR 0x11800044 + +#define INTC1_CONFIG1_READ 0x11800048 +#define INTC1_CONFIG1_SET 0x11800048 +#define INTC1_CONFIG1_CLEAR 0x1180004C + +#define INTC1_CONFIG2_READ 0x11800050 +#define INTC1_CONFIG2_SET 0x11800050 +#define INTC1_CONFIG2_CLEAR 0x11800054 + +#define INTC1_REQ0_INT 0x11800054 +#define INTC1_SOURCE_READ 0x11800058 +#define INTC1_SOURCE_SET 0x11800058 +#define INTC1_SOURCE_CLEAR 0x1180005C +#define INTC1_REQ1_INT 0x1180005C + +#define INTC1_ASSIGN_REQ_READ 0x11800060 +#define INTC1_ASSIGN_REQ_SET 0x11800060 +#define INTC1_ASSIGN_REQ_CLEAR 0x11800064 + +#define INTC1_WAKEUP_READ 0x11800068 +#define INTC1_WAKEUP_SET 0x11800068 +#define INTC1_WAKEUP_CLEAR 0x1180006C + +#define INTC1_MASK_READ 0x11800070 +#define INTC1_MASK_SET 0x11800070 +#define INTC1_MASK_CLEAR 0x11800074 + +#define INTC1_R_EDGE_DETECT 0x11800078 +#define INTC1_R_EDGE_DETECT_CLEAR 0x11800078 +#define INTC1_F_EDGE_DETECT_CLEAR 0x1180007C + +#define INTC1_TEST_BIT 0x11800080 + +/* Interrupt Configuration Modes */ +#define INTC_INT_DISABLED 0 +#define INTC_INT_RISE_EDGE 0x1 +#define INTC_INT_FALL_EDGE 0x2 +#define INTC_INT_RISE_AND_FALL_EDGE 0x3 +#define INTC_INT_HIGH_LEVEL 0x5 +#define INTC_INT_LOW_LEVEL 0x6 +#define INTC_INT_HIGH_AND_LOW_LEVEL 0x7 + +/* Interrupt Numbers */ +#define AU1000_UART0_INT 0 +#define AU1000_UART1_INT 1 +#define AU1000_UART2_INT 2 +#define AU1000_UART3_INT 3 +#define AU1000_SSI0_INT 4 +#define AU1000_SSI1_INT 5 +#define AU1000_DMA0_INT 6 +#define AU1000_DMA1_INT 7 +#define AU1000_DMA2_INT 8 +#define AU1000_DMA3_INT 9 +#define AU1000_DMA4_INT 10 +#define AU1000_DMA5_INT 11 +#define AU1000_DMA6_INT 12 +#define AU1000_DMA7_INT 13 +#define AU1000_PC0_INT 14 +#define AU1000_PC0_MATCH0_INT 15 +#define AU1000_PC0_MATCH1_INT 16 +#define AU1000_PC0_MATCH2_INT 17 +#define AU1000_PC1_INT 18 +#define AU1000_PC1_MATCH0_INT 19 +#define AU1000_PC1_MATCH1_INT 20 +#define AU1000_PC1_MATCH2_INT 21 +#define AU1000_IRDA_TX_INT 22 +#define AU1000_IRDA_RX_INT 23 +#define AU1000_USB_DEV_REQ_INT 24 +#define AU1000_USB_DEV_SUS_INT 25 +#define AU1000_USB_HOST_INT 26 +#define AU1000_ACSYNC_INT 27 +#define AU1000_MAC0_DMA_INT 28 +#define AU1000_MAC1_DMA_INT 29 +#define AU1000_ETH0_IRQ AU1000_MAC0_DMA_INT +#define AU1000_ETH1_IRQ AU1000_MAC1_DMA_INT +#define AU1000_I2S_UO_INT 30 +#define AU1000_AC97_INT 31 +#define AU1000_LAST_INTC0_INT AU1000_AC97_INT +#define AU1000_GPIO_0 32 +#define AU1000_GPIO_1 33 +#define AU1000_GPIO_2 34 +#define AU1000_GPIO_3 35 +#define AU1000_GPIO_4 36 +#define AU1000_GPIO_5 37 +#define AU1000_GPIO_6 38 +#define AU1000_GPIO_7 39 +#define AU1000_GPIO_8 40 +#define AU1000_GPIO_9 41 +#define AU1000_GPIO_10 42 +#define AU1000_GPIO_11 43 +#define AU1000_GPIO_12 44 +#define AU1000_GPIO_13 45 +#define AU1000_GPIO_14 46 +#define AU1000_GPIO_15 47 +#define AU1000_GPIO_16 48 +#define AU1000_GPIO_17 49 +#define AU1000_GPIO_18 50 +#define AU1000_GPIO_19 51 +#define AU1000_GPIO_20 52 +#define AU1000_GPIO_21 53 +#define AU1000_GPIO_22 54 +#define AU1000_GPIO_23 55 +#define AU1000_GPIO_24 56 +#define AU1000_GPIO_25 57 +#define AU1000_GPIO_26 58 +#define AU1000_GPIO_27 59 +#define AU1000_GPIO_28 60 +#define AU1000_GPIO_29 61 +#define AU1000_GPIO_30 62 +#define AU1000_GPIO_31 63 + +/* Programmable Counters 0 and 1 */ +#define PC_BASE 0x11900000 +#define PC_COUNTER_CNTRL (PC_BASE + 0x14) + #define PC_CNTRL_E1S (1<<23) + #define PC_CNTRL_T1S (1<<20) + #define PC_CNTRL_M21 (1<<19) + #define PC_CNTRL_M11 (1<<18) + #define PC_CNTRL_M01 (1<<17) + #define PC_CNTRL_C1S (1<<16) + #define PC_CNTRL_BP (1<<14) + #define PC_CNTRL_EN1 (1<<13) + #define PC_CNTRL_BT1 (1<<12) + #define PC_CNTRL_EN0 (1<<11) + #define PC_CNTRL_BT0 (1<<10) + #define PC_CNTRL_E0 (1<<8) + #define PC_CNTRL_E0S (1<<7) + #define PC_CNTRL_32S (1<<5) + #define PC_CNTRL_T0S (1<<4) + #define PC_CNTRL_M20 (1<<3) + #define PC_CNTRL_M10 (1<<2) + #define PC_CNTRL_M00 (1<<1) + #define PC_CNTRL_C0S (1<<0) + +/* Programmable Counter 0 Registers */ +#define PC0_TRIM (PC_BASE + 0) +#define PC0_COUNTER_WRITE (PC_BASE + 4) +#define PC0_MATCH0 (PC_BASE + 8) +#define PC0_MATCH1 (PC_BASE + 0xC) +#define PC0_MATCH2 (PC_BASE + 0x10) +#define PC0_COUNTER_READ (PC_BASE + 0x40) + +/* Programmable Counter 1 Registers */ +#define PC1_TRIM (PC_BASE + 0x44) +#define PC1_COUNTER_WRITE (PC_BASE + 0x48) +#define PC1_MATCH0 (PC_BASE + 0x4C) +#define PC1_MATCH1 (PC_BASE + 0x50) +#define PC1_MATCH2 (PC_BASE + 0x54) +#define PC1_COUNTER_READ (PC_BASE + 0x58) + + +/* I2S Controller */ +#define I2S_DATA 0x11000000 +#define I2S_CONFIG_STATUS 0x11000001 +#define I2S_CONTROL 0x11000002 + +/* Ethernet Controllers */ +#define AU1000_ETH0_BASE 0x10500000 +#define AU1000_ETH1_BASE 0x10510000 + +/* 4 byte offsets from AU1000_ETH_BASE */ +#define MAC_CONTROL 0x0 + #define MAC_RX_ENABLE (1<<2) + #define MAC_TX_ENABLE (1<<3) + #define MAC_DEF_CHECK (1<<5) + #define MAC_SET_BL(X) (((X)&0x3)<<6) + #define MAC_AUTO_PAD (1<<8) + #define MAC_DISABLE_RETRY (1<<10) + #define MAC_DISABLE_BCAST (1<<11) + #define MAC_LATE_COL (1<<12) + #define MAC_HASH_MODE (1<<13) + #define MAC_HASH_ONLY (1<<15) + #define MAC_PASS_ALL (1<<16) + #define MAC_INVERSE_FILTER (1<<17) + #define MAC_PROMISCUOUS (1<<18) + #define MAC_PASS_ALL_MULTI (1<<19) + #define MAC_FULL_DUPLEX (1<<20) + #define MAC_NORMAL_MODE 0 + #define MAC_INT_LOOPBACK (1<<21) + #define MAC_EXT_LOOPBACK (1<<22) + #define MAC_DISABLE_RX_OWN (1<<23) + #define MAC_BIG_ENDIAN (1<<30) + #define MAC_RX_ALL (1<<31) +#define MAC_ADDRESS_HIGH 0x4 +#define MAC_ADDRESS_LOW 0x8 +#define MAC_MCAST_HIGH 0xC +#define MAC_MCAST_LOW 0x10 +#define MAC_MII_CNTRL 0x14 + #define MAC_MII_BUSY (1<<0) + #define MAC_MII_READ 0 + #define MAC_MII_WRITE (1<<1) + #define MAC_SET_MII_SELECT_REG(X) (((X)&0x1f)<<6) + #define MAC_SET_MII_SELECT_PHY(X) (((X)&0x1f)<<11) +#define MAC_MII_DATA 0x18 +#define MAC_FLOW_CNTRL 0x1C + #define MAC_FLOW_CNTRL_BUSY (1<<0) + #define MAC_FLOW_CNTRL_ENABLE (1<<1) + #define MAC_PASS_CONTROL (1<<2) + #define MAC_SET_PAUSE(X) (((X)&0xffff)<<16) +#define MAC_VLAN1_TAG 0x20 +#define MAC_VLAN2_TAG 0x24 + +/* Ethernet Controller Enable */ +#define MAC0_ENABLE 0x10520000 +#define MAC1_ENABLE 0x10520004 + #define MAC_EN_CLOCK_ENABLE (1<<0) + #define MAC_EN_RESET0 (1<<1) + #define MAC_EN_TOSS (1<<2) + #define MAC_EN_CACHEABLE (1<<3) + #define MAC_EN_RESET1 (1<<4) + #define MAC_EN_RESET2 (1<<5) + #define MAC_DMA_RESET (1<<6) + +/* Ethernet Controller DMA Channels */ + +#define MAC0_TX_DMA_ADDR 0x14004000 +#define MAC1_TX_DMA_ADDR 0x14004200 +/* offsets from MAC_TX_RING_ADDR address */ +#define MAC_TX_BUFF0_STATUS 0x0 + #define TX_FRAME_ABORTED (1<<0) + #define TX_JAB_TIMEOUT (1<<1) + #define TX_NO_CARRIER (1<<2) + #define TX_LOSS_CARRIER (1<<3) + #define TX_EXC_DEF (1<<4) + #define TX_LATE_COLL_ABORT (1<<5) + #define TX_EXC_COLL (1<<6) + #define TX_UNDERRUN (1<<7) + #define TX_DEFERRED (1<<8) + #define TX_LATE_COLL (1<<9) + #define TX_COLL_CNT_MASK (0xF<<10) + #define TX_PKT_RETRY (1<<31) +#define MAC_TX_BUFF0_ADDR 0x4 + #define TX_DMA_ENABLE (1<<0) + #define TX_T_DONE (1<<1) + #define TX_GET_DMA_BUFFER(X) (((X)>>2)&0x3) +#define MAC_TX_BUFF0_LEN 0x8 +#define MAC_TX_BUFF1_STATUS 0x10 +#define MAC_TX_BUFF1_ADDR 0x14 +#define MAC_TX_BUFF1_LEN 0x18 +#define MAC_TX_BUFF2_STATUS 0x20 +#define MAC_TX_BUFF2_ADDR 0x24 +#define MAC_TX_BUFF2_LEN 0x28 +#define MAC_TX_BUFF3_STATUS 0x30 +#define MAC_TX_BUFF3_ADDR 0x34 +#define MAC_TX_BUFF3_LEN 0x38 + +#define MAC0_RX_DMA_ADDR 0x14004100 +#define MAC1_RX_DMA_ADDR 0x14004300 +/* offsets from MAC_RX_RING_ADDR */ +#define MAC_RX_BUFF0_STATUS 0x0 + #define RX_FRAME_LEN_MASK 0x3fff + #define RX_WDOG_TIMER (1<<14) + #define RX_RUNT (1<<15) + #define RX_OVERLEN (1<<16) + #define RX_COLL (1<<17) + #define RX_ETHER (1<<18) + #define RX_MII_ERROR (1<<19) + #define RX_DRIBBLING (1<<20) + #define RX_CRC_ERROR (1<<21) + #define RX_VLAN1 (1<<22) + #define RX_VLAN2 (1<<23) + #define RX_LEN_ERROR (1<<24) + #define RX_CNTRL_FRAME (1<<25) + #define RX_U_CNTRL_FRAME (1<<26) + #define RX_MCAST_FRAME (1<<27) + #define RX_BCAST_FRAME (1<<28) + #define RX_FILTER_FAIL (1<<29) + #define RX_PACKET_FILTER (1<<30) + #define RX_MISSED_FRAME (1<<31) + + #define RX_ERROR (RX_WDOG_TIMER | RX_RUNT | RX_OVERLEN | \ + RX_COLL | RX_MII_ERROR | RX_CRC_ERROR | \ + RX_LEN_ERROR | RX_U_CNTRL_FRAME | RX_MISSED_FRAME) +#define MAC_RX_BUFF0_ADDR 0x4 + #define RX_DMA_ENABLE (1<<0) + #define RX_T_DONE (1<<1) + #define RX_GET_DMA_BUFFER(X) (((X)>>2)&0x3) + #define RX_SET_BUFF_ADDR(X) ((X)&0xffffffc0) +#define MAC_RX_BUFF1_STATUS 0x10 +#define MAC_RX_BUFF1_ADDR 0x14 +#define MAC_RX_BUFF2_STATUS 0x20 +#define MAC_RX_BUFF2_ADDR 0x24 +#define MAC_RX_BUFF3_STATUS 0x30 +#define MAC_RX_BUFF3_ADDR 0x34 + + +/* UARTS 0-3 */ +#define UART0_ADDR 0x11100000 +#define UART1_ADDR 0x11200000 +#define UART2_ADDR 0x11300000 +#define UART3_ADDR 0x11400000 + +#define UART_RX 0 /* Receive buffer */ +#define UART_TX 4 /* Transmit buffer */ +#define UART_IER 8 /* Interrupt Enable Register */ +#define UART_IIR 0xC /* Interrupt ID Register */ +#define UART_FCR 0x10 /* FIFO Control Register */ +#define UART_LCR 0x14 /* Line Control Register */ +#define UART_MCR 0x18 /* Modem Control Register */ +#define UART_LSR 0x1C /* Line Status Register */ +#define UART_MSR 0x20 /* Modem Status Register */ +#define UART_CLK 0x28 /* Baud Rat4e Clock Divider */ +#define UART_MOD_CNTRL 0x100 /* Module Control */ + +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ +#define UART_FCR_TRIGGER_MASK 0xF0 /* Mask for the FIFO trigger range */ +#define UART_FCR_R_TRIGGER_1 0x00 /* Mask for receive trigger set at 1 */ +#define UART_FCR_R_TRIGGER_4 0x40 /* Mask for receive trigger set at 4 */ +#define UART_FCR_R_TRIGGER_8 0x80 /* Mask for receive trigger set at 8 */ +#define UART_FCR_R_TRIGGER_14 0xA0 /* Mask for receive trigger set at 14 */ +#define UART_FCR_T_TRIGGER_0 0x00 /* Mask for transmit trigger set at 0 */ +#define UART_FCR_T_TRIGGER_4 0x10 /* Mask for transmit trigger set at 4 */ +#define UART_FCR_T_TRIGGER_8 0x20 /* Mask for transmit trigger set at 8 */ +#define UART_FCR_T_TRIGGER_12 0x30 /* Mask for transmit trigger set at 12 */ + +/* + * These are the definitions for the Line Control Register + */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ + +/* + * These are the definitions for the Interrupt Identification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +/* + * These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ + +/* + * These are the definitions for the Modem Control Register + */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +/* + * These are the definitions for the Modem Status Register + */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ + + + +/* SSIO */ +#define SSI0_STATUS 0x11600000 +#define SSI0_INT 0x11600004 +#define SSI0_INT_ENABLE 0x11600008 +#define SSI0_CONFIG 0x11600020 +#define SSI0_ADATA 0x11600024 +#define SSI0_CLKDIV 0x11600028 +#define SSI0_CONTROL 0x11600100 + +/* SSI1 */ +#define SSI1_STATUS 0x11680000 +#define SSI1_INT 0x11680004 +#define SSI1_INT_ENABLE 0x11680008 +#define SSI1_CONFIG 0x11680020 +#define SSI1_ADATA 0x11680024 +#define SSI1_CLKDIV 0x11680028 +#define SSI1_CONTROL 0x11680100 + +/* IrDA Controller */ +#define IR_RING_PTR_STATUS 0x11500000 +#define IR_RING_BASE_ADDR_H 0x11500004 +#define IR_RING_BASE_ADDR_L 0x11500008 +#define IR_RING_SIZE 0x1150000C +#define IR_RING_PROMPT 0x11500010 +#define IR_RING_ADDR_CMPR 0x11500014 +#define IR_CONFIG_1 0x11500020 +#define IR_SIR_FLAGS 0x11500024 +#define IR_ENABLE 0x11500028 +#define IR_READ_PHY_CONFIG 0x1150002C +#define IR_WRITE_PHY_CONFIG 0x11500030 +#define IR_MAX_PKT_LEN 0x11500034 +#define IR_RX_BYTE_CNT 0x11500038 +#define IR_CONFIG_2 0x1150003C +#define IR_INTERFACE_CONFIG 0x11500040 + +/* GPIO */ +#define TSTATE_STATE_READ 0x11900100 +#define TSTATE_STATE_SET 0x11900100 +#define OUTPUT_STATE_READ 0x11900108 +#define OUTPUT_STATE_SET 0x11900108 +#define OUTPUT_STATE_CLEAR 0x1190010C +#define PIN_STATE 0x11900110 + +/* Power Management */ +#define PM_SCRATCH_0 0x11900018 +#define PM_SCRATCH_1 0x1190001C +#define PM_WAKEUP_SOURCE_MASK 0x11900034 +#define PM_ENDIANESS 0x11900038 +#define PM_POWERUP_CONTROL 0x1190003C +#define PM_WAKEUP_CAUSE 0x1190005C +#define PM_SLEEP_POWER 0x11900078 +#define PM_SLEEP 0x1190007C + +/* Clock Controller */ +#define FQ_CNTRL_1 0x11900020 +#define FQ_CNTRL_2 0x11900024 +#define CLOCK_SOURCE_CNTRL 0x11900028 +#define CPU_PLL_CNTRL 0x11900060 +#define AUX_PLL_CNTRL 0x11900064 + +/* AC97 Controller */ +#define AC97_CONFIG 0x10000000 +#define AC97_STATUS 0x10000004 +#define AC97_DATA 0x10000008 +#define AC97_CMD 0x1000000C +#define AC97_CNTRL 0x10000010 + +#endif diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h index 966cf0932..2bb64bfc5 100644 --- a/include/asm-mips/bootinfo.h +++ b/include/asm-mips/bootinfo.h @@ -30,10 +30,11 @@ #define MACH_GROUP_GLOBESPAN 15 /* Globespan PVR Referrence Board */ #define MACH_GROUP_SIBYTE 16 /* Sibyte Eval Boards */ #define MACH_GROUP_TOSHIBA 17 /* Toshiba Reference Systems TSBREF */ +#define MACH_GROUP_ALCHEMY 18 /* Alchemy Semi Eval Boards*/ #define GROUP_NAMES { "unknown", "Jazz", "Digital", "ARC", "SNI", "ACN", \ "SGI", "Cobalt", "NEC DDB", "Baget", "Cosine", "Galileo", "Momentum", \ - "ITE", "Philips", "Globepspan", "SiByte", "Toshiba" } + "ITE", "Philips", "Globepspan", "SiByte", "Toshiba", "Alchemy" } /* * Valid machtype values for group unknown (low order halfword of mips_machtype) @@ -185,6 +186,13 @@ #define GROUP_TOSHIBA_NAMES { "Pallas", "TopasCE", "JMR" } +/* + * Valid machtype for group Alchemy + */ +#define MACH_PB1000 0 /* Au1000-based eval board */ + +#define GROUP_ALCHEMY_NAMES { "PB1000" } /* the actual board name */ + /* * Valid cputype values */ @@ -225,14 +233,15 @@ #define CPU_TX3912 34 #define CPU_TX3922 35 #define CPU_TX3927 36 -#define CPU_LAST 36 +#define CPU_AU1000 37 +#define CPU_LAST 37 #define CPU_NAMES { "unknown", "R2000", "R3000", "R3000A", "R3041", "R3051", \ "R3052", "R3081", "R3081E", "R4000PC", "R4000SC", "R4000MC", \ "R4200", "R4400PC", "R4400SC", "R4400MC", "R4600", "R6000", \ "R6000A", "R8000", "R10000", "R4300", "R4650", "R4700", "R5000", \ "R5000A", "R4640", "Nevada", "RM7000", "R5432", "MIPS 4Kc", \ - "MIPS 5Kc", "R4310", "SiByte SB1", "TX3912", "TX3922", "TX3927" } + "MIPS 5Kc", "R4310", "SiByte SB1", "TX3912", "TX3922", "TX3927", "Au1000" } #define COMMAND_LINE_SIZE 256 diff --git a/include/asm-mips/cpu.h b/include/asm-mips/cpu.h index adfef1f9c..439928ad4 100644 --- a/include/asm-mips/cpu.h +++ b/include/asm-mips/cpu.h @@ -25,6 +25,7 @@ #define PRID_COMP_LEGACY 0x000000 #define PRID_COMP_MIPS 0x010000 +#define PRID_COMP_ALCHEMY 0x030000 /* * Don't know who should be here...QED and Sandcraft, maybe? */ @@ -36,6 +37,7 @@ * be examined. These are valid when 23:16 == PRID_COMP_LEGACY */ #define PRID_IMP_R2000 0x0100 +#define PRID_IMP_AU1000 0x0100 #define PRID_IMP_R3000 0x0200 /* Same as R2000A */ #define PRID_IMP_R6000 0x0300 /* Same as R3000A */ #define PRID_IMP_R4000 0x0400 diff --git a/include/asm-mips/serial.h b/include/asm-mips/serial.h index b5b8c6ff2..6d5634aa3 100644 --- a/include/asm-mips/serial.h +++ b/include/asm-mips/serial.h @@ -134,6 +134,21 @@ #define IVR_SERIAL_PORT_DEFNS #endif +#ifdef CONFIG_AU1000_UART +#include +#define AU1000_SERIAL_PORT_DEFNS \ + { baud_base: 0, port: UART0_ADDR, irq: AU1000_UART0_INT, \ + flags: STD_COM_FLAGS, type: 1 }, \ + { baud_base: 0, port: UART1_ADDR, irq: AU1000_UART1_INT, \ + flags: STD_COM_FLAGS, type: 1 }, \ + { baud_base: 0, port: UART2_ADDR, irq: AU1000_UART2_INT, \ + flags: STD_COM_FLAGS, type: 1 }, \ + { baud_base: 0, port: UART3_ADDR, irq: AU1000_UART3_INT, \ + flags: STD_COM_FLAGS, type: 1 }, +#else +#define AU1000_SERIAL_PORT_DEFNS +#endif + #ifdef CONFIG_HAVE_STD_PC_SERIAL_PORT #define STD_SERIAL_PORT_DEFNS \ /* UART CLK PORT IRQ FLAGS */ \ @@ -240,4 +255,5 @@ STD_SERIAL_PORT_DEFNS \ EXTRA_SERIAL_PORT_DEFNS \ HUB6_SERIAL_PORT_DFNS \ - MOMENCO_OCELOT_SERIAL_PORT_DEFNS + MOMENCO_OCELOT_SERIAL_PORT_DEFNS\ + AU1000_SERIAL_PORT_DEFNS -- cgit v1.2.3