diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-09-12 01:29:55 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-09-12 01:29:55 +0000 |
commit | 545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch) | |
tree | e9ce4bc598d06374bda906f18365984bf22a526a /drivers/sbus/char | |
parent | 4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff) |
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'drivers/sbus/char')
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 = + ¢ral_bus->child->fhc_regs.uregs->fhc_uart_imap; + zs_dcookie.iclr = + ¢ral_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) */ |