diff -Nawur linuxppc_2_4/arch/ppc/config.in linuxppc_2_4-virtex/arch/ppc/config.in --- linuxppc_2_4/arch/ppc/config.in 2005-05-18 10:13:32.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/config.in 2005-06-29 15:08:17.000000000 +0200 @@ -63,9 +63,18 @@ if [ "$CONFIG_40x" = "y" ]; then choice 'Machine Type' \ "Oak CONFIG_OAK \ + Virtex CONFIG_VIRTEX \ Walnut CONFIG_WALNUT" Walnut fi +if [ "$CONFIG_VIRTEX" = "y" ]; then + bool " Ignore Virtex Machine Check Exception" CONFIG_VIRTEX_MCE_IGNORE +fi + +if [ "$CONFIG_VIRTEX" = "y" ]; then + hex " Virtex interrupt controller address" CONFIG_VIRTEX_XINTC_ADDR 0x80000000 +fi + if [ "$CONFIG_44x" = "y" ]; then choice 'Machine Type' \ "Ebony CONFIG_EBONY \ @@ -181,6 +190,10 @@ define_bool CONFIG_405GP y fi + if [ "$CONFIG_VIRTEX" = "y" ]; then + define_bool CONFIG_405 y + fi + if [ "$CONFIG_EBONY" = "y" ]; then define_bool CONFIG_440GP y fi diff -Nawur linuxppc_2_4/arch/ppc/kernel/cputable.c linuxppc_2_4-virtex/arch/ppc/kernel/cputable.c --- linuxppc_2_4/arch/ppc/kernel/cputable.c 2005-05-18 10:13:33.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/kernel/cputable.c 2005-06-29 15:08:17.000000000 +0200 @@ -377,6 +377,16 @@ 32, 32, 0, /*__setup_cpu_405 */ }, + { + 0xffffe000, // masque du Processeur Version Register + 0x20010000, // valeur du Processeur Version Register + "405 Virtex", // nom du processeur + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, CPU_FTR_CAN_DOZE, // fonct. noyau + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, // fonct. utilisateur + 32, // taille du cache d'instructions + 32, // taille du cache de données + 0, // pointeur sur la fonction de configuration du processeur + }, #endif /* CONFIG_4xx */ #ifdef CONFIG_44x { /* 440GP Rev. B */ diff -Nawur linuxppc_2_4/arch/ppc/kernel/Makefile linuxppc_2_4-virtex/arch/ppc/kernel/Makefile --- linuxppc_2_4/arch/ppc/kernel/Makefile 2005-05-18 10:13:33.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/kernel/Makefile 2005-06-29 15:08:17.000000000 +0200 @@ -54,7 +54,14 @@ obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o endif obj-$(CONFIG_40x) += ppc4xx_setup.o -obj-$(CONFIG_4xx) += ocp.o ppc4xx_pic.o todc_time.o + +obj-$(CONFIG_4xx) += todc_time.o +ifeq ($(CONFIG_VIRTEX),y) +obj-$(CONFIG_40x) += virtex_pic.o +else +obj-$(CONFIG_4xx) += ocp.o ppc4xx_pic.o +endif + ifeq ($(CONFIG_PCI),y) obj-$(CONFIG_40x) += ppc405_pci.o obj-$(CONFIG_4xx) += indirect_pci.o pci_auto.o diff -Nawur linuxppc_2_4/arch/ppc/kernel/ppc4xx_setup.c linuxppc_2_4-virtex/arch/ppc/kernel/ppc4xx_setup.c --- linuxppc_2_4/arch/ppc/kernel/ppc4xx_setup.c 2005-05-18 10:13:34.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/kernel/ppc4xx_setup.c 2005-07-04 09:22:04.000000000 +0200 @@ -167,8 +167,10 @@ static void __init m4xx_map_io(void) { +#ifndef CONFIG_VIRTEX io_block_mapping(PPC4xx_ONB_IO_VADDR, PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO); +#endif #ifdef CONFIG_PCI io_block_mapping(PPC4xx_PCI_IO_VADDR, PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO); diff -Nawur linuxppc_2_4/arch/ppc/kernel/setup.c linuxppc_2_4-virtex/arch/ppc/kernel/setup.c --- linuxppc_2_4/arch/ppc/kernel/setup.c 2005-05-18 10:13:34.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/kernel/setup.c 2005-06-29 15:08:17.000000000 +0200 @@ -430,16 +430,16 @@ struct bi_record *find_bootinfo(void) { struct bi_record *rec; - extern char __bss_start[]; + extern char __bss_start; - rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); + rec = (struct bi_record *)_ALIGN((ulong)&__bss_start+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) { /* * This 0x10000 offset is a terrible hack but it will go away when * we have the bootloader handle all the relocation and * prom calls -- Cort */ - rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); + rec = (struct bi_record *)_ALIGN((ulong)&__bss_start+0x10000+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) return NULL; } diff -Nawur linuxppc_2_4/arch/ppc/kernel/virtex_pic.c linuxppc_2_4-virtex/arch/ppc/kernel/virtex_pic.c --- linuxppc_2_4/arch/ppc/kernel/virtex_pic.c 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/arch/ppc/kernel/virtex_pic.c 2005-06-29 15:08:17.000000000 +0200 @@ -0,0 +1,80 @@ +#include "linux/irq.h" /* struct hw_interrupt_type */ +#include "asm/io.h" /* out_be32(), in_be32(), ioremap() */ +#include "linux/init.h" /* __init */ + +static u32 *intc = 0; + +#define ISR ((volatile u32 *)(intc)) /* Interrupt Status Register */ +#define IPR ((volatile u32 *)(intc+1)) /* Interrupt Pending Register */ +#define IER ((volatile u32 *)(intc+2)) /* Interrupt Enable Register */ +#define IAR ((volatile u32 *)(intc+3)) /* Interrupt Acknowledge Register */ +#define SIE ((volatile u32 *)(intc+4)) /* Set Interrupt Enable bits */ +#define CIE ((volatile u32 *)(intc+5)) /* Clear Interrupt Enable bits */ +#define IVR ((volatile u32 *)(intc+6)) /* Interrupt Vector Register */ +#define MER ((volatile u32 *)(intc+7)) /* Master Enable Register */ + +struct hw_interrupt_type *ppc4xx_pic; + +#define IRQ_TO_MASK(irq) (1 << ((irq) & 0x1F)) + +static void virtex_xintc_enable(unsigned int irq) +{ + out_be32(SIE, IRQ_TO_MASK(irq)); +} + +static void virtex_xintc_disable(unsigned int irq) +{ + out_be32(CIE, IRQ_TO_MASK(irq)); +} + +static void virtex_xintc_disable_and_ack(unsigned int irq) +{ + out_be32(CIE, IRQ_TO_MASK(irq)); + out_be32(IAR, IRQ_TO_MASK(irq)); +} + +int virtex_xintc_get_irq(struct pt_regs *regs) +{ + return in_be32(IVR); +} + +static void ced(unsigned int irq) +{ + return; +} + +static void dump_intc_registers(void) +{ + printk(KERN_EMERG "ISR (0x%x) = 0x%x\n", ISR, *ISR); + printk(KERN_EMERG "IPR (0x%x) = 0x%x\n", IPR, *IPR); + printk(KERN_EMERG "IER (0x%x) = 0x%x\n", IER, *IER); + printk(KERN_EMERG "IAR (0x%x) = 0x%x\n", IAR, *IAR); + printk(KERN_EMERG "SIE (0x%x) = 0x%x\n", SIE, *SIE); + printk(KERN_EMERG "CIE (0x%x) = 0x%x\n", CIE, *CIE); + printk(KERN_EMERG "IVR (0x%x) = 0x%x\n", IVR, *IVR); + printk(KERN_EMERG "MER (0x%x) = 0x%x\n", MER, *MER); +} + +static struct hw_interrupt_type virtex_pic = { + "Virtex Xilinx Interrupt Controller", + NULL, + NULL, + virtex_xintc_enable, + virtex_xintc_disable, + virtex_xintc_disable_and_ack, + NULL, + NULL, +}; + +void __init ppc4xx_pic_init(void) +{ + intc = ioremap(CONFIG_VIRTEX_XINTC_ADDR, 32); + + out_be32(MER, 0x00000000); /* Disable IRQ output signal */ + out_be32(IER, 0x00000000); /* Disable all interrupt sources */ + out_be32(IAR, 0xFFFFFFFF); /* Acknowledge all sources */ + out_be32(MER, 0x00000003); /* Master & hardware Interrupt enable */ + + ppc4xx_pic = &virtex_pic; + ppc_md.get_irq = virtex_xintc_get_irq; +} diff -Nawur linuxppc_2_4/arch/ppc/platforms/Makefile linuxppc_2_4-virtex/arch/ppc/platforms/Makefile --- linuxppc_2_4/arch/ppc/platforms/Makefile 2005-05-18 10:13:34.000000000 +0200 +++ linuxppc_2_4-virtex/arch/ppc/platforms/Makefile 2005-06-29 15:08:17.000000000 +0200 @@ -35,6 +35,7 @@ obj-$(CONFIG_EBONY) += ebony.o obj-$(CONFIG_OCOTEA) += ocotea.o obj-$(CONFIG_WALNUT) += walnut.o +obj-$(CONFIG_VIRTEX) += virtex.o obj-$(CONFIG_APUS) += apus_setup.o ifeq ($(CONFIG_APUS),y) diff -Nawur linuxppc_2_4/arch/ppc/platforms/virtex.c linuxppc_2_4-virtex/arch/ppc/platforms/virtex.c --- linuxppc_2_4/arch/ppc/platforms/virtex.c 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/arch/ppc/platforms/virtex.c 2005-07-04 15:12:31.000000000 +0200 @@ -0,0 +1,29 @@ +#include "virtex.h" +#include "linux/init.h" /* __init */ +#include "asm/machdep.h" /* ppc_md */ +#include "asm/processor.h" /* mtspr */ + +static void board_restart(char *cmd) +{ + mtspr(SPRN_DBCR0, DBCR0_RST); +} + +void __init board_init(void) +{ + ppc_md.restart = board_restart; +} + +void __init board_io_mapping(void) +{ + return; +} + +void __init board_setup_arch(void) +{ + return; +} + +void __init board_setup_irq(void) +{ + return; +} diff -Nawur linuxppc_2_4/arch/ppc/platforms/virtex.h linuxppc_2_4-virtex/arch/ppc/platforms/virtex.h --- linuxppc_2_4/arch/ppc/platforms/virtex.h 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/arch/ppc/platforms/virtex.h 2005-07-04 14:28:54.000000000 +0200 @@ -0,0 +1,8 @@ +#ifndef __VIRTEX_H__ +#define __VIRTEX_H__ + +#include "asm/ppcboot.h" /* struct bd_t */ + +#define bi_tbfreq bi_intfreq + +#endif /* __VIRTEX_H__ */ diff -Nawur linuxppc_2_4/Documentation/Configure.help linuxppc_2_4-virtex/Documentation/Configure.help --- linuxppc_2_4/Documentation/Configure.help 2005-05-18 10:13:16.000000000 +0200 +++ linuxppc_2_4-virtex/Documentation/Configure.help 2005-06-29 15:08:18.000000000 +0200 @@ -20798,6 +20798,23 @@ driver as a module, you can specify the number of presses at load time with "insmod button reboot_count=". +Xilinx UART Lite support +CONFIG_SERIAL_UARTLITE + This driver provides support for a single UART Lite and always uses + interrupts. You must configure the base address of the register + block and the interrupt number. + +CONFIG_SERIAL_UARTLITE_BASEADDR + Xilinx UART Lite register base address in hex. + +CONFIG_SERIAL_UARTLITE_INT + Xilinx UART Lite interrupt number in decimal. + +Xilinx UART Lite console support +CONFIG_SERIAL_UARTLITE_CONSOLE + Say Y to this and CONFIG_SERIAL_UARTLITE if you want boot messages + to be sent to the UART Lite. + Sound card support CONFIG_SOUND If you have a sound card in your computer, i.e. if it can say more diff -Nawur linuxppc_2_4/drivers/char/Config.in linuxppc_2_4-virtex/drivers/char/Config.in --- linuxppc_2_4/drivers/char/Config.in 2005-05-18 10:13:42.000000000 +0200 +++ linuxppc_2_4-virtex/drivers/char/Config.in 2005-06-29 15:08:16.000000000 +0200 @@ -25,6 +25,19 @@ tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL fi fi +if [ "$CONFIG_VIRTEX" = "y" ]; then + tristate 'Xilinx UART Lite support' CONFIG_SERIAL_UARTLITE + if [ "$CONFIG_SERIAL_UARTLITE" = "y" ]; then + bool ' Support for console on UART Lite serial port' CONFIG_SERIAL_UARTLITE_CONSOLE + if [ "$CONFIG_SERIAL_UARTLITE_CONSOLE" = "y" ]; then + define_bool CONFIG_SERIAL_CONSOLE y + fi + fi + if [ "$CONFIG_SERIAL_UARTLITE" != "n" ]; then + hex ' UART Lite registers base address (C_BASEADDR)' CONFIG_SERIAL_UARTLITE_BASEADDR 80600000 + int ' UART Lite interrupt' CONFIG_SERIAL_UARTLITE_INT 1 + fi +fi dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS diff -Nawur linuxppc_2_4/drivers/char/Makefile linuxppc_2_4-virtex/drivers/char/Makefile --- linuxppc_2_4/drivers/char/Makefile 2005-05-18 10:13:42.000000000 +0200 +++ linuxppc_2_4-virtex/drivers/char/Makefile 2005-06-29 15:08:16.000000000 +0200 @@ -212,6 +212,7 @@ obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o obj-$(CONFIG_TXX927_SERIAL) += serial_txx927.o obj-$(CONFIG_SGI_L1_SERIAL) += sn_serial.o +obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o subdir-$(CONFIG_RIO) += rio subdir-$(CONFIG_INPUT) += joystick diff -Nawur linuxppc_2_4/drivers/char/tty_io.c linuxppc_2_4-virtex/drivers/char/tty_io.c --- linuxppc_2_4/drivers/char/tty_io.c 2005-05-18 10:13:43.000000000 +0200 +++ linuxppc_2_4-virtex/drivers/char/tty_io.c 2005-06-29 15:08:15.000000000 +0200 @@ -164,6 +164,7 @@ extern void txx9_serial_console_init(void); extern void sb1250_serial_console_init(void); extern void arc_console_init(void); +extern void uartlite_console_init(void); #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -2311,6 +2312,10 @@ #ifdef CONFIG_IP22_SERIAL sgi_serial_console_init(); #endif +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + uartlite_console_init(); +#endif + } static struct tty_driver dev_tty_driver, dev_syscons_driver; diff -Nawur linuxppc_2_4/drivers/char/uartlite.c linuxppc_2_4-virtex/drivers/char/uartlite.c --- linuxppc_2_4/drivers/char/uartlite.c 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/drivers/char/uartlite.c 2005-06-29 17:25:42.000000000 +0200 @@ -0,0 +1,1378 @@ +/* + * xilinx_uartlite.c: Driver for the Xilinx UART lite + * + * Copyright 2003 Mind n.v. + * + * http://mind.be/ + * + * Author : Peter De Schrijver (p2@mind.be) + * + * Based on drivers/char/serial_amba.c + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL) version 2, incorporated herein by + * reference. Drivers based on or derived from this code fall under the GPL + * and must retain the authorship, copyright and this license notice. This + * file is not a complete program and may only be used when the entire + * operating system is licensed under the GPL. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SERIAL_UARTLITE_NAME "ttyS" +#define SERIAL_UARTLITE_MAJOR TTY_MAJOR +#define SERIAL_UARTLITE_MINOR 64 +#define SERIAL_UARTLITE_NR 1 + +#define CALLOUT_UARTLITE_NAME "cua" +#define CALLOUT_UARTLITE_MAJOR TTYAUX_MAJOR +#define CALLOUT_UARTLITE_MINOR 64 +#define CALLOUT_UARTLITE_NR SERIAL_UARTLITE_NR + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#define UARTLITE_XMIT_SIZE 1024 +#define WAKEUP_CHARS 256 +#define UARTLITE_ISR_PASS_LIMIT 256 + +#define EVT_WRITE_WAKEUP 0 + +#define UART_RFIFO 0 +#define UART_WFIFO 4 + +#define UART_SR 8 +#define UART_RX_FIFO_VALID_DATA (1<<0) +#define UART_TX_FIFO_EMPTY (1<<2) +#define UART_TX_FIFO_FULL (1<<3) +#define UART_OVERRUN_ERROR (1<<5) +#define UART_FRAME_ERROR (1<<6) +#define UART_PAR_ERROR (1<<7) +#define UART_ERROR (UART_OVERRUN_ERROR | UART_FRAME_ERROR | UART_PAR_ERROR) +#define UART_BUSY UART_TX_FIFO_EMPTY + +#define UART_CR 0xc +#define UART_ENABLE_INTR (1<<4) +#define UART_RST_RX_FIFO (1<<1) +#define UART_RST_TX_FIFO (1<<0) + +#define UART_REGSIZE 0x10 + +#define UART_GET_CR(p) (p->cr) +#define UART_GET_SR(p) ((*(volatile unsigned int *)(((unsigned long)p->uart_base)+UART_SR))) +#define UART_PUT_CR(p,x) (*(volatile unsigned int *)(((unsigned long)p->uart_base)+UART_CR)=p->cr=x) +#define UART_PUT_CHAR(p,x) ((*(volatile unsigned int *)(((unsigned long)p->uart_base)+UART_WFIFO))=x) +#define UART_GET_CHAR(p) (*(volatile unsigned int *)(((unsigned long)p->uart_base)+UART_RFIFO)) + +#define UART_RX_DATA(x) (x & UART_RX_FIFO_VALID_DATA) + +static struct tty_driver uartlite_driver, uartlitecallout_driver; +static int uartlite_refcount; +static struct tty_struct *uartlite_table[SERIAL_UARTLITE_NR]; +static struct termios *uartlite_termios[SERIAL_UARTLITE_NR]; +static struct termios *uartlite_termios_locked[SERIAL_UARTLITE_NR]; + +#if defined(CONFIG_SERIAL_UARTLITE_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +static u_char *tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); + +struct uartlite_icount { + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; + +#define IS_CONSOLE (1<<0) + +struct uartlite_port { + unsigned long uart_physbase; + void *uart_base; + unsigned int irq; + unsigned int uartclk; + unsigned int fifosize; + unsigned int tiocm_support; + unsigned int cr; + unsigned int flags; +}; + +struct uartlite_state { + struct uartlite_icount icount; + unsigned int line; + unsigned int close_delay; + unsigned int closing_wait; + unsigned int flags; + struct termios normal_termios; + struct termios callout_termios; + + int count; + struct uartlite_info *info; +}; + +struct uartlite_info { + struct uartlite_port *port; + struct uartlite_state *state; + struct tty_struct *tty; + unsigned char x_char; + unsigned char old_status; + unsigned char read_status_mask; + unsigned char ignore_status_mask; + unsigned int xmit_busy; + struct circ_buf xmit; + unsigned int flags; +#ifdef SUPPORT_SYSRQ + unsigned long sysrq; +#endif + + unsigned int event; + unsigned int timeout; + int blocked_open; + pid_t session; + pid_t pgrp; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + wait_queue_head_t delta_msr_wait; +}; + +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE +static struct console uartlite_cons; +#endif + +static void uartlite_wait_until_sent(struct tty_struct *tty, int timeout); + +static struct uartlite_port uartlite_ports[SERIAL_UARTLITE_NR] = { + { + .uart_physbase = CONFIG_SERIAL_UARTLITE_BASEADDR, + .irq = CONFIG_SERIAL_UARTLITE_INT, + }, +}; + +static struct uartlite_state uartlite_state[SERIAL_UARTLITE_NR]; + +static void +uartlite_enable_rx_interrupt(struct uartlite_info *info) +{ + unsigned int cr; + + cr = UART_GET_CR(info->port); + cr |= UART_ENABLE_INTR; + UART_PUT_CR(info->port, cr); +} + +static void +uartlite_disable_rx_interrupt(struct uartlite_info *info) +{ + unsigned int cr; + + cr = UART_GET_CR(info->port); + cr |= UART_ENABLE_INTR; + UART_PUT_CR(info->port, cr); + +} + +static void +uartlite_reset_rx_fifo(struct uartlite_info *info) +{ + unsigned int cr; + + cr = UART_GET_CR(info->port); + cr |= UART_RST_RX_FIFO; + UART_PUT_CR(info->port, cr); +} + +static void +uartlite_stop(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + + UART_PUT_CR(info->port, 0); + info->xmit_busy = 0; + +} + +static void +uartlite_start(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + info->xmit_busy = 0; +} + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void +uartlite_event(struct uartlite_info *info, int event) +{ + info->event |= 1 << event; + tasklet_schedule(&info->tlet); +} + +static void +#ifdef SUPPORT_SYSRQ +uartlite_rx_chars(struct uartlite_info *info, struct pt_regs *regs) +#else +uartlite_rx_chars(struct uartlite_info *info) +#endif +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, flg, ignored = 0; + struct uartlite_port *port = info->port; + struct uartlite_icount *icount = &info->state->icount; + + status = UART_GET_SR(port); + while (UART_RX_DATA(status)) { + ch = UART_GET_CHAR(port); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + + icount->rx++; + + flg = TTY_NORMAL; + + if (status & UART_ERROR) + goto handle_error; + +#ifdef SUPPORT_SYSRQ + if (info->sysrq) { + if (ch && time_before(jiffies, info->sysrq)) { + handle_sysrq(ch, regs, NULL, NULL); + info->sysrq = 0; + goto ignore_char; + } + info->sysrq = 0; + } +#endif + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UART_GET_SR(port); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (status & UART_PAR_ERROR) + icount->parity++; + else if (status & UART_FRAME_ERROR) + icount->frame++; + + if (status & UART_OVERRUN_ERROR) + icount->overrun++; + + if (status & info->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + status &= info->read_status_mask; + + if (status & UART_PAR_ERROR) + flg = TTY_PARITY; + else if (status & UART_FRAME_ERROR) + flg = TTY_FRAME; + + if (status & UART_OVERRUN_ERROR) { + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + info->sysrq = 0; +#endif + goto error_return; +} + +static void +uartlite_tx_chars(struct uartlite_info *info) +{ + struct uartlite_port *port = info->port; + int count; + + if (info->x_char) { + UART_PUT_CHAR(port, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped || info->tty->hw_stopped) { + info->xmit_busy = 0; + return; + } + + count = port->fifosize; + + do { + UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = + (info->xmit.tail + 1) & (UARTLITE_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, + UARTLITE_XMIT_SIZE) < WAKEUP_CHARS) + uartlite_event(info, EVT_WRITE_WAKEUP); +} + +static void +uartlite_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uartlite_info *info = dev_id; + unsigned int status, pass_counter = 0; + + status = UART_GET_SR(info->port); + do { + if (status & UART_RX_FIFO_VALID_DATA) +#ifdef SUPPORT_SYSRQ + uartlite_rx_chars(info, regs); +#else + uartlite_rx_chars(info); +#endif + if (status & UART_TX_FIFO_EMPTY) + uartlite_tx_chars(info); + if (pass_counter++ > UARTLITE_ISR_PASS_LIMIT) + break; + + status = UART_GET_SR(info->port); + } while (status & (UART_TX_FIFO_EMPTY | UART_RX_FIFO_VALID_DATA)); +} + +static void +uartlite_tasklet_action(unsigned long data) +{ + struct uartlite_info *info = (struct uartlite_info *) data; + struct tty_struct *tty; + + tty = info->tty; + + if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); +} + +static int +uartlite_startup(struct uartlite_info *info) +{ + unsigned long flags; + unsigned long page; + int retval = 0; + + 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 (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + + /* + * Map register block to virtual address if console device hasn't + * done it already. + */ + if (info->port->uart_base == NULL) { + info->port->uart_base = ioremap_nocache(info->port->uart_physbase, + UART_REGSIZE); + if (info->port->uart_base == NULL) { + printk(KERN_ERR + "uartlite_startup() %s%d: invalid register base address %p\n", + info->tty->driver.name, info->state->line, + (void *)info->port->uart_physbase); + retval = -ENODEV; + goto errclean; + } + } + + /* + * Allocate IRQ + */ + retval = request_irq(info->port->irq, uartlite_int, 0, "uartlite", info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + goto errclean; + } + + uartlite_enable_rx_interrupt(info); + /* clear anything we might have received while interrupts were disabled + * as an alternative to going and reading that data now. + */ + uartlite_reset_rx_fifo(info); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + + errclean: + if (info->port->uart_base != NULL) { + iounmap(info->port->uart_base); + info->port->uart_base = NULL; + } + errout: + restore_flags(flags); + return retval; +} + +static void +uartlite_shutdown(struct uartlite_info *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); /* Disable interrupts */ + + free_irq(info->port->irq, info); + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = NULL; + free_page(pg); + } + + /* + * disable all interrupts, disable the port + */ + + UART_PUT_CR(info->port, 0); + + if(!(info->port->flags & IS_CONSOLE)) { + info->port->uart_base = NULL; + iounmap(info->port->uart_base); + } + + /* kill off our tasklet */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +static void +uartlite_put_char(struct tty_struct *tty, u_char ch) +{ + struct uartlite_info *info = tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); + cli(); + if (CIRC_SPACE(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE) != + 0) { + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = + (info->xmit.head + 1) & (UARTLITE_XMIT_SIZE - 1); + } + UART_PUT_CHAR(info->port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (UARTLITE_XMIT_SIZE - 1); + info->state->icount.tx++; + info->xmit_busy = 1; + restore_flags(flags); +} + +static void +uartlite_flush_chars(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + + if (info->xmit.head == info->xmit.tail + || tty->stopped || tty->hw_stopped || !info->xmit.buf) + return; +} + +static int +uartlite_write(struct tty_struct *tty, int from_user, + const u_char * buf, int count) +{ + + struct uartlite_info *info = tty->driver_data; + unsigned long flags; + int c, ret = 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, + UARTLITE_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, + UARTLITE_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = (info->xmit.head + c) & + (UARTLITE_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, + UARTLITE_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) & + (UARTLITE_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->xmit_busy) { + save_flags(flags); + cli(); + UART_PUT_CHAR(info->port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = + (info->xmit.tail + 1) & (UARTLITE_XMIT_SIZE - 1); + info->state->icount.tx++; + info->xmit_busy = 1; + restore_flags(flags); + } + + return ret; +} + +static int +uartlite_write_room(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE); +} + +static int +uartlite_chars_in_buffer(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE); +} + +static void +uartlite_flush_buffer(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty); +} + +static void +uartlite_send_xchar(struct tty_struct *tty, char ch) +{ + struct uartlite_info *info = tty->driver_data; + unsigned long flags; + + info->x_char = ch; + if (ch && !info->xmit_busy) { + save_flags(flags); + cli(); + UART_PUT_CHAR(info->port, ch); + info->xmit_busy = 1; + restore_flags(flags); + } +} + +static void +uartlite_throttle(struct tty_struct *tty) +{ + if (I_IXOFF(tty)) + uartlite_send_xchar(tty, STOP_CHAR(tty)); +} + +static void +uartlite_unthrottle(struct tty_struct *tty) +{ + + struct uartlite_info *info = tty->driver_data; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + uartlite_send_xchar(tty, STOP_CHAR(tty)); + } +} + +static int +get_serial_info(struct uartlite_info *info, struct serial_struct *retinfo) +{ + struct uartlite_state *state = info->state; + struct uartlite_port *port = info->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof (tmp)); + tmp.type = 0; + tmp.line = state->line; + tmp.port = port->uart_physbase; + if (HIGH_BITS_OFFSET) + tmp.port_high = port->uart_physbase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = 0; + tmp.xmit_fifo_size = port->fifosize; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + + if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) + return -EFAULT; + return 0; +} + +static int +set_serial_info(struct uartlite_info *info, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct uartlite_state *state, old_state; + struct uartlite_port *port; + unsigned long new_port; + unsigned int i, change_irq, change_port; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof (new_serial))) + return -EFAULT; + + state = info->state; + old_state = *state; + port = info->port; + + 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 != port->irq; + change_port = new_port != port->uart_physbase; + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != port->fifosize) || + ((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)); + goto check_and_exit; + } + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0)) + return -EINVAL; + + if (new_serial.type && change_port) { + for (i = 0; i < SERIAL_UARTLITE_NR; i++) + if ((port != uartlite_ports + i) && + uartlite_ports[i].uart_physbase != new_port) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + 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; + port->fifosize = new_serial.xmit_fifo_size; + + if (change_port || change_irq) { + uartlite_shutdown(info); + port->irq = new_serial.irq; + port->uart_physbase = new_port; + } + + check_and_exit: + if (port->uart_physbase == 0) + return 0; + if ((info->flags & ASYNC_INITIALIZED)) { + if ((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) { + return 0; + } + } else + retval = uartlite_startup(info); + return retval; +} + +static int +get_lsr_info(struct uartlite_info *info, unsigned int *value) +{ + unsigned int result, status; + + status = UART_GET_SR(info->port); + result = status & UART_BUSY ? TIOCSER_TEMT : 0; + + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + UARTLITE_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + return put_user(result, value); +} + +static int +get_modem_info(struct uartlite_info *info, unsigned int *value) +{ + return put_user(0, value); +} + +static int +set_modem_info(struct uartlite_info *info, unsigned int cmd, + unsigned int *value) +{ + + return 0; +} + +static void +uartlite_break_ctl(struct tty_struct *tty, int break_state) +{ + return; +} + +static int +uartlite_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct uartlite_info *info = tty->driver_data; + struct uartlite_icount cnow; + struct serial_icounter_struct icount; + unsigned long flags; + + 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 TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + case TIOCMIWAIT: + return 0; + + case TIOCGICOUNT: + save_flags(flags); + cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = icount.dsr = icount.rng = icount.dcd = icount.brk = 0; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user((void *) arg, &icount, sizeof (icount)) + ? -EFAULT : 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void +uartlite_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + return; + +} + +static void +uartlite_close(struct tty_struct *tty, struct file *filp) +{ + struct uartlite_info *info = tty->driver_data; + struct uartlite_state *state; + unsigned long flags; + + if (!info) + return; + + state = info->state; + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (state->count != 1)) { + printk(KERN_ERR + "uartlite_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + + if (--state->count < 0) { + printk(KERN_ERR + "rs_close: bad serial port count for %s%d: %d\n", + tty->driver.name, info->state->line, state->count); + state->count = 0; + } + + if (state->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + + tty->closing = 1; + if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->state->closing_wait); + + if (info->flags & ASYNC_INITIALIZED) { + uartlite_disable_rx_interrupt(info); + uartlite_wait_until_sent(tty, info->timeout); + } + + uartlite_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 = NULL; + if (info->blocked_open) { + if (info->state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->state->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; +} + +static void +uartlite_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uartlite_info *info = (struct uartlite_info *) tty->driver_data; + unsigned long char_time, expire; + + if (info->port->fifosize == 0) + return; + + char_time = (info->timeout - HZ / 50) / info->port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + if (!timeout || timeout > 2 * info->timeout) + timeout = 2 * info->timeout; + + expire = jiffies + timeout; + + while (UART_GET_SR(info->port) & UART_BUSY) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); +} + +static void +uartlite_hangup(struct tty_struct *tty) +{ + struct uartlite_info *info = tty->driver_data; + struct uartlite_state *state = info->state; + + uartlite_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + uartlite_shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + info->tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +static int +block_til_ready(struct tty_struct *tty, struct file *filp, + struct uartlite_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct uartlite_state *state = info->state; + unsigned long flags; + int do_clocal = 0, extra_count = 0, retval; + + /* + * 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); + return (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + } + + /* + * 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); + 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(); + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static struct uartlite_info * +uartlite_get(int line) +{ + struct uartlite_info *info; + struct uartlite_state *state = uartlite_state + line; + + state->count++; + if (state->info) + return state->info; + info = kmalloc(sizeof (struct uartlite_info), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof (struct uartlite_info)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->flags = state->flags; + info->state = state; + info->port = uartlite_ports + line; + tasklet_init(&info->tlet, uartlite_tasklet_action, + (unsigned long) info); + } + if (state->info) { + kfree(info); + return state->info; + } + state->info = info; + return info; +} + +static int +uartlite_open(struct tty_struct *tty, struct file *filp) +{ + struct uartlite_info *info; + int retval, line = MINOR(tty->device) - tty->driver.minor_start; + + // is this a line that we've got? + MOD_INC_USE_COUNT; + if (line >= SERIAL_UARTLITE_NR) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + info = uartlite_get(line); + if (!info) + return -ENOMEM; + + tty->driver_data = info; + info->tty = tty; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* + * Make sure we have the temporary buffer allocated + */ + if (!tmp_buf) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + if (tmp_buf) + free_page(page); + else if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + tmp_buf = (u_char *) page; + } + + /* + * If the port is in 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; + return -EAGAIN; + } + + /* + * Start up the serial port + */ + retval = uartlite_startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + 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; + } +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + if (uartlite_cons.cflag && uartlite_cons.index == line) { + tty->termios->c_cflag = uartlite_cons.cflag; + uartlite_cons.cflag = 0; + } +#endif + + info->session = current->session; + info->pgrp = current->pgrp; + return 0; +} + +int __init +uartlite_init(void) +{ + int i; + + uartlite_driver.magic = TTY_DRIVER_MAGIC; + uartlite_driver.driver_name = "serial_uartlite"; + uartlite_driver.name = SERIAL_UARTLITE_NAME; + uartlite_driver.major = SERIAL_UARTLITE_MAJOR; + uartlite_driver.minor_start = SERIAL_UARTLITE_MINOR; + uartlite_driver.num = SERIAL_UARTLITE_NR; + uartlite_driver.type = TTY_DRIVER_TYPE_SERIAL; + uartlite_driver.subtype = SERIAL_TYPE_NORMAL; + uartlite_driver.init_termios = tty_std_termios; + uartlite_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + uartlite_driver.flags = TTY_DRIVER_REAL_RAW; + uartlite_driver.refcount = &uartlite_refcount; + uartlite_driver.table = uartlite_table; + uartlite_driver.termios = uartlite_termios; + uartlite_driver.termios_locked = uartlite_termios_locked; + + uartlite_driver.open = uartlite_open; + uartlite_driver.close = uartlite_close; + uartlite_driver.write = uartlite_write; + uartlite_driver.put_char = uartlite_put_char; + uartlite_driver.flush_chars = uartlite_flush_chars; + uartlite_driver.write_room = uartlite_write_room; + uartlite_driver.chars_in_buffer = uartlite_chars_in_buffer; + uartlite_driver.flush_buffer = uartlite_flush_buffer; + uartlite_driver.ioctl = uartlite_ioctl; + uartlite_driver.throttle = uartlite_throttle; + uartlite_driver.unthrottle = uartlite_unthrottle; + uartlite_driver.send_xchar = uartlite_send_xchar; + uartlite_driver.set_termios = uartlite_set_termios; + uartlite_driver.stop = uartlite_stop; + uartlite_driver.start = uartlite_start; + uartlite_driver.hangup = uartlite_hangup; + uartlite_driver.break_ctl = uartlite_break_ctl; + uartlite_driver.wait_until_sent = uartlite_wait_until_sent; + uartlite_driver.read_proc = NULL; + + /* + * The callout device is just like the normal device except for + * the major number and the subtype code. + */ + uartlitecallout_driver = uartlite_driver; + uartlitecallout_driver.name = CALLOUT_UARTLITE_NAME; + uartlitecallout_driver.major = CALLOUT_UARTLITE_MAJOR; + uartlitecallout_driver.subtype = SERIAL_TYPE_CALLOUT; + uartlitecallout_driver.read_proc = NULL; + uartlitecallout_driver.proc_entry = NULL; + + if (tty_register_driver(&uartlite_driver)) + panic("Couldn't register uartlite serial driver\n"); + if (tty_register_driver(&uartlitecallout_driver)) + panic("Couldn't register uartlite callout driver\n"); + + for (i = 0; i < SERIAL_UARTLITE_NR; i++) { + struct uartlite_state *state = uartlite_state + i; + state->line = i; + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + state->callout_termios = uartlitecallout_driver.init_termios; + state->normal_termios = uartlite_driver.init_termios; + } + + return 0; +} + +__initcall(uartlite_init); + +static void +uartlite_console_write(struct console *co, const char *s, u_int count) +{ + struct uartlite_port *port = &uartlite_ports[co->index]; + unsigned int status, old_cr; + int i; + + old_cr = UART_GET_CR(port); + UART_PUT_CR(port, 0); + + for (i = 0; i < count; i++) { + do { + status = UART_GET_SR(port); + } while (status & 0x8); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_SR(port); + } while (status & 0x8); + UART_PUT_CHAR(port, '\r'); + } + } + + do { + status = UART_GET_SR(port); + } while (!(status & 0x4)); + UART_PUT_CR(port, old_cr); +} + +static kdev_t +uartlite_console_device(struct console *c) +{ + return MKDEV(SERIAL_UARTLITE_MAJOR, SERIAL_UARTLITE_MINOR + c->index); +} + +static int __init +uartlite_console_setup(struct console *co, char *options) +{ + struct uartlite_port *port = &uartlite_ports[co->index]; + + port->uart_base = ioremap_nocache(port->uart_physbase, UART_REGSIZE); + port->flags|=IS_CONSOLE; + if (port->uart_base == NULL) { + return -1; + } + + UART_PUT_CR(port, 0); + + if (co->index >= SERIAL_UARTLITE_NR) + co->index = 0; + + return 0; +} + +static struct console uartlite_cons = { + .name = "ttyS", + .write = uartlite_console_write, + .device = uartlite_console_device, + .setup = uartlite_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init +uartlite_console_init(void) +{ + register_console(&uartlite_cons); +} diff -Nawur linuxppc_2_4/drivers/net/Config.in linuxppc_2_4-virtex/drivers/net/Config.in --- linuxppc_2_4/drivers/net/Config.in 2005-05-18 10:13:50.000000000 +0200 +++ linuxppc_2_4-virtex/drivers/net/Config.in 2005-07-04 09:59:28.000000000 +0200 @@ -47,6 +47,15 @@ fi fi fi + if [ "$CONFIG_VIRTEX" = "y" ]; then + tristate ' Xilinx ethernet MAC support' CONFIG_XEMAC + if [ "$CONFIG_XEMAC" != "n" ]; then + hex 'XEMAC MAC address' CONFIG_XEMAC_MAC 0x00030D07181F + hex 'XEMAC registers base address (C_BASEADDR)' CONFIG_XEMAC_BASE 0x80c00000 + hex 'XEMAC registers high address (C_HIGHADDR)' CONFIG_XEMAC_HIGH 0x80c0ffff + int ' XEMAC interrupt' CONFIG_XEMAC_IRQ 0 + fi + fi if [ "$CONFIG_ZORRO" = "y" ]; then tristate ' Ariadne support' CONFIG_ARIADNE tristate ' A2065 support' CONFIG_A2065 diff -Nawur linuxppc_2_4/drivers/net/Makefile linuxppc_2_4-virtex/drivers/net/Makefile --- linuxppc_2_4/drivers/net/Makefile 2005-05-18 10:13:50.000000000 +0200 +++ linuxppc_2_4-virtex/drivers/net/Makefile 2005-06-29 15:10:08.000000000 +0200 @@ -95,6 +95,7 @@ obj-$(CONFIG_FEALNX) += fealnx.o mii.o obj-$(CONFIG_TC35815) += tc35815.o obj-$(CONFIG_TIGON3) += tg3.o +obj-$(CONFIG_XEMAC) += xemac.o ifeq ($(CONFIG_E100),y) obj-y += e100/e100.o diff -Nawur linuxppc_2_4/drivers/net/xemac.c linuxppc_2_4-virtex/drivers/net/xemac.c --- linuxppc_2_4/drivers/net/xemac.c 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/drivers/net/xemac.c 2005-07-04 14:35:59.000000000 +0200 @@ -0,0 +1,837 @@ +/* + * xemac.c: A driver for Xilinx 10/100Mbit/s ethernet MAC core + * + * Copyright 2002 Mind NV + * + * http://www.mind.be/ + * + * Author : Peter De Schrijver (p2@mind.be) + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL) version 2, incorporated herein by + * reference. Drivers based on or derived from this code fall under the GPL + * and must retain the authorship, copyright and this license notice. This + * file is not a complete program and may only be used when the entire + * operating system is licensed under the GPL. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xemac.h" + +/* RX and TX DMA seem to be broken */ + +#undef USE_RX_DMA +#undef USE_TX_DMA + +#define XEMAC_BASE CONFIG_XEMAC_BASE +#define XEMAC_LEN CONFIG_XEMAC_HIGH - CONFIG_XEMAC_BASE + 1 +#define XEMAC_IRQ CONFIG_XEMAC_IRQ + +#define TX_TIMEOUT (4*HZ) + +#define MAX_UNITS 1 + +static int media[MAX_UNITS] = { -1 }; + +static int max_interrupt_work = 100; + +static inline void xemac_writel(unsigned int v,unsigned int a) { + + out_be32((volatile unsigned int *)(a),(v)); + +} + +static inline unsigned int xemac_readl(unsigned int a) { + + return in_be32((volatile unsigned int *)(a)); + +} + +static void mdio_write (struct net_device *dev, int phy_id, int location, + int value) { + + unsigned int ioaddr=dev->base_addr; + + xemac_writel(value, ioaddr+MGTDR); + xemac_writel(MGTCR_SB | (phy_id << 25) | (location << 20) | + MGTCR_IE, ioaddr+MGTCR); + + while(xemac_readl(ioaddr+MGTCR) & MGTCR_BUSY); + +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) { + + unsigned int ioaddr=dev->base_addr; + + xemac_writel(MGTCR_SB | MGTCR_RWN | (phy_id << 25) | + (location << 20) | MGTCR_IE, ioaddr+MGTCR); + while(xemac_readl(ioaddr+MGTCR) & MGTCR_BUSY); + + return(xemac_readl(ioaddr+MGTDR) & 0xffff); + +} + + +static void xemac_hw_start(struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + unsigned int ioaddr=dev->base_addr; + int par,i; + + printk("dev: %p, dev->base_addr: %08lx, xp: %p\n",dev,dev->base_addr,xp); + printk("ioaddr: %08x\n",ioaddr); + +#ifdef USE_TX_DMA + for(i=0;itx_buffer_descr[i].device_status=0; + xp->tx_buffer_descr[i].control=DMACR_SG_DISABLE; + xp->tx_buffer_descr[i].length=0; + xp->tx_buffer_descr[i].status=0; + xp->tx_buffer_descr[i].next= + virt_to_bus(&xp->tx_buffer_descr[(i+1)%NUM_TX_DESCR]); + xp->tx_buffer_descr[i].flags=0; + xp->tx_buffer_descr[i].req_length=0; + xp->tx_buffer_descr[i].destination=XEMAC_BASE+FIFO_TX_DATA; + xp->tx_buffer_descr[i].source=xp->tx_buffer_dma_addr+i*TX_BUF_LEN; + xp->tx_buffer_descr[i].buffer=xp->tx_buffer+i*TX_BUF_LEN; + } +#endif + + xemac_writel(ECR_RSTTX | ECR_RSTRX,ioaddr+ECR); + udelay(100); + xemac_writel(ECR_ENPHY | ECR_TXPAD | ECR_TXFCS | ECR_UA | ECR_BA, + ioaddr+ECR); + + mdio_write(dev, xp->phys[0], 0, 0x8000); + while(mdio_read(dev, xp->phys[0],0) & 0x8000); + + xemac_writel((dev->dev_addr[0] << 8) | + (dev->dev_addr[1]), ioaddr+SAH); + + xemac_writel((dev->dev_addr[2] << 24) | + (dev->dev_addr[3] << 16) | + (dev->dev_addr[4] << 8) | + (dev->dev_addr[5]), ioaddr+SAL); + + par=mdio_read(dev, xp->phys[0], 0x11); + + xemac_writel(xemac_readl(ioaddr+ECR) | ((par & (1<<9)) ? (1<<31) : 0), ioaddr+ECR); + xemac_writel(xemac_readl(ioaddr+ECR) | ECR_ENTX | ECR_ENRX, ioaddr+ECR); + +#ifdef USE_TX_DMA + xemac_writel(DMA_IX_PKT_DONE | DMA_IX_DMA_ERROR, + ioaddr + TX_DMA_IEREG); +#endif + +#ifdef USE_RX_DMA + xemac_writel(DMA_IX_PKT_DONE | DMA_IX_DMA_ERROR, + ioaddr + RX_DMA_IEREG); +#endif + + xp->cur_tx=xp->tx_inuse=xp->tx_dirty=0; +#ifdef USE_TX_DMA + xemac_writel(xp->tx_dma_addr,ioaddr+TX_DMA_BDA); +#endif + xp->cur_rx=0; + +#ifdef USE_RX_DMA + xemac_writel(xp->rx_dma_addr,ioaddr+RX_DMA_BDA); +#endif + + + xemac_writel(EMAC_INT | +#ifdef USE_RX_DMA + RECV_DMA_INT | +#endif +#ifdef USE_TX_DMA + XMIT_DMA_INT | +#endif + 0,ioaddr+INT_MASK); + + xemac_writel(0 | +#ifndef USE_RX_DMA + RECV_DONE_INT | +#endif +#ifndef USE_TX_DMA + TX_DONE_INT | +#endif + 0, ioaddr+IPIF_IE); + + +#ifdef USE_RX_DMA + xemac_writel(xemac_readl(ioaddr+RX_DMAC_REG) & ~DMACR_SG_DISABLE, + ioaddr+RX_DMAC_REG); + xemac_writel(xemac_readl(ioaddr+RX_SWCR_REG) | SWCR_SG_ENABLE, + ioaddr+RX_SWCR_REG); +#endif + + xemac_writel(1<<31, ioaddr + INT_GLOBAL); + + netif_start_queue(dev); + +} + +static void xemac_set_multicast(struct net_device *dev) { + + unsigned int ioaddr=dev->base_addr; + + if(dev->flags & IFF_PROMISC) { + printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); + xemac_writel(xemac_readl(ioaddr+ECR) | ECR_PROMISC,ioaddr + ECR); + } + if((dev->flags & IFF_ALLMULTI) || dev->mc_count) + xemac_writel(xemac_readl(ioaddr+ECR) | ECR_MULTI,ioaddr + ECR); + +} + +static struct net_device_stats *xemac_get_stats(struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + + return &xp->stats; + +} + +static void xemac_tx_timeout(struct net_device *dev) { + + unsigned int ioaddr=dev->base_addr; + + xemac_writel(xemac_readl(ioaddr+TX_SWCR_REG) & ~SWCR_SG_ENABLE,ioaddr+TX_SWCR_REG); + xemac_writel(0,ioaddr+TX_RESET_REG); + xemac_writel(0, ioaddr+INT_MASK); + xemac_writel((xemac_readl(ioaddr+ECR) | ECR_RSTTX | ECR_RSTRX) & + ~(ECR_ENTX | ECR_ENRX | ECR_ENPHY), + ioaddr+ECR); + + xemac_hw_start(dev); + +} + +#ifndef USE_TX_DMA +static void inline write_tx_fifo(struct sk_buff *skb,unsigned int ioaddr) { + + int i; + unsigned int *data; + + data=(unsigned int *)skb->data; + + for(i=skb->len;i>0;i-=4){ + *(unsigned int *)(ioaddr+FIFO_TX_DATA)=*(data++); + } + if(i>1) { + *(unsigned short *)(ioaddr+FIFO_TX_DATA)=*((unsigned short *)data++); + i-=2; + } + if(i>0) { + *(unsigned char *)(ioaddr+FIFO_TX_DATA)=*(unsigned char *)data; + } + +} +#endif + +#ifdef USE_TX_DMA +static int xemac_start_xmit(struct sk_buff *skb, struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + unsigned int ioaddr=dev->base_addr; + int entry,len=skb->len; + + spin_lock_irq(&xp->lock); + + entry=xp->cur_tx ; + + if(xp->tx_inuse) + xp->tx_buffer_descr[(entry-1)% NUM_TX_DESCR].control&=~DMACR_SG_DISABLE; + + if(likely(len < TX_BUF_LEN)) { + skb_copy_and_csum_dev(skb, xp->tx_buffer_descr[entry].buffer); + dev_kfree_skb(skb); + } + else { + dev_kfree_skb(skb); + xp->stats.tx_dropped++; + return 0; + } + + xp->tx_buffer_descr[entry].length=len; + xp->tx_buffer_descr[entry].req_length=len; + xp->tx_buffer_descr[entry].control=DMACR_SOURCE_INCR | DMACR_DEST_LOCAL + | DMACR_LAST_BD | DMACR_SG_DISABLE; + +#if 0 + if(!(xemac_readl(ioaddr+TX_DMAS_REG) & DMASR_SG_BUSY)) { +#endif + xemac_writel(xemac_readl(ioaddr+TX_DMAC_REG) & ~DMACR_SG_DISABLE,ioaddr+TX_DMAC_REG); + xemac_writel(xemac_readl(ioaddr+TX_SWCR_REG) | SWCR_SG_ENABLE,ioaddr+TX_SWCR_REG); +#if 0 + } +#endif + + xp->cur_tx=(entry + 1) % NUM_TX_DESCR; + xp->tx_inuse++; + if(xp->tx_inuse >= (NUM_TX_DESCR-1)) + netif_stop_queue (dev); + + dev->trans_start = jiffies; + + spin_unlock_irq(&xp->lock); + + return 0; + +} +#else +static int xemac_start_xmit(struct sk_buff *skb, struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + unsigned int ioaddr=dev->base_addr; + + spin_lock_irq(&xp->lock); + + xp->tx_buffers[xp->tx_dirty]=skb; + + if(!xp->tx_inuse) { + write_tx_fifo(xp->tx_buffers[xp->cur_tx],ioaddr); + xemac_writel(skb->len,ioaddr + TPLR); + } + + xp->tx_inuse++; + xp->tx_dirty=(xp->tx_dirty+1) % NUM_TX_BUFFERS; + + if(xp->tx_inuse==NUM_TX_BUFFERS) + netif_stop_queue (dev); + + dev->trans_start = jiffies; + + spin_unlock_irq(&xp->lock); + + return 0; + +} +#endif + +#ifdef USE_RX_DMA +static inline void xemac_rx_interrupt(struct net_device *dev, + struct xemac_priv *xp, + unsigned int ioaddr) { + + int bdcount; + int cur_rx=xp->cur_rx,len; + + bdcount=xemac_readl(ioaddr + RX_DMA_PKTCNT); + + for(;bdcount;bdcount--) { + struct sk_buff *skb=xp->rx_buffer_descr[cur_rx].skb; + struct rx_buffer_descr *rxb=&xp->rx_buffer_descr[cur_rx]; + + len=rxb->length; + + skb_put(skb,len); + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); + + skb=dev_alloc_skb(MAX_ETH_FRAME_SIZE); + if(skb) { + rxb->skb=skb; + dma_cache_wback_inv((unsigned long)skb->data,MAX_ETH_FRAME_SIZE); + rxb->destination=virt_to_bus(skb->data); + rxb->control=DMACR_GEN_BD_INTR|DMACR_DEST_INCR|DMACR_SOURCE_LOCAL; + rxb->length=MAX_ETH_FRAME_SIZE; + rxb->req_length=MAX_ETH_FRAME_SIZE; + } + else { + panic("FIXME: out of memory\n"); + } + + cur_rx=(++cur_rx) % NUM_RX_DESCR; + + } + + xp->cur_rx=cur_rx; + + return ; + +} +#else +static inline void xemac_rx_fifo_interrupt(struct net_device *dev, + struct xemac_priv *xp, + unsigned int ioaddr) { + + struct sk_buff *skb; + unsigned int *data; + int len,i; + + len=xemac_readl(ioaddr + RPLR); + skb=dev_alloc_skb(len+2); + if(skb) { + skb->dev = dev; + skb_reserve(skb,2); + data=(unsigned int *)skb->data; + for(i=len;i>0;i-=4) { + *(data++)=*(unsigned int *)(ioaddr+FIFO_RX_DATA); + } + if(i>1) { + *((unsigned short *)data++)=*(unsigned short *)(ioaddr+FIFO_RX_DATA); + i-=2; + } + if(i>0) { + *(unsigned char *)data=*(unsigned char *)(ioaddr+FIFO_RX_DATA); + } + skb_put (skb, len); + skb->protocol = eth_type_trans (skb, dev); + + netif_rx (skb); + dev->last_rx = jiffies; + } + else { + printk (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + xp->stats.rx_dropped++; + writel(0,ioaddr+ FIFO_RX_RESET); + } + +} +#endif + +#ifdef USE_TX_DMA +static inline void xemac_tx_interrupt(struct net_device *dev, + struct xemac_priv *xp, + unsigned int ioaddr) { + + int status; + int tx_dirty=xp->tx_dirty; + + status=xemac_readl(ioaddr+TX_DMA_ISREG); + + if(status & DMA_IX_PKT_DONE) { + while(tx_dirty!=xp->cur_tx) { + xp->tx_buffer_descr[tx_dirty].control=DMACR_SG_DISABLE; + tx_dirty=(tx_dirty+1)%NUM_TX_DESCR; + xp->tx_inuse--; + } + xemac_writel(DMA_IX_PKT_DONE, ioaddr+TX_DMA_ISREG); + } + + if(status & DMA_IX_DMA_ERROR) { + printk(KERN_WARNING "%s: TX DMA error\n", dev->name); + xemac_hw_start(dev); + } + + xp->tx_dirty=tx_dirty; + + if (netif_queue_stopped (dev)) + netif_wake_queue (dev); + + +} +#else +static inline void xemac_tx_fifo_interrupt(struct net_device *dev, + struct xemac_priv *xp, + unsigned int ioaddr) { + + int status; + + status=xemac_readl(ioaddr+TSR); + + dev_kfree_skb_irq(xp->tx_buffers[xp->cur_tx]); + xp->cur_tx=(xp->cur_tx+1) % NUM_TX_BUFFERS; + xp->tx_inuse--; + if(xp->tx_inuse) { + write_tx_fifo(xp->tx_buffers[xp->cur_tx],ioaddr); + xemac_writel(xp->tx_buffers[xp->cur_tx]->len,ioaddr + TPLR); + } + + if (netif_queue_stopped (dev)) + netif_wake_queue (dev); + +} +#endif + +static inline void xemac_int(struct net_device *dev, + struct xemac_priv *xp, + unsigned int ioaddr) { + + int status; + + status=xemac_readl(ioaddr+IPIF_IS) & (RECV_DONE_INT | TX_DONE_INT); + + if(status & RECV_DONE_INT) { + xemac_rx_fifo_interrupt(dev,xp,ioaddr); + xemac_writel(RECV_DONE_INT,ioaddr + IPIF_IS); + } + + if(status & TX_DONE_INT) { + xemac_tx_fifo_interrupt(dev,xp,ioaddr); + xemac_writel(TX_DONE_INT,ioaddr + IPIF_IS); + } + + return ; + +} + +static void xemac_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { + + struct net_device *dev = (struct net_device *) dev_instance; + struct xemac_priv *xp=dev->priv; + unsigned int ioaddr=dev->base_addr; + int status,workcount=max_interrupt_work; + + spin_lock(&xp->lock); + + do { + status=xemac_readl(ioaddr+INT_PENDING); + +#ifdef USE_RX_DMA + if(status & RECV_DMA_INT) { + xemac_rx_interrupt(dev,xp,ioaddr); + xemac_writel(RECV_DMA_INT, ioaddr+INT_STATUS); + } +#endif +#ifdef USE_TX_DMA + if(status & XMIT_DMA_INT) { + xemac_tx_interrupt(dev,xp,ioaddr); + xemac_writel(XMIT_DMA_INT, ioaddr+INT_STATUS); + } +#endif + if(status & EMAC_INT) { + xemac_int(dev,xp,ioaddr); + xemac_writel(EMAC_INT, ioaddr+INT_STATUS); + } + + if(status & ERROR_INT) + xemac_writel(ERROR_INT,ioaddr+INT_STATUS); + + workcount--; + + } while(status && (workcount>0)); + + if(workcount<= 0) { + printk(KERN_WARNING + "%s: Too much work at interrupt, " + "status: %08x.\n", dev->name,status); + xemac_writel(0xffffffff, ioaddr+INT_STATUS); + } + + spin_unlock (&xp->lock); + +} +static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { + + int rc=0; + u16 *data = (u16 *) & rq->ifr_data; + struct xemac_priv *xp=dev->priv; + + switch (cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0]=xp->phys[0] & 0x3f; + /* Fall Through */ + + case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */ + data[3] = mdio_read (dev, data[0], data[1] & 0x1f); + break; + + case SIOCDEVPRIVATE + 2: /* Write the specified MII register */ + if (!capable (CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + mdio_write (dev, data[0], data[1] & 0x1f, data[2]); + break; + + default: + rc = -EOPNOTSUPP; + break; + + } + + return rc; +} + +static inline void xemac_thread_iter(struct net_device *dev, struct xemac_priv *xp) { + + unsigned int ioaddr=dev->base_addr; + int mii_lpa; + + mii_lpa=mdio_read (dev, xp->phys[0], MII_LPA); + if (!xp->mii.force_media && mii_lpa != 0xffff) { + + int duplex = (mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == 0x0040; + + if(xp->mii.full_duplex != duplex) { + + unsigned int ctrl; + + xp->mii.full_duplex = duplex; + + if(mii_lpa) { + printk (KERN_INFO + "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", + dev->name, xp->mii.full_duplex ? "full" : "half", + xp->phys[0], mii_lpa); + } else { + printk(KERN_INFO + "%s: media is unconnected, link down, or " + "incompatible connection\n", dev->name); + } + ctrl=xemac_readl(ioaddr+ECR); + xemac_writel(ctrl & ~(ECR_ENTX | ECR_ENRX),ioaddr+ECR); + ctrl&=~ECR_FD; + ctrl|=xp->mii.full_duplex ? ECR_FD : 0; + xemac_writel(ctrl,ioaddr+ECR); + } + + } +} + +static int xemac_thread (void *data) { + + struct net_device *dev = data; + struct xemac_priv *xp=dev->priv; + unsigned long timeout; + + daemonize(); + reparent_to_init(); + spin_lock_irq(¤t->sigmask_lock); + sigemptyset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + strncpy (current->comm, dev->name, sizeof(current->comm) - 1); + current->comm[sizeof(current->comm) - 1] = '\0'; + + while(1) { + timeout = HZ; + do { + timeout = interruptible_sleep_on_timeout (&xp->thr_wait, timeout); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + } + + if(xp->time_to_die) + break; + + rtnl_lock(); + xemac_thread_iter(dev, xp); + rtnl_unlock(); + } + + complete_and_exit (&xp->thr_exited, 0); +} + +static int xemac_close(struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + unsigned int ioaddr=dev->base_addr; + + netif_stop_queue (dev); + + spin_lock_irq (&xp->lock); + + xemac_writel(0, ioaddr+INT_MASK); + xemac_writel((xemac_readl(ioaddr+ECR) | ECR_RSTTX | ECR_RSTRX) & + ~(ECR_ENTX | ECR_ENRX | ECR_ENPHY), + ioaddr+ECR); + + spin_unlock_irq (&xp->lock); + + synchronize_irq (); + + consistent_free(xp->rx_buffer_descr); + consistent_free(xp->tx_buffer); +#ifdef USE_RX_DMA + consistent_free(xp->tx_buffer_descr); +#endif + + free_irq (dev->irq, dev); + + return 0; +} + +static int xemac_open(struct net_device *dev) { + + struct xemac_priv *xp=dev->priv; + struct rx_buffer_descr *rxb; + int retval,i; + +#ifdef USE_TX_DMA + xp->tx_buffer_descr=consistent_alloc(GFP_KERNEL|GFP_DMA, + NUM_TX_DESCR * sizeof(struct tx_buffer_descr), + &xp->tx_dma_addr); + + if(!xp->tx_buffer_descr) + return -ENOMEM; + + xp->tx_buffer=consistent_alloc(GFP_KERNEL|GFP_DMA, + NUM_TX_DESCR * TX_BUF_LEN, + &xp->tx_buffer_dma_addr); + + if(!xp->tx_buffer) + return -ENOMEM; +#endif + +#ifdef USE_RX_DMA + rxb=consistent_alloc(GFP_KERNEL|GFP_DMA, + NUM_RX_DESCR * sizeof(struct rx_buffer_descr), + &xp->rx_dma_addr); + + if(!rxb) + return -ENOMEM; + + xp->rx_buffer_descr=rxb; + + for(i=0;idevice_status=0; + rxb->control=DMACR_DEST_INCR|DMACR_SOURCE_LOCAL| DMACR_SG_DISABLE | DMACR_LAST_BD; + rxb->length=MAX_ETH_FRAME_SIZE; + rxb->status=0; + rxb->next=virt_to_bus(&xp->rx_buffer_descr[(i+1)%NUM_RX_DESCR]); + rxb->flags=0; + rxb->req_length=MAX_ETH_FRAME_SIZE; + rxb->source=XEMAC_BASE+FIFO_RX_DATA; + + skb=dev_alloc_skb(MAX_ETH_FRAME_SIZE); + rxb->skb=skb; + dma_cache_inv((unsigned long)skb->data,MAX_ETH_FRAME_SIZE); + rxb->destination=0x30000000; /* virt_to_bus(skb->data); */ + +printk("rxb->destination: %08x, skb->data: %08x\n",rxb->destination, skb->data); + + } +#endif + + retval=request_irq(dev->irq, xemac_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) { + return retval; + } + + xemac_hw_start(dev); + + xp->time_to_die = 0; + xp->thr_pid = kernel_thread (xemac_thread, dev, CLONE_FS | CLONE_FILES); + if (xp->thr_pid < 0) + printk (KERN_WARNING "%s: unable to start kernel thread\n", dev->name); + + return 0; + +} + +int __devinit xemac_probe(void) { + + unsigned int ioaddr,ver_major,ver_minor,rev_letter; + struct net_device *dev; + struct xemac_priv *xp; + int err; + + if(!request_region(XEMAC_BASE, XEMAC_LEN,"Xilinx Ethernet MAC")) + return -EBUSY; + + ioaddr=(unsigned int)ioremap(XEMAC_BASE, XEMAC_LEN); + + if(ioaddr==0) { + printk(KERN_ERR "cannot remap xemac MMIO range, aborting\n"); + release_region(XEMAC_BASE, XEMAC_LEN); + return -EIO; + } + + dev=alloc_etherdev(sizeof(struct xemac_priv)); + if(dev==NULL) { + release_region(XEMAC_BASE, XEMAC_LEN); + return -ENOMEM; + } + + xp=dev->priv; + dev->open=xemac_open; + dev->hard_start_xmit=xemac_start_xmit; + dev->stop=xemac_close; + dev->get_stats=xemac_get_stats; + dev->set_multicast_list=xemac_set_multicast; + dev->do_ioctl=mii_ioctl; + dev->tx_timeout=xemac_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->irq=XEMAC_IRQ; + + dev->base_addr = (unsigned int) ioaddr; + + xp->phys[0]=0; + + spin_lock_init (&xp->lock); + init_waitqueue_head (&xp->thr_wait); + init_completion (&xp->thr_exited); + + err=register_netdev(dev); + if(err) { + release_region(XEMAC_BASE, XEMAC_LEN); + kfree(dev); + return err; + } + + dev->dev_addr[0]=(unsigned char)((CONFIG_XEMAC_MAC >> 40) & 0xFF); + dev->dev_addr[1]=(unsigned char)((CONFIG_XEMAC_MAC >> 32) & 0xFF); + dev->dev_addr[2]=(unsigned char)((CONFIG_XEMAC_MAC >> 24) & 0xFF); + dev->dev_addr[3]=(unsigned char)((CONFIG_XEMAC_MAC >> 16) & 0xFF); + dev->dev_addr[4]=(unsigned char)((CONFIG_XEMAC_MAC >> 8 ) & 0xFF); + dev->dev_addr[5]=(unsigned char)(CONFIG_XEMAC_MAC & 0xFF); + + ver_major=xemac_readl(ioaddr + EMIR) >> 28; + ver_minor=(xemac_readl(ioaddr + EMIR) >> 21) & 0x7f; + rev_letter=(xemac_readl(ioaddr + EMIR) >> 16) & 0x1f; + + printk(KERN_INFO "%s: Xilinx Ethernet MAC (Rev %d.%d%c) at 0x%x," + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + ver_major,ver_minor,'a'+rev_letter, + XEMAC_BASE, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + if(media[0] > 0) { + xp->medialock=1; + xp->duplex=media[0] & 0x20 ? 100 : 10; + xp->speed=(media[0] & 0x10) ? 0x0100 : 0; + + printk(KERN_INFO "Forcing %dMbps %s-duplex operation.\n", + (xp->speed? 100 : 10), + (xp->duplex ? "full" : "half")); + + mdio_write(dev, xp->phys[0], 4, (xp->duplex ? 0x2000 : 0) | + (xp->speed ? 0x0100 : 0)); + + } + + return 0; + +} + +module_init(xemac_probe); + diff -Nawur linuxppc_2_4/drivers/net/xemac.h linuxppc_2_4-virtex/drivers/net/xemac.h --- linuxppc_2_4/drivers/net/xemac.h 1970-01-01 01:00:00.000000000 +0100 +++ linuxppc_2_4-virtex/drivers/net/xemac.h 2005-07-04 14:26:51.000000000 +0200 @@ -0,0 +1,196 @@ +/* + * xemac.h: A driver for Xilinx 10/100Mbit/s ethernet MAC core + * + * Copyright 2002 Mind NV + * + * http://www.mind.be/ + * + * Author : Peter De Schrijver (p2@mind.be) + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL) version 2, incorporated herein by + * reference. Drivers based on or derived from this code fall under the GPL + * and must retain the authorship, copyright and this license notice. This + * file is not a complete program and may only be used when the entire + * operating system is licensed under the GPL. + * + */ + +#ifndef _XEMAC_H +#define _XEMAC_H + +#include +#include + +#define NUM_TX_DESCR 32 +#define NUM_TX_BUFFERS 4 +#define NUM_RX_DESCR 1 + +#define MAX_ETH_FRAME_SIZE 1536 +#define TX_BUF_LEN MAX_ETH_FRAME_SIZE + +/* DMA buffer descriptor */ + +struct tx_buffer_descr { + + unsigned int device_status; + unsigned int control; + unsigned int source; + unsigned int destination; + unsigned int length; + unsigned int status; + unsigned int next; + unsigned char *buffer; + unsigned int flags; + unsigned int req_length; + +}; + +struct rx_buffer_descr { + + unsigned int device_status; + unsigned int control; + unsigned int source; + unsigned int destination; + unsigned int length; + unsigned int status; + unsigned int next; + struct sk_buff *skb; + unsigned int flags; + unsigned int req_length; + +}; + +struct xemac_priv { + + struct net_device_stats stats; + spinlock_t lock; + struct tx_buffer_descr *tx_buffer_descr; + struct rx_buffer_descr *rx_buffer_descr; + struct sk_buff *tx_buffers[NUM_TX_BUFFERS]; + unsigned char *tx_buffer; + unsigned int tx_dma_addr; + unsigned int tx_buffer_dma_addr; + unsigned int rx_dma_addr; + int cur_rx,cur_tx, tx_inuse,tx_dirty; + unsigned int medialock:1; + unsigned int duplex:1; + unsigned int speed:1; + struct mii_if_info mii; + char phys[1]; + pid_t thr_pid; + wait_queue_head_t thr_wait; + struct completion thr_exited; + int time_to_die; +}; + + +/* emac core registers */ + +#define EMIR 0x1100 + +#define ECR 0x1104 +#define ECR_PROMISC (1<<14) +#define ECR_MULTI (1<<16) +#define ECR_FD (1<<31) +#define ECR_RSTTX (1<<30) +#define ECR_ENTX (1<<29) +#define ECR_RSTRX (1<<28) +#define ECR_ENRX (1<<27) +#define ECR_ENPHY (1<<26) +#define ECR_UA (1<<17) +#define ECR_BA (1<<15) +#define ECR_TXPAD (1<<25) +#define ECR_TXFCS (1<<24) + +#define SAH 0x110c +#define SAL 0x1110 + +#define MGTCR 0x1114 +#define MGTCR_SB (1<<31) +#define MGTCR_BUSY (1<<31) +#define MGTCR_RWN (1<<30) +#define MGTCR_IE (1<<19) + +#define MGTDR 0x1118 + +#define RPLR 0x111C + +#define TPLR 0x1120 + +#define TSR 0x1124 + +/* emac ipif registers */ + +#define IPIF_IS 0x20 +#define IPIF_IE 0x28 + +#define RECV_DONE_INT 0x2 +#define TX_DONE_INT 0x1 + +/* interrupt registers */ + +#define INT_STATUS 0 + +#define INT_PENDING 4 + +#define INT_MASK 8 + +#define INT_GLOBAL 0x1c + +#define SEND_FIFO_INT 0x00000020 +#define RECV_FIFO_INT 0x00000020 +#define RECV_DMA_INT 0x00000010 +#define XMIT_DMA_INT 0x00000008 +#define EMAC_INT 0x00000004 +#define ERROR_INT 0x00000001 + +/* TX and RX FIFO registers */ + +#define FIFO_RX_RESET 0x2010 + +#define FIFO_TX_DATA 0x2100 +#define FIFO_RX_DATA 0x2200 + +/* TX and RX DMA registers */ + +#define TX_RESET_REG 0x2300 + +#define TX_SWCR_REG 0x231c +#define RX_SWCR_REG 0x235c +#define SWCR_SG_ENABLE 0x80000000 + +#define TX_DMAC_REG 0x2304 +#define RX_DMAC_REG 0x2344 +#define DMACR_SOURCE_INCR 0x80000000 +#define DMACR_DEST_INCR 0x40000000 +#define DMACR_SOURCE_LOCAL 0x20000000 +#define DMACR_DEST_LOCAL 0x10000000 +#define DMACR_SG_DISABLE 0x08000000 +#define DMACR_GEN_BD_INTR 0x04000000 +#define DMACR_LAST_BD 0x02000000 + +#define TX_DMAS_REG 0x2314 +#define RX_DMAS_REG 0x2354 +#define DMASR_SG_BUSY 0x08000000 + +#define TX_DMA_BDA 0x2318 +#define RX_DMA_BDA 0x2358 + +#define TX_DMA_ISREG 0x232C +#define RX_DMA_ISREG 0x236C +#define TX_DMA_IEREG 0x2330 +#define RX_DMA_IEREG 0x2370 + +#define DMA_IX_DMA_DONE 1 +#define DMA_IX_DMA_ERROR 2 +#define DMA_IX_PKT_DONE 4 +#define DMA_IX_PKT_THRESHOLD 8 +#define DMA_IX_PKT_WAITBOUND 16 +#define DMA_IX_SG_DISABLE_ACK 32 +#define DMA_IX_SG_END 64 +#define DMA_IX_BD_DONE 128 + +#define RX_DMA_PKTCNT 0x2360 + +#endif diff -Nawur linuxppc_2_4/include/asm-ppc/ibm4xx.h linuxppc_2_4-virtex/include/asm-ppc/ibm4xx.h --- linuxppc_2_4/include/asm-ppc/ibm4xx.h 2005-05-18 10:14:21.000000000 +0200 +++ linuxppc_2_4-virtex/include/asm-ppc/ibm4xx.h 2005-07-04 14:26:31.000000000 +0200 @@ -66,6 +66,10 @@ #include #endif +#if defined(CONFIG_VIRTEX) +#include +#endif + #ifndef PPC4xx_MACHINE_NAME #define PPC4xx_MACHINE_NAME "Unidentified 4xx class" #endif diff -Nawur linuxppc_2_4/include/asm-ppc/irq.h linuxppc_2_4-virtex/include/asm-ppc/irq.h --- linuxppc_2_4/include/asm-ppc/irq.h 2005-05-18 10:14:21.000000000 +0200 +++ linuxppc_2_4-virtex/include/asm-ppc/irq.h 2005-07-04 14:26:31.000000000 +0200 @@ -50,7 +50,8 @@ #define NR_AIC_IRQS 32 #define NR_IRQS (NR_AIC_IRQS + NR_BOARD_IRQS) - +#elif defined (CONFIG_VIRTEX) +#define NR_IRQS 16 #elif !defined (CONFIG_403) /* diff -Nawur linuxppc_2_4/include/asm-ppc/processor.h linuxppc_2_4-virtex/include/asm-ppc/processor.h --- linuxppc_2_4/include/asm-ppc/processor.h 2005-05-18 10:14:22.000000000 +0200 +++ linuxppc_2_4-virtex/include/asm-ppc/processor.h 2005-07-04 14:26:31.000000000 +0200 @@ -36,7 +36,13 @@ #define MSR_EE (1<<15) /* External Interrupt Enable */ #define MSR_PR (1<<14) /* Problem State / Privilege Level */ #define MSR_FP (1<<13) /* Floating Point enable */ + +#ifndef CONFIG_VIRTEX_MCE_IGNORE #define MSR_ME (1<<12) /* Machine Check Enable */ +#else +#define MSR_ME 0 +#endif + #define MSR_FE0 (1<<11) /* Floating Exception mode 0 */ #define MSR_SE (1<<10) /* Single Step */ #define MSR_DWE (1<<10) /* Debug Wait Enable (4xx) */ diff -Nawur linuxppc_2_4/Makefile linuxppc_2_4-virtex/Makefile --- linuxppc_2_4/Makefile 2005-05-18 10:13:13.000000000 +0200 +++ linuxppc_2_4-virtex/Makefile 2005-06-29 15:08:17.000000000 +0200 @@ -91,7 +91,7 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) -CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O1 \ -fno-strict-aliasing -fno-common ifndef CONFIG_FRAME_POINTER CFLAGS += -fomit-frame-pointer