cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

newport_con.c (19270B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * newport_con.c: Abscon for newport hardware
      4 * 
      5 * (C) 1998 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
      6 * (C) 1999 Ulf Carlsson (ulfc@thepuffingruop.com)
      7 * 
      8 * This driver is based on sgicons.c and cons_newport.
      9 * 
     10 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
     11 * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
     12 */
     13#include <linux/init.h>
     14#include <linux/kernel.h>
     15#include <linux/errno.h>
     16#include <linux/kd.h>
     17#include <linux/selection.h>
     18#include <linux/console.h>
     19#include <linux/vt_kern.h>
     20#include <linux/mm.h>
     21#include <linux/module.h>
     22#include <linux/slab.h>
     23
     24#include <asm/io.h>
     25#include <linux/uaccess.h>
     26#include <asm/page.h>
     27#include <asm/gio_device.h>
     28
     29#include <video/newport.h>
     30
     31#include <linux/linux_logo.h>
     32#include <linux/font.h>
     33
     34#define NEWPORT_LEN	0x10000
     35
     36#define FONT_DATA ((unsigned char *)font_vga_8x16.data)
     37
     38static unsigned char *font_data[MAX_NR_CONSOLES];
     39
     40static struct newport_regs *npregs;
     41static unsigned long newport_addr;
     42
     43static int logo_active;
     44static int topscan;
     45static int xcurs_correction = 29;
     46static int newport_xsize;
     47static int newport_ysize;
     48static int newport_has_init;
     49
     50static int newport_set_def_font(int unit, struct console_font *op);
     51
     52#define BMASK(c) (c << 24)
     53
     54#define RENDER(regs, cp) do { \
     55(regs)->go.zpattern = BMASK((cp)[0x0]); (regs)->go.zpattern = BMASK((cp)[0x1]); \
     56(regs)->go.zpattern = BMASK((cp)[0x2]); (regs)->go.zpattern = BMASK((cp)[0x3]); \
     57(regs)->go.zpattern = BMASK((cp)[0x4]); (regs)->go.zpattern = BMASK((cp)[0x5]); \
     58(regs)->go.zpattern = BMASK((cp)[0x6]); (regs)->go.zpattern = BMASK((cp)[0x7]); \
     59(regs)->go.zpattern = BMASK((cp)[0x8]); (regs)->go.zpattern = BMASK((cp)[0x9]); \
     60(regs)->go.zpattern = BMASK((cp)[0xa]); (regs)->go.zpattern = BMASK((cp)[0xb]); \
     61(regs)->go.zpattern = BMASK((cp)[0xc]); (regs)->go.zpattern = BMASK((cp)[0xd]); \
     62(regs)->go.zpattern = BMASK((cp)[0xe]); (regs)->go.zpattern = BMASK((cp)[0xf]); \
     63} while(0)
     64
     65#define TESTVAL 0xdeadbeef
     66#define XSTI_TO_FXSTART(val) (((val) & 0xffff) << 11)
     67
     68static inline void newport_render_background(int xstart, int ystart,
     69					     int xend, int yend, int ci)
     70{
     71	newport_wait(npregs);
     72	npregs->set.wrmask = 0xffffffff;
     73	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
     74				 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
     75				 | NPORT_DMODE0_STOPY);
     76	npregs->set.colori = ci;
     77	npregs->set.xystarti =
     78	    (xstart << 16) | ((ystart + topscan) & 0x3ff);
     79	npregs->go.xyendi =
     80	    ((xend + 7) << 16) | ((yend + topscan + 15) & 0x3ff);
     81}
     82
     83static inline void newport_init_cmap(void)
     84{
     85	unsigned short i;
     86
     87	for (i = 0; i < 16; i++) {
     88		newport_bfwait(npregs);
     89		newport_cmap_setaddr(npregs, color_table[i]);
     90		newport_cmap_setrgb(npregs,
     91				    default_red[i],
     92				    default_grn[i], default_blu[i]);
     93	}
     94}
     95
     96static const struct linux_logo *newport_show_logo(void)
     97{
     98#ifdef CONFIG_LOGO_SGI_CLUT224
     99	const struct linux_logo *logo = fb_find_logo(8);
    100	const unsigned char *clut;
    101	const unsigned char *data;
    102	unsigned long i;
    103
    104	if (!logo)
    105		return NULL;
    106	clut = logo->clut;
    107	data = logo->data;
    108
    109	for (i = 0; i < logo->clutsize; i++) {
    110		newport_bfwait(npregs);
    111		newport_cmap_setaddr(npregs, i + 0x20);
    112		newport_cmap_setrgb(npregs, clut[0], clut[1], clut[2]);
    113		clut += 3;
    114	}
    115
    116	newport_wait(npregs);
    117	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
    118				 NPORT_DMODE0_CHOST);
    119
    120	npregs->set.xystarti = ((newport_xsize - logo->width) << 16) | (0);
    121	npregs->set.xyendi = ((newport_xsize - 1) << 16);
    122	newport_wait(npregs);
    123
    124	for (i = 0; i < logo->width*logo->height; i++)
    125		npregs->go.hostrw0 = *data++ << 24;
    126
    127	return logo;
    128#else
    129	return NULL;
    130#endif /* CONFIG_LOGO_SGI_CLUT224 */
    131}
    132
    133static inline void newport_clear_screen(int xstart, int ystart, int xend,
    134					int yend, int ci)
    135{
    136	if (logo_active)
    137		return;
    138
    139	newport_wait(npregs);
    140	npregs->set.wrmask = 0xffffffff;
    141	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
    142				 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
    143				 | NPORT_DMODE0_STOPY);
    144	npregs->set.colori = ci;
    145	npregs->set.xystarti = (xstart << 16) | ystart;
    146	npregs->go.xyendi = (xend << 16) | yend;
    147}
    148
    149static inline void newport_clear_lines(int ystart, int yend, int ci)
    150{
    151	ystart = ((ystart << 4) + topscan) & 0x3ff;
    152	yend = ((yend << 4) + topscan + 15) & 0x3ff;
    153	newport_clear_screen(0, ystart, 1280 + 63, yend, ci);
    154}
    155
    156static void newport_reset(void)
    157{
    158	unsigned short treg;
    159	int i;
    160
    161	newport_wait(npregs);
    162	treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
    163	newport_vc2_set(npregs, VC2_IREG_CONTROL,
    164			(treg | VC2_CTRL_EVIDEO));
    165
    166	treg = newport_vc2_get(npregs, VC2_IREG_CENTRY);
    167	newport_vc2_set(npregs, VC2_IREG_RADDR, treg);
    168	npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
    169			       NPORT_DMODE_W2 | VC2_PROTOCOL);
    170	for (i = 0; i < 128; i++) {
    171		newport_bfwait(npregs);
    172		if (i == 92 || i == 94)
    173			npregs->set.dcbdata0.byshort.s1 = 0xff00;
    174		else
    175			npregs->set.dcbdata0.byshort.s1 = 0x0000;
    176	}
    177
    178	newport_init_cmap();
    179
    180	/* turn off popup plane */
    181	npregs->set.dcbmode = (DCB_XMAP0 | R_DCB_XMAP9_PROTOCOL |
    182			       XM9_CRS_CONFIG | NPORT_DMODE_W1);
    183	npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE;
    184	npregs->set.dcbmode = (DCB_XMAP1 | R_DCB_XMAP9_PROTOCOL |
    185			       XM9_CRS_CONFIG | NPORT_DMODE_W1);
    186	npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE;
    187
    188	topscan = 0;
    189	npregs->cset.topscan = 0x3ff;
    190	npregs->cset.xywin = (4096 << 16) | 4096;
    191
    192	/* Clear the screen. */
    193	newport_clear_screen(0, 0, 1280 + 63, 1024, 0);
    194}
    195
    196/*
    197 * calculate the actual screen size by reading
    198 * the video timing out of the VC2
    199 */
    200static void newport_get_screensize(void)
    201{
    202	int i, cols;
    203	unsigned short ventry, treg;
    204	unsigned short linetable[128];	/* should be enough */
    205
    206	ventry = newport_vc2_get(npregs, VC2_IREG_VENTRY);
    207	newport_vc2_set(npregs, VC2_IREG_RADDR, ventry);
    208	npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
    209			       NPORT_DMODE_W2 | VC2_PROTOCOL);
    210	for (i = 0; i < 128; i++) {
    211		newport_bfwait(npregs);
    212		linetable[i] = npregs->set.dcbdata0.byshort.s1;
    213	}
    214
    215	newport_xsize = newport_ysize = 0;
    216	for (i = 0; i < ARRAY_SIZE(linetable) - 1 && linetable[i + 1]; i += 2) {
    217		cols = 0;
    218		newport_vc2_set(npregs, VC2_IREG_RADDR, linetable[i]);
    219		npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
    220				       NPORT_DMODE_W2 | VC2_PROTOCOL);
    221		do {
    222			newport_bfwait(npregs);
    223			treg = npregs->set.dcbdata0.byshort.s1;
    224			if ((treg & 1) == 0)
    225				cols += (treg >> 7) & 0xfe;
    226			if ((treg & 0x80) == 0) {
    227				newport_bfwait(npregs);
    228				treg = npregs->set.dcbdata0.byshort.s1;
    229			}
    230		} while ((treg & 0x8000) == 0);
    231		if (cols) {
    232			if (cols > newport_xsize)
    233				newport_xsize = cols;
    234			newport_ysize += linetable[i + 1];
    235		}
    236	}
    237	printk("NG1: Screensize %dx%d\n", newport_xsize, newport_ysize);
    238}
    239
    240static void newport_get_revisions(void)
    241{
    242	unsigned int tmp;
    243	unsigned int board_rev;
    244	unsigned int rex3_rev;
    245	unsigned int vc2_rev;
    246	unsigned int cmap_rev;
    247	unsigned int xmap9_rev;
    248	unsigned int bt445_rev;
    249	unsigned int bitplanes;
    250
    251	rex3_rev = npregs->cset.status & NPORT_STAT_VERS;
    252
    253	npregs->set.dcbmode = (DCB_CMAP0 | NCMAP_PROTOCOL |
    254			       NCMAP_REGADDR_RREG | NPORT_DMODE_W1);
    255	tmp = npregs->set.dcbdata0.bybytes.b3;
    256	cmap_rev = tmp & 7;
    257	board_rev = (tmp >> 4) & 7;
    258	bitplanes = ((board_rev > 1) && (tmp & 0x80)) ? 8 : 24;
    259
    260	npregs->set.dcbmode = (DCB_CMAP1 | NCMAP_PROTOCOL |
    261			       NCMAP_REGADDR_RREG | NPORT_DMODE_W1);
    262	tmp = npregs->set.dcbdata0.bybytes.b3;
    263	if ((tmp & 7) < cmap_rev)
    264		cmap_rev = (tmp & 7);
    265
    266	vc2_rev = (newport_vc2_get(npregs, VC2_IREG_CONFIG) >> 5) & 7;
    267
    268	npregs->set.dcbmode = (DCB_XMAP0 | R_DCB_XMAP9_PROTOCOL |
    269			       XM9_CRS_REVISION | NPORT_DMODE_W1);
    270	xmap9_rev = npregs->set.dcbdata0.bybytes.b3 & 7;
    271
    272	npregs->set.dcbmode = (DCB_BT445 | BT445_PROTOCOL |
    273			       BT445_CSR_ADDR_REG | NPORT_DMODE_W1);
    274	npregs->set.dcbdata0.bybytes.b3 = BT445_REVISION_REG;
    275	npregs->set.dcbmode = (DCB_BT445 | BT445_PROTOCOL |
    276			       BT445_CSR_REVISION | NPORT_DMODE_W1);
    277	bt445_rev = (npregs->set.dcbdata0.bybytes.b3 >> 4) - 0x0a;
    278
    279#define L(a)     (char)('A'+(a))
    280	printk
    281	    ("NG1: Revision %d, %d bitplanes, REX3 revision %c, VC2 revision %c, xmap9 revision %c, cmap revision %c, bt445 revision %c\n",
    282	     board_rev, bitplanes, L(rex3_rev), L(vc2_rev), L(xmap9_rev),
    283	     L(cmap_rev ? (cmap_rev + 1) : 0), L(bt445_rev));
    284#undef L
    285
    286	if (board_rev == 3)	/* I don't know all affected revisions */
    287		xcurs_correction = 21;
    288}
    289
    290static void newport_exit(void)
    291{
    292	int i;
    293
    294	/* free memory used by user font */
    295	for (i = 0; i < MAX_NR_CONSOLES; i++)
    296		newport_set_def_font(i, NULL);
    297}
    298
    299/* Can't be __init, do_take_over_console may call it later */
    300static const char *newport_startup(void)
    301{
    302	int i;
    303
    304	npregs->cset.config = NPORT_CFG_GD0;
    305
    306	if (newport_wait(npregs))
    307		goto out_unmap;
    308
    309	npregs->set.xstarti = TESTVAL;
    310	if (npregs->set._xstart.word != XSTI_TO_FXSTART(TESTVAL))
    311		goto out_unmap;
    312
    313	for (i = 0; i < MAX_NR_CONSOLES; i++)
    314		font_data[i] = FONT_DATA;
    315
    316	newport_reset();
    317	newport_get_revisions();
    318	newport_get_screensize();
    319	newport_has_init = 1;
    320
    321	return "SGI Newport";
    322
    323out_unmap:
    324	return NULL;
    325}
    326
    327static void newport_init(struct vc_data *vc, int init)
    328{
    329	int cols, rows;
    330
    331	cols = newport_xsize / 8;
    332	rows = newport_ysize / 16;
    333	vc->vc_can_do_color = 1;
    334	if (init) {
    335		vc->vc_cols = cols;
    336		vc->vc_rows = rows;
    337	} else
    338		vc_resize(vc, cols, rows);
    339}
    340
    341static void newport_deinit(struct vc_data *c)
    342{
    343	if (!con_is_bound(&newport_con) && newport_has_init) {
    344		newport_exit();
    345		newport_has_init = 0;
    346	}
    347}
    348
    349static void newport_clear(struct vc_data *vc, int sy, int sx, int height,
    350			  int width)
    351{
    352	int xend = ((sx + width) << 3) - 1;
    353	int ystart = ((sy << 4) + topscan) & 0x3ff;
    354	int yend = (((sy + height) << 4) + topscan - 1) & 0x3ff;
    355
    356	if (logo_active)
    357		return;
    358
    359	if (ystart < yend) {
    360		newport_clear_screen(sx << 3, ystart, xend, yend,
    361				     (vc->state.color & 0xf0) >> 4);
    362	} else {
    363		newport_clear_screen(sx << 3, ystart, xend, 1023,
    364				     (vc->state.color & 0xf0) >> 4);
    365		newport_clear_screen(sx << 3, 0, xend, yend,
    366				     (vc->state.color & 0xf0) >> 4);
    367	}
    368}
    369
    370static void newport_putc(struct vc_data *vc, int charattr, int ypos,
    371			 int xpos)
    372{
    373	unsigned char *p;
    374
    375	p = &font_data[vc->vc_num][(charattr & 0xff) << 4];
    376	charattr = (charattr >> 8) & 0xff;
    377	xpos <<= 3;
    378	ypos <<= 4;
    379
    380	newport_render_background(xpos, ypos, xpos, ypos,
    381				  (charattr & 0xf0) >> 4);
    382
    383	/* Set the color and drawing mode. */
    384	newport_wait(npregs);
    385	npregs->set.colori = charattr & 0xf;
    386	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
    387				 NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
    388				 NPORT_DMODE0_L32);
    389
    390	/* Set coordinates for bitmap operation. */
    391	npregs->set.xystarti = (xpos << 16) | ((ypos + topscan) & 0x3ff);
    392	npregs->set.xyendi = ((xpos + 7) << 16);
    393	newport_wait(npregs);
    394
    395	/* Go, baby, go... */
    396	RENDER(npregs, p);
    397}
    398
    399static void newport_putcs(struct vc_data *vc, const unsigned short *s,
    400			  int count, int ypos, int xpos)
    401{
    402	int i;
    403	int charattr;
    404	unsigned char *p;
    405
    406	charattr = (scr_readw(s) >> 8) & 0xff;
    407
    408	xpos <<= 3;
    409	ypos <<= 4;
    410
    411	if (!logo_active)
    412		/* Clear the area behing the string */
    413		newport_render_background(xpos, ypos,
    414					  xpos + ((count - 1) << 3), ypos,
    415					  (charattr & 0xf0) >> 4);
    416
    417	newport_wait(npregs);
    418
    419	/* Set the color and drawing mode. */
    420	npregs->set.colori = charattr & 0xf;
    421	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
    422				 NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
    423				 NPORT_DMODE0_L32);
    424
    425	for (i = 0; i < count; i++, xpos += 8) {
    426		p = &font_data[vc->vc_num][(scr_readw(s++) & 0xff) << 4];
    427
    428		newport_wait(npregs);
    429
    430		/* Set coordinates for bitmap operation. */
    431		npregs->set.xystarti =
    432		    (xpos << 16) | ((ypos + topscan) & 0x3ff);
    433		npregs->set.xyendi = ((xpos + 7) << 16);
    434
    435		/* Go, baby, go... */
    436		RENDER(npregs, p);
    437	}
    438}
    439
    440static void newport_cursor(struct vc_data *vc, int mode)
    441{
    442	unsigned short treg;
    443	int xcurs, ycurs;
    444
    445	switch (mode) {
    446	case CM_ERASE:
    447		treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
    448		newport_vc2_set(npregs, VC2_IREG_CONTROL,
    449				(treg & ~(VC2_CTRL_ECDISP)));
    450		break;
    451
    452	case CM_MOVE:
    453	case CM_DRAW:
    454		treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
    455		newport_vc2_set(npregs, VC2_IREG_CONTROL,
    456				(treg | VC2_CTRL_ECDISP));
    457		xcurs = (vc->vc_pos - vc->vc_visible_origin) / 2;
    458		ycurs = ((xcurs / vc->vc_cols) << 4) + 31;
    459		xcurs = ((xcurs % vc->vc_cols) << 3) + xcurs_correction;
    460		newport_vc2_set(npregs, VC2_IREG_CURSX, xcurs);
    461		newport_vc2_set(npregs, VC2_IREG_CURSY, ycurs);
    462	}
    463}
    464
    465static int newport_switch(struct vc_data *vc)
    466{
    467	static int logo_drawn = 0;
    468
    469	topscan = 0;
    470	npregs->cset.topscan = 0x3ff;
    471
    472	if (!logo_drawn) {
    473		if (newport_show_logo()) {
    474			logo_drawn = 1;
    475			logo_active = 1;
    476		}
    477	}
    478
    479	return 1;
    480}
    481
    482static int newport_blank(struct vc_data *c, int blank, int mode_switch)
    483{
    484	unsigned short treg;
    485
    486	if (blank == 0) {
    487		/* unblank console */
    488		treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
    489		newport_vc2_set(npregs, VC2_IREG_CONTROL,
    490				(treg | VC2_CTRL_EDISP));
    491	} else {
    492		/* blank console */
    493		treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
    494		newport_vc2_set(npregs, VC2_IREG_CONTROL,
    495				(treg & ~(VC2_CTRL_EDISP)));
    496	}
    497	return 1;
    498}
    499
    500static int newport_set_font(int unit, struct console_font *op)
    501{
    502	int w = op->width;
    503	int h = op->height;
    504	int size = h * op->charcount;
    505	int i;
    506	unsigned char *new_data, *data = op->data, *p;
    507
    508	/* ladis: when I grow up, there will be a day... and more sizes will
    509	 * be supported ;-) */
    510	if ((w != 8) || (h != 16)
    511	    || (op->charcount != 256 && op->charcount != 512))
    512		return -EINVAL;
    513
    514	if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size,
    515	     GFP_USER))) return -ENOMEM;
    516
    517	new_data += FONT_EXTRA_WORDS * sizeof(int);
    518	FNTSIZE(new_data) = size;
    519	FNTCHARCNT(new_data) = op->charcount;
    520	REFCOUNT(new_data) = 0;	/* usage counter */
    521	FNTSUM(new_data) = 0;
    522
    523	p = new_data;
    524	for (i = 0; i < op->charcount; i++) {
    525		memcpy(p, data, h);
    526		data += 32;
    527		p += h;
    528	}
    529
    530	/* check if font is already used by other console */
    531	for (i = 0; i < MAX_NR_CONSOLES; i++) {
    532		if (font_data[i] != FONT_DATA
    533		    && FNTSIZE(font_data[i]) == size
    534		    && !memcmp(font_data[i], new_data, size)) {
    535			kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
    536			/* current font is the same as the new one */
    537			if (i == unit)
    538				return 0;
    539			new_data = font_data[i];
    540			break;
    541		}
    542	}
    543	/* old font is user font */
    544	if (font_data[unit] != FONT_DATA) {
    545		if (--REFCOUNT(font_data[unit]) == 0)
    546			kfree(font_data[unit] -
    547			      FONT_EXTRA_WORDS * sizeof(int));
    548	}
    549	REFCOUNT(new_data)++;
    550	font_data[unit] = new_data;
    551
    552	return 0;
    553}
    554
    555static int newport_set_def_font(int unit, struct console_font *op)
    556{
    557	if (font_data[unit] != FONT_DATA) {
    558		if (--REFCOUNT(font_data[unit]) == 0)
    559			kfree(font_data[unit] -
    560			      FONT_EXTRA_WORDS * sizeof(int));
    561		font_data[unit] = FONT_DATA;
    562	}
    563
    564	return 0;
    565}
    566
    567static int newport_font_default(struct vc_data *vc, struct console_font *op, char *name)
    568{
    569	return newport_set_def_font(vc->vc_num, op);
    570}
    571
    572static int newport_font_set(struct vc_data *vc, struct console_font *font, unsigned flags)
    573{
    574	return newport_set_font(vc->vc_num, font);
    575}
    576
    577static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
    578		enum con_scroll dir, unsigned int lines)
    579{
    580	int count, x, y;
    581	unsigned short *s, *d;
    582	unsigned short chattr;
    583
    584	logo_active = 0;	/* it's time to disable the logo now.. */
    585
    586	if (t == 0 && b == vc->vc_rows) {
    587		if (dir == SM_UP) {
    588			topscan = (topscan + (lines << 4)) & 0x3ff;
    589			newport_clear_lines(vc->vc_rows - lines,
    590					    vc->vc_rows - 1,
    591					    (vc->state.color & 0xf0) >> 4);
    592		} else {
    593			topscan = (topscan + (-lines << 4)) & 0x3ff;
    594			newport_clear_lines(0, lines - 1,
    595					    (vc->state.color & 0xf0) >> 4);
    596		}
    597		npregs->cset.topscan = (topscan - 1) & 0x3ff;
    598		return false;
    599	}
    600
    601	count = (b - t - lines) * vc->vc_cols;
    602	if (dir == SM_UP) {
    603		x = 0;
    604		y = t;
    605		s = (unsigned short *) (vc->vc_origin +
    606					vc->vc_size_row * (t + lines));
    607		d = (unsigned short *) (vc->vc_origin +
    608					vc->vc_size_row * t);
    609		while (count--) {
    610			chattr = scr_readw(s++);
    611			if (chattr != scr_readw(d)) {
    612				newport_putc(vc, chattr, y, x);
    613				scr_writew(chattr, d);
    614			}
    615			d++;
    616			if (++x == vc->vc_cols) {
    617				x = 0;
    618				y++;
    619			}
    620		}
    621		d = (unsigned short *) (vc->vc_origin +
    622					vc->vc_size_row * (b - lines));
    623		x = 0;
    624		y = b - lines;
    625		for (count = 0; count < (lines * vc->vc_cols); count++) {
    626			if (scr_readw(d) != vc->vc_video_erase_char) {
    627				newport_putc(vc, vc->vc_video_erase_char,
    628					     y, x);
    629				scr_writew(vc->vc_video_erase_char, d);
    630			}
    631			d++;
    632			if (++x == vc->vc_cols) {
    633				x = 0;
    634				y++;
    635			}
    636		}
    637	} else {
    638		x = vc->vc_cols - 1;
    639		y = b - 1;
    640		s = (unsigned short *) (vc->vc_origin +
    641					vc->vc_size_row * (b - lines) - 2);
    642		d = (unsigned short *) (vc->vc_origin +
    643					vc->vc_size_row * b - 2);
    644		while (count--) {
    645			chattr = scr_readw(s--);
    646			if (chattr != scr_readw(d)) {
    647				newport_putc(vc, chattr, y, x);
    648				scr_writew(chattr, d);
    649			}
    650			d--;
    651			if (x-- == 0) {
    652				x = vc->vc_cols - 1;
    653				y--;
    654			}
    655		}
    656		d = (unsigned short *) (vc->vc_origin +
    657					vc->vc_size_row * t);
    658		x = 0;
    659		y = t;
    660		for (count = 0; count < (lines * vc->vc_cols); count++) {
    661			if (scr_readw(d) != vc->vc_video_erase_char) {
    662				newport_putc(vc, vc->vc_video_erase_char,
    663					     y, x);
    664				scr_writew(vc->vc_video_erase_char, d);
    665			}
    666			d++;
    667			if (++x == vc->vc_cols) {
    668				x = 0;
    669				y++;
    670			}
    671		}
    672	}
    673	return true;
    674}
    675
    676static void newport_save_screen(struct vc_data *vc) { }
    677
    678const struct consw newport_con = {
    679	.owner		  = THIS_MODULE,
    680	.con_startup	  = newport_startup,
    681	.con_init	  = newport_init,
    682	.con_deinit	  = newport_deinit,
    683	.con_clear	  = newport_clear,
    684	.con_putc	  = newport_putc,
    685	.con_putcs	  = newport_putcs,
    686	.con_cursor	  = newport_cursor,
    687	.con_scroll	  = newport_scroll,
    688	.con_switch	  = newport_switch,
    689	.con_blank	  = newport_blank,
    690	.con_font_set	  = newport_font_set,
    691	.con_font_default = newport_font_default,
    692	.con_save_screen  = newport_save_screen
    693};
    694
    695static int newport_probe(struct gio_device *dev,
    696			 const struct gio_device_id *id)
    697{
    698	int err;
    699
    700	if (!dev->resource.start)
    701		return -EINVAL;
    702
    703	if (npregs)
    704		return -EBUSY; /* we only support one Newport as console */
    705
    706	newport_addr = dev->resource.start + 0xF0000;
    707	if (!request_mem_region(newport_addr, NEWPORT_LEN, "Newport"))
    708		return -ENODEV;
    709
    710	npregs = (struct newport_regs *)/* ioremap cannot fail */
    711		ioremap(newport_addr, sizeof(struct newport_regs));
    712	console_lock();
    713	err = do_take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
    714	console_unlock();
    715
    716	if (err) {
    717		iounmap((void *)npregs);
    718		release_mem_region(newport_addr, NEWPORT_LEN);
    719	}
    720	return err;
    721}
    722
    723static void newport_remove(struct gio_device *dev)
    724{
    725	give_up_console(&newport_con);
    726	iounmap((void *)npregs);
    727	release_mem_region(newport_addr, NEWPORT_LEN);
    728}
    729
    730static struct gio_device_id newport_ids[] = {
    731	{ .id = 0x7e },
    732	{ .id = 0xff }
    733};
    734
    735MODULE_ALIAS("gio:7e");
    736
    737static struct gio_driver newport_driver = {
    738	.name = "newport",
    739	.id_table = newport_ids,
    740	.probe = newport_probe,
    741	.remove = newport_remove,
    742};
    743module_driver(newport_driver, gio_register_driver, gio_unregister_driver);
    744
    745MODULE_LICENSE("GPL");