Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
d4d9c3ac
Commit
d4d9c3ac
authored
Jul 30, 2002
by
David S. Miller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
OpenPROM: Kill len check, it is pointless.
parent
aa2a3d64
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
0 additions
and
3832 deletions
+0
-3832
drivers/sbus/char/openprom.c
drivers/sbus/char/openprom.c
+0
-3
drivers/sbus/char/sunserial.c
drivers/sbus/char/sunserial.c
+0
-457
drivers/sbus/char/sunserial.h
drivers/sbus/char/sunserial.h
+0
-53
drivers/sbus/char/zs.c
drivers/sbus/char/zs.c
+0
-2892
drivers/sbus/char/zs.h
drivers/sbus/char/zs.h
+0
-427
drivers/serial/sunkbd.c
drivers/serial/sunkbd.c
+0
-0
drivers/serial/sunkbd.h
drivers/serial/sunkbd.h
+0
-0
drivers/serial/sunmouse.c
drivers/serial/sunmouse.c
+0
-0
drivers/serial/sunmouse.h
drivers/serial/sunmouse.h
+0
-0
drivers/serial/sunsab.c
drivers/serial/sunsab.c
+0
-0
drivers/serial/sunsu.c
drivers/serial/sunsu.c
+0
-0
No files found.
drivers/sbus/char/openprom.c
View file @
d4d9c3ac
...
...
@@ -334,9 +334,6 @@ static int copyin_string(char *user, size_t len, char **ptr)
{
char
*
tmp
;
if
(
len
<
0
||
len
+
1
<
0
)
return
-
EINVAL
;
tmp
=
kmalloc
(
len
+
1
,
GFP_KERNEL
);
if
(
!
tmp
)
return
-
ENOMEM
;
...
...
drivers/sbus/char/sunserial.c
deleted
100644 → 0
View file @
aa2a3d64
/* $Id: sunserial.c,v 1.81 2002/01/05 01:13:43 davem Exp $
* serial.c: Serial port driver infrastructure for the Sparc.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/string.h>
#include <linux/kbd_diacr.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <asm/oplib.h>
#include "sunserial.h"
int
serial_console
;
int
stop_a_enabled
=
1
;
int
__init
con_is_present
(
void
)
{
return
serial_console
?
0
:
1
;
}
static
void
__init
nop_rs_kgdb_hook
(
int
channel
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
}
static
void
nop_rs_change_mouse_baud
(
int
baud
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
}
static
int
nop_rs_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
return
0
;
}
struct
sunserial_operations
rs_ops
=
{
0
,
nop_rs_kgdb_hook
,
nop_rs_change_mouse_baud
,
nop_rs_read_proc
};
void
rs_init
(
void
)
{
static
int
invoked
=
0
;
if
(
!
invoked
)
{
struct
initfunc
*
init
;
invoked
=
1
;
init
=
rs_ops
.
rs_init
;
while
(
init
)
{
(
void
)
init
->
init
();
init
=
init
->
next
;
}
}
}
void
__init
rs_kgdb_hook
(
int
channel
)
{
rs_ops
.
rs_kgdb_hook
(
channel
);
}
void
__init
serial_console_init
(
void
)
{
return
;
}
void
rs_change_mouse_baud
(
int
baud
)
{
rs_ops
.
rs_change_mouse_baud
(
baud
);
}
int
rs_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
return
rs_ops
.
rs_read_proc
(
page
,
start
,
off
,
count
,
eof
,
data
);
}
int
register_serial
(
struct
serial_struct
*
req
)
{
return
-
1
;
}
void
unregister_serial
(
int
line
)
{
}
static
void
nop_compute_shiftstate
(
void
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
}
static
void
nop_setledstate
(
struct
kbd_struct
*
kbd
,
unsigned
int
ledstate
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
}
static
unsigned
char
nop_getledstate
(
void
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
return
0
;
}
static
int
nop_setkeycode
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
return
-
EINVAL
;
}
static
int
nop_getkeycode
(
unsigned
int
scancode
)
{
printk
(
"Oops: %s called
\n
"
,
__FUNCTION__
);
return
-
EINVAL
;
}
struct
sunkbd_operations
kbd_ops
=
{
0
,
nop_compute_shiftstate
,
nop_setledstate
,
nop_getledstate
,
nop_setkeycode
,
nop_getkeycode
};
#ifdef CONFIG_USB
extern
void
pci_compute_shiftstate
(
void
);
extern
int
pci_setkeycode
(
unsigned
int
,
unsigned
int
);
extern
int
pci_getkeycode
(
unsigned
int
);
extern
void
pci_setledstate
(
struct
kbd_struct
*
,
unsigned
int
);
extern
unsigned
char
pci_getledstate
(
void
);
extern
int
pcikbd_init
(
void
);
#endif
int
kbd_init
(
void
)
{
struct
initfunc
*
init
;
int
err
=
-
ENODEV
;
init
=
kbd_ops
.
kbd_init
;
while
(
init
)
{
err
=
init
->
init
();
init
=
init
->
next
;
}
#ifdef CONFIG_USB
if
(
!
serial_console
&&
kbd_ops
.
compute_shiftstate
==
nop_compute_shiftstate
)
{
printk
(
"kbd_init: Assuming USB keyboard.
\n
"
);
kbd_ops
.
compute_shiftstate
=
pci_compute_shiftstate
;
kbd_ops
.
setledstate
=
pci_setledstate
;
kbd_ops
.
getledstate
=
pci_getledstate
;
kbd_ops
.
setkeycode
=
pci_setkeycode
;
kbd_ops
.
getkeycode
=
pci_getkeycode
;
pcikbd_init
();
}
#endif
return
err
;
}
void
compute_shiftstate
(
void
)
{
kbd_ops
.
compute_shiftstate
();
}
void
setledstate
(
struct
kbd_struct
*
kbd
,
unsigned
int
ledstate
)
{
kbd_ops
.
setledstate
(
kbd
,
ledstate
);
}
unsigned
char
getledstate
(
void
)
{
return
kbd_ops
.
getledstate
();
}
int
setkeycode
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
return
kbd_ops
.
setkeycode
(
scancode
,
keycode
);
}
int
getkeycode
(
unsigned
int
scancode
)
{
return
kbd_ops
.
getkeycode
(
scancode
);
}
void
*
__init
sunserial_alloc_bootmem
(
unsigned
long
size
)
{
void
*
ret
;
ret
=
__alloc_bootmem
(
size
,
SMP_CACHE_BYTES
,
0UL
);
if
(
ret
!=
NULL
)
memset
(
ret
,
0
,
size
);
return
ret
;
}
void
sunserial_setinitfunc
(
int
(
*
init
)
(
void
))
{
struct
initfunc
*
rs_init
;
rs_init
=
sunserial_alloc_bootmem
(
sizeof
(
struct
initfunc
));
if
(
rs_init
==
NULL
)
{
prom_printf
(
"sunserial_setinitfunc: Cannot alloc initfunc.
\n
"
);
prom_halt
();
}
rs_init
->
init
=
init
;
rs_init
->
next
=
rs_ops
.
rs_init
;
rs_ops
.
rs_init
=
rs_init
;
}
void
sunserial_console_termios
(
struct
console
*
con
)
{
char
mode
[
16
],
buf
[
16
],
*
s
;
char
*
mode_prop
=
"ttyX-mode"
;
char
*
cd_prop
=
"ttyX-ignore-cd"
;
char
*
dtr_prop
=
"ttyX-rts-dtr-off"
;
int
baud
,
bits
,
stop
,
cflag
;
char
parity
;
int
carrier
=
0
;
int
rtsdtr
=
1
;
int
topnd
,
nd
;
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. */
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
;
case
9600
:
cflag
|=
B9600
;
break
;
case
19200
:
cflag
|=
B19200
;
break
;
case
38400
:
cflag
|=
B38400
;
break
;
default:
baud
=
9600
;
cflag
|=
B9600
;
break
;
}
switch
(
bits
)
{
case
5
:
cflag
|=
CS5
;
break
;
case
6
:
cflag
|=
CS6
;
break
;
case
7
:
cflag
|=
CS7
;
break
;
case
8
:
cflag
|=
CS8
;
break
;
default:
cflag
|=
CS8
;
break
;
}
switch
(
parity
)
{
case
'o'
:
cflag
|=
(
PARENB
|
PARODD
);
break
;
case
'e'
:
cflag
|=
PARENB
;
break
;
case
'n'
:
default
:
break
;
}
switch
(
stop
)
{
case
2
:
cflag
|=
CSTOPB
;
break
;
case
1
:
default
:
break
;
}
con
->
cflag
=
cflag
;
}
void
sunkbd_setinitfunc
(
int
(
*
init
)
(
void
))
{
struct
initfunc
*
kbd_init
;
kbd_init
=
sunserial_alloc_bootmem
(
sizeof
(
struct
initfunc
));
if
(
kbd_init
==
NULL
)
{
prom_printf
(
"sunkbd_setinitfunc: Cannot alloc initfunc.
\n
"
);
prom_halt
();
}
kbd_init
->
init
=
init
;
kbd_init
->
next
=
kbd_ops
.
kbd_init
;
kbd_ops
.
kbd_init
=
kbd_init
;
}
#ifdef CONFIG_PCI
void
sunkbd_install_keymaps
(
ushort
**
src_key_maps
,
unsigned
int
src_keymap_count
,
char
*
src_func_buf
,
char
**
src_func_table
,
int
src_funcbufsize
,
int
src_funcbufleft
,
struct
kbdiacr
*
src_accent_table
,
unsigned
int
src_accent_table_size
)
{
extern
unsigned
int
keymap_count
;
int
i
,
j
;
for
(
i
=
0
;
i
<
MAX_NR_KEYMAPS
;
i
++
)
{
if
(
src_key_maps
[
i
])
{
if
(
!
key_maps
[
i
])
{
key_maps
[
i
]
=
(
ushort
*
)
sunserial_alloc_bootmem
(
NR_KEYS
*
sizeof
(
ushort
));
if
(
key_maps
[
i
]
==
NULL
)
{
prom_printf
(
"sunkbd_install_keymaps: "
"Cannot alloc key_map(%d).
\n
"
,
i
);
prom_halt
();
}
}
for
(
j
=
0
;
j
<
NR_KEYS
;
j
++
)
key_maps
[
i
][
j
]
=
src_key_maps
[
i
][
j
];
}
key_maps
[
i
]
=
src_key_maps
[
i
];
}
keymap_count
=
src_keymap_count
;
for
(
i
=
0
;
i
<
MAX_NR_FUNC
;
i
++
)
func_table
[
i
]
=
src_func_table
[
i
];
funcbufptr
=
src_func_buf
;
funcbufsize
=
src_funcbufsize
;
funcbufleft
=
src_funcbufleft
;
for
(
i
=
0
;
i
<
MAX_DIACR
;
i
++
)
accent_table
[
i
]
=
src_accent_table
[
i
];
accent_table_size
=
src_accent_table_size
;
}
#endif
extern
int
su_probe
(
void
);
extern
int
zs_probe
(
void
);
#ifdef CONFIG_SAB82532
extern
int
sab82532_probe
(
void
);
#endif
#ifdef CONFIG_PCI
extern
int
ps2kbd_probe
(
void
);
#endif
void
__init
sun_serial_setup
(
void
)
{
int
ret
=
1
;
#if defined(CONFIG_PCI) && !defined(__sparc_v9__)
/*
* Probing sequence on sparc differs from sparc64.
* Keyboard is probed ahead of su because we want su function
* when keyboard is active. su is probed ahead of zs in order to
* get console on MrCoffee with fine but disconnected zs.
*/
if
(
!
serial_console
)
ps2kbd_probe
();
if
(
su_probe
()
==
0
)
return
;
#endif
if
(
zs_probe
()
==
0
)
return
;
#ifdef CONFIG_SAB82532
ret
=
sab82532_probe
();
#endif
#if defined(CONFIG_PCI) && defined(__sparc_v9__)
/*
* Keyboard serial devices.
*
* Well done, Sun, prom_devopen("/pci@1f,4000/ebus@1/su@14,3083f8")
* hangs the machine if no keyboard is connected to the device...
* All PCI PROMs seem to do this, I have seen this on the Ultra 450
* with version 3.5 PROM, and on the Ultra/AX with 3.1.5 PROM.
*
* So be very careful not to probe for keyboards if we are on a
* serial console.
*/
if
(
!
serial_console
)
ps2kbd_probe
();
if
(
su_probe
()
==
0
)
return
;
#endif
if
(
!
ret
)
return
;
#ifdef __sparc_v9__
{
extern
int
this_is_starfire
;
/* Hello, Starfire. Pleased to meet you :) */
if
(
this_is_starfire
!=
0
)
return
;
}
#endif
prom_printf
(
"No serial devices found, bailing out.
\n
"
);
prom_halt
();
}
drivers/sbus/char/sunserial.h
deleted
100644 → 0
View file @
aa2a3d64
/* $Id: sunserial.h,v 1.19 1999/12/01 10:45:59 davem Exp $
* sunserial.h: SUN serial driver infrastructure (including keyboards).
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#ifndef _SPARC_SUNSERIAL_H
#define _SPARC_SUNSERIAL_H 1
#include <linux/config.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/kbd_kern.h>
#include <linux/console.h>
struct
initfunc
{
int
(
*
init
)
(
void
);
struct
initfunc
*
next
;
};
struct
sunserial_operations
{
struct
initfunc
*
rs_init
;
void
(
*
rs_kgdb_hook
)
(
int
);
void
(
*
rs_change_mouse_baud
)
(
int
);
int
(
*
rs_read_proc
)
(
char
*
,
char
**
,
off_t
,
int
,
int
*
,
void
*
);
};
struct
sunkbd_operations
{
struct
initfunc
*
kbd_init
;
void
(
*
compute_shiftstate
)
(
void
);
void
(
*
setledstate
)
(
struct
kbd_struct
*
,
unsigned
int
);
unsigned
char
(
*
getledstate
)
(
void
);
int
(
*
setkeycode
)
(
unsigned
int
,
unsigned
int
);
int
(
*
getkeycode
)
(
unsigned
int
);
};
extern
struct
sunserial_operations
rs_ops
;
extern
struct
sunkbd_operations
kbd_ops
;
extern
void
sunserial_setinitfunc
(
int
(
*
)
(
void
));
extern
void
sunkbd_setinitfunc
(
int
(
*
)
(
void
));
extern
int
serial_console
;
extern
int
stop_a_enabled
;
extern
void
sunserial_console_termios
(
struct
console
*
);
#ifdef CONFIG_PCI
extern
void
sunkbd_install_keymaps
(
ushort
**
,
unsigned
int
,
char
*
,
char
**
,
int
,
int
,
struct
kbdiacr
*
,
unsigned
int
);
#endif
#endif
/* !(_SPARC_SUNSERIAL_H) */
drivers/sbus/char/zs.c
deleted
100644 → 0
View file @
aa2a3d64
/* $Id: zs.c,v 1.71 2002/01/12 07:04:54 davem 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@yahoo.com>.
*
* Fixed to use tty_get_baud_rate().
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*
* /proc/tty/driver/serial now exists and is readable.
* Alex Buell <alex.buell@tahallah.demon.co.uk>, 2001-12-23
*
*/
#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/init.h>
#include <linux/bootmem.h>
#include <linux/sysrq.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/page.h>
#include <asm/pgtable.h>
#include <asm/sbus.h>
#ifdef __sparc_v9__
#include <asm/fhc.h>
#endif
#ifdef CONFIG_PCI
#include <linux/pci.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
/* On 32-bit sparcs we need to delay after register accesses
* to accomodate sun4 systems, but we do not need to flush writes.
* On 64-bit sparc we only need to flush single writes to ensure
* completion.
*/
#ifndef __sparc_v9__
#define ZSDELAY() udelay(5)
#define ZSDELAY_LONG() udelay(20)
#define ZS_WSYNC(channel) do { } while(0)
#else
#define ZSDELAY()
#define ZSDELAY_LONG()
#define ZS_WSYNC(__channel) \
sbus_readb(&((__channel)->control))
#endif
struct
sun_zslayout
**
zs_chips
;
struct
sun_zschannel
**
zs_channels
;
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
;
/* Console hooks... */
#ifdef CONFIG_SERIAL_CONSOLE
static
struct
console
zs_console
;
static
int
zs_console_init
(
void
);
/*
* Define this to get the zs_fair_output() functionality.
*/
#undef SERIAL_CONSOLE_FAIR_OUTPUT
#endif
/* CONFIG_SERIAL_CONSOLE */
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
);
static
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
#define SERIAL_DO_RESTART
/* 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
);
static
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
#undef ZS_LOG
#ifdef ZS_LOG
struct
zs_logent
{
u8
reg
,
val
;
u8
write
,
__pad
;
#define REGIRQ 0xff
#define REGDATA 0xfe
#define REGCTRL 0xfd
};
struct
zs_logent
zslog
[
32
];
int
zs_curlog
;
#define ZSLOG(__reg, __val, __write) \
do{ int index = zs_curlog; \
zslog[index].reg = (__reg); \
zslog[index].val = (__val); \
zslog[index].write = (__write); \
zs_curlog = (index + 1) & (32 - 1); \
}while(0)
int
zs_dumplog
(
char
*
buffer
)
{
int
len
=
0
;
int
i
;
for
(
i
=
0
;
i
<
32
;
i
++
)
{
u8
reg
,
val
,
write
;
reg
=
zslog
[
i
].
reg
;
val
=
zslog
[
i
].
val
;
write
=
zslog
[
i
].
write
;
len
+=
sprintf
(
buffer
+
len
,
"ZSLOG[%2d]: reg %2x val %2x %s
\n
"
,
i
,
reg
,
val
,
write
?
"write"
:
"read"
);
}
len
+=
sprintf
(
buffer
+
len
,
"ZS current log index %d
\n
"
,
zs_curlog
);
return
len
;
}
#else
#define ZSLOG(x,y,z) do { } while (0)
#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
=
0
;
static
DECLARE_MUTEX
(
tmp_buf_sem
);
static
inline
int
serial_paranoia_check
(
struct
sun_serial
*
info
,
kdev_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
;
}
/* 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
unsigned
char
read_zsreg
(
struct
sun_zschannel
*
channel
,
unsigned
char
reg
)
{
unsigned
char
retval
;
sbus_writeb
(
reg
,
&
channel
->
control
);
ZSDELAY
();
retval
=
sbus_readb
(
&
channel
->
control
);
ZSDELAY
();
ZSLOG
(
reg
,
retval
,
0
);
return
retval
;
}
static
void
write_zsreg
(
struct
sun_zschannel
*
channel
,
unsigned
char
reg
,
unsigned
char
value
)
{
ZSLOG
(
reg
,
value
,
1
);
sbus_writeb
(
reg
,
&
channel
->
control
);
ZSDELAY
();
sbus_writeb
(
value
,
&
channel
->
control
);
ZSDELAY
();
}
static
void
load_zsregs
(
struct
sun_serial
*
info
,
unsigned
char
*
regs
)
{
struct
sun_zschannel
*
channel
=
info
->
zs_channel
;
unsigned
long
flags
;
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
);
ZSDELAY_LONG
();
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
);
}
#define ZS_PUT_CHAR_MAX_DELAY 2000
/* 10 ms */
static
void
zs_put_char
(
struct
sun_zschannel
*
channel
,
char
ch
)
{
int
loops
=
ZS_PUT_CHAR_MAX_DELAY
;
/* Do not change this to use ZSDELAY as this is
* a timed polling loop and on sparc64 ZSDELAY
* is a nop. -DaveM
*/
do
{
u8
val
=
sbus_readb
(
&
channel
->
control
);
ZSLOG
(
REGCTRL
,
val
,
0
);
if
(
val
&
Tx_BUF_EMP
)
break
;
udelay
(
5
);
}
while
(
--
loops
);
sbus_writeb
(
ch
,
&
channel
->
data
);
ZSDELAY
();
ZS_WSYNC
(
channel
);
ZSLOG
(
REGDATA
,
ch
,
1
);
}
/* Sets or clears DTR/RTS on the requested line */
static
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
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
(
!
stop_a_enabled
)
return
;
/* 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
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
void
receive_chars
(
struct
sun_serial
*
info
,
struct
pt_regs
*
regs
)
{
struct
tty_struct
*
tty
=
info
->
tty
;
int
do_queue_task
=
0
;
while
(
1
)
{
unsigned
char
ch
,
r1
;
r1
=
read_zsreg
(
info
->
zs_channel
,
R1
);
if
(
r1
&
(
PAR_ERR
|
Rx_OVR
|
CRC_ERR
))
{
sbus_writeb
(
ERR_RES
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
ERR_RES
,
1
);
}
ch
=
sbus_readb
(
&
info
->
zs_channel
->
data
);
ZSLOG
(
REGDATA
,
ch
,
0
);
ch
&=
info
->
parity_mask
;
ZSDELAY
();
/* 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
);
goto
next_char
;
}
if
(
info
->
cons_mouse
)
{
sun_mouse_inbyte
(
ch
,
0
);
goto
next_char
;
}
if
(
info
->
is_cons
)
{
if
(
ch
==
0
)
{
/* whee, break received */
batten_down_hatches
();
/* Continue execution... */
return
;
}
}
#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
;
do_queue_task
++
;
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
break
;
tty
->
flip
.
count
++
;
if
(
r1
&
PAR_ERR
)
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_PARITY
;
else
if
(
r1
&
Rx_OVR
)
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_OVERRUN
;
else
if
(
r1
&
CRC_ERR
)
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_FRAME
;
else
*
tty
->
flip
.
flag_buf_ptr
++
=
0
;
*
tty
->
flip
.
char_buf_ptr
++
=
ch
;
next_char:
{
unsigned
char
stat
;
/* Check if we have another character... */
stat
=
sbus_readb
(
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
stat
,
0
);
if
(
!
(
stat
&
Rx_CH_AV
))
break
;
}
}
if
(
do_queue_task
!=
0
)
queue_task
(
&
tty
->
flip
.
tqueue
,
&
tq_timer
);
}
static
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... */
sbus_writeb
(
RES_Tx_P
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_Tx_P
,
1
);
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
)
{
sbus_writeb
(
RES_Tx_P
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_Tx_P
,
1
);
}
}
static
void
status_handle
(
struct
sun_serial
*
info
)
{
unsigned
char
status
;
/* Get status from Read Register 0 */
status
=
sbus_readb
(
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
status
,
0
);
/* Clear status condition... */
sbus_writeb
(
RES_EXT_INT
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_EXT_INT
,
1
);
#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
)
{
if
(
info
->
break_abort
)
batten_down_hatches
();
if
(
info
->
cons_mouse
)
sun_mouse_inbyte
(
0
,
1
);
}
/* XXX Whee, put in a buffer somewhere, the status information
* XXX whee whee whee... Where does the information go...
*/
return
;
}
/*
* 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
;
int
i
;
info
=
(
struct
sun_serial
*
)
dev_id
;
ZSLOG
(
REGIRQ
,
0
,
0
);
for
(
i
=
0
;
i
<
NUM_SERIAL
;
i
++
)
{
unsigned
char
r3
=
read_zsreg
(
info
->
zs_channel
,
3
);
/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
if
(
r3
&
(
CHAEXT
|
CHATxIP
|
CHARxIP
))
{
sbus_writeb
(
RES_H_IUS
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_H_IUS
,
1
);
if
(
r3
&
CHARxIP
)
receive_chars
(
info
,
regs
);
if
(
r3
&
CHAEXT
)
status_handle
(
info
);
if
(
r3
&
CHATxIP
)
transmit_chars
(
info
);
}
/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
info
=
info
->
zs_next
;
if
(
r3
&
(
CHBEXT
|
CHBTxIP
|
CHBRxIP
))
{
sbus_writeb
(
RES_H_IUS
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_H_IUS
,
1
);
if
(
r3
&
CHBRxIP
)
receive_chars
(
info
,
regs
);
if
(
r3
&
CHBEXT
)
status_handle
(
info
);
if
(
r3
&
CHBTxIP
)
transmit_chars
(
info
);
}
info
=
info
->
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
);
}
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.
*/
sbus_writeb
(
ERR_RES
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
ERR_RES
,
1
);
sbus_writeb
(
RES_H_IUS
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_H_IUS
,
1
);
/*
* 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.
*/
sbus_writeb
(
ERR_RES
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
ERR_RES
,
1
);
sbus_writeb
(
RES_H_IUS
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
info
->
zs_channel
);
ZSLOG
(
REGCTRL
,
RES_H_IUS
,
1
);
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
|=
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
cflag
;
int
baud
,
quot
=
0
;
int
brg
;
if
(
!
info
->
tty
||
!
info
->
tty
->
termios
)
return
;
cflag
=
info
->
tty
->
termios
->
c_cflag
;
if
(
!
info
->
port
)
return
;
baud
=
tty_get_baud_rate
(
info
->
tty
);
if
((
baud
==
38400
)
&&
((
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
)
{
info
->
zs_baud
=
baud
;
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
);
}
/* 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
((
sbus_readb
(
&
chan
->
control
)
&
Tx_BUF_EMP
)
==
0
)
udelay
(
5
);
sbus_writeb
(
kgdb_char
,
&
chan
->
data
);
ZS_WSYNC
(
chan
);
ZSLOG
(
REGDATA
,
kgdb_char
,
1
);
}
char
getDebugChar
(
void
)
{
struct
sun_zschannel
*
chan
=
zs_kgdbchan
;
u8
val
;
do
{
val
=
sbus_readb
(
&
chan
->
control
);
ZSLOG
(
REGCTRL
,
val
,
0
);
udelay
(
5
);
}
while
((
val
&
Rx_CH_AV
)
==
0
);
val
=
sbus_readb
(
&
chan
->
data
);
ZSLOG
(
REGDATA
,
val
,
0
);
return
val
;
}
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
;
save_flags
(
flags
);
cli
();
if
(
info
->
xmit_cnt
<=
0
||
tty
->
stopped
||
tty
->
hw_stopped
||
!
info
->
xmit_buf
)
goto
out
;
/* 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
]);
/*
* 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
--
;
out:
restore_flags
(
flags
);
}
static
int
zs_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
unsigned
char
*
buf
,
int
count
)
{
struct
sun_serial
*
info
=
(
struct
sun_serial
*
)
tty
->
driver_data
;
unsigned
long
flags
;
int
c
,
total
=
0
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"zs_write"
))
return
0
;
if
(
!
info
||
!
info
->
xmit_buf
||
!
tmp_buf
)
return
0
;
save_flags
(
flags
);
if
(
from_user
)
{
down
(
&
tmp_buf_sem
);
while
(
1
)
{
c
=
MIN
(
count
,
MIN
(
SERIAL_XMIT_SIZE
-
info
->
xmit_cnt
-
1
,
SERIAL_XMIT_SIZE
-
info
->
xmit_head
));
if
(
c
<=
0
)
break
;
c
-=
copy_from_user
(
tmp_buf
,
buf
,
c
);
if
(
!
c
)
{
if
(
!
total
)
total
=
-
EFAULT
;
break
;
}
cli
();
c
=
MIN
(
c
,
MIN
(
SERIAL_XMIT_SIZE
-
info
->
xmit_cnt
-
1
,
SERIAL_XMIT_SIZE
-
info
->
xmit_head
));
memcpy
(
info
->
xmit_buf
+
info
->
xmit_head
,
tmp_buf
,
c
);
info
->
xmit_head
=
((
info
->
xmit_head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
));
info
->
xmit_cnt
+=
c
;
restore_flags
(
flags
);
buf
+=
c
;
count
-=
c
;
total
+=
c
;
}
up
(
&
tmp_buf_sem
);
}
else
{
while
(
1
)
{
cli
();
c
=
MIN
(
count
,
MIN
(
SERIAL_XMIT_SIZE
-
info
->
xmit_cnt
-
1
,
SERIAL_XMIT_SIZE
-
info
->
xmit_head
));
if
(
c
<=
0
)
{
restore_flags
(
flags
);
break
;
}
memcpy
(
info
->
xmit_buf
+
info
->
xmit_head
,
buf
,
c
);
info
->
xmit_head
=
((
info
->
xmit_head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
));
info
->
xmit_cnt
+=
c
;
restore_flags
(
flags
);
buf
+=
c
;
count
-=
c
;
total
+=
c
;
}
}
cli
();
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
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
;
if
(
copy_to_user
(
retinfo
,
&
tmp
,
sizeof
(
*
retinfo
)))
return
-
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
(
!
capable
(
CAP_SYS_ADMIN
))
{
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
(
new_serial
.
baud_base
<
9600
)
return
-
EINVAL
;
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
=
sbus_readb
(
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
status
,
0
);
sti
();
if
(
put_user
(
status
,
value
))
return
-
EFAULT
;
return
0
;
}
static
int
get_modem_info
(
struct
sun_serial
*
info
,
unsigned
int
*
value
)
{
unsigned
char
status
;
unsigned
int
result
;
cli
();
status
=
sbus_readb
(
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
status
,
0
);
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
);
if
(
put_user
(
result
,
value
))
return
-
EFAULT
;
return
0
;
}
static
int
set_modem_info
(
struct
sun_serial
*
info
,
unsigned
int
cmd
,
unsigned
int
*
value
)
{
unsigned
int
arg
;
if
(
get_user
(
arg
,
value
))
return
-
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
;
cli
();
write_zsreg
(
info
->
zs_channel
,
5
,
(
info
->
curregs
[
5
]
|
SND_BRK
));
schedule_timeout
(
duration
);
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
:
if
(
put_user
(
C_CLOCAL
(
tty
)
?
1
:
0
,
(
unsigned
long
*
)
arg
))
return
-
EFAULT
;
return
0
;
case
TIOCSSOFTCAR
:
if
(
get_user
(
arg
,
(
unsigned
long
*
)
arg
))
return
-
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
:
if
(
copy_to_user
((
struct
sun_serial
*
)
arg
,
info
,
sizeof
(
struct
sun_serial
)))
return
-
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
;
schedule_timeout
(
info
->
close_delay
);
}
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
;
if
(
info
->
is_cons
)
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
);
}
/*
*
* line_info - returns information about each channel
*
*/
static
inline
int
line_info
(
char
*
buf
,
struct
sun_serial
*
info
)
{
unsigned
char
status
;
char
stat_buf
[
30
];
int
ret
;
ret
=
sprintf
(
buf
,
"%d: uart:Zilog8530 port:%x irq:%d"
,
info
->
line
,
info
->
port
,
info
->
irq
);
cli
();
status
=
sbus_readb
(
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
status
,
0
);
sti
();
stat_buf
[
0
]
=
0
;
stat_buf
[
1
]
=
0
;
if
(
info
->
curregs
[
5
]
&
RTS
)
strcat
(
stat_buf
,
"|RTS"
);
if
(
status
&
CTS
)
strcat
(
stat_buf
,
"|CTS"
);
if
(
info
->
curregs
[
5
]
&
DTR
)
strcat
(
stat_buf
,
"|DTR"
);
if
(
status
&
SYNC
)
strcat
(
stat_buf
,
"|DSR"
);
if
(
status
&
DCD
)
strcat
(
stat_buf
,
"|CD"
);
ret
+=
sprintf
(
buf
+
ret
,
" baud:%d %s
\n
"
,
info
->
zs_baud
,
stat_buf
+
1
);
return
ret
;
}
/*
*
* zs_read_proc() - called when /proc/tty/driver/serial is read.
*
*/
int
zs_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
char
*
revision
=
"$Revision: 1.71 $"
;
char
*
version
,
*
p
;
int
i
,
len
=
0
,
l
;
off_t
begin
=
0
;
version
=
strchr
(
revision
,
' '
);
p
=
strchr
(
++
version
,
' '
);
*
p
=
'\0'
;
len
+=
sprintf
(
page
,
"serinfo:1.0 driver:%s
\n
"
,
version
);
*
p
=
' '
;
for
(
i
=
0
;
i
<
NUM_CHANNELS
&&
len
<
4000
;
i
++
)
{
l
=
line_info
(
page
+
len
,
&
zs_soft
[
i
]);
len
+=
l
;
if
(
len
+
begin
>
off
+
count
)
goto
done
;
if
(
len
+
begin
<
off
)
{
begin
+=
len
;
len
=
0
;
}
}
*
eof
=
1
;
done:
if
(
off
>=
len
+
begin
)
return
0
;
*
start
=
page
+
(
off
-
begin
);
return
((
count
<
begin
+
len
-
off
)
?
count
:
begin
+
len
-
off
);
}
/*
* ------------------------------------------------------------
* zs_open() and friends
* ------------------------------------------------------------
*/
static
int
block_til_ready
(
struct
tty_struct
*
tty
,
struct
file
*
filp
,
struct
sun_serial
*
info
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
int
retval
,
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
();
set_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
(
signal_pending
(
current
))
{
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
;
}
if
(
!
tmp_buf
)
{
unsigned
long
page
=
get_free_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
if
(
tmp_buf
)
free_page
(
page
);
else
tmp_buf
=
(
unsigned
char
*
)
page
;
}
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
);
}
#ifdef CONFIG_SERIAL_CONSOLE
if
(
zs_console
.
cflag
&&
zs_console
.
index
==
line
)
{
tty
->
termios
->
c_cflag
=
zs_console
.
cflag
;
zs_console
.
cflag
=
0
;
change_speed
(
info
);
}
#endif
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.71 $"
;
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
sun_zslayout
*
__init
get_zs
(
int
chip
)
{
unsigned
int
vaddr
[
2
]
=
{
0
,
0
};
unsigned
long
mapped_addr
=
0
;
int
busnode
,
seen
,
zsnode
,
sun4u_ino
;
static
int
irq
=
0
;
if
(
chip
<
0
||
chip
>=
NUM_SERIAL
)
{
prom_printf
(
"get_zs bogon zs chip number"
);
prom_halt
();
}
if
(
central_bus
)
busnode
=
central_bus
->
child
->
prom_node
;
else
busnode
=
prom_searchsiblings
(
prom_getchild
(
prom_root_node
),
"sbus"
);
if
(
busnode
==
0
||
busnode
==
-
1
)
{
prom_printf
(
"get_zs: no zs bus to search"
);
prom_halt
();
}
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
==
-
1
||
central_bus
!=
NULL
)
{
struct
sbus_bus
*
sbus
=
NULL
;
struct
sbus_dev
*
sdev
=
NULL
;
/* "address" property is not guarenteed,
* everything in I/O is implicitly mapped
* anyways by our clever TLB miss handling
* scheme, so don't fail here. -DaveM
*/
if
(
central_bus
==
NULL
)
{
for_each_sbus
(
sbus
)
{
for_each_sbusdev
(
sdev
,
sbus
)
{
if
(
sdev
->
prom_node
==
zsnode
)
goto
found
;
}
}
}
found:
if
(
sdev
==
NULL
&&
central_bus
==
NULL
)
prom_halt
();
if
(
central_bus
==
NULL
)
{
mapped_addr
=
sbus_ioremap
(
&
sdev
->
resource
[
0
],
0
,
PAGE_SIZE
,
"Zilog Registers"
);
}
else
{
struct
linux_prom_registers
zsregs
[
1
];
int
err
;
err
=
prom_getproperty
(
zsnode
,
"reg"
,
(
char
*
)
&
zsregs
[
0
],
sizeof
(
zsregs
));
if
(
err
==
-
1
)
{
prom_printf
(
"ZS: Cannot map Zilog regs.
\n
"
);
prom_halt
();
}
apply_fhc_ranges
(
central_bus
->
child
,
&
zsregs
[
0
],
1
);
apply_central_ranges
(
central_bus
,
&
zsregs
[
0
],
1
);
mapped_addr
=
((((
u64
)
zsregs
[
0
].
which_io
)
<<
32UL
)
|
((
u64
)
zsregs
[
0
].
phys_addr
));
}
}
else
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
)
{
if
(
central_bus
)
{
unsigned
long
iclr
,
imap
;
iclr
=
central_bus
->
child
->
fhc_regs
.
uregs
+
FHC_UREGS_ICLR
;
imap
=
central_bus
->
child
->
fhc_regs
.
uregs
+
FHC_UREGS_IMAP
;
irq
=
zilog_irq
=
build_irq
(
12
,
0
,
iclr
,
imap
);
}
else
{
irq
=
zilog_irq
=
sbus_build_irq
(
sbus_root
,
sun4u_ino
);
}
}
break
;
}
zsnode
=
prom_getsibling
(
zsnode
);
seen
++
;
}
if
(
!
zsnode
)
panic
(
"get_zs: whee chip not found"
);
if
(
!
vaddr
[
0
]
&&
!
mapped_addr
)
panic
(
"get_zs: whee no serial chip mappable"
);
if
(
mapped_addr
!=
0
)
{
return
(
struct
sun_zslayout
*
)
mapped_addr
;
}
else
{
return
(
struct
sun_zslayout
*
)
prom_virt_to_phys
((
unsigned
long
)
vaddr
[
0
],
0
);
}
}
#else
/* !(__sparc_v9__) */
static
struct
sun_zslayout
*
__init
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
;
int
cpunode
=
0
,
bbnode
=
0
;
static
int
irq
=
0
;
int
chipid
=
chip
;
iospace
=
0
;
if
(
chip
<
0
||
chip
>=
NUM_SERIAL
)
panic
(
"get_zs bogon zs chip number"
);
if
(
sparc_cpu_model
==
sun4
)
{
struct
resource
dummy_resource
;
/* 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
;
dummy_resource
.
start
=
paddr
;
dummy_resource
.
end
=
paddr
+
8
-
1
;
dummy_resource
.
flags
=
IORESOURCE_IO
;
vaddr
[
0
]
=
sbus_ioremap
(
&
dummy_resource
,
0
,
8
,
"Zilog Serial"
);
}
else
{
/* Can use the prom for other machine types */
zsnode
=
prom_getchild
(
prom_root_node
);
if
(
sparc_cpu_model
==
sun4d
)
{
int
no
=
0
;
tmpnode
=
zsnode
;
zsnode
=
0
;
bbnode
=
0
;
while
(
tmpnode
&&
(
tmpnode
=
prom_searchsiblings
(
tmpnode
,
"cpu-unit"
)))
{
bbnode
=
prom_getchild
(
tmpnode
);
if
(
bbnode
&&
(
bbnode
=
prom_searchsiblings
(
bbnode
,
"bootbus"
)))
{
if
(
no
==
(
chip
>>
1
))
{
cpunode
=
tmpnode
;
zsnode
=
prom_getchild
(
bbnode
);
chipid
=
(
chip
&
1
);
break
;
}
no
++
;
}
tmpnode
=
prom_getsibling
(
tmpnode
);
}
if
(
!
tmpnode
)
panic
(
"get_zs: couldn't find %dth bootbus
\n
"
,
chip
>>
1
);
}
else
{
tmpnode
=
prom_searchsiblings
(
zsnode
,
"obio"
);
if
(
tmpnode
)
zsnode
=
prom_getchild
(
tmpnode
);
}
if
(
!
zsnode
)
panic
(
"get_zs no zs serial prom node"
);
while
(
zsnode
)
{
zsnode
=
prom_searchsiblings
(
zsnode
,
"zs"
);
slave
=
prom_getintdefault
(
zsnode
,
"slave"
,
-
1
);
if
(
slave
==
chipid
)
{
/* The one we want */
if
(
sparc_cpu_model
!=
sun4d
)
{
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"
);
}
}
else
{
/* On sun4d don't have address property :( */
struct
linux_prom_registers
zsreg
[
4
];
struct
resource
res
;
if
(
prom_getproperty
(
zsnode
,
"reg"
,
(
char
*
)
zsreg
,
sizeof
(
zsreg
))
==
-
1
)
{
prom_printf
(
"Cannot map zs regs
\n
"
);
prom_halt
();
}
prom_apply_generic_ranges
(
bbnode
,
cpunode
,
zsreg
,
1
);
res
.
start
=
zsreg
[
0
].
phys_addr
;
res
.
end
=
res
.
start
+
8
-
1
;
res
.
flags
=
zsreg
[
0
].
which_io
|
IORESOURCE_IO
;
vaddr
[
0
]
=
sbus_ioremap
(
&
res
,
0
,
8
,
"Zilog Serial"
);
}
zs_nodes
[
chip
]
=
zsnode
;
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
"
,
"intr"
,
len
,
sizeof
(
struct
linux_prom_irqs
));
panic
(
"zilog: intr 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
);
}
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
/* 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
));
}
void
__init
zs_init_alloc_failure
(
const
char
*
table_name
)
{
prom_printf
(
"zs_probe: Cannot alloc %s.
\n
"
,
table_name
);
prom_halt
();
}
void
*
__init
zs_alloc_bootmem
(
unsigned
long
size
)
{
void
*
ret
;
ret
=
__alloc_bootmem
(
size
,
SMP_CACHE_BYTES
,
0UL
);
if
(
ret
!=
NULL
)
memset
(
ret
,
0
,
size
);
return
ret
;
}
void
__init
zs_alloc_tables
(
void
)
{
zs_chips
=
(
struct
sun_zslayout
**
)
zs_alloc_bootmem
(
NUM_SERIAL
*
sizeof
(
struct
sun_zslayout
*
));
if
(
zs_chips
==
NULL
)
zs_init_alloc_failure
(
"zs_chips"
);
zs_channels
=
(
struct
sun_zschannel
**
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
sun_zschannel
*
));
if
(
zs_channels
==
NULL
)
zs_init_alloc_failure
(
"zs_channels"
);
zs_nodes
=
(
int
*
)
zs_alloc_bootmem
(
NUM_SERIAL
*
sizeof
(
int
));
if
(
zs_nodes
==
NULL
)
zs_init_alloc_failure
(
"zs_nodes"
);
zs_soft
=
(
struct
sun_serial
*
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
sun_serial
));
if
(
zs_soft
==
NULL
)
zs_init_alloc_failure
(
"zs_soft"
);
zs_ttys
=
(
struct
tty_struct
*
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
tty_struct
));
if
(
zs_ttys
==
NULL
)
zs_init_alloc_failure
(
"zs_ttys"
);
serial_table
=
(
struct
tty_struct
**
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
tty_struct
*
));
if
(
serial_table
==
NULL
)
zs_init_alloc_failure
(
"serial_table"
);
serial_termios
=
(
struct
termios
**
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
termios
*
));
if
(
serial_termios
==
NULL
)
zs_init_alloc_failure
(
"serial_termios"
);
serial_termios_locked
=
(
struct
termios
**
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
termios
*
));
if
(
serial_termios_locked
==
NULL
)
zs_init_alloc_failure
(
"serial_termios_locked"
);
}
int
__init
zs_probe
(
void
)
{
int
node
;
if
(
sparc_cpu_model
==
sun4
)
goto
no_probe
;
NUM_SERIAL
=
0
;
node
=
prom_getchild
(
prom_root_node
);
if
(
sparc_cpu_model
==
sun4d
)
{
int
bbnode
;
while
(
node
&&
(
node
=
prom_searchsiblings
(
node
,
"cpu-unit"
)))
{
bbnode
=
prom_getchild
(
node
);
if
(
bbnode
&&
prom_searchsiblings
(
bbnode
,
"bootbus"
))
NUM_SERIAL
+=
2
;
node
=
prom_getsibling
(
node
);
}
goto
no_probe
;
}
#ifdef __sparc_v9__
else
if
(
sparc_cpu_model
==
sun4u
)
{
int
central_node
;
/* Central bus zilogs must be checked for first,
* since Enterprise boxes might have SBUSes as well.
*/
central_node
=
prom_finddevice
(
"/central"
);
if
(
central_node
!=
0
&&
central_node
!=
-
1
)
node
=
prom_searchsiblings
(
prom_getchild
(
central_node
),
"fhc"
);
else
node
=
prom_searchsiblings
(
node
,
"sbus"
);
if
(
node
!=
0
&&
node
!=
-
1
)
node
=
prom_getchild
(
node
);
if
(
node
==
0
||
node
==
-
1
)
return
-
ENODEV
;
}
#endif
/* __sparc_v9__ */
else
{
node
=
prom_searchsiblings
(
node
,
"obio"
);
if
(
node
)
node
=
prom_getchild
(
node
);
NUM_SERIAL
=
2
;
goto
no_probe
;
}
node
=
prom_searchsiblings
(
node
,
"zs"
);
if
(
!
node
)
return
-
ENODEV
;
NUM_SERIAL
=
2
;
no_probe:
zs_alloc_tables
();
/* Fill in rs_ops struct... */
#ifdef CONFIG_SERIAL_CONSOLE
sunserial_setinitfunc
(
zs_console_init
);
#endif
sunserial_setinitfunc
(
zs_init
);
rs_ops
.
rs_kgdb_hook
=
zs_kgdb_hook
;
rs_ops
.
rs_change_mouse_baud
=
zs_change_mouse_baud
;
sunkbd_setinitfunc
(
sun_kbd_init
);
kbd_ops
.
compute_shiftstate
=
sun_compute_shiftstate
;
kbd_ops
.
setledstate
=
sun_setledstate
;
kbd_ops
.
getledstate
=
sun_getledstate
;
kbd_ops
.
setkeycode
=
sun_setkeycode
;
kbd_ops
.
getkeycode
=
sun_getkeycode
;
#if defined(__sparc_v9__) && defined(CONFIG_PCI)
sunkbd_install_keymaps
(
sun_key_maps
,
sun_keymap_count
,
sun_func_buf
,
sun_func_table
,
sun_funcbufsize
,
sun_funcbufleft
,
sun_accent_table
,
sun_accent_table_size
);
#endif
return
0
;
}
static
inline
void
zs_prepare
(
void
)
{
int
channel
,
chip
;
unsigned
long
flags
;
if
(
!
NUM_SERIAL
)
return
;
save_and_cli
(
flags
);
/* 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
].
line
=
channel
;
}
zs_soft
[
channel
].
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
;
}
restore_flags
(
flags
);
}
int
__init
zs_init
(
void
)
{
int
channel
,
brg
,
i
;
unsigned
long
flags
;
struct
sun_serial
*
info
;
char
dummy
;
/* Setup base handler, and timer table. */
init_bh
(
SERIAL_BH
,
do_serial_bh
);
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"
;
#ifdef CONFIG_DEVFS_FS
serial_driver
.
name
=
"tts/%d"
;
#else
serial_driver
.
name
=
"ttyS"
;
#endif
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 */
/* I just did. :-) -AIB 2001-12-23 */
serial_driver
.
read_proc
=
zs_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/%d"
;
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
"
);
save_flags
(
flags
);
cli
();
/* Initialize Softinfo */
zs_prepare
();
/* Grab IRQ line before poking the chips so we do
* not lose any interrupts.
*/
if
(
request_irq
(
zilog_irq
,
zs_interrupt
,
SA_SHIRQ
,
"Zilog8530"
,
zs_chain
))
{
prom_printf
(
"Unable to attach zs intr
\n
"
);
prom_halt
();
}
/* 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
);
ZSDELAY_LONG
();
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
;
init_waitqueue_head
(
&
info
->
open_wait
);
init_waitqueue_head
(
&
info
->
close_wait
);
printk
(
"tty%02d at 0x%04x (irq = %s)"
,
info
->
line
,
info
->
port
,
__irq_itoa
(
info
->
irq
));
printk
(
" is a Zilog8530
\n
"
);
}
restore_flags
(
flags
);
keyboard_zsinit
(
kbd_put_char
);
return
0
;
}
/* 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.
*/
static
void
__init
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
);
}
#ifdef CONFIG_SERIAL_CONSOLE
/* This is for console output over ttya/ttyb */
static
void
zs_console_putchar
(
struct
sun_serial
*
info
,
char
ch
)
{
int
loops
=
ZS_PUT_CHAR_MAX_DELAY
;
unsigned
long
flags
;
if
(
!
info
->
zs_channel
)
return
;
save_flags
(
flags
);
cli
();
zs_put_char
(
info
->
zs_channel
,
ch
);
while
(
!
(
read_zsreg
(
info
->
zs_channel
,
R1
)
&
ALL_SNT
)
&&
--
loops
)
udelay
(
5
);
restore_flags
(
flags
);
}
#ifdef SERIAL_CONSOLE_FAIR_OUTPUT
/*
* Fair output driver allows a process to speak.
*/
static
void
zs_fair_output
(
struct
sun_serial
*
info
)
{
unsigned
long
flags
;
int
left
;
/* Output no more than that */
char
c
;
if
(
info
==
NULL
)
return
;
if
(
info
->
xmit_buf
==
NULL
)
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_console_putchar
(
info
,
c
);
cli
();
left
=
MIN
(
info
->
xmit_cnt
,
left
-
1
);
}
/* Last character is being transmitted now (hopefully). */
sbus_writeb
(
RES_Tx_P
,
&
info
->
zs_channel
->
control
);
ZSDELAY
();
ZSLOG
(
REGCTRL
,
RES_Tx_P
,
1
);
restore_flags
(
flags
);
return
;
}
#endif
/*
* zs_console_write is registered for printk.
*/
static
void
zs_console_write
(
struct
console
*
con
,
const
char
*
s
,
unsigned
count
)
{
struct
sun_serial
*
info
;
int
i
;
info
=
zs_soft
+
con
->
index
;
for
(
i
=
0
;
i
<
count
;
i
++
,
s
++
)
{
if
(
*
s
==
'\n'
)
zs_console_putchar
(
info
,
'\r'
);
zs_console_putchar
(
info
,
*
s
);
}
#ifdef SERIAL_CONSOLE_FAIR_OUTPUT
/* Comment this if you want to have a strict interrupt-driven output */
zs_fair_output
(
info
);
#endif
}
static
kdev_t
zs_console_device
(
struct
console
*
con
)
{
return
mk_kdev
(
TTY_MAJOR
,
64
+
con
->
index
);
}
static
int
__init
zs_console_setup
(
struct
console
*
con
,
char
*
options
)
{
static
struct
tty_struct
c_tty
;
static
struct
termios
c_termios
;
struct
sun_serial
*
info
;
int
brg
,
baud
;
info
=
zs_soft
+
con
->
index
;
info
->
is_cons
=
1
;
printk
(
"Console: ttyS%d (Zilog8530)
\n
"
,
info
->
line
);
sunserial_console_termios
(
con
);
memset
(
&
c_tty
,
0
,
sizeof
(
c_tty
));
memset
(
&
c_termios
,
0
,
sizeof
(
c_termios
));
c_tty
.
termios
=
&
c_termios
;
c_termios
.
c_cflag
=
con
->
cflag
;
baud
=
tty_get_baud_rate
(
&
c_tty
);
info
->
zs_baud
=
baud
;
switch
(
con
->
cflag
&
CSIZE
)
{
case
CS5
:
zscons_regs
[
3
]
=
Rx5
|
RxENAB
;
zscons_regs
[
5
]
=
Tx5
|
TxENAB
;
info
->
parity_mask
=
0x1f
;
break
;
case
CS6
:
zscons_regs
[
3
]
=
Rx6
|
RxENAB
;
zscons_regs
[
5
]
=
Tx6
|
TxENAB
;
info
->
parity_mask
=
0x3f
;
break
;
case
CS7
:
zscons_regs
[
3
]
=
Rx7
|
RxENAB
;
zscons_regs
[
5
]
=
Tx7
|
TxENAB
;
info
->
parity_mask
=
0x7f
;
break
;
default:
case
CS8
:
zscons_regs
[
3
]
=
Rx8
|
RxENAB
;
zscons_regs
[
5
]
=
Tx8
|
TxENAB
;
info
->
parity_mask
=
0xff
;
break
;
}
zscons_regs
[
5
]
|=
DTR
;
if
(
con
->
cflag
&
PARENB
)
zscons_regs
[
4
]
|=
PAR_ENAB
;
if
(
!
(
con
->
cflag
&
PARODD
))
zscons_regs
[
4
]
|=
PAR_EVEN
;
if
(
con
->
cflag
&
CSTOPB
)
zscons_regs
[
4
]
|=
SB2
;
else
zscons_regs
[
4
]
|=
SB1
;
brg
=
BPS_TO_BRG
(
baud
,
ZS_CLOCK
/
info
->
clk_divisor
);
zscons_regs
[
12
]
=
brg
&
0xff
;
zscons_regs
[
13
]
=
(
brg
>>
8
)
&
0xff
;
memcpy
(
info
->
curregs
,
zscons_regs
,
sizeof
(
zscons_regs
));
load_zsregs
(
info
,
zscons_regs
);
ZS_CLEARERR
(
info
->
zs_channel
);
ZS_CLEARFIFO
(
info
->
zs_channel
);
return
0
;
}
static
struct
console
zs_console
=
{
.
name
=
"ttyS"
,
.
write
=
zs_console_write
,
.
device
=
zs_console_device
,
.
setup
=
zs_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
};
static
int
__init
zs_console_init
(
void
)
{
extern
int
con_is_present
(
void
);
if
(
con_is_present
())
return
0
;
zs_console
.
index
=
serial_console
-
1
;
register_console
(
&
zs_console
);
return
0
;
}
#endif
/* CONFIG_SERIAL_CONSOLE */
drivers/sbus/char/zs.h
deleted
100644 → 0
View file @
aa2a3d64
/* $Id: zs.h,v 1.3 1999/09/21 14:38:18 davem 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 _ZS_H
#define _ZS_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
;
wait_queue_head_t
open_wait
;
wait_queue_head_t
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 { sbus_writeb(ERR_RES, &channel->control); \
udelay(5); } while(0)
#define ZS_CLEARSTAT(channel) do { sbus_writeb(RES_EXT_INT, &channel->control); \
udelay(5); } while(0)
#define ZS_CLEARFIFO(channel) do { sbus_readb(&channel->data); \
udelay(2); \
sbus_readb(&channel->data); \
udelay(2); \
sbus_readb(&channel->data); \
udelay(2); } while(0)
#endif
/* !(_ZS_H) */
drivers/s
bus/char
/sunkbd.c
→
drivers/s
erial
/sunkbd.c
View file @
d4d9c3ac
File moved
drivers/s
bus/char
/sunkbd.h
→
drivers/s
erial
/sunkbd.h
View file @
d4d9c3ac
File moved
drivers/s
bus/char
/sunmouse.c
→
drivers/s
erial
/sunmouse.c
View file @
d4d9c3ac
File moved
drivers/s
bus/char
/sunmouse.h
→
drivers/s
erial
/sunmouse.h
View file @
d4d9c3ac
File moved
drivers/s
bus/char/sab82532
.c
→
drivers/s
erial/sunsab
.c
View file @
d4d9c3ac
File moved
drivers/s
bus/char/
su.c
→
drivers/s
erial/sun
su.c
View file @
d4d9c3ac
File moved
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment