Commit d4da6773 authored by Ralf Bächle's avatar Ralf Bächle Committed by Linus Torvalds

[PATCH] Baget update

This is an update for the Russian Baget industrial controller.  Suffers a
bit from bitrot, the authors have promised an update to me.
parent a16b2ff6
......@@ -4,10 +4,11 @@
#
obj-y := baget.o print.o setup.o time.o irq.o bagetIRQ.o \
reset.o wbflush.o
obj-$(CONFIG_SERIAL) += vacserial.o
reset.o
obj-$(CONFIG_VAC_RTC) += vacrtc.o
EXTRA_AFLAGS := $(CFLAGS)
bagetIRQ.o : bagetIRQ.S
$(CC) $(CFLAGS) -c -o $@ $<
......@@ -28,7 +29,7 @@ dummy.c:
dummy.o: dummy.c image.bin ramdisk.bin
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJCOPY) --add-section=.vmlinux=image.bin \
--add-section=.ramdisk=ramdisk.bin $@
--add-section=.ramdisk=ramdisk.bin $@
balo.h: image
$(NM) $< | awk ' \
......@@ -39,13 +40,13 @@ balo.h: image
/balo_ramdisk_size/ { printf "#define RAMDISK_SIZE 0x%s\n", $$1 } \
' > $@
balo.o: balo.c balo.h
$(CC) $(CFLAGS) -c $<
$(CC) $(CFLAGS) -c $<
balo_supp.o: balo_supp.S
$(CC) $(CFLAGS) -c $<
balo: balo.o dummy.o balo_supp.o print.o
$(LD) $(LDFLAGS) -T ld.script.balo -o $@ $^
$(LD) $(LDFLAGS) -T ld.script.balo -o $@ $^
clean:
rm -f balo balo.h dummy.c image image.bin
......
/* $Id: baget.c,v 1.1 1999/01/17 03:49:37 ralf Exp $
*
/*
* baget.c: Baget low level stuff
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
......@@ -21,7 +20,7 @@
* Following code is based on routines from 'mm/vmalloc.c'
* Additional parameters ioaddr is needed to iterate across real I/O address.
*/
static inline int alloc_area_pte(pte_t * pte, unsigned long address,
static inline int alloc_area_pte(pte_t * pte, unsigned long address,
unsigned long size, unsigned long ioaddr)
{
unsigned long end;
......@@ -36,7 +35,7 @@ static inline int alloc_area_pte(pte_t * pte, unsigned long address,
printk("kseg2_alloc_io: page already exists\n");
/*
* For MIPS looks pretty to have transparent mapping
* for KSEG2 areas -- user can't access one, and no
* for KSEG2 areas -- user can't access one, and no
* problems with virtual <--> physical translation.
*/
page = ioaddr & PAGE_MASK;
......@@ -50,7 +49,7 @@ static inline int alloc_area_pte(pte_t * pte, unsigned long address,
return 0;
}
static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address,
static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address,
unsigned long size, unsigned long ioaddr)
{
unsigned long end;
......
/* $Id: bagetIRQ.S,v 1.1 1999/01/17 03:49:37 ralf Exp $
/*
* bagetIRQ.S: Interrupt exception dispatch code for Baget/MIPS
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
......@@ -25,28 +24,28 @@ NESTED(bagetIRQ, PT_SIZE, sp)
.set push
.set noreorder
jal a1
.set pop
move a0, sp
.set pop
move a0, sp
la a1, ret_from_irq
jr a1
END(bagetIRQ)
#define DBE_HANDLER 0x1C
NESTED(try_read, PT_SIZE, sp)
mfc0 t3, CP0_STATUS # save flags and
CLI # disable interrupts
li t0, KSEG2
sltu t1, t0, a0 # Is it KSEG2 address ?
beqz t1, mapped # No - already mapped !
move t0, a0
sltu t1, t0, a0 # Is it KSEG2 address ?
beqz t1, mapped # No - already mapped !
move t0, a0
ori t0, 0xfff
xori t0, 0xfff # round address to page
ori t1, t0, 0xf00 # prepare EntryLo (N,V,D,G)
ori t1, t0, 0xf00 # prepare EntryLo (N,V,D,G)
mfc0 t2, CP0_ENTRYHI # save ASID value
mtc0 zero, CP0_INDEX
......@@ -57,15 +56,15 @@ NESTED(try_read, PT_SIZE, sp)
tlbwi # ... and write ones
nop
nop
mtc0 t2, CP0_ENTRYHI
mapped:
mtc0 t2, CP0_ENTRYHI
mapped:
la t0, exception_handlers
lw t1, DBE_HANDLER(t0) # save real handler
la t2, dbe_handler
la t2, dbe_handler
sw t2, DBE_HANDLER(t0) # set temporary local handler
li v0, -1 # default (failure) value
li t2, 1
beq t2, a1, 1f
li t2, 2
......@@ -81,13 +80,13 @@ mapped:
b out
4: lw v0, (a0) # word
out:
out:
sw t1, DBE_HANDLER(t0) # restore real handler
mtc0 t3, CP0_STATUS # restore CPU flags
jr ra
dbe_handler:
jr ra
dbe_handler:
li v0, -1 # mark our failure
.set push
.set noreorder
......
/* $Id$
*
/*
* balo.c: BAget LOader
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*
*/
#include <linux/kernel.h>
#include <asm/system.h>
......@@ -18,7 +16,7 @@ static char *banner = "\nBaget Linux Loader v0.2\n";
static void mem_move (long *to, long *from, long size)
{
while (size > 0) {
while (size > 0) {
*to++ = *from++;
size -= sizeof(long);
}
......@@ -42,27 +40,29 @@ static volatile enum balo_state_enum {
static __inline__ void reset_and_jump(int start, int mem_upper)
{
unsigned long tmp;
__asm__ __volatile__(
".set\tnoreorder\n\t"
".set\tnoat\n\t"
"mfc0\t$1,$12\n\t"
"mfc0\t$1, $12\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"ori\t$1,$1,0xff00\n\t"
"xori\t$1,$1,0xff00\n\t"
"mtc0\t$1,$12\n\t"
"ori\t$1, $1, 0xff00\n\t"
"xori\t$1, $1, 0xff00\n\t"
"mtc0\t$1, $12\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"move\t$4,%1\n\t"
"jr\t%0\n\t"
"move\t%0, %2\n\t"
"jr\t%1\n\t"
"nop\n\t"
".set\tat\n\t"
".set\treorder"
: /* no outputs */
:"Ir" (start), "Ir" (mem_upper)
:"$1", "$4", "memory");
".set\treorder"
: "=&r" (tmp)
: "Ir" (start), "Ir" (mem_upper)
: "memory");
}
static void start_kernel(void)
......@@ -71,7 +71,7 @@ static void start_kernel(void)
extern char _ramdisk_start, _ramdisk_end;
outs( "Relocating Linux... " );
mem_move((long*)KSEG0, (long*)&_vmlinux_start,
mem_move((long*)KSEG0, (long*)&_vmlinux_start,
&_vmlinux_end-&_vmlinux_start);
outs("done.\n");
......@@ -86,7 +86,7 @@ static void start_kernel(void)
outs("done.\n");
}
{
{
extern void flush_cache_low(int isize, int dsize);
flush_cache_low(256*1024,256*1024);
}
......@@ -102,10 +102,10 @@ static void mem_probe(void)
balo_state = MEM_PROBE;
outs("RAM: <");
while(mem_limit < mem_limit_dbe) {
if (can_write(mem_limit) && *mem_limit != 0)
if (can_write(mem_limit) && *mem_limit != 0)
break; /* cycle found */
outc('.');
if (can_write(mem_limit))
if (can_write(mem_limit))
*mem_limit = -1; /* mark */
mem_limit += 0x40000;
}
......@@ -124,7 +124,7 @@ static void print_regs(void)
}
void int_handler(struct pt_regs *regs)
{
{
switch (balo_state) {
case BALO_INIT:
balo_printf("\nBALO: trap in balo itself.\n");
......@@ -162,7 +162,7 @@ static void mem_init(void)
while(1) {
*mem_limit_dbe;
if (can_write(mem_limit_dbe))
if (can_write(mem_limit_dbe))
*mem_limit_dbe = 0;
mem_limit_dbe += 0x40000; /* +1M */
......@@ -174,7 +174,7 @@ void balo_entry(void)
{
extern void except_vec3_generic(void);
cli();
cli();
outs(banner);
memcpy((void *)(KSEG0 + 0x80), &except_vec3_generic, 0x80);
mem_init();
......
/* $Id: balo_supp.S,v 1.1 1999/01/17 03:49:38 ralf Exp $
/*
* balo_supp.S: BAget Loader supplement
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
.text
.set mips1
/* General exception vector. */
NESTED(except_vec3_generic, 0, sp)
.set noat
......@@ -21,7 +20,7 @@ NESTED(except_vec3_generic, 0, sp)
END(except_vec3_generic)
NESTED(except_vec3_generic_code, 0, sp)
SAVE_ALL
SAVE_ALL
mfc0 k1, CP0_CAUSE
la k0, int_cause
sw k1, (k0)
......@@ -34,7 +33,7 @@ NESTED(except_vec3_generic_code, 0, sp)
la k0, badvaddr
sw k1, (k0)
la k0, int_handler
la k0, int_handler
.set noreorder
jal k0
.set reorder
......@@ -48,7 +47,7 @@ NESTED(flush_cache_low, PT_SIZE, sp)
.set at
.set macro
.set noreorder
move t1, a0 # ISIZE
move t2, a1 # DSIZE
......@@ -89,7 +88,7 @@ NESTED(flush_cache_low, PT_SIZE, sp)
sb zero, -8(t0)
bne t0, t1, 1b
sb zero, -4(t0)
la v0, 1f
or v0, KSEG1
j v0 # Run uncached
......
......@@ -17,7 +17,6 @@
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <asm/bitops.h>
#include <asm/bootinfo.h>
......@@ -28,24 +27,24 @@
#include <asm/baget/baget.h>
unsigned long spurious_count = 0;
volatile unsigned long irq_err_count;
/*
* This table is a correspondence between IRQ numbers and CPU PILs
*/
static int irq_to_pil_map[BAGET_IRQ_NR] = {
static int irq_to_pil_map[BAGET_IRQ_NR] = {
7/*fixme: dma_err -1*/,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 0x00 - 0x0f */
-1,-1,-1,-1, 3,-1,-1,-1, 2, 2, 2,-1, 3,-1,-1,3/*fixme: lance*/, /* 0x10 - 0x1f */
-1,-1,-1,-1,-1,-1, 5,-1,-1,-1,-1,-1, 7,-1,-1,-1, /* 0x20 - 0x2f */
-1, 3, 2/*fixme systimer:3*/, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 /* 0x30 - 0x3f */
};
static inline int irq_to_pil(int irq_nr)
static inline int irq_to_pil(int irq_nr)
{
int pil = -1;
if (irq_nr >= BAGET_IRQ_NR)
if (irq_nr >= BAGET_IRQ_NR)
baget_printk("irq_to_pil: too large irq_nr = 0x%x\n", irq_nr);
else {
pil = irq_to_pil_map[irq_nr];
......@@ -60,13 +59,13 @@ static inline int irq_to_pil(int irq_nr)
static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask)
{
unsigned long status = read_32bit_cp0_register(CP0_STATUS);
unsigned long status = read_c0_status();
status &= ~((clr_mask & 0xFF) << 8);
status |= (set_mask & 0xFF) << 8;
write_32bit_cp0_register(CP0_STATUS, status);
write_c0_status(status);
}
/*
/*
* These two functions may be used for unconditional IRQ
* masking via their PIL protection.
*/
......@@ -92,26 +91,26 @@ static inline void unmask_irq(unsigned int irq_nr)
static volatile unsigned int pil_in_use[BAGET_PIL_NR] = { 0, };
void mask_irq_count(int irq_nr)
void mask_irq_count(int irq_nr)
{
unsigned long flags;
int pil = irq_to_pil(irq_nr);
save_and_cli(flags);
local_irq_save(flags);
if (!--pil_in_use[pil])
mask_irq(irq_nr);
restore_flags(flags);
local_irq_restore(flags);
}
void unmask_irq_count(int irq_nr)
void unmask_irq_count(int irq_nr)
{
unsigned long flags;
int pil = irq_to_pil(irq_nr);
save_and_cli(flags);
local_irq_save(flags);
if (!pil_in_use[pil]++)
unmask_irq(irq_nr);
restore_flags(flags);
local_irq_restore(flags);
}
/*
......@@ -122,18 +121,18 @@ void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
save_and_cli(flags);
local_irq_save(flags);
mask_irq(irq_nr);
restore_flags(flags);
local_irq_restore(flags);
}
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
save_and_cli(flags);
local_irq_save(flags);
unmask_irq(irq_nr);
restore_flags(flags);
local_irq_restore(flags);
}
/*
......@@ -142,31 +141,31 @@ void enable_irq(unsigned int irq_nr)
*/
static struct irqaction *irq_action[BAGET_IRQ_NR] = { NULL, };
int show_interrupts(struct seq_file *p, void *v)
int get_irq_list(char *buf)
{
int i;
int i, len = 0;
struct irqaction * action;
unsigned long flags;
for (i = 0 ; i < BAGET_IRQ_NR ; i++) {
local_irq_save(flags);
action = irq_action[i];
if (!action)
goto skip;
seq_printf(p, "%2d: %8d %c %s",
if (!action)
gotos skip;
len += sprintf(buf+len, "%2d: %8d %c %s",
i, kstat_cpu(0).irqs[i],
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
seq_printf(p, ",%s %s",
len += sprintf(buf+len, ",%s %s",
(action->flags & SA_INTERRUPT) ? " +" : "",
action->name);
}
seq_putc(p, '\n');
len += sprintf(buf+len, "\n");
skip:
local_irq_restore(flags);
}
return 0;
return len;
}
......@@ -183,10 +182,10 @@ static void do_IRQ(int irq, struct pt_regs * regs)
int do_random, cpu;
cpu = smp_processor_id();
irq_enter(cpu, irq);
kstat_cpu(cpu).irqs[irq]++;
irq_enter();
kstat_cpus(cpu)[irq]++;
mask_irq(irq);
mask_irq(irq);
action = *(irq + irq_action);
if (action) {
if (!(action->flags & SA_INTERRUPT))
......@@ -205,7 +204,7 @@ static void do_IRQ(int irq, struct pt_regs * regs)
printk("do_IRQ: Unregistered IRQ (0x%X) occurred\n", irq);
}
unmask_irq(irq);
irq_exit(cpu, irq);
irq_exit();
/* unmasking and bottom half handling is done magically for us. */
}
......@@ -213,14 +212,14 @@ static void do_IRQ(int irq, struct pt_regs * regs)
/*
* What to do in case of 'no VIC register available' for current interrupt
*/
static void vic_reg_error(unsigned long address, unsigned char active_pils)
static void vic_reg_error(unsigned long address, unsigned char active_pils)
{
printk("\nNo VIC register found: reg=%08lx active_pils=%02x\n"
"Current interrupt mask from CP0_CAUSE: %02x\n",
address, 0xff & active_pils,
0xff & (read_32bit_cp0_register(CP0_CAUSE)>>8));
"Current interrupt mask from CP0_CAUSE: %02x\n",
address, 0xff & active_pils,
0xff & (read_c0_cause()>>8));
{ int i; for (i=0; i<10000; i++) udelay(1000); }
}
}
static char baget_fpu_irq = BAGET_FPU_IRQ;
#define BAGET_INT_FPU {(unsigned long)&baget_fpu_irq, 1}
......@@ -230,27 +229,27 @@ static char baget_fpu_irq = BAGET_FPU_IRQ;
*/
asmlinkage void baget_interrupt(struct pt_regs *regs)
{
static struct baget_int_reg int_reg[BAGET_PIL_NR] = {
static struct baget_int_reg int_reg[BAGET_PIL_NR] = {
BAGET_INT_NONE, BAGET_INT_NONE, BAGET_INT0_ACK, BAGET_INT1_ACK,
BAGET_INT_NONE, BAGET_INT_FPU, BAGET_INT_NONE, BAGET_INT5_ACK
BAGET_INT_NONE, BAGET_INT_FPU, BAGET_INT_NONE, BAGET_INT5_ACK
};
unsigned char active_pils;
while ((active_pils = read_32bit_cp0_register(CP0_CAUSE)>>8)) {
while ((active_pils = read_c0_cause()>>8)) {
int pil;
struct baget_int_reg* reg;
for (pil = 0; pil < BAGET_PIL_NR; pil++) {
if (!(active_pils & (1<<pil))) continue;
reg = &int_reg[pil];
if (reg->address) {
extern int try_read(unsigned long,int);
int irq = try_read(reg->address, reg->size);
if (irq != -1)
if (irq != -1)
do_IRQ(BAGET_IRQ_MASK(irq), regs);
else
else
vic_reg_error(reg->address, active_pils);
} else {
printk("baget_interrupt: unknown interrupt "
......@@ -291,9 +290,9 @@ int setup_baget_irq(int irq, struct irqaction * new)
if (new->flags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
save_and_cli(flags);
local_irq_save(flags);
*p = new;
restore_flags(flags);
local_irq_restore(flags);
if (!shared) {
unmask_irq_count(irq);
......@@ -302,9 +301,9 @@ int setup_baget_irq(int irq, struct irqaction * new)
return 0;
}
int request_irq(unsigned int irq,
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
unsigned long irqflags,
const char * devname,
void *dev_id)
{
......@@ -315,12 +314,12 @@ int request_irq(unsigned int irq,
return -EINVAL;
if (!handler)
return -EINVAL;
if (irq_to_pil_map[irq] < 0)
if (irq_to_pil_map[irq] < 0)
return -EINVAL;
action = (struct irqaction *)
kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
if (!action)
return -ENOMEM;
action->handler = handler;
......@@ -337,13 +336,13 @@ int request_irq(unsigned int irq,
return retval;
}
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action, **p;
unsigned long flags;
if (irq >= BAGET_IRQ_NR)
if (irq >= BAGET_IRQ_NR)
printk("Trying to free IRQ%d\n",irq);
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
......@@ -351,11 +350,11 @@ void free_irq(unsigned int irq, void *dev_id)
continue;
/* Found it - now free it */
save_and_cli(flags);
local_irq_save(flags);
*p = action->next;
if (!irq[irq_action])
unmask_irq_count(irq);
restore_flags(flags);
local_irq_restore(flags);
kfree(action);
return;
}
......@@ -380,7 +379,7 @@ static void write_err_interrupt(int irq, void *dev_id, struct pt_regs * regs)
*(volatile char*) BAGET_WRERR_ACK = 0;
}
static struct irqaction irq0 =
static struct irqaction irq0 =
{ write_err_interrupt, SA_INTERRUPT, 0, "bus write error", NULL, NULL};
void __init init_IRQ(void)
......@@ -393,6 +392,6 @@ void __init init_IRQ(void)
/* Enable interrupts for pils 2 and 3 (lines 0 and 1) */
modify_cp0_intmask(0, (1<<2)|(1<<3));
if (setup_baget_irq(0, &irq0) < 0)
if (setup_baget_irq(0, &irq0) < 0)
printk("init_IRQ: unable to register write_err irq\n");
}
OUTPUT_FORMAT("elf32-bigmips")
OUTPUT_FORMAT("elf32-tradbigmips")
OUTPUT_ARCH(mips)
ENTRY(balo_entry)
SECTIONS
......@@ -42,13 +42,13 @@ SECTIONS
/* Startup code */
. = ALIGN(4096);
__init_begin = .;
*(.text.init)
*(.data.init)
*(.text.init)
*(.data.init)
. = ALIGN(4096); /* Align double page for init_task_union */
__init_end = .;
*(.fini)
*(.reginfo)
*(.fini)
*(.reginfo)
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. It would
be more correct to do this:
......@@ -68,18 +68,18 @@ SECTIONS
*(.data)
CONSTRUCTORS
*(.data1)
*(.data1)
_gp = . + 0x8000;
*(.lit8)
*(.lit4)
*(.ctors)
*(.dtors)
*(.got.plt) *(.got)
*(.dynamic)
*(.lit8)
*(.lit4)
*(.ctors)
*(.dtors)
*(.got.plt) *(.got)
*(.dynamic)
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
*(.sdata)
*(.sdata)
_edata = .;
PROVIDE (edata = .);
......@@ -96,21 +96,21 @@ SECTIONS
/* These are needed for ELF backends which have not yet been
converted to the new style linker. */
*(.stab)
*(.stabstr)
*(.stab)
*(.stabstr)
/* DWARF debug sections.
Symbols in the .debug DWARF section are relative to the beginning of the
section so we begin .debug at 0. It's not clear yet what needs to happen
for the others. */
*(.debug)
*(.debug_srcinfo)
*(.debug_aranges)
*(.debug_pubnames)
*(.debug_sfnames)
*(.line)
*(.debug)
*(.debug_srcinfo)
*(.debug_aranges)
*(.debug_pubnames)
*(.debug_sfnames)
*(.line)
/* These must appear regardless of . */
*(.gptab.data) *(.gptab.sdata)
*(.gptab.bss) *(.gptab.sbss)
*(.gptab.data) *(.gptab.sdata)
*(.gptab.bss) *(.gptab.sbss)
_vmlinux_start = .;
*(.vmlinux)
......
/* $Id: print.c,v 1.1 1999/01/17 03:49:38 ralf Exp $
*
/*
* print.c: Simple print fascility
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*
*/
#include <stdarg.h>
#include <linux/kernel.h>
......@@ -16,7 +14,7 @@
*/
// #define BAGET_PRINTK
/*
/*
* This function is same for BALO and Linux baget_printk,
* and normally prints characted to second (UART A) console.
*/
......@@ -27,7 +25,7 @@ static void outc_low(char c)
{
int i;
vac_outb(c, VAC_UART_B_TX);
for (i=0; i<10000; i++)
for (i=0; i<10000; i++)
delay();
}
......@@ -38,7 +36,7 @@ void outc(char c)
outc_low(c);
}
void outs(char *s)
void outs(char *s)
{
while(*s) outc(*s++);
}
......@@ -79,7 +77,7 @@ static __inline__ void puthex( int a )
void __init balo_printf( char *f, ... )
{
int *arg = (int*)&f + 1;
char c;
char c;
int format = 0;
while((c = *f++) != 0) {
......@@ -112,7 +110,7 @@ void __init balo_printf( char *f, ... )
}
void __init balo_hungup(void)
{
{
outs("Hunging up.\n");
while(1);
while(1);
}
/*
* init.c: PROM library initialisation code.
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#include <linux/init.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
char arcs_cmdline[COMMAND_LINE_SIZE];
char arcs_cmdline[CL_SIZE];
const char *get_system_type(void)
{
/* Should probably return one of "BT23-201", "BT23-202" */
return "Baget";
}
void __init prom_init(unsigned int mem_upper)
{
......
/* $Id: setup.c,v 1.4 1999/10/09 00:00:57 ralf Exp $
*
/*
* setup.c: Baget/MIPS specific setup, including init of the feature struct.
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*
*/
#include <linux/init.h>
#include <linux/kernel.h>
......@@ -17,9 +15,9 @@
long int vac_memory_upper;
#define CACHEABLE_STR(val) ((val) ? "not cached" : "cached")
static void __init vac_show(void)
{
{
int i;
unsigned short val, decode = vac_inw(VAC_DECODE_CTRL);
unsigned short a24_base = vac_inw(VAC_A24_BASE);
......@@ -38,7 +36,7 @@ static void __init vac_show(void)
VAC_IOSEL3_CTRL,
VAC_IOSEL4_CTRL,
VAC_IOSEL5_CTRL };
printk("[DSACKi %s, DRAMCS%s qualified, boundary%s qualified%s]\n",
(decode & VAC_DECODE_DSACKI) ? "on" : "off",
(decode & VAC_DECODE_QFY_DRAMCS) ? "" : " not",
......@@ -72,24 +70,24 @@ static void __init vac_show(void)
VAC_ICFSEL_MODULE_VAL(vac_inw(VAC_ICFSEL_BASE)))<<4,
(decode & VAC_DECODE_QFY_ICFSEL) ? "qualified" : "");
printk("region0 at 00000000 (%dMB)\t[dram, %s, delay %d cpuclk"
", cached]\n",
(vac_inw(VAC_DRAM_MASK)+1)>>4,
(decode & VAC_DECODE_DSACK) ? "D32" : "3state",
VAC_DECODE_CPUCLK_VAL(decode));
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) {
unsigned long from =
unsigned long from =
((unsigned long)vac_inw(bndr[i]))<<16;
unsigned long to =
unsigned long to =
((unsigned long)
((i+1 == sizeof(bndr)/sizeof(bndr[0])) ?
((i+1 == sizeof(bndr)/sizeof(bndr[0])) ?
0xff00 : vac_inw(bndr[i+1])))<<16;
val = vac_inw(regs[i]);
printk("region%d at %08lx (%dMB)\t[%s %s/%s, %s]\n",
printk("region%d at %08lx (%dMB)\t[%s %s/%s, %s]\n",
i+1,
from,
(unsigned int)((to - from) >> 20),
......@@ -97,13 +95,13 @@ static void __init vac_show(void)
asiz[VAC_REG_ASIZ_VAL(val)],
((val & VAC_REG_WORD) ? "D16" : "D32"),
CACHEABLE_STR(val&VAC_A24_A24_CACHINH));
if (a24_addr >= from && a24_addr < to)
printk("\ta24 at %08lx (%dMB)\t[vme, A24/%s, %s]\n",
a24_addr,
min((unsigned int)(a24_addr - from)>>20, 32U),
(a24_base & VAC_A24_DATAPATH) ? "user" :
((a24_base & VAC_A24_D32_ENABLE) ?
((a24_base & VAC_A24_D32_ENABLE) ?
"D32" : "D16"),
CACHEABLE_STR(a24_base & VAC_A24_A24_CACHINH));
}
......@@ -123,7 +121,7 @@ static void __init vac_show(void)
(VAC_CTRL_DELAY_IOWR_VAL(val)&1) ? ".5" : "",
VAC_CTRL_DELAY_IOSELI_VAL(val)/2,
(VAC_CTRL_DELAY_IOSELI_VAL(val)&1) ? ".5" : "");
printk("region5 at fff00000 (896KB)\t[local io, %s]\n",
CACHEABLE_STR(vac_inw(VAC_A24_BASE) & VAC_A24_IO_CACHINH));
......@@ -132,7 +130,7 @@ static void __init vac_show(void)
printk("\tio%d[ack %d cpuclk%s, %s%srecovery %d cpuclk, "
"\n\t read %d%s cpuclk, write %d%s cpuclk, "
"assert %d%s%s cpuclk]\n",
i,
i,
VAC_CTRL_DELAY_DSACKI_VAL(val),
state[val & (VAC_CTRL_IORD|VAC_CTRL_IOWR)],
(val & VAC_CTRL_DSACK0) ? "dsack0*, " : "",
......@@ -144,15 +142,15 @@ static void __init vac_show(void)
(VAC_CTRL_DELAY_IOWR_VAL(val)&1) ? ".5" : "",
VAC_CTRL_DELAY_IOSELI_VAL(val)/2,
(VAC_CTRL_DELAY_IOSELI_VAL(val)&1) ? ".5" : "",
(vac_inw(VAC_DEV_LOC) & VAC_DEV_LOC_IOSEL(i)) ?
(vac_inw(VAC_DEV_LOC) & VAC_DEV_LOC_IOSEL(i)) ?
", id" : "");
}
printk("region6 at fffe0000 (128KB)\t[vme, A16/%s, "
"not cached]\n",
(a24_base & VAC_A24_A16D32_ENABLE) ?
(a24_base & VAC_A24_A16D32_ENABLE) ?
((a24_base & VAC_A24_A16D32) ? "D32" : "D16") : "user");
val = vac_inw(VAC_SHRCS_CTRL);
printk("shared[ack %d cpuclk%s, %s%srecovery %d cpuclk, "
"read %d%s, write %d%s, assert %d%s]\n",
......@@ -183,8 +181,8 @@ static void __init vac_init(void)
default:
panic("Unknown VAC revision number");
}
vac_outw(mem_limit-1, VAC_DRAM_MASK);
vac_outw(mem_limit-1, VAC_DRAM_MASK);
vac_outw(mem_limit, VAC_BNDR2);
vac_outw(mem_limit, VAC_BNDR3);
vac_outw(((BAGET_A24M_BASE>>16)&~VAC_A24_D32_ENABLE)|VAC_A24_DATAPATH,
......@@ -294,19 +292,19 @@ static void __init vac_start(void)
vac_outw(VAC_INT_CTRL_TIMER_PIO10|
VAC_INT_CTRL_UART_B_PIO7|
VAC_INT_CTRL_UART_A_PIO7,VAC_INT_CTRL);
/*
/*
* Set quadro speed for both UARTs.
* To do it we need use formulae from VIC/VAC manual,
* keeping in mind Baget's 50MHz frequency...
*/
vac_outw((500000/(384*16))<<8,VAC_CPU_CLK_DIV);
vac_outw((500000/(384*16))<<8,VAC_CPU_CLK_DIV);
}
static void __init vic_show(void)
{
unsigned char val;
char *timeout[] = { "4", "16", "32", "64", "128", "256", "disabled" };
char *deadlock[] = { "[dedlk only]", "[dedlk only]",
char *deadlock[] = { "[dedlk only]", "[dedlk only]",
"[dedlk], [halt w/ rmc], [lberr]",
"[dedlk], [halt w/o rmc], [lberr]" };
......@@ -319,7 +317,7 @@ static void __init vic_show(void)
printk("metastability delay ");
printk("%s ",
deadlock[VIC_IFACE_CFG_DEADLOCK_VAL(val)]);
printk("interrupts: ");
val = vic_inb(VIC_ERR_INT);
......@@ -332,7 +330,7 @@ static void __init vic_show(void)
if (!(val & VIC_ERR_INT_ACFAIL))
printk("[acfail] ");
printk("\n");
printk("timeouts: ");
val = vic_inb(VIC_XFER_TIMO);
printk("local %s, vme %s ",
......@@ -358,7 +356,7 @@ static void __init vic_show(void)
printk("[local boundary cross]");
if (val & VIC_BXFER_DEF_VME_CROSS)
printk("[vme boundary cross]");
}
static void __init vic_init(void)
......@@ -373,7 +371,7 @@ static void __init vic_init(void)
vic_outb(VIC_INT_IPL(3)|VIC_INT_DISABLE,VIC_VME_INT2);
vic_outb(VIC_INT_IPL(3)|VIC_INT_DISABLE,VIC_VME_INT3);
vic_outb(VIC_INT_IPL(3)|VIC_INT_DISABLE,VIC_VME_INT4);
/*
/*
vic_outb(VIC_INT_IPL(3)|VIC_INT_DISABLE, VIC_VME_INT5);
*/
vic_outb(VIC_INT_IPL(3)|VIC_INT_DISABLE, VIC_VME_INT6);
......@@ -388,7 +386,7 @@ static void __init vic_init(void)
VIC_INT_HIGH|VIC_INT_DISABLE, VIC_LINT3);
vic_outb(VIC_INT_IPL(3)|VIC_INT_NOAUTO|VIC_INT_EDGE|
VIC_INT_LOW|VIC_INT_DISABLE, VIC_LINT4);
/*
/*
vic_outb(VIC_INT_IPL(3)|VIC_INT_NOAUTO|VIC_INT_LEVEL|
VIC_INT_LOW|VIC_INT_DISABLE, VIC_LINT5);
*/
......@@ -447,7 +445,7 @@ static void __init vic_init(void)
VIC_RELEASE_RWD, VIC_RELEASE);
vic_outb(VIC_IC6_RUN, VIC_IC6);
vic_outb(0, VIC_IC7);
vic_show();
}
......@@ -471,7 +469,7 @@ void __init baget_irq_setup(void)
extern void baget_machine_restart(char *command);
extern void baget_machine_halt(void);
extern void baget_machine_power_off(void);
void __init baget_setup(void)
{
printk("BT23/63-201n found.\n");
......
......@@ -20,23 +20,23 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/system.h>
#include <asm/baget/baget.h>
/*
/*
* To have precision clock, we need to fix available clock frequency
*/
#define FREQ_NOM 79125 /* Baget frequency ratio */
#define FREQ_DEN 10000
static inline int timer_intr_valid(void)
static inline int timer_intr_valid(void)
{
static unsigned long long ticks, valid_ticks;
if (ticks++ * FREQ_DEN >= valid_ticks * FREQ_NOM) {
/*
* We need no overflow checks,
/*
* We need no overflow checks,
* due baget unable to work 3000 years...
* At least without reboot...
*/
......@@ -62,10 +62,10 @@ static void __init timer_enable(void)
vic_outb(ss0cr0, VIC_SS0CR0);
vic_outb(VIC_INT_IPL(6)|VIC_INT_NOAUTO|VIC_INT_EDGE|
VIC_INT_LOW|VIC_INT_ENABLE, VIC_LINT2);
VIC_INT_LOW|VIC_INT_ENABLE, VIC_LINT2);
}
static struct irqaction timer_irq =
static struct irqaction timer_irq =
{ timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
void __init time_init(void)
......@@ -82,17 +82,19 @@ void do_gettimeofday(struct timeval *tv)
do {
seq = read_seqbegin(&xtime_lock);
*tv = xtime;
tv->tv_sec = xtime.tv_sec;
tv->tv_usec = xtime.tv_nsec / 1000;
} while (read_seqretry(&xtime_lock, seq));
}
void do_settimeofday(struct timeval *tv)
{
write_seqlock_irq (&xtime_lock);
xtime = *tv;
write_seqlock_irq(&xtime_lock);
xtime.tv_usec = tv->tv_sec;
xtime.tv_nsec = tv->tv_usec;
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq (&xtime_lock);
write_sequnlock_irq(&xtime_lock);
}
/*
* vacserial.c: VAC UART serial driver
* This code stealed and adopted from linux/drivers/char/serial.c
* See that for author info
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#undef SERIAL_PARANOIA_CHECK
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
#define CONFIG_SERIAL_SHARE_IRQ
/* Set of debugging defines */
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
#define RS_STROBE_TIME (10*HZ)
#define RS_ISR_PASS_LIMIT 2 /* Beget is not a super-computer (old=256) */
#define IRQ_T(state) \
((state->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
#define SERIAL_INLINE
#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
#define DBG_CNT(s) baget_printk("(%s):[%x] refc=%d, serc=%d, ttyc=%d-> %s\n", \
tty->name,(info->flags),serial_driver->refcount,info->count,tty->count,s)
#else
#define DBG_CNT(s)
#endif
#define QUAD_UART_SPEED /* Useful for Baget */
/*
* End of serial driver configuration section.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#ifdef CONFIG_SERIAL_CONSOLE
#include <linux/console.h>
#endif
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/serial.h>
#include <asm/baget/baget.h>
#define BAGET_VAC_UART_IRQ 0x35
/*
* Implementation note:
* It was descovered by means of advanced electronic tools,
* if the driver works via TX_READY interrupts then VIC generates
* strange self-eliminating traps. Thus, the driver is rewritten to work
* via TX_EMPTY
*/
/* VAC-specific check/debug switches */
#undef CHECK_REG_INDEX
#undef DEBUG_IO_PORT_A
#ifdef SERIAL_INLINE
#define _INLINE_ inline
#endif
static char *serial_name = "VAC Serial driver";
static char *serial_version = "4.26";
static DECLARE_TASK_QUEUE(tq_serial);
static struct tty_driver *serial_driver;
/* 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_SERIAL_CONSOLE
static struct console sercons;
#endif
static void autoconfig(struct serial_state * info);
static void change_speed(struct async_struct *info);
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
static void rs_timer(unsigned long dummy);
static struct timer_list vacs_timer;
/*
* Here we define the default xmit fifo size used for each type of
* UART
*/
static struct serial_uart_config uart_config[] = {
{ "unknown", 1, 0 }, /* Must go first -- used as unassigned */
{ "VAC UART", 1, 0 }
};
#define VAC_UART_TYPE 1 /* Just index in above array */
static struct serial_state rs_table[] = {
/*
* VAC has tricky layout for pair of his SIO registers,
* so we need special function to access ones.
* To identify port we use their TX offset
*/
{ 0, 9600, VAC_UART_B_TX, BAGET_VAC_UART_IRQ,
STD_COM_FLAGS }, /* VAC UART B */
{ 0, 9600, VAC_UART_A_TX, BAGET_VAC_UART_IRQ,
STD_COM_FLAGS } /* VAC UART A */
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#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;
static DECLARE_MUTEX(tmp_buf_sem);
static inline int serial_paranoia_check(struct async_struct *info,
char *name, 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, name, routine);
return 1;
}
if (info->magic != SERIAL_MAGIC) {
printk(badmagic, name, routine);
return 1;
}
#endif
return 0;
}
/*
To unify UART A/B access we will use following function
to compute register offsets by register index.
*/
#define VAC_UART_MODE 0
#define VAC_UART_TX 1
#define VAC_UART_RX 2
#define VAC_UART_INT_MASK 3
#define VAC_UART_INT_STATUS 4
#define VAC_UART_REG_NR 5
static inline int uart_offset_map(unsigned long port, int reg_index)
{
static const unsigned int ind_to_reg[VAC_UART_REG_NR][NR_PORTS] = {
{ VAC_UART_B_MODE, VAC_UART_A_MODE },
{ VAC_UART_B_TX, VAC_UART_A_TX },
{ VAC_UART_B_RX, VAC_UART_A_RX },
{ VAC_UART_B_INT_MASK, VAC_UART_A_INT_MASK },
{ VAC_UART_B_INT_STATUS, VAC_UART_A_INT_STATUS }
};
#ifdef CHECK_REG_INDEX
if (reg_index > VAC_UART_REG_NR) panic("vacserial: bad reg_index");
#endif
return ind_to_reg[reg_index][port == VAC_UART_B_TX ? 0 : 1];
}
static inline unsigned int serial_inw(struct async_struct *info, int offset)
{
int val = vac_inw(uart_offset_map(info->port,offset));
#ifdef DEBUG_IO_PORT_A
if (info->port == VAC_UART_A_TX)
printk("UART_A_IN: reg = 0x%04x, val = 0x%04x\n",
uart_offset_map(info->port,offset), val);
#endif
return val;
}
static inline unsigned int serial_inp(struct async_struct *info, int offset)
{
return serial_inw(info, offset);
}
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
return serial_inw(info, offset);
}
static inline void serial_outw(struct async_struct *info,int offset, int value)
{
#ifdef DEBUG_IO_PORT_A
if (info->port == VAC_UART_A_TX)
printk("UART_A_OUT: offset = 0x%04x, val = 0x%04x\n",
uart_offset_map(info->port,offset), value);
#endif
vac_outw(value, uart_offset_map(info->port,offset));
}
static inline void serial_outp(struct async_struct *info,int offset, int value)
{
serial_outw(info,offset,value);
}
static inline void serial_out(struct async_struct *info,int offset, int value)
{
serial_outw(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->name, "rs_stop"))
return;
save_flags(flags); cli();
if (info->IER & VAC_UART_INT_TX_EMPTY) {
info->IER &= ~VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK, 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->name, "rs_start"))
return;
save_flags(flags); cli();
if (info->xmit_cnt && info->xmit_buf
&& !(info->IER & VAC_UART_INT_TX_EMPTY)) {
info->IER |= VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK, 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 tty_struct *tty = info->tty;
unsigned short rx;
unsigned char ch;
int ignored = 0;
struct async_icount *icount;
icount = &info->state->icount;
do {
rx = serial_inw(info, VAC_UART_RX);
ch = VAC_UART_RX_DATA_MASK & rx;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
*tty->flip.char_buf_ptr = ch;
icount->rx++;
#ifdef SERIAL_DEBUG_INTR
baget_printk("DR%02x:%02x...", rx, *status);
#endif
*tty->flip.flag_buf_ptr = 0;
if (*status & (VAC_UART_STATUS_RX_BREAK_CHANGE
| VAC_UART_STATUS_RX_ERR_PARITY
| VAC_UART_STATUS_RX_ERR_FRAME
| VAC_UART_STATUS_RX_ERR_OVERRUN)) {
/*
* For statistics only
*/
if (*status & VAC_UART_STATUS_RX_BREAK_CHANGE) {
*status &= ~(VAC_UART_STATUS_RX_ERR_FRAME
| VAC_UART_STATUS_RX_ERR_PARITY);
icount->brk++;
} else if (*status & VAC_UART_STATUS_RX_ERR_PARITY)
icount->parity++;
else if (*status & VAC_UART_STATUS_RX_ERR_FRAME)
icount->frame++;
if (*status & VAC_UART_STATUS_RX_ERR_OVERRUN)
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;
if (*status & (VAC_UART_STATUS_RX_BREAK_CHANGE)) {
#ifdef SERIAL_DEBUG_INTR
baget_printk("handling break....");
#endif
*tty->flip.flag_buf_ptr = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (*status & VAC_UART_STATUS_RX_ERR_PARITY)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (*status & VAC_UART_STATUS_RX_ERR_FRAME)
*tty->flip.flag_buf_ptr = TTY_FRAME;
if (*status & VAC_UART_STATUS_RX_ERR_OVERRUN) {
/*
* Overrun is special, since it's
* reported immediately, and doesn't
* affect the current character
*/
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
tty->flip.count++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
*tty->flip.flag_buf_ptr = TTY_OVERRUN;
}
}
}
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
ignore_char:
*status = serial_inw(info, VAC_UART_INT_STATUS);
} while ((*status & VAC_UART_STATUS_RX_READY));
tty_flip_buffer_push(tty);
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
{
int count;
if (info->x_char) {
serial_outw(info, VAC_UART_TX,
(((unsigned short)info->x_char)<<8));
info->state->icount.tx++;
info->x_char = 0;
if (intr_done)
*intr_done = 0;
return;
}
if ((info->xmit_cnt <= 0) || info->tty->stopped ||
info->tty->hw_stopped) {
info->IER &= ~VAC_UART_INT_TX_EMPTY;
serial_outw(info, VAC_UART_INT_MASK, info->IER);
return;
}
count = info->xmit_fifo_size;
do {
serial_out(info, VAC_UART_TX,
(unsigned short)info->xmit_buf[info->xmit_tail++] \
<< 8);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->state->icount.tx++;
if (--info->xmit_cnt <= 0)
break;
} while (--count > 0);
if (info->xmit_cnt < WAKEUP_CHARS)
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
#ifdef SERIAL_DEBUG_INTR
baget_printk("THRE...");
#endif
if (intr_done)
*intr_done = 0;
if (info->xmit_cnt <= 0) {
info->IER &= ~VAC_UART_INT_TX_EMPTY;
serial_outw(info, VAC_UART_INT_MASK, info->IER);
}
}
static _INLINE_ void check_modem_status(struct async_struct *info)
{
#if 0 /* VAC hasn't modem control */
wake_up_interruptible(&info->open_wait);
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
#endif
}
#ifdef CONFIG_SERIAL_SHARE_IRQ
/*
* Specific functions needed for VAC UART interrupt enter/leave
*/
#define VAC_INT_CTRL_UART_ENABLE \
(VAC_INT_CTRL_TIMER_PIO10|VAC_INT_CTRL_UART_B_PIO7|VAC_INT_CTRL_UART_A_PIO7)
#define VAC_INT_CTRL_UART_DISABLE(info) \
(VAC_INT_CTRL_TIMER_PIO10 | \
((info->port == VAC_UART_A_TX) ? \
(VAC_INT_CTRL_UART_A_DISABLE|VAC_INT_CTRL_UART_B_PIO7) : \
(VAC_INT_CTRL_UART_A_PIO7|VAC_INT_CTRL_UART_B_DISABLE)))
/*
* Following two functions were proposed by Pavel Osipenko
* to make VAC/VIC behaviour more regular.
*/
static void intr_begin(struct async_struct* info)
{
serial_outw(info, VAC_UART_INT_MASK, 0);
}
static void intr_end(struct async_struct* info)
{
vac_outw(VAC_INT_CTRL_UART_DISABLE(info), VAC_INT_CTRL);
vac_outw(VAC_INT_CTRL_UART_ENABLE, VAC_INT_CTRL);
serial_outw(info, VAC_UART_INT_MASK, info->IER);
}
/*
* This is the serial driver's generic interrupt routine
*/
static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
int status;
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
#ifdef SERIAL_DEBUG_INTR
baget_printk("rs_interrupt(%d)...", irq);
#endif
info = IRQ_ports[irq];
if (!info)
return;
do {
intr_begin(info); /* Mark we begin port handling */
if (!info->tty ||
(serial_inw (info, VAC_UART_INT_STATUS)
& VAC_UART_STATUS_INTS) == 0)
{
if (!end_mark)
end_mark = info;
goto next;
}
end_mark = 0;
info->last_active = jiffies;
status = serial_inw(info, VAC_UART_INT_STATUS);
#ifdef SERIAL_DEBUG_INTR
baget_printk("status = %x...", status);
#endif
if (status & VAC_UART_STATUS_RX_READY) {
receive_chars(info, &status);
}
check_modem_status(info);
if (status & VAC_UART_STATUS_TX_EMPTY)
transmit_chars(info, 0);
next:
intr_end(info); /* Mark this port handled */
info = info->next_port;
if (!info) {
info = IRQ_ports[irq];
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
break; /* Prevent infinite loops */
}
continue;
}
} while (end_mark != info);
#ifdef SERIAL_DEBUG_INTR
baget_printk("end.\n");
#endif
}
#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
/* The original driver was simplified here:
two functions were joined to reduce code */
#define rs_interrupt_single rs_interrupt
/*
* -------------------------------------------------------------------
* 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);
}
}
/*
* ---------------------------------------------------------------
* 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 ? timeout : 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 (!state->port || !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;
#ifdef SERIAL_DEBUG_OPEN
baget_printk("starting up ttys%d (irq %d)...", info->line, state->irq);
#endif
if (uart_config[info->state->type].flags & UART_STARTECH) {
/* Wake up UART */
serial_outp(info, VAC_UART_MODE, 0);
serial_outp(info, VAC_UART_INT_MASK, 0);
}
/*
* Allocate the IRQ if necessary
*/
if (state->irq && (!IRQ_ports[state->irq] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
#ifdef CONFIG_SERIAL_SHARE_IRQ
free_irq(state->irq, NULL);
handler = rs_interrupt;
#else
retval = -EBUSY;
goto errout;
#endif /* CONFIG_SERIAL_SHARE_IRQ */
} else
handler = rs_interrupt_single;
retval = request_irq(state->irq, handler, IRQ_T(state),
"serial", NULL);
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);
/*
* Clear the interrupt registers.
*/
/* (void) serial_inw(info, VAC_UART_INT_STATUS); */ /* (see above) */
(void) serial_inw(info, VAC_UART_RX);
/*
* Now, initialize the UART
*/
serial_outp(info, VAC_UART_MODE, VAC_UART_MODE_INITIAL); /*reset DLAB*/
/*
* Finally, enable interrupts
*/
info->IER = VAC_UART_INT_RX_BREAK_CHANGE | VAC_UART_INT_RX_ERRS | \
VAC_UART_INT_RX_READY;
serial_outp(info, VAC_UART_INT_MASK, info->IER); /*enable interrupts*/
/*
* And clear the interrupt registers again for luck.
*/
(void)serial_inp(info, VAC_UART_INT_STATUS);
(void)serial_inp(info, VAC_UART_RX);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
/*
* Set up serial timers...
*/
mod_timer(&vacs_timer, jiffies + 2*HZ/100);
/*
* and set the speed of the serial port
*/
change_speed(info);
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
baget_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] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
free_irq(state->irq, NULL);
retval = request_irq(state->irq, rs_interrupt_single,
IRQ_T(state), "serial", NULL);
if (retval)
printk("serial shutdown: request_irq: error %d"
" Couldn't reacquire IRQ.\n", retval);
} else
free_irq(state->irq, NULL);
}
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
info->xmit_buf = 0;
}
info->IER = 0;
serial_outp(info, VAC_UART_INT_MASK, 0x00); /* disable all intrs */
/* disable break condition */
serial_out(info, VAC_UART_MODE, serial_inp(info, VAC_UART_MODE) & \
~VAC_UART_MODE_SEND_BREAK);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
/*
* When we set line mode, we call this function
* for Baget-specific adjustments.
*/
static inline unsigned short vac_uart_mode_fixup (unsigned short cval)
{
#ifdef QUAD_UART_SPEED
/*
* When we are using 4-x advantage in speed:
*
* Disadvantage : can't support 75, 150 bauds
* Advantage : can support 19200, 38400 bauds
*/
char speed = 7 & (cval >> 10);
cval &= ~(7 << 10);
cval |= VAC_UART_MODE_BAUD(speed-2);
#endif
/*
* In general, we have Tx and Rx ON all time
* and use int mask flag for their disabling.
*/
cval |= VAC_UART_MODE_RX_ENABLE;
cval |= VAC_UART_MODE_TX_ENABLE;
cval |= VAC_UART_MODE_CHAR_RX_ENABLE;
cval |= VAC_UART_MODE_CHAR_TX_ENABLE;
/* Low 4 bits are not used in UART */
cval &= ~0xf;
return cval;
}
/*
* 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)
{
unsigned short port;
int quot = 0, baud_base, baud;
unsigned cflag, cval;
int bits;
unsigned long flags;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
if (!(port = info->port))
return;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS7: cval = 0x0; bits = 9; break;
case CS8: cval = VAC_UART_MODE_8BIT_CHAR; bits = 10; break;
/* Never happens, but GCC is too dumb to figure it out */
case CS5:
case CS6:
default: cval = 0x0; bits = 9; break;
}
cval &= ~VAC_UART_MODE_PARITY_ENABLE;
if (cflag & PARENB) {
cval |= VAC_UART_MODE_PARITY_ENABLE;
bits++;
}
if (cflag & PARODD)
cval |= VAC_UART_MODE_PARITY_ODD;
/* 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 &&
((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 ever 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 */
serial_out(info, VAC_UART_INT_MASK, info->IER);
/*
* Set up parity check flag
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
info->read_status_mask = VAC_UART_STATUS_RX_ERR_OVERRUN | \
VAC_UART_STATUS_TX_EMPTY | VAC_UART_STATUS_RX_READY;
if (I_INPCK(info->tty))
info->read_status_mask |= VAC_UART_STATUS_RX_ERR_FRAME | \
VAC_UART_STATUS_RX_ERR_PARITY;
if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
info->read_status_mask |= VAC_UART_STATUS_RX_BREAK_CHANGE;
/*
* Characters to ignore
*/
info->ignore_status_mask = 0;
if (I_IGNPAR(info->tty))
info->ignore_status_mask |= VAC_UART_STATUS_RX_ERR_PARITY | \
VAC_UART_STATUS_RX_ERR_FRAME;
if (I_IGNBRK(info->tty)) {
info->ignore_status_mask |= VAC_UART_STATUS_RX_BREAK_CHANGE;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (I_IGNPAR(info->tty))
info->ignore_status_mask |= \
VAC_UART_STATUS_RX_ERR_OVERRUN;
}
/*
* !!! ignore all characters if CREAD is not set
*/
if ((cflag & CREAD) == 0)
info->ignore_status_mask |= VAC_UART_STATUS_RX_READY;
save_flags(flags); cli();
switch (baud) {
default:
case 9600:
cval |= VAC_UART_MODE_BAUD(7);
break;
case 4800:
cval |= VAC_UART_MODE_BAUD(6);
break;
case 2400:
cval |= VAC_UART_MODE_BAUD(5);
break;
case 1200:
cval |= VAC_UART_MODE_BAUD(4);
break;
case 600:
cval |= VAC_UART_MODE_BAUD(3);
break;
case 300:
cval |= VAC_UART_MODE_BAUD(2);
break;
#ifndef QUAD_UART_SPEED
case 150:
#else
case 38400:
#endif
cval |= VAC_UART_MODE_BAUD(1);
break;
#ifndef QUAD_UART_SPEED
case 75:
#else
case 19200:
#endif
cval |= VAC_UART_MODE_BAUD(0);
break;
}
/* Baget VAC need some adjustments for computed value */
cval = vac_uart_mode_fixup(cval);
serial_outp(info, VAC_UART_MODE, cval);
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->name, "rs_put_char"))
return;
if (!tty || !info->xmit_buf)
return;
save_flags(flags); cli();
if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
restore_flags(flags);
return;
}
info->xmit_buf[info->xmit_head++] = ch;
info->xmit_head &= SERIAL_XMIT_SIZE-1;
info->xmit_cnt++;
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->name, "rs_flush_chars"))
return;
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!info->xmit_buf)
return;
save_flags(flags); cli();
info->IER |= VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK, 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->name, "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) {
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0)
break;
c -= copy_from_user(tmp_buf, buf, c);
if (!c) {
if (!ret)
ret = -EFAULT;
break;
}
cli();
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
info->xmit_head = ((info->xmit_head + c) &
(SERIAL_XMIT_SIZE-1));
info->xmit_cnt += c;
restore_flags(flags);
buf += c;
count -= c;
ret += c;
}
up(&tmp_buf_sem);
} else {
while (1) {
cli();
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0) {
restore_flags(flags);
break;
}
memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head = ((info->xmit_head + c) &
(SERIAL_XMIT_SIZE-1));
info->xmit_cnt += c;
restore_flags(flags);
buf += c;
count -= c;
ret += c;
}
}
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
!(info->IER & VAC_UART_INT_TX_EMPTY)) {
info->IER |= VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK, info->IER);
}
return ret;
}
static int rs_write_room(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
int ret;
if (serial_paranoia_check(info, tty->name, "rs_write_room"))
return 0;
ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
}
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->name, "rs_chars_in_buffer"))
return 0;
return info->xmit_cnt;
}
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->name, "rs_flush_buffer"))
return;
save_flags(flags); cli();
info->xmit_cnt = 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);
}
/*
* 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->name, "rs_send_char"))
return;
info->x_char = ch;
if (ch) {
/* Make sure transmit interrupts are on */
info->IER |= VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK, 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;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
baget_printk("throttle %s: %d....\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
if (serial_paranoia_check(info, tty->name, "rs_throttle"))
return;
if (I_IXOFF(tty))
rs_send_xchar(tty, STOP_CHAR(tty));
}
static void rs_unthrottle(struct tty_struct * tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
baget_printk("unthrottle %s: %d....\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
return;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
rs_send_xchar(tty, START_CHAR(tty));
}
}
/*
* ------------------------------------------------------------
* 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;
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;
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;
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
state = info->state;
old_state = *state;
change_irq = new_serial.irq != state->irq;
change_port = (new_serial.port != 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 = ((state->flags & ~ASYNC_USR_MASK) |
(info->flags & ASYNC_USR_MASK));
state->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
new_serial.irq = new_serial.irq;
if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) ||
(new_serial.baud_base == 0) || (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[state->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_serial.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->type = new_serial.type;
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;
release_region(state->port,8);
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_serial.port;
info->hub6 = state->hub6 = new_serial.hub6;
}
if (state->type != PORT_UNKNOWN)
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);
}
} 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 short status;
unsigned int result;
unsigned long flags;
save_flags(flags); cli();
status = serial_inw(info, VAC_UART_INT_STATUS);
restore_flags(flags);
result = ((status & VAC_UART_STATUS_TX_EMPTY) ? TIOCSER_TEMT : 0);
return put_user(result,value);
}
static int get_modem_info(struct async_struct * info, unsigned int *value)
{
unsigned int result;
result = TIOCM_CAR | TIOCM_DSR;
return put_user(result,value);
}
static int set_modem_info(struct async_struct * info, unsigned int cmd,
unsigned int *value)
{
unsigned int arg;
if (get_user(arg, value))
return -EFAULT;
switch (cmd) {
default:
return -EINVAL;
}
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->name, "rs_break"))
return;
if (!info->port)
return;
save_flags(flags); cli();
if (break_state == -1)
serial_outp(info, VAC_UART_MODE,
serial_inp(info, VAC_UART_MODE) | \
VAC_UART_MODE_SEND_BREAK);
else
serial_outp(info, VAC_UART_MODE,
serial_inp(info, VAC_UART_MODE) & \
~VAC_UART_MODE_SEND_BREAK);
restore_flags(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
int error;
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
unsigned long flags;
if (serial_paranoia_check(info, tty->name, "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);
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);
p_cuser = (struct serial_icounter_struct *) arg;
error = put_user(cnow.cts, &p_cuser->cts);
if (error) return error;
error = put_user(cnow.dsr, &p_cuser->dsr);
if (error) return error;
error = put_user(cnow.rng, &p_cuser->rng);
if (error) return error;
error = put_user(cnow.dcd, &p_cuser->dcd);
if (error) return error;
error = put_user(cnow.rx, &p_cuser->rx);
if (error) return error;
error = put_user(cnow.tx, &p_cuser->tx);
if (error) return error;
error = put_user(cnow.frame, &p_cuser->frame);
if (error) return error;
error = put_user(cnow.overrun, &p_cuser->overrun);
if (error) return error;
error = put_user(cnow.parity, &p_cuser->parity);
if (error) return error;
error = put_user(cnow.brk, &p_cuser->brk);
if (error) return error;
error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
if (error) return error;
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 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);
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(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->name, "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
baget_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.
*/
baget_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) {
baget_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;
/*
* 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 &= ~(VAC_UART_INT_RX_BREAK_CHANGE | VAC_UART_INT_RX_ERRS);
info->read_status_mask &= ~VAC_UART_STATUS_RX_READY;
if (info->flags & ASYNC_INITIALIZED) {
serial_outw(info, VAC_UART_INT_MASK, 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) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(info->close_delay);
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
MOD_DEC_USE_COUNT;
restore_flags(flags);
}
/*
* 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->name, "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)
char_time = MIN(char_time, timeout);
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
baget_printk("In rs_wait_until_sent(%d) check=%lu...",
timeout, char_time);
baget_printk("jiff=%lu...", jiffies);
#endif
while (!((lsr = serial_inp(info, VAC_UART_INT_STATUS)) & \
VAC_UART_STATUS_TX_EMPTY)) {
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
baget_printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
current->state = TASK_RUNNING;
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
baget_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->name, "rs_hangup"))
return;
state = info->state;
rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
state->count = 0;
info->flags &= ~ASYNC_NORMAL_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 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))) {
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
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
baget_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) {
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_CLOSING))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
baget_printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, state->count);
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (extra_count)
state->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
baget_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->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 = tty->index;
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->name, "rs_open")) {
/* MOD_DEC_USE_COUNT; "info->tty" will cause this */
return -ENODEV;
}
#ifdef SERIAL_DEBUG_OPEN
baget_printk("rs_open %s, count = %d\n", tty->name,
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; "info->tty" will cause this */
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; "info->tty" will cause this */
#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; "info->tty" will cause this */
return retval;
}
retval = block_til_ready(tty, filp, info);
if (retval) {
/* MOD_DEC_USE_COUNT; "info->tty" will cause this */
#ifdef SERIAL_DEBUG_OPEN
baget_printk("rs_open returning after block_til_ready "
"with %d\n",
retval);
#endif
return retval;
}
#ifdef CONFIG_SERIAL_CONSOLE
if (sercons.cflag && sercons.index == line) {
tty->termios->c_cflag = sercons.cflag;
sercons.cflag = 0;
change_speed(info);
}
#endif
#ifdef SERIAL_DEBUG_OPEN
baget_printk("rs_open %s successful...", tty->name);
#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;
int ret;
ret = sprintf(buf, "%d: uart:%s port:%X 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;
}
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);
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\n", serial_version);
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 _INLINE_ void show_serial_version(void)
{
printk(KERN_INFO "%s version %s with", serial_name, serial_version);
#ifdef CONFIG_SERIAL_SHARE_IRQ
printk(" SHARE_IRQ");
#endif
#define SERIAL_OPT
#ifdef CONFIG_SERIAL_DETECT_IRQ
printk(" DETECT_IRQ");
#endif
#ifdef SERIAL_OPT
printk(" enabled\n");
#else
printk(" no serial options enabled\n");
#endif
#undef SERIAL_OPT
}
/*
* 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.
*/
/*
* Functionality of this function is reduced: we already know we have a VAC,
* but still need to perform some important actions (see code :-).
*/
static void autoconfig(struct serial_state * state)
{
struct async_struct *info, scr_info;
unsigned long flags;
/* Setting up important parameters */
state->type = VAC_UART_TYPE;
state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;
info = &scr_info; /* This is just for serial_{in,out} */
info->magic = SERIAL_MAGIC;
info->port = state->port;
info->flags = state->flags;
save_flags(flags); cli();
/* + Flush VAC input fifo */
(void)serial_in(info, VAC_UART_RX);
(void)serial_in(info, VAC_UART_RX);
(void)serial_in(info, VAC_UART_RX);
(void)serial_in(info, VAC_UART_RX);
/* Disable interrupts */
serial_outp(info, VAC_UART_INT_MASK, 0);
restore_flags(flags);
}
int register_serial(struct serial_struct *req);
void unregister_serial(int line);
EXPORT_SYMBOL(register_serial);
EXPORT_SYMBOL(unregister_serial);
/*
* Important function for VAC UART check and reanimation.
*/
static void rs_timer(unsigned long dummy)
{
static unsigned long last_strobe = 0;
struct async_struct *info;
unsigned int i;
unsigned long flags;
if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
for (i=1; i < NR_IRQS; i++) {
info = IRQ_ports[i];
if (!info)
continue;
save_flags(flags); cli();
#ifdef CONFIG_SERIAL_SHARE_IRQ
if (info->next_port) {
do {
serial_out(info, VAC_UART_INT_MASK, 0);
info->IER |= VAC_UART_INT_TX_EMPTY;
serial_out(info, VAC_UART_INT_MASK,
info->IER);
info = info->next_port;
} while (info);
rs_interrupt(i, NULL, NULL);
} else
#endif /* CONFIG_SERIAL_SHARE_IRQ */
rs_interrupt_single(i, NULL, NULL);
restore_flags(flags);
}
}
last_strobe = jiffies;
mod_timer(&vacs_timer, jiffies + RS_STROBE_TIME);
/*
* It looks this code for case we share IRQ with console...
*/
if (IRQ_ports[0]) {
save_flags(flags); cli();
#ifdef CONFIG_SERIAL_SHARE_IRQ
rs_interrupt(0, NULL, NULL);
#else
rs_interrupt_single(0, NULL, NULL);
#endif
restore_flags(flags);
mod_timer(&vacs_timer, jiffies + IRQ_timeout[0] - 2);
}
}
static struct tty_operations rs_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
.put_char = rs_put_char,
.flush_chars = rs_flush_chars,
.write_room = rs_write_room,
.chars_in_buffer = rs_chars_in_buffer,
.flush_buffer = rs_flush_buffer,
.ioctl = rs_ioctl,
.throttle = rs_throttle,
.unthrottle = rs_unthrottle,
.send_xchar = rs_send_xchar,
.set_termios = rs_set_termios,
.stop = rs_stop,
.start = rs_start,
.hangup = rs_hangup,
.break_ctl = rs_break,
.wait_until_sent = rs_wait_until_sent,
.read_proc = rs_read_proc,
};
/*
* The serial driver boot-time initialization code!
*/
int __init rs_init(void)
{
int i;
struct serial_state * state;
extern void atomwide_serial_init (void);
extern void dualsp_serial_init (void);
#ifdef CONFIG_ATOMWIDE_SERIAL
atomwide_serial_init ();
#endif
#ifdef CONFIG_DUALSP_SERIAL
dualsp_serial_init ();
#endif
init_bh(SERIAL_BH, do_serial_bh);
init_timer(&vacs_timer);
vacs_timer.function = rs_timer;
vacs_timer.expires = 0;
for (i = 0; i < NR_IRQS; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
}
/*
* It is not a good idea to share interrupts with console,
* but it looks we cannot avoid it.
*/
#if 0
#ifdef CONFIG_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
#endif
serial_driver = alloc_tty_driver(NR_PORTS);
if (!serial_driver)
return -ENOMEM;
show_serial_version();
/* Initialize the tty_driver structure */
serial_driver->driver_name = "serial";
serial_driver->name = "ttyS";
serial_driver->major = TTY_MAJOR;
serial_driver->minor_start = 64;
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_set_operations(serial_driver, &rs_ops);
if (tty_register_driver(serial_driver))
panic("Couldn't register serial driver\n");
for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
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->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 = state->irq;
if (check_region(state->port,8))
continue;
if (state->flags & ASYNC_BOOT_AUTOCONF)
autoconfig(state);
}
/*
* Detect the IRQ only once every port is initialised,
* because some 16450 do not reset to 0 the MCR register.
*/
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%04x (irq = %d) is a %s\n",
state->line,
(state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
state->port, state->irq,
uart_config[state->type].name);
}
return 0;
}
/*
* register_serial and unregister_serial allows for serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
int register_serial(struct serial_struct *req)
{
int i;
unsigned long flags;
struct serial_state *state;
save_flags(flags);
cli();
for (i = 0; i < NR_PORTS; i++) {
if (rs_table[i].port == req->port)
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=%d,irq=%d): "
"device already open\n", i, req->port, req->irq);
return -1;
}
state->irq = req->irq;
state->port = req->port;
state->flags = req->flags;
autoconfig(state);
if (state->type == PORT_UNKNOWN) {
restore_flags(flags);
printk("register_serial(): autoconfig failed\n");
return -1;
}
restore_flags(flags);
printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n",
state->line, state->port, state->irq,
uart_config[state->type].name);
return state->line;
}
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);
restore_flags(flags);
}
#ifdef MODULE
int init_module(void)
{
return rs_init();
}
void cleanup_module(void)
{
unsigned long flags;
int e1, e2;
int i;
printk("Unloading %s: version %s\n", serial_name, serial_version);
save_flags(flags);
cli();
del_timer_sync(&vacs_timer);
remove_bh(SERIAL_BH);
if ((e1 = tty_unregister_driver(serial_driver)))
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
restore_flags(flags);
put_tty_driver(serial_driver);
for (i = 0; i < NR_PORTS; i++) {
if (rs_table[i].type != PORT_UNKNOWN)
release_region(rs_table[i].port, 8);
}
if (tmp_buf) {
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
}
#endif /* MODULE */
/*
* ------------------------------------------------------------
* Serial console driver
* ------------------------------------------------------------
*/
#ifdef CONFIG_SERIAL_CONSOLE
#define BOTH_EMPTY (VAC_UART_STATUS_TX_EMPTY | VAC_UART_STATUS_TX_EMPTY)
/*
* Wait for transmitter & holding register to empty
*/
static inline void wait_for_xmitr(struct async_struct *info)
{
int lsr;
unsigned int tmout = 1000000;
do {
lsr = serial_inp(info, VAC_UART_INT_STATUS);
if (--tmout == 0) break;
} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
}
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*/
static void serial_console_write(struct console *co, const char *s,
unsigned count)
{
struct serial_state *ser;
int ier;
unsigned i;
struct async_struct scr_info; /* serial_{in,out} because HUB6 */
ser = rs_table + co->index;
scr_info.magic = SERIAL_MAGIC;
scr_info.port = ser->port;
scr_info.flags = ser->flags;
/*
* First save the IER then disable the interrupts
*/
ier = serial_inp(&scr_info, VAC_UART_INT_MASK);
serial_outw(&scr_info, VAC_UART_INT_MASK, 0x00);
/*
* Now, do each character
*/
for (i = 0; i < count; i++, s++) {
wait_for_xmitr(&scr_info);
/*
* Send the character out.
* If a LF, also do CR...
*/
serial_outp(&scr_info, VAC_UART_TX, (unsigned short)*s << 8);
if (*s == 10) {
wait_for_xmitr(&scr_info);
serial_outp(&scr_info, VAC_UART_TX, 13 << 8);
}
}
/*
* Finally, Wait for transmitter & holding register to empty
* and restore the IER
*/
wait_for_xmitr(&scr_info);
serial_outp(&scr_info, VAC_UART_INT_MASK, ier);
}
static struct tty_driver *serial_console_device(struct console *c, int *index)
{
*index = c->index;
return serial_driver;
}
/*
* 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)
{
struct serial_state *ser;
unsigned cval;
int baud = 9600;
int bits = 8;
int parity = 'n';
int cflag = CREAD | HUPCL | CLOCAL;
int quot = 0;
char *s;
struct async_struct scr_info; /* serial_{in,out} because HUB6 */
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
*/
ser = rs_table + co->index;
scr_info.magic = SERIAL_MAGIC;
scr_info.port = ser->port;
scr_info.flags = ser->flags;
quot = ser->baud_base / baud;
cval = cflag & (CSIZE | CSTOPB);
cval >>= 4;
cval &= ~VAC_UART_MODE_PARITY_ENABLE;
if (cflag & PARENB)
cval |= VAC_UART_MODE_PARITY_ENABLE;
if (cflag & PARODD)
cval |= VAC_UART_MODE_PARITY_ODD;
/*
* Disable UART interrupts, set DTR and RTS high
* and set speed.
*/
switch (baud) {
default:
case 9600:
cval |= VAC_UART_MODE_BAUD(7);
break;
case 4800:
cval |= VAC_UART_MODE_BAUD(6);
break;
case 2400:
cval |= VAC_UART_MODE_BAUD(5);
break;
case 1200:
cval |= VAC_UART_MODE_BAUD(4);
break;
case 600:
cval |= VAC_UART_MODE_BAUD(3);
break;
case 300:
cval |= VAC_UART_MODE_BAUD(2);
break;
#ifndef QUAD_UART_SPEED
case 150:
#else
case 38400:
#endif
cval |= VAC_UART_MODE_BAUD(1);
break;
#ifndef QUAD_UART_SPEED
case 75:
#else
case 19200:
#endif
cval |= VAC_UART_MODE_BAUD(0);
break;
}
/* Baget VAC need some adjustments for computed value */
cval = vac_uart_mode_fixup(cval);
serial_outp(&scr_info, VAC_UART_MODE, cval);
serial_outp(&scr_info, VAC_UART_INT_MASK, 0);
return 0;
}
static struct console sercons = {
.name = "ttyS",
.write = serial_console_write,
.device = serial_console_device,
.setup = serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
/*
* Register console.
*/
long __init serial_console_init(long kmem_start, long kmem_end)
{
register_console(&sercons);
return kmem_start;
}
#endif
#ifdef CONFIG_REMOTE_DEBUG
#undef PRINT_DEBUG_PORT_INFO
/*
* This is the interface to the remote debugger stub.
* I've put that here to be able to control the serial
* device more directly.
*/
static int initialized;
static int rs_debug_init(struct async_struct *info)
{
int quot;
autoconfig(info); /* autoconfigure ttyS0, whatever that is */
#ifdef PRINT_DEBUG_PORT_INFO
baget_printk("kgdb debug interface:: tty%02d at 0x%04x",
info->line, info->port);
switch (info->type) {
case PORT_8250:
baget_printk(" is a 8250\n");
break;
case PORT_16450:
baget_printk(" is a 16450\n");
break;
case PORT_16550:
baget_printk(" is a 16550\n");
break;
case PORT_16550A:
baget_printk(" is a 16550A\n");
break;
case PORT_16650:
baget_printk(" is a 16650\n");
break;
default:
baget_printk(" is of unknown type -- unusable\n");
break;
}
#endif
if (info->port == PORT_UNKNOWN)
return -1;
/*
* Clear all interrupts
*/
(void)serial_inp(info, VAC_UART_INT_STATUS);
(void)serial_inp(info, VAC_UART_RX);
/*
* Now, initialize the UART
*/
serial_outp(info,VAC_UART_MODE,VAC_UART_MODE_INITIAL); /* reset DLAB */
if (info->flags & ASYNC_FOURPORT) {
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
} else {
info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
}
info->MCR = info->MCR_noint; /* no interrupts, please */
/*
* and set the speed of the serial port
* (currently hardwired to 9600 8N1
*/
quot = info->baud_base / 9600; /* baud rate is fixed to 9600 */
/* FIXME: if rs_debug interface is needed, we need to set speed here */
return 0;
}
int putDebugChar(char c)
{
struct async_struct *info = rs_table;
if (!initialized) { /* need to init device first */
if (rs_debug_init(info) == 0)
initialized = 1;
else
return 0;
}
while ((serial_inw(info, VAC_UART_INT_STATUS) & \
VAC_UART_STATUS_TX_EMPTY) == 0)
;
serial_out(info, VAC_UART_TX, (unsigned short)c << 8);
return 1;
}
char getDebugChar(void)
{
struct async_struct *info = rs_table;
if (!initialized) { /* need to init device first */
if (rs_debug_init(info) == 0)
initialized = 1;
else
return 0;
}
while (!(serial_inw(info, VAC_UART_INT_STATUS) & \
VAC_UART_STATUS_RX_READY))
;
return(serial_inp(info, VAC_UART_RX));
}
#endif /* CONFIG_REMOTE_DEBUG */
/*
* Setup the right wbflush routine for Baget/MIPS.
*
* Copyright (C) 1999 Gleb Raiko & Vladimir Roganov
*/
#include <linux/init.h>
#include <asm/bootinfo.h>
void (*__wbflush) (void);
static void wbflush_baget(void);
void __init wbflush_setup(void)
{
__wbflush = wbflush_baget;
}
/*
* Baget/MIPS doesn't need to write back the WB.
*/
static void wbflush_baget(void)
{
}
/* $Id$
/*
* baget.h: Definitions specific to Baget/MIPS machines.
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
......@@ -9,13 +9,13 @@
#include "vic.h"
#include "vac.h"
#define VIC_BASE 0xBFFC0000
#define VIC_BASE 0xBFFC0000
#define VAC_BASE 0xBFFD0000
/* Baget interrupt registers and their sizes */
struct baget_int_reg {
struct baget_int_reg {
unsigned long address;
int size; /* in bytes */
};
......@@ -43,8 +43,8 @@ struct baget_int_reg {
#define BAGET_IRQ_MASK(x) ((NR_IRQS-1) & (x))
#define BAGET_FPU_IRQ 0x26
#define BAGET_VIC_TIMER_IRQ 0x32
#define BAGET_VAC_TIMER_IRQ 0x36
#define BAGET_VIC_TIMER_IRQ 0x32
#define BAGET_VAC_TIMER_IRQ 0x36
#define BAGET_BSM_IRQ 0x3C
#define BAGET_LANCE_MEM_BASE 0xfcf10000
......
/* $Id$
*
/*
* vac.h: Various VIC controller defines. The VIC is a VME controller
* used in Baget/MIPS series.
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#ifndef _MIPS_VAC_H
#define _MIPS_VAC_H
#ifndef _ASM_VAC_H
#define _ASM_VAC_H
#define VAC_SLSEL1_MASK 0x000
#define VAC_SLSEL1_BASE 0x100
......@@ -22,8 +21,8 @@
#define VAC_A24_MASK (0x3f<<9)
#define VAC_A24_D32_ENABLE (1<<8)
#define VAC_A24_A24_CACHINH (1<<7)
#define VAC_A24_A16D32_ENABLE (1<<6)
#define VAC_A24_A16D32 (1<<5)
#define VAC_A24_A16D32_ENABLE (1<<6)
#define VAC_A24_A16D32 (1<<5)
#define VAC_A24_DATAPATH (1<<4)
#define VAC_A24_IO_CACHINH (1<<3)
#define VAC_REG1 0x900
......@@ -197,13 +196,13 @@
#define VAC_ID 0x2900
#ifndef __LANGUAGE_ASSEMBLY__
#ifndef __ASSEMBLY__
#define vac_inb(p) (*(volatile unsigned char *)(VAC_BASE + (p)))
#define vac_outb(v,p) (*((volatile unsigned char *)(VAC_BASE + (p))) = v)
#define vac_inw(p) (*(volatile unsigned short*)(VAC_BASE + (p)))
#define vac_outw(v,p) (*((volatile unsigned short*)(VAC_BASE + (p))) = v)
#endif /* __LANGUAGE_ASSEMBLY__ */
#endif /* !__ASSEMBLY__ */
#endif /* !(_MIPS_VAC_H) */
#endif /* _ASM_VAC_H */
/* $Id$
*
/*
* vic.h: Various VIC controller defines. The VIC is an interrupt controller
* used in Baget/MIPS series.
*
* Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
*/
#ifndef _MIPS_VIC_H
#define _MIPS_VIC_H
#ifndef _ASM_BAGET_VIC_H
#define _ASM_BAGET_VIC_H
#define VIC_VME_II 0x3
#define VIC_VME_INT1 0x7
......@@ -168,10 +167,10 @@
#define VIC_SSxCR1_TF1(x) ((x)&0xf)
#define VIC_RELEASE 0xD3
#define VIC_RELEASE_BLKXFER_BLEN(x) ((x)&0x1f)
#define VIC_RELEASE_ROR (0<<6)
#define VIC_RELEASE_RWD (1<<6)
#define VIC_RELEASE_ROC (2<<6)
#define VIC_RELEASE_BCAP (3<<6)
#define VIC_RELEASE_ROR (0<<6)
#define VIC_RELEASE_RWD (1<<6)
#define VIC_RELEASE_ROC (2<<6)
#define VIC_RELEASE_BCAP (3<<6)
#define VIC_BXFER_CTRL 0xD7
#define VIC_BXFER_CTRL_MODULE (1<<7)
#define VIC_BXFER_CTRL_LOCAL (1<<6)
......@@ -183,11 +182,11 @@
#define VIC_BXFER_LEN_HI 0xDF
#define VIC_SYS_RESET 0xE3
#ifndef __LANGUAGE_ASSEMBLY__
#ifndef __ASSEMBLY__
#define vic_inb(p) (*(volatile unsigned char *)(VIC_BASE + (p)))
#define vic_outb(v,p) (*((volatile unsigned char *)(VIC_BASE + (p))) = v)
#endif /* __LANGUAGE_ASSEMBLY__ */
#endif /* !__ASSEMBLY__ */
#endif /* !(_MIPS_VIC_H) */
#endif /* _ASM_BAGET_VIC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment