summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/char
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /drivers/sbus/char
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'drivers/sbus/char')
-rw-r--r--drivers/sbus/char/Config.in1
-rw-r--r--drivers/sbus/char/Makefile22
-rw-r--r--drivers/sbus/char/cgfourteen.c4
-rw-r--r--drivers/sbus/char/cgsix.c8
-rw-r--r--drivers/sbus/char/cgthree.c4
-rw-r--r--drivers/sbus/char/creator.c231
-rw-r--r--drivers/sbus/char/fb.h26
-rw-r--r--drivers/sbus/char/leo.c16
-rw-r--r--drivers/sbus/char/mach64.c203
-rw-r--r--drivers/sbus/char/mach64.h587
-rw-r--r--drivers/sbus/char/pcicons.c750
-rw-r--r--drivers/sbus/char/pcicons.h79
-rw-r--r--drivers/sbus/char/pcikbd.c964
-rw-r--r--drivers/sbus/char/pcikbd.h123
-rw-r--r--drivers/sbus/char/sab82532.c2173
-rw-r--r--drivers/sbus/char/sbuscons.c1644
-rw-r--r--drivers/sbus/char/su.c678
-rw-r--r--drivers/sbus/char/suncons.c1778
-rw-r--r--drivers/sbus/char/sunfb.c14
-rw-r--r--drivers/sbus/char/sunkbd.c43
-rw-r--r--drivers/sbus/char/sunkbd.h27
-rw-r--r--drivers/sbus/char/sunmouse.c35
-rw-r--r--drivers/sbus/char/sunmouse.h13
-rw-r--r--drivers/sbus/char/sunserial.c2678
-rw-r--r--drivers/sbus/char/sunserial.h457
-rw-r--r--drivers/sbus/char/tcx.c10
-rw-r--r--drivers/sbus/char/zs.c2786
-rw-r--r--drivers/sbus/char/zs.h428
28 files changed, 11095 insertions, 4687 deletions
diff --git a/drivers/sbus/char/Config.in b/drivers/sbus/char/Config.in
index 3f25aa828..90ecfc49a 100644
--- a/drivers/sbus/char/Config.in
+++ b/drivers/sbus/char/Config.in
@@ -41,6 +41,7 @@ fi
comment 'Misc Linux/SPARC drivers'
tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO
tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC
+tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile
index a5d2b3016..dfcc7afe3 100644
--- a/drivers/sbus/char/Makefile
+++ b/drivers/sbus/char/Makefile
@@ -50,10 +50,14 @@ endif
#endif
O_TARGET := sunchar.o
-O_OBJ := ${FB_OBJS} suncons.o sunfb.o
-O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o
+O_OBJ := ${FB_OBJS} suncons.o sbuscons.o pcicons.o sunfb.o
+O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o zs.o
M_OBJS :=
+ifeq ($(ARCH),sparc64)
+O_OBJS += su.o pcikbd.o
+endif
+
ifeq ($(CONFIG_SUN_OPENPROMIO),y)
O_OBJS += openprom.o
else
@@ -86,6 +90,20 @@ else
endif
endif
+ifeq ($(CONFIG_SAB82532),y)
+O_OBJS += sab82532.o
+else
+ ifeq ($(CONFIG_SAB82532),m)
+ M_OBJS += sab82532.o
+ endif
+endif
+
+# Add PCI console/fb drivers here.
+#
+ifeq ($(CONFIG_PCI),y)
+O_OBJS += mach64.o
+endif
+
include $(TOPDIR)/Rules.make
vfc.o: vfc_dev.o vfc_i2c.o
diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c
index 40fb9fd70..dbe071b98 100644
--- a/drivers/sbus/char/cgfourteen.c
+++ b/drivers/sbus/char/cgfourteen.c
@@ -1,4 +1,4 @@
-/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $
+/* $Id: cgfourteen.c,v 1.25 1997/08/20 07:38:36 davem Exp $
* cgfourteen.c: Sun SparcStation console support.
*
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -456,7 +456,7 @@ __initfunc(void cg14_setup (fbinfo_t *fb, int slot, int con_node, u32 cg14, int
fb->setcurshape = cg14_setcurshape;
fb->ioctl = cg14_ioctl;
fb->switch_from_graph = cg14_switch_from_graph;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->reset = cg14_reset;
fb->blank = 0;
fb->unblank = 0;
diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c
index 825d49b4a..4c24a8c0b 100644
--- a/drivers/sbus/char/cgsix.c
+++ b/drivers/sbus/char/cgsix.c
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $
+/* $Id: cgsix.c,v 1.37 1997/08/22 15:55:20 jj Exp $
* cgsix.c: cgsix frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -409,8 +409,8 @@ cg6_reset (fbinfo_t *fb)
struct cg6_info *cg6 = &(fb->info.cg6);
unsigned int rev, conf;
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
/* Turn off stuff in the Transform Engine. */
cg6->tec->tec_matrix = 0;
cg6->tec->tec_clip = 0;
@@ -466,7 +466,7 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io))
fb->setcursor = cg6_setcursor;
fb->setcursormap = cg6_setcursormap;
fb->setcurshape = cg6_setcurshape;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->blitc = cg6_blitc;
fb->setw = cg6_setw;
fb->cpyw = cg6_cpyw;
diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c
index aea1bff32..95d8a07f4 100644
--- a/drivers/sbus/char/cgthree.c
+++ b/drivers/sbus/char/cgthree.c
@@ -1,4 +1,4 @@
-/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $
+/* $Id: cgthree.c,v 1.24 1997/08/20 07:38:37 davem Exp $
* cgtree.c: cg3 frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -213,7 +213,7 @@ __initfunc(void cg3_setup (fbinfo_t *fb, int slot, u32 cg3, int cg3_io,
fb->type.fb_cmsize = 256;
fb->mmap = cg3_mmap;
fb->loadcmap = cg3_loadcmap;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->ioctl = 0; /* no special ioctls */
fb->reset = 0;
fb->blank = cg3_blank;
diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c
index 3ce90092a..815e397e8 100644
--- a/drivers/sbus/char/creator.c
+++ b/drivers/sbus/char/creator.c
@@ -1,4 +1,4 @@
-/* $Id: creator.c,v 1.8 1997/07/22 06:14:12 davem Exp $
+/* $Id: creator.c,v 1.12 1997/08/25 07:50:27 jj Exp $
* creator.c: Creator/Creator3D frame buffer driver
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -101,14 +101,24 @@
#define FFB_UCSR_RP_BUSY 0x02000000
struct ffb_fbc {
- u8 xxx1[0x200];
+ u8 xxx1[0x60];
+ volatile u32 by;
+ volatile u32 bx;
+ u32 xxx2;
+ u32 xxx3;
+ volatile u32 bh;
+ volatile u32 bw;
+ u8 xxx4[0x188];
volatile u32 ppc;
- u8 xxx2[0x50];
+ u32 xxx5;
+ volatile u32 fg;
+ volatile u32 bg;
+ u8 xxx6[0x44];
volatile u32 fbc;
volatile u32 rop;
- u8 xxx3[0x34];
+ u8 xxx7[0x34];
volatile u32 pmask;
- u8 xxx4[12];
+ u8 xxx8[12];
volatile u32 clip0min;
volatile u32 clip0max;
volatile u32 clip1min;
@@ -117,11 +127,17 @@ struct ffb_fbc {
volatile u32 clip2max;
volatile u32 clip3min;
volatile u32 clip3max;
- u8 xxx5[0x3c];
+ u8 xxx9[0x3c];
volatile u32 unk1;
- u8 xxx6[0x500];
volatile u32 unk2;
- u8 xxx7[0xfc];
+ u8 xxx10[0x10];
+ volatile u32 fontxy;
+ volatile u32 fontw;
+ volatile u32 fontinc;
+ volatile u32 font;
+ u8 xxx11[0x4dc];
+ volatile u32 unk3;
+ u8 xxx12[0xfc];
volatile u32 ucsr;
};
@@ -141,6 +157,7 @@ static void ffb_blitc(unsigned short, int, int);
static void ffb_setw(int, int, unsigned short, int);
static void ffb_cpyw(int, int, unsigned short *, int);
static void ffb_fill(int, int, int *);
+static void ffb_penguin(int,int,int);
static struct {
unsigned long voff;
@@ -176,6 +193,8 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
uint size, page, r, map_size;
unsigned long map_offset = 0;
int i;
+ int alignment;
+ struct vm_area_struct *vmm;
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
@@ -184,15 +203,18 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
/* Try to align RAM */
#define ALIGNMENT 0x400000
map_offset = vma->vm_offset + size;
+ alignment = 0;
if (vma->vm_offset < FFB_FBC_REGS_VOFF) {
- struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
- int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
-
- if (alignment == ALIGNMENT) alignment = 0;
- if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
- vma->vm_start += alignment;
- vma->vm_end += alignment;
- }
+ vmm = find_vma(current->mm, vma->vm_start);
+ alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
+ } else if (vma->vm_offset >= FFB_DFB8R_VOFF && (vma->vm_offset & (ALIGNMENT - 1)) == 0x4000) {
+ vmm = find_vma(current->mm, vma->vm_start);
+ alignment = ALIGNMENT - (vma->vm_start & (ALIGNMENT - 1));
+ }
+ if (alignment == ALIGNMENT) alignment = 0;
+ if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
+ vma->vm_start += alignment;
+ vma->vm_end += alignment;
}
#undef ALIGNMENT
@@ -205,7 +227,7 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++)
if (ffbmmap[i].voff == vma->vm_offset+page) {
map_size = ffbmmap[i].size;
- map_offset = fb->info.ffb.physbase + ffbmmap[i].poff;
+ map_offset = (fb->info.ffb.physbase + ffbmmap[i].poff) & _PAGE_PADDR;
}
if (!map_size){
@@ -380,8 +402,8 @@ ffb_setcursor (fbinfo_t *fb)
dac->type2 = 0x104;
/* Should this be just 0x7ff?? Should I do some margin handling and setcurshape
in that case? */
- dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16)
- |((c->cpos.fby - c->chot.fby) & 0xffff);
+ dac->value2 = (((c->cpos.fby - c->chot.fby) & 0xffff) << 16)
+ |((c->cpos.fbx - c->chot.fbx) & 0xffff);
ffb_curs_enable (fb, fb->cursor.enable);
}
@@ -480,32 +502,14 @@ ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a
void
ffb_reset (fbinfo_t *fb)
{
- struct ffb_info *ffb = &(fb->info.ffb);
- int fifo;
-
- if (fb->setcursor)
- sun_hw_hide_cursor ();
-
- while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8);
- ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) |
- (FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) |
- (FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) |
- (FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) |
- (FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) |
- (FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT);
- ffb->fbc->fbc = (FFB_FBC_WB_A << FFB_FBC_WB_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_BE_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_GE_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_RE_SHIFT);
- ffb->fbc->rop = (FFB_ROP_NEW << FFB_ROP_RGB_SHIFT);
- ffb->fbc->pmask = 0x00ffffff;
- while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY);
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
}
__initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start))
{
fb->info.ffb.clut = (u32 *)(memory_start);
- fb->color_map = (u8 *)(memory_start+256*4+256);
+ fb->color_map = (u8 *)(memory_start+256*4);
return memory_start + 256*4 + 256*3;
}
@@ -513,10 +517,11 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
{
struct ffb_info *ffbinfo;
struct linux_prom64_registers regs[2*PROMREG_MAX];
-
+ int type;
+
if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0)
return;
- ffb = regs[0].phys_addr;
+ ffb = (long)__va(regs[0].phys_addr);
printk ("creator%d at 0x%016lx ", slot, ffb);
fb->base = ffb; /* ??? */
@@ -536,9 +541,11 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
fb->setw = ffb_setw;
fb->cpyw = ffb_cpyw;
fb->fill = ffb_fill;
+ fb->draw_penguin = ffb_penguin;
fb->ioctl = ffb_ioctl;
fb->cursor.hwsize.fbx = 64;
fb->cursor.hwsize.fby = 64;
+ fb->type.fb_depth = 24;
ffbinfo = (struct ffb_info *) &fb->info.ffb;
@@ -546,16 +553,18 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF);
ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF);
-
+
ffbinfo->dac->type = 0x8000;
ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c);
if (slot == sun_prom_console_id)
fb_restore_palette = ffb_restore_palette;
+
+ type = prom_getintdefault (ffb_node, "board_type", 8);
/* Initialize Brooktree DAC */
- printk("DAC %d\n", ffbinfo->dac_rev);
+ printk("TYPE %d DAC %d\n", type, ffbinfo->dac_rev);
if (slot && sun_prom_console_id == slot)
return;
@@ -573,18 +582,152 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
extern unsigned char vga_font[];
+#define FFB_BLITC_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ register u32 *clut = fbinfo[0].info.ffb.clut; \
+ int i; \
+ ffb->ppc = 0x203; \
+ ffb->fg = clut[attr & 0xf]; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->bg = clut[attr>>4];
+#define FFB_BLITC_BODY4(count,x,y,start,action) \
+ while (count >= 4) { \
+ count -= 4; \
+ ffb->fontw = 32; \
+ ffb->fontinc = 0x10000; \
+ ffb->fontxy = (y << 16) + x; \
+ x += 32; \
+ start; \
+ for (i = 0; i < CHAR_HEIGHT; i++) { \
+ action; \
+ } \
+ }
+#define FFB_BLITC_BODY1(x,y,action) \
+ ffb->fontw = 8; \
+ ffb->fontinc = 0x10000; \
+ ffb->fontxy = (y << 16) + x; \
+ x += 8; \
+ for (i = 0; i < CHAR_HEIGHT; i++) { \
+ action; \
+ }
+#define FFB_BLITC_END \
+ }
+
static void ffb_blitc(unsigned short charattr, int xoff, int yoff)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ unsigned char *p = &vga_font[((unsigned char)charattr) << 4];
+ FFB_BLITC_START(attrib)
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p++) << 24))
+ FFB_BLITC_END
}
static void ffb_setw(int xoff, int yoff, unsigned short c, int count)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(c);
+ unsigned char *p = &vga_font[((unsigned char)c) << 4];
+ register unsigned char *q;
+ register uint l;
+ FFB_BLITC_START(attrib)
+ if (count >= 4) {
+ FFB_BLITC_BODY4(count, xoff, yoff, q = p,
+ l = *q++;
+ l |= l << 8;
+ l |= l << 16;
+ ffb->font=l)
+ }
+ while (count) {
+ count--;
+ q = p;
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*q++) << 24));
+ }
+ FFB_BLITC_END
}
static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p);
+ unsigned char *p1, *p2, *p3, *p4;
+ FFB_BLITC_START(attrib)
+ if (count >= 4) {
+ FFB_BLITC_BODY4(count, xoff, yoff,
+ p1 = &vga_font[((unsigned char)*p++) << 4];
+ p2 = &vga_font[((unsigned char)*p++) << 4];
+ p3 = &vga_font[((unsigned char)*p++) << 4];
+ p4 = &vga_font[((unsigned char)*p++) << 4],
+ ffb->font=((uint)*p4++) | ((((uint)*p3++) | ((((uint)*p2++) | (((uint)*p1++) << 8)) << 8)) << 8))
+ }
+ while (count) {
+ count--;
+ p1 = &vga_font[((unsigned char)*p++) << 4];
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p1++) << 24));
+ }
+ FFB_BLITC_END
}
+#if 0
+#define FFB_FILL_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ register u32 *clut = fbinfo[0].info.ffb.clut; \
+ ffb->ppc =0x1803; \
+ ffb->fg = clut[attr & 0xf]; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->unk2 = 8;
+#define FFB_FILL_END \
+ }
+#else
+#define FFB_FILL_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ ffb->ppc = 0x1803; \
+ ffb->fg = 0; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->unk2 = 8;
+#define FFB_FILL_END \
+ }
+#endif
+
static void ffb_fill(int attrib, int count, int *boxes)
{
+ attrib = 5;
+ FFB_FILL_START(attrib)
+ while (count-- > 0) {
+ ffb->by = boxes[1];
+ ffb->bx = boxes[0];
+ ffb->bw = boxes[2];
+ ffb->bh = boxes[3];
+ boxes += 4;
+ }
+ FFB_FILL_END
+}
+
+__initfunc(void ffb_penguin(int x_margin, int y_margin, int ncpus))
+{
+ int i, j, k;
+ u32 *p, *q;
+ unsigned char *r;
+ unsigned char c;
+
+ p = (u32 *)(fbinfo[0].info.ffb.physbase + FFB_DFB24_POFF + y_margin*8192 + x_margin*4);
+ for (i = 0; i < 80; i++, p += 2048) {
+ q = p;
+ for (j = 0; j < ncpus; j++) {
+ r = linux_logo + 80 * i;
+ for (k = 0; k < 80; k++, r++) {
+ c = *r - 32;
+ *q++ = (linux_logo_red[c]) |
+ (linux_logo_green[c]<<8) |
+ (linux_logo_blue[c]<<16);
+ }
+ q += 8;
+ }
+ }
}
diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h
index 0aa9f2b48..ea58a6346 100644
--- a/drivers/sbus/char/fb.h
+++ b/drivers/sbus/char/fb.h
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $
+/* $Id: fb.h,v 1.33 1997/08/25 07:50:29 jj Exp $
* fb.h: contains the definitions of the structures that various sun
* frame buffer can use to do console driver stuff.
*
@@ -122,6 +122,7 @@ typedef struct fbinfo {
struct tcx_info tcx;
struct leo_info leo;
struct ffb_info ffb;
+ void *private;
} info; /* per frame information */
int space; /* I/O space this card resides in */
int blanked; /* true if video blanked */
@@ -134,6 +135,7 @@ typedef struct fbinfo {
int emulations[4]; /* possible emulations (-1 N/A) */
int prom_node; /* node of the device in prom tree */
int base_depth; /* depth of fb->base piece */
+ int linebytes; /* number of bytes in a row */
struct cg_cursor cursor; /* kernel state of hw cursor */
int (*mmap)(struct inode *, struct file *, struct vm_area_struct *,
long fb_base, struct fbinfo *);
@@ -149,16 +151,20 @@ typedef struct fbinfo {
void (*setcursormap)(struct fbinfo *, unsigned char *,
unsigned char *, unsigned char *);
unsigned long (*postsetup)(struct fbinfo *, unsigned long);
+ void (*clear_fb)(int);
+ void (*set_other_palette)(int);
void (*blitc)(unsigned short, int, int);
void (*setw)(int, int, unsigned short, int);
void (*cpyw)(int, int, unsigned short *, int);
void (*fill)(int, int, int *);
+ void (*draw_penguin)(int,int,int);
unsigned char *color_map;
struct openpromfs_dev proc_entry;
} fbinfo_t;
#define CM(i, j) [3*(i)+(j)]
+extern unsigned char sparc_color_table[];
extern unsigned char reverse_color_table[];
#define CHARATTR_TO_SUNCOLOR(attr) \
@@ -196,13 +202,13 @@ extern unsigned long get_phys (unsigned long addr);
extern int get_iospace (unsigned long addr);
extern void render_screen(void);
-extern void sun_hw_hide_cursor(void);
-extern void sun_hw_set_cursor(int, int);
-extern int sun_hw_scursor(struct fbcursor *,fbinfo_t *);
-extern int sun_hw_cursor_shown;
+extern void sbus_hw_hide_cursor(void);
+extern void sbus_hw_set_cursor(int, int);
+extern int sbus_hw_scursor(struct fbcursor *,fbinfo_t *);
+extern int sbus_hw_cursor_shown;
extern int sun_prom_console_id;
-extern unsigned long sun_cg_postsetup(fbinfo_t *, unsigned long);
+extern unsigned long cg_postsetup(fbinfo_t *, unsigned long);
#define FB_DEV(x) (MINOR(x) / 32)
@@ -216,4 +222,12 @@ extern void tcx_setup (fbinfo_t *, int, int, u32, struct linux_sbus_device *);
extern void creator_setup (fbinfo_t *, int, int, unsigned long, int);
extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space);
+extern unsigned char linux_logo_red[];
+extern unsigned char linux_logo_green[];
+extern unsigned char linux_logo_blue[];
+extern unsigned char linux_logo[];
+extern unsigned char linux_logo_bw[];
+extern unsigned int linux_logo_colors;
+extern char logo_banner[];
+
#endif __SPARC_FB_H_
diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c
index 1b5d06e4b..e08cf1822 100644
--- a/drivers/sbus/char/leo.c
+++ b/drivers/sbus/char/leo.c
@@ -1,4 +1,4 @@
-/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $
+/* $Id: leo.c,v 1.25 1997/08/22 17:33:58 jj Exp $
* leo.c: SUNW,leo 24/8bit frame buffer driver
*
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -9,12 +9,12 @@
#include <linux/tty.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
+#include <linux/delay.h>
#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/fbio.h>
#include <asm/pgtable.h>
-#include <asm/delay.h>
#include <asm/uaccess.h>
/* These must be included after asm/fbio.h */
@@ -129,6 +129,7 @@ static void leo_blitc(unsigned short, int, int);
static void leo_setw(int, int, unsigned short, int);
static void leo_cpyw(int, int, unsigned short *, int);
static void leo_fill(int, int, int *);
+static void leo_penguin(int,int,int);
static void
leo_restore_palette (fbinfo_t *fb)
@@ -496,8 +497,8 @@ leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a
static void
leo_reset (fbinfo_t *fb)
{
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
}
@@ -537,6 +538,7 @@ __initfunc(void leo_setup (fbinfo_t *fb, int slot, u32 leo, int leo_io))
fb->setw = leo_setw;
fb->cpyw = leo_cpyw;
fb->fill = leo_fill;
+ fb->draw_penguin = leo_penguin;
fb->base_depth = 0;
leoinfo = (struct leo_info *) &fb->info.leo;
@@ -692,3 +694,9 @@ static void leo_fill(int attrib, int count, int *boxes)
us->fill = (boxes[0] & 0x7ff) | ((boxes[1] & 0x7ff) << 11) | ((i & 3) << 29) | ((i & 8) ? 0x80000000 : 0);
}
}
+
+__initfunc(void leo_penguin(int x_margin, int y_margin, int ncpus))
+{
+ suncons_ops.clear_screen();
+ /* FIXME: Write this */
+}
diff --git a/drivers/sbus/char/mach64.c b/drivers/sbus/char/mach64.c
new file mode 100644
index 000000000..c3c562306
--- /dev/null
+++ b/drivers/sbus/char/mach64.c
@@ -0,0 +1,203 @@
+/* $Id: mach64.c,v 1.8 1997/08/25 07:50:34 jj Exp $
+ * mach64.c: Ultra/PCI Mach64 console driver.
+ *
+ * Just about all of this is from the PPC/mac driver, see that for
+ * author info. I'm only responsible for grafting it into working
+ * on PCI Ultra's. The two drivers should be merged.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/proc_fs.h>
+
+#include <asm/oplib.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/sbus.h>
+
+#include "pcicons.h"
+#include "mach64.h"
+#include "fb.h"
+
+#define MACH64_REGOFF 0x7ffc00
+#define MACH64_FBOFF 0x800000
+
+static inline void mach64_waitq(int entries)
+{
+ unsigned short base = (0x8000 >> entries);
+
+ while((pcivga_readl(MACH64_REGOFF + FIFO_STAT) & 0xffff) > base)
+ barrier();
+}
+
+static inline void mach64_idle(void)
+{
+ mach64_waitq(16);
+ while(pcivga_readl(MACH64_REGOFF + GUI_STAT) & 1)
+ barrier();
+}
+
+#if 0 /* not used yet */
+static void mach64_st_514(int offset, char val)
+{
+ mach64_waitq(5);
+ pcivga_writeb(1, MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb((offset & 0xff), MACH64_REGOFF + DAC_W_INDEX);
+ pcivga_writeb(((offset>>8)&0xff), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(val, MACH64_REGOFF + DAC_MASK);
+ pcivga_writeb(0, MACH64_REGOFF + DAC_CNTL);
+}
+
+static void mach64_st_pll(int offset, char val)
+{
+ mach64_waitq(3);
+ pcivga_writeb(((offset<<2)|PLL_WR_EN), MACH64_REGOFF + CLOCK_CNTL + 1);
+ pcivga_writeb(val, MACH64_REGOFF + CLOCK_CNTL + 2);
+ pcivga_writeb(((offset<<2)&~PLL_WR_EN), MACH64_REGOFF + CLOCK_CNTL + 1);
+}
+#endif
+
+static int
+mach64_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
+ long base, fbinfo_t *fb)
+{
+ return -ENOSYS;
+}
+
+static void
+mach64_loadcmap(fbinfo_t *fb, int index, int count)
+{
+ unsigned char tmp;
+ int i;
+
+ mach64_waitq(2);
+ tmp = pcivga_readb(MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb(tmp & 0xfc, MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb(0xff, MACH64_REGOFF + DAC_MASK);
+ for(i = index; count--; i++) {
+ mach64_waitq(4);
+ pcivga_writeb(i, MACH64_REGOFF + DAC_W_INDEX);
+ pcivga_writeb(fb->color_map CM(i, 0), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(fb->color_map CM(i, 1), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(fb->color_map CM(i, 2), MACH64_REGOFF + DAC_DATA);
+ }
+}
+
+static void
+mach64_blank(fbinfo_t *fb)
+{
+ unsigned char gen_cntl;
+
+ gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+ gen_cntl |= 0x40;
+ pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static void
+mach64_unblank(fbinfo_t *fb)
+{
+ unsigned char gen_cntl;
+
+ gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+ gen_cntl &= ~(0x4c);
+ pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static struct mach64_info mach64;
+
+int mach64_init(fbinfo_t *fb)
+{
+ struct pci_dev *pdev;
+ struct pcidev_cookie *cookie;
+ unsigned long addr;
+
+ memset(&mach64, 0, sizeof(mach64));
+ for(pdev = pci_devices; pdev; pdev = pdev->next) {
+ if((pdev->vendor == PCI_VENDOR_ID_ATI) &&
+ (pdev->device == PCI_DEVICE_ID_ATI_264VT))
+ break;
+ }
+ if(!pdev)
+ return -1;
+
+ addr = pdev->base_address[0];
+ pcivga_iobase = pcivga_membase = 0;
+ if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+ else
+ pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+ addr = pdev->base_address[1];
+ if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+ else
+ pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+ if(!pcivga_iobase || !pcivga_membase) {
+ prom_printf("mach64_init: I/O or MEM baseaddr is missing\n");
+ prom_printf("mach64_init: ba[0]=%016lx ba[1]=%016lx\n",
+ pdev->base_address[0], pdev->base_address[1]);
+ prom_halt();
+ }
+
+ printk("mach64_init: IOBASE[%016lx] MEMBASE[%016lx]\n",
+ pcivga_iobase, pcivga_membase);
+
+ cookie = (struct pcidev_cookie *)pdev->sysdata;
+ fb->prom_node = cookie->prom_node;
+ fb->proc_entry.node = cookie->pbm->prom_node;
+
+ fb->type.fb_type = FBTYPE_PCI_MACH64;
+ fb->type.fb_cmsize = 256;
+ fb->info.private = (void *)&mach64;
+ fb->base = pcivga_membase + MACH64_FBOFF;
+
+ switch(pcivga_readl(MACH64_REGOFF + MEM_CNTL) & MEM_SIZE_ALIAS) {
+ case MEM_SIZE_512K:
+ mach64.total_vram = 0x80000;
+ break;
+ case MEM_SIZE_1M:
+ mach64.total_vram = 0x100000;
+ break;
+ case MEM_SIZE_2M:
+ mach64.total_vram = 0x200000;
+ break;
+ case MEM_SIZE_4M:
+ mach64.total_vram = 0x400000;
+ break;
+ case MEM_SIZE_6M:
+ mach64.total_vram = 0x600000;
+ break;
+ case MEM_SIZE_8M:
+ mach64.total_vram = 0x800000;
+ break;
+ default:
+ mach64.total_vram = 0x80000;
+ break;
+ }
+
+ if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
+ & CFG_CHIP_TYPE) == MACH64_VT_ID)
+ mach64.flags |= MACH64_MASK_VT;
+
+ printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n",
+ mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0);
+
+ fb->mmap = mach64_mmap;
+ fb->loadcmap = mach64_loadcmap;
+ fb->ioctl = 0;
+ fb->reset = 0;
+ fb->blank = mach64_blank;
+ fb->unblank = mach64_unblank;
+ fb->setcursor = 0;
+
+ return 0;
+}
diff --git a/drivers/sbus/char/mach64.h b/drivers/sbus/char/mach64.h
new file mode 100644
index 000000000..8bf6be478
--- /dev/null
+++ b/drivers/sbus/char/mach64.h
@@ -0,0 +1,587 @@
+/* $Id: mach64.h,v 1.3 1997/08/24 12:13:07 ecd Exp $
+ * mach64.h: Ultra/PCI mach64 driver constants etc.
+ *
+ * Copyright 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _MACH64_H
+#define _MACH64_H 1
+
+struct mach64_info {
+ unsigned int color_mode;
+ unsigned int flags;
+ unsigned int total_vram;
+};
+
+/* The mach64_info flag bits. */
+#define MACH64_MASK_VT 0x00000001
+
+/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
+
+#define CRTC_H_TOTAL_DISP 0x0000 /* Dword offset 00 */
+#define CRTC_H_SYNC_STRT_WID 0x0004 /* Dword offset 01 */
+#define CRTC_H_SYNC_STRT 0x0004
+#define CRTC_H_SYNC_DLY 0x0005
+#define CRTC_H_SYNC_WID 0x0006
+
+#define CRTC_V_TOTAL_DISP 0x0008 /* Dword offset 02 */
+#define CRTC_V_TOTAL 0x0008
+#define CRTC_V_DISP 0x000a
+#define CRTC_V_SYNC_STRT_WID 0x000C /* Dword offset 03 */
+#define CRTC_V_SYNC_STRT 0x000c
+#define CRTC_V_SYNC_WID 0x000e
+
+#define CRTC_VLINE_CRNT_VLINE 0x0010 /* Dword offset 04 */
+#define CRTC_OFF_PITCH 0x0014 /* Dword offset 05 */
+#define CRTC_OFFSET 0x0014
+#define CRTC_PITCH 0x0016
+
+#define CRTC_INT_CNTL 0x0018 /* Dword offset 06 */
+#define CRTC_GEN_CNTL 0x001C /* Dword offset 07 */
+#define CRTC_PIX_WIDTH 0x001d
+#define CRTC_FIFO 0x001e
+#define CRTC_EXT_DISP 0x001f
+
+#define OVR_CLR 0x0040 /* Dword offset 10 */
+#define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 11 */
+#define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 12 */
+
+#define CUR_CLR0 0x0060 /* Dword offset 18 */
+#define CUR_CLR1 0x0064 /* Dword offset 19 */
+#define CUR_OFFSET 0x0068 /* Dword offset 1A */
+#define CUR_HORZ_VERT_POSN 0x006C /* Dword offset 1B */
+#define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 1C */
+
+#define SCRATCH_REG0 0x0080 /* Dword offset 20 */
+#define SCRATCH_REG1 0x0084 /* Dword offset 21 */
+
+#define CLOCK_CNTL 0x0090 /* Dword offset 24 */
+#define CLOCK_SEL_CNTL 0x0090 // Dword offset 24
+
+#define BUS_CNTL 0x00A0 /* Dword offset 28 */
+
+#define MEM_CNTL 0x00B0 /* Dword offset 2C */
+
+#define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 2D */
+#define MEM_VGA_RP_SEL 0x00B8 /* Dword offset 2E */
+
+#define DAC_REGS 0x00C0 /* Dword offset 30 */
+#define DAC_W_INDEX 0x00C0 /* Dword offset 30 */
+#define DAC_DATA 0x00C1 /* Dword offset 30 */
+#define DAC_MASK 0x00C2 /* Dword offset 30 */
+#define DAC_R_INDEX 0x00C3 /* Dword offset 30 */
+#define DAC_CNTL 0x00C4 /* Dword offset 31 */
+
+#define GEN_TEST_CNTL 0x00D0 /* Dword offset 34 */
+
+#define CONFIG_CNTL 0x00DC /* Dword offset 37 (CT, ET, VT) */
+#define CONFIG_CHIP_ID 0x00E0 /* Dword offset 38 */
+#define CONFIG_STAT0 0x00E4 /* Dword offset 39 */
+#define CONFIG_STAT1 0x00E8 /* Dword offset 3A */
+
+
+/* GUI MEMORY MAPPED Registers */
+
+#define DST_OFF_PITCH 0x0100 /* Dword offset 40 */
+#define DST_X 0x0104 /* Dword offset 41 */
+#define DST_Y 0x0108 /* Dword offset 42 */
+#define DST_Y_X 0x010C /* Dword offset 43 */
+#define DST_WIDTH 0x0110 /* Dword offset 44 */
+#define DST_HEIGHT 0x0114 /* Dword offset 45 */
+#define DST_HEIGHT_WIDTH 0x0118 /* Dword offset 46 */
+#define DST_X_WIDTH 0x011C /* Dword offset 47 */
+#define DST_BRES_LNTH 0x0120 /* Dword offset 48 */
+#define DST_BRES_ERR 0x0124 /* Dword offset 49 */
+#define DST_BRES_INC 0x0128 /* Dword offset 4A */
+#define DST_BRES_DEC 0x012C /* Dword offset 4B */
+#define DST_CNTL 0x0130 /* Dword offset 4C */
+
+#define SRC_OFF_PITCH 0x0180 /* Dword offset 60 */
+#define SRC_X 0x0184 /* Dword offset 61 */
+#define SRC_Y 0x0188 /* Dword offset 62 */
+#define SRC_Y_X 0x018C /* Dword offset 63 */
+#define SRC_WIDTH1 0x0190 /* Dword offset 64 */
+#define SRC_HEIGHT1 0x0194 /* Dword offset 65 */
+#define SRC_HEIGHT1_WIDTH1 0x0198 /* Dword offset 66 */
+#define SRC_X_START 0x019C /* Dword offset 67 */
+#define SRC_Y_START 0x01A0 /* Dword offset 68 */
+#define SRC_Y_X_START 0x01A4 /* Dword offset 69 */
+#define SRC_WIDTH2 0x01A8 /* Dword offset 6A */
+#define SRC_HEIGHT2 0x01AC /* Dword offset 6B */
+#define SRC_HEIGHT2_WIDTH2 0x01B0 /* Dword offset 6C */
+#define SRC_CNTL 0x01B4 /* Dword offset 6D */
+
+#define HOST_DATA0 0x0200 /* Dword offset 80 */
+#define HOST_DATA1 0x0204 /* Dword offset 81 */
+#define HOST_DATA2 0x0208 /* Dword offset 82 */
+#define HOST_DATA3 0x020C /* Dword offset 83 */
+#define HOST_DATA4 0x0210 /* Dword offset 84 */
+#define HOST_DATA5 0x0214 /* Dword offset 85 */
+#define HOST_DATA6 0x0218 /* Dword offset 86 */
+#define HOST_DATA7 0x021C /* Dword offset 87 */
+#define HOST_DATA8 0x0220 /* Dword offset 88 */
+#define HOST_DATA9 0x0224 /* Dword offset 89 */
+#define HOST_DATAA 0x0228 /* Dword offset 8A */
+#define HOST_DATAB 0x022C /* Dword offset 8B */
+#define HOST_DATAC 0x0230 /* Dword offset 8C */
+#define HOST_DATAD 0x0234 /* Dword offset 8D */
+#define HOST_DATAE 0x0238 /* Dword offset 8E */
+#define HOST_DATAF 0x023C /* Dword offset 8F */
+#define HOST_CNTL 0x0240 /* Dword offset 90 */
+
+#define PAT_REG0 0x0280 /* Dword offset A0 */
+#define PAT_REG1 0x0284 /* Dword offset A1 */
+#define PAT_CNTL 0x0288 /* Dword offset A2 */
+
+#define SC_LEFT 0x02A0 /* Dword offset A8 */
+#define SC_RIGHT 0x02A4 /* Dword offset A9 */
+#define SC_LEFT_RIGHT 0x02A8 /* Dword offset AA */
+#define SC_TOP 0x02AC /* Dword offset AB */
+#define SC_BOTTOM 0x02B0 /* Dword offset AC */
+#define SC_TOP_BOTTOM 0x02B4 /* Dword offset AD */
+
+#define DP_BKGD_CLR 0x02C0 /* Dword offset B0 */
+#define DP_FRGD_CLR 0x02C4 /* Dword offset B1 */
+#define DP_WRITE_MASK 0x02C8 /* Dword offset B2 */
+#define DP_CHAIN_MASK 0x02CC /* Dword offset B3 */
+#define DP_PIX_WIDTH 0x02D0 /* Dword offset B4 */
+#define DP_MIX 0x02D4 /* Dword offset B5 */
+#define DP_SRC 0x02D8 /* Dword offset B6 */
+
+#define CLR_CMP_CLR 0x0300 /* Dword offset C0 */
+#define CLR_CMP_MASK 0x0304 /* Dword offset C1 */
+#define CLR_CMP_CNTL 0x0308 /* Dword offset C2 */
+
+#define FIFO_STAT 0x0310 /* Dword offset C4 */
+
+#define CONTEXT_MASK 0x0320 /* Dword offset C8 */
+#define CONTEXT_LOAD_CNTL 0x032C /* Dword offset CB */
+
+#define GUI_TRAJ_CNTL 0x0330 /* Dword offset CC */
+#define GUI_STAT 0x0338 /* Dword offset CE */
+
+
+/* CRTC control values (mostly CRTC_GEN_CNTL) */
+
+#define CRTC_H_SYNC_NEG 0x00200000
+#define CRTC_V_SYNC_NEG 0x00200000
+
+#define CRTC_DBL_SCAN_EN 0x00000001
+#define CRTC_INTERLACE_EN 0x00000002
+#define CRTC_HSYNC_DIS 0x00000004
+#define CRTC_VSYNC_DIS 0x00000008
+#define CRTC_CSYNC_EN 0x00000010
+#define CRTC_PIX_BY_2_EN 0x00000020
+#define CRTC_BLANK 0x00000040
+
+#define CRTC_PIX_WIDTH_MASK 0x00000700
+#define CRTC_PIX_WIDTH_4BPP 0x00000100
+#define CRTC_PIX_WIDTH_8BPP 0x00000200
+#define CRTC_PIX_WIDTH_15BPP 0x00000300
+#define CRTC_PIX_WIDTH_16BPP 0x00000400
+#define CRTC_PIX_WIDTH_24BPP 0x00000500
+#define CRTC_PIX_WIDTH_32BPP 0x00000600
+
+#define CRTC_BYTE_PIX_ORDER 0x00000800
+#define CRTC_PIX_ORDER_MSN_LSN 0x00000000
+#define CRTC_PIX_ORDER_LSN_MSN 0x00000800
+
+#define CRTC_FIFO_LWM 0x000f0000
+#define CRTC_EXT_DISP_EN 0x01000000
+#define CRTC_EXT_EN 0x02000000
+
+#define CRTC_CRNT_VLINE 0x07f00000
+#define CRTC_VBLANK 0x00000001
+
+/* DAC control values */
+
+#define DAC_EXT_SEL_RS2 0x01
+#define DAC_EXT_SEL_RS3 0x02
+#define DAC_8BIT_EN 0x00000100
+#define DAC_PIX_DLY_MASK 0x00000600
+#define DAC_PIX_DLY_0NS 0x00000000
+#define DAC_PIX_DLY_2NS 0x00000200
+#define DAC_PIX_DLY_4NS 0x00000400
+#define DAC_BLANK_ADJ_MASK 0x00001800
+#define DAC_BLANK_ADJ_0 0x00000000
+#define DAC_BLANK_ADJ_1 0x00000800
+#define DAC_BLANK_ADJ_2 0x00001000
+
+
+/* Mix control values */
+
+#define MIX_NOT_DST 0x0000
+#define MIX_0 0x0001
+#define MIX_1 0x0002
+#define MIX_DST 0x0003
+#define MIX_NOT_SRC 0x0004
+#define MIX_XOR 0x0005
+#define MIX_XNOR 0x0006
+#define MIX_SRC 0x0007
+#define MIX_NAND 0x0008
+#define MIX_NOT_SRC_OR_DST 0x0009
+#define MIX_SRC_OR_NOT_DST 0x000a
+#define MIX_OR 0x000b
+#define MIX_AND 0x000c
+#define MIX_SRC_AND_NOT_DST 0x000d
+#define MIX_NOT_SRC_AND_DST 0x000e
+#define MIX_NOR 0x000f
+
+/* Maximum engine dimensions */
+#define ENGINE_MIN_X 0
+#define ENGINE_MIN_Y 0
+#define ENGINE_MAX_X 4095
+#define ENGINE_MAX_Y 16383
+
+/* Mach64 engine bit constants - these are typically ORed together */
+
+/* BUS_CNTL register constants */
+#define BUS_FIFO_ERR_ACK 0x00200000
+#define BUS_HOST_ERR_ACK 0x00800000
+
+/* GEN_TEST_CNTL register constants */
+#define GEN_OVR_OUTPUT_EN 0x20
+#define HWCURSOR_ENABLE 0x80
+#define GUI_ENGINE_ENABLE 0x100
+#define BLOCK_WRITE_ENABLE 0x200
+
+/* CLOCK_CNTL register constants */
+#define CLOCK_SEL 0x0f
+#define CLOCK_DIV 0x30
+#define CLOCK_DIV1 0x00
+#define CLOCK_DIV2 0x10
+#define CLOCK_DIV4 0x20
+#define CLOCK_STROBE 0x40
+#define PLL_WR_EN 0x02
+
+/* PLL registers */
+#define PLL_MACRO_CNTL 0x01
+#define PLL_REF_DIV 0x02
+#define PLL_GEN_CNTL 0x03
+#define MCLK_FB_DIV 0x04
+#define PLL_VCLK_CNTL 0x05
+#define VCLK_POST_DIV 0x06
+#define VCLK0_FB_DIV 0x07
+#define VCLK1_FB_DIV 0x08
+#define VCLK2_FB_DIV 0x09
+#define VCLK3_FB_DIV 0x0A
+#define PLL_XCLK_CNTL 0x0B
+#define PLL_TEST_CTRL 0x0E
+#define PLL_TEST_COUNT 0x0F
+
+/* Fields in PLL registers */
+#define PLL_PC_GAIN 0x07
+#define PLL_VC_GAIN 0x18
+#define PLL_DUTY_CYC 0xE0
+#define PLL_OVERRIDE 0x01
+#define PLL_MCLK_RST 0x02
+#define OSC_EN 0x04
+#define EXT_CLK_EN 0x08
+#define MCLK_SRC_SEL 0x70
+#define EXT_CLK_CNTL 0x80
+#define VCLK_SRC_SEL 0x03
+#define PLL_VCLK_RST 0x04
+#define VCLK_INVERT 0x08
+#define VCLK0_POST 0x03
+#define VCLK1_POST 0x0C
+#define VCLK2_POST 0x30
+#define VCLK3_POST 0xC0
+
+/* CONFIG_CNTL register constants */
+#define APERTURE_4M_ENABLE 1
+#define APERTURE_8M_ENABLE 2
+#define VGA_APERTURE_ENABLE 4
+
+/* CONFIG_STAT0 register constants (GX, CX) */
+#define CFG_BUS_TYPE 0x00000007
+#define CFG_MEM_TYPE 0x00000038
+#define CFG_INIT_DAC_TYPE 0x00000e00
+
+/* CONFIG_STAT0 register constants (CT, ET, VT) */
+#define CFG_MEM_TYPE_xT 0x00000007
+
+#define ISA 0
+#define EISA 1
+#define LOCAL_BUS 6
+#define PCI 7
+
+/* Memory types for GX, CX */
+#define DRAMx4 0
+#define VRAMx16 1
+#define VRAMx16ssr 2
+#define DRAMx16 3
+#define GraphicsDRAMx16 4
+#define EnhancedVRAMx16 5
+#define EnhancedVRAMx16ssr 6
+
+/* Memory types for CT, ET, VT, GT */
+#define DRAM 0
+#define EDO_DRAM 1
+#define PSEUDO_EDO 2
+#define SDRAM 3
+
+#define DAC_INTERNAL 0x00
+#define DAC_IBMRGB514 0x01
+#define DAC_ATI68875 0x02
+#define DAC_TVP3026_A 0x72
+#define DAC_BT476 0x03
+#define DAC_BT481 0x04
+#define DAC_ATT20C491 0x14
+#define DAC_SC15026 0x24
+#define DAC_MU9C1880 0x34
+#define DAC_IMSG174 0x44
+#define DAC_ATI68860_B 0x05
+#define DAC_ATI68860_C 0x15
+#define DAC_TVP3026_B 0x75
+#define DAC_STG1700 0x06
+#define DAC_ATT498 0x16
+#define DAC_STG1702 0x07
+#define DAC_SC15021 0x17
+#define DAC_ATT21C498 0x27
+#define DAC_STG1703 0x37
+#define DAC_CH8398 0x47
+#define DAC_ATT20C408 0x57
+
+#define CLK_ATI18818_0 0
+#define CLK_ATI18818_1 1
+#define CLK_STG1703 2
+#define CLK_CH8398 3
+#define CLK_INTERNAL 4
+#define CLK_ATT20C408 5
+#define CLK_IBMRGB514 6
+
+/* MEM_CNTL register constants */
+#define MEM_SIZE_ALIAS 0x00000007
+#define MEM_SIZE_512K 0x00000000
+#define MEM_SIZE_1M 0x00000001
+#define MEM_SIZE_2M 0x00000002
+#define MEM_SIZE_4M 0x00000003
+#define MEM_SIZE_6M 0x00000004
+#define MEM_SIZE_8M 0x00000005
+#define MEM_SIZE_ALIAS_GTB 0x0000000F
+#define MEM_SIZE_2M_GTB 0x00000003
+#define MEM_SIZE_4M_GTB 0x00000007
+#define MEM_SIZE_6M_GTB 0x00000009
+#define MEM_SIZE_8M_GTB 0x0000000B
+#define MEM_BNDRY 0x00030000
+#define MEM_BNDRY_0K 0x00000000
+#define MEM_BNDRY_256K 0x00010000
+#define MEM_BNDRY_512K 0x00020000
+#define MEM_BNDRY_1M 0x00030000
+#define MEM_BNDRY_EN 0x00040000
+
+/* ATI PCI constants */
+#define PCI_ATI_VENDOR_ID 0x1002
+#define PCI_MACH64_GX 0x4758
+#define PCI_MACH64_CX 0x4358
+#define PCI_MACH64_CT 0x4354
+#define PCI_MACH64_ET 0x4554
+#define PCI_MACH64_VT 0x5654
+#define PCI_MACH64_GT 0x4754
+
+/* CONFIG_CHIP_ID register constants */
+#define CFG_CHIP_TYPE 0x0000FFFF
+#define CFG_CHIP_CLASS 0x00FF0000
+#define CFG_CHIP_REV 0xFF000000
+#define CFG_CHIP_VERSION 0x07000000
+#define CFG_CHIP_FOUNDRY 0x38000000
+#define CFG_CHIP_REVISION 0xC0000000
+
+/* Chip IDs read from CONFIG_CHIP_ID */
+#define MACH64_GX_ID 0xD7
+#define MACH64_CX_ID 0x57
+#define MACH64_CT_ID 0x4354
+#define MACH64_ET_ID 0x4554
+#define MACH64_VT_ID 0x5654
+#define MACH64_GT_ID 0x4754
+
+/* Mach64 chip types */
+#define MACH64_UNKNOWN 0
+#define MACH64_GX 1
+#define MACH64_CX 2
+#define MACH64_CT 3
+#define MACH64_ET 4
+#define MACH64_VT 5
+#define MACH64_GT 6
+
+/* DST_CNTL register constants */
+#define DST_X_RIGHT_TO_LEFT 0
+#define DST_X_LEFT_TO_RIGHT 1
+#define DST_Y_BOTTOM_TO_TOP 0
+#define DST_Y_TOP_TO_BOTTOM 2
+#define DST_X_MAJOR 0
+#define DST_Y_MAJOR 4
+#define DST_X_TILE 8
+#define DST_Y_TILE 0x10
+#define DST_LAST_PEL 0x20
+#define DST_POLYGON_ENABLE 0x40
+#define DST_24_ROTATION_ENABLE 0x80
+
+/* SRC_CNTL register constants */
+#define SRC_PATTERN_ENABLE 1
+#define SRC_ROTATION_ENABLE 2
+#define SRC_LINEAR_ENABLE 4
+#define SRC_BYTE_ALIGN 8
+#define SRC_LINE_X_RIGHT_TO_LEFT 0
+#define SRC_LINE_X_LEFT_TO_RIGHT 0x10
+
+/* HOST_CNTL register constants */
+#define HOST_BYTE_ALIGN 1
+
+/* GUI_TRAJ_CNTL register constants */
+#define PAT_MONO_8x8_ENABLE 0x01000000
+#define PAT_CLR_4x2_ENABLE 0x02000000
+#define PAT_CLR_8x1_ENABLE 0x04000000
+
+/* DP_CHAIN_MASK register constants */
+#define DP_CHAIN_4BPP 0x8888
+#define DP_CHAIN_7BPP 0xD2D2
+#define DP_CHAIN_8BPP 0x8080
+#define DP_CHAIN_8BPP_RGB 0x9292
+#define DP_CHAIN_15BPP 0x4210
+#define DP_CHAIN_16BPP 0x8410
+#define DP_CHAIN_24BPP 0x8080
+#define DP_CHAIN_32BPP 0x8080
+
+/* DP_PIX_WIDTH register constants */
+#define DST_1BPP 0
+#define DST_4BPP 1
+#define DST_8BPP 2
+#define DST_15BPP 3
+#define DST_16BPP 4
+#define DST_32BPP 6
+#define SRC_1BPP 0
+#define SRC_4BPP 0x100
+#define SRC_8BPP 0x200
+#define SRC_15BPP 0x300
+#define SRC_16BPP 0x400
+#define SRC_32BPP 0x600
+#define HOST_1BPP 0
+#define HOST_4BPP 0x10000
+#define HOST_8BPP 0x20000
+#define HOST_15BPP 0x30000
+#define HOST_16BPP 0x40000
+#define HOST_32BPP 0x60000
+#define BYTE_ORDER_MSB_TO_LSB 0
+#define BYTE_ORDER_LSB_TO_MSB 0x1000000
+
+/* DP_MIX register constants */
+#define BKGD_MIX_NOT_D 0
+#define BKGD_MIX_ZERO 1
+#define BKGD_MIX_ONE 2
+#define BKGD_MIX_D 3
+#define BKGD_MIX_NOT_S 4
+#define BKGD_MIX_D_XOR_S 5
+#define BKGD_MIX_NOT_D_XOR_S 6
+#define BKGD_MIX_S 7
+#define BKGD_MIX_NOT_D_OR_NOT_S 8
+#define BKGD_MIX_D_OR_NOT_S 9
+#define BKGD_MIX_NOT_D_OR_S 10
+#define BKGD_MIX_D_OR_S 11
+#define BKGD_MIX_D_AND_S 12
+#define BKGD_MIX_NOT_D_AND_S 13
+#define BKGD_MIX_D_AND_NOT_S 14
+#define BKGD_MIX_NOT_D_AND_NOT_S 15
+#define BKGD_MIX_D_PLUS_S_DIV2 0x17
+#define FRGD_MIX_NOT_D 0
+#define FRGD_MIX_ZERO 0x10000
+#define FRGD_MIX_ONE 0x20000
+#define FRGD_MIX_D 0x30000
+#define FRGD_MIX_NOT_S 0x40000
+#define FRGD_MIX_D_XOR_S 0x50000
+#define FRGD_MIX_NOT_D_XOR_S 0x60000
+#define FRGD_MIX_S 0x70000
+#define FRGD_MIX_NOT_D_OR_NOT_S 0x80000
+#define FRGD_MIX_D_OR_NOT_S 0x90000
+#define FRGD_MIX_NOT_D_OR_S 0xa0000
+#define FRGD_MIX_D_OR_S 0xb0000
+#define FRGD_MIX_D_AND_S 0xc0000
+#define FRGD_MIX_NOT_D_AND_S 0xd0000
+#define FRGD_MIX_D_AND_NOT_S 0xe0000
+#define FRGD_MIX_NOT_D_AND_NOT_S 0xf0000
+#define FRGD_MIX_D_PLUS_S_DIV2 0x170000
+
+/* DP_SRC register constants */
+#define BKGD_SRC_BKGD_CLR 0
+#define BKGD_SRC_FRGD_CLR 1
+#define BKGD_SRC_HOST 2
+#define BKGD_SRC_BLIT 3
+#define BKGD_SRC_PATTERN 4
+#define FRGD_SRC_BKGD_CLR 0
+#define FRGD_SRC_FRGD_CLR 0x100
+#define FRGD_SRC_HOST 0x200
+#define FRGD_SRC_BLIT 0x300
+#define FRGD_SRC_PATTERN 0x400
+#define MONO_SRC_ONE 0
+#define MONO_SRC_PATTERN 0x10000
+#define MONO_SRC_HOST 0x20000
+#define MONO_SRC_BLIT 0x30000
+
+/* CLR_CMP_CNTL register constants */
+#define COMPARE_FALSE 0
+#define COMPARE_TRUE 1
+#define COMPARE_NOT_EQUAL 4
+#define COMPARE_EQUAL 5
+#define COMPARE_DESTINATION 0
+#define COMPARE_SOURCE 0x1000000
+
+/* FIFO_STAT register constants */
+#define FIFO_ERR 0x80000000
+
+/* CONTEXT_LOAD_CNTL constants */
+#define CONTEXT_NO_LOAD 0
+#define CONTEXT_LOAD 0x10000
+#define CONTEXT_LOAD_AND_DO_FILL 0x20000
+#define CONTEXT_LOAD_AND_DO_LINE 0x30000
+#define CONTEXT_EXECUTE 0
+#define CONTEXT_CMD_DISABLE 0x80000000
+
+/* GUI_STAT register constants */
+#define ENGINE_IDLE 0
+#define ENGINE_BUSY 1
+#define SCISSOR_LEFT_FLAG 0x10
+#define SCISSOR_RIGHT_FLAG 0x20
+#define SCISSOR_TOP_FLAG 0x40
+#define SCISSOR_BOTTOM_FLAG 0x80
+
+/* ATI VGA Extended Regsiters */
+#define sioATIEXT 0x1ce
+#define bioATIEXT 0x3ce
+
+#define ATI2E 0xae
+#define ATI32 0xb2
+#define ATI36 0xb6
+
+/* VGA Graphics Controller Registers */
+#define VGAGRA 0x3ce
+#define GRA06 0x06
+
+/* VGA Seququencer Registers */
+#define VGASEQ 0x3c4
+#define SEQ02 0x02
+#define SEQ04 0x04
+
+#define MACH64_MAX_X ENGINE_MAX_X
+#define MACH64_MAX_Y ENGINE_MAX_Y
+
+#define INC_X 0x0020
+#define INC_Y 0x0080
+
+#define RGB16_555 0x0000
+#define RGB16_565 0x0040
+#define RGB16_655 0x0080
+#define RGB16_664 0x00c0
+
+#define POLY_TEXT_TYPE 0x0001
+#define IMAGE_TEXT_TYPE 0x0002
+#define TEXT_TYPE_8_BIT 0x0004
+#define TEXT_TYPE_16_BIT 0x0008
+#define POLY_TEXT_TYPE_8 (POLY_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define IMAGE_TEXT_TYPE_8 (IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define POLY_TEXT_TYPE_16 (POLY_TEXT_TYPE | TEXT_TYPE_16_BIT)
+#define IMAGE_TEXT_TYPE_16 (IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT)
+
+#define MACH64_NUM_CLOCKS 16
+#define MACH64_NUM_FREQS 50
+
+#endif /* !(_MACH64_H) */
diff --git a/drivers/sbus/char/pcicons.c b/drivers/sbus/char/pcicons.c
new file mode 100644
index 000000000..82c0df9aa
--- /dev/null
+++ b/drivers/sbus/char/pcicons.c
@@ -0,0 +1,750 @@
+/* $Id: pcicons.c,v 1.9 1997/08/28 02:23:24 ecd Exp $
+ * pcicons.c: PCI specific probing and console operations layer.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PCI
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/major.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/smp.h>
+
+#include <linux/kd.h>
+#include <linux/console_struct.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+
+#include "pcicons.h"
+#include "fb.h"
+
+static int x_margin = 0;
+static int y_margin = 0;
+static int skip_bytes;
+
+static void pci_cursor_blink(unsigned long);
+static __u32 *cursor_screen_pos;
+static __u32 cursor_bits;
+static int cursor_pos = -1;
+static int cursor_off = 1;
+static struct timer_list pci_cursor_timer = {
+ NULL, NULL, 0, 0, pci_cursor_blink
+};
+
+extern int serial_console;
+
+static void pci_install_consops(void);
+static int (*fbuf_offset)(int);
+
+static int color_fbuf_offset_1152_128(int cindex)
+{
+ register int i = (cindex >> 7);
+ /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128(int cindex)
+{
+ register int i = (cindex >> 7);
+ /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+ return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static __u32 expand_bits_8[16] = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static void pci_blitc(unsigned int charattr, unsigned long addr)
+{
+ static int cached_attr = -1;
+ static __u32 fg, bg;
+ fbinfo_t *fb = &fbinfo[0];
+ __u32 *screen;
+ unsigned char attrib;
+ unsigned char *fp;
+ unsigned long flags;
+ int i, idx;
+
+ if ((charattr & 0xff00) != cached_attr) {
+ cached_attr = charattr;
+ attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ fg = attrib & 0x0f;
+ fg |= fg << 8;
+ fg |= fg << 16;
+ bg = attrib >> 4;
+ bg |= bg << 8;
+ bg |= bg << 16;
+ fg ^= bg;
+ }
+
+ idx = (addr - video_mem_base) >> 1;
+ save_flags(flags); cli();
+ if (cursor_pos == idx)
+ cursor_pos = -1;
+ restore_flags(flags);
+
+ screen = (__u32 *)(fb->base + fbuf_offset(idx));
+ fp = &vga_font[(charattr & 0xff) << 4];
+
+ for(i = 0; i < 16; i++) {
+ int bits = *fp++;
+
+ screen[0] = (expand_bits_8[bits >> 4] & fg) ^ bg;
+ screen[1] = (expand_bits_8[bits & 0x0f] & fg) ^ bg;
+ screen = (__u32 *) (((unsigned long)screen) + fb->linebytes);
+ }
+}
+
+
+static void pci_memsetw(void *s, unsigned short c, unsigned int count)
+{
+ unsigned short *p = (unsigned short *)s;
+
+ count >>= 1;
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ while (count) {
+ --count;
+ *p++ = c;
+ }
+ return;
+ }
+ if ((unsigned long)(p + count) > video_mem_base &&
+ (unsigned long)p < video_mem_term) {
+ for (; p < (unsigned short *)video_mem_base && count; count--)
+ *p++ = c;
+ for (; p < (unsigned short *)video_mem_term && count; count--) {
+ if (*p != c) {
+ *p = c;
+ pci_blitc(c, (unsigned long)p);
+ }
+ ++p;
+ }
+ }
+ for (; count; count--)
+ *p++ = c;
+}
+
+static void pci_memcpyw(unsigned short *dst, unsigned short *src,
+ unsigned int count)
+{
+ unsigned short c;
+
+ count >>= 1;
+ if ((unsigned long)(dst + count) > video_mem_base &&
+ (unsigned long)dst < video_mem_term) {
+ for (; dst < (unsigned short *)video_mem_base && count; count--)
+ *dst++ = *src++;
+ for (; dst < (unsigned short *)video_mem_term && count;
+ count--) {
+ c = *src++;
+ if (*dst != c) {
+ *dst = c;
+ pci_blitc(c, (unsigned long)dst);
+ }
+ ++dst;
+ }
+ }
+ for (; count; count--)
+ *dst++ = *src++;
+}
+
+static void pci_scr_writew(unsigned short val, unsigned short *addr)
+{
+ if (*addr != val) {
+ *addr = val;
+ if ((unsigned long)addr < video_mem_term &&
+ (unsigned long)addr >= video_mem_base &&
+ vt_cons[fg_console]->vc_mode == KD_TEXT)
+ pci_blitc(val, (unsigned long) addr);
+ }
+}
+
+static unsigned short pci_scr_readw(unsigned short *addr)
+{
+ return *addr;
+}
+
+static void pci_get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y * video_size_row + (x << 1);
+}
+
+static void pci_set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y * video_size_row + (x << 1);
+}
+
+static void pci_invert_cursor(int cpos)
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned char color;
+ __u32 *screen, mask;
+ int i;
+
+ del_timer(&pci_cursor_timer);
+
+ if (cpos == -1) {
+ if (cursor_off)
+ return;
+ screen = cursor_screen_pos;
+ mask = cursor_bits;
+ } else {
+ screen = (__u32 *)(fb->base + fbuf_offset(cpos)
+ + 14 * fb->linebytes);
+
+ color = CHARATTR_TO_SUNCOLOR(
+ vc_cons[fg_console].d->vc_color << 8);
+
+ mask = (color ^ (color >> 4)) & 0x0f;
+ mask |= mask << 8;
+ mask |= mask << 16;
+
+ cursor_screen_pos = screen;
+ cursor_bits = mask;
+
+ pci_cursor_timer.expires = jiffies + (HZ >> 2);
+ add_timer(&pci_cursor_timer);
+ }
+
+ for (i = 0; i < 2; i++) {
+ screen[0] ^= mask;
+ screen[1] ^= mask;
+ screen = (__u32 *)((unsigned long)screen + fb->linebytes);
+ }
+}
+
+static void pci_cursor_blink(unsigned long ignored)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (cursor_pos != -1) {
+ pci_invert_cursor(cursor_pos);
+ cursor_off = 1 - cursor_off;
+ }
+ restore_flags(flags);
+}
+
+static void pci_hide_cursor(void)
+{
+ unsigned long flags;
+
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ save_flags(flags); cli();
+ if (cursor_pos != -1) {
+ pci_invert_cursor(-1);
+ cursor_pos = -1;
+ }
+ cursor_off = 1;
+ restore_flags(flags);
+}
+
+static void pci_set_cursor(int currcons)
+{
+ unsigned long flags;
+ int old_cursor;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ save_flags(flags); cli();
+ if (!deccm) {
+ pci_hide_cursor();
+ } else {
+ old_cursor = cursor_pos;
+ cursor_pos = (pos - video_mem_base) >> 1;
+ if (old_cursor != -1)
+ pci_invert_cursor(-1);
+ pci_invert_cursor(cursor_pos);
+ cursor_off = 0;
+ }
+ restore_flags(flags);
+}
+
+static void pci_set_palette(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+
+ if (console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ if (fb->loadcmap) {
+ int i, j;
+
+ for (i = 0; i < 16; i++) {
+ j = sparc_color_table[i];
+ fb->color_map CM(i, 0) = default_red[j];
+ fb->color_map CM(i, 1) = default_grn[j];
+ fb->color_map CM(i, 2) = default_blu[j];
+ }
+ fb->loadcmap(fb, 0, 16);
+ }
+}
+
+static void pci_set_other_palette(int n)
+{
+ fbinfo_t *fb = &fbinfo[n];
+
+ if (!n) {
+ pci_set_palette();
+ return;
+ }
+
+ if (fb->loadcmap) {
+ fb->color_map CM(0, 0) = 0;
+ fb->color_map CM(0, 1) = 0;
+ fb->color_map CM(0, 2) = 0;
+ fb->loadcmap(fb, 0, 1);
+ }
+}
+
+static void pci_restore_palette(void)
+{
+ if (fb_restore_palette)
+ fb_restore_palette(&fbinfo[0]);
+}
+
+static int pci_set_get_font(char *arg, int set, int ch512)
+{
+ int i, line;
+
+ if (!arg)
+ return -EINVAL;
+
+ if (!set) {
+ if (clear_user(arg, sizeof(vga_font)))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value;
+
+ value = vga_font[i * CHAR_HEIGHT + line];
+ __put_user_ret(value, (arg + (i * 32 + line)),
+ -EFAULT);
+ }
+ }
+ return 0;
+ }
+
+ if (verify_area(VERIFY_READ, arg, 256 * CHAR_HEIGHT))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value;
+ __get_user_ret(value, (arg + (i + 32 + line)), -EFAULT);
+ vga_font[i * CHAR_HEIGHT + line] = value;
+ }
+ }
+ return 0;
+}
+
+static int pci_con_adjust_height(unsigned long fontheight)
+{
+ return -EINVAL;
+}
+
+static int pci_set_get_cmap(unsigned char *arg, int set)
+{
+ int i;
+
+ if (set)
+ i = VERIFY_READ;
+ else
+ i = VERIFY_WRITE;
+
+ if (verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+ return -EFAULT;
+
+ for (i = 0; i < 16; i++) {
+ if (set) {
+ __get_user_ret(default_red[i], (arg + 0), -EFAULT);
+ __get_user_ret(default_grn[i], (arg + 1), -EFAULT);
+ __get_user_ret(default_blu[i], (arg + 2), -EFAULT);
+ } else {
+ __put_user_ret(default_red[i], (arg + 0), -EFAULT);
+ __put_user_ret(default_grn[i], (arg + 1), -EFAULT);
+ __put_user_ret(default_blu[i], (arg + 2), -EFAULT);
+ }
+ arg += 3;
+ }
+
+ if (set) {
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (vc_cons_allocated(i)) {
+ int j, k;
+
+ for (j = k = 0; j < 16; j++) {
+ vc_cons[i].d->vc_palette[k++] =
+ default_red[i];
+ vc_cons[i].d->vc_palette[k++] =
+ default_grn[i];
+ vc_cons[i].d->vc_palette[k++] =
+ default_blu[i];
+ }
+ }
+ }
+ pci_set_palette();
+ }
+ return -EINVAL;
+}
+
+static void pci_clear_screen(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+
+ if (fb->base)
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fb->type.fb_depth * fb->type.fb_height
+ * fb->type.fb_width) / 8);
+ memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+}
+
+static void pci_clear_fb(int n)
+{
+ fbinfo_t *fb = &fbinfo[n];
+
+ if (!n) {
+ pci_clear_screen();
+ } else if (fb->base) {
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fb->type.fb_depth * fb->type.fb_height
+ * fb->type.fb_width) / 8);
+ }
+}
+
+static void pci_render_screen(void)
+{
+ int count;
+ unsigned short *p;
+
+ count = video_num_columns * video_num_lines;
+ p = (unsigned short *)video_mem_base;
+
+ for (; count--; p++)
+ pci_blitc(*p, (unsigned long)p);
+}
+
+static void pci_clear_margin(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned long p;
+ int h, he;
+
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin << 1));
+ memset((void *)(fb->base + fb->linebytes * fb->type.fb_height
+ - skip_bytes + (x_margin << 1)),
+ (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin << 1));
+ he = fb->type.fb_height - 2 * y_margin;
+ if (fb->type.fb_depth == 1) {
+ for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+ h < he; p += fb->linebytes, h++)
+ memset((void *)p, ~(0), (x_margin << 1));
+ } else {
+ for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+ h < he; p += fb->linebytes, h++)
+ memset((void *)p, reverse_color_table[0],
+ (x_margin << 1));
+ }
+
+ if (fb->switch_from_graph)
+ fb->switch_from_graph();
+}
+
+static unsigned long
+pci_postsetup(fbinfo_t *fb, unsigned long memory_start)
+{
+ fb->color_map = (char *)memory_start;
+ pci_set_palette();
+ return memory_start + fb->type.fb_cmsize * 3;
+}
+
+__initfunc(static unsigned long
+pci_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+ can_do_color = 1;
+
+ video_type = VIDEO_TYPE_SUNPCI;
+ *display_desc = "SUNPCI";
+
+ if(!serial_console) {
+ /* If we fall back to PROM then our output
+ * have to remain readable.
+ */
+ prom_putchar('\033');
+ prom_putchar('[');
+ prom_putchar('H');
+
+ /*
+ * Fake the screen memory with some CPU memory
+ */
+ video_mem_base = kmem_start;
+ kmem_start += video_screen_size;
+ video_mem_term = kmem_start;
+ }
+
+ return kmem_start;
+}
+
+__initfunc(static void pci_con_type_init_finish(void))
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned char *p = (unsigned char *)fb->base + skip_bytes;
+ char q[2] = { 0, 5 };
+ unsigned short *ush;
+ int currcons = 0;
+ int cpu, ncpus;
+ int i;
+
+ if (serial_console)
+ return;
+
+ ncpus = linux_num_cpus;
+ if (ncpus > 4)
+ ncpus = 4;
+
+#if 0
+ if (fb->draw_penguin)
+ fb->draw_penguin(x_margin, y_margin, ncpus);
+ else
+#endif
+ if (fb->type.fb_depth == 8 && fb->loadcmap) {
+ for (i = 0; i < linux_logo_colors; i++) {
+ fb->color_map CM(i + 32, 0) = linux_logo_red[i];
+ fb->color_map CM(i + 32, 1) = linux_logo_green[i];
+ fb->color_map CM(i + 32, 2) = linux_logo_blue[i];
+ }
+ fb->loadcmap(fb, 32, linux_logo_colors);
+ for (i = 0; i < 80; i++, p += fb->linebytes) {
+ for (cpu = 0; cpu < ncpus; cpu++)
+ memcpy(p + (cpu * 88), linux_logo + 80 * i, 80);
+ }
+ } else if (fb->type.fb_depth == 1) {
+ for (i = 0; i < 80; i++, p += fb->linebytes) {
+ for (cpu = 0; cpu < ncpus; cpu++)
+ memcpy(p + (cpu * 11),
+ linux_logo_bw + 10 * i, 10);
+ }
+ }
+ putconsxy(0, q);
+
+ ush = (unsigned short *)video_mem_base + video_num_columns * 2 + 20
+ + 10 * (ncpus - 1);
+
+ for (p = logo_banner; *p; p++, ush++) {
+ *ush = (attr << 8) + *p;
+ pci_blitc(*ush, (unsigned long)ush);
+ }
+
+ for (i = 0; i < 5; i++) {
+ ush = (unsigned short *)video_mem_base + i * video_num_columns;
+ memset(ush, 0xff, 20);
+ }
+}
+
+unsigned long pcivga_iobase = 0;
+unsigned long pcivga_membase = 0;
+
+static struct {
+ int depth;
+ int resx, resy;
+ int x_margin, y_margin;
+} scr_def[] = {
+ { 8, 1152, 900, 64, 18 },
+ { 8, 1024, 768, 0, 0 },
+ { 0 }
+};
+
+extern int mach64_init(fbinfo_t *fb);
+
+__initfunc(int pci_console_probe(void))
+{
+ fbinfo_t *fb = &fbinfo[0];
+ char *p;
+ int i;
+
+ if (1
+#if 1
+ && mach64_init(fb)
+#endif
+ && 1) {
+ return -ENODEV;
+ }
+ fbinfos++;
+
+ fb->clear_fb = pci_clear_fb;
+ fb->set_other_palette = pci_set_other_palette;
+ fb->postsetup = pci_postsetup;
+ fb->blanked = 0;
+
+ fb->type.fb_height = prom_getintdefault(fb->prom_node, "height", 900);
+ fb->type.fb_width = prom_getintdefault(fb->prom_node, "width", 1152);
+ fb->type.fb_depth = prom_getintdefault(fb->prom_node, "depth", 8);
+ fb->linebytes = prom_getintdefault(fb->prom_node, "linebytes", 1152);
+ fb->type.fb_size = PAGE_ALIGN(fb->linebytes * fb->type.fb_height);
+
+ fb->proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, 0);
+ fb->proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ prom_getname(fb->prom_node, fb->proc_entry.name, 32 - 3);
+ p = strchr(fb->proc_entry.name, 0);
+ sprintf(p, ":%d", 0);
+
+ for (i = 0; scr_def[i].depth; i++) {
+ if ((scr_def[i].resx != fb->type.fb_width) ||
+ (scr_def[i].resy != fb->type.fb_height) ||
+ (scr_def[i].depth != fb->type.fb_depth))
+ continue;
+ x_margin = scr_def[i].x_margin;
+ y_margin = scr_def[i].y_margin;
+ skip_bytes = y_margin * fb->linebytes + x_margin;
+ switch (fb->type.fb_width) {
+ case 1152:
+ fbuf_offset = color_fbuf_offset_1152_128;
+ break;
+ case 1024:
+ fbuf_offset = color_fbuf_offset_1024_128;
+ break;
+ default:
+ prom_printf("can't handle console width %d\n",
+ fb->type.fb_width);
+ prom_halt();
+ }
+ }
+
+ pci_install_consops();
+ return fb_init();
+}
+
+__initfunc(void pci_console_inithook(void))
+{
+ extern char *console_fb_path;
+ char prop[16];
+ int node = 0;
+ int width, height, depth, linebytes;
+ int x_margin, y_margin;
+ int i, len;
+
+ if (console_fb_path) {
+ char *p;
+ for (p = console_fb_path; *p && *p != ' '; p++) ;
+ *p = 0;
+ node = prom_pathtoinode(console_fb_path);
+ }
+ if (!node) {
+ node = prom_inst2pkg(prom_stdout);
+ if (!node) {
+ prom_printf("can't find output-device node\n");
+ prom_halt();
+ }
+ len = prom_getproperty(node, "device_type", prop, sizeof(prop));
+ if (len < 0) {
+ prom_printf("output-device doesn't have"
+ " device_type property\n");
+ prom_halt();
+ }
+ if (len != sizeof("display") ||
+ strncmp("display", prop, sizeof("display"))) {
+ prom_printf("output-device is %s"
+ " not \"display\"\n", prop);
+ prom_halt();
+ }
+ }
+
+ depth = prom_getintdefault(node, "depth", 8);
+ width = prom_getintdefault(node, "width", 1152);
+ height = prom_getintdefault(node, "height", 900);
+ linebytes = prom_getintdefault(node, "linebytes", 1152);
+
+ for (i = 0; scr_def[i].depth; i++) {
+ if ((scr_def[i].resx != width) ||
+ (scr_def[i].resy != height) ||
+ (scr_def[i].depth != depth))
+ continue;
+ x_margin = scr_def[i].x_margin;
+ y_margin = scr_def[i].y_margin;
+
+ ORIG_VIDEO_COLS = width / 8 - 2 * x_margin / depth;
+ ORIG_VIDEO_LINES = (height - 2 * y_margin) / 16;
+ }
+
+ suncons_ops.con_type_init = pci_con_type_init;
+}
+
+__initfunc(static void pci_install_consops(void))
+{
+ suncons_ops.memsetw = pci_memsetw;
+ suncons_ops.memcpyw = pci_memcpyw;
+ suncons_ops.scr_writew = pci_scr_writew;
+ suncons_ops.scr_readw = pci_scr_readw;
+
+ suncons_ops.get_scrmem = pci_get_scrmem;
+ suncons_ops.set_scrmem = pci_set_scrmem;
+
+ suncons_ops.hide_cursor = pci_hide_cursor;
+ suncons_ops.set_cursor = pci_set_cursor;
+ suncons_ops.set_get_font = pci_set_get_font;
+ suncons_ops.con_adjust_height = pci_con_adjust_height;
+ suncons_ops.set_get_cmap = pci_set_get_cmap;
+ suncons_ops.set_palette = pci_set_palette;
+ suncons_ops.set_other_palette = pci_set_other_palette;
+ suncons_ops.console_restore_palette = pci_restore_palette;
+
+ suncons_ops.con_type_init = pci_con_type_init;
+ suncons_ops.con_type_init_finish = pci_con_type_init_finish;
+
+ suncons_ops.clear_screen = pci_clear_screen;
+ suncons_ops.render_screen = pci_render_screen;
+ suncons_ops.clear_margin = pci_clear_margin;
+}
+
+#endif /* CONFIG_PCI */
diff --git a/drivers/sbus/char/pcicons.h b/drivers/sbus/char/pcicons.h
new file mode 100644
index 000000000..7a351a247
--- /dev/null
+++ b/drivers/sbus/char/pcicons.h
@@ -0,0 +1,79 @@
+/* $Id: pcicons.h,v 1.2 1997/08/24 12:13:11 ecd Exp $
+ * pcicons.h: Stuff which is generic across all PCI console drivers.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef PCICONS_H
+#define PCICONS_H
+
+#include <linux/pci.h>
+#include <asm/asi.h>
+#include <asm/io.h>
+
+extern unsigned long pcivga_iobase;
+extern unsigned long pcivga_membase;
+
+extern unsigned char vga_font[8192];
+
+extern __inline__ unsigned int pcivga_inb(unsigned long off)
+{
+ return inb(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inw(unsigned long off)
+{
+ return inw(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inl(unsigned long off)
+{
+ return inl(pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outb(unsigned char val, unsigned long off)
+{
+ outb(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outw(unsigned short val, unsigned long off)
+{
+ outw(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outl(unsigned int val, unsigned long off)
+{
+ outl(val, pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_readb(unsigned long off)
+{
+ return readb(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readw(unsigned long off)
+{
+ return readw(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readl(unsigned long off)
+{
+ return readl(pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writeb(unsigned char val, unsigned long off)
+{
+ writeb(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writew(unsigned short val, unsigned long off)
+{
+ writew(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writel(unsigned int val, unsigned long off)
+{
+ writel(val, pcivga_membase + off);
+}
+
+#endif /* PCICONS_H */
diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c
new file mode 100644
index 000000000..86f527027
--- /dev/null
+++ b/drivers/sbus/char/pcikbd.c
@@ -0,0 +1,964 @@
+/* $Id: pcikbd.c,v 1.4 1997/09/05 22:59:53 ecd Exp $
+ * pcikbd.c: Ultra/AX PC keyboard support.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This code is mainly put together from various places in
+ * drivers/char, please refer to these sources for credits
+ * to the original authors.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/poll.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/miscdevice.h>
+#include <linux/kbd_ll.h>
+#include <linux/init.h>
+
+#include <asm/ebus.h>
+#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/keyboard.h>
+
+#include "pcikbd.h"
+#include "sunserial.h"
+
+static int kbd_node;
+static int beep_node;
+
+static unsigned long pcikbd_iobase = 0;
+static unsigned int pcikbd_irq;
+
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected = 0;
+static volatile unsigned char acknowledge = 0;
+static volatile unsigned char resend = 0;
+
+static inline void kb_wait(void)
+{
+ unsigned long start = jiffies;
+
+ do {
+ if(!(inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
+ return;
+ } while (jiffies - start < KBC_TIMEOUT);
+}
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ * The keycodes 1-88,96-111,119 are fairly standard, and
+ * should probably not be changed - changing might confuse X.
+ * X also interprets scancode 0x5d (KEY_Begin).
+ *
+ * For 1-88 keycode equals scancode.
+ */
+
+#define E0_KPENTER 96
+#define E0_RCTRL 97
+#define E0_KPSLASH 98
+#define E0_PRSCR 99
+#define E0_RALT 100
+#define E0_BREAK 101 /* (control-pause) */
+#define E0_HOME 102
+#define E0_UP 103
+#define E0_PGUP 104
+#define E0_LEFT 105
+#define E0_RIGHT 106
+#define E0_END 107
+#define E0_DOWN 108
+#define E0_PGDN 109
+#define E0_INS 110
+#define E0_DEL 111
+
+#define E1_PAUSE 119
+
+/*
+ * The keycodes below are randomly located in 89-95,112-118,120-127.
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85 /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86 124
+/* tfj@olivia.ping.dk:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */
+ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */
+ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */
+ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */
+};
+
+/* BTC */
+#define E0_MACRO 112
+/* LK450 */
+#define E0_F13 113
+#define E0_F14 114
+#define E0_HELP 115
+#define E0_DO 116
+#define E0_F17 117
+#define E0_KPMINPLUS 118
+/*
+ * My OmniKey generates e0 4c for the "OMNI" key and the
+ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
+ */
+#define E0_OK 124
+/*
+ * New microsoft keyboard is rumoured to have
+ * e0 5b (left window button), e0 5c (right window button),
+ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW 125
+#define E0_MSRW 126
+#define E0_MSTM 127
+
+static unsigned char e0_keys[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
+ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
+ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
+ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
+ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
+ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
+ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
+ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
+ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
+};
+
+static unsigned int prev_scancode = 0;
+
+int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ if(scancode < SC_LIM || scancode > 255 || keycode > 127)
+ return -EINVAL;
+ if(scancode < 128)
+ high_keys[scancode - SC_LIM] = keycode;
+ else
+ e0_keys[scancode - 128] = keycode;
+ return 0;
+}
+
+int pcikbd_getkeycode(unsigned int scancode)
+{
+ return
+ (scancode < SC_LIM || scancode > 255) ? -EINVAL :
+ (scancode < 128) ? high_keys[scancode - SC_LIM] :
+ e0_keys[scancode - 128];
+}
+
+int do_acknowledge(unsigned char scancode)
+{
+ if(reply_expected) {
+ if(scancode == KBD_REPLY_ACK) {
+ acknowledge = 1;
+ reply_expected = 0;
+ return 0;
+ } else if(scancode == KBD_REPLY_RESEND) {
+ resend = 1;
+ reply_expected = 0;
+ return 0;
+ }
+ }
+ if(scancode == 0) {
+ prev_scancode = 0;
+ return 0;
+ }
+ return 1;
+}
+
+int pcikbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+ if(scancode == 0xff) {
+ prev_scancode = 0;
+ return 0;
+ }
+ if(scancode == 0xe0 || scancode == 0xe1) {
+ prev_scancode = scancode;
+ return 0;
+ }
+ return 1;
+}
+
+int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode)
+{
+ if(prev_scancode) {
+ if(prev_scancode != 0xe0) {
+ if(prev_scancode == 0xe1 && scancode == 0x1d) {
+ prev_scancode = 0x100;
+ return 0;
+ } else if(prev_scancode == 0x100 && scancode == 0x45) {
+ *keycode = E1_PAUSE;
+ prev_scancode = 0;
+ } else {
+ prev_scancode = 0;
+ return 0;
+ }
+ } else {
+ prev_scancode = 0;
+ if(scancode == 0x2a || scancode == 0x36)
+ return 0;
+ if(e0_keys[scancode])
+ *keycode = e0_keys[scancode];
+ else
+ return 0;
+ }
+ } else if(scancode >= SC_LIM) {
+ *keycode = high_keys[scancode - SC_LIM];
+ if(!*keycode)
+ return 0;
+
+ } else
+ *keycode = scancode;
+ return 1;
+}
+
+char pcikbd_unexpected_up(unsigned char keycode)
+{
+ if(keycode >= SC_LIM || keycode == 85)
+ return 0;
+ else
+ return 0200;
+}
+
+static void
+pcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char status;
+
+ /*
+ * This IRQ might be shared with the 16550A serial chip,
+ * so we check dev_id to see if it was for us.
+ * (See also drivers/sbus/char/su.c).
+ */
+ if (dev_id)
+ return;
+
+ /* kbd_pt_regs = regs; */
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ do {
+ unsigned char scancode;
+
+ if(status & kbd_read_mask & KBD_STAT_MOUSE_OBF)
+ break;
+ scancode = inb(pcikbd_iobase + KBD_DATA_REG);
+ if((status & KBD_STAT_OBF) && do_acknowledge(scancode))
+ /* handle_scancode(scancode) */;
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ } while(status & KBD_STAT_OBF);
+ mark_bh(KEYBOARD_BH);
+}
+
+static int send_data(unsigned char data)
+{
+ int retries = 3;
+ unsigned long start;
+
+ do {
+ kb_wait();
+ acknowledge = resend = 0;
+ reply_expected = 1;
+ outb(data, pcikbd_iobase + KBD_DATA_REG);
+ start = jiffies;
+ do {
+ if(acknowledge)
+ return 1;
+ if(jiffies - start >= KBD_TIMEOUT)
+ return 0;
+ } while(!resend);
+ } while(retries-- > 0);
+ return 0;
+}
+
+void pcikbd_leds(unsigned char leds)
+{
+ if(!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))
+ send_data(KBD_CMD_ENABLE);
+
+}
+
+__initfunc(static int pcikbd_wait_for_input(void))
+{
+ int status, data;
+ unsigned long start = jiffies;
+
+ do {
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ if(!(status & KBD_STAT_OBF))
+ continue;
+ data = inb(pcikbd_iobase + KBD_DATA_REG);
+ if(status & (KBD_STAT_GTO | KBD_STAT_PERR))
+ continue;
+ return (data & 0xff);
+ } while(jiffies - start < KBD_INIT_TIMEOUT);
+ return -1;
+}
+
+__initfunc(static void pcikbd_write(int address, int data))
+{
+ int status;
+
+ do {
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ } while (status & KBD_STAT_IBF);
+ outb(data, pcikbd_iobase + address);
+}
+
+__initfunc(static char *do_pcikbd_hwinit(void))
+{
+ while(pcikbd_wait_for_input() != -1)
+ ;
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
+ if(pcikbd_wait_for_input() != 0xff)
+ return "Keyboard failed self test";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
+ if(pcikbd_wait_for_input() != 0x00)
+ return "Keyboard interface failed self test";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE);
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_RESET);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Keyboard reset failed, no ACK";
+ if(pcikbd_wait_for_input() != KBD_REPLY_POR)
+ return "Keyboard reset failed, no ACK";
+
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Disable keyboard: no ACK";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
+ pcikbd_write(KBD_DATA_REG,
+ (KBD_MODE_KBD_INT | KBD_MODE_SYS |
+ KBD_MODE_DISABLE_MOUSE | KBD_MODE_KCC));
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Enable keyboard: no ACK";
+
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Set rate: no ACK";
+ pcikbd_write(KBD_DATA_REG, 0x00);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Set rate: no ACK";
+
+ return NULL; /* success */
+}
+
+__initfunc(void pcikbd_hwinit(void))
+{
+ char *msg;
+
+ disable_irq(pcikbd_irq);
+ msg = do_pcikbd_hwinit();
+ enable_irq(pcikbd_irq);
+
+ if(msg)
+ printk("8042: keyboard init failure [%s]\n", msg);
+}
+
+__initfunc(int pcikbd_probe(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct linux_ebus_child *child;
+
+ for_all_ebusdev(edev, ebus) {
+ if(!strcmp(edev->prom_name, "8042")) {
+ for_each_edevchild(edev, child) {
+ if (!strcmp(child->prom_name, "kb_ps2"))
+ goto found;
+ }
+ }
+ }
+ printk("pcikbd_probe: no 8042 found\n");
+ return -ENODEV;
+
+found:
+ pcikbd_iobase = child->base_address[0];
+ if (check_region(pcikbd_iobase, sizeof(unsigned long))) {
+ printk("8042: can't get region %lx, %d\n",
+ pcikbd_iobase, (int)sizeof(unsigned long));
+ return -ENODEV;
+ }
+ request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
+
+ pcikbd_irq = child->irqs[0];
+ if (request_irq(pcikbd_irq, &pcikbd_interrupt,
+ SA_SHIRQ, "keyboard", NULL)) {
+ printk("8042: cannot register IRQ %x\n", pcikbd_irq);
+ return -ENODEV;
+ }
+
+ printk("8042(kbd): iobase[%016lx] irq[%x]\n", pcikbd_iobase, pcikbd_irq);
+
+ /* pcikbd_init(); */
+ kbd_read_mask = KBD_STAT_OBF;
+ return 0;
+}
+
+
+
+/*
+ * Here begins the Mouse Driver.
+ */
+
+static int ms_node;
+
+static unsigned long pcimouse_iobase = 0;
+static unsigned int pcimouse_irq;
+
+#define PSMOUSE_MINOR 1 /* Minor device # for this mouse */
+
+#define AUX_BUF_SIZE 2048
+
+struct aux_queue {
+ unsigned long head;
+ unsigned long tail;
+ struct wait_queue *proc_list;
+ struct fasync_struct *fasync;
+ unsigned char buf[AUX_BUF_SIZE];
+};
+
+static struct aux_queue *queue;
+static int aux_ready = 0;
+static int aux_count = 0;
+static int aux_present = 0;
+
+/*
+ * Shared subroutines
+ */
+
+static unsigned int get_from_queue(void)
+{
+ unsigned int result;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ result = queue->buf[queue->tail];
+ queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
+ restore_flags(flags);
+ return result;
+}
+
+
+static inline int queue_empty(void)
+{
+ return queue->head == queue->tail;
+}
+
+static int fasync_aux(struct inode *inode, struct file *filp, int on)
+{
+ int retval;
+
+ retval = fasync_helper(inode, filp, on, &queue->fasync);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+/*
+ * PS/2 Aux Device
+ */
+
+#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | \
+ KBD_MODE_SYS | KBD_MODE_KBD_INT)
+
+#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | \
+ KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
+
+#define MAX_RETRIES 60 /* some aux operations take long time*/
+
+/*
+ * Status polling
+ */
+
+static int poll_aux_status(void)
+{
+ int retries=0;
+
+ while ((inb(pcimouse_iobase + KBD_STATUS_REG) &
+ (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) {
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF)
+ == AUX_STAT_OBF)
+ inb(pcimouse_iobase + KBD_DATA_REG);
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
+ schedule();
+ retries++;
+ }
+ return (retries < MAX_RETRIES);
+}
+
+/*
+ * Write to aux device
+ */
+
+static void aux_write_dev(int val)
+{
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */
+ poll_aux_status();
+ outb_p(val, pcimouse_iobase + KBD_DATA_REG); /* Write data */
+}
+
+/*
+ * Write to device & handle returned ack
+ */
+
+__initfunc(static int aux_write_ack(int val))
+{
+ aux_write_dev(val);
+ poll_aux_status();
+
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF)
+ return (inb(pcimouse_iobase + KBD_DATA_REG));
+ return 0;
+}
+
+/*
+ * Write aux device command
+ */
+
+static void aux_write_cmd(int val)
+{
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(val, pcimouse_iobase + KBD_DATA_REG);
+}
+
+/*
+ * AUX handler critical section start and end.
+ *
+ * Only one process can be in the critical section and all keyboard sends are
+ * deferred as long as we're inside. This is necessary as we may sleep when
+ * waiting for the keyboard controller and other processes / BH's can
+ * preempt us. Please note that the input buffer must be flushed when
+ * aux_end_atomic() is called and the interrupt is no longer enabled as not
+ * doing so might cause the keyboard driver to ignore all incoming keystrokes.
+ */
+
+static struct semaphore aux_sema4 = MUTEX;
+
+static inline void aux_start_atomic(void)
+{
+ down(&aux_sema4);
+ disable_bh(KEYBOARD_BH);
+}
+
+static inline void aux_end_atomic(void)
+{
+ enable_bh(KEYBOARD_BH);
+ up(&aux_sema4);
+}
+
+/*
+ * Interrupt from the auxiliary device: a character
+ * is waiting in the keyboard/aux controller.
+ */
+
+void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int head = queue->head;
+ int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
+
+ /*
+ * This IRQ might be shared with the 16550A serial chip,
+ * so we check dev_id to see if it was for us.
+ * (See also drivers/sbus/char/su.c).
+ */
+ if (dev_id)
+ return;
+
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF)
+ return;
+
+ add_mouse_randomness(queue->buf[head] = inb(pcimouse_iobase + KBD_DATA_REG));
+ if (head != maxhead) {
+ head++;
+ head &= AUX_BUF_SIZE-1;
+ }
+ queue->head = head;
+ aux_ready = 1;
+ if (queue->fasync)
+ kill_fasync(queue->fasync, SIGIO);
+ wake_up_interruptible(&queue->proc_list);
+}
+
+static int release_aux(struct inode * inode, struct file * file)
+{
+ fasync_aux(inode, file, 0);
+ if (--aux_count)
+ return 0;
+ aux_start_atomic();
+
+ /* Disable controller ints */
+ aux_write_cmd(AUX_INTS_OFF);
+ poll_aux_status();
+
+ /* Disable Aux device */
+ outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ aux_end_atomic();
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable auxiliary device.
+ */
+
+static int open_aux(struct inode * inode, struct file * file)
+{
+ if (!aux_present)
+ return -ENODEV;
+ aux_start_atomic();
+ if (aux_count++) {
+ aux_end_atomic();
+ return 0;
+ }
+ if (!poll_aux_status()) { /* FIXME: Race condition */
+ aux_count--;
+ aux_end_atomic();
+ return -EBUSY;
+ }
+ queue->head = queue->tail = 0; /* Flush input queue */
+
+ MOD_INC_USE_COUNT;
+
+ poll_aux_status();
+ outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG); /* Enable Aux */
+ aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */
+ aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */
+ poll_aux_status();
+ aux_end_atomic();
+
+ aux_ready = 0;
+ return 0;
+}
+
+/*
+ * Write to the aux device.
+ */
+
+static long write_aux(struct inode * inode, struct file * file,
+ const char * buffer, unsigned long count)
+{
+ int retval = 0;
+
+ if (count) {
+ int written = 0;
+
+ aux_start_atomic();
+ do {
+ char c;
+ if (!poll_aux_status())
+ break;
+ outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);
+ if (!poll_aux_status())
+ break;
+ get_user(c, buffer++);
+ outb(c, pcimouse_iobase + KBD_DATA_REG);
+ written++;
+ } while (--count);
+ aux_end_atomic();
+ retval = -EIO;
+ if (written) {
+ retval = written;
+ inode->i_mtime = CURRENT_TIME;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Generic part continues...
+ */
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static long read_aux(struct inode * inode, struct file * file,
+ char * buffer, unsigned long count)
+{
+ struct wait_queue wait = { current, NULL };
+ int i = count;
+ unsigned char c;
+
+ if (queue_empty()) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ add_wait_queue(&queue->proc_list, &wait);
+repeat:
+ current->state = TASK_INTERRUPTIBLE;
+ if (queue_empty() && !(current->signal & ~current->blocked)) {
+ schedule();
+ goto repeat;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&queue->proc_list, &wait);
+ }
+ while (i > 0 && !queue_empty()) {
+ c = get_from_queue();
+ put_user(c, buffer++);
+ i--;
+ }
+ aux_ready = !queue_empty();
+ if (count-i) {
+ inode->i_atime = CURRENT_TIME;
+ return count-i;
+ }
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ return 0;
+}
+
+static unsigned int aux_poll(struct file *file, poll_table * wait)
+{
+ poll_wait(&queue->proc_list, wait);
+ if (aux_ready)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+struct file_operations psaux_fops = {
+ NULL, /* seek */
+ read_aux,
+ write_aux,
+ NULL, /* readdir */
+ aux_poll,
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_aux,
+ release_aux,
+ NULL,
+ fasync_aux,
+};
+
+static struct miscdevice psaux_mouse = {
+ PSMOUSE_MINOR, "ps2aux", &psaux_fops
+};
+
+__initfunc(int pcimouse_init(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct linux_ebus_child *child;
+
+ for_all_ebusdev(edev, ebus) {
+ if(!strcmp(edev->prom_name, "8042")) {
+ for_each_edevchild(edev, child) {
+ if (!strcmp(child->prom_name, "kdmouse"))
+ goto found;
+ }
+ }
+ }
+ printk("pcimouse_init: no 8042 found\n");
+ return -ENODEV;
+
+found:
+ pcimouse_iobase = child->base_address[0];
+ /*
+ * Just in case the iobases for kbd/mouse ever differ...
+ */
+ if (!check_region(pcimouse_iobase, sizeof(unsigned long)))
+ request_region(pcimouse_iobase, sizeof(unsigned long),
+ "8042 controller");
+
+ pcimouse_irq = child->irqs[0];
+ if (request_irq(pcimouse_irq, &pcimouse_interrupt,
+ SA_SHIRQ, "mouse", NULL)) {
+ printk("8042: Cannot register IRQ %x\n", pcimouse_irq);
+ return -ENODEV;
+ }
+
+ printk("8042(mouse): iobase[%016lx] irq[%x]\n",
+ pcimouse_iobase, pcimouse_irq);
+
+ printk("8042: PS/2 auxiliary pointing device detected.\n");
+ aux_present = 1;
+ kbd_read_mask = AUX_STAT_OBF;
+
+ misc_register(&psaux_mouse);
+ queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+ memset(queue, 0, sizeof(*queue));
+ queue->head = queue->tail = 0;
+ queue->proc_list = NULL;
+ aux_start_atomic();
+ outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
+ aux_write_ack(AUX_SET_SAMPLE);
+ aux_write_ack(100);
+ aux_write_ack(AUX_SET_RES);
+ aux_write_ack(3);
+ aux_write_ack(AUX_SET_SCALE21);
+ poll_aux_status();
+ outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG);
+ poll_aux_status();
+ aux_end_atomic();
+
+ return 0;
+}
+
+
+__initfunc(static int ps2_init(void))
+{
+ int err;
+
+ err = pcikbd_probe();
+ if (err)
+ return err;
+
+ err = pcimouse_init();
+ if (err)
+ return err;
+
+ return 0;
+}
+
+__initfunc(int ps2kbd_probe(unsigned long *memory_start))
+{
+ int pnode, enode, node, dnode;
+ int kbnode = 0, msnode = 0, bnode = 0;
+ int devices = 0;
+ char prop[128];
+ int len;
+
+ /*
+ * Get the nodes for keyboard and mouse from 'aliases'...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "aliases");
+ if (!node)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+ if (len > 0)
+ kbnode = prom_pathtoinode(prop);
+ if (!kbnode)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+ if (len > 0)
+ msnode = prom_pathtoinode(prop);
+ if (!msnode)
+ return -ENODEV;
+
+ /*
+ * Find matching EBus nodes...
+ */
+ node = prom_getchild(prom_root_node);
+ pnode = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (pnode) {
+ enode = prom_getchild(pnode);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ node = prom_getchild(enode);
+ bnode = prom_searchsiblings(node, "beeper");
+
+ node = prom_getchild(enode);
+ node = prom_searchsiblings(node, "8042");
+
+ /*
+ * For each '8042' on this EBus...
+ */
+ while (node) {
+ /*
+ * Does it match?
+ */
+ dnode = prom_getchild(node);
+ dnode = prom_searchsiblings(dnode, "kb_ps2");
+ if (dnode == kbnode) {
+ kbd_node = kbnode;
+ beep_node = bnode;
+ ++devices;
+ }
+
+ dnode = prom_getchild(node);
+ dnode = prom_searchsiblings(dnode, "kdmouse");
+ if (dnode == msnode) {
+ ms_node = msnode;
+ ++devices;
+ }
+
+ /*
+ * Found everything we need?
+ */
+ if (devices == 2)
+ goto found;
+
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "8042");
+ }
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ pnode = prom_getsibling(pnode);
+ pnode = prom_searchsiblings(pnode, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, ps2_init);
+ return 0;
+}
diff --git a/drivers/sbus/char/pcikbd.h b/drivers/sbus/char/pcikbd.h
new file mode 100644
index 000000000..234a5e980
--- /dev/null
+++ b/drivers/sbus/char/pcikbd.h
@@ -0,0 +1,123 @@
+/* $Id: pcikbd.h,v 1.1 1997/08/24 02:53:25 davem Exp $
+ * pcikbd.h: PCI/PC 8042 keyboard/mouse driver stuff. Mostly snarfed
+ * from the existing driver by Martin Mares.
+ *
+ * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Ultra/AX specific hacks are:
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __PCIKBD_H
+#define __PCIKBD_H
+
+/*
+ * Configuration Switches
+ */
+
+#define KBD_REPORT_ERR /* Report keyboard errors */
+#define KBD_REPORT_UNKN /* Report unknown scan codes */
+#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */
+
+#define KBD_INIT_TIMEOUT HZ /* Timeout for initializing the keyboard */
+#define KBC_TIMEOUT (HZ/4) /* Timeout for sending to keyboard controller */
+#define KBD_TIMEOUT (HZ/4) /* Timeout for keyboard command acknowledge */
+
+/*
+ * Internal variables of the driver
+ */
+
+extern unsigned char kbd_read_mask;
+extern unsigned char aux_device_present;
+
+extern unsigned long pcikbd_iobase;
+extern unsigned int pcikbd_irq;
+
+/*
+ * Keyboard Controller Registers
+ *
+ * NOTE: These are offsets from pcikbd_iobase, not absolute.
+ */
+
+#define KBD_STATUS_REG 0x04
+#define KBD_CNTL_REG KBD_STATUS_REG
+#define KBD_DATA_REG 0x00
+
+/*
+ * Keyboard Controller Commands
+ */
+
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+
+/*
+ * Keyboard Commands
+ */
+
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/*
+ * Keyboard Replies
+ */
+
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/*
+ * Status Register Bits
+ */
+
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF)
+
+/*
+ * Controller Mode Register Bits
+ */
+
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generage IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+/*
+ * Mouse Commands
+ */
+
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_RESET 0xFF /* Reset aux device */
+
+#endif /* __PCIKBD_H */
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
new file mode 100644
index 000000000..775fbbe5a
--- /dev/null
+++ b/drivers/sbus/char/sab82532.c
@@ -0,0 +1,2173 @@
+/* $Id: sab82532.c,v 1.4 1997/09/03 17:04:21 ecd Exp $
+ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ */
+
+#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/serial_reg.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/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/sab82532.h>
+#include <asm/uaccess.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int sab82532_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
+
+static void change_speed(struct sab82532 *info);
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * This assumes you have a 29.4912 MHz clock for your UART.
+ */
+#define BASE_BAUD ( 29491200 / 16 )
+
+static struct sab82532 *sab82532_chain = 0;
+static struct tty_struct *sab82532_table[NR_PORTS];
+static struct termios *sab82532_termios[NR_PORTS];
+static struct termios *sab82532_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by sab82532_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 = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sab82532 *info,
+ kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null sab82532 for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds.
+ *
+ * The formula is: Baud = BASE_BAUD / ((N + 1) * (1 << M)),
+ *
+ * with 0 <= N < 64 and 0 <= M < 16
+ *
+ * XXX: Speeds with M = 0 might not work properly for XTAL frequencies
+ * above 10 MHz.
+ */
+struct ebrg_struct {
+ int baud;
+ int n;
+ int m;
+};
+
+static struct ebrg_struct ebrg_table[] = {
+ { 0, 0, 0 },
+ { 50, 35, 10 },
+ { 75, 47, 9 },
+ { 110, 32, 9 },
+ { 134, 53, 8 },
+ { 150, 47, 8 },
+ { 200, 35, 8 },
+ { 300, 47, 7 },
+ { 600, 47, 6 },
+ { 1200, 47, 5 },
+ { 1800, 31, 5 },
+ { 2400, 47, 4 },
+ { 4800, 47, 3 },
+ { 9600, 47, 2 },
+ { 19200, 47, 1 },
+ { 38400, 23, 1 },
+ { 57600, 15, 1 },
+ { 115200, 7, 1 },
+ { 230400, 3, 1 },
+ { 460800, 1, 1 },
+ { 76800, 11, 1 },
+ { 153600, 5, 1 },
+ { 307200, 3, 1 },
+ { 614400, 3, 0 },
+ { 921600, 0, 1 },
+};
+
+#define NR_EBRG_VALUES (sizeof(ebrg_table)/sizeof(struct ebrg_struct))
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_stop() and sab82532_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void sab82532_stop(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static void sab82532_start(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_start"))
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static void batten_down_hatches(void)
+{
+ /* If we are doing kadb, we call the debugger
+ * else we just drop into the boot monitor.
+ * Note that we must flush the user windows
+ * first before giving up control.
+ */
+ printk("\n");
+ flush_user_windows();
+#ifndef __sparc_v9__
+ if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) &&
+ (((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR))
+ sp_enter_debugger();
+ else
+#endif
+ prom_cmdline();
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * sab82532_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: sab82532_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * sab82532_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 sab82532_sched_event(struct sab82532 *info, int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+static inline void receive_chars(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char buf[32];
+ unsigned char status;
+ int free_fifo = 0;
+ int i, count = 0;
+
+ /* Read number of BYTES (Character + Status) available. */
+ if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
+ count = info->recv_fifo_size;
+ free_fifo++;
+ }
+ if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
+ count = info->regs->r.rbcl & (info->recv_fifo_size - 1);
+ free_fifo++;
+ }
+ if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+#if 1
+ printk("sab82532: receive_chars: RFO");
+#endif
+ free_fifo++;
+ }
+
+ /* Issue a FIFO read command in case we where idle. */
+ if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RFRD;
+ }
+
+ /* Read the FIFO. */
+ for (i = 0; i < (count << 1); i++)
+ buf[i] = info->regs->r.rfifo[i];
+
+ /* Issue Receive Message Complete command. */
+ if (free_fifo) {
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RMC;
+ }
+
+ for (i = 0; i < count; ) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+#if 1
+ printk("sab82532: receive_chars: tty overrun\n");
+#endif
+ info->icount.buf_overrun++;
+ break;
+ }
+
+ tty->flip.count++;
+ *tty->flip.char_buf_ptr++ = buf[i++];
+ status = buf[i++];
+ info->icount.rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
+#endif
+
+ if (status & SAB82532_RSTAT_PE) {
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ info->icount.parity++;
+ } else if (status & SAB82532_RSTAT_FE) {
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+ info->icount.frame++;
+ }
+#ifdef CMSPAR
+ else if (status & SAB82532_RSTAT_PARITY)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+#endif
+ else
+ *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+ }
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static inline void transmit_chars(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ int i;
+
+ if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+ info->tty->hw_stopped) {
+ if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
+ info->all_sent = 1;
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ return;
+ }
+
+ /* Stuff 32 bytes into Transmit FIFO. */
+ info->all_sent = 0;
+ for (i = 0; i < info->xmit_fifo_size; i++) {
+ info->regs->w.xfifo[i] = info->xmit_buf[info->xmit_tail++];
+ info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+ info->icount.tx++;
+ if (--info->xmit_cnt <= 0)
+ break;
+ }
+
+ /* Issue a Transmit Frame command. */
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_XF;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (info->xmit_cnt <= 0) {
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+}
+
+static inline void check_status(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ struct tty_struct *tty = info->tty;
+ int modem_change = 0;
+
+ if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+ if (info->is_console) {
+ batten_down_hatches();
+ return;
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ info->icount.buf_overrun++;
+ goto check_modem;
+ }
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ *tty->flip.char_buf_ptr++ = 0;
+ info->icount.brk++;
+ }
+
+ if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ info->icount.buf_overrun++;
+ goto check_modem;
+ }
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ *tty->flip.char_buf_ptr++ = 0;
+ info->icount.overrun++;
+ }
+
+ if (info->is_console)
+ return;
+
+check_modem:
+ if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
+ info->dcd = (info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : 1;
+ info->icount.dcd++;
+ modem_change++;
+#if 0
+ printk("DCD change: %d\n", info->icount.dcd);
+#endif
+ }
+ if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
+ info->cts = info->regs->r.star & SAB82532_STAR_CTS;
+ info->icount.cts++;
+ modem_change++;
+#if 0
+ printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
+#endif
+ }
+ if ((info->regs->r.pvr & info->pvr_dsr_bit) ^ info->dsr) {
+ info->dsr = info->regs->r.pvr & info->pvr_dsr_bit;
+ info->icount.dsr++;
+ modem_change++;
+#if 0
+ printk("DSR change: %d\n", info->icount.dsr);
+#endif
+ }
+ if (modem_change)
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ if ((info->flags & ASYNC_CHECK_CD) &&
+ (stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
+
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (info->dcd) ? "on" : "off");
+#endif
+
+ if (info->dcd)
+ wake_up_interruptible(&info->open_wait);
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("scheduling hangup...");
+#endif
+
+ queue_task(&info->tqueue_hangup, &tq_scheduler);
+ }
+ }
+
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (info->cts) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ sab82532_sched_event(info,
+ RS_EVENT_WRITE_WAKEUP);
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+ } else {
+ if (!(info->cts)) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ }
+ }
+ }
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct sab82532 *info = dev_id;
+ union sab82532_irq_status status;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("sab82532_interrupt(%d)...", irq);
+#endif
+
+ status.stat = 0;
+ if (info->regs->r.gis & SAB82532_GIS_ISA0)
+ status.sreg.isr0 = info->regs->r.isr0;
+ if (info->regs->r.gis & SAB82532_GIS_ISA1)
+ status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("%d<%02x.%02x>", info->line,
+ status.sreg.isr0, status.sreg.isr1);
+#endif
+
+ if (!status.stat)
+ goto next;
+
+ if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ receive_chars(info, &status);
+ if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+ (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+ check_status(info, &status);
+ if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+ transmit_chars(info, &status);
+
+next:
+ info = info->next;
+ status.stat = 0;
+ if (info->regs->r.gis & SAB82532_GIS_ISB0)
+ status.sreg.isr0 = info->regs->r.isr0;
+ if (info->regs->r.gis & SAB82532_GIS_ISB1)
+ status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("%d<%02x.%02x>", info->line,
+ status.sreg.isr0, status.sreg.isr1);
+#endif
+
+ if (!status.stat)
+ goto done;
+
+ if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ receive_chars(info, &status);
+ if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+ (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+ check_status(info, &status);
+ if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+ transmit_chars(info, &status);
+
+done:
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * sab82532_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 sab82532_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 sab82532 *info = (struct sab82532 *)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);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct sab82532 *info = (struct sab82532 *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+
+static int startup(struct sab82532 *info)
+{
+ unsigned long flags;
+ unsigned long page;
+ unsigned char stat;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ free_page(page);
+ goto errout;
+ }
+
+ if (!info->regs) {
+ 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
+ printk("starting up serial port %d...", info->line);
+#endif
+
+ /*
+ * Clear the FIFO buffers.
+ */
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RRES;
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_XRES;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ stat = info->regs->r.isr0;
+ stat = info->regs->r.isr1;
+
+ /*
+ * Now, initialize the UART
+ */
+ info->regs->w.ccr0 = 0; /* power-down */
+ info->regs->w.ccr0 = SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
+ SAB82532_CCR0_SM_ASYNC;
+ info->regs->w.ccr1 = SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7;
+ info->regs->w.ccr2 = SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
+ SAB82532_CCR2_TOE;
+ info->regs->w.ccr3 = 0;
+ info->regs->w.ccr4 = SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG;
+ info->regs->w.mode = SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
+ SAB82532_MODE_RAC;
+ info->regs->w.rfc = SAB82532_RFC_DPS | SAB82532_RFC_RFDF;
+ switch (info->recv_fifo_size) {
+ case 1:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_1;
+ break;
+ case 4:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_4;
+ break;
+ case 16:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_16;
+ break;
+ default:
+ info->recv_fifo_size = 32;
+ /* fall through */
+ case 32:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_32;
+ break;
+ }
+ info->regs->rw.ccr0 |= SAB82532_CCR0_PU; /* power-up */
+
+ /*
+ * Finally, enable interrupts
+ */
+ info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+ SAB82532_IMR0_PLLA;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
+ SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
+ SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * 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 -ENODEV;
+}
+
+/*
+ * 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 sab82532 *info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d...", info->line);
+#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);
+
+ if (info->xmit_buf) {
+ free_page((unsigned long)info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ /* Disable Interrupts */
+ info->interrupt_mask0 = 0xff;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->interrupt_mask1 = 0xff;
+ info->regs->w.imr1 = info->interrupt_mask1;
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+
+ /* Disable break condition */
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+
+ /* Disable Receiver */
+ info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+
+ /* Power Down */
+ info->regs->rw.ccr0 &= ~(SAB82532_CCR0_PU);
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sab82532 *info)
+{
+ unsigned long flags;
+ unsigned int ebrg;
+ tcflag_t cflag;
+ unsigned char dafo;
+ int i;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5: dafo = SAB82532_DAFO_CHL5; break;
+ case CS6: dafo = SAB82532_DAFO_CHL6; break;
+ case CS7: dafo = SAB82532_DAFO_CHL7; break;
+ case CS8: dafo = SAB82532_DAFO_CHL8; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: dafo = SAB82532_DAFO_CHL5; break;
+ }
+
+ if (cflag & CSTOPB)
+ dafo |= SAB82532_DAFO_STOP;
+
+ if (cflag & PARENB)
+ dafo |= SAB82532_DAFO_PARE;
+
+ if (cflag & PARODD) {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_MARK;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_ODD;
+ } else {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_SPACE;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_EVEN;
+ }
+
+ /* Determine EBRG values based on baud rate */
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~(CBAUDEX);
+ if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ ebrg = ebrg_table[i].n;
+ ebrg |= (ebrg_table[i].m << 6);
+
+ /* CTS flow control flags */
+ if (cflag & CRTSCTS)
+ info->flags |= ASYNC_CTS_FLOW;
+ else
+ info->flags &= ~(ASYNC_CTS_FLOW);
+
+ if (cflag & CLOCAL)
+ info->flags &= ~(ASYNC_CHECK_CD);
+ else
+ info->flags |= ASYNC_CHECK_CD;
+ if (info->tty)
+ info->tty->hw_stopped = 0;
+
+ /*
+ * Set up parity check flag
+ * XXX: not implemented, yet.
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ /*
+ * Characters to ignore
+ * XXX: not implemented, yet.
+ */
+
+ /*
+ * !!! ignore all characters if CREAD is not set
+ * XXX: not implemented, yet.
+ */
+ if ((cflag & CREAD) == 0)
+ info->ignore_status_mask |= SAB82532_ISR0_RPF |
+ SAB82532_ISR0_TCD |
+ SAB82532_ISR0_TIME;
+
+ save_flags(flags); cli();
+ info->regs->w.dafo = dafo;
+ info->regs->w.bgr = ebrg & 0xff;
+ info->regs->rw.ccr2 &= ~(0xc0);
+ info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
+ if (info->flags & ASYNC_CTS_FLOW) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
+ } else {
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_FCTS;
+ }
+ info->regs->rw.mode |= SAB82532_MODE_RAC;
+ restore_flags(flags);
+}
+
+static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_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 sab82532_flush_chars(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static int sab82532_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf || !tmp_buf)
+ return 0;
+
+ if (from_user)
+ down(&tmp_buf_sem);
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ 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);
+ } else
+ 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 (from_user)
+ up(&tmp_buf_sem);
+
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ (info->interrupt_mask1 & SAB82532_IMR1_XPR)) {
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+
+ restore_flags(flags);
+ return ret;
+}
+
+static int sab82532_write_room(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int sab82532_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void sab82532_flush_buffer(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ 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 sab82532_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
+ return;
+
+ if (info->regs->r.star & SAB82532_STAR_TEC)
+ udelay(1);
+ info->regs->w.tic = ch;
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void sab82532_throttle(struct tty_struct * tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ sab82532_send_xchar(tty, STOP_CHAR(tty));
+#if 0
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+#endif
+}
+
+static void sab82532_unthrottle(struct tty_struct * tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ sab82532_send_xchar(tty, START_CHAR(tty));
+ }
+
+#if 0
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sab82532 *info,
+ struct serial_struct *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = (unsigned long)info->regs;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.xmit_fifo_size = info->xmit_fifo_size;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ tmp.hub6 = 0;
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_serial_info(struct sab82532 *info,
+ struct serial_struct *new_info)
+{
+ return 0;
+}
+
+
+/*
+ * 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 sab82532 * info, unsigned int *value)
+{
+ unsigned int result;
+
+ result = info->all_sent ? TIOCSER_TEMT : 0;
+ return put_user(result, value);
+}
+
+
+static int get_modem_info(struct sab82532 * info, unsigned int *value)
+{
+ unsigned int result;
+
+ result = ((info->regs->r.mode & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
+ | ((info->regs->r.pvr & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
+ | ((info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
+ | ((info->regs->r.pvr & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
+ | ((info->regs->r.star & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
+ return put_user(result,value);
+}
+
+static int set_modem_info(struct sab82532 * info, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+
+ error = get_user(arg, value);
+ if (error)
+ return error;
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+ break;
+ case TIOCMSET:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ } else {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ } else {
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct sab82532 * info, int duration)
+{
+ if (!info->regs)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("sab82532_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
+ cli();
+ info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+ schedule();
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+ sti();
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("done jiffies=%lu\n", jiffies);
+#endif
+}
+
+/*
+ * This routine sets the break condition on the serial port.
+ */
+static void begin_break(struct sab82532 * info)
+{
+ if (!info->regs)
+ return;
+ info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+}
+
+/*
+ * This routine clears the break condition on the serial port.
+ */
+static void end_break(struct sab82532 * info)
+{
+ if (!info->regs)
+ return;
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+}
+
+static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct sab82532 * info = (struct sab82532 *)tty->driver_data;
+ int retval;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ if (!arg) {
+ send_break(info, HZ/4); /* 1/4 second */
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ }
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ return 0;
+ case TIOCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ begin_break(info);
+ return 0;
+ case TIOCCBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ end_break(info);
+ return 0;
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ error = get_user(arg, (unsigned int *) arg);
+ if (error)
+ return error;
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct sab82532 *) arg,
+ info, sizeof(struct sab82532)))
+ 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:
+ cli();
+ /* note the counters on entry */
+ cprev = info->icount;
+ sti();
+ while (1) {
+ interruptible_sleep_on(&info->delta_msr_wait);
+ /* see if a signal did it */
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ cli();
+ cnow = info->icount; /* atomic copy */
+ sti();
+ 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:
+ cli();
+ cnow = info->icount;
+ sti();
+ 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;
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void sab82532_set_termios(struct tty_struct *tty,
+ struct termios *old_termios)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if ( (tty->termios->c_cflag == old_termios->c_cflag)
+ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag)))
+ return;
+
+ change_speed(info);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(tty->termios->c_cflag & CBAUD)) {
+ info->regs->w.mode |= SAB82532_MODE_FRTS;
+ info->regs->w.mode |= SAB82532_MODE_RTS;
+ info->regs->w.pvr |= info->pvr_dtr_bit;
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->regs->w.pvr &= ~(info->pvr_dtr_bit);
+ if (!tty->hw_stopped ||
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ info->regs->w.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->w.mode |= SAB82532_MODE_RTS;
+ }
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ sab82532_start(tty);
+ }
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_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 sab82532_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("sab82532_close: bad serial port count; tty->count is 1,"
+ " info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("sab82532_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and turn off
+ * the receiver.
+ */
+ info->interrupt_mask0 |= SAB82532_IMR0_TCD;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+ if (info->flags & ASYNC_INITIALIZED) {
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ sab82532_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;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+}
+
+/*
+ * sab82532_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+
+ if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
+ return;
+
+ 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
+ printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+
+ /* XXX: Implement this... */
+
+ current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void sab82532_hangup(struct tty_struct *tty)
+{
+ struct sab82532 * info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
+ return;
+
+ sab82532_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sab82532 *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+
+ /*
+ * 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
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * sab82532_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttyS%d, count = %d\n",
+ info->line, info->count);
+#endif
+ cli();
+ if (!tty_hung_up_p(filp))
+ info->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ }
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
+ (do_clocal || !(info->regs->r.vstr & SAB82532_VSTR_CD)))
+ break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
+ info->line, info->count, info->flags, do_clocal, info->regs->r.vstr);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ 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 sab82532_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sab82532 *info = sab82532_chain;
+ int retval, line;
+ unsigned long page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open: count = %d\n", info->count);
+#endif
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS))
+ return -ENODEV;
+
+ while (info) {
+ if (info->line == line)
+ break;
+ info = info->next;
+ }
+ if (!info) {
+ printk("sab82532_open: can't find info for line %d\n",
+ line);
+ return -ENODEV;
+ }
+
+ info->count++;
+ if (serial_paranoia_check(info, tty->device, "sab82532_open"))
+ return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+ tty->driver_data = info;
+ info->tty = tty;
+
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ MOD_INC_USE_COUNT;
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:SAB82532 ", info->line);
+ switch (info->type) {
+ case 0:
+ ret += sprintf(buf+ret, "V1.0 ");
+ break;
+ case 1:
+ ret += sprintf(buf+ret, "V2.0 ");
+ break;
+ case 2:
+ ret += sprintf(buf+ret, "V3.2 ");
+ break;
+ default:
+ ret += sprintf(buf+ret, "V?.? ");
+ break;
+ }
+ ret += sprintf(buf+ret, "port:%lX irq:%d",
+ (unsigned long)info->regs, info->irq);
+
+ if (!info->regs) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+}
+
+int sab82532_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.4 $");
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ len += line_info(page + len, sab82532_table[i]);
+ 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 + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * sab82532_init() and friends
+ *
+ * sab82532_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+__initfunc(static int get_sab82532(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct sab82532 *sab;
+ unsigned long regs, offset;
+ int i;
+
+ for_all_ebusdev(edev, ebus)
+ if (!strcmp(edev->prom_name, "se"))
+ break;
+ if (!edev)
+ return -ENODEV;
+
+ printk("%s: SAB82532 at 0x%lx IRQ %x\n", __FUNCTION__,
+ edev->base_address[0], edev->irqs[0]);
+
+ regs = edev->base_address[0];
+ offset = sizeof(union sab82532_async_regs);
+
+ for (i = 0; i < 2; i++) {
+ sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
+ GFP_KERNEL);
+ if (!sab) {
+ printk("sab82532: can't alloc sab struct\n");
+ break;
+ }
+ memset(sab, 0, sizeof(struct sab82532));
+
+ sab->regs = (union sab82532_async_regs *)(regs + offset);
+ sab->irq = edev->irqs[0];
+
+ if (check_region((unsigned long)sab->regs,
+ sizeof(union sab82532_async_regs))) {
+ kfree(sab);
+ continue;
+ }
+ request_region((unsigned long)sab->regs,
+ sizeof(union sab82532_async_regs),
+ "serial(sab82532)");
+
+ sab->regs->w.ipc = SAB82532_IPC_IC_ACT_LOW;
+
+ sab->next = sab82532_chain;
+ sab82532_chain = sab;
+
+ offset -= sizeof(union sab82532_async_regs);
+ }
+ return 0;
+}
+
+/* Hooks for running a serial console. con_init() calls this if the
+ * console is run over one of the ttya/ttyb serial ports.
+ * 'chip' should be zero, as for now we only have one chip on board.
+ * 'line' is decoded as 0=ttya, 1=ttyb.
+ */
+void
+sab82532_cons_hook(int chip, int out, int line)
+{
+ prom_printf("sab82532: serial console is not implemented, yet\n");
+ prom_halt();
+}
+
+void
+sab82532_kgdb_hook(int line)
+{
+ prom_printf("sab82532: kgdb support is not implemented, yet\n");
+ prom_halt();
+}
+
+__initfunc(static inline void show_serial_version(void))
+{
+ char *revision = "$Revision: 1.4 $";
+ char *version, *p;
+
+ version = strchr(revision, ' ');
+ p = strchr(++version, ' ');
+ *p = '\0';
+ printk("SAB82532 serial driver version %s\n", version);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int sab82532_init(void))
+{
+ struct sab82532 *info;
+ int i;
+
+ if (!sab82532_chain)
+ get_sab82532();
+ if (!sab82532_chain)
+ return -ENODEV;
+
+ init_bh(SERIAL_BH, do_serial_bh);
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NR_PORTS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &sab82532_refcount;
+ serial_driver.table = sab82532_table;
+ serial_driver.termios = sab82532_termios;
+ serial_driver.termios_locked = sab82532_termios_locked;
+
+ serial_driver.open = sab82532_open;
+ serial_driver.close = sab82532_close;
+ serial_driver.write = sab82532_write;
+ serial_driver.put_char = sab82532_put_char;
+ serial_driver.flush_chars = sab82532_flush_chars;
+ serial_driver.write_room = sab82532_write_room;
+ serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
+ serial_driver.flush_buffer = sab82532_flush_buffer;
+ serial_driver.ioctl = sab82532_ioctl;
+ serial_driver.throttle = sab82532_throttle;
+ serial_driver.unthrottle = sab82532_unthrottle;
+ serial_driver.send_xchar = sab82532_send_xchar;
+ serial_driver.set_termios = sab82532_set_termios;
+ serial_driver.stop = sab82532_stop;
+ serial_driver.start = sab82532_start;
+ serial_driver.hangup = sab82532_hangup;
+ serial_driver.wait_until_sent = sab82532_wait_until_sent;
+ serial_driver.read_proc = sab82532_read_proc;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ for (info = sab82532_chain, i = 0; info; info = info->next, i++) {
+ info->magic = SERIAL_MAGIC;
+ info->line = i;
+ info->tty = 0;
+ info->count = 0;
+
+ info->type = info->regs->r.vstr & 0x0f;
+ info->regs->w.pcr = ~((1 << 1) | (1 << 2) | (1 << 4));
+ info->regs->w.pim = 0xff;
+ if (info->line == 0) {
+ info->pvr_dsr_bit = (1 << 0);
+ info->pvr_dtr_bit = (1 << 1);
+ } else {
+ info->pvr_dsr_bit = (1 << 3);
+ info->pvr_dtr_bit = (1 << 2);
+ }
+ info->regs->w.pvr = (1 << 1) | (1 << 2) | (1 << 4);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+
+ info->xmit_fifo_size = 32;
+ info->recv_fifo_size = 32;
+ info->custom_divisor = 16;
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = 30*HZ;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios = callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ info->delta_msr_wait = 0;
+ info->icount.cts = info->icount.dsr =
+ info->icount.rng = info->icount.dcd = 0;
+ info->icount.rx = info->icount.tx = 0;
+ info->icount.frame = info->icount.parity = 0;
+ info->icount.overrun = info->icount.brk = 0;
+
+ if (!(info->line & 0x01)) {
+ if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
+ "serial(sab82532)", info)) {
+ printk("sab82532: can't get IRQ %x\n",
+ info->irq);
+ panic("sab82532 initialization failed");
+ }
+ }
+
+ printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a %s\n",
+ info->line, (unsigned long)info->regs, info->irq,
+ "SAB82532");
+ }
+ return 0;
+}
+
+__initfunc(int sab82532_probe(unsigned long *memory_start))
+{
+ int node, enode, snode;
+
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (node) {
+ enode = prom_getchild(node);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ snode = prom_getchild(enode);
+ snode = prom_searchsiblings(snode, "se");
+ if (snode)
+ goto found;
+
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, sab82532_init);
+ rs_ops.rs_cons_hook = sab82532_cons_hook;
+ rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ if (get_sab82532())
+ return -ENODEV;
+
+ return sab82532_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();
+ timer_active &= ~(1 << RS_TIMER);
+ timer_table[RS_TIMER].fn = NULL;
+ timer_table[RS_TIMER].expires = 0;
+ remove_bh(SERIAL_BH);
+ if ((e1 = tty_unregister_driver(&serial_driver)))
+ printk("SERIAL: failed to unregister serial driver (%d)\n",
+ e1);
+ if ((e2 = tty_unregister_driver(&callout_driver)))
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ restore_flags(flags);
+
+ for (i = 0; i < NR_PORTS; i++) {
+ if (sab82532_table[i].type != PORT_UNKNOWN)
+ release_region(sab82532_table[i].port, 8);
+ }
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/sbus/char/sbuscons.c b/drivers/sbus/char/sbuscons.c
new file mode 100644
index 000000000..a0d47d79c
--- /dev/null
+++ b/drivers/sbus/char/sbuscons.c
@@ -0,0 +1,1644 @@
+/* $Id: sbuscons.c,v 1.7 1997/08/28 09:30:07 davem Exp $
+ * sbuscons.c: Routines specific to SBUS frame buffer consoles.
+ *
+ * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ * Copyright (C) 1995,1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Added render_screen and faster scrolling Nov/27, miguel
+ * Added console palette code for cg6 Dec/13/95, miguel
+ * Added generic frame buffer support Dec/14/95, miguel
+ * Added cgsix and bwtwo drivers Jan/96, miguel
+ * Added 4m, and cg3 driver Feb/96, miguel
+ * Fixed the cursor on color displays Feb/96, miguel.
+ * Cleaned up the detection code, generic 8bit depth display
+ * code, Mar/96 miguel
+ * Hacked support for cg14 video cards -- Apr/96, miguel.
+ * Color support for cg14 video cards -- May/96, miguel.
+ * Code split, Dave Redman, May/96
+ * Be more VT change friendly, May/96, miguel.
+ * Support for hw cursor and graphics acceleration, Jun/96, jj.
+ * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
+ * Added TCX support (8/24bit), Aug/96, jj.
+ * Support for multiple framebuffers, Sep/96, jj.
+ * Fix bwtwo inversion and handle inverse monochrome cells in
+ * sun_blitc, Nov/96, ecd.
+ * Fix sun_blitc and screen size on displays other than 1152x900,
+ * 128x54 chars, Nov/96, jj.
+ * Fix cursor spots left on some non-accelerated fbs, changed
+ * software cursor to be like the hw one, Nov/96, jj.
+ *
+ * Much of this driver is derived from the DEC TGA driver by
+ * Jay Estabrook who has done a nice job with the console
+ * driver abstraction btw.
+ *
+ * We try to make everything a power of two if possible to
+ * speed up the bit blit. Doing multiplies, divides, and
+ * remainder routines end up calling software library routines
+ * since not all Sparcs have the hardware to do it.
+ *
+ * TODO:
+ * do not blank the screen when frame buffer is mapped.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/fbio.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
+
+#include "fb.h"
+
+#define cmapsz 8192
+
+#include "suncons_font.h"
+
+#define ASM_BLITC
+
+int sbus_hw_cursor_shown = 0;
+
+void sbus_hw_hide_cursor(void);
+void sbus_hw_set_cursor(int,int);
+
+static int sbus_blitc(uint, unsigned long);
+
+static void sbus_install_consops(void);
+
+extern void register_console(void (*proc)(const char *));
+extern void console_print(const char *);
+extern void putconsxy(int, char *);
+extern unsigned char vga_font[];
+extern int serial_console;
+extern char *console_fb_path;
+
+/* The following variables describe a Sparc console. */
+
+/* Screen dimensions and color depth. */
+static int con_depth, con_width, con_height, con_type;
+
+/* Base address of first line. */
+static unsigned char *con_fb_base;
+
+/* Screen parameters: we compute those at startup to make the code faster */
+static int chars_per_line; /* number of bytes per line */
+static int ints_per_line; /* number of ints per line */
+static int ints_per_cursor; /* 14 * ints_per_line */
+static int skip_bytes; /* number of bytes we skip for the y margin + x_margin */
+static int x_margin, y_margin; /* the x and y margins */
+static int bytes_per_row; /* bytes used by one screen line (of 16 scan lines) */
+int sun_prom_console_id = 0;
+
+/* Functions used by the SPARC dependent console code
+ * to perform the fb_restore_palette function.
+ */
+extern void (*fb_restore_palette)(fbinfo_t *fbinfo);
+static void sbus_set_palette (void);
+
+ /* Our screen looks like at 1152 X 900:
+ *
+ * 0,0
+ * ------------------------------------------------------------------
+ * | ^^^^^^^^^^^ |
+ * | 18 y-pixels |
+ * | ^^^^^^^^^^^ |
+ * 13 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
+ * ....
+ * 54 chars from top to bottom
+ * ....
+ * 888 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
+ * | ^^^^^^^^^^^ |
+ * | 18 y-pixels |
+ * | ^^^^^^^^^^^ |
+ * ------------------------------------------------------------------
+ */
+/* First for MONO displays. */
+#define SCREEN_WIDTH 1152 /* Screen width in pixels */
+#define SCREEN_HEIGHT 900 /* Screen height in pixels */
+#define CHARS_PER_LINE 144 /* Make this empirical for speed */
+#define NICE_Y_MARGIN 18 /* We skip 18 y-pixels at top/bottom */
+#define NICE_X_MARGIN 8 /* We skip 64 x-pixels at left/right */
+#define FBUF_TOP_SKIP 2592 /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
+#define CHAR_HEIGHT 16
+#define ONE_ROW 2304 /* CHARS_PER_LINE * CHAR_HEIGHT */
+
+/* Now we have this, to compute the base frame buffer position
+ * for a new character to be rendered. 1 and 8 bit depth.
+ */
+#define FBUF_OFFSET(cindex) \
+ (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
+ ((NICE_X_MARGIN) + (((cindex)&127))))
+
+
+#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
+
+/* These four routines are optimizations for the _generic routine for
+ * the most common cases.
+ * I guess doing twice sll is much faster than doing .mul, sra faster
+ * than doing .div, and the disadvantage that someone has to call it
+ * (it cannot be inline) runs away, 'cause otherwise it would have to
+ * call .mul anyway.
+ * The shifting + addition only routines won't eat any stack frame :))
+ * Names come from width, screen_num_columns.
+ */
+static int color_fbuf_offset_1280_144 (int cindex)
+{
+ register int i = (cindex/144);
+ /* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+}
+
+static int color_fbuf_offset_1152_128 (int cindex)
+{
+ register int i = (cindex>>7);
+ /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128 (int cindex)
+{
+ register int i = (cindex>>7);
+ /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+ return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_800_96 (int cindex)
+{
+ register int i = (cindex / 96);
+ /* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
+ return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
+}
+
+static int color_fbuf_offset_640_80 (int cindex)
+{
+ register int i = (cindex/80);
+ /* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
+ return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
+}
+
+static int color_fbuf_offset_generic (int cindex)
+{
+ return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
+}
+
+static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
+
+static int do_accel = 0;
+
+/* For the cursor, we just invert the 8x16 block at the cursor
+ * location. Easy enough...
+ *
+ * Hide the cursor from view, during blanking, usually...
+ */
+static int cursor_pos = -1;
+
+static unsigned int under_cursor[4];
+
+static void sbus_hide_cursor(void)
+{
+ unsigned long flags;
+ int j;
+
+ if (fbinfo[0].setcursor) {
+ sbus_hw_hide_cursor();
+ return;
+ }
+
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return; /* Don't paint anything on fb which is not ours,
+ but turn off the hw cursor in such case */
+
+ __save_and_cli(flags);
+
+ if(cursor_pos == -1) {
+ __restore_flags (flags);
+ return;
+ }
+ switch (con_depth){
+ case 1: {
+ unsigned char *dst;
+ dst = (unsigned char *)((unsigned long)con_fb_base +
+ FBUF_OFFSET(cursor_pos));
+ for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
+ *dst = ~(*dst);
+ break;
+ }
+ case 8: {
+ unsigned int *dst;
+
+ dst = (unsigned int *)((unsigned long)con_fb_base +
+ COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
+ dst[0] = under_cursor[0];
+ dst[1] = under_cursor[1];
+ dst[ints_per_line] = under_cursor[2];
+ dst[ints_per_line+1] = under_cursor[3];
+ break;
+ }
+ default:
+ break;
+ }
+ cursor_pos = -1;
+ __restore_flags(flags);
+}
+
+static void sbus_set_cursor(int currcons)
+{
+ int j, idx, oldpos;
+ unsigned long flags;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ if (fbinfo[0].setcursor) {
+ if (!deccm)
+ sbus_hide_cursor();
+ else {
+ idx = (pos - video_mem_base) >> 1;
+
+ sbus_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
+ }
+ return;
+ }
+
+ __save_and_cli(flags);
+
+ idx = (pos - video_mem_base) >> 1;
+ oldpos = cursor_pos;
+ if (!deccm) {
+ sbus_hide_cursor ();
+ __restore_flags (flags);
+ return;
+ }
+ cursor_pos = idx;
+ switch (con_depth){
+ case 1: {
+ unsigned char *dst, *opos;
+
+ dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
+ opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
+ if(oldpos != -1) {
+ /* Restore what was at the old position */
+ for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
+ *opos = ~*opos;
+ }
+ }
+ for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
+ *dst = ~*dst;
+ }
+ break;
+ }
+ case 8: {
+ unsigned int *dst, *opos;
+ dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
+
+ if(oldpos != -1) {
+ opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
+ opos[0] = under_cursor[0];
+ opos[1] = under_cursor[1];
+ opos[ints_per_line] = under_cursor[2];
+ opos[ints_per_line+1] = under_cursor[3];
+ }
+ under_cursor[0] = dst[0];
+ under_cursor[1] = dst[1];
+ under_cursor[2] = dst[ints_per_line];
+ under_cursor[3] = dst[ints_per_line+1];
+ dst[0] = 0x00000000;
+ dst[1] = 0x00000000;
+ dst[ints_per_line] = 0x00000000;
+ dst[ints_per_line+1] = 0x00000000;
+ break;
+ }
+ default:
+ }
+ __restore_flags(flags);
+}
+
+/*
+ * Render the current screen
+ * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
+ * to avoid the caching that is being done in selection.h
+ */
+
+static void sbus_render_screen(void)
+{
+ int count;
+ unsigned short *contents;
+
+ count = video_num_columns * video_num_lines;
+ contents = (unsigned short *) video_mem_base;
+
+ for (;count--; contents++)
+ sbus_blitc (*contents, (unsigned long) contents);
+}
+
+__initfunc(static unsigned long
+sbus_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+ can_do_color = (con_type != FBTYPE_SUN2BW);
+
+ video_type = VIDEO_TYPE_SUN;
+ *display_desc = "SUN";
+
+ if (!serial_console) {
+ /* If we fall back to PROM then our output have to remain readable. */
+ prom_putchar('\033'); prom_putchar('['); prom_putchar('H');
+
+ /*
+ * fake the screen memory with some CPU memory
+ */
+ video_mem_base = kmem_start;
+ kmem_start += video_screen_size;
+ video_mem_term = kmem_start;
+ }
+ return kmem_start;
+}
+
+__initfunc(static void sbus_con_type_init_finish(void))
+{
+ int i, cpu;
+ char *p = con_fb_base + skip_bytes;
+ char q[2] = {0,5};
+ int currcons = 0;
+ unsigned short *ush;
+ int ncpus;
+
+ if (serial_console)
+ return;
+ ncpus = linux_num_cpus;
+ if (ncpus > 4) ncpus = 4;
+ if (fbinfo[0].draw_penguin) {
+ (*fbinfo[0].draw_penguin)(x_margin, y_margin, ncpus);
+ } else if (con_depth == 8 && fbinfo[0].loadcmap) {
+ for (i = 0; i < linux_logo_colors; i++) {
+ fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
+ fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
+ fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
+ }
+ (*fbinfo [0].loadcmap)(&fbinfo [0], 0, linux_logo_colors + 32);
+ for (i = 0; i < 80; i++, p += chars_per_line){
+ for (cpu = 0; cpu < ncpus; cpu++){
+ memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
+ }
+ }
+ } else if (con_depth == 1) {
+ for (i = 0; i < 80; i++, p += chars_per_line)
+ memcpy (p, linux_logo_bw + 10 * i, 10);
+ }
+ putconsxy(0, q);
+ ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (ncpus - 1);
+
+ p = logo_banner;
+ for (; *p; p++, ush++) {
+ *ush = (attr << 8) + *p;
+ sbus_blitc (*ush, (unsigned long) ush);
+ }
+ for (i = 0; i < 5; i++) {
+ ush = (unsigned short *) video_mem_base + i * video_num_columns;
+ memset (ush, 0, 20);
+ }
+}
+
+/*
+ * NOTE: get_scrmem() and set_scrmem() are here only because
+ * the VGA version of set_scrmem() has some direct VGA references.
+ */
+static void sbus_get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+static void sbus_set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+/*
+ * PIO_FONT support.
+ */
+static int sbus_set_get_font(char * arg, int set, int ch512)
+{
+ int i, line;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* download the current font */
+ if (!set){
+ if(clear_user(arg, cmapsz))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value = vga_font[i];
+
+ /* Access checked by the above clear_user */
+ __put_user_ret (value, (arg + (i * 32 + line)),
+ -EFAULT);
+ }
+ }
+ return 0;
+ }
+
+ /* set the font */
+
+ if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++){
+ unsigned char value;
+ __get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
+ vga_font [i*CHAR_HEIGHT + line] = value;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Adjust the screen to fit a font of a certain height
+ *
+ * Returns < 0 for error, 0 if nothing changed, and the number
+ * of lines on the adjusted console if changed.
+ *
+ * for now, we only support the built-in font...
+ */
+static int sbus_con_adjust_height(unsigned long fontheight)
+{
+ return -EINVAL;
+}
+
+static int sbus_set_get_cmap(unsigned char * arg, int set)
+{
+ int i;
+
+ if(set)
+ i = VERIFY_READ;
+ else
+ i = VERIFY_WRITE;
+ if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+ return -EFAULT;
+ for (i=0; i<16; i++) {
+ if (set) {
+ __get_user_ret(default_red[i], (arg+0),-EFAULT);
+ __get_user_ret(default_grn[i], (arg+1),-EFAULT);
+ __get_user_ret(default_blu[i], (arg+2),-EFAULT);
+ } else {
+ __put_user_ret(default_red[i], (arg+0),-EFAULT);
+ __put_user_ret(default_grn[i], (arg+1),-EFAULT);
+ __put_user_ret(default_blu[i], (arg+2),-EFAULT);
+ }
+ arg += 3;
+ }
+ if (set) {
+ for (i=0; i<MAX_NR_CONSOLES; i++)
+ if (vc_cons_allocated(i)) {
+ int j, k ;
+ for (j=k=0; j<16; j++) {
+ vc_cons[i].d->vc_palette[k++] = default_red[j];
+ vc_cons[i].d->vc_palette[k++] = default_grn[j];
+ vc_cons[i].d->vc_palette[k++] = default_blu[j];
+ }
+ }
+ sbus_set_palette();
+ }
+
+ return 0;
+}
+
+static void sbus_clear_screen(void)
+{
+ if (fbinfo[0].fill) {
+ int rects [4];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = con_width;
+ rects [3] = con_height;
+ (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
+ } else if (fbinfo[0].base && fbinfo[0].base_depth)
+ memset (con_fb_base,
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ (con_depth * con_height * con_width) / 8);
+ /* also clear out the "shadow" screen memory */
+ memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+ cursor_pos = -1;
+}
+
+static void sbus_clear_fb(int n)
+{
+ if (!n) {
+ sbus_clear_screen ();
+ } else if (fbinfo[n].base && fbinfo[n].base_depth) {
+ memset((void *)fbinfo[n].base,
+ (fbinfo[n].base_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fbinfo[n].base_depth * fbinfo[n].type.fb_height
+ * fbinfo[n].type.fb_width) / 8);
+ }
+}
+
+static void sbus_clear_margin(void)
+{
+ int h, he, i;
+ unsigned char *p;
+
+ if (fbinfo[0].fill) {
+ int rects [16];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = con_width;
+ rects [3] = y_margin;
+ rects [4] = 0;
+ rects [5] = y_margin;
+ rects [6] = x_margin;
+ rects [7] = con_height;
+ rects [8] = con_width - x_margin;
+ rects [9] = y_margin;
+ rects [10] = con_width;
+ rects [11] = con_height;
+ rects [12] = x_margin;
+ rects [13] = con_height - y_margin;
+ rects [14] = con_width - x_margin;
+ rects [15] = con_height;
+ (*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
+ } else {
+ memset (con_fb_base,
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin<<1));
+ memset (con_fb_base + chars_per_line * con_height
+ - skip_bytes + (x_margin<<1),
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin<<1));
+ he = con_height - 2 * y_margin;
+ i = 2 * x_margin;
+ if (con_depth == 1) {
+ for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+ h <= he; p += chars_per_line, h++)
+ memset (p, ~(0), i);
+ } else {
+ for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+ h <= he; p += chars_per_line, h++)
+ memset (p, reverse_color_table[0], i);
+ }
+ }
+ if (fbinfo [0].switch_from_graph)
+ (*fbinfo [0].switch_from_graph)();
+}
+
+/* Call the frame buffer routine for setting the palette */
+static void sbus_set_palette (void)
+{
+ if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ if (fbinfo [0].loadcmap){
+ int i, j;
+
+ /* First keep color_map with the palette colors */
+ for (i = 0; i < 16; i++){
+ j = sparc_color_table [i];
+ fbinfo[0].color_map CM(i,0) = default_red [j];
+ fbinfo[0].color_map CM(i,1) = default_grn [j];
+ fbinfo[0].color_map CM(i,2) = default_blu [j];
+ }
+ (*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
+ }
+}
+
+static void sbus_set_other_palette (int n)
+{
+ if (!n) {
+ sbus_set_palette ();
+ return;
+ }
+ if (fbinfo [n].loadcmap){
+ fbinfo[n].color_map CM(0,0) = 0;
+ fbinfo[n].color_map CM(0,1) = 0;
+ fbinfo[n].color_map CM(0,2) = 0;
+ (*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
+ }
+}
+
+/* Called when returning to prom */
+static void sbus_console_restore_palette (void)
+{
+ if (fb_restore_palette)
+ (*fb_restore_palette) (&fbinfo[0]);
+}
+
+__initfunc(unsigned long cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+{
+ fb->color_map = (char *)start_mem;
+ return start_mem + 256*3;
+}
+
+static char *known_cards [] __initdata = {
+ "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
+ "cgfourteen", "SUNW,leo", "SUNW,ffb", 0
+};
+static char *v0_known_cards [] __initdata = {
+ "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
+};
+
+__initfunc(static int known_card (char *name, char **known_cards))
+{
+ int i;
+
+ for (i = 0; known_cards [i]; i++)
+ if (strcmp (name, known_cards [i]) == 0)
+ return 1;
+ return 0;
+}
+
+static struct {
+ int depth;
+ int resx, resy;
+ int x_margin, y_margin;
+} scr_def [] = {
+ { 8, 1280, 1024, 64, 80 },
+ { 8, 1152, 1024, 64, 80 },
+ { 8, 1152, 900, 64, 18 },
+ { 8, 1024, 768, 0, 0 },
+ { 8, 800, 600, 16, 12 },
+ { 8, 640, 480, 0, 0 },
+ { 1, 1152, 900, 8, 18 },
+ { 0 },
+};
+
+__initfunc(static int cg14_present(void))
+{
+ int root, n;
+
+ root = prom_getchild (prom_root_node);
+ if ((n = prom_searchsiblings (root, "obio")) == 0)
+ return 0;
+
+ n = prom_getchild (n);
+ if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
+ return 0;
+ return n;
+}
+
+__initfunc(static int creator_present (void))
+{
+#ifdef __sparc_v9__
+ int root, n;
+
+ root = prom_getchild (prom_root_node);
+ if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
+ return 0;
+ return n;
+#else
+ return 0;
+#endif
+}
+
+__initfunc(static void
+ sparc_framebuffer_setup(int primary, int con_node,
+ int type, struct linux_sbus_device *sbdp,
+ uint base, unsigned long con_base, int prom_fb,
+ int parent_node))
+{
+ static int frame_buffers = 1;
+ int n, i;
+ int linebytes;
+ uint io = 0;
+ char *p;
+
+ if (primary)
+ n = 0;
+ else {
+ if (frame_buffers == FRAME_BUFFERS)
+ return; /* Silently ignore */
+ n = frame_buffers++;
+ }
+
+ if (prom_fb) sun_prom_console_id = n;
+
+ if (sbdp)
+ io = sbdp->reg_addrs [0].which_io;
+
+ /* Fill in common fb information */
+ fbinfo [n].clear_fb = sbus_clear_fb;
+ fbinfo [n].set_other_palette = sbus_set_other_palette;
+ fbinfo [n].type.fb_type = type;
+ fbinfo [n].real_type = type;
+ fbinfo [n].prom_node = con_node;
+ memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
+ fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
+ fbinfo [n].type.fb_width = prom_getintdefault(con_node, "width", 1152);
+ fbinfo [n].type.fb_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
+ linebytes = prom_getint(con_node, "linebytes");
+ if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
+ fbinfo [n].type.fb_size = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
+ fbinfo [n].space = io;
+ fbinfo [n].blanked = 0;
+ if (con_base >= PAGE_OFFSET)
+ fbinfo [n].base = con_base;
+ else
+ fbinfo [n].base = 0;
+ fbinfo [n].cursor.hwsize.fbx = 32;
+ fbinfo [n].cursor.hwsize.fby = 32;
+ fbinfo [n].proc_entry.node = parent_node;
+ fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
+ fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
+ p = strchr (fbinfo [n].proc_entry.name, 0);
+ sprintf (p, ":%d", n);
+
+ /* Should be filled in for supported video cards */
+ fbinfo [n].mmap = 0;
+ fbinfo [n].loadcmap = 0;
+ fbinfo [n].ioctl = 0;
+ fbinfo [n].reset = 0;
+ fbinfo [n].blank = 0;
+ fbinfo [n].unblank = 0;
+ fbinfo [n].setcursor = 0;
+ fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
+
+ /* Per card setup */
+ switch (fbinfo [n].type.fb_type){
+#ifdef SUN_FB_CGTHREE
+ case FBTYPE_SUN3COLOR:
+ cg3_setup (&fbinfo [n], n, base, io, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_TCX
+ case FBTYPE_TCXCOLOR:
+ tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_CGSIX
+ case FBTYPE_SUNFAST_COLOR:
+ cg6_setup (&fbinfo [n], n, base, io);
+ break;
+#endif
+#ifdef SUN_FB_BWTWO
+ case FBTYPE_SUN2BW:
+ bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_CGFOURTEEN
+ case FBTYPE_MDICOLOR:
+ cg14_setup (&fbinfo [n], n, con_node, base, io);
+ break;
+#endif
+#ifdef SUN_FB_LEO
+ case FBTYPE_SUNLEO:
+ leo_setup (&fbinfo [n], n, base, io);
+ break;
+#endif
+#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
+ case FBTYPE_CREATOR:
+ creator_setup (&fbinfo [n], n, con_node, base, io);
+ break;
+#endif
+ default:
+ fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
+ return;
+ }
+
+ if (n)
+ return;
+
+ /* Code below here is just executed for the first frame buffer */
+ con_type = type;
+ con_height = fbinfo [n].type.fb_height;
+ con_width = fbinfo [n].type.fb_width;
+ con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
+ for (i = 0; scr_def [i].depth; i++){
+ if ((scr_def [i].resx != con_width) ||
+ (scr_def [i].resy != con_height))
+ continue;
+ if (scr_def [i].depth != con_depth)
+ continue;
+ x_margin = scr_def [i].x_margin;
+ y_margin = scr_def [i].y_margin;
+ chars_per_line = (con_width * con_depth) / 8;
+ skip_bytes = chars_per_line * y_margin + x_margin;
+ ints_per_line = chars_per_line / 4;
+ ints_per_cursor = 14 * ints_per_line;
+ bytes_per_row = CHAR_HEIGHT * chars_per_line;
+ ORIG_VIDEO_COLS = con_width / 8 -
+ 2 * x_margin / con_depth;
+ ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
+ switch (chars_per_line) {
+ case 1280:
+ if (ORIG_VIDEO_COLS == 144)
+ color_fbuf_offset =
+ color_fbuf_offset_1280_144;
+ break;
+ case 1152:
+ if (ORIG_VIDEO_COLS == 128)
+ color_fbuf_offset =
+ color_fbuf_offset_1152_128;
+ break;
+ case 1024:
+ if (ORIG_VIDEO_COLS == 128)
+ color_fbuf_offset =
+ color_fbuf_offset_1024_128;
+ break;
+ case 800:
+ if (ORIG_VIDEO_COLS == 96)
+ color_fbuf_offset =
+ color_fbuf_offset_800_96;
+ break;
+ case 640:
+ if (ORIG_VIDEO_COLS == 80)
+ color_fbuf_offset =
+ color_fbuf_offset_640_80;
+ break;
+ }
+ break;
+ }
+
+ if (!scr_def [i].depth){
+ x_margin = y_margin = 0;
+ prom_printf ("console: unknown video resolution %dx%d,"
+ " depth %d\n",
+ con_width, con_height, con_depth);
+ prom_halt ();
+ }
+
+ /* P3: I fear this strips 15inch 1024/768 PC-like
+ * monitors out. */
+ if ((linebytes*8) / con_depth != con_width) {
+ prom_printf("console: unusual video, linebytes=%d, "
+ "width=%d, height=%d depth=%d\n",
+ linebytes, con_width, con_height,
+ con_depth);
+ prom_halt ();
+ }
+}
+
+__initfunc(int sbus_console_probe(void))
+{
+ int propl, con_node, default_node = 0;
+ char prop[16];
+ struct linux_sbus_device *sbdp, *sbdprom;
+ struct linux_sbus *sbus;
+ int creator = 0, cg14 = 0;
+ char prom_name[40];
+ int type, card_found = 0;
+ unsigned long con_base;
+ u32 tmp;
+ u32 prom_console_node = 0;
+
+ if(SBus_chain == 0)
+ return -1;
+
+ sbdprom = 0;
+ switch(prom_vers) {
+ case PROM_V0:
+ /* V0 proms are at sun4c only. Can skip many checks. */
+ con_type = FBTYPE_NOTYPE;
+ for_each_sbusdev(sbdp, SBus_chain) {
+ /* If no "address" than it is not the PROM console. */
+ if(sbdp->num_vaddrs) {
+ if(known_card(sbdp->prom_name, v0_known_cards)) {
+ sbdprom = sbdp;
+ strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
+ break;
+ }
+ }
+ }
+ if(!sbdprom) return -1;
+ for_each_sbusdev(sbdp, SBus_chain) {
+ con_node = sbdp->prom_node;
+
+ if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
+ !strncmp(sbdp->prom_name, "cgthree+", 8)) {
+ type = FBTYPE_SUNFAST_COLOR;
+ } else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
+ !strncmp(sbdp->prom_name, "cgRDI", 5)) {
+ type = FBTYPE_SUN3COLOR;
+ } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
+ type = FBTYPE_SUN2BW;
+ } else
+ continue;
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
+ (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
+ sbdp->my_bus->prom_node);
+ /* XXX HACK */
+ if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+ break;
+ }
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ if (console_fb_path) {
+ char *q, c;
+
+ for (q = console_fb_path; *q && *q != ' '; q++);
+ c = *q;
+ *q = 0;
+ default_node = prom_pathtoinode(console_fb_path);
+ if (default_node) {
+ prom_printf ("Using %s for console\n", console_fb_path);
+ prom_console_node = prom_inst2pkg(prom_stdout);
+ if (prom_console_node == default_node)
+ prom_console_node = 0;
+ }
+ }
+ if (!default_node)
+ default_node = prom_inst2pkg(prom_stdout);
+ propl = prom_getproperty(default_node, "device_type",
+ prop, sizeof (prop));
+ if (propl < 0) {
+ prom_printf ("output-device doesn't have device_type property\n");
+ prom_halt ();
+ } else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
+ prop [propl] = 0;
+ prom_printf ("console_probe: output-device is %s"
+ " (not \"display\")\n", prop);
+ prom_halt ();
+ }
+ for_all_sbusdev(sbdp, sbus) {
+ if ((sbdp->prom_node == default_node)
+ && known_card (sbdp->prom_name, known_cards)) {
+ sbdprom = sbdp;
+ break;
+ }
+ }
+ if (sbdprom)
+ card_found = 1;
+ if (!card_found)
+ card_found = cg14 = cg14_present ();
+ if (!card_found){
+ card_found = creator = creator_present ();
+ }
+ if (!card_found){
+ prom_printf ("Could not find a known video card on this machine\n");
+ prom_halt ();
+ }
+
+ for_all_sbusdev(sbdp, sbus) {
+ if (!known_card (sbdp->prom_name, known_cards))
+ continue;
+ con_node = sbdp->prom_node;
+ prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
+ sbdp->num_registers, sbdp);
+
+ propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
+ con_base = tmp;
+ if (propl != 4) con_base = 0;
+ propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
+ if (propl < 0 || propl >= sizeof (prom_name)) {
+ /* Early cg3s had no "emulation". */
+ propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
+ if (propl < 0) {
+ prom_printf("console: no device name!!\n");
+ return -1;
+ }
+ }
+ prom_name [sizeof (prom_name) - 1] = 0;
+ if(!strcmp(prom_name, "cgsix") ||
+ !strcmp(prom_name, "cgthree+")) {
+ type = FBTYPE_SUNFAST_COLOR;
+ } else if(!strcmp(prom_name, "cgthree") ||
+ !strcmp(prom_name, "cgRDI")) {
+ type = FBTYPE_SUN3COLOR;
+ } else if(!strcmp(prom_name, "cgfourteen")) {
+ type = FBTYPE_MDICOLOR;
+ } else if(!strcmp(prom_name, "SUNW,leo")) {
+ type = FBTYPE_SUNLEO;
+ } else if(!strcmp(prom_name, "bwtwo")) {
+ type = FBTYPE_SUN2BW;
+ } else if(!strcmp(prom_name,"SUNW,tcx")){
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
+ (uint)sbdp->reg_addrs [10].phys_addr, con_base,
+ prom_console_node == con_node, sbdp->my_bus->prom_node);
+ continue;
+ } else {
+ prom_printf("console: \"%s\" is unsupported\n", prom_name);
+ continue;
+ }
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
+ (uint)sbdp->reg_addrs [0].phys_addr, con_base,
+ prom_console_node == con_node, sbdp->my_bus->prom_node);
+ /* XXX HACK */
+ if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+ break;
+ }
+ if (cg14) {
+ sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR,
+ 0, 0, 0, prom_console_node == cg14,
+ prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
+ }
+ if (creator){
+ sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
+ 0, 0, 0, prom_console_node == creator,
+ prom_root_node);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
+ prom_printf ("Couldn't setup your primary frame buffer.\n");
+ prom_halt ();
+ }
+
+ if (fbinfo [0].blitc)
+ do_accel = 1;
+
+ con_fb_base = (unsigned char *)fbinfo[0].base;
+ if (!con_fb_base){
+ prom_printf ("PROM does not have an 'address' property for this\n"
+ "frame buffer and the Linux drivers do not know how\n"
+ "to map the video of this device\n");
+ prom_halt ();
+ }
+ sbus_install_consops();
+ return fb_init ();
+}
+
+/*
+ * sbus_blitc
+ *
+ * Displays an ASCII character at a specified character cell
+ * position.
+ *
+ * Called from scr_writew() when the destination is
+ * the "shadow" screen
+ */
+static uint
+fontmask_bits[16] = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static int sbus_blitc(uint charattr, unsigned long addr)
+{
+ unsigned int fgmask, bgmask;
+ unsigned char attrib;
+ int j, idx;
+ unsigned char *font_row;
+
+ if (do_accel) {
+ (*fbinfo[0].blitc)(charattr,
+ x_margin + (((addr - video_mem_base) % video_size_row)<<2),
+ y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
+ return 0;
+ }
+
+ /* Invalidate the cursor position if necessary. */
+ idx = (addr - video_mem_base) >> 1;
+
+ attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ font_row = &vga_font[(j = (charattr & 0xff)) << 4];
+
+ switch (con_depth){
+ case 1: {
+ register unsigned char *dst;
+ unsigned long flags;
+
+ dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
+
+ __save_and_cli(flags);
+ if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
+ for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+ *dst = ~(*font_row);
+ } else {
+ for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+ *dst = *font_row;
+ }
+ __restore_flags(flags);
+ break;
+ }
+ case 8: {
+#ifdef ASM_BLITC
+ const int cpl = chars_per_line;
+ /* The register assignment is important here, do not modify without touching the assembly code as well */
+ register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7");
+ register unsigned int *dst __asm__("g1");
+#else
+ const int ipl = ints_per_line;
+ unsigned int data2, data3, data4;
+ unsigned int data, rowbits;
+ register unsigned int *dst;
+ unsigned long flags;
+#endif
+ const uint *fontm_bits = fontmask_bits;
+
+ dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
+ if (j == ' ') /* space is quite common, so we optimize a bit */ {
+#ifdef ASM_BLITC
+#define BLITC_SPACE \
+ "\n\t std %%g4, [%%g1]" \
+ "\n\t std %%g4, [%%g1 + %0]" \
+ "\n\t add %%g1, %1, %%g1"
+#define BLITC_SPC \
+ "\n\t std %0, [%1]" \
+ "\n\t std %0, [%1 + %2]"
+
+ x1 = attrib >> 4;
+ x1 |= x1 << 8;
+ x1 |= x1 << 16;
+ x3 = cpl << 1;
+
+ __asm__ __volatile__ (
+ "\n\t mov %2, %3"
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
+ __save_and_cli (flags);
+ if (idx != cursor_pos)
+ __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl));
+ else
+ __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
+ __restore_flags (flags);
+#else
+ bgmask = attrib >> 4;
+ bgmask |= bgmask << 8;
+ bgmask |= bgmask << 16;
+
+ for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ }
+ /* Prevent cursor spots left on the screen */
+ __save_and_cli(flags);
+ if (idx != cursor_pos) {
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ dst += ipl;
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ } else {
+ under_cursor [0] = bgmask;
+ under_cursor [1] = bgmask;
+ under_cursor [2] = bgmask;
+ under_cursor [3] = bgmask;
+ }
+ __restore_flags(flags);
+#endif
+ } else /* non-space */ {
+ fgmask = attrib & 0x0f;
+ bgmask = attrib >> 4;
+ fgmask |= fgmask << 8;
+ fgmask |= fgmask << 16;
+ bgmask |= bgmask << 8;
+ bgmask |= bgmask << 16;
+
+#ifdef ASM_BLITC
+#define BLITC_INIT \
+ "\n\t ld [%0], %%g2"
+#define BLITC_BODY(ST1,SC1,ST2,SC2) \
+ "\n\t " #ST1 " %%g2, " #SC1 ", %%g7" \
+ "\n\t " #ST2 " %%g2, " #SC2 ", %7" \
+ "\n\t and %%g7, 0x3c, %%g7" \
+ "\n\t and %7, 0x3c, %7" \
+ "\n\t ld [%1 + %%g7], %6" \
+ "\n\t and %6, %2, %%g7" \
+ "\n\t andn %3, %6, %6" \
+ "\n\t or %%g7, %6, %6" \
+ "\n\t ld [%1 + %7], %7" \
+ "\n\t and %7, %2, %%g7" \
+ "\n\t andn %3, %7, %7" \
+ "\n\t or %%g7, %7, %7"
+#define BLITC_BODYEND \
+ "\n\t sll %3, 2, %%g7" \
+ "\n\t srl %3, 2, %3" \
+ "\n\t and %%g7, 0x3c, %%g7" \
+ "\n\t and %3, 0x3c, %3" \
+ "\n\t ld [%0 + %%g7], %4" \
+ "\n\t and %4, %1, %%g7" \
+ "\n\t andn %2, %4, %4" \
+ "\n\t or %%g7, %4, %4" \
+ "\n\t ld [%0 + %3], %3" \
+ "\n\t and %3, %1, %%g7" \
+ "\n\t andn %2, %3, %3" \
+ "\n\t or %%g7, %3, %3"
+#define BLITC_STOREIT \
+ "\n\t std %6, [%5]" \
+ "\n\t add %5, %4, %5" \
+ "\n\t"
+#define BLITC_STORE \
+ "\n\t std %%g4, [%0]" \
+ "\n\t std %%g2, [%0 + %1]"
+
+ for (j = 0; j < 3; j++, font_row+=4) {
+ __asm__ __volatile__ (BLITC_INIT
+ BLITC_BODY(srl, 26, srl, 22)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 18, srl, 14)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 10, srl, 6)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 2, sll, 2)
+ BLITC_STOREIT
+ : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+ "r" (x1), "r" (x2));
+ }
+ __asm__ __volatile__ (BLITC_INIT
+ BLITC_BODY(srl, 26, srl, 22)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 18, srl, 14)
+ BLITC_STOREIT
+ /* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
+ BLITC_BODY(srl, 10, srl, 6)
+ : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+ "r" (x1), "r" (x2));
+ /* Prepare the data the bottom line (and put it into g2,g3) */
+ __asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
+ "r" (x3), "r" (x4));
+ __save_and_cli(flags);
+ if (idx != cursor_pos)
+ __asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
+ else
+ __asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
+ __restore_flags (flags);
+#else
+ for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+ rowbits = *font_row;
+ data = fontm_bits[(rowbits>>4)&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ *dst = data;
+ data = fontm_bits[rowbits&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ *(dst+1) = data;
+ }
+ rowbits = *font_row;
+ data = fontm_bits[(rowbits>>4)&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ data2 = fontm_bits[rowbits&0xf];
+ data2 = (data2 & fgmask) | (~data2 & bgmask);
+ rowbits = font_row[1];
+ data3 = fontm_bits[(rowbits>>4)&0xf];
+ data3 = (data3 & fgmask) | (~data3 & bgmask);
+ data4 = fontm_bits[rowbits&0xf];
+ data4 = (data4 & fgmask) | (~data4 & bgmask);
+
+ /* Prevent cursor spots left on the screen */
+ __save_and_cli(flags);
+
+ if (idx != cursor_pos) {
+ *dst = data;
+ *(dst+1) = data2;
+ dst += ipl;
+ *dst = data3;
+ *(dst+1) = data4;
+ } else {
+ under_cursor [0] = data;
+ under_cursor [1] = data2;
+ under_cursor [2] = data3;
+ under_cursor [3] = data4;
+ }
+
+ __restore_flags(flags);
+#endif
+ }
+ break;
+ } /* case */
+ } /* switch */
+ return (0);
+}
+
+static void sbus_scr_writew(unsigned short val, unsigned short * addr)
+{
+ /*
+ * always deposit the char/attr, then see if it was to "screen" mem.
+ * if so, then render the char/attr onto the real screen.
+ */
+ if (*addr != val) {
+ *addr = val;
+ if ((unsigned long)addr < video_mem_term &&
+ (unsigned long)addr >= video_mem_base &&
+ vt_cons [fg_console]->vc_mode == KD_TEXT)
+ sbus_blitc(val, (unsigned long) addr);
+ }
+}
+
+static unsigned short sbus_scr_readw(unsigned short * addr)
+{
+ return *addr;
+}
+
+static void sbus_memsetw(void * s, unsigned short c, unsigned int count)
+{
+ unsigned short * addr = (unsigned short *) s;
+
+ count >>= 1;
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ }
+ if ((unsigned long) addr + count > video_mem_term ||
+ (unsigned long) addr < video_mem_base) {
+ if ((unsigned long) addr + count <= video_mem_term ||
+ (unsigned long) addr > video_mem_base) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ } else {
+ while (count) {
+ count--;
+ scr_writew(c, addr++);
+ }
+ }
+#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
+ } else if (do_accel) {
+ int yoff = (((long)addr - (long)video_mem_base) >> 1) / video_num_columns;
+ int xoff = (((long)addr - (long)video_mem_base) >> 1) % video_num_columns;
+ unsigned short * last = addr;
+
+ while (count) {
+ count--;
+ if (*addr != c) {
+ if (xoff == video_num_columns) {
+ if (last != addr)
+ GX_SETW
+ xoff = 0;
+ yoff++;
+ last = addr;
+ }
+ *addr++ = c;
+ xoff++;
+ } else {
+ if (last != addr)
+ GX_SETW
+ if (xoff == video_num_columns) {
+ xoff = 0;
+ yoff++;
+ }
+ addr++;
+ xoff++;
+ last = addr;
+ }
+ }
+ if (last != addr)
+ GX_SETW
+ } else {
+ while (count) {
+ count--;
+ if (*addr != c) {
+ sbus_blitc(c, (unsigned long)addr);
+ *addr++ = c;
+ } else
+ addr++;
+ }
+ }
+}
+
+static void sbus_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ memcpy(to, from, count);
+ return;
+ }
+ if ((unsigned long) to + count > video_mem_term ||
+ (unsigned long) to < video_mem_base) {
+ if ((unsigned long) to + count <= video_mem_term ||
+ (unsigned long) to > video_mem_base)
+ memcpy(to, from, count);
+ else {
+ count >>= 1;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(from++), to++);
+ }
+ }
+#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
+ } else if (do_accel) {
+ int yoff = (((long)to - (long)video_mem_base) >> 1) / video_num_columns;
+ int xoff = (((long)to - (long)video_mem_base) >> 1) % video_num_columns;
+ unsigned short * last = to;
+
+ count >>=1;
+ while (count) {
+ count--;
+ if (*to != *from) {
+ if (xoff == video_num_columns) {
+ if (last != to)
+ GX_CPYW
+ xoff = 0;
+ yoff++;
+ last = to;
+ } else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
+ GX_CPYW
+ last = to;
+ }
+ *to++ = *from++;
+ xoff++;
+ } else {
+ if (last != to)
+ GX_CPYW
+ if (xoff == video_num_columns) {
+ xoff = 0;
+ yoff++;
+ }
+ to++;
+ xoff++;
+ last = to;
+ from++;
+ }
+ }
+ if (last != to)
+ GX_CPYW
+ } else {
+ count >>= 1;
+ while (count) {
+ count--;
+ if (*to != *from) {
+ sbus_blitc(*from, (unsigned long)to);
+ *to++ = *from++;
+ } else {
+ from++;
+ to++;
+ }
+ }
+ }
+}
+
+#undef pos
+int sbus_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
+{
+ int op;
+ int i, bytes = 0;
+ struct fbcursor f;
+ char red[2], green[2], blue[2];
+
+ if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+ return -EFAULT;
+ op = f.set;
+ if (op & FB_CUR_SETSHAPE){
+ if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
+ return -EINVAL;
+ if ((uint) f.size.fby > fb->cursor.hwsize.fby)
+ return -EINVAL;
+ if (f.size.fbx > 32)
+ bytes = f.size.fby << 3;
+ else
+ bytes = f.size.fby << 2;
+ }
+ if (op & FB_CUR_SETCMAP){
+ if (f.cmap.index || f.cmap.count != 2)
+ return -EINVAL;
+ if (copy_from_user (red, f.cmap.red, 2) ||
+ copy_from_user (green, f.cmap.green, 2) ||
+ copy_from_user (blue, f.cmap.blue, 2))
+ return -EFAULT;
+ }
+ if (op & FB_CUR_SETCMAP)
+ (*fb->setcursormap) (fb, red, green, blue);
+ if (op & FB_CUR_SETSHAPE){
+ uint u;
+
+ fb->cursor.size = f.size;
+ memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+ if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+ copy_from_user (fb->cursor.bits [1], f.image, bytes))
+ return -EFAULT;
+ if (f.size.fbx <= 32) {
+ u = ~(0xffffffff >> f.size.fbx);
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][i] &= u;
+ fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+ }
+ } else {
+ u = ~(0xffffffff >> (f.size.fbx - 32));
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][2*i+1] &= u;
+ fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+ fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+ }
+ }
+ (*fb->setcurshape) (fb);
+ }
+ if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+ if (op & FB_CUR_SETCUR)
+ fb->cursor.enable = f.enable;
+ if (op & FB_CUR_SETPOS)
+ fb->cursor.cpos = f.pos;
+ if (op & FB_CUR_SETHOT)
+ fb->cursor.chot = f.hot;
+ (*fb->setcursor) (fb);
+ }
+ return 0;
+}
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+void sbus_hw_hide_cursor (void)
+{
+ fbinfo[0].cursor.enable = 0;
+ (*fbinfo[0].setcursor)(&fbinfo[0]);
+ sbus_hw_cursor_shown = 0;
+}
+
+void sbus_hw_set_cursor (int xoff, int yoff)
+{
+ if (!sbus_hw_cursor_shown) {
+ fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
+ fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
+ fbinfo[0].cursor.chot.fbx = 0;
+ fbinfo[0].cursor.chot.fby = 0;
+ fbinfo[0].cursor.enable = 1;
+ memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
+ fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
+ fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
+ fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
+ fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
+ (*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+ (*fbinfo[0].setcurshape) (&fbinfo[0]);
+ sbus_hw_cursor_shown = 1;
+ }
+ fbinfo[0].cursor.cpos.fbx = xoff;
+ fbinfo[0].cursor.cpos.fby = yoff;
+ (*fbinfo[0].setcursor)(&fbinfo[0]);
+}
+
+__initfunc(static void sbus_install_consops(void))
+{
+ suncons_ops.memsetw = sbus_memsetw;
+ suncons_ops.memcpyw = sbus_memcpyw;
+ suncons_ops.scr_writew = sbus_scr_writew;
+ suncons_ops.scr_readw = sbus_scr_readw;
+
+ suncons_ops.get_scrmem = sbus_get_scrmem;
+ suncons_ops.set_scrmem = sbus_set_scrmem;
+
+ suncons_ops.hide_cursor = sbus_hide_cursor;
+ suncons_ops.set_cursor = sbus_set_cursor;
+ suncons_ops.set_get_font = sbus_set_get_font;
+ suncons_ops.con_adjust_height = sbus_con_adjust_height;
+ suncons_ops.set_get_cmap = sbus_set_get_cmap;
+ suncons_ops.set_palette = sbus_set_palette;
+ suncons_ops.set_other_palette = sbus_set_other_palette;
+ suncons_ops.console_restore_palette = sbus_console_restore_palette;
+
+ suncons_ops.con_type_init = sbus_con_type_init;
+ suncons_ops.con_type_init_finish = sbus_con_type_init_finish;
+
+ suncons_ops.clear_screen = sbus_clear_screen;
+ suncons_ops.render_screen = sbus_render_screen;
+ suncons_ops.clear_margin = sbus_clear_margin;
+}
diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c
new file mode 100644
index 000000000..6cc5801cd
--- /dev/null
+++ b/drivers/sbus/char/su.c
@@ -0,0 +1,678 @@
+/* $Id: su.c,v 1.3 1997/09/03 11:54:56 ecd Exp $
+ * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This is mainly a very stripped down version of drivers/char/serial.c,
+ * credits go to authors mentioned therein.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static char *serial_name = "kbd/mouse serial driver";
+static char *serial_version = "1.0";
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+
+/* We are on a NS PC87303 clocked with 24.0 MHz, which results
+ * in a UART clock of 1.8462 MHz.
+ */
+#define BAUD_BASE (1846200 / 16)
+
+struct su_struct {
+ int magic;
+ unsigned long port;
+ int baud_base;
+ int type;
+ int irq;
+ int flags;
+ unsigned char IER;
+ unsigned char MCR;
+ int line;
+ int cflag;
+ int kbd_node;
+ int ms_node;
+ char name[16];
+};
+
+static struct su_struct su_table[] = {
+ { 0, 0, BAUD_BASE, PORT_UNKNOWN },
+ { 0, 0, BAUD_BASE, PORT_UNKNOWN }
+};
+
+#define NR_PORTS (sizeof(su_table) / sizeof(struct su_struct))
+
+static void autoconfig(struct su_struct * info);
+static void change_speed(struct su_struct *info);
+
+/*
+ * Here we define the default xmit fifo size used for each type of
+ * UART
+ */
+static struct serial_uart_config uart_config[] = {
+ { "unknown", 1, 0 },
+ { "8250", 1, 0 },
+ { "16450", 1, 0 },
+ { "16550", 1, 0 },
+ { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+ { "cirrus", 1, 0 },
+ { "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH },
+ { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+ UART_STARTECH },
+ { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+ { 0, 0}
+};
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+static inline
+unsigned int su_inb(struct su_struct *info, unsigned long offset)
+{
+ return inb(info->port + offset);
+}
+
+static inline void
+su_outb(struct su_struct *info, unsigned long offset, int value)
+{
+ outb(value, info->port + offset);
+}
+
+static inline void
+receive_chars(struct su_struct *info, struct pt_regs *regs)
+{
+ unsigned char status = 0;
+ unsigned char ch;
+
+ do {
+ ch = su_inb(info, UART_RX);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, status);
+#endif
+
+ if (info->kbd_node) {
+ if(ch == SUNKBD_RESET) {
+ l1a_state.kbd_id = 1;
+ l1a_state.l1_down = 0;
+ } else if(l1a_state.kbd_id) {
+ l1a_state.kbd_id = 0;
+ } else if(ch == SUNKBD_L1) {
+ l1a_state.l1_down = 1;
+ } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ l1a_state.l1_down = 0;
+ } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ /* whee... */
+ batten_down_hatches();
+ /* Continue execution... */
+ l1a_state.l1_down = 0;
+ l1a_state.kbd_id = 0;
+ return;
+ }
+ sunkbd_inchar(ch, regs);
+ } else {
+ sun_mouse_inbyte(ch);
+ }
+
+ status = su_inb(info, UART_LSR);
+ } while (status & UART_LSR_DR);
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void
+su_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct su_struct *info = (struct su_struct *)dev_id;
+ unsigned char status;
+
+ /*
+ * We might share interrupts with ps2kbd/ms driver,
+ * in case we want to use the 16550A as general serial
+ * driver in the presence of ps2 devices, so do a
+ * sanity check here, needs to be done in ps2kbd/ms
+ * driver, too.
+ */
+ if (!info || info->magic != SERIAL_MAGIC)
+ return;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("su_interrupt(%d)...", irq);
+#endif
+
+ if (su_inb(info, UART_IIR) & UART_IIR_NO_INT)
+ return;
+
+ status = su_inb(info, UART_LSR);
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
+ receive_chars(info, regs);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+static int
+startup(struct su_struct * info)
+{
+ unsigned long flags;
+ int retval=0;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ goto errout;
+ }
+
+ if (!info->port || !info->type) {
+ goto errout;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up su%d (irq %x)...", info->line, info->irq);
+#endif
+
+ if (info->type == PORT_16750)
+ su_outb(info, UART_IER, 0);
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ if (uart_config[info->type].flags & UART_CLEAR_FIFO)
+ su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+
+ /*
+ * At this point there's no way the LSR could still be 0xFF;
+ * if it is, then bail out, because there's likely no UART
+ * here.
+ */
+ if (su_inb(info, UART_LSR) == 0xff) {
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Allocate the IRQ if necessary
+ */
+ retval = request_irq(info->irq, su_interrupt, SA_SHIRQ,
+ info->name, info);
+ if (retval)
+ goto errout;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ su_inb(info, UART_RX);
+ su_inb(info, UART_IIR);
+ su_inb(info, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ su_outb(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ info->MCR = UART_MCR_OUT2;
+ su_outb(info, UART_MCR, info->MCR);
+
+ /*
+ * Finally, enable interrupts
+ */
+ info->IER = UART_IER_RLSI | UART_IER_RDI;
+ su_outb(info, UART_IER, info->IER); /* enable interrupts */
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ su_inb(info, UART_LSR);
+ su_inb(info, UART_RX);
+ su_inb(info, UART_IIR);
+ su_inb(info, UART_MSR);
+
+ /*
+ * 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 is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+change_speed(struct su_struct *info)
+{
+ unsigned char cval, fcr = 0;
+ int quot = 0;
+ unsigned long flags;
+ int i;
+
+ /* byte size and parity */
+ switch (info->cflag & CSIZE) {
+ case CS5: cval = 0x00; break;
+ case CS6: cval = 0x01; break;
+ case CS7: cval = 0x02; break;
+ case CS8: cval = 0x03; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: cval = 0x00; break;
+ }
+ if (info->cflag & CSTOPB) {
+ cval |= 0x04;
+ }
+ if (info->cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+ }
+ if (!(info->cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (info->cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /* Determine divisor based on baud rate */
+ i = info->cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ info->cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (!quot) {
+ if (baud_table[i] == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2 * info->baud_base / 269);
+ else if (baud_table[i])
+ quot = info->baud_base / baud_table[i];
+ /* If the quotient is ever zero, default to 1200 bps */
+ if (!quot)
+ quot = info->baud_base / 1200;
+ }
+
+ /* Set up FIFO's */
+ if (uart_config[info->type].flags & UART_USE_FIFO)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+
+ su_outb(info, UART_IER, info->IER);
+
+ save_flags(flags); cli();
+ su_outb(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ su_outb(info, UART_DLL, quot & 0xff); /* LS of divisor */
+ su_outb(info, UART_DLM, quot >> 8); /* MS of divisor */
+ if (info->type == PORT_16750)
+ su_outb(info, UART_FCR, fcr); /* set fcr */
+ su_outb(info, UART_LCR, cval); /* reset DLAB */
+ if (info->type != PORT_16750)
+ su_outb(info, UART_FCR, fcr); /* set fcr */
+ restore_flags(flags);
+}
+
+static void
+su_put_char(unsigned char c)
+{
+ struct su_struct *info = su_table;
+ int lsr;
+
+ if (!info->kbd_node)
+ ++info;
+
+ do {
+ lsr = inb(info->port + UART_LSR);
+ } while (!(lsr & UART_LSR_THRE));
+
+ /* Send the character out. */
+ su_outb(info, UART_TX, c);
+}
+
+static void
+su_change_mouse_baud(int baud)
+{
+ struct su_struct *info = su_table;
+
+ if (!info->ms_node)
+ ++info;
+ if (!info)
+ return;
+
+ info->cflag &= ~(CBAUDEX | CBAUD);
+ switch(baud) {
+ case 1200:
+ info->cflag |= B1200;
+ break;
+ case 2400:
+ info->cflag |= B2400;
+ break;
+ case 4800:
+ info->cflag |= B4800;
+ break;
+ case 9600:
+ info->cflag |= B9600;
+ break;
+ default:
+ printk("su_change_mouse_baud: unknown baud rate %d, "
+ "defaulting to 1200\n", baud);
+ info->cflag |= 1200;
+ break;
+ }
+ change_speed(info);
+}
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void
+show_su_version(void)
+{
+ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+/*
+ * This routine is called by su_init() to initialize a specific serial
+ * port. It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A. The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void
+autoconfig(struct su_struct *info)
+{
+ unsigned char status1, status2, scratch, scratch2;
+ struct linux_ebus_device *dev;
+ struct linux_ebus *ebus;
+ unsigned long flags;
+
+ for_all_ebusdev(dev, ebus) {
+ if (!strncmp(dev->prom_name, "su", 2)) {
+ if (dev->prom_node == info->kbd_node)
+ break;
+ if (dev->prom_node == info->ms_node)
+ break;
+ }
+ }
+ if (!dev)
+ return;
+
+ info->port = dev->base_address[0];
+ if (check_region(info->port, 8))
+ return;
+
+ info->irq = dev->irqs[0];
+
+#ifdef DEBUG_SERIAL_OPEN
+ printk("Found 'su' at %016lx IRQ %08x\n",
+ dev->base_address[0], dev->irqs[0]);
+#endif
+
+ info->magic = SERIAL_MAGIC;
+
+ save_flags(flags); cli();
+
+ /*
+ * Do a simple existence test first; if we fail this, there's
+ * no point trying anything else.
+ *
+ * 0x80 is used as a nonsense port to prevent against false
+ * positives due to ISA bus float. The assumption is that
+ * 0x80 is a non-existent port; which should be safe since
+ * include/asm/io.h also makes this assumption.
+ */
+ scratch = su_inb(info, UART_IER);
+ su_outb(info, UART_IER, 0);
+ scratch2 = su_inb(info, UART_IER);
+ su_outb(info, UART_IER, scratch);
+ if (scratch2) {
+ restore_flags(flags);
+ return; /* We failed; there's nothing here */
+ }
+
+ scratch = su_inb(info, UART_MCR);
+ su_outb(info, UART_MCR, UART_MCR_LOOP | scratch);
+ scratch2 = su_inb(info, UART_MSR);
+ su_outb(info, UART_MCR, UART_MCR_LOOP | 0x0A);
+ status1 = su_inb(info, UART_MSR) & 0xF0;
+ su_outb(info, UART_MCR, scratch);
+ su_outb(info, UART_MSR, scratch2);
+ if (status1 != 0x90) {
+ restore_flags(flags);
+ return;
+ }
+
+ scratch2 = su_inb(info, UART_LCR);
+ su_outb(info, UART_LCR, 0xBF); /* set up for StarTech test */
+ su_outb(info, UART_EFR, 0); /* EFR is the same as FCR */
+ su_outb(info, UART_LCR, 0);
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = su_inb(info, UART_IIR) >> 6;
+ switch (scratch) {
+ case 0:
+ info->type = PORT_16450;
+ break;
+ case 1:
+ info->type = PORT_UNKNOWN;
+ break;
+ case 2:
+ info->type = PORT_16550;
+ break;
+ case 3:
+ info->type = PORT_16550A;
+ break;
+ }
+ if (info->type == PORT_16550A) {
+ /* Check for Startech UART's */
+ su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ if (su_inb(info, UART_EFR) == 0) {
+ info->type = PORT_16650;
+ } else {
+ su_outb(info, UART_LCR, 0xBF);
+ if (su_inb(info, UART_EFR) == 0)
+ info->type = PORT_16650V2;
+ }
+ }
+ if (info->type == PORT_16550A) {
+ /* Check for TI 16750 */
+ su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ su_outb(info, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ scratch = su_inb(info, UART_IIR) >> 5;
+ if (scratch == 7) {
+ su_outb(info, UART_LCR, 0);
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = su_inb(info, UART_IIR) >> 5;
+ if (scratch == 6)
+ info->type = PORT_16750;
+ }
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
+ su_outb(info, UART_LCR, scratch2);
+ if (info->type == PORT_16450) {
+ scratch = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, 0xa5);
+ status1 = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, 0x5a);
+ status2 = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, scratch);
+
+ if ((status1 != 0xa5) || (status2 != 0x5a))
+ info->type = PORT_8250;
+ }
+
+ if (info->type == PORT_UNKNOWN) {
+ restore_flags(flags);
+ return;
+ }
+
+ sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
+ request_region(info->port, 8, info->name);
+
+ /*
+ * Reset the UART.
+ */
+ su_outb(info, UART_MCR, 0x00);
+ su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+ su_inb(info, UART_RX);
+
+ restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int su_init(void))
+{
+ int i;
+ struct su_struct *info;
+
+ show_su_version();
+
+ for (i = 0, info = su_table; i < NR_PORTS; i++, info++) {
+ info->line = i;
+ if (info->kbd_node)
+ info->cflag = B1200 | CS8 | CREAD;
+ else
+ info->cflag = B4800 | CS8 | CREAD;
+
+ autoconfig(info);
+ if (info->type == PORT_UNKNOWN)
+ continue;
+
+ printk(KERN_INFO "%s at %16lx (irq = %08x) is a %s\n",
+ info->name, info->port, info->irq,
+ uart_config[info->type].name);
+
+ startup(info);
+ if (info->kbd_node)
+ keyboard_zsinit(su_put_char);
+ else
+ sun_mouse_zsinit();
+ }
+ return 0;
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+ struct su_struct *info = su_table;
+ int node, enode, sunode;
+ int kbnode = 0, msnode = 0;
+ int devices = 0;
+ char prop[128];
+ int len;
+
+ /*
+ * Get the nodes for keyboard and mouse from 'aliases'...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "aliases");
+ if (!node)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+ if (len > 0)
+ kbnode = prom_pathtoinode(prop);
+ if (!kbnode)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+ if (len > 0)
+ msnode = prom_pathtoinode(prop);
+ if (!msnode)
+ return -ENODEV;
+
+ /*
+ * Find matching EBus nodes...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (node) {
+ enode = prom_getchild(node);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ sunode = prom_getchild(enode);
+ sunode = prom_searchsiblings(sunode, "su");
+
+ /*
+ * For each 'su' on this EBus...
+ */
+ while (sunode) {
+ /*
+ * Does it match?
+ */
+ if (sunode == kbnode) {
+ info->kbd_node = kbnode;
+ ++info;
+ ++devices;
+ }
+ if (sunode == msnode) {
+ info->ms_node = msnode;
+ ++info;
+ ++devices;
+ }
+
+ /*
+ * Found everything we need?
+ */
+ if (devices == NR_PORTS)
+ goto found;
+
+ sunode = prom_getsibling(sunode);
+ sunode = prom_searchsiblings(sunode, "su");
+ }
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, su_init);
+ rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+ return 0;
+}
diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c
index 111f65f97..7e88b4688 100644
--- a/drivers/sbus/char/suncons.c
+++ b/drivers/sbus/char/suncons.c
@@ -1,706 +1,259 @@
-/* $Id: suncons.c,v 1.67 1997/07/20 05:59:42 davem Exp $
- *
- * suncons.c: Sun SparcStation console support.
- *
- * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- *
- * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Added render_screen and faster scrolling Nov/27, miguel
- * Added console palette code for cg6 Dec/13/95, miguel
- * Added generic frame buffer support Dec/14/95, miguel
- * Added cgsix and bwtwo drivers Jan/96, miguel
- * Added 4m, and cg3 driver Feb/96, miguel
- * Fixed the cursor on color displays Feb/96, miguel.
- * Cleaned up the detection code, generic 8bit depth display
- * code, Mar/96 miguel
- * Hacked support for cg14 video cards -- Apr/96, miguel.
- * Color support for cg14 video cards -- May/96, miguel.
- * Code split, Dave Redman, May/96
- * Be more VT change friendly, May/96, miguel.
- * Support for hw cursor and graphics acceleration, Jun/96, jj.
- * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
- * Added TCX support (8/24bit), Aug/96, jj.
- * Support for multiple framebuffers, Sep/96, jj.
- * Fix bwtwo inversion and handle inverse monochrome cells in
- * sun_blitc, Nov/96, ecd.
- * Fix sun_blitc and screen size on displays other than 1152x900,
- * 128x54 chars, Nov/96, jj.
- * Fix cursor spots left on some non-accelerated fbs, changed
- * software cursor to be like the hw one, Nov/96, jj.
- *
- * Much of this driver is derived from the DEC TGA driver by
- * Jay Estabrook who has done a nice job with the console
- * driver abstraction btw.
- *
- * We try to make everything a power of two if possible to
- * speed up the bit blit. Doing multiplies, divides, and
- * remainder routines end up calling software library routines
- * since not all Sparcs have the hardware to do it.
- *
- * TODO:
- * do not blank the screen when frame buffer is mapped.
+/* $Id: suncons.c,v 1.73 1997/08/25 07:50:33 jj Exp $
+ * suncons.c: Sparc platform console generic layer.
*
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*/
-
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kd.h>
-#include <linux/malloc.h>
-#include <linux/major.h>
-#include <linux/mm.h>
#include <linux/types.h>
-#include <linux/version.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/vt.h>
+#include <linux/selection.h>
#include <linux/proc_fs.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
-#include <asm/bitops.h>
-#include <asm/oplib.h>
#include <asm/sbus.h>
#include <asm/fbio.h>
-#include <asm/io.h>
-#include <asm/smp.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/consolemap.h>
-#include <linux/selection.h>
-#include <linux/console_struct.h>
#include "fb.h"
-#define cmapsz 8192
-
-#include "suncons_font.h"
#include <asm/linux_logo.h>
fbinfo_t *fbinfo;
int fbinfos;
+unsigned int linux_logo_colors __initdata = LINUX_LOGO_COLORS;
+char logo_banner[] __initdata = linux_logo_banner;
-#define ASM_BLITC
+extern struct console vt_console_driver;
-int sun_hw_cursor_shown = 0;
+/* Infrastructure. */
-void sun_hw_hide_cursor(void);
-void sun_hw_set_cursor(int,int);
+static void nop_memsetw(void *s, unsigned short c, unsigned int count)
+{
+}
-extern void register_console(void (*proc)(const char *));
-extern void console_print(const char *);
-extern void putconsxy(int, char *);
-extern unsigned char vga_font[];
-extern int serial_console;
-char *console_fb_path = NULL; /* Set in setup.c */
+static void nop_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+}
-/* The following variables describe a Sparc console. */
+static void nop_scr_writew(unsigned short val, unsigned short *addr)
+{
+}
-/* Screen dimensions and color depth. */
-static int con_depth, con_width, con_height, con_type;
+static unsigned short nop_scr_readw(unsigned short *addr)
+{
+ return 0;
+}
-/* Base address of first line. */
-static unsigned char *con_fb_base;
+static void nop_get_scrmem(int a)
+{
+}
-/* Screen parameters: we compute those at startup to make the code faster */
-static int chars_per_line; /* number of bytes per line */
-static int ints_per_line; /* number of ints per line */
-static int ints_per_cursor; /* 14 * ints_per_line */
-static int skip_bytes; /* number of bytes we skip for the y margin + x_margin */
-static int x_margin, y_margin; /* the x and y margins */
-static int bytes_per_row; /* bytes used by one screen line (of 16 scan lines) */
-int sun_prom_console_id = 0;
+static void nop_set_scrmem(int a, long b)
+{
+}
-/* Functions used by the SPARC dependent console code
- * to perform the fb_restore_palette function.
- */
-void (*fb_restore_palette)(fbinfo_t *fbinfo);
-void set_palette (void);
+static void nop_set_origin(unsigned short offset)
+{
+}
- /* Our screen looks like at 1152 X 900:
- *
- * 0,0
- * ------------------------------------------------------------------
- * | ^^^^^^^^^^^ |
- * | 18 y-pixels |
- * | ^^^^^^^^^^^ |
- * 13 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
- * ....
- * 54 chars from top to bottom
- * ....
- * 888 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
- * | ^^^^^^^^^^^ |
- * | 18 y-pixels |
- * | ^^^^^^^^^^^ |
- * ------------------------------------------------------------------
- */
-/* First for MONO displays. */
-#define SCREEN_WIDTH 1152 /* Screen width in pixels */
-#define SCREEN_HEIGHT 900 /* Screen height in pixels */
-#define CHARS_PER_LINE 144 /* Make this empirical for speed */
-#define NICE_Y_MARGIN 18 /* We skip 18 y-pixels at top/bottom */
-#define NICE_X_MARGIN 8 /* We skip 64 x-pixels at left/right */
-#define FBUF_TOP_SKIP 2592 /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
-#define CHAR_HEIGHT 16
-#define ONE_ROW 2304 /* CHARS_PER_LINE * CHAR_HEIGHT */
-
-/* Now we have this, to compute the base frame buffer position
- * for a new character to be rendered. 1 and 8 bit depth.
- */
-#define FBUF_OFFSET(cindex) \
- (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
- ((NICE_X_MARGIN) + (((cindex)&127))))
+static void nop_hide_cursor(void)
+{
+}
+static void nop_set_cursor(int c)
+{
+}
-#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
+static int nop_set_get_font(char *a, int b, int c)
+{
+ return 0;
+}
-/* These four routines are optimizations for the _generic routine for
- * the most common cases.
- * I guess doing twice sll is much faster than doing .mul, sra faster
- * than doing .div, and the disadvantage that someone has to call it
- * (it cannot be inline) runs away, 'cause otherwise it would have to
- * call .mul anyway.
- * The shifting + addition only routines won't eat any stack frame :))
- * Names come from width, screen_num_columns.
- */
-static int
-color_fbuf_offset_1280_144 (int cindex)
+static int nop_con_adjust_height(unsigned long arg)
{
- register int i = (cindex/144);
- /* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
- return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+ return -EINVAL;
}
-static int
-color_fbuf_offset_1152_128 (int cindex)
+static int nop_set_get_cmap(unsigned char *arg, int a)
{
- register int i = (cindex>>7);
- /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
- return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+ return 0;
}
-static int
-color_fbuf_offset_1024_128 (int cindex)
+static void nop_set_palette(void)
{
- register int i = (cindex>>7);
- /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
- return skip_bytes + (i << 14) + ((cindex & 127) << 3);
}
-static int
-color_fbuf_offset_800_96 (int cindex)
+static void nop_set_other_palette(int a)
{
- register int i = (cindex / 96);
- /* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
- return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
}
-static int
-color_fbuf_offset_640_80 (int cindex)
+static void nop_console_restore_palette(void)
{
- register int i = (cindex/80);
- /* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
- return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
}
-
-static int
-color_fbuf_offset_generic (int cindex)
+
+static unsigned long nop_con_type_init(unsigned long mem_start,
+ const char **display_desc)
{
- return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
+ prom_printf("YIEEE: nop_con_type_init called!\n");
+ return mem_start;
}
-static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
-
-static int do_accel = 0;
+static void nop_con_type_init_finish(void)
+{
+ prom_printf("YIEEE: nop_con_type_init_finish called!\n");
+}
-void
-__set_origin(unsigned short offset)
+static void nop_vesa_blank(void)
{
- /*
- * should not be called, but if so, do nothing...
- */
}
-/* For the cursor, we just invert the 8x16 block at the cursor
- * location. Easy enough...
- *
- * Hide the cursor from view, during blanking, usually...
- */
-static int cursor_pos = -1;
+static void nop_vesa_unblank(void)
+{
+}
-static unsigned int under_cursor[4];
+static void nop_set_vesa_blanking(const unsigned long arg)
+{
+}
-void
-hide_cursor(void)
+static void nop_vesa_powerdown(void)
{
- unsigned long flags;
- int j;
+}
- if (fbinfo[0].setcursor) {
- sun_hw_hide_cursor();
- return;
- }
-
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- return; /* Don't paint anything on fb which is not ours,
- but turn off the hw cursor in such case */
-
- __save_and_cli(flags);
-
- if(cursor_pos == -1) {
- __restore_flags (flags);
- return;
- }
- switch (con_depth){
- case 1: {
- unsigned char *dst;
- dst = (unsigned char *)((unsigned long)con_fb_base +
- FBUF_OFFSET(cursor_pos));
- for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
- *dst = ~(*dst);
- break;
- }
- case 8: {
- unsigned int *dst;
-
- dst = (unsigned int *)((unsigned long)con_fb_base +
- COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
- dst[0] = under_cursor[0];
- dst[1] = under_cursor[1];
- dst[ints_per_line] = under_cursor[2];
- dst[ints_per_line+1] = under_cursor[3];
- break;
- }
- default:
- break;
- }
- cursor_pos = -1;
- __restore_flags(flags);
+static void nop_clear_screen(void)
+{
}
-void
-set_cursor(int currcons)
+static void nop_render_screen(void)
{
- int j, idx, oldpos;
- unsigned long flags;
+}
- if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
- return;
+static void nop_clear_margin(void)
+{
+}
-#if 0
-/* This is a nop anyway */
- if (__real_origin != __origin)
- __set_origin(__real_origin);
-#endif
-
- if (fbinfo[0].setcursor) {
- if (!deccm)
- hide_cursor();
- else {
- idx = (pos - video_mem_base) >> 1;
-
- sun_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
- }
- return;
- }
+struct suncons_operations suncons_ops = {
+ nop_memsetw,
+ nop_memcpyw,
+ nop_scr_writew,
+ nop_scr_readw,
+ nop_get_scrmem,
+ nop_set_scrmem,
+ nop_set_origin,
+ nop_hide_cursor,
+ nop_set_cursor,
+ nop_set_get_font,
+ nop_con_adjust_height,
+ nop_set_get_cmap,
+ nop_set_palette,
+ nop_set_other_palette,
+ nop_console_restore_palette,
+ nop_con_type_init,
+ nop_con_type_init_finish,
+ nop_vesa_blank,
+ nop_vesa_unblank,
+ nop_set_vesa_blanking,
+ nop_vesa_powerdown,
+ nop_clear_screen,
+ nop_render_screen,
+ nop_clear_margin
+};
- __save_and_cli(flags);
+/* Entry points. */
- idx = (pos - video_mem_base) >> 1;
- oldpos = cursor_pos;
- if (!deccm) {
- hide_cursor ();
- __restore_flags (flags);
- return;
- }
- cursor_pos = idx;
- switch (con_depth){
- case 1: {
- unsigned char *dst, *opos;
-
- dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
- opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
- if(oldpos != -1) {
- /* Restore what was at the old position */
- for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
- *opos = ~*opos;
- }
- }
- for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
- *dst = ~*dst;
- }
- break;
- }
- case 8: {
- unsigned int *dst, *opos;
- dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
-
- if(oldpos != -1) {
- opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
- opos[0] = under_cursor[0];
- opos[1] = under_cursor[1];
- opos[ints_per_line] = under_cursor[2];
- opos[ints_per_line+1] = under_cursor[3];
- }
- under_cursor[0] = dst[0];
- under_cursor[1] = dst[1];
- under_cursor[2] = dst[ints_per_line];
- under_cursor[3] = dst[ints_per_line+1];
- dst[0] = 0x00000000;
- dst[1] = 0x00000000;
- dst[ints_per_line] = 0x00000000;
- dst[ints_per_line+1] = 0x00000000;
- break;
- }
- default:
- }
- __restore_flags(flags);
+void get_scrmem(int a)
+{
+ suncons_ops.get_scrmem(a);
}
-/*
- * Render the current screen
- * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
- * to avoid the caching that is being done in selection.h
- */
-void
-render_screen(void)
+void set_scrmem(int a, long b)
{
- int count;
- unsigned short *contents;
-
- count = video_num_columns * video_num_lines;
- contents = (unsigned short *) video_mem_base;
-
- for (;count--; contents++)
- sun_blitc (*contents, (unsigned long) contents);
+ suncons_ops.set_scrmem(a, b);
}
-__initfunc(void serial_finish_init(void (*printfunc)(const char *, int)))
+void __set_origin(unsigned short offset)
{
- char buffer[2048];
-
- sprintf (buffer, linux_serial_image, UTS_RELEASE);
- (*printfunc)(buffer, strlen(buffer));
+ suncons_ops.set_origin(offset);
}
-__initfunc(void con_type_init_finish(void))
+void hide_cursor(void)
{
- int i, cpu;
- char *p = con_fb_base + skip_bytes;
- char q[2] = {0,5};
- int currcons = 0;
- unsigned short *ush;
-
- if (serial_console)
- return;
- if (con_type == FBTYPE_SUNLEO) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = con_width;
- rects [3] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
- return; /* Dunno how to display logo on leo/zx yet */
- }
- if (con_depth == 8 && fbinfo[0].loadcmap) {
- for (i = 0; i < LINUX_LOGO_COLORS; i++) {
- fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
- fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
- fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
- }
- (*fbinfo [0].loadcmap)(&fbinfo [0], 0, LINUX_LOGO_COLORS + 32);
- for (i = 0; i < 80; i++, p += chars_per_line){
- for (cpu = 0; cpu < linux_num_cpus; cpu++){
- memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
- }
- }
- } else if (con_depth == 1) {
- for (i = 0; i < 80; i++, p += chars_per_line)
- memcpy (p, linux_logo_bw + 10 * i, 10);
- }
- putconsxy(0, q);
- ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (linux_num_cpus - 1);
-
- p = linux_logo_banner;
- for (; *p; p++, ush++) {
- *ush = (attr << 8) + *p;
- sun_blitc (*ush, (unsigned long) ush);
- }
- for (i = 0; i < 5; i++) {
- ush = (unsigned short *) video_mem_base + i * video_num_columns;
- memset (ush, 0, 20);
- }
+ suncons_ops.hide_cursor();
}
-__initfunc(unsigned long
-con_type_init(unsigned long kmem_start, const char **display_desc))
+void set_cursor(int currcons)
{
- can_do_color = (con_type != FBTYPE_SUN2BW);
-
- video_type = VIDEO_TYPE_SUN;
- *display_desc = "SUN";
-
- if (!serial_console) {
- /* If we fall back to PROM then our output have to remain readable. */
- prom_putchar('\033'); prom_putchar('['); prom_putchar('H');
-
- /*
- * fake the screen memory with some CPU memory
- */
- video_mem_base = kmem_start;
- kmem_start += video_screen_size;
- video_mem_term = kmem_start;
- }
- return kmem_start;
+ suncons_ops.set_cursor(currcons);
}
-/*
- * NOTE: get_scrmem() and set_scrmem() are here only because
- * the VGA version of set_scrmem() has some direct VGA references.
- */
-void
-get_scrmem(int currcons)
+int set_get_font(char *arg, int set, int ch512)
{
- memcpyw((unsigned short *)vc_scrbuf[currcons],
- (unsigned short *)origin, video_screen_size);
- origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
- scr_end = video_mem_end = video_mem_start + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ return suncons_ops.set_get_font(arg, set, ch512);
}
-void
-set_scrmem(int currcons, long offset)
+int con_adjust_height(unsigned long fontheight)
{
- if (video_mem_term - video_mem_base < offset + video_screen_size)
- offset = 0;
- memcpyw((unsigned short *)(video_mem_base + offset),
- (unsigned short *) origin, video_screen_size);
- video_mem_start = video_mem_base;
- video_mem_end = video_mem_term;
- origin = video_mem_base + offset;
- scr_end = origin + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ return suncons_ops.con_adjust_height(fontheight);
}
-/*
- * PIO_FONT support.
- */
-int
-set_get_font(char * arg, int set, int ch512)
+int set_get_cmap(unsigned char *arg, int set)
{
- int i, line;
-
- if (!arg)
- return -EINVAL;
-
- /* download the current font */
- if (!set){
- if(clear_user(arg, cmapsz))
- return -EFAULT;
- for (i = 0; i < 256; i++) {
- for (line = 0; line < CHAR_HEIGHT; line++) {
- unsigned char value = vga_font[i];
-
- /* Access checked by the above clear_user */
- __put_user_ret (value, (arg + (i * 32 + line)),
- -EFAULT);
- }
- }
- return 0;
- }
-
- /* set the font */
-
- if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
- for (i = 0; i < 256; i++) {
- for (line = 0; line < CHAR_HEIGHT; line++){
- unsigned char value;
- __get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
- vga_font [i*CHAR_HEIGHT + line] = value;
- }
- }
- return 0;
+ return suncons_ops.set_get_cmap(arg, set);
}
-/*
- * Adjust the screen to fit a font of a certain height
- *
- * Returns < 0 for error, 0 if nothing changed, and the number
- * of lines on the adjusted console if changed.
- *
- * for now, we only support the built-in font...
- */
-int
-con_adjust_height(unsigned long fontheight)
+void set_palette(void)
{
- return -EINVAL;
+ suncons_ops.set_palette();
}
-int
-set_get_cmap(unsigned char * arg, int set)
+void set_other_palette(int n)
{
- int i;
-
- if(set)
- i = VERIFY_READ;
- else
- i = VERIFY_WRITE;
- if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
- return -EFAULT;
- for (i=0; i<16; i++) {
- if (set) {
- __get_user_ret(default_red[i], (arg+0),-EFAULT);
- __get_user_ret(default_grn[i], (arg+1),-EFAULT);
- __get_user_ret(default_blu[i], (arg+2),-EFAULT);
- } else {
- __put_user_ret(default_red[i], (arg+0),-EFAULT);
- __put_user_ret(default_grn[i], (arg+1),-EFAULT);
- __put_user_ret(default_blu[i], (arg+2),-EFAULT);
- }
- arg += 3;
- }
- if (set) {
- for (i=0; i<MAX_NR_CONSOLES; i++)
- if (vc_cons_allocated(i)) {
- int j, k ;
- for (j=k=0; j<16; j++) {
- vc_cons[i].d->vc_palette[k++] = default_red[j];
- vc_cons[i].d->vc_palette[k++] = default_grn[j];
- vc_cons[i].d->vc_palette[k++] = default_blu[j];
- }
- }
- set_palette();
- }
-
- return 0;
+ suncons_ops.set_other_palette(n);
}
-void
-sun_clear_screen(void)
+void console_restore_palette(void)
{
- if (fbinfo[0].fill) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = con_width;
- rects [3] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
- } else if (fbinfo[0].base && fbinfo[0].base_depth)
- memset (con_fb_base,
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- (con_depth * con_height * con_width) / 8);
- /* also clear out the "shadow" screen memory */
- memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
- cursor_pos = -1;
+ suncons_ops.console_restore_palette();
}
-void
-sun_clear_fb(int n)
+unsigned long con_type_init(unsigned long mem_start, const char **disp_desc)
{
- if (!n) sun_clear_screen ();
-#if 0
-/* This makes in some configurations serious problems.
- * Who cares if other screens are cleared?
- */
- else if (fbinfo[n].fill) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = fbinfo[n].type.fb_width;
- rects [3] = fbinfo[n].type.fb_height;
- (*fbinfo[n].fill)(reverse_color_table[0], 1, rects);
- }
-#endif
- else if (fbinfo[n].base && fbinfo[n].base_depth) {
- memset((void *)fbinfo[n].base,
- (fbinfo[n].base_depth == 1) ?
- ~(0) : reverse_color_table[0],
- (fbinfo[n].base_depth * fbinfo[n].type.fb_height
- * fbinfo[n].type.fb_width) / 8);
- }
+ return suncons_ops.con_type_init(mem_start, disp_desc);
}
-void
-sun_clear_margin(void)
+void con_type_init_finish(void)
{
- int h, he, i;
- unsigned char *p;
-
- if (fbinfo[0].fill) {
- int rects [16];
-
- memset (rects, 0, sizeof (rects));
- rects [2] = con_width;
- rects [3] = y_margin;
- rects [5] = y_margin;
- rects [6] = x_margin;
- rects [7] = con_height;
- rects [8] = con_width - x_margin;
- rects [9] = y_margin;
- rects [10] = con_width;
- rects [11] = con_height;
- rects [12] = x_margin;
- rects [13] = con_height - y_margin;
- rects [14] = con_width - x_margin;
- rects [15] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
- } else {
- memset (con_fb_base,
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- skip_bytes - (x_margin<<1));
- memset (con_fb_base + chars_per_line * con_height
- - skip_bytes + (x_margin<<1),
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- skip_bytes - (x_margin<<1));
- he = con_height - 2 * y_margin;
- i = 2 * x_margin;
- if (con_depth == 1) {
- for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
- h <= he; p += chars_per_line, h++)
- memset (p, ~(0), i);
- } else {
- for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
- h <= he; p += chars_per_line, h++)
- memset (p, reverse_color_table[0], i);
- }
- }
- if (fbinfo [0].switch_from_graph)
- (*fbinfo [0].switch_from_graph)();
+ suncons_ops.con_type_init_finish();
}
-/*
- * dummy routines for the VESA blanking code, which is VGA only,
- * so we don't have to carry that stuff around for the Sparc...
- */
void vesa_blank(void)
{
+ suncons_ops.vesa_blank();
}
void vesa_unblank(void)
{
+ suncons_ops.vesa_unblank();
}
void set_vesa_blanking(const unsigned long arg)
{
+ suncons_ops.set_vesa_blanking(arg);
}
void vesa_powerdown(void)
{
+ suncons_ops.vesa_powerdown();
+}
+
+void render_screen(void)
+{
+ suncons_ops.render_screen();
}
/*
@@ -711,53 +264,14 @@ unsigned char reverse_color_table[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
};
-static unsigned char sparc_color_table[] = {
+unsigned char sparc_color_table[] = {
15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11
};
-/* Call the frame buffer routine for setting the palette */
-void
-set_palette (void)
-{
- if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
- return;
-
- if (fbinfo [0].loadcmap){
- int i, j;
-
- /* First keep color_map with the palette colors */
- for (i = 0; i < 16; i++){
- j = sparc_color_table [i];
- fbinfo[0].color_map CM(i,0) = default_red [j];
- fbinfo[0].color_map CM(i,1) = default_grn [j];
- fbinfo[0].color_map CM(i,2) = default_blu [j];
- }
- (*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
- }
-}
-
-void
-set_other_palette (int n)
-{
- if (!n) {
- set_palette ();
- return;
- }
- if (fbinfo [n].loadcmap){
- fbinfo[n].color_map CM(0,0) = 0;
- fbinfo[n].color_map CM(0,1) = 0;
- fbinfo[n].color_map CM(0,2) = 0;
- (*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
- }
-}
+/* Probing engine. */
-/* Called when returning to prom */
-void
-console_restore_palette (void)
-{
- if (fb_restore_palette)
- (*fb_restore_palette) (&fbinfo[0]);
-}
+char *console_fb_path = NULL;
+void (*fb_restore_palette)(fbinfo_t *fbinfo) = NULL;
unsigned long
get_phys (unsigned long addr)
@@ -765,978 +279,98 @@ get_phys (unsigned long addr)
return __get_phys(addr);
}
-int
-get_iospace (unsigned long addr)
-{
- return __get_iospace(addr);
-}
+extern int sbus_console_probe(void);
+extern int serial_console;
-__initfunc(unsigned long sun_cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+__initfunc(static unsigned long finish_console_init(unsigned long memory_start))
{
- fb->color_map = (char *)start_mem;
- return start_mem + 256*3;
-}
-
-static char *known_cards [] __initdata = {
- "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
- "cgfourteen", "SUNW,leo", "SUNW,ffb", 0
-};
-static char *v0_known_cards [] __initdata = {
- "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
-};
+ static int confinish_has_run = 0;
+ int i, j;
-__initfunc(static int known_card (char *name, char **known_cards))
-{
- int i;
+ if(confinish_has_run != 0) {
+ printk("finish_console_init: Someone tries to run me twice.\n");
+ return memory_start;
+ }
+ for(i = FRAME_BUFFERS; i > 1; i--)
+ if(fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE)
+ break;
+ fbinfos = i;
- for (i = 0; known_cards [i]; i++)
- if (strcmp (name, known_cards [i]) == 0)
- return 1;
- return 0;
-}
+ for(j = 0; j < i; j++)
+ if (fbinfo[j].postsetup)
+ memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start);
-static struct {
- int depth;
- int resx, resy;
- int x_margin, y_margin;
-} scr_def [] = {
- { 8, 1280, 1024, 64, 80 },
- { 8, 1152, 1024, 64, 80 },
- { 8, 1152, 900, 64, 18 },
- { 8, 1024, 768, 0, 0 },
- { 8, 800, 600, 16, 12 },
- { 8, 640, 480, 0, 0 },
- { 1, 1152, 900, 8, 18 },
- { 0 },
-};
+ suncons_ops.clear_screen();
-__initfunc(static int cg14_present(void))
-{
- int root, n;
+ for(j = 1; j < i; j++)
+ if(fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
+ fbinfo[j].clear_fb(j);
+ fbinfo[j].set_other_palette(j);
+ }
+#if defined(CONFIG_PROC_FS) && \
+ ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
+ for (j = 0; j < i; j++)
+ if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE)
+ proc_openprom_regdev (&fbinfo[j].proc_entry);
+#endif
- root = prom_getchild (prom_root_node);
- if ((n = prom_searchsiblings (root, "obio")) == 0)
- return 0;
+ confinish_has_run = 1;
- n = prom_getchild (n);
- if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
- return 0;
- return n;
+ return memory_start;
}
-__initfunc(static int creator_present (void))
-{
- int root, n;
-
-#ifdef __sparc_v9__
- root = prom_getchild (prom_root_node);
- if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
- return 0;
- return n;
-#else
- return 0;
+#ifdef CONFIG_PCI
+extern void pci_console_inithook(void);
#endif
-}
-__initfunc(static void
- sparc_framebuffer_setup(int primary, int con_node,
- int type, struct linux_sbus_device *sbdp,
- uint base, unsigned long con_base, int prom_fb,
- int parent_node))
+__initfunc(unsigned long sun_console_init(unsigned long memory_start))
{
- static int frame_buffers = 1;
- int n, i;
- int linebytes;
- uint io = 0;
- char *p;
-
- if (primary)
- n = 0;
- else {
- if (frame_buffers == FRAME_BUFFERS)
- return; /* Silently ignore */
- n = frame_buffers++;
- }
-
- if (prom_fb) sun_prom_console_id = n;
-
- if (sbdp)
- io = sbdp->reg_addrs [0].which_io;
-
- /* Fill in common fb information */
- fbinfo [n].type.fb_type = type;
- fbinfo [n].real_type = type;
- fbinfo [n].prom_node = con_node;
- memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
- fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
- fbinfo [n].type.fb_width = prom_getintdefault(con_node, "width", 1152);
- fbinfo [n].type.fb_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
- linebytes = prom_getint(con_node, "linebytes");
- if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
- fbinfo [n].type.fb_size = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
- fbinfo [n].space = io;
- fbinfo [n].blanked = 0;
- if (con_base >= PAGE_OFFSET)
- fbinfo [n].base = con_base;
- else
- fbinfo [n].base = 0;
- fbinfo [n].cursor.hwsize.fbx = 32;
- fbinfo [n].cursor.hwsize.fby = 32;
- fbinfo [n].proc_entry.node = parent_node;
- fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
- fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
- prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
- p = strchr (fbinfo [n].proc_entry.name, 0);
- sprintf (p, ":%d", n);
-
- /* Should be filled in for supported video cards */
- fbinfo [n].mmap = 0;
- fbinfo [n].loadcmap = 0;
- fbinfo [n].ioctl = 0;
- fbinfo [n].reset = 0;
- fbinfo [n].blank = 0;
- fbinfo [n].unblank = 0;
- fbinfo [n].setcursor = 0;
- fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
-
- /* Per card setup */
- switch (fbinfo [n].type.fb_type){
-#ifdef SUN_FB_CGTHREE
- case FBTYPE_SUN3COLOR:
- cg3_setup (&fbinfo [n], n, base, io, sbdp);
- break;
-#endif
-#ifdef SUN_FB_TCX
- case FBTYPE_TCXCOLOR:
- tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
- break;
-#endif
-#ifdef SUN_FB_CGSIX
- case FBTYPE_SUNFAST_COLOR:
- cg6_setup (&fbinfo [n], n, base, io);
- break;
-#endif
-#ifdef SUN_FB_BWTWO
- case FBTYPE_SUN2BW:
- bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
- break;
-#endif
-#ifdef SUN_FB_CGFOURTEEN
- case FBTYPE_MDICOLOR:
- cg14_setup (&fbinfo [n], n, con_node, base, io);
- break;
-#endif
-#ifdef SUN_FB_LEO
- case FBTYPE_SUNLEO:
- leo_setup (&fbinfo [n], n, base, io);
- break;
-#endif
-#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
- case FBTYPE_CREATOR:
- creator_setup (&fbinfo [n], n, con_node, base, io);
- break;
-#endif
- default:
- fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
- return;
- }
+ int i;
- if (n)
- return;
-
- /* Code below here is just executed for the first frame buffer */
- con_type = type;
- con_height = fbinfo [n].type.fb_height;
- con_width = fbinfo [n].type.fb_width;
- con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
- for (i = 0; scr_def [i].depth; i++){
- if ((scr_def [i].resx != con_width) ||
- (scr_def [i].resy != con_height))
- continue;
- if (scr_def [i].depth != con_depth)
- continue;
- x_margin = scr_def [i].x_margin;
- y_margin = scr_def [i].y_margin;
- chars_per_line = (con_width * con_depth) / 8;
- skip_bytes = chars_per_line * y_margin + x_margin;
- ints_per_line = chars_per_line / 4;
- ints_per_cursor = 14 * ints_per_line;
- bytes_per_row = CHAR_HEIGHT * chars_per_line;
- ORIG_VIDEO_COLS = con_width / 8 -
- 2 * x_margin / con_depth;
- ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
- switch (chars_per_line) {
- case 1280:
- if (ORIG_VIDEO_COLS == 144)
- color_fbuf_offset =
- color_fbuf_offset_1280_144;
- break;
- case 1152:
- if (ORIG_VIDEO_COLS == 128)
- color_fbuf_offset =
- color_fbuf_offset_1152_128;
- break;
- case 1024:
- if (ORIG_VIDEO_COLS == 128)
- color_fbuf_offset =
- color_fbuf_offset_1024_128;
- break;
- case 800:
- if (ORIG_VIDEO_COLS == 96)
- color_fbuf_offset =
- color_fbuf_offset_800_96;
- break;
- case 640:
- if (ORIG_VIDEO_COLS == 80)
- color_fbuf_offset =
- color_fbuf_offset_640_80;
- break;
- }
- break;
- }
-
- if (!scr_def [i].depth){
- x_margin = y_margin = 0;
- prom_printf ("console: unknown video resolution %dx%d,"
- " depth %d\n",
- con_width, con_height, con_depth);
- prom_halt ();
- }
-
- /* P3: I fear this strips 15inch 1024/768 PC-like
- * monitors out. */
- if ((linebytes*8) / con_depth != con_width) {
- prom_printf("console: unusual video, linebytes=%d, "
- "width=%d, height=%d depth=%d\n",
- linebytes, con_width, con_height,
- con_depth);
- prom_halt ();
- }
-}
+ /* Nothing to do in this case. */
+ if(serial_console)
+ return memory_start;
-__initfunc(static int sparc_console_probe(void))
-{
- int propl, con_node, default_node = 0, i;
- char prop[16];
- struct linux_sbus_device *sbdp, *sbdprom;
- struct linux_sbus *sbus;
- int creator = 0, cg14 = 0;
- char prom_name[40];
- int type, card_found = 0;
- unsigned long con_base;
- u32 tmp;
- u32 prom_console_node = 0;
+ fbinfo = (fbinfo_t *)memory_start;
+ memset(fbinfo, 0, FRAME_BUFFERS * sizeof(fbinfo_t));
+ memory_start += (FRAME_BUFFERS * sizeof(fbinfo_t));
+ fbinfos = 0;
for (i = 0; i < FRAME_BUFFERS; i++)
fbinfo [i].type.fb_type = FBTYPE_NOTYPE;
- sbdprom = 0;
- switch(prom_vers) {
- case PROM_V0:
- /* V0 proms are at sun4c only. Can skip many checks. */
- con_type = FBTYPE_NOTYPE;
- if(SBus_chain == 0) {
- prom_printf("SBUS chain is NULL, bailing out...\n");
- prom_halt();
- }
- for_each_sbusdev(sbdp, SBus_chain) {
- /* If no "address" than it is not the PROM console. */
- if(sbdp->num_vaddrs) {
- if(known_card(sbdp->prom_name, v0_known_cards)) {
- sbdprom = sbdp;
- strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
- break;
- }
- }
- }
- if(!sbdprom) return -1;
- for_each_sbusdev(sbdp, SBus_chain) {
- con_node = sbdp->prom_node;
-
- if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
- !strncmp(sbdp->prom_name, "cgthree+", 8)) {
- type = FBTYPE_SUNFAST_COLOR;
- } else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
- !strncmp(sbdp->prom_name, "cgRDI", 5)) {
- type = FBTYPE_SUN3COLOR;
- } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
- type = FBTYPE_SUN2BW;
- } else
- continue;
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
- (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
- sbdp->my_bus->prom_node);
- /* XXX HACK */
- if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
- break;
- }
- break;
- case PROM_V2:
- case PROM_V3:
- case PROM_P1275:
- if (console_fb_path) {
- char *q, c;
-
- for (q = console_fb_path; *q && *q != ' '; q++);
- c = *q;
- *q = 0;
- default_node = prom_pathtoinode(console_fb_path);
- if (default_node) {
- prom_printf ("Using %s for console\n", console_fb_path);
- prom_console_node = prom_inst2pkg(prom_stdout);
- if (prom_console_node == default_node)
- prom_console_node = 0;
- }
- }
- if (!default_node)
- default_node = prom_inst2pkg(prom_stdout);
- propl = prom_getproperty(default_node, "device_type",
- prop, sizeof (prop));
- if (propl < 0) {
- prom_printf ("output-device doesn't have device_type property\n");
- prom_halt ();
- } else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
- prop [propl] = 0;
- prom_printf ("console_probe: output-device is %s"
- " (not \"display\")\n", prop);
- prom_halt ();
- }
- for_all_sbusdev(sbdp, sbus) {
- if ((sbdp->prom_node == default_node)
- && known_card (sbdp->prom_name, known_cards)) {
- sbdprom = sbdp;
- break;
- }
- }
- if (sbdprom)
- card_found = 1;
- if (!card_found)
- card_found = cg14 = cg14_present ();
- if (!card_found){
- card_found = creator = creator_present ();
- }
- if (!card_found){
- prom_printf ("Could not find a known video card on this machine\n");
- prom_halt ();
- }
-
- for_all_sbusdev(sbdp, sbus) {
- if (!known_card (sbdp->prom_name, known_cards))
- continue;
- con_node = sbdp->prom_node;
- prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
- sbdp->num_registers, sbdp);
-
- propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
- con_base = tmp;
- if (propl != 4) con_base = 0;
- propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
- if (propl < 0 || propl >= sizeof (prom_name)) {
- /* Early cg3s had no "emulation". */
- propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
- if (propl < 0) {
- prom_printf("console: no device name!!\n");
- return -1;
- }
- }
- prom_name [sizeof (prom_name) - 1] = 0;
- if(!strcmp(prom_name, "cgsix") ||
- !strcmp(prom_name, "cgthree+")) {
- type = FBTYPE_SUNFAST_COLOR;
- } else if(!strcmp(prom_name, "cgthree") ||
- !strcmp(prom_name, "cgRDI")) {
- type = FBTYPE_SUN3COLOR;
- } else if(!strcmp(prom_name, "cgfourteen")) {
- type = FBTYPE_MDICOLOR;
- } else if(!strcmp(prom_name, "SUNW,leo")) {
- type = FBTYPE_SUNLEO;
- } else if(!strcmp(prom_name, "bwtwo")) {
- type = FBTYPE_SUN2BW;
- } else if(!strcmp(prom_name,"SUNW,tcx")){
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
- (uint)sbdp->reg_addrs [10].phys_addr, con_base,
- prom_console_node == con_node, sbdp->my_bus->prom_node);
- continue;
- } else {
- prom_printf("console: \"%s\" is unsupported\n", prom_name);
- continue;
- }
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
- (uint)sbdp->reg_addrs [0].phys_addr, con_base,
- prom_console_node == con_node, sbdp->my_bus->prom_node);
- /* XXX HACK */
- if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
- break;
- }
- if (cg14) {
- sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR,
- 0, 0, 0, prom_console_node == cg14,
- prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
- }
- if (creator){
- sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
- 0, 0, 0, prom_console_node == creator,
- prom_root_node);
- }
- break;
- default:
- return -1;
- }
-
- if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
- prom_printf ("Couldn't setup your primary frame buffer.\n");
- prom_halt ();
- }
-
- if (fbinfo [0].blitc)
- do_accel = 1;
-
- con_fb_base = (unsigned char *)fbinfo[0].base;
- if (!con_fb_base){
- prom_printf ("PROM does not have an 'address' property for this\n"
- "frame buffer and the Linux drivers do not know how\n"
- "to map the video of this device\n");
- prom_halt ();
- }
- return fb_init ();
-}
-/* video init code, called from within the SBUS bus scanner at
- * boot time.
- */
-__initfunc(unsigned long sun_console_init(unsigned long memory_start))
-{
- int i, j;
- if(serial_console)
+ if(sbus_console_probe()) {
+#ifdef CONFIG_PCI
+ pci_console_inithook();
return memory_start;
-
- fbinfo = (fbinfo_t *)memory_start;
- memset (fbinfo, 0, FRAME_BUFFERS * sizeof (fbinfo_t));
- if(sparc_console_probe()) {
- prom_printf("Could not probe console, bailing out...\n");
+#else
+ /* XXX We need to write PROM console fallback driver... */
+ prom_printf("Could not probe SBUS console, bailing out...\n");
prom_halt();
- }
-
- sun_clear_screen();
- for (i = FRAME_BUFFERS; i > 1; i--)
- if (fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE) break;
- fbinfos = i;
- memory_start = memory_start + i * sizeof (fbinfo_t);
- for (j = 0; j < i; j++)
- if (fbinfo[j].postsetup)
- memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start);
- for (j = 1; j < i; j++)
- if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
- sun_clear_fb(j);
- set_other_palette(j);
- }
-#if defined(CONFIG_PROC_FS) && ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
- for (j = 0; j < i; j++)
- if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE)
- proc_openprom_regdev (&fbinfo[j].proc_entry);
#endif
- return memory_start;
-}
-
-/*
- * sun_blitc
- *
- * Displays an ASCII character at a specified character cell
- * position.
- *
- * Called from scr_writew() when the destination is
- * the "shadow" screen
- */
-static uint
-fontmask_bits[16] = {
- 0x00000000,
- 0x000000ff,
- 0x0000ff00,
- 0x0000ffff,
- 0x00ff0000,
- 0x00ff00ff,
- 0x00ffff00,
- 0x00ffffff,
- 0xff000000,
- 0xff0000ff,
- 0xff00ff00,
- 0xff00ffff,
- 0xffff0000,
- 0xffff00ff,
- 0xffffff00,
- 0xffffffff
-};
-
-int
-sun_blitc(uint charattr, unsigned long addr)
-{
- unsigned int fgmask, bgmask;
- unsigned char attrib;
- int j, idx;
- unsigned char *font_row;
-
- if (do_accel) {
- (*fbinfo[0].blitc)(charattr,
- x_margin + (((addr - video_mem_base) % video_size_row)<<2),
- y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
- return 0;
- }
-
- /* Invalidate the cursor position if necessary. */
- idx = (addr - video_mem_base) >> 1;
-
- attrib = CHARATTR_TO_SUNCOLOR(charattr);
- font_row = &vga_font[(j = (charattr & 0xff)) << 4];
-
- switch (con_depth){
- case 1: {
- register unsigned char *dst;
- unsigned long flags;
-
- dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
-
- __save_and_cli(flags);
- if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
- for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
- *dst = ~(*font_row);
- } else {
- for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
- *dst = *font_row;
- }
- __restore_flags(flags);
- break;
}
- case 8: {
-#ifdef ASM_BLITC
- const int cpl = chars_per_line;
- /* The register assignment is important here, do not modify without touching the assembly code as well */
- register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7");
- register unsigned int *dst __asm__("g1");
-#else
- const int ipl = ints_per_line;
- unsigned int data2, data3, data4;
- unsigned int data, rowbits;
- register unsigned int *dst;
- unsigned long flags;
-#endif
- const uint *fontm_bits = fontmask_bits;
-
- dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
- if (j == ' ') /* space is quite common, so we optimize a bit */ {
-#ifdef ASM_BLITC
-#define BLITC_SPACE \
- "\n\t std %%g4, [%%g1]" \
- "\n\t std %%g4, [%%g1 + %0]" \
- "\n\t add %%g1, %1, %%g1"
-#define BLITC_SPC \
- "\n\t std %0, [%1]" \
- "\n\t std %0, [%1 + %2]"
-
- x1 = attrib >> 4;
- x1 |= x1 << 8;
- x1 |= x1 << 16;
- x3 = cpl << 1;
-
- __asm__ __volatile__ (
- "\n\t mov %2, %3"
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
- __save_and_cli (flags);
- if (idx != cursor_pos)
- __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl));
- else
- __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
- __restore_flags (flags);
-#else
- bgmask = attrib >> 4;
- bgmask |= bgmask << 8;
- bgmask |= bgmask << 16;
-
- for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
- *dst = bgmask;
- *(dst+1) = bgmask;
- }
- /* Prevent cursor spots left on the screen */
- __save_and_cli(flags);
- if (idx != cursor_pos) {
- *dst = bgmask;
- *(dst+1) = bgmask;
- dst += ipl;
- *dst = bgmask;
- *(dst+1) = bgmask;
- } else {
- under_cursor [0] = bgmask;
- under_cursor [1] = bgmask;
- under_cursor [2] = bgmask;
- under_cursor [3] = bgmask;
- }
- __restore_flags(flags);
-#endif
- } else /* non-space */ {
- fgmask = attrib & 0x0f;
- bgmask = attrib >> 4;
- fgmask |= fgmask << 8;
- fgmask |= fgmask << 16;
- bgmask |= bgmask << 8;
- bgmask |= bgmask << 16;
-
-#ifdef ASM_BLITC
-#define BLITC_INIT \
- "\n\t ld [%0], %%g2"
-#define BLITC_BODY(ST1,SC1,ST2,SC2) \
- "\n\t " #ST1 " %%g2, " #SC1 ", %%g7" \
- "\n\t " #ST2 " %%g2, " #SC2 ", %7" \
- "\n\t and %%g7, 0x3c, %%g7" \
- "\n\t and %7, 0x3c, %7" \
- "\n\t ld [%1 + %%g7], %6" \
- "\n\t and %6, %2, %%g7" \
- "\n\t andn %3, %6, %6" \
- "\n\t or %%g7, %6, %6" \
- "\n\t ld [%1 + %7], %7" \
- "\n\t and %7, %2, %%g7" \
- "\n\t andn %3, %7, %7" \
- "\n\t or %%g7, %7, %7"
-#define BLITC_BODYEND \
- "\n\t sll %3, 2, %%g7" \
- "\n\t srl %3, 2, %3" \
- "\n\t and %%g7, 0x3c, %%g7" \
- "\n\t and %3, 0x3c, %3" \
- "\n\t ld [%0 + %%g7], %4" \
- "\n\t and %4, %1, %%g7" \
- "\n\t andn %2, %4, %4" \
- "\n\t or %%g7, %4, %4" \
- "\n\t ld [%0 + %3], %3" \
- "\n\t and %3, %1, %%g7" \
- "\n\t andn %2, %3, %3" \
- "\n\t or %%g7, %3, %3"
-#define BLITC_STOREIT \
- "\n\t std %6, [%5]" \
- "\n\t add %5, %4, %5" \
- "\n\t"
-#define BLITC_STORE \
- "\n\t std %%g4, [%0]" \
- "\n\t std %%g2, [%0 + %1]"
-
- for (j = 0; j < 3; j++, font_row+=4) {
- __asm__ __volatile__ (BLITC_INIT
- BLITC_BODY(srl, 26, srl, 22)
- BLITC_STOREIT
- BLITC_BODY(srl, 18, srl, 14)
- BLITC_STOREIT
- BLITC_BODY(srl, 10, srl, 6)
- BLITC_STOREIT
- BLITC_BODY(srl, 2, sll, 2)
- BLITC_STOREIT
- : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
- "r" (x1), "r" (x2));
- }
- __asm__ __volatile__ (BLITC_INIT
- BLITC_BODY(srl, 26, srl, 22)
- BLITC_STOREIT
- BLITC_BODY(srl, 18, srl, 14)
- BLITC_STOREIT
- /* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
- BLITC_BODY(srl, 10, srl, 6)
- : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
- "r" (x1), "r" (x2));
- /* Prepare the data the bottom line (and put it into g2,g3) */
- __asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
- "r" (x3), "r" (x4));
- __save_and_cli(flags);
- if (idx != cursor_pos)
- __asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
- else
- __asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
- __restore_flags (flags);
-#else
- for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
- rowbits = *font_row;
- data = fontm_bits[(rowbits>>4)&0xf];
- data = (data & fgmask) | (~data & bgmask);
- *dst = data;
- data = fontm_bits[rowbits&0xf];
- data = (data & fgmask) | (~data & bgmask);
- *(dst+1) = data;
- }
- rowbits = *font_row;
- data = fontm_bits[(rowbits>>4)&0xf];
- data = (data & fgmask) | (~data & bgmask);
- data2 = fontm_bits[rowbits&0xf];
- data2 = (data2 & fgmask) | (~data2 & bgmask);
- rowbits = font_row[1];
- data3 = fontm_bits[(rowbits>>4)&0xf];
- data3 = (data3 & fgmask) | (~data3 & bgmask);
- data4 = fontm_bits[rowbits&0xf];
- data4 = (data4 & fgmask) | (~data4 & bgmask);
-
- /* Prevent cursor spots left on the screen */
- __save_and_cli(flags);
-
- if (idx != cursor_pos) {
- *dst = data;
- *(dst+1) = data2;
- dst += ipl;
- *dst = data3;
- *(dst+1) = data4;
- } else {
- under_cursor [0] = data;
- under_cursor [1] = data2;
- under_cursor [2] = data3;
- under_cursor [3] = data4;
- }
-
- __restore_flags(flags);
-#endif
- }
- break;
- } /* case */
- } /* switch */
- return (0);
+ return finish_console_init(memory_start);
}
-void memsetw(void * s, unsigned short c, unsigned int count)
-{
- unsigned short * addr = (unsigned short *) s;
+#ifdef CONFIG_PCI
+extern int pci_console_probe(void);
- count /= 2;
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
- while (count) {
- count--;
- *addr++ = c;
- }
- return;
- }
- if ((unsigned long) addr + count > video_mem_term ||
- (unsigned long) addr < video_mem_base) {
- if ((unsigned long) addr + count <= video_mem_term ||
- (unsigned long) addr > video_mem_base) {
- while (count) {
- count--;
- *addr++ = c;
- }
- return;
- } else {
- while (count) {
- count--;
- scr_writew(c, addr++);
- }
- }
-#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
- } else if (do_accel) {
- int yoff = (addr - (unsigned short *)video_mem_base) / video_num_columns;
- int xoff = (addr - (unsigned short *)video_mem_base) % video_num_columns;
- unsigned short * last = addr;
-
- while (count) {
- count--;
- if (*addr != c) {
- if (xoff == video_num_columns) {
- if (last != addr)
- GX_SETW
- xoff = 0;
- yoff++;
- last = addr;
- }
- *addr++ = c;
- xoff++;
- } else {
- if (last != addr)
- GX_SETW
- if (xoff == video_num_columns) {
- xoff = 0;
- yoff++;
- }
- addr++;
- xoff++;
- last = addr;
- }
- }
- if (last != addr)
- GX_SETW
- } else {
- while (count) {
- count--;
- if (*addr != c) {
- sun_blitc(c, (unsigned long)addr);
- *addr++ = c;
- } else
- addr++;
- }
- }
-}
-
-void memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+__initfunc(unsigned long pci_console_init(unsigned long memory_start))
{
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
- memcpy(to, from, count);
- return;
- }
- if ((unsigned long) to + count > video_mem_term ||
- (unsigned long) to < video_mem_base) {
- if ((unsigned long) to + count <= video_mem_term ||
- (unsigned long) to > video_mem_base)
- memcpy(to, from, count);
- else {
- count /= 2;
- while (count) {
- count--;
- scr_writew(scr_readw(from++), to++);
- }
- }
-#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
- } else if (do_accel) {
- int yoff = (to - (unsigned short *)video_mem_base) / video_num_columns;
- int xoff = (to - (unsigned short *)video_mem_base) % video_num_columns;
- unsigned short * last = to;
-
- count /= 2;
- while (count) {
- count--;
- if (*to != *from) {
- if (xoff == video_num_columns) {
- if (last != to)
- GX_CPYW
- xoff = 0;
- yoff++;
- last = to;
- } else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
- GX_CPYW
- last = to;
- }
- *to++ = *from++;
- xoff++;
- } else {
- if (last != to)
- GX_CPYW
- if (xoff == video_num_columns) {
- xoff = 0;
- yoff++;
- }
- to++;
- xoff++;
- last = to;
- from++;
- }
- }
- if (last != to)
- GX_CPYW
- } else {
- count /= 2;
- while (count) {
- count--;
- if (*to != *from) {
- sun_blitc(*from, (unsigned long)to);
- *to++ = *from++;
- } else {
- from++;
- to++;
- }
- }
- }
-}
+ /* Nothing to do in this case. */
+ if(serial_console)
+ return memory_start;
-#undef pos
-int
-sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
-{
- int op;
- int i, bytes = 0;
- struct fbcursor f;
- char red[2], green[2], blue[2];
-
- if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
- return -EFAULT;
- op = f.set;
- if (op & FB_CUR_SETSHAPE){
- if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
- return -EINVAL;
- if ((uint) f.size.fby > fb->cursor.hwsize.fby)
- return -EINVAL;
- if (f.size.fbx > 32)
- bytes = f.size.fby << 3;
- else
- bytes = f.size.fby << 2;
- }
- if (op & FB_CUR_SETCMAP){
- if (f.cmap.index || f.cmap.count != 2)
- return -EINVAL;
- if (copy_from_user (red, f.cmap.red, 2) ||
- copy_from_user (green, f.cmap.green, 2) ||
- copy_from_user (blue, f.cmap.blue, 2))
- return -EFAULT;
- }
- if (op & FB_CUR_SETCMAP)
- (*fb->setcursormap) (fb, red, green, blue);
- if (op & FB_CUR_SETSHAPE){
- uint u;
-
- fb->cursor.size = f.size;
- memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
- if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
- copy_from_user (fb->cursor.bits [1], f.image, bytes))
- return -EFAULT;
- if (f.size.fbx <= 32) {
- u = ~(0xffffffff >> f.size.fbx);
- for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
- fb->cursor.bits [0][i] &= u;
- fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
- }
- } else {
- u = ~(0xffffffff >> (f.size.fbx - 32));
- for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
- fb->cursor.bits [0][2*i+1] &= u;
- fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
- fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
- }
- }
- (*fb->setcurshape) (fb);
- }
- if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
- if (op & FB_CUR_SETCUR)
- fb->cursor.enable = f.enable;
- if (op & FB_CUR_SETPOS)
- fb->cursor.cpos = f.pos;
- if (op & FB_CUR_SETHOT)
- fb->cursor.chot = f.hot;
- (*fb->setcursor) (fb);
+ if(pci_console_probe()) {
+ prom_printf("Could not probe PCI console, bailing out...\n");
+ prom_halt();
}
- return 0;
-}
-static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+ memory_start = finish_console_init(memory_start);
-void
-sun_hw_hide_cursor (void)
-{
- fbinfo[0].cursor.enable = 0;
- (*fbinfo[0].setcursor)(&fbinfo[0]);
- sun_hw_cursor_shown = 0;
-}
+ con_type_init_finish();
+ register_console(&vt_console_driver);
-void
-sun_hw_set_cursor (int xoff, int yoff)
-{
- if (!sun_hw_cursor_shown) {
- fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
- fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
- fbinfo[0].cursor.chot.fbx = 0;
- fbinfo[0].cursor.chot.fby = 0;
- fbinfo[0].cursor.enable = 1;
- memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
- fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
- fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
- fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
- fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
- (*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
- (*fbinfo[0].setcurshape) (&fbinfo[0]);
- sun_hw_cursor_shown = 1;
- }
- fbinfo[0].cursor.cpos.fbx = xoff;
- fbinfo[0].cursor.cpos.fby = yoff;
- (*fbinfo[0].setcursor)(&fbinfo[0]);
+ return memory_start;
}
+#endif
diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c
index 885581975..2ebd9cc5a 100644
--- a/drivers/sbus/char/sunfb.c
+++ b/drivers/sbus/char/sunfb.c
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $
+/* $Id: sunfb.c,v 1.28 1997/08/22 15:55:23 jj Exp $
* sunfb.c: Sun generic frame buffer support.
*
* Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -42,7 +42,6 @@
#include "fb.h"
extern void set_other_palette (int);
-extern void sun_clear_fb(int);
extern void set_cursor (int);
#define FB_SETUP(err) \
@@ -197,9 +196,9 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
if (fb == fbinfo) {
if (vt_cons[fg_console]->vc_mode == KD_TEXT)
return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
- sun_hw_cursor_shown = 0; /* Forget state of our text cursor */
+ sbus_hw_cursor_shown = 0; /* Forget state of our text cursor */
}
- return sun_hw_scursor ((struct fbcursor *) arg, fb);
+ return sbus_hw_scursor ((struct fbcursor *) arg, fb);
case FBIOSCURPOS:
if (!fb->setcursor) return -EINVAL;
@@ -242,8 +241,11 @@ fb_close (struct inode * inode, struct file *filp)
vt_cons [fb->vtconsole]->vc_mode = KD_TEXT;
/* Leaving graphics mode, turn off the cursor */
- if (fb->mmaped)
- sun_clear_fb (minor);
+ if (fb->mmaped) {
+ fb->clear_fb (minor);
+ if (!minor && suncons_ops.clear_margin)
+ suncons_ops.clear_margin();
+ }
cursor.set = FB_CUR_SETCUR;
cursor.enable = 0;
diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c
index 54b09cd76..dd4d933d9 100644
--- a/drivers/sbus/char/sunkbd.c
+++ b/drivers/sbus/char/sunkbd.c
@@ -1,10 +1,14 @@
/* keyboard.c: Sun keyboard driver.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ *
* Added vuid event generation and /dev/kbd device for SunOS
* compatibility - Miguel (miguel@nuclecu.unam.mx)
+ *
+ * Added PCI 8042 controller support -DaveM
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
@@ -16,11 +20,11 @@
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/random.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <asm/kbio.h>
#include <asm/vuid_event.h>
-#include <asm/delay.h>
#include <asm/bitops.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
@@ -29,6 +33,15 @@
#include <linux/kbd_diacr.h>
#include <linux/vt_kern.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/pbm.h>
+#include <asm/ebus.h>
+#endif
+
+#include "sunkbd.h"
+
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
/* Define this one if you are making a new frame buffer driver */
@@ -63,6 +76,7 @@ extern void reset_vc(unsigned int new_console);
extern void scrollback(int);
extern void scrollfront(int);
+struct l1a_kbd_state l1a_state = { 0, 0 };
unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
unsigned char aux_device_present = 0x00; /* To make kernel/ksyms.c happy */
@@ -208,7 +222,23 @@ static unsigned char sunkbd_clickp;
#define KEY_ALT 0x86
#define KEY_L1 0x87
-extern void kbd_put_char(unsigned char ch);
+/* Do to kbd_init() being called before rs_init(), and kbd_init() doing:
+ *
+ * init_bh(KEYBOARD_BH, kbd_bh);
+ * mark_bh(KEYBOARD_BH);
+ *
+ * this might well be called before some driver has claimed interest in
+ * handling the keyboard input/output. So we need to assign an initial nop.
+ *
+ * Otherwise this would lead to the following (DaveM might want to look at):
+ *
+ * sparc64_dtlb_refbit_catch(),
+ * do_sparc64_fault(),
+ * kernel NULL pointer dereference at do_sparc64_fault + 0x2c0 ;-(
+ */
+static void nop_kbd_put_char(unsigned char c) { }
+static void (*kbd_put_char)(unsigned char) = nop_kbd_put_char;
+
static inline void send_cmd(unsigned char c)
{
kbd_put_char(c);
@@ -1425,13 +1455,18 @@ file_operations kbd_fops =
NULL, /* revalidate */
};
-__initfunc(void keyboard_zsinit(void))
+__initfunc(void keyboard_zsinit(void (*put_char)(unsigned char)))
{
int timeout = 0;
+ kbd_put_char = put_char;
+ if (!kbd_put_char)
+ panic("keyboard_zsinit: no put_char parameter");
+
/* Test out the leds */
sunkbd_type = 255;
send_cmd(SKBDCMD_RESET);
+ send_cmd(SKBDCMD_RESET);
while((sunkbd_type==255) && timeout < 500000) {
udelay(100);
timeout += 20;
diff --git a/drivers/sbus/char/sunkbd.h b/drivers/sbus/char/sunkbd.h
new file mode 100644
index 000000000..151c77457
--- /dev/null
+++ b/drivers/sbus/char/sunkbd.h
@@ -0,0 +1,27 @@
+/* $Id: sunkbd.h,v 1.1 1997/08/28 02:23:34 ecd Exp $
+ * sunkbd.h: Defines needed by SUN Keyboard drivers
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNKBD_H
+#define _SPARC_SUNKBD_H 1
+
+/* Keyboard defines for L1-A processing... */
+#define SUNKBD_RESET 0xff
+#define SUNKBD_L1 0x01
+#define SUNKBD_UP 0x80
+#define SUNKBD_A 0x4d
+
+struct l1a_kbd_state {
+ int kbd_id;
+ int l1_down;
+};
+
+extern struct l1a_kbd_state l1a_state;
+
+extern void keyboard_zsinit(void (*kbd_put_char)(unsigned char));
+extern void sunkbd_inchar(unsigned char, struct pt_regs *);
+extern void batten_down_hatches(void);
+
+#endif /* !(_SPARC_SUNKBD_H) */
diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c
index 789a332c0..9f5bbb5b6 100644
--- a/drivers/sbus/char/sunmouse.c
+++ b/drivers/sbus/char/sunmouse.c
@@ -1,6 +1,6 @@
/* sunmouse.c: Sun mouse driver for the Sparc
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
*
* Parts based on the psaux.c driver written by:
@@ -10,6 +10,7 @@
* Jan/5/96 Added VUID support, sigio support - miguel.
* Mar/5/96 Added proper mouse stream support - miguel.
* Sep/96 Allow more than one reader -miguel.
+ * Aug/97 Added PCI 8042 controller support -DaveM
*/
/* The mouse is run off of one of the Zilog serial ports. On
@@ -37,6 +38,7 @@
* FIXME: We need to support more than one mouse.
* */
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
@@ -90,7 +92,7 @@ static struct sun_mouse sunmouse;
extern void mouse_put_char(char ch);
-/* #define SMOUSE_DEBUG */
+#undef SMOUSE_DEBUG
static void
push_event (Firm_event *ev)
@@ -125,6 +127,9 @@ push_char (char c)
int next = (sunmouse.head + 1) % STREAM_SIZE;
if (next != sunmouse.tail){
+#ifdef SMOUSE_DEBUG
+ printk("P<%02x>\n", (unsigned char)c);
+#endif
sunmouse.queue.stream [sunmouse.head] = c;
sunmouse.head = next;
}
@@ -142,14 +147,14 @@ static int mouse_baud = 4800; /* Initial rate set by zilog driver. */
/* Change the baud rate after receiving too many "bogon bytes". */
void sun_mouse_change_baud(void)
{
- extern void zs_change_mouse_baud(int newbaud);
+ extern void rs_change_mouse_baud(int newbaud);
if(mouse_baud == 1200)
mouse_baud = 4800;
else
mouse_baud = 1200;
- zs_change_mouse_baud(mouse_baud);
+ rs_change_mouse_baud(mouse_baud);
mouse_baud_changing = 1;
}
@@ -373,9 +378,25 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer,
char *p = buffer, *end = buffer+count;
while (p < end && !queue_empty ()){
- copy_to_user_ret((Firm_event *)p, get_from_queue(),
- sizeof(Firm_event), -EFAULT);
- p += sizeof (Firm_event);
+#ifdef CONFIG_SPARC32_COMPAT
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ Firm_event *q = get_from_queue();
+
+ copy_to_user_ret((Firm_event *)p, q,
+ sizeof(Firm_event)-sizeof(struct timeval),
+ -EFAULT);
+ p += sizeof(Firm_event)-sizeof(struct timeval);
+ __put_user_ret(q->time.tv_sec, (u32 *)p, -EFAULT);
+ p += sizeof(u32);
+ __put_user_ret(q->time.tv_usec, (u32 *)p, -EFAULT);
+ p += sizeof(u32);
+ } else
+#endif
+ {
+ copy_to_user_ret((Firm_event *)p, get_from_queue(),
+ sizeof(Firm_event), -EFAULT);
+ p += sizeof (Firm_event);
+ }
}
sunmouse.ready = !queue_empty ();
inode->i_atime = CURRENT_TIME;
diff --git a/drivers/sbus/char/sunmouse.h b/drivers/sbus/char/sunmouse.h
new file mode 100644
index 000000000..78d9eb73c
--- /dev/null
+++ b/drivers/sbus/char/sunmouse.h
@@ -0,0 +1,13 @@
+/* $Id: sunmouse.h,v 1.1 1997/08/28 02:23:38 ecd Exp $
+ * sunmouse.h: Interface to the SUN mouse driver.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNMOUSE_H
+#define _SPARC_SUNMOUSE_H 1
+
+extern void sun_mouse_zsinit(void);
+extern void sun_mouse_inbyte(unsigned char);
+
+#endif /* !(_SPARC_SUNMOUSE_H) */
diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c
index c0e6ab71d..da90d5ade 100644
--- a/drivers/sbus/char/sunserial.c
+++ b/drivers/sbus/char/sunserial.c
@@ -1,2668 +1,134 @@
-/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $
- * serial.c: Serial port driver for the Sparc.
+/* $Id: sunserial.c,v 1.50 1997/09/03 11:54:59 ecd Exp $
+ * serial.c: Serial port driver infrastructure for the Sparc.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
+#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/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/keyboard.h>
-#include <linux/console.h>
-#include <linux/init.h>
+#include <linux/serial.h>
-#include <asm/io.h>
-#include <asm/irq.h>
#include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-#include <asm/delay.h>
-#include <asm/kdebug.h>
#include "sunserial.h"
-static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
-#define NUM_SERIAL num_serial
-#define NUM_CHANNELS (NUM_SERIAL * 2)
-
-#define KEYBOARD_LINE 0x2
-#define MOUSE_LINE 0x3
-
-struct sun_zslayout **zs_chips;
-struct sun_zschannel **zs_channels;
-struct sun_zschannel *zs_conschan;
-struct sun_zschannel *zs_mousechan;
-struct sun_zschannel *zs_kbdchan;
-struct sun_zschannel *zs_kgdbchan;
-int *zs_nodes;
-
-struct sun_serial *zs_soft;
-struct sun_serial *zs_chain; /* IRQ servicing chain */
-int zilog_irq;
-
-struct tty_struct *zs_ttys;
-/** struct tty_struct *zs_constty; **/
-
-/* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-static struct l1a_kbd_state l1a_state = { 0, 0 };
-struct sun_serial *zs_consinfo = 0;
-
-/* Keyboard defines for L1-A processing... */
-#define SUNKBD_RESET 0xff
-#define SUNKBD_L1 0x01
-#define SUNKBD_UP 0x80
-#define SUNKBD_A 0x4d
-
-extern void sunkbd_inchar(unsigned char ch, struct pt_regs *regs);
-extern void sun_mouse_inbyte(unsigned char byte);
-
-static unsigned char kgdb_regs[16] = {
- 0, 0, 0, /* write 0, 1, 2 */
- (Rx8 | RxENAB), /* write 3 */
- (X16CLK | SB1 | PAR_EVEN), /* write 4 */
- (DTR | Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 0, 0, /* BRG time constant, write 12 + 13 */
- (BRSRC | BRENAB), /* write 14 */
- (DCDIE) /* write 15 */
-};
-
-static unsigned char zscons_regs[16] = {
- 0, /* write 0 */
- (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
- 0, /* write 2 */
- (Rx8 | RxENAB), /* write 3 */
- (X16CLK), /* write 4 */
- (DTR | Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV | MIE), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 0, 0, /* BRG time constant, write 12 + 13 */
- (BRSRC | BRENAB), /* write 14 */
- (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
-};
-
-#define ZS_CLOCK 4915200 /* Zilog input clock rate */
-
-DECLARE_TASK_QUEUE(tq_serial);
-
-struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL 1
-#define SERIAL_TYPE_CALLOUT 2
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void change_speed(struct sun_serial *info);
-
-static struct tty_struct **serial_table;
-static struct termios **serial_termios;
-static struct termios **serial_termios_locked;
-
-#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 memcpy_fromfs 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[4096]; /* This is cheating */
-static struct semaphore tmp_buf_sem = MUTEX;
-
-static inline int serial_paranoia_check(struct sun_serial *info,
- dev_t device, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- static const char *badmagic =
- "Warning: bad magic number for serial struct (%d, %d) in %s\n";
- static const char *badinfo =
- "Warning: null sun_serial for (%d, %d) in %s\n";
-
- if (!info) {
- printk(badinfo, MAJOR(device), MINOR(device), routine);
- return 1;
- }
- if (info->magic != SERIAL_MAGIC) {
- printk(badmagic, MAJOR(device), MINOR(device), routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 76800, 0 };
-
-/*
- * Reading and writing Zilog8530 registers. The delays are to make this
- * driver work on the Sun4 which needs a settling delay after each chip
- * register access, other machines handle this in hardware via auxiliary
- * flip-flops which implement the settle time we do in software.
- */
-static inline unsigned char read_zsreg(struct sun_zschannel *channel,
- unsigned char reg)
-{
- unsigned char retval;
-
- channel->control = reg;
- udelay(5);
- retval = channel->control;
- udelay(5);
- return retval;
-}
-
-static inline void write_zsreg(struct sun_zschannel *channel,
- unsigned char reg, unsigned char value)
-{
- channel->control = reg;
- udelay(5);
- channel->control = value;
- udelay(5);
-}
-
-static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
-{
- unsigned long flags;
- struct sun_zschannel *channel = info->zs_channel;
- unsigned char stat;
- int i;
-
- for (i = 0; i < 1000; i++) {
- stat = read_zsreg(channel, R1);
- if (stat & ALL_SNT)
- break;
- udelay(100);
- }
- write_zsreg(channel, R3, 0);
- ZS_CLEARSTAT(channel);
- ZS_CLEARERR(channel);
- ZS_CLEARFIFO(channel);
-
- /* Load 'em up */
- save_flags(flags); cli();
- if (info->channelA)
- write_zsreg(channel, R9, CHRA);
- else
- write_zsreg(channel, R9, CHRB);
- udelay(20); /* wait for some old sun4's */
- write_zsreg(channel, R4, regs[R4]);
- write_zsreg(channel, R3, regs[R3] & ~RxENAB);
- write_zsreg(channel, R5, regs[R5] & ~TxENAB);
- write_zsreg(channel, R9, regs[R9] & ~MIE);
- write_zsreg(channel, R10, regs[R10]);
- write_zsreg(channel, R11, regs[R11]);
- write_zsreg(channel, R12, regs[R12]);
- write_zsreg(channel, R13, regs[R13]);
- write_zsreg(channel, R14, regs[R14] & ~BRENAB);
- write_zsreg(channel, R14, regs[R14]);
- write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
- write_zsreg(channel, R3, regs[R3]);
- write_zsreg(channel, R5, regs[R5]);
- write_zsreg(channel, R15, regs[R15]);
- write_zsreg(channel, R0, RES_EXT_INT);
- write_zsreg(channel, R0, ERR_RES);
- write_zsreg(channel, R1, regs[R1]);
- write_zsreg(channel, R9, regs[R9]);
- restore_flags(flags);
-}
-
-static inline void zs_put_char(struct sun_zschannel *channel, char ch)
+static void nop_rs_cons_hook(int chip, int out, int line)
{
- int loops = 0;
-
- while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
- loops++;
- udelay(5);
- }
- channel->data = ch;
- udelay(5);
+ printk("Oops: %s called\n", __FUNCTION__);
}
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct sun_serial *ss, int set)
+static void nop_rs_kgdb_hook(int channel)
{
- unsigned long flags;
-
- save_flags(flags); cli();
- if(set) {
- ss->curregs[5] |= (RTS | DTR);
- write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
- } else {
- ss->curregs[5] &= ~(RTS | DTR);
- write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
- }
- restore_flags(flags);
- return;
+ printk("Oops: %s called\n", __FUNCTION__);
}
-static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
+static void nop_rs_change_mouse_baud(int baud)
{
- int brg;
-
- if(intson) {
- kgdb_regs[R1] = INT_ALL_Rx;
- kgdb_regs[R9] |= MIE;
- } else {
- kgdb_regs[R1] = 0;
- kgdb_regs[R9] &= ~MIE;
- }
- brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
- kgdb_regs[R12] = (brg & 255);
- kgdb_regs[R13] = ((brg >> 8) & 255);
- load_zsregs(ss, kgdb_regs);
+ printk("Oops: %s called\n", __FUNCTION__);
}
-/*
- * ------------------------------------------------------------
- * 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)
+static int nop_rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_stop"))
- return;
-
- save_flags(flags); cli();
- if (info->curregs[5] & TxENAB) {
- info->curregs[5] &= ~TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
- restore_flags(flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_start"))
- return;
-
- save_flags(flags); cli();
- if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
- restore_flags(flags);
-}
-
-/* Drop into either the boot monitor or kadb upon receiving a break
- * from keyboard/console input.
- */
-static void batten_down_hatches(void)
-{
- /* If we are doing kadb, we call the debugger
- * else we just drop into the boot monitor.
- * Note that we must flush the user windows
- * first before giving up control.
- */
- printk("\n");
- flush_user_windows();
-#ifndef __sparc_v9__
- if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
- (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
- sp_enter_debugger();
- else
-#endif
- prom_cmdline();
-
- /* XXX We want to notify the keyboard driver that all
- * XXX keys are in the up state or else weird things
- * XXX happen...
- */
-
- return;
-}
-
-
-/*
- * ----------------------------------------------------------------------
- *
- * 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 sun_serial *info,
- int event)
-{
- info->event |= 1 << event;
- queue_task(&info->tqueue, &tq_serial);
- mark_bh(SERIAL_BH);
-}
-
-#ifndef __sparc_v9__
-extern void breakpoint(void); /* For the KGDB frame character */
-#endif
-
-static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- do {
- ch = (info->zs_channel->data) & info->parity_mask;
- udelay(5);
-
- /* If this is the console keyboard, we need to handle
- * L1-A's here.
- */
- if(info->cons_keyb) {
- if(ch == SUNKBD_RESET) {
- l1a_state.kbd_id = 1;
- l1a_state.l1_down = 0;
- } else if(l1a_state.kbd_id) {
- l1a_state.kbd_id = 0;
- } else if(ch == SUNKBD_L1) {
- l1a_state.l1_down = 1;
- } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
- l1a_state.l1_down = 0;
- } else if(ch == SUNKBD_A && l1a_state.l1_down) {
- /* whee... */
- batten_down_hatches();
- /* Continue execution... */
- l1a_state.l1_down = 0;
- l1a_state.kbd_id = 0;
- return;
- }
- sunkbd_inchar(ch, regs);
- return;
- }
- if(info->cons_mouse) {
- sun_mouse_inbyte(ch);
- return;
- }
- if(info->is_cons) {
- if(ch==0) {
- /* whee, break received */
- batten_down_hatches();
- /* Continue execution... */
- return;
-#if 0
- } else if (ch == 1) {
- show_state();
- return;
- } else if (ch == 2) {
- show_buffers();
- return;
-#endif
- }
- /* It is a 'keyboard interrupt' ;-) */
- wake_up(&keypress_wait);
- }
-#ifndef __sparc_v9__
- /* Look for kgdb 'stop' character, consult the gdb
- * documentation for remote target debugging and
- * arch/sparc/kernel/sparc-stub.c to see how all this works.
- */
- if((info->kgdb_channel) && (ch =='\003')) {
- breakpoint();
- return;
- }
-#endif
- if(!tty)
- return;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- break;
-
- tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = 0;
- *tty->flip.char_buf_ptr++ = ch;
-
- /* Check if we have another character... */
- stat = info->zs_channel->control;
- udelay(5);
- if (!(stat & Rx_CH_AV))
- break;
-
- /* ... and see if it is clean. */
- stat = read_zsreg(info->zs_channel, R1);
- } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
-
- queue_task(&tty->flip.tqueue, &tq_timer);
-}
-
-static _INLINE_ void transmit_chars(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
-
- if (info->x_char) {
- /* Send next char */
- zs_put_char(info->zs_channel, info->x_char);
- info->x_char = 0;
- return;
- }
-
- if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
- /* That's peculiar... */
- info->zs_channel->control = RES_Tx_P;
- udelay(5);
- return;
- }
-
- /* Send char */
- zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
-
- if (info->xmit_cnt < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
- if(info->xmit_cnt <= 0) {
- info->zs_channel->control = RES_Tx_P;
- udelay(5);
- }
-}
-
-static _INLINE_ void status_handle(struct sun_serial *info)
-{
- unsigned char status;
-
- /* Get status from Read Register 0 */
- status = info->zs_channel->control;
- udelay(5);
- /* Clear status condition... */
- info->zs_channel->control = RES_EXT_INT;
- udelay(5);
-
-#if 0
- if(status & DCD) {
- if((info->tty->termios->c_cflag & CRTSCTS) &&
- ((info->curregs[3] & AUTO_ENAB)==0)) {
- info->curregs[3] |= AUTO_ENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- }
- } else {
- if((info->curregs[3] & AUTO_ENAB)) {
- info->curregs[3] &= ~AUTO_ENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- }
- }
-#endif
- /* Whee, if this is console input and this is a
- * 'break asserted' status change interrupt, call
- * the boot prom.
- */
- if((status & BRK_ABRT) && info->break_abort)
- batten_down_hatches();
-
- /* XXX Whee, put in a buffer somewhere, the status information
- * XXX whee whee whee... Where does the information go...
- */
- return;
-}
-
-static _INLINE_ void special_receive(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- stat = read_zsreg(info->zs_channel, R1);
- if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
- ch = info->zs_channel->data;
- udelay(5);
- }
-
- if (!tty)
- goto clear;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- goto done;
-
- tty->flip.count++;
- if(stat & PAR_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if(stat & Rx_OVR)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else if(stat & CRC_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
- queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
- info->zs_channel->control = ERR_RES;
- udelay(5);
-}
-
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
- struct sun_serial *info;
- unsigned char zs_intreg;
- int i;
-
- info = (struct sun_serial *)dev_id;
- for (i = 0; i < NUM_SERIAL; i++) {
- zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
- zs_intreg &= STATUS_MASK;
-
- /* NOTE: The read register 2, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the B
- * channel and is only valid when read from channel B.
- * When read from channel A, read register 2 contains
- * the value written to write register 2.
- */
-
- /* Channel A -- /dev/ttya or /dev/kbd, could be the console */
- if (zs_intreg == CHA_Rx_AVAIL) {
- receive_chars(info, regs);
- return;
- }
- if(zs_intreg == CHA_Tx_EMPTY) {
- transmit_chars(info);
- return;
- }
- if (zs_intreg == CHA_EXT_STAT) {
- status_handle(info);
- return;
- }
- if (zs_intreg == CHA_SPECIAL) {
- special_receive(info);
- return;
- }
-
- /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
- if(zs_intreg == CHB_Rx_AVAIL) {
- receive_chars(info->zs_next, regs);
- return;
- }
- if(zs_intreg == CHB_Tx_EMPTY) {
- transmit_chars(info->zs_next);
- return;
- }
- if (zs_intreg == CHB_EXT_STAT) {
- status_handle(info->zs_next);
- return;
- }
-
- /* NOTE: The default value for the IRQ status in read register
- * 2 in channel B is CHB_SPECIAL, so we need to look at
- * read register 3 in channel A to check if this is a
- * real interrupt, or just the default value.
- * Yes... broken hardware...
- */
-
- zs_intreg = read_zsreg(info->zs_channel, 3);
- if (zs_intreg & CHBRxIP) {
- special_receive(info->zs_next);
- return;
- }
- info = info->zs_next->zs_next;
- }
-}
-
-/*
- * -------------------------------------------------------------------
- * 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 sun_serial *info = (struct sun_serial *) 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);
- }
-}
-
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- *
- */
-static void do_serial_hangup(void *private_)
-{
- struct sun_serial *info = (struct sun_serial *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-#ifdef SERIAL_DEBUG_OPEN
- printk("do_serial_hangup<%p: tty-%d\n",
- __builtin_return_address(0), info->line);
-#endif
-
- tty_hangup(tty);
-}
-
-
-/*
- * This subroutine is called when the RS_TIMER goes off. It is used
- * by the serial driver to handle ports that do not have an interrupt
- * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
- */
-
-static void rs_timer(void)
-{
- printk("rs_timer called\n");
- prom_halt();
- return;
-}
-
-static int startup(struct sun_serial * info)
-{
- unsigned long flags;
-
- if (info->flags & ZILOG_INITIALIZED)
- return 0;
-
- if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
- if (!info->xmit_buf)
- return -ENOMEM;
- }
-
- save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
-#endif
-
- /*
- * Clear the FIFO buffers and disable them
- * (they will be reenabled in change_speed())
- */
- ZS_CLEARFIFO(info->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- info->zs_channel->control = ERR_RES;
- udelay(5);
- info->zs_channel->control = RES_H_IUS;
- udelay(5);
-
- /*
- * Now, initialize the Zilog
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
- (EXT_INT_ENAB | INT_ALL_Rx);
- info->curregs[3] |= (RxENAB | Rx8);
- /* We enable Tx interrupts as needed. */
- info->curregs[5] |= (TxENAB | Tx8);
- info->curregs[9] |= (NV | MIE);
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- write_zsreg(info->zs_channel, 9, info->curregs[9]);
-
- /*
- * And clear the interrupt registers again for luck.
- */
- info->zs_channel->control = ERR_RES;
- udelay(5);
- info->zs_channel->control = RES_H_IUS;
- udelay(5);
-
- 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...
- */
-#if 0 /* Works well and stops the machine. */
- timer_table[RS_TIMER].expires = jiffies + 2;
- timer_active |= 1 << RS_TIMER;
-#endif
-
- /*
- * and set the speed of the serial port
- */
- change_speed(info);
-
- info->flags |= ZILOG_INITIALIZED;
- restore_flags(flags);
+ printk("Oops: %s called\n", __FUNCTION__);
return 0;
}
-/*
- * 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 sun_serial * info)
-{
- unsigned long flags;
-
- if (!(info->flags & ZILOG_INITIALIZED))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
-#endif
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- if (info->xmit_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
- }
-
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
-
- info->flags &= ~ZILOG_INITIALIZED;
- restore_flags(flags);
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct sun_serial *info)
-{
- unsigned short port;
- unsigned cflag;
- int quot = 0;
- int i;
- int brg;
-
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
- if (!(port = info->port))
- return;
- i = cflag & CBAUD;
- if (cflag & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i != 5)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i = 16;
- }
- if (i == 15) {
- if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
- i += 1;
- if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
- quot = info->custom_divisor;
- }
- if (quot) {
- info->zs_baud = info->baud_base / quot;
- info->clk_divisor = 16;
-
- info->curregs[4] = X16CLK;
- info->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
- info->curregs[12] = (brg & 255);
- info->curregs[13] = ((brg >> 8) & 255);
- info->curregs[14] = BRSRC | BRENAB;
- zs_rtsdtr(info, 1);
- } else if (baud_table[i]) {
- info->zs_baud = baud_table[i];
- info->clk_divisor = 16;
-
- info->curregs[4] = X16CLK;
- info->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
- info->curregs[12] = (brg & 255);
- info->curregs[13] = ((brg >> 8) & 255);
- info->curregs[14] = BRSRC | BRENAB;
- zs_rtsdtr(info, 1);
- } else {
- zs_rtsdtr(info, 0);
- return;
- }
-
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx5;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx5;
- info->parity_mask = 0x1f;
- break;
- case CS6:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx6;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx6;
- info->parity_mask = 0x3f;
- break;
- case CS7:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx7;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx7;
- info->parity_mask = 0x7f;
- break;
- case CS8:
- default: /* defaults to 8 bits */
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx8;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx8;
- info->parity_mask = 0xff;
- break;
- }
- info->curregs[4] &= ~(0x0c);
- if (cflag & CSTOPB) {
- info->curregs[4] |= SB2;
- } else {
- info->curregs[4] |= SB1;
- }
- if (cflag & PARENB) {
- info->curregs[4] |= PAR_ENAB;
- } else {
- info->curregs[4] &= ~PAR_ENAB;
- }
- if (!(cflag & PARODD)) {
- info->curregs[4] |= PAR_EVEN;
- } else {
- info->curregs[4] &= ~PAR_EVEN;
- }
-
- /* Load up the new values */
- load_zsregs(info, info->curregs);
-
- return;
-}
-
-/* This is for mouse/keyboard output.
- * XXX mouse output??? can we send it commands??? XXX
- */
-void kbd_put_char(unsigned char ch)
-{
- struct sun_zschannel *chan = zs_kbdchan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-
-void mouse_put_char(char ch)
-{
- struct sun_zschannel *chan = zs_mousechan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-
-
-/* This is for console output over ttya/ttyb */
-static void rs_put_char(char ch)
-{
- struct sun_zschannel *chan = zs_conschan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
- struct sun_zschannel *chan = zs_kgdbchan;
-
- while((chan->control & Tx_BUF_EMP)==0)
- udelay(5);
- chan->data = kgdb_char;
-}
-
-char getDebugChar(void)
-{
- struct sun_zschannel *chan = zs_kgdbchan;
-
- while((chan->control & Rx_CH_AV)==0)
- barrier();
- return chan->data;
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void rs_fair_output(void)
-{
- int left; /* Output no more than that */
- unsigned long flags;
- struct sun_serial *info = zs_consinfo;
- char c;
-
- if (info == 0) return;
- if (info->xmit_buf == 0) return;
-
- save_flags(flags); cli();
- left = info->xmit_cnt;
- while (left != 0) {
- c = info->xmit_buf[info->xmit_tail];
- info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- restore_flags(flags);
-
- rs_put_char(c);
-
- cli();
- left = MIN(info->xmit_cnt, left-1);
- }
-
- /* Last character is being transmitted now (hopefully). */
- zs_conschan->control = RES_Tx_P;
- udelay(5);
-
- restore_flags(flags);
- return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *s, unsigned count)
-{
- int i;
-
- for (i = 0; i < count; i++, s++) {
- if(*s == '\n')
- rs_put_char('\r');
- rs_put_char(*s);
- }
-
- /* Comment this if you want to have a strict interrupt-driven output */
- rs_fair_output();
-}
-
-static void zs_console_wait_key(void)
-{
- sleep_on(&keypress_wait);
-}
-
-static int zs_console_device(void)
-{
- extern int serial_console;
-
- return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- return;
-
- if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- !info->xmit_buf)
- return;
-
- /* Enable transmitter */
- save_flags(flags); cli();
- info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
-
- /*
- * Send a first (bootstrapping) character. A best solution is
- * to call transmit_chars() here which handles output in a
- * generic way. Current transmit_chars() not only transmits,
- * but resets interrupts also what we do not desire here.
- * XXX Discuss with David.
- */
- zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
-
- restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c, total = 0;
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_write"))
- return 0;
-
- if (!info || !info->xmit_buf)
- return 0;
-
- save_flags(flags);
- while (1) {
- cli();
- c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- if (c <= 0)
- break;
-
- if (from_user) {
- down(&tmp_buf_sem);
- copy_from_user(tmp_buf, buf, c);
- 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);
- up(&tmp_buf_sem);
- } else
- 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;
- total += c;
- }
-
- if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
- /* Enable transmitter */
- info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
-#if 1
- if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
- zs_put_char(info->zs_channel,
- info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- }
-#endif
- restore_flags(flags);
- return total;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- int ret;
-
- if (serial_paranoia_check(info, tty->device, "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 sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
- return 0;
- return info->xmit_cnt;
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
- return;
- cli();
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-}
-
-/*
- * ------------------------------------------------------------
- * 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 sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("throttle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->device, "rs_throttle"))
- return;
-
- if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
-
- /* Turn off RTS line */
- cli();
- info->curregs[5] &= ~RTS;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
-}
-
-static void rs_unthrottle(struct tty_struct * tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
- return;
-
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- info->x_char = START_CHAR(tty);
- }
-
- /* Assert RTS line */
- cli();
- info->curregs[5] |= RTS;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct sun_serial * info,
- struct serial_struct * retinfo)
-{
- struct serial_struct tmp;
-
- if (!retinfo)
- return -EFAULT;
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = info->port;
- tmp.irq = info->irq;
- tmp.flags = info->flags;
- tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
- return 0;
-}
+struct sunserial_operations rs_ops = {
+ 0,
+ nop_rs_cons_hook,
+ nop_rs_kgdb_hook,
+ nop_rs_change_mouse_baud,
+ nop_rs_read_proc
+};
-static int set_serial_info(struct sun_serial * info,
- struct serial_struct * new_info)
+int rs_init(void)
{
- struct serial_struct new_serial;
- struct sun_serial old_info;
- int retval = 0;
-
- if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
- return -EFAULT;
- old_info = *info;
+ struct rs_initfunc *init;
+ int err = -ENODEV;
- if (!suser()) {
- if ((new_serial.baud_base != info->baud_base) ||
- (new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ZILOG_USR_MASK) !=
- (info->flags & ~ZILOG_USR_MASK)))
- return -EPERM;
- info->flags = ((info->flags & ~ZILOG_USR_MASK) |
- (new_serial.flags & ZILOG_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- goto check_and_exit;
+ init = rs_ops.rs_init;
+ while (init) {
+ err = init->rs_init();
+ init = init->next;
}
-
- if (info->count > 1)
- return -EBUSY;
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~ZILOG_FLAGS) |
- (new_serial.flags & ZILOG_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->type = new_serial.type;
- info->close_delay = new_serial.close_delay;
- info->closing_wait = new_serial.closing_wait;
-
-check_and_exit:
- retval = startup(info);
- return retval;
+ return err;
}
-/*
- * 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 sun_serial * info, unsigned int *value)
+void rs_cons_hook(int chip, int out, int line)
{
- unsigned char status;
-
- cli();
- status = info->zs_channel->control;
- sti();
- put_user_ret(status,value, -EFAULT);
- return 0;
+ rs_ops.rs_cons_hook(chip, out, line);
}
-static int get_modem_info(struct sun_serial * info, unsigned int *value)
+void rs_kgdb_hook(int channel)
{
- unsigned char status;
- unsigned int result;
-
- cli();
- status = info->zs_channel->control;
- sti();
- result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
- | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
- | ((status & DCD) ? TIOCM_CAR : 0)
- | ((status & SYNC) ? TIOCM_DSR : 0)
- | ((status & CTS) ? TIOCM_CTS : 0);
- put_user_ret(result, value, -EFAULT);
- return 0;
+ rs_ops.rs_kgdb_hook(channel);
}
-static int set_modem_info(struct sun_serial * info, unsigned int cmd,
- unsigned int *value)
+void rs_change_mouse_baud(int baud)
{
- unsigned int arg;
-
- get_user_ret(arg, value, -EFAULT);
- switch (cmd) {
- case TIOCMBIS:
- if (arg & TIOCM_RTS)
- info->curregs[5] |= RTS;
- if (arg & TIOCM_DTR)
- info->curregs[5] |= DTR;
- break;
- case TIOCMBIC:
- if (arg & TIOCM_RTS)
- info->curregs[5] &= ~RTS;
- if (arg & TIOCM_DTR)
- info->curregs[5] &= ~DTR;
- break;
- case TIOCMSET:
- info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
- | ((arg & TIOCM_RTS) ? RTS : 0)
- | ((arg & TIOCM_DTR) ? DTR : 0));
- break;
- default:
- return -EINVAL;
- }
- cli();
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
- return 0;
+ rs_ops.rs_change_mouse_baud(baud);
}
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break( struct sun_serial * info, int duration)
+int rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- cli();
- write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
- schedule();
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
+ return rs_ops.rs_read_proc(page, start, off, count, eof, data);
}
-static int rs_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
+int register_serial(struct serial_struct *req)
{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
- int retval;
-
- if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
- return -ENODEV;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- }
-
- switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (!arg)
- send_break(info, HZ/4); /* 1/4 second */
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- return 0;
- case TIOCGSOFTCAR:
- put_user_ret(C_CLOCAL(tty) ? 1 : 0,
- (unsigned long *) arg, -EFAULT);
- return 0;
- case TIOCSSOFTCAR:
- get_user_ret(arg, (unsigned long *) arg, -EFAULT);
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
- case TIOCMGET:
- return get_modem_info(info, (unsigned int *) arg);
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- return set_modem_info(info, cmd, (unsigned int *) arg);
- case TIOCGSERIAL:
- return get_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, (unsigned int *) arg);
-
- case TIOCSERGSTRUCT:
- copy_to_user_ret((struct sun_serial *) arg,
- info, sizeof(struct sun_serial), -EFAULT);
- return 0;
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
+ return -1;
}
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+void unregister_serial(int line)
{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (tty->termios->c_cflag == old_termios->c_cflag)
- return;
-
- change_speed(info);
-
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
- rs_start(tty);
- }
}
-/*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * ZILOG 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)
+void
+sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void))
{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
+ struct rs_initfunc *rs_init;
- if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
- return;
-
- save_flags(flags); cli();
-
- if (tty_hung_up_p(filp)) {
- restore_flags(flags);
- return;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close tty-%d, count = %d\n", info->line, info->count);
-#endif
- if ((tty->count == 1) && (info->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk("rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
- }
- if (--info->count < 0) {
- printk("rs_close: bad serial port count for ttys%d: %d\n",
- info->line, info->count);
- info->count = 0;
- }
- if (info->count) {
- restore_flags(flags);
- return;
- }
- info->flags |= ZILOG_CLOSING;
- /*
- * Save the termios structure, since this port may have
- * separate termios for callout and dialin.
- */
- if (info->flags & ZILOG_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
- if (info->flags & ZILOG_CALLOUT_ACTIVE)
- info->callout_termios = *tty->termios;
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->closing_wait != ZILOG_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.
- */
- /** if (!info->iscons) ... **/
- info->curregs[3] &= ~RxENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- info->curregs[1] &= ~(RxINT_MASK);
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- ZS_CLEARFIFO(info->zs_channel);
+ *memory_start = (*memory_start + 7) & ~(7);
+ rs_init = (struct rs_initfunc *) *memory_start;
+ *memory_start += sizeof(struct rs_initfunc);
- 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 (tty->ldisc.num != ldiscs[N_TTY].num) {
- if (tty->ldisc.close)
- (tty->ldisc.close)(tty);
- tty->ldisc = ldiscs[N_TTY];
- tty->termios->c_line = N_TTY;
- if (tty->ldisc.open)
- (tty->ldisc.open)(tty);
- }
- if (info->blocked_open) {
- if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + info->close_delay;
- schedule();
- }
- wake_up_interruptible(&info->open_wait);
- }
- info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
- ZILOG_CLOSING);
- wake_up_interruptible(&info->close_wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count);
-#endif
- restore_flags(flags);
+ rs_init->rs_init = init;
+ rs_init->next = rs_ops.rs_init;
+ rs_ops.rs_init = rs_init;
}
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void rs_hangup(struct tty_struct *tty)
-{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_hangup"))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_hangup<%p: tty-%d, count = %d bye\n",
- __builtin_return_address(0), info->line, info->count);
+extern int zs_probe(unsigned long *);
+#ifdef CONFIG_SAB82532
+extern int sab82532_probe(unsigned long *);
#endif
-
- rs_flush_buffer(tty);
- shutdown(info);
- info->event = 0;
- info->count = 0;
- info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
- info->tty = 0;
- wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
- struct sun_serial *info)
-{
- struct wait_queue wait = { current, NULL };
- int retval;
- int do_clocal = 0;
- unsigned char r0;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (info->flags & ZILOG_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ZILOG_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If this is a callout device, then just make sure the normal
- * device isn't being used.
- */
- if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
- if (info->flags & ZILOG_NORMAL_ACTIVE)
- return -EBUSY;
- if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
- (info->flags & ZILOG_SESSION_LOCKOUT) &&
- (info->session != current->session))
- return -EBUSY;
- if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
- (info->flags & ZILOG_PGRP_LOCKOUT) &&
- (info->pgrp != current->pgrp))
- return -EBUSY;
- info->flags |= ZILOG_CALLOUT_ACTIVE;
- return 0;
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- if (info->flags & ZILOG_CALLOUT_ACTIVE)
- return -EBUSY;
- info->flags |= ZILOG_NORMAL_ACTIVE;
- return 0;
- }
-
- if (info->flags & ZILOG_CALLOUT_ACTIVE) {
- if (info->normal_termios.c_cflag & CLOCAL)
- do_clocal = 1;
- } else {
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
- }
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready before block: ttys%d, count = %d\n",
- info->line, info->count);
+#ifdef __sparc_v9__
+extern int ps2kbd_probe(unsigned long *);
+extern int su_probe(unsigned long *);
#endif
- cli();
- if(!tty_hung_up_p(filp))
- info->count--;
- sti();
- info->blocked_open++;
- while (1) {
- cli();
- if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
- zs_rtsdtr(info, 1);
- sti();
- current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) ||
- !(info->flags & ZILOG_INITIALIZED)) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready hup-ed: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ZILOG_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
-
- cli();
- r0 = read_zsreg(info->zs_channel, R0);
- sti();
- if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
- !(info->flags & ZILOG_CLOSING) &&
- (do_clocal || (DCD & r0)))
- break;
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready blocking: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
- if (retval)
- return retval;
- info->flags |= ZILOG_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its ZILOG structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-int rs_open(struct tty_struct *tty, struct file * filp)
-{
- struct sun_serial *info;
- int retval, line;
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- /* The zilog lines for the mouse/keyboard must be
- * opened using their respective drivers.
- */
- if ((line < 0) || (line >= NUM_CHANNELS))
- return -ENODEV;
- if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
- return -ENODEV;
- info = zs_soft + line;
- /* Is the kgdb running over this line? */
- if (info->kgdb_channel)
- return -ENODEV;
- if (serial_paranoia_check(info, tty->device, "rs_open"))
- return -ENODEV;
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
- info->count);
-#endif
- if (info->tty != 0 && info->tty != tty) {
- /* Never happen? */
- printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
- return -EBUSY;
- }
- info->count++;
- tty->driver_data = info;
- info->tty = tty;
-
- /*
- * Start up serial port
- */
- retval = startup(info);
- if (retval)
- return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open returning after block_til_ready with %d\n",
- retval);
-#endif
- return retval;
- }
-
- if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
- if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
- *tty->termios = info->normal_termios;
- else
- *tty->termios = info->callout_termios;
- change_speed(info);
- }
-
- info->session = current->session;
- info->pgrp = current->pgrp;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttys%d successful...", info->line);
-#endif
- return 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-static void show_serial_version(void)
-{
- char *revision = "$Revision: 1.2 $";
- char *version, *p;
- version = strchr(revision, ' ');
- p = strchr(++version, ' ');
- *p = '\0';
- printk("Sparc Zilog8530 serial driver version %s\n", version);
- *p = ' ';
-}
-
-/* Probe the PROM for the request zs chip number.
- *
- * Note: The Sun Voyager shows two addresses and two intr for it's
- * Zilogs, what the second does, I don't know. It does work
- * with using only the first number of each property.
- */
-static struct sun_zslayout *get_zs(int chip)
+unsigned long
+sun_serial_setup(unsigned long memory_start)
{
- struct linux_prom_irqs tmp_irq[2];
- unsigned int paddr = 0;
- unsigned int vaddr[2] = { 0, 0 };
- int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
- static int irq = 0;
+ /* Probe for controllers. */
+ if (zs_probe(&memory_start) == 0)
+ return memory_start;
-#if CONFIG_AP1000
- printk("No zs chip\n");
- return NULL;
+#ifdef CONFIG_SAB82532
+ sab82532_probe(&memory_start);
#endif
-
- iospace = 0;
- if(chip < 0 || chip >= NUM_SERIAL)
- panic("get_zs bogon zs chip number");
-
- if(sparc_cpu_model == sun4) {
- /* Grrr, these have to be hardcoded aieee */
- switch(chip) {
- case 0:
- paddr = 0xf1000000;
- break;
- case 1:
- paddr = 0xf0000000;
- break;
- };
- iospace = 0;
- zs_nodes[chip] = 0;
- if(!irq)
- zilog_irq = irq = 12;
- vaddr[0] = (unsigned long)
- sparc_alloc_io(paddr, 0, 8,
- "Zilog Serial", iospace, 0);
- } else {
- /* Can use the prom for other machine types */
- zsnode = prom_getchild(prom_root_node);
- if (sparc_cpu_model == sun4d) {
- int board, node;
-
- tmpnode = zsnode;
- while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
- board = prom_getintdefault (tmpnode, "board#", -1);
- if (board == (chip >> 1)) {
- node = prom_getchild(tmpnode);
- if (node && (node = prom_searchsiblings(node, "bootbus"))) {
- zsnode = node;
- break;
- }
- }
- tmpnode = prom_getsibling(tmpnode);
- }
- if (!tmpnode)
- panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
- } else if (sparc_cpu_model == sun4u) {
- tmpnode = prom_searchsiblings(zsnode, "sbus");
- if(tmpnode)
- zsnode = prom_getchild(tmpnode);
- } else {
- tmpnode = prom_searchsiblings(zsnode, "obio");
- if(tmpnode)
- zsnode = prom_getchild(tmpnode);
- }
- if(!zsnode)
- panic("get_zs no zs serial prom node");
- seen = 0;
- while(zsnode) {
- zsnode = prom_searchsiblings(zsnode, "zs");
- slave = prom_getintdefault(zsnode, "slave", -1);
- if((slave == chip) ||
- (sparc_cpu_model == sun4u && seen == chip)) {
- /* The one we want */
- len = prom_getproperty(zsnode, "address",
- (void *) vaddr,
- sizeof(vaddr));
- if (len % sizeof(unsigned int)) {
- prom_printf("WHOOPS: proplen for %s "
- "was %d, need multiple of "
- "%d\n", "address", len,
- sizeof(unsigned int));
- panic("zilog: address property");
- }
- zs_nodes[chip] = zsnode;
- if(sparc_cpu_model == sun4u) {
- len = prom_getproperty(zsnode, "interrupts",
- (char *) &sun4u_irq,
- sizeof(tmp_irq));
- tmp_irq[0].pri = sun4u_irq;
- } else {
- len = prom_getproperty(zsnode, "intr",
- (char *) tmp_irq,
- sizeof(tmp_irq));
- if (len % sizeof(struct linux_prom_irqs)) {
- prom_printf(
- "WHOOPS: proplen for %s "
- "was %d, need multiple of "
- "%d\n", "address", len,
- sizeof(struct linux_prom_irqs));
- panic("zilog: address property");
- }
- }
- if(!irq) {
- irq = zilog_irq = tmp_irq[0].pri;
- } else {
- if(tmp_irq[0].pri != irq)
- panic("zilog: bogon irqs");
- }
- break;
- }
- zsnode = prom_getsibling(zsnode);
- seen++;
- }
- if(!zsnode)
- panic("get_zs whee chip not found");
- }
- if(!vaddr[0])
- panic("get_zs whee no serial chip mappable");
-
- return (struct sun_zslayout *)(unsigned long) vaddr[0];
-}
-
-static inline void
-init_zscons_termios(struct termios *termios)
-{
- char mode[16], buf[16];
- char *mode_prop = "ttyX-mode";
- char *cd_prop = "ttyX-ignore-cd";
- char *dtr_prop = "ttyX-rts-dtr-off";
- char *s;
- int baud, bits, cflag;
- char parity;
- int topnd, nd;
- int channel, stop;
- int carrier = 0;
- int rtsdtr = 1;
- extern int serial_console;
-
- if (!serial_console)
- return;
-
- if (serial_console == 1) {
- mode_prop[3] = 'a';
- cd_prop[3] = 'a';
- dtr_prop[3] = 'a';
- } else {
- mode_prop[3] = 'b';
- cd_prop[3] = 'b';
- dtr_prop[3] = 'b';
- }
-
- topnd = prom_getchild(prom_root_node);
- nd = prom_searchsiblings(topnd, "options");
- if (!nd) {
- strcpy(mode, "9600,8,n,1,-");
- goto no_options;
- }
-
- if (!prom_node_has_property(nd, mode_prop)) {
- strcpy(mode, "9600,8,n,1,-");
- goto no_options;
- }
-
- memset(mode, 0, sizeof(mode));
- prom_getstring(nd, mode_prop, mode, sizeof(mode));
-
- if (prom_node_has_property(nd, cd_prop)) {
- memset(buf, 0, sizeof(buf));
- prom_getstring(nd, cd_prop, buf, sizeof(buf));
- if (!strcmp(buf, "false"))
- carrier = 1;
-
- /* XXX this is unused below. */
- }
-
- if (prom_node_has_property(nd, cd_prop)) {
- memset(buf, 0, sizeof(buf));
- prom_getstring(nd, cd_prop, buf, sizeof(buf));
- if (!strcmp(buf, "false"))
- rtsdtr = 0;
-
- /* XXX this is unused below. */
- }
-
-no_options:
- cflag = CREAD | HUPCL | CLOCAL;
-
- s = mode;
- baud = simple_strtoul(s, 0, 0);
- s = strchr(s, ',');
- bits = simple_strtoul(++s, 0, 0);
- s = strchr(s, ',');
- parity = *(++s);
- s = strchr(s, ',');
- stop = simple_strtoul(++s, 0, 0);
- s = strchr(s, ',');
- /* XXX handshake is not handled here. */
-
- for (channel = 0; channel < NUM_CHANNELS; channel++)
- if (zs_soft[channel].is_cons)
- break;
-
- switch (baud) {
- case 150:
- cflag |= B150;
- break;
- case 300:
- cflag |= B300;
- break;
- case 600:
- cflag |= B600;
- break;
- case 1200:
- cflag |= B1200;
- break;
- case 2400:
- cflag |= B2400;
- break;
- case 4800:
- cflag |= B4800;
- break;
- default:
- baud = 9600;
- case 9600:
- cflag |= B9600;
- break;
- case 19200:
- cflag |= B19200;
- break;
- case 38400:
- cflag |= B38400;
- break;
- }
- zs_soft[channel].zs_baud = baud;
-
- switch (bits) {
- case 5:
- zscons_regs[3] = Rx5 | RxENAB;
- zscons_regs[5] = Tx5 | TxENAB;
- zs_soft[channel].parity_mask = 0x1f;
- cflag |= CS5;
- break;
- case 6:
- zscons_regs[3] = Rx6 | RxENAB;
- zscons_regs[5] = Tx6 | TxENAB;
- zs_soft[channel].parity_mask = 0x3f;
- cflag |= CS6;
- break;
- case 7:
- zscons_regs[3] = Rx7 | RxENAB;
- zscons_regs[5] = Tx7 | TxENAB;
- zs_soft[channel].parity_mask = 0x7f;
- cflag |= CS7;
- break;
- default:
- case 8:
- zscons_regs[3] = Rx8 | RxENAB;
- zscons_regs[5] = Tx8 | TxENAB;
- zs_soft[channel].parity_mask = 0xff;
- cflag |= CS8;
- break;
- }
- zscons_regs[5] |= DTR;
-
- switch (parity) {
- case 'o':
- zscons_regs[4] |= PAR_ENAB;
- cflag |= (PARENB | PARODD);
- break;
- case 'e':
- zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
- cflag |= PARENB;
- break;
- default:
- case 'n':
- break;
- }
-
- switch (stop) {
- default:
- case 1:
- zscons_regs[4] |= SB1;
- break;
- case 2:
- cflag |= CSTOPB;
- zscons_regs[4] |= SB2;
- break;
- }
-
- termios->c_cflag = cflag;
-}
-
-static inline void
-rs_cons_check(struct sun_serial *ss, int channel)
-{
- int i, o, io;
- static int consout_registered = 0;
- static int msg_printed = 0;
- static struct console console = {
- zs_console_print, 0,
- zs_console_wait_key, zs_console_device };
-
- i = o = io = 0;
-
- /* Is this one of the serial console lines? */
- if((zs_cons_chanout != channel) &&
- (zs_cons_chanin != channel))
- return;
- zs_conschan = ss->zs_channel;
- zs_consinfo = ss;
-
- /* Register the console output putchar, if necessary */
- if((zs_cons_chanout == channel)) {
- o = 1;
- /* double whee.. */
- if(!consout_registered) {
- extern void serial_finish_init (void (*)(const char *, unsigned count));
-
- serial_finish_init (zs_console_print);
- register_console(&console);
- consout_registered = 1;
- }
- }
-
- /* If this is console input, we handle the break received
- * status interrupt on this line to mean prom_halt().
- */
- if(zs_cons_chanin == channel) {
- ss->break_abort = 1;
- i = 1;
- }
- if(o && i)
- io = 1;
-
- /* Set flag variable for this port so that it cannot be
- * opened for other uses by accident.
- */
- ss->is_cons = 1;
-
- if(io) {
- if(!msg_printed) {
- printk("zs%d: console I/O\n", ((channel>>1)&1));
- msg_printed = 1;
- }
- } else {
- printk("zs%d: console %s\n", ((channel>>1)&1),
- (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
- }
-}
-
-extern void keyboard_zsinit(void);
-extern void sun_mouse_zsinit(void);
-
-/* This is for the auto baud rate detection in the mouse driver. */
-void zs_change_mouse_baud(int newbaud)
-{
- int channel = MOUSE_LINE;
- int brg;
-
- zs_soft[channel].zs_baud = newbaud;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- (ZS_CLOCK / zs_soft[channel].clk_divisor));
- write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
-}
-
-__initfunc(unsigned long sun_serial_setup (unsigned long memory_start))
-{
- char *p;
- int i;
-
- if (sparc_cpu_model == sun4d) {
- int node = prom_searchsiblings(prom_getchild(prom_root_node), "boards");
- NUM_SERIAL = 0;
- if (!node)
- panic ("Cannot find out count of boards");
- else
- node = prom_getchild(node);
- while (node && (node = prom_searchsiblings(node, "bif"))) {
- NUM_SERIAL += 2;
- node = prom_getsibling(node);
- }
- }
- p = (char *)((memory_start + 7) & ~7);
- zs_chips = (struct sun_zslayout **)(p);
- i = NUM_SERIAL * sizeof (struct sun_zslayout *);
- zs_channels = (struct sun_zschannel **)(p + i);
- i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
- zs_nodes = (int *)(p + i);
- i += NUM_SERIAL * sizeof (int);
- zs_soft = (struct sun_serial *)(p + i);
- i += NUM_CHANNELS * sizeof (struct sun_serial);
- zs_ttys = (struct tty_struct *)(p + i);
- i += NUM_CHANNELS * sizeof (struct tty_struct);
- serial_table = (struct tty_struct **)(p + i);
- i += NUM_CHANNELS * sizeof (struct tty_struct *);
- serial_termios = (struct termios **)(p + i);
- i += NUM_CHANNELS * sizeof (struct termios *);
- serial_termios_locked = (struct termios **)(p + i);
- i += NUM_CHANNELS * sizeof (struct termios *);
- memset (p, 0, i);
- return (((unsigned long)p) + i + 7) & ~7;
-}
-
-__initfunc(int rs_init(void))
-{
- int chip, channel, brg, i;
- unsigned long flags;
- struct sun_serial *info;
- char dummy;
-
-#if CONFIG_AP1000
- printk("not doing rs_init()\n");
- return 0;
+#ifdef __sparc_v9__
+ if (ps2kbd_probe(&memory_start) == 0)
+ return memory_start;
+ if (su_probe(&memory_start) == 0)
+ return memory_start;
#endif
- /* Setup base handler, and timer table. */
- init_bh(SERIAL_BH, do_serial_bh);
- timer_table[RS_TIMER].fn = rs_timer;
- timer_table[RS_TIMER].expires = 0;
-
- show_serial_version();
-
- /* Initialize the tty_driver structure */
- /* SPARC: Not all of this is exactly right for us. */
-
- memset(&serial_driver, 0, sizeof(struct tty_driver));
- serial_driver.magic = TTY_DRIVER_MAGIC;
- serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
- serial_driver.major = TTY_MAJOR;
- serial_driver.minor_start = 64;
- serial_driver.num = NUM_CHANNELS;
- 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;
- serial_driver.refcount = &serial_refcount;
- serial_driver.table = serial_table;
- serial_driver.termios = serial_termios;
- serial_driver.termios_locked = serial_termios_locked;
-
- serial_driver.open = rs_open;
- serial_driver.close = rs_close;
- serial_driver.write = rs_write;
- serial_driver.flush_chars = rs_flush_chars;
- serial_driver.write_room = rs_write_room;
- serial_driver.chars_in_buffer = rs_chars_in_buffer;
- serial_driver.flush_buffer = rs_flush_buffer;
- serial_driver.ioctl = rs_ioctl;
- serial_driver.throttle = rs_throttle;
- serial_driver.unthrottle = rs_unthrottle;
- serial_driver.set_termios = rs_set_termios;
- serial_driver.stop = rs_stop;
- serial_driver.start = rs_start;
- serial_driver.hangup = rs_hangup;
-
- /* I'm too lazy, someone write versions of this for us. -DaveM */
- serial_driver.read_proc = 0;
- serial_driver.proc_entry = 0;
-
- init_zscons_termios(&serial_driver.init_termios);
-
- /*
- * The callout device is just like normal device except for
- * major number and the subtype code.
- */
- callout_driver = serial_driver;
- callout_driver.name = "cua";
- callout_driver.major = TTYAUX_MAJOR;
- callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-
- if (tty_register_driver(&serial_driver))
- panic("Couldn't register serial driver\n");
- if (tty_register_driver(&callout_driver))
- panic("Couldn't register callout driver\n");
-
- save_flags(flags); cli();
-
- /* Set up our interrupt linked list */
- zs_chain = &zs_soft[0];
- for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
- zs_soft[channel].zs_next = &zs_soft[channel + 1];
- zs_soft[channel + 1].zs_next = 0;
-
- /* Initialize Softinfo */
- for(chip = 0; chip < NUM_SERIAL; chip++) {
- /* If we are doing kgdb over one of the channels on
- * chip zero, kgdb_channel will be set to 1 by the
- * rs_kgdb_hook() routine below.
- */
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- zs_soft[(chip*2)].kgdb_channel = 0;
- zs_soft[(chip*2)+1].kgdb_channel = 0;
- }
-
- /* First, set up channel A on this chip. */
- channel = chip * 2;
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- zs_soft[channel].cons_keyb = 0;
- zs_soft[channel].cons_mouse = 0;
- zs_soft[channel].channelA = 1;
-
- /* Now, channel B */
- channel++;
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- zs_soft[channel].cons_keyb = 0;
- zs_soft[channel].cons_mouse = 0;
- zs_soft[channel].channelA = 0;
- }
-
- /* Initialize Hardware */
- for(channel = 0; channel < NUM_CHANNELS; channel++) {
-
- /* Hardware reset each chip */
- if (!(channel & 1)) {
- write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
- udelay(20); /* wait for some old sun4's */
- dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
- }
-
- if(channel == KEYBOARD_LINE) {
- zs_soft[channel].cons_keyb = 1;
- zs_soft[channel].parity_mask = 0xff;
- zs_kbdchan = zs_soft[channel].zs_channel;
-
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (TCBR | RCBR));
- zs_soft[channel].zs_baud = 1200;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
- /* Enable Rx/Tx, IRQs, and inform kbd driver */
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3,
- (Rx8 | RxENAB));
- write_zsreg(zs_soft[channel].zs_channel, R5,
- (Tx8 | TxENAB | DTR | RTS));
-
- write_zsreg(zs_soft[channel].zs_channel, R15,
- (DCDIE | CTSIE | TxUIE | BRKIE));
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
-
- write_zsreg(zs_soft[channel].zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx));
- write_zsreg(zs_soft[channel].zs_channel, R9,
- (NV | MIE));
- ZS_CLEARERR(zs_soft[channel].zs_channel);
- ZS_CLEARFIFO(zs_soft[channel].zs_channel);
- } else if(channel == MOUSE_LINE) {
- zs_soft[channel].cons_mouse = 1;
- zs_soft[channel].parity_mask = 0xff;
- zs_mousechan = zs_soft[channel].zs_channel;
-
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (TCBR | RCBR));
-
- zs_soft[channel].zs_baud = 4800;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
- /* Enable Rx, IRQs, and inform mouse driver */
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3,
- (Rx8 | RxENAB));
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-
- write_zsreg(zs_soft[channel].zs_channel, R15,
- (DCDIE | CTSIE | TxUIE | BRKIE));
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
-
- write_zsreg(zs_soft[channel].zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx));
- write_zsreg(zs_soft[channel].zs_channel, R9,
- (NV | MIE));
-
- sun_mouse_zsinit();
- } else if (zs_soft[channel].is_cons) {
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- zscons_regs[12] = brg & 0xff;
- zscons_regs[13] = (brg >> 8) & 0xff;
-
- memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
- load_zsregs(&zs_soft[channel], zscons_regs);
-
- ZS_CLEARERR(zs_soft[channel].zs_channel);
- ZS_CLEARFIFO(zs_soft[channel].zs_channel);
- } else if (zs_soft[channel].kgdb_channel) {
- /* If this is the kgdb line, enable interrupts because
- * we now want to receive the 'control-c' character
- * from the client attached to us asynchronously.
- */
- zs_soft[channel].parity_mask = 0xff;
- kgdb_chaninit(&zs_soft[channel], 1,
- zs_soft[channel].zs_baud);
- } else {
- zs_soft[channel].parity_mask = 0xff;
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (RCBR | TCBR));
- zs_soft[channel].zs_baud = 9600;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- }
- }
-
- for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
- info->magic = SERIAL_MAGIC;
- info->port = (long) info->zs_channel;
- info->line = i;
- info->tty = 0;
- info->irq = zilog_irq;
- info->custom_divisor = 16;
- info->close_delay = 50;
- info->closing_wait = 3000;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- info->tqueue.routine = do_softint;
- info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
- info->callout_termios = callout_driver.init_termios;
- info->normal_termios = serial_driver.init_termios;
- info->open_wait = 0;
- info->close_wait = 0;
- printk("tty%02d at 0x%04x (irq = %d)", info->line,
- info->port, info->irq);
- printk(" is a Zilog8530\n");
- }
-
- if (request_irq(zilog_irq, rs_interrupt,
- (SA_INTERRUPT | SA_STATIC_ALLOC),
- "Zilog8530", zs_chain))
- panic("Unable to attach zs intr\n");
- restore_flags(flags);
-
- keyboard_zsinit();
- return 0;
-}
-
-/*
- * register_serial and unregister_serial allows for serial ports to be
- * configured at run-time, to support PCMCIA modems.
- */
-/* SPARC: Unused at this time, just here to make things link. */
-int register_serial(struct serial_struct *req)
-{
- return -1;
-}
-
-void unregister_serial(int line)
-{
- return;
-}
-
-/* Hooks for running a serial console. con_init() calls this if the
- * console is being run over one of the ttya/ttyb serial ports.
- * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
- * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
- * are addressed backwards, channel B is first, then channel A.
- */
-void
-rs_cons_hook(int chip, int out, int line)
-{
- int channel;
-
- if(chip)
- panic("rs_cons_hook called with chip not zero");
- if(line != 1 && line != 2)
- panic("rs_cons_hook called with line not ttya or ttyb");
- channel = line - 1;
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- }
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- if(out)
- zs_cons_chanout = ((chip * 2) + channel);
- else
- zs_cons_chanin = ((chip * 2) + channel);
- rs_cons_check(&zs_soft[channel], channel);
-}
-
-/* This is called at boot time to prime the kgdb serial debugging
- * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1
- * for /dev/ttyb which is determined in setup_arch() from the
- * boot command line flags.
- */
-void
-rs_kgdb_hook(int tty_num)
-{
- int chip = 0;
-
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- }
- zs_soft[tty_num].zs_channel = zs_channels[tty_num];
- zs_kgdbchan = zs_soft[tty_num].zs_channel;
- zs_soft[tty_num].change_needed = 0;
- zs_soft[tty_num].clk_divisor = 16;
- zs_soft[tty_num].zs_baud = 9600;
- zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
- /* Turn on transmitter/receiver at 8-bits/char */
- kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
- ZS_CLEARERR(zs_kgdbchan);
- ZS_CLEARFIFO(zs_kgdbchan);
+ prom_printf("No serial devices found, bailing out.\n");
+ prom_halt();
+ return memory_start;
}
diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h
index ae4260cad..8877d4b56 100644
--- a/drivers/sbus/char/sunserial.h
+++ b/drivers/sbus/char/sunserial.h
@@ -1,437 +1,48 @@
-/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $
- * serial.h: Definitions for the Sparc Zilog serial driver.
+/* $Id: sunserial.h,v 1.13 1997/09/03 11:55:00 ecd Exp $
+ * sunserial.h: SUN serial driver infrastructure.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
-#ifndef _SPARC_SERIAL_H
-#define _SPARC_SERIAL_H
-/* Just one channel */
-struct sun_zschannel {
- volatile unsigned char control;
- volatile unsigned char pad1;
- volatile unsigned char data;
- volatile unsigned char pad2;
-};
+#ifndef _SPARC_SUNSERIAL_H
+#define _SPARC_SUNSERIAL_H 1
-/* The address space layout for each zs chip. Yes they are
- * backwards.
- */
-struct sun_zslayout {
- struct sun_zschannel channelB;
- struct sun_zschannel channelA;
-};
+#include <linux/tty.h>
-/* We need to keep track of the keyboard state, *ahead* of what
- * the keyboard driver sees, which will be later on after the
- * interrupt via tqueue wait queues and/or base-handler processing.
- */
-struct l1a_kbd_state {
- unsigned char kbd_id;
- unsigned char l1_down;
+struct rs_initfunc {
+ int (*rs_init) (void);
+ struct rs_initfunc *next;
};
-#define NUM_ZSREGS 16
-
-struct serial_struct {
- int type;
- int line;
- int port;
- int irq;
- int flags;
- int xmit_fifo_size;
- int custom_divisor;
- int baud_base;
- unsigned short close_delay;
- char reserved_char[2];
- int hub6;
- unsigned short closing_wait; /* time to wait before closing */
- unsigned short closing_wait2; /* no longer used... */
- int reserved[4];
+struct sunserial_operations {
+ struct rs_initfunc *rs_init;
+ void (*rs_cons_hook) (int, int, int);
+ void (*rs_kgdb_hook) (int);
+ void (*rs_change_mouse_baud) (int);
+ int (*rs_read_proc) (char *, char **, off_t, int, int *, void *);
};
/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output. 65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF 0
-#define ZILOG_CLOSING_WAIT_NONE 65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
- on the callout port */
-#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK 0x0030
-#define ZILOG_SPD_HI 0x0010 /* Use 76800 instead of 38400 bps */
-#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
- * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
+ * XXX: Work in progress, don't worry this will go away in a few days. (ecd)
*
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-struct sun_serial {
- struct sun_serial *zs_next; /* For IRQ servicing chain */
- struct sun_zschannel *zs_channel; /* Channel registers */
- unsigned char read_reg_zero;
-
- char soft_carrier; /* Use soft carrier on this channel */
- char cons_keyb; /* Channel runs the keyboard */
- char cons_mouse; /* Channel runs the mouse */
- char break_abort; /* Is serial console in, so process brk/abrt */
- char kgdb_channel; /* Kgdb is running on this channel */
- char is_cons; /* Is this our console. */
-
- char channelA; /* This is channel A. */
- char parity_mask; /* Mask out parity bits in data register. */
-
- /* We need to know the current clock divisor
- * to read the bps rate the chip has currently
- * loaded.
- */
- unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
- int zs_baud;
-
- /* Current write register values */
- unsigned char curregs[NUM_ZSREGS];
-
- char change_needed;
-
- int magic;
- int baud_base;
- int port;
- int irq;
- int flags; /* defined in tty.h */
- int type; /* UART type */
- struct tty_struct *tty;
- int read_status_mask;
- int ignore_status_mask;
- int timeout;
- int xmit_fifo_size;
- int custom_divisor;
- int x_char; /* xon/xoff character */
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- unsigned long event;
- unsigned long last_active;
- int line;
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
- long session; /* Session of opening process */
- long pgrp; /* pgrp of opening process */
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- struct tq_struct tqueue;
- struct tq_struct tqueue_hangup;
- struct termios normal_termios;
- struct termios callout_termios;
- struct wait_queue *open_wait;
- struct wait_queue *close_wait;
-};
-
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
+ * To support multiple keyboards in one binary we have to take care
+ * about (at least) the following:
+ *
+ * int shift_state;
+ *
+ * char *func_buf;
+ * char *func_bufptr;
+ * int funcbufsize;
+ * int funcbufleft;
+ * char **func_table;
+ *
+ * XXX: keymaps need to be handled...
+ *
+ * struct kbd_struct *kbd_table;
+ * int (*kbd_init)(void);
*/
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define FLAG 0x7e
-
-/* Write Register 0 */
-#define R0 0 /* Register selects */
-#define R1 1
-#define R2 2
-#define R3 3
-#define R4 4
-#define R5 5
-#define R6 6
-#define R7 7
-#define R8 8
-#define R9 9
-#define R10 10
-#define R11 11
-#define R12 12
-#define R13 13
-#define R14 14
-#define R15 15
-
-#define NULLCODE 0 /* Null Code */
-#define POINT_HIGH 0x8 /* Select upper half of registers */
-#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
-#define SEND_ABORT 0x18 /* HDLC Abort */
-#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
-#define RES_Tx_P 0x28 /* Reset TxINT Pending */
-#define ERR_RES 0x30 /* Error Reset */
-#define RES_H_IUS 0x38 /* Reset highest IUS */
-
-#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
-#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
-#define RES_EOM_L 0xC0 /* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
-#define TxINT_ENAB 0x2 /* Tx Int Enable */
-#define PAR_SPEC 0x4 /* Parity is special condition */
-
-#define RxINT_DISAB 0 /* Rx Int Disable */
-#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
-#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
-#define INT_ERR_Rx 0x18 /* Int on error only */
-#define RxINT_MASK 0x18
-
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define RxENAB 0x1 /* Rx Enable */
-#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
-#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
-#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
-#define ENT_HM 0x10 /* Enter Hunt Mode */
-#define AUTO_ENAB 0x20 /* Auto Enables */
-#define Rx5 0x0 /* Rx 5 Bits/Character */
-#define Rx7 0x40 /* Rx 7 Bits/Character */
-#define Rx6 0x80 /* Rx 6 Bits/Character */
-#define Rx8 0xc0 /* Rx 8 Bits/Character */
-#define RxN_MASK 0xc0
-
-/* Write Register 4 */
-
-#define PAR_ENAB 0x1 /* Parity Enable */
-#define PAR_EVEN 0x2 /* Parity Even/Odd* */
-
-#define SYNC_ENAB 0 /* Sync Modes Enable */
-#define SB1 0x4 /* 1 stop bit/char */
-#define SB15 0x8 /* 1.5 stop bits/char */
-#define SB2 0xc /* 2 stop bits/char */
-
-#define MONSYNC 0 /* 8 Bit Sync character */
-#define BISYNC 0x10 /* 16 bit sync character */
-#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
-#define EXTSYNC 0x30 /* External Sync Mode */
-
-#define X1CLK 0x0 /* x1 clock mode */
-#define X16CLK 0x40 /* x16 clock mode */
-#define X32CLK 0x80 /* x32 clock mode */
-#define X64CLK 0xC0 /* x64 clock mode */
-
-/* Write Register 5 */
-
-#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
-#define RTS 0x2 /* RTS */
-#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
-#define TxENAB 0x8 /* Tx Enable */
-#define SND_BRK 0x10 /* Send Break */
-#define Tx5 0x0 /* Tx 5 bits (or less)/character */
-#define Tx7 0x20 /* Tx 7 bits/character */
-#define Tx6 0x40 /* Tx 6 bits/character */
-#define Tx8 0x60 /* Tx 8 bits/character */
-#define TxN_MASK 0x60
-#define DTR 0x80 /* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define VIS 1 /* Vector Includes Status */
-#define NV 2 /* No Vector */
-#define DLC 4 /* Disable Lower Chain */
-#define MIE 8 /* Master Interrupt Enable */
-#define STATHI 0x10 /* Status high */
-#define NORESET 0 /* No reset on write to R9 */
-#define CHRB 0x40 /* Reset channel B */
-#define CHRA 0x80 /* Reset channel A */
-#define FHWRES 0xc0 /* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define BIT6 1 /* 6 bit/8bit sync */
-#define LOOPMODE 2 /* SDLC Loop mode */
-#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
-#define MARKIDLE 8 /* Mark/flag on idle */
-#define GAOP 0x10 /* Go active on poll */
-#define NRZ 0 /* NRZ mode */
-#define NRZI 0x20 /* NRZI mode */
-#define FM1 0x40 /* FM1 (transition = 1) */
-#define FM0 0x60 /* FM0 (transition = 0) */
-#define CRCPS 0x80 /* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define TRxCXT 0 /* TRxC = Xtal output */
-#define TRxCTC 1 /* TRxC = Transmit clock */
-#define TRxCBR 2 /* TRxC = BR Generator Output */
-#define TRxCDP 3 /* TRxC = DPLL output */
-#define TRxCOI 4 /* TRxC O/I */
-#define TCRTxCP 0 /* Transmit clock = RTxC pin */
-#define TCTRxCP 8 /* Transmit clock = TRxC pin */
-#define TCBR 0x10 /* Transmit clock = BR Generator output */
-#define TCDPLL 0x18 /* Transmit clock = DPLL output */
-#define RCRTxCP 0 /* Receive clock = RTxC pin */
-#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
-#define RCBR 0x40 /* Receive clock = BR Generator output */
-#define RCDPLL 0x60 /* Receive clock = DPLL output */
-#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define BRENAB 1 /* Baud rate generator enable */
-#define BRSRC 2 /* Baud rate generator source */
-#define DTRREQ 4 /* DTR/Request function */
-#define AUTOECHO 8 /* Auto Echo */
-#define LOOPBAK 0x10 /* Local loopback */
-#define SEARCH 0x20 /* Enter search mode */
-#define RMC 0x40 /* Reset missing clock */
-#define DISDPLL 0x60 /* Disable DPLL */
-#define SSBR 0x80 /* Set DPLL source = BR generator */
-#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
-#define SFMM 0xc0 /* Set FM mode */
-#define SNRZI 0xe0 /* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define ZCIE 2 /* Zero count IE */
-#define DCDIE 8 /* DCD IE */
-#define SYNCIE 0x10 /* Sync/hunt IE */
-#define CTSIE 0x20 /* CTS IE */
-#define TxUIE 0x40 /* Tx Underrun/EOM IE */
-#define BRKIE 0x80 /* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define Rx_CH_AV 0x1 /* Rx Character Available */
-#define ZCOUNT 0x2 /* Zero count */
-#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
-#define DCD 0x8 /* DCD */
-#define SYNC 0x10 /* Sync/hunt */
-#define CTS 0x20 /* CTS */
-#define TxEOM 0x40 /* Tx underrun */
-#define BRK_ABRT 0x80 /* Break/Abort */
-
-/* Read Register 1 */
-#define ALL_SNT 0x1 /* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define RES3 0x8 /* 0/3 */
-#define RES4 0x4 /* 0/4 */
-#define RES5 0xc /* 0/5 */
-#define RES6 0x2 /* 0/6 */
-#define RES7 0xa /* 0/7 */
-#define RES8 0x6 /* 0/8 */
-#define RES18 0xe /* 1/8 */
-#define RES28 0x0 /* 2/8 */
-/* Special Rx Condition Interrupts */
-#define PAR_ERR 0x10 /* Parity error */
-#define Rx_OVR 0x20 /* Rx Overrun Error */
-#define CRC_ERR 0x40 /* CRC/Framing Error */
-#define END_FR 0x80 /* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-#define CHB_Tx_EMPTY 0x00
-#define CHB_EXT_STAT 0x02
-#define CHB_Rx_AVAIL 0x04
-#define CHB_SPECIAL 0x06
-#define CHA_Tx_EMPTY 0x08
-#define CHA_EXT_STAT 0x0a
-#define CHA_Rx_AVAIL 0x0c
-#define CHA_SPECIAL 0x0e
-#define STATUS_MASK 0x0e
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
-#define CHBTxIP 0x2 /* Channel B Tx IP */
-#define CHBRxIP 0x4 /* Channel B Rx IP */
-#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
-#define CHATxIP 0x10 /* Channel A Tx IP */
-#define CHARxIP 0x20 /* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10 (misc status bits) */
-#define ONLOOP 2 /* On loop */
-#define LOOPSEND 0x10 /* Loop sending */
-#define CLK2MIS 0x40 /* Two clocks missing */
-#define CLK1MIS 0x80 /* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel) do { channel->control = ERR_RES; \
- udelay(5); } while(0)
-
-#define ZS_CLEARSTAT(channel) do { channel->control = RES_EXT_INT; \
- udelay(5); } while(0)
-#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
- garbage = channel->data; \
- udelay(2); \
- garbage = channel->data; \
- udelay(2); \
- garbage = channel->data; \
- udelay(2); } while(0)
+extern struct sunserial_operations rs_ops;
+extern void sunserial_setinitfunc(unsigned long *, int (*) (void));
-#endif /* !(_SPARC_SERIAL_H) */
+#endif /* !(_SPARC_SUNSERIAL_H) */
diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c
index 9eaad5bc7..ec0736ff0 100644
--- a/drivers/sbus/char/tcx.c
+++ b/drivers/sbus/char/tcx.c
@@ -1,4 +1,4 @@
-/* $Id: tcx.c,v 1.18 1997/07/22 06:14:09 davem Exp $
+/* $Id: tcx.c,v 1.20 1997/08/22 15:55:14 jj Exp $
* tcx.c: SUNW,tcx 24/8bit frame buffer driver
*
* Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -259,8 +259,8 @@ tcx_reset (fbinfo_t *fb)
{
struct tcx_info *tcx = &(fb->info.tcx);
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb->setcursor && fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
/* Reset control plane to 8bit mode if necessary */
if (fb->open && fb->mmaped)
tcx_set_control_plane (fb);
@@ -292,7 +292,7 @@ __initfunc(void tcx_setup (fbinfo_t *fb, int slot, int node, u32 tcx, struct lin
fb->emulations [1] = FBTYPE_SUN3COLOR;
fb->emulations [2] = FBTYPE_MEMCOLOR;
fb->switch_from_graph = tcx_switch_from_graph;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
tcxinfo = (struct tcx_info *) &fb->info.tcx;
@@ -309,7 +309,7 @@ __initfunc(void tcx_setup (fbinfo_t *fb, int slot, int node, u32 tcx, struct lin
tcxinfo->tec = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_TEC_OFFSET], 0,
sizeof (struct tcx_tec), "tcx_tec", fb->space, 0);
if (!fb->base){
- fb->base = (unsigned long)
+ fb->base = (uint) (unsigned long)
sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET],
0, fb->type.fb_size, "tcx_ram", fb->space, 0);
}
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
new file mode 100644
index 000000000..3d6759a7c
--- /dev/null
+++ b/drivers/sbus/char/zs.c
@@ -0,0 +1,2786 @@
+/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $
+ * zs.c: Zilog serial port driver for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ */
+
+#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/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/keyboard.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/kdebug.h>
+
+#include <asm/sbus.h>
+#ifdef __sparc_v9__
+#include <asm/fhc.h>
+#endif
+
+#include "sunserial.h"
+#include "zs.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
+#define NUM_SERIAL num_serial
+#define NUM_CHANNELS (NUM_SERIAL * 2)
+
+#define KEYBOARD_LINE 0x2
+#define MOUSE_LINE 0x3
+
+struct sun_zslayout **zs_chips;
+struct sun_zschannel **zs_channels;
+struct sun_zschannel *zs_conschan;
+struct sun_zschannel *zs_mousechan;
+struct sun_zschannel *zs_kbdchan;
+struct sun_zschannel *zs_kgdbchan;
+int *zs_nodes;
+
+struct sun_serial *zs_soft;
+struct sun_serial *zs_chain; /* IRQ servicing chain */
+int zilog_irq;
+
+struct tty_struct *zs_ttys;
+/** struct tty_struct *zs_constty; **/
+
+/* Console hooks... */
+static int zs_cons_chanout = 0;
+static int zs_cons_chanin = 0;
+struct sun_serial *zs_consinfo = 0;
+
+static unsigned char kgdb_regs[16] = {
+ 0, 0, 0, /* write 0, 1, 2 */
+ (Rx8 | RxENAB), /* write 3 */
+ (X16CLK | SB1 | PAR_EVEN), /* write 4 */
+ (DTR | Tx8 | TxENAB), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 0, 0, /* BRG time constant, write 12 + 13 */
+ (BRSRC | BRENAB), /* write 14 */
+ (DCDIE) /* write 15 */
+};
+
+static unsigned char zscons_regs[16] = {
+ 0, /* write 0 */
+ (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
+ 0, /* write 2 */
+ (Rx8 | RxENAB), /* write 3 */
+ (X16CLK), /* write 4 */
+ (DTR | Tx8 | TxENAB), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV | MIE), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 0, 0, /* BRG time constant, write 12 + 13 */
+ (BRSRC | BRENAB), /* write 14 */
+ (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
+};
+
+#define ZS_CLOCK 4915200 /* Zilog input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+int zs_init(void);
+void zs_cons_hook(int, int, int);
+void zs_kgdb_hook(int);
+
+static void change_speed(struct sun_serial *info);
+
+static struct tty_struct **serial_table;
+static struct termios **serial_termios;
+static struct termios **serial_termios_locked;
+
+#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 memcpy_fromfs 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[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sun_serial *info,
+ dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: null sun_serial for (%d, %d) in %s\n";
+
+ if (!info) {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 76800, 0 };
+
+/*
+ * Reading and writing Zilog8530 registers. The delays are to make this
+ * driver work on the Sun4 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ */
+static inline unsigned char read_zsreg(struct sun_zschannel *channel,
+ unsigned char reg)
+{
+ unsigned char retval;
+
+ channel->control = reg;
+ udelay(5);
+ retval = channel->control;
+ udelay(5);
+ return retval;
+}
+
+static inline void write_zsreg(struct sun_zschannel *channel,
+ unsigned char reg, unsigned char value)
+{
+ channel->control = reg;
+ udelay(5);
+ channel->control = value;
+ udelay(5);
+}
+
+static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
+{
+ unsigned long flags;
+ struct sun_zschannel *channel = info->zs_channel;
+ unsigned char stat;
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ stat = read_zsreg(channel, R1);
+ if (stat & ALL_SNT)
+ break;
+ udelay(100);
+ }
+ write_zsreg(channel, R3, 0);
+ ZS_CLEARSTAT(channel);
+ ZS_CLEARERR(channel);
+ ZS_CLEARFIFO(channel);
+
+ /* Load 'em up */
+ save_flags(flags); cli();
+ if (info->channelA)
+ write_zsreg(channel, R9, CHRA);
+ else
+ write_zsreg(channel, R9, CHRB);
+ udelay(20); /* wait for some old sun4's */
+ write_zsreg(channel, R4, regs[R4]);
+ write_zsreg(channel, R3, regs[R3] & ~RxENAB);
+ write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+ write_zsreg(channel, R9, regs[R9] & ~MIE);
+ write_zsreg(channel, R10, regs[R10]);
+ write_zsreg(channel, R11, regs[R11]);
+ write_zsreg(channel, R12, regs[R12]);
+ write_zsreg(channel, R13, regs[R13]);
+ write_zsreg(channel, R14, regs[R14] & ~BRENAB);
+ write_zsreg(channel, R14, regs[R14]);
+ write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
+ write_zsreg(channel, R3, regs[R3]);
+ write_zsreg(channel, R5, regs[R5]);
+ write_zsreg(channel, R15, regs[R15]);
+ write_zsreg(channel, R0, RES_EXT_INT);
+ write_zsreg(channel, R0, ERR_RES);
+ write_zsreg(channel, R1, regs[R1]);
+ write_zsreg(channel, R9, regs[R9]);
+ restore_flags(flags);
+}
+
+static inline void zs_put_char(struct sun_zschannel *channel, char ch)
+{
+ int loops = 0;
+
+ while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
+ loops++;
+ udelay(5);
+ }
+ channel->data = ch;
+ udelay(5);
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct sun_serial *ss, int set)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if(set) {
+ ss->curregs[5] |= (RTS | DTR);
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ } else {
+ ss->curregs[5] &= ~(RTS | DTR);
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ }
+ restore_flags(flags);
+ return;
+}
+
+static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
+{
+ int brg;
+
+ if(intson) {
+ kgdb_regs[R1] = INT_ALL_Rx;
+ kgdb_regs[R9] |= MIE;
+ } else {
+ kgdb_regs[R1] = 0;
+ kgdb_regs[R9] &= ~MIE;
+ }
+ brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+ kgdb_regs[R12] = (brg & 255);
+ kgdb_regs[R13] = ((brg >> 8) & 255);
+ load_zsregs(ss, kgdb_regs);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_stop() and zs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void zs_stop(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->curregs[5] & TxENAB) {
+ info->curregs[5] &= ~TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+static void zs_start(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_start"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+/* Drop into either the boot monitor or kadb upon receiving a break
+ * from keyboard/console input.
+ */
+void batten_down_hatches(void)
+{
+ /* If we are doing kadb, we call the debugger
+ * else we just drop into the boot monitor.
+ * Note that we must flush the user windows
+ * first before giving up control.
+ */
+ printk("\n");
+ flush_user_windows();
+#ifndef __sparc_v9__
+ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
+ (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
+ sp_enter_debugger();
+ else
+#endif
+ prom_cmdline();
+
+ /* XXX We want to notify the keyboard driver that all
+ * XXX keys are in the up state or else weird things
+ * XXX happen...
+ */
+
+ return;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * zs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: zs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * zs_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 zs_sched_event(struct sun_serial *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+#ifndef __sparc_v9__
+extern void breakpoint(void); /* For the KGDB frame character */
+#endif
+
+static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, stat;
+
+ do {
+ ch = (info->zs_channel->data) & info->parity_mask;
+ udelay(5);
+
+ /* If this is the console keyboard, we need to handle
+ * L1-A's here.
+ */
+ if(info->cons_keyb) {
+ if(ch == SUNKBD_RESET) {
+ l1a_state.kbd_id = 1;
+ l1a_state.l1_down = 0;
+ } else if(l1a_state.kbd_id) {
+ l1a_state.kbd_id = 0;
+ } else if(ch == SUNKBD_L1) {
+ l1a_state.l1_down = 1;
+ } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ l1a_state.l1_down = 0;
+ } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ /* whee... */
+ batten_down_hatches();
+ /* Continue execution... */
+ l1a_state.l1_down = 0;
+ l1a_state.kbd_id = 0;
+ return;
+ }
+ sunkbd_inchar(ch, regs);
+ return;
+ }
+ if(info->cons_mouse) {
+ sun_mouse_inbyte(ch);
+ return;
+ }
+ if(info->is_cons) {
+ if(ch==0) {
+ /* whee, break received */
+ batten_down_hatches();
+ /* Continue execution... */
+ return;
+#if 0
+ } else if (ch == 1) {
+ show_state();
+ return;
+ } else if (ch == 2) {
+ show_buffers();
+ return;
+#endif
+ }
+ /* It is a 'keyboard interrupt' ;-) */
+ wake_up(&keypress_wait);
+ }
+#ifndef __sparc_v9__
+ /* Look for kgdb 'stop' character, consult the gdb
+ * documentation for remote target debugging and
+ * arch/sparc/kernel/sparc-stub.c to see how all this works.
+ */
+ if((info->kgdb_channel) && (ch =='\003')) {
+ breakpoint();
+ return;
+ }
+#endif
+ if(!tty)
+ return;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = 0;
+ *tty->flip.char_buf_ptr++ = ch;
+
+ /* Check if we have another character... */
+ stat = info->zs_channel->control;
+ udelay(5);
+ if (!(stat & Rx_CH_AV))
+ break;
+
+ /* ... and see if it is clean. */
+ stat = read_zsreg(info->zs_channel, R1);
+ } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static _INLINE_ void transmit_chars(struct sun_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+
+ if (info->x_char) {
+ /* Send next char */
+ zs_put_char(info->zs_channel, info->x_char);
+ info->x_char = 0;
+ return;
+ }
+
+ if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+ /* That's peculiar... */
+ info->zs_channel->control = RES_Tx_P;
+ udelay(5);
+ return;
+ }
+
+ /* Send char */
+ zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+ if(info->xmit_cnt <= 0) {
+ info->zs_channel->control = RES_Tx_P;
+ udelay(5);
+ }
+}
+
+static _INLINE_ void status_handle(struct sun_serial *info)
+{
+ unsigned char status;
+
+ /* Get status from Read Register 0 */
+ status = info->zs_channel->control;
+ udelay(5);
+ /* Clear status condition... */
+ info->zs_channel->control = RES_EXT_INT;
+ udelay(5);
+
+#if 0
+ if(status & DCD) {
+ if((info->tty->termios->c_cflag & CRTSCTS) &&
+ ((info->curregs[3] & AUTO_ENAB)==0)) {
+ info->curregs[3] |= AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ } else {
+ if((info->curregs[3] & AUTO_ENAB)) {
+ info->curregs[3] &= ~AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ }
+#endif
+ /* Whee, if this is console input and this is a
+ * 'break asserted' status change interrupt, call
+ * the boot prom.
+ */
+ if((status & BRK_ABRT) && info->break_abort)
+ batten_down_hatches();
+
+ /* XXX Whee, put in a buffer somewhere, the status information
+ * XXX whee whee whee... Where does the information go...
+ */
+ return;
+}
+
+static _INLINE_ void special_receive(struct sun_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, stat;
+
+ stat = read_zsreg(info->zs_channel, R1);
+ if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+ ch = info->zs_channel->data;
+ udelay(5);
+ }
+
+ if (!tty)
+ goto clear;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ goto done;
+
+ tty->flip.count++;
+ if(stat & PAR_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ else if(stat & Rx_OVR)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ else if(stat & CRC_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+
+done:
+ queue_task(&tty->flip.tqueue, &tq_timer);
+clear:
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+}
+
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct sun_serial *info;
+ unsigned char zs_intreg;
+ int i;
+
+ info = (struct sun_serial *)dev_id;
+ for (i = 0; i < NUM_SERIAL; i++) {
+ zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
+ zs_intreg &= STATUS_MASK;
+
+ /* NOTE: The read register 2, which holds the irq status,
+ * does so for both channels on each chip. Although
+ * the status value itself must be read from the B
+ * channel and is only valid when read from channel B.
+ * When read from channel A, read register 2 contains
+ * the value written to write register 2.
+ */
+
+ /* Channel A -- /dev/ttya or /dev/kbd, could be the console */
+ if (zs_intreg == CHA_Rx_AVAIL) {
+ receive_chars(info, regs);
+ return;
+ }
+ if(zs_intreg == CHA_Tx_EMPTY) {
+ transmit_chars(info);
+ return;
+ }
+ if (zs_intreg == CHA_EXT_STAT) {
+ status_handle(info);
+ return;
+ }
+ if (zs_intreg == CHA_SPECIAL) {
+ special_receive(info);
+ return;
+ }
+
+ /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
+ if(zs_intreg == CHB_Rx_AVAIL) {
+ receive_chars(info->zs_next, regs);
+ return;
+ }
+ if(zs_intreg == CHB_Tx_EMPTY) {
+ transmit_chars(info->zs_next);
+ return;
+ }
+ if (zs_intreg == CHB_EXT_STAT) {
+ status_handle(info->zs_next);
+ return;
+ }
+
+ /* NOTE: The default value for the IRQ status in read register
+ * 2 in channel B is CHB_SPECIAL, so we need to look at
+ * read register 3 in channel A to check if this is a
+ * real interrupt, or just the default value.
+ * Yes... broken hardware...
+ */
+
+ zs_intreg = read_zsreg(info->zs_channel, 3);
+ if (zs_intreg & CHBRxIP) {
+ special_receive(info->zs_next);
+ return;
+ }
+ info = info->zs_next->zs_next;
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * 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
+ * zs_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 zs_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 sun_serial *info = (struct sun_serial *) 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);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> zs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct sun_serial *info = (struct sun_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("do_serial_hangup<%p: tty-%d\n",
+ __builtin_return_address(0), info->line);
+#endif
+
+ tty_hangup(tty);
+}
+
+
+/*
+ * This subroutine is called when the RS_TIMER goes off. It is used
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
+ */
+
+static void zs_timer(void)
+{
+ printk("zs_timer called\n");
+ prom_halt();
+ return;
+}
+
+static int startup(struct sun_serial * info)
+{
+ unsigned long flags;
+
+ if (info->flags & ZILOG_INITIALIZED)
+ return 0;
+
+ if (!info->xmit_buf) {
+ info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+ if (!info->xmit_buf)
+ return -ENOMEM;
+ }
+
+ save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ ZS_CLEARFIFO(info->zs_channel);
+ info->xmit_fifo_size = 1;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+ info->zs_channel->control = RES_H_IUS;
+ udelay(5);
+
+ /*
+ * Now, initialize the Zilog
+ */
+ zs_rtsdtr(info, 1);
+
+ /*
+ * Finally, enable sequencing and interrupts
+ */
+ info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
+ (EXT_INT_ENAB | INT_ALL_Rx);
+ info->curregs[3] |= (RxENAB | Rx8);
+ /* We enable Tx interrupts as needed. */
+ info->curregs[5] |= (TxENAB | Tx8);
+ info->curregs[9] |= (NV | MIE);
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+ info->zs_channel->control = RES_H_IUS;
+ udelay(5);
+
+ 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...
+ */
+#if 0 /* Works well and stops the machine. */
+ timer_table[RS_TIMER].expires = jiffies + 2;
+ timer_active |= 1 << RS_TIMER;
+#endif
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ info->flags |= ZILOG_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * 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 sun_serial * info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ZILOG_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ZILOG_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sun_serial *info)
+{
+ unsigned short port;
+ unsigned cflag;
+ int quot = 0;
+ int i;
+ int brg;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+ if (!(port = info->port))
+ return;
+ i = cflag & CBAUD;
+ if (cflag & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i != 5)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i = 16;
+ }
+ if (i == 15) {
+ if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
+ i += 1;
+ if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
+ quot = info->custom_divisor;
+ }
+ if (quot) {
+ info->zs_baud = info->baud_base / quot;
+ info->clk_divisor = 16;
+
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRSRC | BRENAB;
+ zs_rtsdtr(info, 1);
+ } else if (baud_table[i]) {
+ info->zs_baud = baud_table[i];
+ info->clk_divisor = 16;
+
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRSRC | BRENAB;
+ zs_rtsdtr(info, 1);
+ } else {
+ zs_rtsdtr(info, 0);
+ return;
+ }
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx5;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx5;
+ info->parity_mask = 0x1f;
+ break;
+ case CS6:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx6;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx6;
+ info->parity_mask = 0x3f;
+ break;
+ case CS7:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx7;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx7;
+ info->parity_mask = 0x7f;
+ break;
+ case CS8:
+ default: /* defaults to 8 bits */
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx8;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx8;
+ info->parity_mask = 0xff;
+ break;
+ }
+ info->curregs[4] &= ~(0x0c);
+ if (cflag & CSTOPB) {
+ info->curregs[4] |= SB2;
+ } else {
+ info->curregs[4] |= SB1;
+ }
+ if (cflag & PARENB) {
+ info->curregs[4] |= PAR_ENAB;
+ } else {
+ info->curregs[4] &= ~PAR_ENAB;
+ }
+ if (!(cflag & PARODD)) {
+ info->curregs[4] |= PAR_EVEN;
+ } else {
+ info->curregs[4] &= ~PAR_EVEN;
+ }
+
+ /* Load up the new values */
+ load_zsregs(info, info->curregs);
+
+ return;
+}
+
+/* This is for mouse/keyboard output.
+ * XXX mouse output??? can we send it commands??? XXX
+ */
+static void kbd_put_char(unsigned char ch)
+{
+ struct sun_zschannel *chan = zs_kbdchan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+void mouse_put_char(char ch)
+{
+ struct sun_zschannel *chan = zs_mousechan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+
+/* This is for console output over ttya/ttyb */
+static void zs_cons_put_char(char ch)
+{
+ struct sun_zschannel *chan = zs_conschan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+ struct sun_zschannel *chan = zs_kgdbchan;
+
+ while((chan->control & Tx_BUF_EMP)==0)
+ udelay(5);
+ chan->data = kgdb_char;
+}
+
+char getDebugChar(void)
+{
+ struct sun_zschannel *chan = zs_kgdbchan;
+
+ while((chan->control & Rx_CH_AV)==0)
+ barrier();
+ return chan->data;
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void zs_fair_output(void)
+{
+ int left; /* Output no more than that */
+ unsigned long flags;
+ struct sun_serial *info = zs_consinfo;
+ char c;
+
+ if (info == 0) return;
+ if (info->xmit_buf == 0) return;
+
+ save_flags(flags); cli();
+ left = info->xmit_cnt;
+ while (left != 0) {
+ c = info->xmit_buf[info->xmit_tail];
+ info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ restore_flags(flags);
+
+ zs_cons_put_char(c);
+
+ cli();
+ left = MIN(info->xmit_cnt, left-1);
+ }
+
+ /* Last character is being transmitted now (hopefully). */
+ zs_conschan->control = RES_Tx_P;
+ udelay(5);
+
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * zs_console_print is registered for printk.
+ */
+static void zs_console_print(const char *s, unsigned count)
+{
+ int i;
+
+ for (i = 0; i < count; i++, s++) {
+ if(*s == '\n')
+ zs_cons_put_char('\r');
+ zs_cons_put_char(*s);
+ }
+
+ /* Comment this if you want to have a strict interrupt-driven output */
+ zs_fair_output();
+}
+
+static void zs_console_wait_key(void)
+{
+ sleep_on(&keypress_wait);
+}
+
+static int zs_console_device(void)
+{
+ extern int serial_console;
+
+ return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
+}
+
+static void zs_flush_chars(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ /* Enable transmitter */
+ save_flags(flags); cli();
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+ /*
+ * Send a first (bootstrapping) character. A best solution is
+ * to call transmit_chars() here which handles output in a
+ * generic way. Current transmit_chars() not only transmits,
+ * but resets interrupts also what we do not desire here.
+ * XXX Discuss with David.
+ */
+ zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+
+ restore_flags(flags);
+}
+
+static int zs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, total = 0;
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_write"))
+ return 0;
+
+ if (!info || !info->xmit_buf)
+ return 0;
+
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ down(&tmp_buf_sem);
+ copy_from_user(tmp_buf, buf, c);
+ 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);
+ up(&tmp_buf_sem);
+ } else
+ 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;
+ total += c;
+ }
+
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+ /* Enable transmitter */
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+#if 1
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+ zs_put_char(info->zs_channel,
+ info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ }
+#endif
+ restore_flags(flags);
+ return total;
+}
+
+static int zs_write_room(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "zs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int zs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void zs_flush_buffer(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void zs_throttle(struct tty_struct * tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "zs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+
+ /* Turn off RTS line */
+ cli();
+ info->curregs[5] &= ~RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static void zs_unthrottle(struct tty_struct * tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "zs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+
+ /* Assert RTS line */
+ cli();
+ info->curregs[5] |= RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sun_serial * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = info->port;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
+ return 0;
+}
+
+static int set_serial_info(struct sun_serial * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct sun_serial old_info;
+ int retval = 0;
+
+ if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+ old_info = *info;
+
+ if (!suser()) {
+ if ((new_serial.baud_base != info->baud_base) ||
+ (new_serial.type != info->type) ||
+ (new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ~ZILOG_USR_MASK) !=
+ (info->flags & ~ZILOG_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+ (new_serial.flags & ZILOG_USR_MASK));
+ info->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->baud_base = new_serial.baud_base;
+ info->flags = ((info->flags & ~ZILOG_FLAGS) |
+ (new_serial.flags & ZILOG_FLAGS));
+ info->custom_divisor = new_serial.custom_divisor;
+ info->type = new_serial.type;
+ info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+ 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 sun_serial * info, unsigned int *value)
+{
+ unsigned char status;
+
+ cli();
+ status = info->zs_channel->control;
+ sti();
+ put_user_ret(status,value, -EFAULT);
+ return 0;
+}
+
+static int get_modem_info(struct sun_serial * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+
+ cli();
+ status = info->zs_channel->control;
+ sti();
+ result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
+ | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
+ | ((status & DCD) ? TIOCM_CAR : 0)
+ | ((status & SYNC) ? TIOCM_DSR : 0)
+ | ((status & CTS) ? TIOCM_CTS : 0);
+ put_user_ret(result, value, -EFAULT);
+ return 0;
+}
+
+static int set_modem_info(struct sun_serial * info, unsigned int cmd,
+ unsigned int *value)
+{
+ unsigned int arg;
+
+ get_user_ret(arg, value, -EFAULT);
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ info->curregs[5] |= RTS;
+ if (arg & TIOCM_DTR)
+ info->curregs[5] |= DTR;
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ info->curregs[5] &= ~RTS;
+ if (arg & TIOCM_DTR)
+ info->curregs[5] &= ~DTR;
+ break;
+ case TIOCMSET:
+ info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
+ | ((arg & TIOCM_RTS) ? RTS : 0)
+ | ((arg & TIOCM_DTR) ? DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+ cli();
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct sun_serial * info, int duration)
+{
+ if (!info->port)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+ cli();
+ write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
+ schedule();
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static int zs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+ int retval;
+
+ if (serial_paranoia_check(info, tty->device, "zs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ send_break(info, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ return 0;
+ case TIOCGSOFTCAR:
+ put_user_ret(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg, -EFAULT);
+ return 0;
+ case TIOCSSOFTCAR:
+ get_user_ret(arg, (unsigned long *) arg, -EFAULT);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ copy_to_user_ret((struct sun_serial *) arg,
+ info, sizeof(struct sun_serial), -EFAULT);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void zs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+
+ change_speed(info);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ zs_start(tty);
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_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
+ * ZILOG structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void zs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "zs_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_close tty-%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("zs_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("zs_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ZILOG_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ZILOG_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.
+ */
+ /** if (!info->iscons) ... **/
+ info->curregs[3] &= ~RxENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ info->curregs[1] &= ~(RxINT_MASK);
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ ZS_CLEARFIFO(info->zs_channel);
+
+ 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 (tty->ldisc.num != ldiscs[N_TTY].num) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ tty->ldisc = ldiscs[N_TTY];
+ tty->termios->c_line = N_TTY;
+ if (tty->ldisc.open)
+ (tty->ldisc.open)(tty);
+ }
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+ ZILOG_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_close tty-%d exiting, count = %d\n", info->line, info->count);
+#endif
+ restore_flags(flags);
+}
+
+/*
+ * zs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void zs_hangup(struct tty_struct *tty)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_hangup"))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_hangup<%p: tty-%d, count = %d bye\n",
+ __builtin_return_address(0), info->line, info->count);
+#endif
+
+ zs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sun_serial *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+ unsigned char r0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ZILOG_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ZILOG_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * zs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ cli();
+ if(!tty_hung_up_p(filp))
+ info->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
+ zs_rtsdtr(info, 1);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready hup-ed: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+
+ cli();
+ r0 = read_zsreg(info->zs_channel, R0);
+ sti();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ !(info->flags & ZILOG_CLOSING) &&
+ (do_clocal || (DCD & r0)))
+ break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int zs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sun_serial *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ /* The zilog lines for the mouse/keyboard must be
+ * opened using their respective drivers.
+ */
+ if ((line < 0) || (line >= NUM_CHANNELS))
+ return -ENODEV;
+ if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
+ return -ENODEV;
+ info = zs_soft + line;
+ /* Is the kgdb running over this line? */
+ if (info->kgdb_channel)
+ return -ENODEV;
+ if (serial_paranoia_check(info, tty->device, "zs_open"))
+ return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+ if (info->tty != 0 && info->tty != tty) {
+ /* Never happen? */
+ printk("zs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
+ return -EBUSY;
+ }
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+ char *revision = "$Revision: 1.3 $";
+ char *version, *p;
+
+ version = strchr(revision, ' ');
+ p = strchr(++version, ' ');
+ *p = '\0';
+ printk("Sparc Zilog8530 serial driver version %s\n", version);
+ *p = ' ';
+}
+
+/* Probe the PROM for the request zs chip number.
+ *
+ * Note: The Sun Voyager shows two addresses and two intr for it's
+ * Zilogs, what the second does, I don't know. It does work
+ * with using only the first number of each property. Also
+ * we have a special version for sun4u.
+ */
+#ifdef __sparc_v9__
+static struct devid_cookie zs_dcookie;
+static unsigned long zs_irq_flags;
+static struct sun_zslayout *get_zs(int chip)
+{
+ unsigned int vaddr[2] = { 0, 0 };
+ int busnode, seen, zsnode, sun4u_ino;
+ static int irq = 0;
+
+ if(chip < 0 || chip >= NUM_SERIAL)
+ panic("get_zs bogon zs chip number");
+
+ if(central_bus)
+ busnode = central_bus->child->prom_node;
+ else
+ busnode = prom_searchsiblings(prom_getchild(prom_root_node), "sbus");
+ if(busnode == 0 || busnode == -1)
+ panic("get_zs: no zs bus to search");
+
+ zsnode = prom_getchild(busnode);
+ seen = 0;
+ while(zsnode) {
+ int slave;
+
+ zsnode = prom_searchsiblings(zsnode, "zs");
+ slave = prom_getintdefault(zsnode, "slave", -1);
+ if((slave == chip) || (seen == chip)) {
+ int len = prom_getproperty(zsnode, "address",
+ (void *) vaddr, sizeof(vaddr));
+
+ if(len % sizeof(unsigned int)) {
+ prom_printf("WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(unsigned int));
+ panic("zilog: address property");
+ }
+ zs_nodes[chip] = zsnode;
+ len = prom_getproperty(zsnode, "interrupts",
+ (char *) &sun4u_ino,
+ (sizeof(sun4u_ino)));
+ if(!irq) {
+ irq = zilog_irq = sun4u_ino;
+
+ /* Construct dcookie. */
+ if(central_bus) {
+ zs_dcookie.imap =
+ &central_bus->child->fhc_regs.uregs->fhc_uart_imap;
+ zs_dcookie.iclr =
+ &central_bus->child->fhc_regs.uregs->fhc_uart_iclr;
+ zs_dcookie.pil = 12;
+ zs_dcookie.bus_cookie = NULL;
+ zs_irq_flags =
+ (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_FHC);
+ } else {
+ zs_dcookie.imap = zs_dcookie.iclr = NULL;
+ zs_dcookie.pil = -1;
+ zs_dcookie.bus_cookie = SBus_chain;
+ zs_irq_flags =
+ (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_SBUS);
+ }
+ } else if(irq != sun4u_ino) {
+ panic("zilog: bogon irqs");
+ }
+ break;
+ }
+ zsnode = prom_getsibling(zsnode);
+ seen++;
+ }
+ if(!zsnode)
+ panic("get_zs: whee chip not found");
+ if(!vaddr[0])
+ panic("get_zs: whee no serial chip mappable");
+ return (struct sun_zslayout *)(unsigned long) vaddr[0];
+}
+#else /* !(__sparc_v9__) */
+static struct sun_zslayout *get_zs(int chip)
+{
+ struct linux_prom_irqs tmp_irq[2];
+ unsigned int paddr = 0;
+ unsigned int vaddr[2] = { 0, 0 };
+ int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
+ static int irq = 0;
+
+#if CONFIG_AP1000
+ printk("No zs chip\n");
+ return NULL;
+#endif
+
+ iospace = 0;
+ if(chip < 0 || chip >= NUM_SERIAL)
+ panic("get_zs bogon zs chip number");
+
+ if(sparc_cpu_model == sun4) {
+ /* Grrr, these have to be hardcoded aieee */
+ switch(chip) {
+ case 0:
+ paddr = 0xf1000000;
+ break;
+ case 1:
+ paddr = 0xf0000000;
+ break;
+ };
+ iospace = 0;
+ zs_nodes[chip] = 0;
+ if(!irq)
+ zilog_irq = irq = 12;
+ vaddr[0] = (unsigned long)
+ sparc_alloc_io(paddr, 0, 8,
+ "Zilog Serial", iospace, 0);
+ } else {
+ /* Can use the prom for other machine types */
+ zsnode = prom_getchild(prom_root_node);
+ if (sparc_cpu_model == sun4d) {
+ int board, node;
+
+ tmpnode = zsnode;
+ while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
+ board = prom_getintdefault (tmpnode, "board#", -1);
+ if (board == (chip >> 1)) {
+ node = prom_getchild(tmpnode);
+ if (node && (node = prom_searchsiblings(node, "bootbus"))) {
+ zsnode = node;
+ break;
+ }
+ }
+ tmpnode = prom_getsibling(tmpnode);
+ }
+ if (!tmpnode)
+ panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
+ } else if (sparc_cpu_model == sun4u) {
+ tmpnode = prom_searchsiblings(zsnode, "sbus");
+ if(tmpnode)
+ zsnode = prom_getchild(tmpnode);
+ } else {
+ tmpnode = prom_searchsiblings(zsnode, "obio");
+ if(tmpnode)
+ zsnode = prom_getchild(tmpnode);
+ }
+ if(!zsnode)
+ panic("get_zs no zs serial prom node");
+ seen = 0;
+ while(zsnode) {
+ zsnode = prom_searchsiblings(zsnode, "zs");
+ slave = prom_getintdefault(zsnode, "slave", -1);
+ if((slave == chip) ||
+ (sparc_cpu_model == sun4u && seen == chip)) {
+ /* The one we want */
+ len = prom_getproperty(zsnode, "address",
+ (void *) vaddr,
+ sizeof(vaddr));
+ if (len % sizeof(unsigned int)) {
+ prom_printf("WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(unsigned int));
+ panic("zilog: address property");
+ }
+ zs_nodes[chip] = zsnode;
+ if(sparc_cpu_model == sun4u) {
+ len = prom_getproperty(zsnode, "interrupts",
+ (char *) &sun4u_irq,
+ sizeof(tmp_irq));
+ tmp_irq[0].pri = sun4u_irq;
+ } else {
+ len = prom_getproperty(zsnode, "intr",
+ (char *) tmp_irq,
+ sizeof(tmp_irq));
+ if (len % sizeof(struct linux_prom_irqs)) {
+ prom_printf(
+ "WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(struct linux_prom_irqs));
+ panic("zilog: address property");
+ }
+ }
+ if(!irq) {
+ irq = zilog_irq = tmp_irq[0].pri;
+ } else {
+ if(tmp_irq[0].pri != irq)
+ panic("zilog: bogon irqs");
+ }
+ break;
+ }
+ zsnode = prom_getsibling(zsnode);
+ seen++;
+ }
+ if(!zsnode)
+ panic("get_zs whee chip not found");
+ }
+ if(!vaddr[0])
+ panic("get_zs whee no serial chip mappable");
+
+ return (struct sun_zslayout *)(unsigned long) vaddr[0];
+}
+#endif
+
+static inline void
+init_zscons_termios(struct termios *termios)
+{
+ char mode[16], buf[16];
+ char *mode_prop = "ttyX-mode";
+ char *cd_prop = "ttyX-ignore-cd";
+ char *dtr_prop = "ttyX-rts-dtr-off";
+ char *s;
+ int baud, bits, cflag;
+ char parity;
+ int topnd, nd;
+ int channel, stop;
+ int carrier = 0;
+ int rtsdtr = 1;
+ extern int serial_console;
+
+ if (!serial_console)
+ return;
+
+ if (serial_console == 1) {
+ mode_prop[3] = 'a';
+ cd_prop[3] = 'a';
+ dtr_prop[3] = 'a';
+ } else {
+ mode_prop[3] = 'b';
+ cd_prop[3] = 'b';
+ dtr_prop[3] = 'b';
+ }
+
+ topnd = prom_getchild(prom_root_node);
+ nd = prom_searchsiblings(topnd, "options");
+ if (!nd) {
+ strcpy(mode, "9600,8,n,1,-");
+ goto no_options;
+ }
+
+ if (!prom_node_has_property(nd, mode_prop)) {
+ strcpy(mode, "9600,8,n,1,-");
+ goto no_options;
+ }
+
+ memset(mode, 0, sizeof(mode));
+ prom_getstring(nd, mode_prop, mode, sizeof(mode));
+
+ if (prom_node_has_property(nd, cd_prop)) {
+ memset(buf, 0, sizeof(buf));
+ prom_getstring(nd, cd_prop, buf, sizeof(buf));
+ if (!strcmp(buf, "false"))
+ carrier = 1;
+
+ /* XXX this is unused below. */
+ }
+
+ if (prom_node_has_property(nd, cd_prop)) {
+ memset(buf, 0, sizeof(buf));
+ prom_getstring(nd, cd_prop, buf, sizeof(buf));
+ if (!strcmp(buf, "false"))
+ rtsdtr = 0;
+
+ /* XXX this is unused below. */
+ }
+
+no_options:
+ cflag = CREAD | HUPCL | CLOCAL;
+
+ s = mode;
+ baud = simple_strtoul(s, 0, 0);
+ s = strchr(s, ',');
+ bits = simple_strtoul(++s, 0, 0);
+ s = strchr(s, ',');
+ parity = *(++s);
+ s = strchr(s, ',');
+ stop = simple_strtoul(++s, 0, 0);
+ s = strchr(s, ',');
+ /* XXX handshake is not handled here. */
+
+ for (channel = 0; channel < NUM_CHANNELS; channel++)
+ if (zs_soft[channel].is_cons)
+ break;
+
+ switch (baud) {
+ case 150:
+ cflag |= B150;
+ break;
+ case 300:
+ cflag |= B300;
+ break;
+ case 600:
+ cflag |= B600;
+ break;
+ case 1200:
+ cflag |= B1200;
+ break;
+ case 2400:
+ cflag |= B2400;
+ break;
+ case 4800:
+ cflag |= B4800;
+ break;
+ default:
+ baud = 9600;
+ case 9600:
+ cflag |= B9600;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ }
+ zs_soft[channel].zs_baud = baud;
+
+ switch (bits) {
+ case 5:
+ zscons_regs[3] = Rx5 | RxENAB;
+ zscons_regs[5] = Tx5 | TxENAB;
+ zs_soft[channel].parity_mask = 0x1f;
+ cflag |= CS5;
+ break;
+ case 6:
+ zscons_regs[3] = Rx6 | RxENAB;
+ zscons_regs[5] = Tx6 | TxENAB;
+ zs_soft[channel].parity_mask = 0x3f;
+ cflag |= CS6;
+ break;
+ case 7:
+ zscons_regs[3] = Rx7 | RxENAB;
+ zscons_regs[5] = Tx7 | TxENAB;
+ zs_soft[channel].parity_mask = 0x7f;
+ cflag |= CS7;
+ break;
+ default:
+ case 8:
+ zscons_regs[3] = Rx8 | RxENAB;
+ zscons_regs[5] = Tx8 | TxENAB;
+ zs_soft[channel].parity_mask = 0xff;
+ cflag |= CS8;
+ break;
+ }
+ zscons_regs[5] |= DTR;
+
+ switch (parity) {
+ case 'o':
+ zscons_regs[4] |= PAR_ENAB;
+ cflag |= (PARENB | PARODD);
+ break;
+ case 'e':
+ zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
+ cflag |= PARENB;
+ break;
+ default:
+ case 'n':
+ break;
+ }
+
+ switch (stop) {
+ default:
+ case 1:
+ zscons_regs[4] |= SB1;
+ break;
+ case 2:
+ cflag |= CSTOPB;
+ zscons_regs[4] |= SB2;
+ break;
+ }
+
+ termios->c_cflag = cflag;
+}
+
+__initfunc(static void serial_finish_init(void (*printfunc)(const char *, unsigned)))
+{
+ extern unsigned char *linux_serial_image;
+ char buffer[2048];
+
+ sprintf (buffer, linux_serial_image, UTS_RELEASE);
+ (*printfunc)(buffer, strlen(buffer));
+}
+
+static inline void
+zs_cons_check(struct sun_serial *ss, int channel)
+{
+ int i, o, io;
+ static int consout_registered = 0;
+ static int msg_printed = 0;
+ static struct console console = {
+ zs_console_print, 0,
+ zs_console_wait_key, zs_console_device };
+
+ i = o = io = 0;
+
+ /* Is this one of the serial console lines? */
+ if((zs_cons_chanout != channel) &&
+ (zs_cons_chanin != channel))
+ return;
+ zs_conschan = ss->zs_channel;
+ zs_consinfo = ss;
+
+ /* Register the console output putchar, if necessary */
+ if((zs_cons_chanout == channel)) {
+ o = 1;
+ /* double whee.. */
+ if(!consout_registered) {
+ serial_finish_init (zs_console_print);
+ register_console(&console);
+ consout_registered = 1;
+ }
+ }
+
+ /* If this is console input, we handle the break received
+ * status interrupt on this line to mean prom_halt().
+ */
+ if(zs_cons_chanin == channel) {
+ ss->break_abort = 1;
+ i = 1;
+ }
+ if(o && i)
+ io = 1;
+
+ /* Set flag variable for this port so that it cannot be
+ * opened for other uses by accident.
+ */
+ ss->is_cons = 1;
+
+ if(io) {
+ if(!msg_printed) {
+ printk("zs%d: console I/O\n", ((channel>>1)&1));
+ msg_printed = 1;
+ }
+ } else {
+ printk("zs%d: console %s\n", ((channel>>1)&1),
+ (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
+ }
+}
+
+/* This is for the auto baud rate detection in the mouse driver. */
+void zs_change_mouse_baud(int newbaud)
+{
+ int channel = MOUSE_LINE;
+ int brg;
+
+ zs_soft[channel].zs_baud = newbaud;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ (ZS_CLOCK / zs_soft[channel].clk_divisor));
+ write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
+}
+
+__initfunc(int zs_probe (unsigned long *memory_start))
+{
+ char *p;
+ int node;
+ int i;
+
+ if(sparc_cpu_model == sun4)
+ goto no_probe;
+
+ node = prom_getchild(prom_root_node);
+ if (sparc_cpu_model == sun4d) {
+ node = prom_searchsiblings(node, "boards");
+ NUM_SERIAL = 0;
+ if (!node)
+ panic ("Cannot find out count of boards");
+ else
+ node = prom_getchild(node);
+ while (node && (node = prom_searchsiblings(node, "bif"))) {
+ NUM_SERIAL += 2;
+ node = prom_getsibling(node);
+ }
+ goto no_probe;
+ } else if (sparc_cpu_model == sun4u) {
+ node = prom_searchsiblings(node, "sbus");
+ if(node)
+ node = prom_getchild(node);
+ if(!node)
+ return -ENODEV;
+ } else {
+ node = prom_searchsiblings(node, "obio");
+ if(node)
+ node = prom_getchild(node);
+ goto no_probe;
+ }
+
+ node = prom_searchsiblings(node, "zs");
+ if (!node)
+ return -ENODEV;
+
+no_probe:
+ p = (char *)((*memory_start + 7) & ~7);
+ zs_chips = (struct sun_zslayout **)(p);
+ i = NUM_SERIAL * sizeof (struct sun_zslayout *);
+ zs_channels = (struct sun_zschannel **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
+ zs_nodes = (int *)(p + i);
+ i += NUM_SERIAL * sizeof (int);
+ zs_soft = (struct sun_serial *)(p + i);
+ i += NUM_CHANNELS * sizeof (struct sun_serial);
+ zs_ttys = (struct tty_struct *)(p + i);
+ i += NUM_CHANNELS * sizeof (struct tty_struct);
+ serial_table = (struct tty_struct **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct tty_struct *);
+ serial_termios = (struct termios **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct termios *);
+ serial_termios_locked = (struct termios **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct termios *);
+ memset (p, 0, i);
+ *memory_start = (((unsigned long)p) + i + 7) & ~7;
+
+ /* Fill in rs_ops struct... */
+ sunserial_setinitfunc(memory_start, zs_init);
+ rs_ops.rs_cons_hook = zs_cons_hook;
+ rs_ops.rs_kgdb_hook = zs_kgdb_hook;
+ rs_ops.rs_change_mouse_baud = zs_change_mouse_baud;
+
+ return 0;
+}
+
+__initfunc(int zs_init(void))
+{
+ int chip, channel, brg, i;
+ unsigned long flags;
+ struct sun_serial *info;
+ char dummy;
+
+#if CONFIG_AP1000
+ printk("not doing zs_init()\n");
+ return 0;
+#endif
+
+#ifdef CONFIG_PCI
+ if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+ return 0;
+#endif
+
+ /* Setup base handler, and timer table. */
+ init_bh(SERIAL_BH, do_serial_bh);
+ timer_table[RS_TIMER].fn = zs_timer;
+ timer_table[RS_TIMER].expires = 0;
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ /* SPARC: Not all of this is exactly right for us. */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NUM_CHANNELS;
+ 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;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = zs_open;
+ serial_driver.close = zs_close;
+ serial_driver.write = zs_write;
+ serial_driver.flush_chars = zs_flush_chars;
+ serial_driver.write_room = zs_write_room;
+ serial_driver.chars_in_buffer = zs_chars_in_buffer;
+ serial_driver.flush_buffer = zs_flush_buffer;
+ serial_driver.ioctl = zs_ioctl;
+ serial_driver.throttle = zs_throttle;
+ serial_driver.unthrottle = zs_unthrottle;
+ serial_driver.set_termios = zs_set_termios;
+ serial_driver.stop = zs_stop;
+ serial_driver.start = zs_start;
+ serial_driver.hangup = zs_hangup;
+
+ /* I'm too lazy, someone write versions of this for us. -DaveM */
+ serial_driver.read_proc = 0;
+ serial_driver.proc_entry = 0;
+
+ init_zscons_termios(&serial_driver.init_termios);
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ save_flags(flags); cli();
+
+ /* Set up our interrupt linked list */
+ zs_chain = &zs_soft[0];
+ for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
+ zs_soft[channel].zs_next = &zs_soft[channel + 1];
+ zs_soft[channel + 1].zs_next = 0;
+
+ /* Initialize Softinfo */
+ for(chip = 0; chip < NUM_SERIAL; chip++) {
+ /* If we are doing kgdb over one of the channels on
+ * chip zero, kgdb_channel will be set to 1 by the
+ * zs_kgdb_hook() routine below.
+ */
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ zs_soft[(chip*2)].kgdb_channel = 0;
+ zs_soft[(chip*2)+1].kgdb_channel = 0;
+ }
+
+ /* First, set up channel A on this chip. */
+ channel = chip * 2;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].cons_keyb = 0;
+ zs_soft[channel].cons_mouse = 0;
+ zs_soft[channel].channelA = 1;
+
+ /* Now, channel B */
+ channel++;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].cons_keyb = 0;
+ zs_soft[channel].cons_mouse = 0;
+ zs_soft[channel].channelA = 0;
+ }
+
+ /* Initialize Hardware */
+ for(channel = 0; channel < NUM_CHANNELS; channel++) {
+
+ /* Hardware reset each chip */
+ if (!(channel & 1)) {
+ write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
+ udelay(20); /* wait for some old sun4's */
+ dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
+ }
+
+ if(channel == KEYBOARD_LINE) {
+ zs_soft[channel].cons_keyb = 1;
+ zs_soft[channel].parity_mask = 0xff;
+ zs_kbdchan = zs_soft[channel].zs_channel;
+
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (TCBR | RCBR));
+ zs_soft[channel].zs_baud = 1200;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+ /* Enable Rx/Tx, IRQs, and inform kbd driver */
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3,
+ (Rx8 | RxENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R5,
+ (Tx8 | TxENAB | DTR | RTS));
+
+ write_zsreg(zs_soft[channel].zs_channel, R15,
+ (DCDIE | CTSIE | TxUIE | BRKIE));
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+ ZS_CLEARERR(zs_soft[channel].zs_channel);
+ ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+ } else if(channel == MOUSE_LINE) {
+ zs_soft[channel].cons_mouse = 1;
+ zs_soft[channel].parity_mask = 0xff;
+ zs_mousechan = zs_soft[channel].zs_channel;
+
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (TCBR | RCBR));
+
+ zs_soft[channel].zs_baud = 4800;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+ /* Enable Rx, IRQs, and inform mouse driver */
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3,
+ (Rx8 | RxENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+
+ write_zsreg(zs_soft[channel].zs_channel, R15,
+ (DCDIE | CTSIE | TxUIE | BRKIE));
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+
+ sun_mouse_zsinit();
+ } else if (zs_soft[channel].is_cons) {
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ zscons_regs[12] = brg & 0xff;
+ zscons_regs[13] = (brg >> 8) & 0xff;
+
+ memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
+ load_zsregs(&zs_soft[channel], zscons_regs);
+
+ ZS_CLEARERR(zs_soft[channel].zs_channel);
+ ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+ } else if (zs_soft[channel].kgdb_channel) {
+ /* If this is the kgdb line, enable interrupts because
+ * we now want to receive the 'control-c' character
+ * from the client attached to us asynchronously.
+ */
+ zs_soft[channel].parity_mask = 0xff;
+ kgdb_chaninit(&zs_soft[channel], 1,
+ zs_soft[channel].zs_baud);
+ } else {
+ zs_soft[channel].parity_mask = 0xff;
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (RCBR | TCBR));
+ zs_soft[channel].zs_baud = 9600;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ }
+ }
+
+ for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
+ info->magic = SERIAL_MAGIC;
+ info->port = (long) info->zs_channel;
+ info->line = i;
+ info->tty = 0;
+ info->irq = zilog_irq;
+ info->custom_divisor = 16;
+ info->close_delay = 50;
+ info->closing_wait = 3000;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios = callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ printk("tty%02d at 0x%04x (irq = %d)", info->line,
+ info->port, info->irq);
+ printk(" is a Zilog8530\n");
+ }
+
+#ifndef __sparc_v9__
+ if (request_irq(zilog_irq, zs_interrupt,
+ (SA_INTERRUPT | SA_STATIC_ALLOC),
+ "Zilog8530", zs_chain))
+ panic("Unable to attach zs intr\n");
+#else
+ zs_dcookie.real_dev_id = zs_chain;
+ if (request_irq(zilog_irq, zs_interrupt,
+ zs_irq_flags, "Zilog8530", &zs_dcookie))
+ panic("Unable to attach zs intr\n");
+#endif
+ restore_flags(flags);
+
+ keyboard_zsinit(kbd_put_char);
+ return 0;
+}
+
+/* Hooks for running a serial console. con_init() calls this if the
+ * console is being run over one of the ttya/ttyb serial ports.
+ * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
+ * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
+ * are addressed backwards, channel B is first, then channel A.
+ */
+void
+zs_cons_hook(int chip, int out, int line)
+{
+ int channel;
+
+#ifdef CONFIG_PCI
+ if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+ return;
+#endif
+
+ if(chip)
+ panic("zs_cons_hook called with chip not zero");
+ if(line != 1 && line != 2)
+ panic("zs_cons_hook called with line not ttya or ttyb");
+ channel = line - 1;
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ if(out)
+ zs_cons_chanout = ((chip * 2) + channel);
+ else
+ zs_cons_chanin = ((chip * 2) + channel);
+ zs_cons_check(&zs_soft[channel], channel);
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1
+ * for /dev/ttyb which is determined in setup_arch() from the
+ * boot command line flags.
+ */
+void
+zs_kgdb_hook(int tty_num)
+{
+ int chip = 0;
+
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[tty_num].zs_channel = zs_channels[tty_num];
+ zs_kgdbchan = zs_soft[tty_num].zs_channel;
+ zs_soft[tty_num].change_needed = 0;
+ zs_soft[tty_num].clk_divisor = 16;
+ zs_soft[tty_num].zs_baud = 9600;
+ zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
+ zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+ /* Turn on transmitter/receiver at 8-bits/char */
+ kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+ ZS_CLEARERR(zs_kgdbchan);
+ ZS_CLEARFIFO(zs_kgdbchan);
+}
diff --git a/drivers/sbus/char/zs.h b/drivers/sbus/char/zs.h
new file mode 100644
index 000000000..fd28e4ced
--- /dev/null
+++ b/drivers/sbus/char/zs.h
@@ -0,0 +1,428 @@
+/* $Id: zs.h,v 1.1 1997/08/28 02:23:45 ecd Exp $
+ * zs.h: Definitions for the Sparc Zilog serial driver.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+#ifndef _SPARC_SERIAL_H
+#define _SPARC_SERIAL_H
+
+/* Just one channel */
+struct sun_zschannel {
+ volatile unsigned char control;
+ volatile unsigned char pad1;
+ volatile unsigned char data;
+ volatile unsigned char pad2;
+};
+
+/* The address space layout for each zs chip. Yes they are
+ * backwards.
+ */
+struct sun_zslayout {
+ struct sun_zschannel channelB;
+ struct sun_zschannel channelA;
+};
+
+#define NUM_ZSREGS 16
+
+struct serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ int reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output. 65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF 0
+#define ZILOG_CLOSING_WAIT_NONE 65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK 0x0030
+#define ZILOG_SPD_HI 0x0010 /* Use 76800 instead of 38400 bps */
+#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct sun_serial {
+ struct sun_serial *zs_next; /* For IRQ servicing chain */
+ struct sun_zschannel *zs_channel; /* Channel registers */
+ unsigned char read_reg_zero;
+
+ char soft_carrier; /* Use soft carrier on this channel */
+ char cons_keyb; /* Channel runs the keyboard */
+ char cons_mouse; /* Channel runs the mouse */
+ char break_abort; /* Is serial console in, so process brk/abrt */
+ char kgdb_channel; /* Kgdb is running on this channel */
+ char is_cons; /* Is this our console. */
+
+ char channelA; /* This is channel A. */
+ char parity_mask; /* Mask out parity bits in data register. */
+
+ /* We need to know the current clock divisor
+ * to read the bps rate the chip has currently
+ * loaded.
+ */
+ unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
+ int zs_baud;
+
+ /* Current write register values */
+ unsigned char curregs[NUM_ZSREGS];
+
+ char change_needed;
+
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags; /* defined in tty.h */
+ int type; /* UART type */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+#define RxINT_MASK 0x18
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENAB 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+#define RxN_MASK 0xc0
+
+/* Write Register 4 */
+
+#define PAR_ENAB 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define TxN_MASK 0x60
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENAB 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define CRC_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY 0x00
+#define CHB_EXT_STAT 0x02
+#define CHB_Rx_AVAIL 0x04
+#define CHB_SPECIAL 0x06
+#define CHA_Tx_EMPTY 0x08
+#define CHA_EXT_STAT 0x0a
+#define CHA_Rx_AVAIL 0x0c
+#define CHA_SPECIAL 0x0e
+#define STATUS_MASK 0x0e
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel) do { channel->control = ERR_RES; \
+ udelay(5); } while(0)
+
+#define ZS_CLEARSTAT(channel) do { channel->control = RES_EXT_INT; \
+ udelay(5); } while(0)
+
+#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); } while(0)
+
+#endif /* !(_SPARC_SERIAL_H) */