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
nexedi
linux
Commits
e79e45cb
Commit
e79e45cb
authored
Aug 03, 2002
by
David S. Miller
Browse files
Options
Browse Files
Download
Plain Diff
Merge nuts.ninka.net:/home/davem/src/BK/BAK-sparc-2.5
into nuts.ninka.net:/home/davem/src/BK/sparc-2.5
parents
757a6bee
4d8ba057
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
938 additions
and
2430 deletions
+938
-2430
arch/sparc64/kernel/devices.c
arch/sparc64/kernel/devices.c
+6
-0
arch/sparc64/kernel/smp.c
arch/sparc64/kernel/smp.c
+77
-88
arch/sparc64/kernel/sparc64_ksyms.c
arch/sparc64/kernel/sparc64_ksyms.c
+4
-0
arch/sparc64/solaris/systbl.S
arch/sparc64/solaris/systbl.S
+2
-2
drivers/net/myri_sbus.c
drivers/net/myri_sbus.c
+8
-2
drivers/net/myri_sbus.h
drivers/net/myri_sbus.h
+1
-0
drivers/sbus/char/openprom.c
drivers/sbus/char/openprom.c
+0
-27
drivers/scsi/qlogicpti.c
drivers/scsi/qlogicpti.c
+21
-19
drivers/serial/sunsab.c
drivers/serial/sunsab.c
+767
-2213
drivers/serial/sunsab.h
drivers/serial/sunsab.h
+4
-63
drivers/serial/sunsu.c
drivers/serial/sunsu.c
+16
-5
drivers/serial/sunzilog.c
drivers/serial/sunzilog.c
+20
-9
include/asm-sparc64/ns87303.h
include/asm-sparc64/ns87303.h
+8
-2
include/linux/serial_core.h
include/linux/serial_core.h
+4
-0
No files found.
arch/sparc64/kernel/devices.c
View file @
e79e45cb
...
...
@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/page.h>
#include <asm/oplib.h>
...
...
@@ -17,6 +18,11 @@
#include <asm/smp.h>
#include <asm/spitfire.h>
/* Used to synchronize acceses to NatSemi SUPER I/O chip configure
* operations in asm/ns87303.h
*/
spinlock_t
ns87303_lock
=
SPIN_LOCK_UNLOCKED
;
struct
prom_cpuinfo
linux_cpus
[
NR_CPUS
]
__initdata
=
{
{
0
}
};
unsigned
prom_cpu_nodes
[
NR_CPUS
];
int
linux_num_cpus
=
0
;
...
...
arch/sparc64/kernel/smp.c
View file @
e79e45cb
...
...
@@ -45,7 +45,6 @@ cpuinfo_sparc cpu_data[NR_CPUS];
/* Please don't make this stuff initdata!!! --DaveM */
static
unsigned
char
boot_cpu_id
;
static
int
smp_activated
;
/* Kernel spinlock */
spinlock_t
kernel_flag
__cacheline_aligned_in_smp
=
SPIN_LOCK_UNLOCKED
;
...
...
@@ -223,85 +222,46 @@ extern unsigned long sparc64_cpu_startup;
*/
static
struct
thread_info
*
cpu_new_thread
=
NULL
;
static
void
__init
smp_boot_cpus
(
unsigned
int
max_cpus
)
static
int
__devinit
smp_boot_one_cpu
(
unsigned
int
cpu
)
{
int
cpucount
=
0
,
i
;
unsigned
long
entry
=
(
unsigned
long
)(
&
sparc64_cpu_startup
);
unsigned
long
cookie
=
(
unsigned
long
)(
&
cpu_new_thread
);
struct
task_struct
*
p
;
int
timeout
,
no
,
ret
;
printk
(
"Entering UltraSMPenguin Mode...
\n
"
);
local_irq_enable
();
smp_store_cpu_info
(
boot_cpu_id
);
if
(
linux_num_cpus
==
1
)
return
;
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
{
if
(
i
==
boot_cpu_id
)
continue
;
if
((
cpucount
+
1
)
==
max_cpus
)
goto
ignorecpu
;
if
(
test_bit
(
i
,
&
phys_cpu_present_map
))
{
unsigned
long
entry
=
(
unsigned
long
)(
&
sparc64_cpu_startup
);
unsigned
long
cookie
=
(
unsigned
long
)(
&
cpu_new_thread
);
struct
task_struct
*
p
;
int
timeout
;
int
no
;
kernel_thread
(
NULL
,
NULL
,
CLONE_IDLETASK
);
prom_printf
(
"Starting CPU %d... "
,
i
);
kernel_thread
(
NULL
,
NULL
,
CLONE_IDLETASK
);
cpucount
++
;
p
=
prev_task
(
&
init_task
);
p
=
prev_task
(
&
init_task
);
init_idle
(
p
,
cpu
);
init_idle
(
p
,
i
);
unhash_process
(
p
);
unhash_process
(
p
);
callin_flag
=
0
;
for
(
no
=
0
;
no
<
linux_num_cpus
;
no
++
)
if
(
linux_cpus
[
no
].
mid
==
i
)
break
;
cpu_new_thread
=
p
->
thread_info
;
set_bit
(
i
,
&
cpu_callout_map
);
prom_startcpu
(
linux_cpus
[
no
].
prom_node
,
entry
,
cookie
);
for
(
timeout
=
0
;
timeout
<
5000000
;
timeout
++
)
{
if
(
callin_flag
)
break
;
udelay
(
100
);
}
if
(
callin_flag
)
{
prom_cpu_nodes
[
i
]
=
linux_cpus
[
no
].
prom_node
;
prom_printf
(
"OK
\n
"
);
}
else
{
cpucount
--
;
printk
(
"Processor %d is stuck.
\n
"
,
i
);
prom_printf
(
"FAILED
\n
"
);
clear_bit
(
i
,
&
cpu_callout_map
);
}
ignorecpu:
}
callin_flag
=
0
;
for
(
no
=
0
;
no
<
linux_num_cpus
;
no
++
)
if
(
linux_cpus
[
no
].
mid
==
cpu
)
break
;
cpu_new_thread
=
p
->
thread_info
;
set_bit
(
cpu
,
&
cpu_callout_map
);
prom_startcpu
(
linux_cpus
[
no
].
prom_node
,
entry
,
cookie
);
for
(
timeout
=
0
;
timeout
<
5000000
;
timeout
++
)
{
if
(
callin_flag
)
break
;
udelay
(
100
);
}
cpu_new_thread
=
NULL
;
if
(
cpucount
==
0
)
{
if
(
max_cpus
!=
1
)
printk
(
"Error: only one processor found.
\n
"
);
if
(
callin_flag
)
{
prom_cpu_nodes
[
cpu
]
=
linux_cpus
[
no
].
prom_node
;
ret
=
0
;
}
else
{
unsigned
long
bogosum
=
0
;
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
{
if
(
cpu_online
(
i
))
bogosum
+=
cpu_data
[
i
].
udelay_val
;
}
printk
(
"Total of %d processors activated "
"(%lu.%02lu BogoMIPS).
\n
"
,
cpucount
+
1
,
bogosum
/
(
500000
/
HZ
),
(
bogosum
/
(
5000
/
HZ
))
%
100
);
smp_activated
=
1
;
printk
(
"Processor %d is stuck.
\n
"
,
cpu
);
clear_bit
(
cpu
,
&
cpu_callout_map
);
ret
=
-
ENODEV
;
}
cpu_new_thread
=
NULL
;
return
ret
;
}
static
void
spitfire_xcall_helper
(
u64
data0
,
u64
data1
,
u64
data2
,
u64
pstate
,
unsigned
long
cpu
)
...
...
@@ -1119,8 +1079,6 @@ static void __init smp_setup_percpu_timer(void)
void
__init
smp_tick_init
(
void
)
{
int
i
;
boot_cpu_id
=
hard_smp_processor_id
();
current_tick_offset
=
timer_tick_offset
;
...
...
@@ -1129,19 +1087,10 @@ void __init smp_tick_init(void)
prom_halt
();
}
atomic_set
(
&
sparc64_num_cpus_online
,
1
);
memset
(
&
cpu_online_map
,
0
,
sizeof
(
cpu_online_map
));
atomic_inc
(
&
sparc64_num_cpus_online
);
set_bit
(
boot_cpu_id
,
&
cpu_online_map
);
prom_cpu_nodes
[
boot_cpu_id
]
=
linux_cpus
[
0
].
prom_node
;
prof_counter
(
boot_cpu_id
)
=
prof_multiplier
(
boot_cpu_id
)
=
1
;
for
(
i
=
0
;
i
<
linux_num_cpus
;
i
++
)
{
if
(
linux_cpus
[
i
].
mid
<
NR_CPUS
)
{
set_bit
(
linux_cpus
[
i
].
mid
,
&
phys_cpu_present_map
);
atomic_inc
(
&
sparc64_num_cpus_possible
);
}
}
}
cycles_t
cacheflush_time
;
...
...
@@ -1272,19 +1221,59 @@ int setup_profiling_timer(unsigned int multiplier)
void
__init
smp_prepare_cpus
(
unsigned
int
max_cpus
)
{
smp_boot_cpus
(
max_cpus
);
int
i
;
for
(
i
=
0
;
i
<
linux_num_cpus
;
i
++
)
{
if
(
linux_cpus
[
i
].
mid
<
max_cpus
)
{
set_bit
(
linux_cpus
[
i
].
mid
,
&
phys_cpu_present_map
);
atomic_inc
(
&
sparc64_num_cpus_possible
);
}
}
if
(
atomic_read
(
&
sparc64_num_cpus_possible
)
>
max_cpus
)
{
for
(
i
=
linux_num_cpus
-
1
;
i
>=
0
;
i
--
)
{
if
(
linux_cpus
[
i
].
mid
!=
boot_cpu_id
)
{
clear_bit
(
linux_cpus
[
i
].
mid
,
&
phys_cpu_present_map
);
atomic_dec
(
&
sparc64_num_cpus_possible
);
if
(
atomic_read
(
&
sparc64_num_cpus_possible
)
<=
max_cpus
)
break
;
}
}
}
smp_store_cpu_info
(
boot_cpu_id
);
}
int
__devinit
__cpu_up
(
unsigned
int
cpu
)
{
set_bit
(
cpu
,
&
smp_commenced_mask
);
while
(
!
test_bit
(
cpu
,
&
cpu_online_map
))
mb
();
return
0
;
int
ret
=
smp_boot_one_cpu
(
cpu
);
if
(
!
ret
)
{
set_bit
(
cpu
,
&
smp_commenced_mask
);
while
(
!
test_bit
(
cpu
,
&
cpu_online_map
))
mb
();
if
(
!
test_bit
(
cpu
,
&
cpu_online_map
))
ret
=
-
ENODEV
;
}
return
ret
;
}
void
__init
smp_cpus_done
(
unsigned
int
max_cpus
)
{
unsigned
long
bogosum
=
0
;
int
i
;
for
(
i
=
0
;
i
<
NR_CPUS
;
i
++
)
{
if
(
cpu_online
(
i
))
bogosum
+=
cpu_data
[
i
].
udelay_val
;
}
printk
(
"Total of %d processors activated "
"(%lu.%02lu BogoMIPS).
\n
"
,
num_online_cpus
(),
bogosum
/
(
500000
/
HZ
),
(
bogosum
/
(
5000
/
HZ
))
%
100
);
/* We want to run this with all the other cpus spinning
* in the kernel.
*/
...
...
arch/sparc64/kernel/sparc64_ksyms.c
View file @
e79e45cb
...
...
@@ -52,6 +52,7 @@
#include <asm/isa.h>
#endif
#include <asm/a.out.h>
#include <asm/ns87303.h>
struct
poll
{
int
fd
;
...
...
@@ -373,3 +374,6 @@ EXPORT_SYMBOL(kbd_pt_regs);
#ifdef CONFIG_DEBUG_BUGVERBOSE
EXPORT_SYMBOL
(
do_BUG
);
#endif
/* for ns8703 */
EXPORT_SYMBOL
(
ns87303_lock
);
arch/sparc64/solaris/systbl.S
View file @
e79e45cb
...
...
@@ -250,8 +250,8 @@ solaris_sys_table:
.
word
solaris_fstatvfs64
/*
fstatvfs64
dP
219
*/
.
word
solaris_setrlimit64
/*
setrlimit64
dP
220
*/
.
word
solaris_getrlimit64
/*
getrlimit64
dP
221
*/
.
word
CHAIN
(
pread
)
/*
pread64
dpdD
222
*/
.
word
CHAIN
(
pwrite
)
/*
pwrite64
dpdD
223
*/
.
word
CHAIN
(
pread
64
)
/*
pread64
dpdD
222
*/
.
word
CHAIN
(
pwrite
64
)
/*
pwrite64
dpdD
223
*/
.
word
CHAIN
(
creat
)
/*
creat64
so
224
*/
.
word
solaris_open
/*
open64
soo
225
*/
.
word
solaris_unimplemented
/*
226
*/
...
...
drivers/net/myri_sbus.c
View file @
e79e45cb
...
...
@@ -546,8 +546,11 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct
myri_eth
*
mp
=
(
struct
myri_eth
*
)
dev
->
priv
;
unsigned
long
lregs
=
mp
->
lregs
;
struct
myri_channel
*
chan
=
&
mp
->
shmem
->
channel
;
unsigned
long
flags
;
u32
status
;
spin_lock_irqsave
(
&
mp
->
irq_lock
,
flags
);
status
=
sbus_readl
(
lregs
+
LANAI_ISTAT
);
DIRQ
((
"myri_interrupt: status[%08x] "
,
status
));
if
(
status
&
ISTAT_HOST
)
{
...
...
@@ -569,6 +572,8 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs)
myri_enable_irq
(
lregs
,
mp
->
cregs
);
}
DIRQ
((
"
\n
"
));
spin_unlock_irqrestore
(
&
mp
->
irq_lock
,
flags
);
}
static
int
myri_open
(
struct
net_device
*
dev
)
...
...
@@ -622,7 +627,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
return
1
;
}
s
ave_and_cli
(
flags
);
s
pin_lock_irqsave
(
&
mp
->
irq_lock
,
flags
);
DHDR
((
"xmit[skbdata(%p)]
\n
"
,
skb
->
data
));
#ifdef DEBUG_HEADER
...
...
@@ -669,7 +674,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
DTX
((
"tbusy=0, returning 0
\n
"
));
netif_start_queue
(
dev
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
mp
->
irq_lock
,
flags
);
return
0
;
}
...
...
@@ -900,6 +905,7 @@ static int __init myri_ether_init(struct net_device *dev, struct sbus_dev *sdev,
printk
(
"%s: MyriCOM MyriNET Ethernet "
,
dev
->
name
);
mp
=
(
struct
myri_eth
*
)
dev
->
priv
;
spin_lock_init
(
&
mp
->
irq_lock
);
mp
->
myri_sdev
=
sdev
;
/* Clean out skb arrays. */
...
...
drivers/net/myri_sbus.h
View file @
e79e45cb
...
...
@@ -269,6 +269,7 @@ struct myri_eth {
/* These are frequently accessed, keep together
* to obtain good cache hit rates.
*/
spinlock_t
irq_lock
;
struct
myri_shmem
*
shmem
;
/* Shared data structures. */
unsigned
long
cregs
;
/* Control register space. */
struct
recvq
*
rqack
;
/* Where we ack rx's. */
...
...
drivers/sbus/char/openprom.c
View file @
e79e45cb
...
...
@@ -147,7 +147,6 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
DATA
*
data
=
(
DATA
*
)
file
->
private_data
;
char
buffer
[
OPROMMAXPARAM
+
1
],
*
buf
;
struct
openpromio
*
opp
;
unsigned
long
flags
;
int
bufsize
,
len
,
error
=
0
;
extern
char
saved_command_line
[];
static
int
cnt
;
...
...
@@ -163,18 +162,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
switch
(
cmd
)
{
case
OPROMGETOPT
:
case
OPROMGETPROP
:
save_and_cli
(
flags
);
len
=
prom_getproplen
(
node
,
opp
->
oprom_array
);
restore_flags
(
flags
);
if
(
len
<=
0
||
len
>
bufsize
)
{
error
=
copyout
((
void
*
)
arg
,
opp
,
sizeof
(
int
));
break
;
}
save_and_cli
(
flags
);
len
=
prom_getproperty
(
node
,
opp
->
oprom_array
,
buffer
,
bufsize
);
restore_flags
(
flags
);
memcpy
(
opp
->
oprom_array
,
buffer
,
len
);
opp
->
oprom_array
[
len
]
=
'\0'
;
...
...
@@ -185,9 +180,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
case
OPROMNXTOPT
:
case
OPROMNXTPROP
:
save_and_cli
(
flags
);
buf
=
prom_nextprop
(
node
,
opp
->
oprom_array
,
buffer
);
restore_flags
(
flags
);
len
=
strlen
(
buf
);
if
(
len
==
0
||
len
+
1
>
bufsize
)
{
...
...
@@ -207,10 +200,8 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
buf
=
opp
->
oprom_array
+
strlen
(
opp
->
oprom_array
)
+
1
;
len
=
opp
->
oprom_array
+
bufsize
-
buf
;
save_and_cli
(
flags
);
error
=
prom_setprop
(
options_node
,
opp
->
oprom_array
,
buf
,
len
);
restore_flags
(
flags
);
if
(
error
<
0
)
error
=
-
EINVAL
;
...
...
@@ -226,13 +217,11 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
node
=
*
((
int
*
)
opp
->
oprom_array
);
save_and_cli
(
flags
);
switch
(
cmd
)
{
case
OPROMNEXT
:
node
=
__prom_getsibling
(
node
);
break
;
case
OPROMCHILD
:
node
=
__prom_getchild
(
node
);
break
;
case
OPROMSETCUR
:
break
;
}
restore_flags
(
flags
);
data
->
current_node
=
node
;
*
((
int
*
)
opp
->
oprom_array
)
=
node
;
...
...
@@ -264,9 +253,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
break
;
case
OPROMPATH2NODE
:
save_and_cli
(
flags
);
node
=
prom_finddevice
(
opp
->
oprom_array
);
restore_flags
(
flags
);
data
->
current_node
=
node
;
*
((
int
*
)
opp
->
oprom_array
)
=
node
;
opp
->
oprom_size
=
sizeof
(
int
);
...
...
@@ -361,7 +348,6 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
{
DATA
*
data
=
(
DATA
*
)
file
->
private_data
;
struct
opiocdesc
op
;
unsigned
long
flags
;
int
error
,
node
,
len
;
char
*
str
,
*
tmp
;
char
buffer
[
64
];
...
...
@@ -379,9 +365,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if
(
error
)
return
error
;
save_and_cli
(
flags
);
len
=
prom_getproplen
(
op
.
op_nodeid
,
str
);
restore_flags
(
flags
);
if
(
len
>
op
.
op_buflen
)
{
kfree
(
str
);
...
...
@@ -405,9 +389,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return
-
ENOMEM
;
}
save_and_cli
(
flags
);
prom_getproperty
(
op
.
op_nodeid
,
str
,
tmp
,
len
);
restore_flags
(
flags
);
tmp
[
len
]
=
'\0'
;
...
...
@@ -431,9 +413,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if
(
error
)
return
error
;
save_and_cli
(
flags
);
tmp
=
prom_nextprop
(
op
.
op_nodeid
,
str
,
buffer
);
restore_flags
(
flags
);
if
(
tmp
)
{
len
=
strlen
(
tmp
);
...
...
@@ -481,9 +461,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return
error
;
}
save_and_cli
(
flags
);
len
=
prom_setprop
(
op
.
op_nodeid
,
str
,
tmp
,
op
.
op_buflen
+
1
);
restore_flags
(
flags
);
if
(
len
!=
op
.
op_buflen
)
return
-
EINVAL
;
...
...
@@ -503,12 +481,10 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if
(
copy_from_user
(
&
node
,
(
void
*
)
arg
,
sizeof
(
int
)))
return
-
EFAULT
;
save_and_cli
(
flags
);
if
(
cmd
==
OPIOCGETNEXT
)
node
=
__prom_getsibling
(
node
);
else
node
=
__prom_getchild
(
node
);
restore_flags
(
flags
);
if
(
__copy_to_user
((
void
*
)
arg
,
&
node
,
sizeof
(
int
)))
return
-
EFAULT
;
...
...
@@ -624,7 +600,6 @@ static struct miscdevice openprom_dev = {
static
int
__init
openprom_init
(
void
)
{
unsigned
long
flags
;
int
error
;
error
=
misc_register
(
&
openprom_dev
);
...
...
@@ -633,10 +608,8 @@ static int __init openprom_init(void)
return
error
;
}
save_and_cli
(
flags
);
options_node
=
prom_getchild
(
prom_root_node
);
options_node
=
prom_searchsiblings
(
options_node
,
"options"
);
restore_flags
(
flags
);
if
(
options_node
==
0
||
options_node
==
-
1
)
{
printk
(
KERN_ERR
"openprom: unable to find options node
\n
"
);
...
...
drivers/scsi/qlogicpti.c
View file @
e79e45cb
...
...
@@ -318,7 +318,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
risc_code_addr
=
0x1000
;
/* all load addresses are at 0x1000 */
s
ave_flags
(
flags
);
cli
(
);
s
pin_lock_irqsave
(
&
qpti
->
lock
,
flags
);
sbus_writew
(
HCCTRL_PAUSE
,
qpti
->
qregs
+
HCCTRL
);
...
...
@@ -366,7 +366,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if
(
qlogicpti_mbox_command
(
qpti
,
param
,
1
))
{
printk
(
KERN_EMERG
"qlogicpti%d: Cannot execute ISP firmware.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -377,7 +377,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
(
param
[
0
]
!=
MBOX_COMMAND_COMPLETE
))
{
printk
(
KERN_EMERG
"qlogicpti%d: Cannot set initiator SCSI ID.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -392,7 +392,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if
(
qlogicpti_mbox_command
(
qpti
,
param
,
1
))
{
printk
(
KERN_EMERG
"qlogicpti%d: Cannot init response queue.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -404,7 +404,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if
(
qlogicpti_mbox_command
(
qpti
,
param
,
1
))
{
printk
(
KERN_EMERG
"qlogicpti%d: Cannot init request queue.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -450,7 +450,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
qlogicpti_mbox_command
(
qpti
,
param
,
0
);
qpti
->
send_marker
=
1
;
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
0
;
}
...
...
@@ -468,7 +468,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
risc_code_addr
=
0x1000
;
/* all f/w modules load at 0x1000 */
risc_code_length
=
sbus_risc_code_length01
;
s
ave_flags
(
flags
);
cli
(
);
s
pin_lock_irqsave
(
&
qpti
->
lock
,
flags
);
/* Verify the checksum twice, one before loading it, and once
* afterwards via the mailbox commands.
...
...
@@ -476,7 +476,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
for
(
i
=
0
;
i
<
risc_code_length
;
i
++
)
csum
+=
risc_code
[
i
];
if
(
csum
)
{
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
printk
(
KERN_EMERG
"qlogicpti%d: Aieee, firmware checksum failed!"
,
qpti
->
qpti_id
);
return
1
;
...
...
@@ -488,7 +488,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
while
(
--
timeout
&&
(
sbus_readw
(
qpti
->
qregs
+
SBUS_CTRL
)
&
SBUS_CTRL_RESET
))
udelay
(
20
);
if
(
!
timeout
)
{
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
printk
(
KERN_EMERG
"qlogicpti%d: Cannot reset the ISP."
,
qpti
->
qpti_id
);
return
1
;
}
...
...
@@ -528,7 +528,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
if
(
qlogicpti_mbox_command
(
qpti
,
param
,
1
))
{
printk
(
KERN_EMERG
"qlogicpti%d: Cannot stop firmware for reload.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -541,7 +541,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
param
[
0
]
!=
MBOX_COMMAND_COMPLETE
)
{
printk
(
"qlogicpti%d: Firmware dload failed, I'm bolixed!
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
}
...
...
@@ -561,7 +561,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(
param
[
0
]
!=
MBOX_COMMAND_COMPLETE
))
{
printk
(
KERN_EMERG
"qlogicpti%d: New firmware csum failure!
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -575,7 +575,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(
param
[
0
]
!=
MBOX_COMMAND_COMPLETE
))
{
printk
(
KERN_EMERG
"qlogicpti%d: AboutFirmware cmd fails.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -591,7 +591,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(
param
[
0
]
!=
MBOX_COMMAND_COMPLETE
))
{
printk
(
KERN_EMERG
"qlogicpti%d: could not set clock rate.
\n
"
,
qpti
->
qpti_id
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
1
;
}
...
...
@@ -608,7 +608,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
qlogicpti_mbox_command
(
qpti
,
param
,
1
);
}
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
0
;
}
...
...
@@ -1166,8 +1166,8 @@ static void ourdone(Scsi_Cmnd *Cmnd)
int
qlogicpti_queuecommand_slow
(
Scsi_Cmnd
*
Cmnd
,
void
(
*
done
)(
Scsi_Cmnd
*
))
{
unsigned
long
flags
;
struct
qlogicpti
*
qpti
=
(
struct
qlogicpti
*
)
Cmnd
->
host
->
hostdata
;
unsigned
long
flags
;
/*
* done checking this host adapter?
...
...
@@ -1178,12 +1178,13 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if
(
qpti
->
sbits
&&
qpti
->
sbits
!=
0xffff
)
{
/* See above about in ourdone this ugliness... */
Cmnd
->
SCp
.
Message
=
((
unsigned
long
)
done
)
&
0xffffffff
;
#ifdef
__sparc_v9__
#ifdef
CONFIG_SPARC64
Cmnd
->
SCp
.
Status
=
((
unsigned
long
)
done
>>
32UL
)
&
0xffffffff
;
#endif
return
qlogicpti_queuecommand
(
Cmnd
,
ourdone
);
}
save_flags
(
flags
);
cli
();
spin_lock_irqsave
(
&
qpti
->
lock
,
flags
);
/*
* We've peeked at all targets for this bus- time
...
...
@@ -1226,7 +1227,8 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if
(
qpti
==
NULL
)
Cmnd
->
host
->
hostt
->
queuecommand
=
qlogicpti_queuecommand
;
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
qpti
->
lock
,
flags
);
return
qlogicpti_queuecommand
(
Cmnd
,
done
);
}
...
...
drivers/serial/sunsab.c
View file @
e79e45cb
/* $Id: sab82532.c,v 1.66 2002/01/08 16:00:16 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*
* Rewrote buffer handling to use CIRC(Circular Buffer) macros.
* Maxim Krasnyanskiy <maxk@qualcomm.com>
...
...
@@ -10,92 +10,61 @@
* rates to be programmed into the UART. Also eliminated a lot of
* duplicated code in the console setup.
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*
* Ported to new 2.5.x UART layer.
* David S. Miller <davem@redhat.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/console.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/sab82532.h>
#include <asm/uaccess.h>
#include <asm/ebus.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#include "sunserial.h"
static
DECLARE_TASK_QUEUE
(
tq_serial
);
/* This is (one of many) a special gross hack to allow SU and
* SAB serials to co-exist on the same machine. -DaveM
*/
#undef SERIAL_BH
#define SERIAL_BH AURORA_BH
static
struct
tty_driver
serial_driver
,
callout_driver
;
static
int
sab82532_refcount
;
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
/* Set of debugging defines */
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
#undef SERIAL_DEBUG_MODEM
#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
#undef SERIAL_DEBUG_SEND_BREAK
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_FIFO
#define SERIAL_DEBUG_OVERFLOW 1
/* Trace things on serial device, useful for console debugging: */
#undef SERIAL_LOG_DEVICE
#ifdef SERIAL_LOG_DEVICE
static
void
dprint_init
(
int
tty
);
#endif
static
void
change_speed
(
struct
sab82532
*
info
);
static
void
sab82532_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
);
#include <linux/serial_core.h>
#include "suncore.h"
#include "sunsab.h"
struct
uart_sunsab_port
{
struct
uart_port
port
;
/* Generic UART port */
union
sab82532_async_regs
*
regs
;
/* Chip registers */
unsigned
long
irqflags
;
/* IRQ state flags */
int
xmit_fifo_size
;
/* TX fifo size */
int
recv_fifo_size
;
/* RX fifo size */
int
dsr
;
/* Current DSR state */
unsigned
int
cec_timeout
;
/* Chip poll timeout... */
unsigned
int
tec_timeout
;
/* likewise */
unsigned
char
interrupt_mask0
;
/* ISR0 masking */
unsigned
char
interrupt_mask1
;
/* ISR1 masking */
unsigned
char
pvr_dtr_bit
;
/* Which PVR bit is DTR */
unsigned
char
pvr_dsr_bit
;
/* Which PVR bit is DSR */
int
type
;
/* SAB82532 version */
int
sab_line
;
/* Internal numbering */
unsigned
int
irq
;
/* Device interrupt */
};
/*
* This assumes you have a 29.4912 MHz clock for your UART.
*/
#define BASE_BAUD ( 29491200 / 16 )
static
struct
sab82532
*
sab82532_chain
=
0
;
static
struct
tty_struct
*
sab82532_table
[
NR_PORTS
];
static
struct
termios
*
sab82532_termios
[
NR_PORTS
];
static
struct
termios
*
sab82532_termios_locked
[
NR_PORTS
];
#ifdef CONFIG_SERIAL_CONSOLE
extern
int
serial_console
;
static
struct
console
sab82532_console
;
static
int
sab82532_console_init
(
void
);
static
void
batten_down_hatches
(
struct
sab82532
*
info
);
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#define SAB_BASE_BAUD ( 29491200 / 16 )
static
char
*
sab82532_version
[
16
]
=
{
"V1.0"
,
"V2.0"
,
"V3.2"
,
"V(0x03)"
,
...
...
@@ -103,840 +72,600 @@ static char *sab82532_version[16] = {
"V(0x08)"
,
"V(0x09)"
,
"V(0x0a)"
,
"V(0x0b)"
,
"V(0x0c)"
,
"V(0x0d)"
,
"V(0x0e)"
,
"V(0x0f)"
};
static
char
serial_version
[
16
];
/*
* tmp_buf is used as a temporary buffer by sab82532_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the serial ports, since it significantly saves
* memory if large numbers of serial ports are open.
*/
static
unsigned
char
*
tmp_buf
=
0
;
static
DECLARE_MUTEX
(
tmp_buf_sem
);
static
inline
int
serial_paranoia_check
(
struct
sab82532
*
info
,
kdev_t
device
,
const
char
*
routine
)
{
#ifdef SERIAL_PARANOIA_CHECK
static
const
char
*
badmagic
=
"Warning: bad magic number for serial struct (%s) in %s
\n
"
;
static
const
char
*
badinfo
=
"Warning: null sab82532 for (%s) in %s
\n
"
;
if
(
!
info
)
{
printk
(
badinfo
,
kdevname
(
device
),
routine
);
return
1
;
}
if
(
info
->
magic
!=
SERIAL_MAGIC
)
{
printk
(
badmagic
,
kdevname
(
device
),
routine
);
return
1
;
}
#endif
return
0
;
}
/*
* This is used to figure out the divisor speeds.
*
* The formula is: Baud = BASE_BAUD / ((N + 1) * (1 << M)),
*
* with 0 <= N < 64 and 0 <= M < 16
*
* 12-Oct-2001 - Replaced table driven approach with code written by
* Theodore Ts'o <tytso@alum.mit.edu> which exactly replicates the
* table. (Modulo bugs for the 307200 and 61440 baud rates, which
* were clearly incorrectly calculated in the original table. This is
* why tables filled with magic constants are evil.)
*/
static
void
calc_ebrg
(
int
baud
,
int
*
n_ret
,
int
*
m_ret
)
{
int
n
,
m
;
if
(
baud
==
0
)
{
*
n_ret
=
0
;
*
m_ret
=
0
;
return
;
}
/*
* We scale numbers by 10 so that we get better accuracy
* without having to use floating point. Here we increment m
* until n is within the valid range.
*/
n
=
(
BASE_BAUD
*
10
)
/
baud
;
m
=
0
;
while
(
n
>=
640
)
{
n
=
n
/
2
;
m
++
;
}
n
=
(
n
+
5
)
/
10
;
/*
* We try very hard to avoid speeds with M == 0 since they may
* not work correctly for XTAL frequences above 10 MHz.
*/
if
((
m
==
0
)
&&
((
n
&
1
)
==
0
))
{
n
=
n
/
2
;
m
++
;
}
*
n_ret
=
n
-
1
;
*
m_ret
=
m
;
}
#define SAB82532_MAX_TEC_TIMEOUT 200000
/* 1 character time (at 50 baud) */
#define SAB82532_MAX_CEC_TIMEOUT 50000
/* 2.5 TX CLKs (at 50 baud) */
static
__inline__
void
s
ab82532_tec_wait
(
struct
sab82532
*
info
)
static
__inline__
void
s
unsab_tec_wait
(
struct
uart_sunsab_port
*
up
)
{
int
timeout
=
info
->
tec_timeout
;
int
timeout
=
up
->
tec_timeout
;
while
((
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_TEC
)
&&
--
timeout
)
while
((
readb
(
&
up
->
regs
->
r
.
star
)
&
SAB82532_STAR_TEC
)
&&
--
timeout
)
udelay
(
1
);
}
static
__inline__
void
s
ab82532_cec_wait
(
struct
sab82532
*
info
)
static
__inline__
void
s
unsab_cec_wait
(
struct
uart_sunsab_port
*
up
)
{
int
timeout
=
info
->
cec_timeout
;
int
timeout
=
up
->
cec_timeout
;
while
((
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_CEC
)
&&
--
timeout
)
while
((
readb
(
&
up
->
regs
->
r
.
star
)
&
SAB82532_STAR_CEC
)
&&
--
timeout
)
udelay
(
1
);
}
static
__inline__
void
sab82532_start_tx
(
struct
sab82532
*
info
)
{
unsigned
long
flags
;
int
i
;
save_flags
(
flags
);
cli
();
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
goto
out
;
if
(
!
test_bit
(
SAB82532_XPR
,
&
info
->
irqflags
))
goto
out
;
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_ALLS
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
clear_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
);
clear_bit
(
SAB82532_XPR
,
&
info
->
irqflags
);
for
(
i
=
0
;
i
<
info
->
xmit_fifo_size
;
i
++
)
{
writeb
(
info
->
xmit
.
buf
[
info
->
xmit
.
tail
],
&
info
->
regs
->
w
.
xfifo
[
i
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
icount
.
tx
++
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
break
;
}
/* Issue a Transmit Frame command. */
sab82532_cec_wait
(
info
);
writeb
(
SAB82532_CMDR_XF
,
&
info
->
regs
->
w
.
cmdr
);
out:
restore_flags
(
flags
);
}
/*
* ------------------------------------------------------------
* sab82532_stop() and sab82532_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static
void
sab82532_stop
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_stop"
))
return
;
save_flags
(
flags
);
cli
();
info
->
interrupt_mask1
|=
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
restore_flags
(
flags
);
}
static
void
sab82532_start
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_start"
))
return
;
save_flags
(
flags
);
cli
();
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_XPR
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
sab82532_start_tx
(
info
);
restore_flags
(
flags
);
}
/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
* subroutines are declared as inline and are folded into
* sab82532_interrupt(). They were separated out for readability's sake.
*
* Note: sab82532_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* sab82532_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
*
* gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
*
* and look at the resulting assemble code in serial.s.
*
* - Ted Ts'o (tytso@mit.edu), 7-Mar-93
* -----------------------------------------------------------------------
*/
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static
void
sab82532_sched_event
(
struct
sab82532
*
info
,
int
event
)
{
info
->
event
|=
1
<<
event
;
queue_task
(
&
info
->
tqueue
,
&
tq_serial
);
mark_bh
(
SERIAL_BH
);
}
static
void
receive_chars
(
struct
sab82532
*
info
,
union
sab82532_irq_status
*
stat
)
static
void
receive_chars
(
struct
uart_sunsab_port
*
up
,
union
sab82532_irq_status
*
stat
,
struct
pt_regs
*
regs
)
{
struct
tty_struct
*
tty
=
info
->
tty
;
struct
tty_struct
*
tty
=
up
->
port
.
info
->
tty
;
unsigned
char
buf
[
32
];
unsigned
char
status
;
int
saw_console_brk
=
0
;
int
free_fifo
=
0
;
int
i
,
count
=
0
;
int
count
=
0
;
int
i
;
/* Read number of BYTES (Character + Status) available. */
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RPF
)
{
count
=
info
->
recv_fifo_size
;
count
=
up
->
recv_fifo_size
;
free_fifo
++
;
}
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_TCD
)
{
count
=
readb
(
&
info
->
regs
->
r
.
rbcl
)
&
(
info
->
recv_fifo_size
-
1
);
count
=
readb
(
&
up
->
regs
->
r
.
rbcl
)
&
(
up
->
recv_fifo_size
-
1
);
free_fifo
++
;
}
/* Issue a FIFO read command in case we where idle. */
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_TIME
)
{
s
ab82532_cec_wait
(
info
);
writeb
(
SAB82532_CMDR_RFRD
,
&
info
->
regs
->
w
.
cmdr
);
s
unsab_cec_wait
(
up
);
writeb
(
SAB82532_CMDR_RFRD
,
&
up
->
regs
->
w
.
cmdr
);
return
;
}
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RFO
)
{
#ifdef SERIAL_DEBUG_OVERFLOW
printk
(
"sab82532: receive_chars: RFO"
);
#endif
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RFO
)
free_fifo
++
;
}
/* Read the FIFO. */
for
(
i
=
0
;
i
<
count
;
i
++
)
buf
[
i
]
=
readb
(
&
info
->
regs
->
r
.
rfifo
[
i
]);
buf
[
i
]
=
readb
(
&
up
->
regs
->
r
.
rfifo
[
i
]);
/* Issue Receive Message Complete command. */
if
(
free_fifo
)
{
s
ab82532_cec_wait
(
info
);
writeb
(
SAB82532_CMDR_RMC
,
&
info
->
regs
->
w
.
cmdr
);
s
unsab_cec_wait
(
up
);
writeb
(
SAB82532_CMDR_RMC
,
&
up
->
regs
->
w
.
cmdr
);
}
if
(
!
tty
)
return
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
unsigned
char
ch
=
buf
[
i
]
;
for
(
i
=
0
;
i
<
count
;
)
{
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
#ifdef SERIAL_DEBUG_OVERFLOW
printk
(
"sab82532: receive_chars: tty overrun
\n
"
);
#endif
info
->
icount
.
buf_overrun
++
;
break
;
if
(
unlikely
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
))
{
tty
->
flip
.
tqueue
.
routine
((
void
*
)
tty
);
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
return
;
// if TTY_DONT_FLIP is set
}
tty
->
flip
.
count
++
;
*
tty
->
flip
.
char_buf_ptr
++
=
buf
[
i
++
];
status
=
buf
[
i
++
];
info
->
icount
.
rx
++
;
*
tty
->
flip
.
char_buf_ptr
=
ch
;
*
tty
->
flip
.
flag_buf_ptr
=
TTY_NORMAL
;
up
->
port
.
icount
.
rx
++
;
if
(
unlikely
(
stat
->
sreg
.
isr0
&
(
SAB82532_ISR0_PERR
|
SAB82532_ISR0_FERR
|
SAB82532_ISR0_RFO
))
||
unlikely
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_BRK
))
{
/*
* For statistics only
*/
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_BRK
)
{
stat
->
sreg
.
isr0
&=
~
(
SAB82532_ISR0_PERR
|
SAB82532_ISR0_FERR
);
up
->
port
.
icount
.
brk
++
;
if
(
up
->
port
.
line
==
up
->
port
.
cons
->
index
)
saw_console_brk
=
1
;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if
(
uart_handle_break
(
&
up
->
port
))
continue
;
}
else
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_PERR
)
up
->
port
.
icount
.
parity
++
;
else
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_FERR
)
up
->
port
.
icount
.
frame
++
;
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RFO
)
up
->
port
.
icount
.
overrun
++
;
/*
* Mask off conditions which should be ingored.
*/
stat
->
sreg
.
isr0
&=
(
up
->
port
.
read_status_mask
&
0xff
);
stat
->
sreg
.
isr1
&=
((
up
->
port
.
read_status_mask
>>
8
)
&
0xff
);
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_BRK
)
{
*
tty
->
flip
.
flag_buf_ptr
=
TTY_BREAK
;
}
else
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_PERR
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_PARITY
;
else
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_FERR
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_FRAME
;
}
#ifdef SERIAL_DEBUG_INTR
printk
(
"DR%02x:%02x..."
,
(
unsigned
char
)
*
(
tty
->
flip
.
char_buf_ptr
-
1
),
status
);
#endif
if
(
uart_handle_sysrq_char
(
&
up
->
port
,
ch
,
regs
))
continue
;
if
(
status
&
SAB82532_RSTAT_PE
)
{
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_PARITY
;
info
->
icount
.
parity
++
;
}
else
if
(
status
&
SAB82532_RSTAT_FE
)
{
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_FRAME
;
info
->
icount
.
frame
++
;
if
((
stat
->
sreg
.
isr0
&
(
up
->
port
.
ignore_status_mask
&
0xff
))
==
0
&&
(
stat
->
sreg
.
isr1
&
((
up
->
port
.
ignore_status_mask
>>
8
)
&
0xff
))
==
0
){
tty
->
flip
.
flag_buf_ptr
++
;
tty
->
flip
.
char_buf_ptr
++
;
tty
->
flip
.
count
++
;
}
if
((
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RFO
)
&&
tty
->
flip
.
count
<
TTY_FLIPBUF_SIZE
)
{
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
*
tty
->
flip
.
flag_buf_ptr
=
TTY_OVERRUN
;
tty
->
flip
.
flag_buf_ptr
++
;
tty
->
flip
.
char_buf_ptr
++
;
tty
->
flip
.
count
++
;
}
else
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_NORMAL
;
}
queue_task
(
&
tty
->
flip
.
tqueue
,
&
tq_timer
);
tty_flip_buffer_push
(
tty
);
if
(
saw_console_brk
)
sun_do_break
();
}
static
void
transmit_chars
(
struct
sab82532
*
info
,
static
void
sunsab_stop_tx
(
struct
uart_port
*
,
unsigned
int
);
static
void
transmit_chars
(
struct
uart_sunsab_port
*
up
,
union
sab82532_irq_status
*
stat
)
{
struct
circ_buf
*
xmit
=
&
up
->
port
.
info
->
xmit
;
int
i
;
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_ALLS
)
{
info
->
interrupt_mask1
|=
SAB82532_IMR1_ALLS
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
set_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
);
up
->
interrupt_mask1
|=
SAB82532_IMR1_ALLS
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
set_bit
(
SAB82532_ALLS
,
&
up
->
irqflags
);
}
if
(
!
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_XPR
))
return
;
if
(
!
(
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_XFW
))
{
#ifdef SERIAL_DEBUG_FIFO
printk
(
"%s: XPR, but no XFW (?)
\n
"
,
__FUNCTION__
);
#endif
if
(
!
(
readb
(
&
up
->
regs
->
r
.
star
)
&
SAB82532_STAR_XFW
))
return
;
}
set_bit
(
SAB82532_XPR
,
&
info
->
irqflags
);
if
(
!
info
->
tty
)
{
info
->
interrupt_mask1
|=
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
return
;
}
set_bit
(
SAB82532_XPR
,
&
up
->
irqflags
);
if
((
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
||
info
->
tty
->
stopped
||
info
->
tty
->
hw_stopped
)
{
info
->
interrupt_mask1
|=
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
if
(
uart_circ_empty
(
xmit
)
||
uart_tx_stopped
(
&
up
->
port
))
{
up
->
interrupt_mask1
|=
SAB82532_IMR1_XPR
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
return
;
}
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_ALLS
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
clear_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
);
up
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_ALLS
);
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
clear_bit
(
SAB82532_ALLS
,
&
up
->
irqflags
);
/* Stuff 32 bytes into Transmit FIFO. */
clear_bit
(
SAB82532_XPR
,
&
info
->
irqflags
);
for
(
i
=
0
;
i
<
info
->
xmit_fifo_size
;
i
++
)
{
writeb
(
info
->
xmit
.
buf
[
info
->
xmit
.
tail
],
&
info
->
regs
->
w
.
xfifo
[
i
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
icount
.
tx
++
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
clear_bit
(
SAB82532_XPR
,
&
up
->
irqflags
);
for
(
i
=
0
;
i
<
up
->
xmit_fifo_size
;
i
++
)
{
writeb
(
xmit
->
buf
[
xmit
->
tail
],
&
up
->
regs
->
w
.
xfifo
[
i
]);
xmit
->
tail
=
(
xmit
->
tail
+
1
)
&
(
UART_XMIT_SIZE
-
1
);
up
->
port
.
icount
.
tx
++
;
if
(
uart_circ_empty
(
xmit
)
)
break
;
}
/* Issue a Transmit Frame command. */
s
ab82532_cec_wait
(
info
);
writeb
(
SAB82532_CMDR_XF
,
&
info
->
regs
->
w
.
cmdr
);
s
unsab_cec_wait
(
up
);
writeb
(
SAB82532_CMDR_XF
,
&
up
->
regs
->
w
.
cmdr
);
if
(
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
)
<
WAKEUP_CHARS
)
sab82532_sched_event
(
info
,
RS_EVEN
T_WRITE_WAKEUP
);
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
uart_event
(
&
up
->
port
,
EV
T_WRITE_WAKEUP
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"THRE..."
);
#endif
if
(
uart_circ_empty
(
xmit
))
sunsab_stop_tx
(
&
up
->
port
,
0
);
}
static
void
check_status
(
struct
sab82532
*
info
,
static
void
check_status
(
struct
uart_sunsab_port
*
up
,
union
sab82532_irq_status
*
stat
)
{
struct
tty_struct
*
tty
=
info
->
tty
;
int
modem_change
=
0
;
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_BRK
)
{
#ifdef CONFIG_SERIAL_CONSOLE
if
(
info
->
is_console
)
{
sun_do_break
(
info
);
return
;
}
#endif
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
info
->
icount
.
buf_overrun
++
;
goto
check_modem
;
}
tty
->
flip
.
count
++
;
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_PARITY
;
*
tty
->
flip
.
char_buf_ptr
++
=
0
;
info
->
icount
.
brk
++
;
}
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
uart_handle_dcd_change
(
&
up
->
port
,
!
(
readb
(
&
up
->
regs
->
r
.
vstr
)
&
SAB82532_VSTR_CD
));
if
(
!
tty
)
return
;
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_CSC
)
uart_handle_cts_change
(
&
up
->
port
,
(
readb
(
&
up
->
regs
->
r
.
star
)
&
SAB82532_STAR_CTS
));
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_RFO
)
{
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
info
->
icount
.
buf_overrun
++
;
goto
check_modem
;
}
tty
->
flip
.
count
++
;
*
tty
->
flip
.
flag_buf_ptr
++
=
TTY_PARITY
;
*
tty
->
flip
.
char_buf_ptr
++
=
0
;
info
->
icount
.
overrun
++
;
if
((
readb
(
&
up
->
regs
->
r
.
pvr
)
&
up
->
pvr_dsr_bit
)
^
up
->
dsr
)
{
up
->
dsr
=
(
readb
(
&
up
->
regs
->
r
.
pvr
)
&
up
->
pvr_dsr_bit
)
?
0
:
1
;
up
->
port
.
icount
.
dsr
++
;
}
check_modem:
if
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
{
info
->
dcd
=
(
readb
(
&
info
->
regs
->
r
.
vstr
)
&
SAB82532_VSTR_CD
)
?
0
:
1
;
info
->
icount
.
dcd
++
;
modem_change
++
;
#ifdef SERIAL_DEBUG_MODEM
printk
(
"DCD change: %d
\n
"
,
info
->
icount
.
dcd
);
#endif
}
if
(
stat
->
sreg
.
isr1
&
SAB82532_ISR1_CSC
)
{
info
->
cts
=
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_CTS
;
info
->
icount
.
cts
++
;
modem_change
++
;
#ifdef SERIAL_DEBUG_MODEM
printk
(
"CTS change: %d, CTS %s
\n
"
,
info
->
icount
.
cts
,
info
->
cts
?
"on"
:
"off"
);
#endif
}
if
((
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dsr_bit
)
^
info
->
dsr
)
{
info
->
dsr
=
(
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dsr_bit
)
?
0
:
1
;
info
->
icount
.
dsr
++
;
modem_change
++
;
#ifdef SERIAL_DEBUG_MODEM
printk
(
"DSR change: %d
\n
"
,
info
->
icount
.
dsr
);
#endif
}
if
(
modem_change
)
wake_up_interruptible
(
&
info
->
delta_msr_wait
);
wake_up_interruptible
(
&
up
->
port
.
info
->
delta_msr_wait
);
}
if
((
info
->
flags
&
ASYNC_CHECK_CD
)
&&
(
stat
->
sreg
.
isr0
&
SAB82532_ISR0_CDSC
))
{
static
void
sunsab_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
uart_sunsab_port
*
up
=
dev_id
;
union
sab82532_irq_status
status
;
unsigned
long
flags
;
#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
printk
(
"ttys%d CD now %s..."
,
info
->
line
,
(
info
->
dcd
)
?
"on"
:
"off"
);
#endif
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
if
(
info
->
dcd
)
wake_up_interruptible
(
&
info
->
open_wait
);
else
if
(
!
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_CALLOUT_NOHUP
)))
{
status
.
stat
=
0
;
if
(
readb
(
&
up
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISA0
)
status
.
sreg
.
isr0
=
readb
(
&
up
->
regs
->
r
.
isr0
);
if
(
readb
(
&
up
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISA1
)
status
.
sreg
.
isr1
=
readb
(
&
up
->
regs
->
r
.
isr1
);
#ifdef SERIAL_DEBUG_OPEN
printk
(
"scheduling hangup..."
);
#endif
MOD_INC_USE_COUNT
;
if
(
schedule_task
(
&
info
->
tqueue_hangup
)
==
0
)
MOD_DEC_USE_COUNT
;
}
if
(
status
.
stat
)
{
if
(
status
.
sreg
.
isr0
&
(
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
|
SAB82532_ISR0_RFO
|
SAB82532_ISR0_RPF
))
receive_chars
(
up
,
&
status
,
regs
);
if
((
status
.
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
||
(
status
.
sreg
.
isr1
&
SAB82532_ISR1_CSC
))
check_status
(
up
,
&
status
);
if
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_ALLS
|
SAB82532_ISR1_XPR
))
transmit_chars
(
up
,
&
status
);
}
if
(
info
->
flags
&
ASYNC_CTS_FLOW
)
{
if
(
info
->
tty
->
hw_stopped
)
{
if
(
info
->
cts
)
{
spin_unlock
(
&
up
->
port
.
lock
);
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk
(
"CTS tx start..."
);
#endif
info
->
tty
->
hw_stopped
=
0
;
sab82532_sched_event
(
info
,
RS_EVENT_WRITE_WAKEUP
);
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_XPR
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
sab82532_start_tx
(
info
);
}
}
else
{
if
(
!
(
info
->
cts
))
{
up
++
;
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk
(
"CTS tx stop..."
);
#endif
info
->
tty
->
hw_stopped
=
1
;
}
}
spin_lock
(
&
up
->
port
.
lock
);
status
.
stat
=
0
;
if
(
readb
(
&
up
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISB0
)
status
.
sreg
.
isr0
=
readb
(
&
up
->
regs
->
r
.
isr0
);
if
(
readb
(
&
up
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISB1
)
status
.
sreg
.
isr1
=
readb
(
&
up
->
regs
->
r
.
isr1
);
if
(
status
.
stat
)
{
if
(
status
.
sreg
.
isr0
&
(
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
|
SAB82532_ISR0_RFO
|
SAB82532_ISR0_RPF
))
receive_chars
(
up
,
&
status
,
regs
);
if
((
status
.
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
||
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_BRK
|
SAB82532_ISR1_CSC
)))
check_status
(
up
,
&
status
);
if
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_ALLS
|
SAB82532_ISR1_XPR
))
transmit_chars
(
up
,
&
status
);
}
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
/* port->lock is not held. */
static
unsigned
int
sunsab_tx_empty
(
struct
uart_port
*
port
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
int
ret
;
/* Do not need a lock for a state test like this. */
if
(
test_bit
(
SAB82532_ALLS
,
&
up
->
irqflags
))
ret
=
TIOCSER_TEMT
;
else
ret
=
0
;
return
ret
;
}
/* port->lock held by caller. */
static
void
sunsab_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
if
(
mctrl
&
TIOCM_RTS
)
{
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
&
~
SAB82532_MODE_FRTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
up
->
regs
->
rw
.
mode
);
}
else
{
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
up
->
regs
->
rw
.
mode
);
}
if
(
mctrl
&
TIOCM_DTR
)
{
writeb
(
readb
(
&
up
->
regs
->
rw
.
pvr
)
&
~
(
up
->
pvr_dtr_bit
),
&
up
->
regs
->
rw
.
pvr
);
}
else
{
writeb
(
readb
(
&
up
->
regs
->
rw
.
pvr
)
|
up
->
pvr_dtr_bit
,
&
up
->
regs
->
rw
.
pvr
);
}
}
/*
* This is the serial driver's generic interrupt routine
*/
static
void
sab82532_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
/* port->lock is not held. */
static
unsigned
int
sunsab_get_mctrl
(
struct
uart_port
*
port
)
{
struct
sab82532
*
info
=
dev_id
;
union
sab82532_irq_status
status
;
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
unsigned
char
val
;
unsigned
int
result
;
#ifdef SERIAL_DEBUG_INTR
printk
(
"sab82532_interrupt(%d)..."
,
irq
);
#endif
result
=
0
;
status
.
stat
=
0
;
if
(
readb
(
&
info
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISA0
)
status
.
sreg
.
isr0
=
readb
(
&
info
->
regs
->
r
.
isr0
);
if
(
readb
(
&
info
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISA1
)
status
.
sreg
.
isr1
=
readb
(
&
info
->
regs
->
r
.
isr1
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"%d<%02x.%02x>"
,
info
->
line
,
status
.
sreg
.
isr0
,
status
.
sreg
.
isr1
);
#endif
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
if
(
!
status
.
stat
)
goto
next
;
val
=
readb
(
&
up
->
regs
->
r
.
pvr
);
result
|=
(
val
&
up
->
pvr_dsr_bit
)
?
0
:
TIOCM_DSR
;
if
(
status
.
sreg
.
isr0
&
(
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
|
SAB82532_ISR0_RFO
|
SAB82532_ISR0_RPF
))
receive_chars
(
info
,
&
status
);
if
((
status
.
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
||
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_BRK
|
SAB82532_ISR1_CSC
)))
check_status
(
info
,
&
status
);
if
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_ALLS
|
SAB82532_ISR1_XPR
))
transmit_chars
(
info
,
&
status
);
val
=
readb
(
&
up
->
regs
->
r
.
vstr
);
result
|=
(
val
&
SAB82532_VSTR_CD
)
?
0
:
TIOCM_CAR
;
next:
info
=
info
->
next
;
status
.
stat
=
0
;
if
(
readb
(
&
info
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISB0
)
status
.
sreg
.
isr0
=
readb
(
&
info
->
regs
->
r
.
isr0
);
if
(
readb
(
&
info
->
regs
->
r
.
gis
)
&
SAB82532_GIS_ISB1
)
status
.
sreg
.
isr1
=
readb
(
&
info
->
regs
->
r
.
isr1
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"%d<%02x.%02x>"
,
info
->
line
,
status
.
sreg
.
isr0
,
status
.
sreg
.
isr1
);
#endif
val
=
readb
(
&
up
->
regs
->
r
.
star
);
result
|=
(
val
&
SAB82532_STAR_CTS
)
?
TIOCM_CTS
:
0
;
if
(
!
status
.
stat
)
goto
done
;
if
(
status
.
sreg
.
isr0
&
(
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
|
SAB82532_ISR0_RFO
|
SAB82532_ISR0_RPF
))
receive_chars
(
info
,
&
status
);
if
((
status
.
sreg
.
isr0
&
SAB82532_ISR0_CDSC
)
||
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_BRK
|
SAB82532_ISR1_CSC
)))
check_status
(
info
,
&
status
);
if
(
status
.
sreg
.
isr1
&
(
SAB82532_ISR1_ALLS
|
SAB82532_ISR1_XPR
))
transmit_chars
(
info
,
&
status
);
done:
;
#ifdef SERIAL_DEBUG_INTR
printk
(
"end.
\n
"
);
#endif
}
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
return
result
;
}
/*
* This routine is used to handle the "bottom half" processing for the
* serial driver, known also the "software interrupt" processing.
* This processing is done at the kernel interrupt level, after the
* sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules
* them using sab82532_sched_event(), and they get done here.
*/
static
void
do_serial_bh
(
void
)
/* port->lock held by caller. */
static
void
sunsab_stop_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_stop
)
{
run_task_queue
(
&
tq_serial
);
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
up
->
interrupt_mask1
|=
SAB82532_IMR1_XPR
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
}
static
void
do_softint
(
void
*
private_
)
/* port->lock held by caller. */
static
void
sunsab_start_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_start
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
private_
;
struct
tty_struct
*
tty
;
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
struct
circ_buf
*
xmit
=
&
up
->
port
.
info
->
xmit
;
int
i
;
tty
=
info
->
tty
;
if
(
!
tty
)
if
(
!
test_bit
(
SAB82532_XPR
,
&
up
->
irqflags
))
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
);
up
->
interrupt_mask1
&=
~
SAB82532_IMR1_XPR
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
clear_bit
(
SAB82532_ALLS
,
&
up
->
irqflags
);
clear_bit
(
SAB82532_XPR
,
&
up
->
irqflags
);
for
(
i
=
0
;
i
<
up
->
xmit_fifo_size
;
i
++
)
{
writeb
(
xmit
->
buf
[
xmit
->
tail
],
&
up
->
regs
->
w
.
xfifo
[
i
]);
xmit
->
tail
=
(
xmit
->
tail
+
1
)
&
(
UART_XMIT_SIZE
-
1
);
up
->
port
.
icount
.
tx
++
;
if
(
uart_circ_empty
(
xmit
))
break
;
}
/* Issue a Transmit Frame command. */
sunsab_cec_wait
(
up
);
writeb
(
SAB82532_CMDR_XF
,
&
up
->
regs
->
w
.
cmdr
);
}
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
*
*/
static
void
do_serial_hangup
(
void
*
private_
)
/* port->lock is not held. */
static
void
sunsab_send_xchar
(
struct
uart_port
*
port
,
char
ch
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
sunsab_tec_wait
(
up
);
writeb
(
ch
,
&
up
->
regs
->
w
.
tic
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
/* port->lock held by caller. */
static
void
sunsab_stop_rx
(
struct
uart_port
*
port
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
up
->
interrupt_mask0
|=
SAB82532_ISR0_TCD
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr0
);
}
/* port->lock held by caller. */
static
void
sunsab_enable_ms
(
struct
uart_port
*
port
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
private_
;
struct
tty_struct
*
tty
;
/* For now we always receive these interrupts. */
}
/* port->lock is not held. */
static
void
sunsab_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
unsigned
char
val
;
tty
=
info
->
tty
;
if
(
tty
)
tty_hangup
(
tty
);
MOD_DEC_USE_COUNT
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
val
=
readb
(
&
up
->
regs
->
rw
.
dafo
);
if
(
break_state
)
val
|=
SAB82532_DAFO_XBRK
;
else
val
&=
~
SAB82532_DAFO_XBRK
;
writeb
(
val
,
&
up
->
regs
->
rw
.
dafo
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
static
void
s
ab82532_init_line
(
struct
sab82532
*
info
)
/* port->lock is not held. */
s
tatic
int
sunsab_startup
(
struct
uart_port
*
port
)
{
unsigned
char
stat
,
tmp
;
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
unsigned
char
tmp
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
/*
* Wait for any commands or immediate characters
*/
s
ab82532_cec_wait
(
info
);
s
ab82532_tec_wait
(
info
);
s
unsab_cec_wait
(
up
);
s
unsab_tec_wait
(
up
);
/*
* Clear the FIFO buffers.
*/
writeb
(
SAB82532_CMDR_RRES
,
&
info
->
regs
->
w
.
cmdr
);
s
ab82532_cec_wait
(
info
);
writeb
(
SAB82532_CMDR_XRES
,
&
info
->
regs
->
w
.
cmdr
);
writeb
(
SAB82532_CMDR_RRES
,
&
up
->
regs
->
w
.
cmdr
);
s
unsab_cec_wait
(
up
);
writeb
(
SAB82532_CMDR_XRES
,
&
up
->
regs
->
w
.
cmdr
);
/*
* Clear the interrupt registers.
*/
stat
=
readb
(
&
info
->
regs
->
r
.
isr0
);
stat
=
readb
(
&
info
->
regs
->
r
.
isr1
);
(
void
)
readb
(
&
up
->
regs
->
r
.
isr0
);
(
void
)
readb
(
&
up
->
regs
->
r
.
isr1
);
/*
* Now, initialize the UART
*/
writeb
(
0
,
&
info
->
regs
->
w
.
ccr0
);
/* power-down */
writeb
(
0
,
&
up
->
regs
->
w
.
ccr0
);
/* power-down */
writeb
(
SAB82532_CCR0_MCE
|
SAB82532_CCR0_SC_NRZ
|
SAB82532_CCR0_SM_ASYNC
,
&
info
->
regs
->
w
.
ccr0
);
writeb
(
SAB82532_CCR1_ODS
|
SAB82532_CCR1_BCR
|
7
,
&
info
->
regs
->
w
.
ccr1
);
SAB82532_CCR0_SM_ASYNC
,
&
up
->
regs
->
w
.
ccr0
);
writeb
(
SAB82532_CCR1_ODS
|
SAB82532_CCR1_BCR
|
7
,
&
up
->
regs
->
w
.
ccr1
);
writeb
(
SAB82532_CCR2_BDF
|
SAB82532_CCR2_SSEL
|
SAB82532_CCR2_TOE
,
&
info
->
regs
->
w
.
ccr2
);
writeb
(
0
,
&
info
->
regs
->
w
.
ccr3
);
writeb
(
SAB82532_CCR4_MCK4
|
SAB82532_CCR4_EBRG
,
&
info
->
regs
->
w
.
ccr4
);
SAB82532_CCR2_TOE
,
&
up
->
regs
->
w
.
ccr2
);
writeb
(
0
,
&
up
->
regs
->
w
.
ccr3
);
writeb
(
SAB82532_CCR4_MCK4
|
SAB82532_CCR4_EBRG
,
&
up
->
regs
->
w
.
ccr4
);
writeb
(
SAB82532_MODE_RTS
|
SAB82532_MODE_FCTS
|
SAB82532_MODE_RAC
,
&
info
->
regs
->
w
.
mode
);
writeb
(
SAB82532_RFC_DPS
|
SAB82532_RFC_RFDF
,
&
info
->
regs
->
w
.
rfc
);
switch
(
info
->
recv_fifo_size
)
{
SAB82532_MODE_RAC
,
&
up
->
regs
->
w
.
mode
);
writeb
(
SAB82532_RFC_DPS
|
SAB82532_RFC_RFDF
,
&
up
->
regs
->
w
.
rfc
);
switch
(
up
->
recv_fifo_size
)
{
case
1
:
tmp
=
readb
(
&
info
->
regs
->
w
.
rfc
);
tmp
=
readb
(
&
up
->
regs
->
w
.
rfc
);
tmp
|=
SAB82532_RFC_RFTH_1
;
writeb
(
tmp
,
&
info
->
regs
->
w
.
rfc
);
writeb
(
tmp
,
&
up
->
regs
->
w
.
rfc
);
break
;
case
4
:
tmp
=
readb
(
&
info
->
regs
->
w
.
rfc
);
tmp
=
readb
(
&
up
->
regs
->
w
.
rfc
);
tmp
|=
SAB82532_RFC_RFTH_4
;
writeb
(
tmp
,
&
info
->
regs
->
w
.
rfc
);
writeb
(
tmp
,
&
up
->
regs
->
w
.
rfc
);
break
;
case
16
:
tmp
=
readb
(
&
info
->
regs
->
w
.
rfc
);
tmp
=
readb
(
&
up
->
regs
->
w
.
rfc
);
tmp
|=
SAB82532_RFC_RFTH_16
;
writeb
(
tmp
,
&
info
->
regs
->
w
.
rfc
);
writeb
(
tmp
,
&
up
->
regs
->
w
.
rfc
);
break
;
default:
info
->
recv_fifo_size
=
32
;
up
->
recv_fifo_size
=
32
;
/* fall through */
case
32
:
tmp
=
readb
(
&
info
->
regs
->
w
.
rfc
);
tmp
=
readb
(
&
up
->
regs
->
w
.
rfc
);
tmp
|=
SAB82532_RFC_RFTH_32
;
writeb
(
tmp
,
&
info
->
regs
->
w
.
rfc
);
writeb
(
tmp
,
&
up
->
regs
->
w
.
rfc
);
break
;
}
tmp
=
readb
(
&
info
->
regs
->
rw
.
ccr0
);
};
tmp
=
readb
(
&
up
->
regs
->
rw
.
ccr0
);
tmp
|=
SAB82532_CCR0_PU
;
/* power-up */
writeb
(
tmp
,
&
info
->
regs
->
rw
.
ccr0
);
writeb
(
tmp
,
&
up
->
regs
->
rw
.
ccr0
);
/*
* Finally, enable interrupts
*/
up
->
interrupt_mask0
=
(
SAB82532_IMR0_PERR
|
SAB82532_IMR0_FERR
|
SAB82532_IMR0_PLLA
);
writeb
(
up
->
interrupt_mask0
,
&
up
->
regs
->
w
.
imr0
);
up
->
interrupt_mask1
=
(
SAB82532_IMR1_BRKT
|
SAB82532_IMR1_ALLS
|
SAB82532_IMR1_XOFF
|
SAB82532_IMR1_TIN
|
SAB82532_IMR1_CSC
|
SAB82532_IMR1_XON
|
SAB82532_IMR1_XPR
);
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
set_bit
(
SAB82532_ALLS
,
&
up
->
irqflags
);
set_bit
(
SAB82532_XPR
,
&
up
->
irqflags
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
return
0
;
}
static
int
startup
(
struct
sab82532
*
info
)
/* port->lock is not held. */
static
void
sunsab_shutdown
(
struct
uart_port
*
port
)
{
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
unsigned
long
page
;
int
retval
=
0
;
unsigned
char
tmp
;
page
=
get_free_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
save_flags
(
flags
);
cli
();
/* Disable Interrupts */
up
->
interrupt_mask0
=
0xff
;
writeb
(
up
->
interrupt_mask0
,
&
up
->
regs
->
w
.
imr0
);
up
->
interrupt_mask1
=
0xff
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
free_page
(
page
);
goto
errout
;
}
/* Disable break condition */
tmp
=
readb
(
&
up
->
regs
->
rw
.
dafo
);
tmp
&=
~
SAB82532_DAFO_XBRK
;
writeb
(
tmp
,
&
up
->
regs
->
rw
.
dafo
);
if
(
!
info
->
regs
)
{
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
free_page
(
page
);
retval
=
-
ENODEV
;
goto
errout
;
}
if
(
info
->
xmit
.
buf
)
free_page
(
page
);
else
info
->
xmit
.
buf
=
(
unsigned
char
*
)
page
;
/* Disable Receiver */
tmp
=
readb
(
&
up
->
regs
->
rw
.
mode
);
tmp
&=
~
SAB82532_MODE_RAC
;
writeb
(
tmp
,
&
up
->
regs
->
rw
.
mode
);
#ifdef SERIAL_DEBUG_OPEN
printk
(
"starting up serial port %d..."
,
info
->
line
);
#endif
/* Power Down */
tmp
=
readb
(
&
up
->
regs
->
rw
.
ccr0
);
tmp
&=
~
SAB82532_CCR0_PU
;
writeb
(
tmp
,
&
up
->
regs
->
rw
.
ccr0
);
/*
* Initialize the Hardware
*/
sab82532_init_line
(
info
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
if
(
info
->
tty
->
termios
->
c_cflag
&
CBAUD
)
{
u8
tmp
;
/*
* This is used to figure out the divisor speeds.
*
* The formula is: Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)),
*
* with 0 <= N < 64 and 0 <= M < 16
*/
tmp
=
readb
(
&
info
->
regs
->
rw
.
mode
);
tmp
&=
~
(
SAB82532_MODE_FRTS
);
tmp
|=
SAB82532_MODE_RTS
;
writeb
(
tmp
,
&
info
->
regs
->
rw
.
mode
);
static
void
calc_ebrg
(
int
baud
,
int
*
n_ret
,
int
*
m_ret
)
{
int
n
,
m
;
tmp
=
readb
(
&
info
->
regs
->
rw
.
pvr
);
tmp
&=
~
(
info
->
pvr_dtr_bit
);
writeb
(
tmp
,
&
info
->
regs
->
rw
.
pvr
);
if
(
baud
==
0
)
{
*
n_ret
=
0
;
*
m_ret
=
0
;
return
;
}
/*
* Finally, enable interrupts
*/
info
->
interrupt_mask0
=
SAB82532_IMR0_PERR
|
SAB82532_IMR0_FERR
|
SAB82532_IMR0_PLLA
;
writeb
(
info
->
interrupt_mask0
,
&
info
->
regs
->
w
.
imr0
);
info
->
interrupt_mask1
=
SAB82532_IMR1_BRKT
|
SAB82532_IMR1_ALLS
|
SAB82532_IMR1_XOFF
|
SAB82532_IMR1_TIN
|
SAB82532_IMR1_CSC
|
SAB82532_IMR1_XON
|
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
set_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
);
if
(
info
->
tty
)
clear_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
set_bit
(
SAB82532_XPR
,
&
info
->
irqflags
);
/*
* and set the speed of the serial port
* We scale numbers by 10 so that we get better accuracy
* without having to use floating point. Here we increment m
* until n is within the valid range.
*/
change_speed
(
info
);
info
->
flags
|=
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
return
0
;
errout:
restore_flags
(
flags
);
return
retval
;
}
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static
void
shutdown
(
struct
sab82532
*
info
)
{
unsigned
long
flags
;
u8
tmp
;
if
(
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
return
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"Shutting down serial port %d..."
,
info
->
line
);
#endif
save_flags
(
flags
);
cli
();
/* Disable interrupts */
n
=
(
SAB_BASE_BAUD
*
10
)
/
baud
;
m
=
0
;
while
(
n
>=
640
)
{
n
=
n
/
2
;
m
++
;
}
n
=
(
n
+
5
)
/
10
;
/*
*
clear delta_msr_wait queue to avoid mem leaks: we may free the irq
*
here so the queue might never be waken up
*
We try very hard to avoid speeds with M == 0 since they may
*
not work correctly for XTAL frequences above 10 MHz.
*/
wake_up_interruptible
(
&
info
->
delta_msr_wait
);
if
(
info
->
xmit
.
buf
)
{
free_page
((
unsigned
long
)
info
->
xmit
.
buf
);
info
->
xmit
.
buf
=
0
;
}
if
(
info
->
is_console
)
{
info
->
interrupt_mask0
=
SAB82532_IMR0_PERR
|
SAB82532_IMR0_FERR
|
SAB82532_IMR0_PLLA
|
SAB82532_IMR0_CDSC
;
writeb
(
info
->
interrupt_mask0
,
&
info
->
regs
->
w
.
imr0
);
info
->
interrupt_mask1
=
SAB82532_IMR1_BRKT
|
SAB82532_IMR1_ALLS
|
SAB82532_IMR1_XOFF
|
SAB82532_IMR1_TIN
|
SAB82532_IMR1_CSC
|
SAB82532_IMR1_XON
|
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
flags
&=
~
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
return
;
}
/* Disable Interrupts */
info
->
interrupt_mask0
=
0xff
;
writeb
(
info
->
interrupt_mask0
,
&
info
->
regs
->
w
.
imr0
);
info
->
interrupt_mask1
=
0xff
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
if
(
!
info
->
tty
||
(
info
->
tty
->
termios
->
c_cflag
&
HUPCL
))
{
tmp
=
readb
(
&
info
->
regs
->
r
.
mode
);
tmp
|=
(
SAB82532_MODE_FRTS
|
SAB82532_MODE_RTS
);
writeb
(
tmp
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
|
info
->
pvr_dtr_bit
,
&
info
->
regs
->
rw
.
pvr
);
if
((
m
==
0
)
&&
((
n
&
1
)
==
0
))
{
n
=
n
/
2
;
m
++
;
}
/* Disable break condition */
tmp
=
readb
(
&
info
->
regs
->
rw
.
dafo
);
tmp
&=
~
(
SAB82532_DAFO_XBRK
);
writeb
(
tmp
,
&
info
->
regs
->
rw
.
dafo
);
/* Disable Receiver */
tmp
=
readb
(
&
info
->
regs
->
rw
.
mode
);
tmp
&=
~
(
SAB82532_MODE_RAC
);
writeb
(
tmp
,
&
info
->
regs
->
rw
.
mode
);
/* Power Down */
tmp
=
readb
(
&
info
->
regs
->
rw
.
ccr0
);
tmp
&=
~
(
SAB82532_CCR0_PU
);
writeb
(
tmp
,
&
info
->
regs
->
rw
.
ccr0
);
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
flags
&=
~
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
*
n_ret
=
n
-
1
;
*
m_ret
=
m
;
}
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
static
void
change_speed
(
struct
sab82532
*
info
)
/* Internal routine, port->lock is held and local interrupts are disabled. */
static
void
sunsab_convert_to_sab
(
struct
uart_sunsab_port
*
up
,
unsigned
int
cflag
,
unsigned
int
iflag
,
int
baud
)
{
unsigned
long
flags
;
unsigned
int
ebrg
;
tcflag_t
cflag
;
unsigned
char
dafo
;
int
bits
,
n
,
m
;
if
(
!
info
->
tty
||
!
info
->
tty
->
termios
)
return
;
cflag
=
info
->
tty
->
termios
->
c_cflag
;
unsigned
int
ebrg
;
unsigned
char
dafo
;
int
bits
,
n
,
m
;
/* Byte size and parity */
switch
(
cflag
&
CSIZE
)
{
...
...
@@ -959,1173 +688,296 @@ static void change_speed(struct sab82532 *info)
}
if
(
cflag
&
PARODD
)
{
#ifdef CMSPAR
if
(
cflag
&
CMSPAR
)
dafo
|=
SAB82532_DAFO_PAR_MARK
;
else
#endif
dafo
|=
SAB82532_DAFO_PAR_ODD
;
dafo
|=
SAB82532_DAFO_PAR_ODD
;
}
else
{
#ifdef CMSPAR
if
(
cflag
&
CMSPAR
)
dafo
|=
SAB82532_DAFO_PAR_SPACE
;
else
#endif
dafo
|=
SAB82532_DAFO_PAR_EVEN
;
dafo
|=
SAB82532_DAFO_PAR_EVEN
;
}
/* Determine EBRG values based on baud rate */
info
->
baud
=
tty_get_baud_rate
(
info
->
tty
);
calc_ebrg
(
info
->
baud
,
&
n
,
&
m
);
calc_ebrg
(
baud
,
&
n
,
&
m
);
ebrg
=
n
|
(
m
<<
6
);
if
(
info
->
baud
)
{
info
->
timeout
=
(
info
->
xmit_fifo_size
*
HZ
*
bits
)
/
info
->
baud
;
info
->
tec_timeout
=
(
10
*
1000000
)
/
info
->
baud
;
info
->
cec_timeout
=
info
->
tec_timeout
>>
2
;
}
else
{
info
->
timeout
=
0
;
info
->
tec_timeout
=
SAB82532_MAX_TEC_TIMEOUT
;
info
->
cec_timeout
=
SAB82532_MAX_CEC_TIMEOUT
;
}
info
->
timeout
+=
HZ
/
50
;
/* Add .02 seconds of slop */
up
->
tec_timeout
=
(
10
*
1000000
)
/
baud
;
up
->
cec_timeout
=
up
->
tec_timeout
>>
2
;
/* CTS flow control flags */
if
(
cflag
&
CRTSCTS
)
info
->
flags
|=
ASYNC_CTS_FLOW
;
else
info
->
flags
&=
~
(
ASYNC_CTS_FLOW
);
if
(
cflag
&
CLOCAL
)
info
->
flags
&=
~
(
ASYNC_CHECK_CD
);
else
info
->
flags
|=
ASYNC_CHECK_CD
;
if
(
info
->
tty
)
info
->
tty
->
hw_stopped
=
0
;
/*
* Set up parity check flag
* XXX: not implemented, yet.
/* We encode read_status_mask and ignore_status_mask like so:
*
* ---------------------
* | ... | ISR1 | ISR0 |
* ---------------------
* .. 15 8 7 0
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
up
->
port
.
read_status_mask
=
(
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
|
SAB82532_ISR0_RFO
|
SAB82532_ISR0_RPF
|
SAB82532_ISR0_CDSC
);
up
->
port
.
read_status_mask
|=
(
SAB82532_ISR1_CSC
|
SAB82532_ISR1_ALLS
|
SAB82532_ISR1_XPR
)
<<
8
;
if
(
iflag
&
INPCK
)
up
->
port
.
read_status_mask
|=
(
SAB82532_ISR0_PERR
|
SAB82532_ISR0_FERR
);
if
(
iflag
&
(
BRKINT
|
PARMRK
))
up
->
port
.
read_status_mask
|=
(
SAB82532_ISR1_BRK
<<
8
);
/*
* Characters to ignore
* XXX: not implemented, yet.
* Characteres to ignore
*/
up
->
port
.
ignore_status_mask
=
0
;
if
(
iflag
&
IGNPAR
)
up
->
port
.
ignore_status_mask
|=
(
SAB82532_ISR0_PERR
|
SAB82532_ISR0_FERR
);
if
(
iflag
&
IGNBRK
)
{
up
->
port
.
ignore_status_mask
|=
(
SAB82532_ISR1_BRK
<<
8
);
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if
(
iflag
&
IGNPAR
)
up
->
port
.
ignore_status_mask
|=
SAB82532_ISR0_RFO
;
}
/*
* !!! ignore all characters if CREAD is not set
* XXX: not implemented, yet.
* ignore all characters if CREAD is not set
*/
if
((
cflag
&
CREAD
)
==
0
)
info
->
ignore_status_mask
|=
SAB82532_ISR0_RPF
|
SAB82532_ISR0_TCD
|
SAB82532_ISR0_TIME
;
save_flags
(
flags
);
cli
();
sab82532_cec_wait
(
info
);
sab82532_tec_wait
(
info
);
writeb
(
dafo
,
&
info
->
regs
->
w
.
dafo
);
writeb
(
ebrg
&
0xff
,
&
info
->
regs
->
w
.
bgr
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
ccr2
)
&
~
(
0xc0
),
&
info
->
regs
->
rw
.
ccr2
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
ccr2
)
|
((
ebrg
>>
2
)
&
0xc0
),
&
info
->
regs
->
rw
.
ccr2
);
if
(
info
->
flags
&
ASYNC_CTS_FLOW
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_RTS
),
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_FCTS
),
&
info
->
regs
->
rw
.
mode
);
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_CSC
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
}
else
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_FRTS
),
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FCTS
,
&
info
->
regs
->
rw
.
mode
);
info
->
interrupt_mask1
|=
SAB82532_IMR1_CSC
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
}
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RAC
,
&
info
->
regs
->
rw
.
mode
);
restore_flags
(
flags
);
}
static
void
sab82532_put_char
(
struct
tty_struct
*
tty
,
unsigned
char
ch
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_put_char"
))
return
;
if
(
!
tty
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
if
(
!
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
))
{
restore_flags
(
flags
);
return
;
}
info
->
xmit
.
buf
[
info
->
xmit
.
head
]
=
ch
;
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
restore_flags
(
flags
);
}
static
void
sab82532_flush_chars
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_flush_chars"
))
return
;
if
((
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
||
tty
->
stopped
||
tty
->
hw_stopped
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_XPR
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
sab82532_start_tx
(
info
);
restore_flags
(
flags
);
}
static
int
sab82532_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
unsigned
char
*
buf
,
int
count
)
{
int
c
,
ret
=
0
;
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_write"
))
return
0
;
if
(
!
tty
||
!
info
->
xmit
.
buf
||
!
tmp_buf
)
return
0
;
save_flags
(
flags
);
if
(
from_user
)
{
down
(
&
tmp_buf_sem
);
while
(
1
)
{
int
c1
;
c
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
count
<
c
)
c
=
count
;
if
(
c
<=
0
)
break
;
c
-=
copy_from_user
(
tmp_buf
,
buf
,
c
);
if
(
!
c
)
{
if
(
!
ret
)
ret
=
-
EFAULT
;
break
;
}
cli
();
c1
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
c1
<
c
)
c
=
c1
;
memcpy
(
info
->
xmit
.
buf
+
info
->
xmit
.
head
,
tmp_buf
,
c
);
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
);
restore_flags
(
flags
);
buf
+=
c
;
count
-=
c
;
ret
+=
c
;
}
up
(
&
tmp_buf_sem
);
up
->
port
.
ignore_status_mask
|=
(
SAB82532_ISR0_RPF
|
SAB82532_ISR0_TCD
);
/* Now bang the new settings into the chip. */
sunsab_cec_wait
(
up
);
sunsab_tec_wait
(
up
);
writeb
(
dafo
,
&
up
->
regs
->
w
.
dafo
);
writeb
(
ebrg
&
0xff
,
&
up
->
regs
->
w
.
bgr
);
writeb
((
readb
(
&
up
->
regs
->
rw
.
ccr2
)
&
~
0xc0
)
|
((
ebrg
>>
2
)
&
0xc0
),
&
up
->
regs
->
rw
.
ccr2
);
if
(
cflag
&
CRTSCTS
)
{
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
&
~
SAB82532_MODE_RTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
&
~
SAB82532_MODE_FCTS
,
&
up
->
regs
->
rw
.
mode
);
up
->
interrupt_mask1
&=
~
SAB82532_IMR1_CSC
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
}
else
{
cli
();
while
(
1
)
{
c
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
count
<
c
)
c
=
count
;
if
(
c
<=
0
)
break
;
memcpy
(
info
->
xmit
.
buf
+
info
->
xmit
.
head
,
buf
,
c
);
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
);
buf
+=
c
;
count
-=
c
;
ret
+=
c
;
}
restore_flags
(
flags
);
}
if
((
info
->
xmit
.
head
!=
info
->
xmit
.
tail
)
&&
!
tty
->
stopped
&&
!
tty
->
hw_stopped
)
{
info
->
interrupt_mask1
&=
~
(
SAB82532_IMR1_XPR
);
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
sab82532_start_tx
(
info
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
&
~
SAB82532_MODE_FRTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FCTS
,
&
up
->
regs
->
rw
.
mode
);
up
->
interrupt_mask1
|=
SAB82532_IMR1_CSC
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
}
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RAC
,
&
up
->
regs
->
rw
.
mode
);
restore_flags
(
flags
);
return
ret
;
}
static
int
sab82532_write_room
(
struct
tty_struct
*
tty
)
/* port->lock is not held. */
static
void
sunsab_change_speed
(
struct
uart_port
*
port
,
unsigned
int
cflag
,
unsigned
int
iflag
,
unsigned
int
quot
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_write_room"
))
return
0
;
return
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
}
static
int
sab82532_chars_in_buffer
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_chars_in_buffer"
))
return
0
;
return
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
}
static
void
sab82532_flush_buffer
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
struct
uart_sunsab_port
*
up
=
(
struct
uart_sunsab_port
*
)
port
;
unsigned
long
flags
;
int
baud
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_flush_buffer"
))
return
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
save_flags
(
flags
);
cli
();
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
restore_flags
(
flags
);
/* Undo what generic UART core did. */
baud
=
(
SAB_BASE_BAUD
/
(
quot
*
16
));
wake_up_interruptible
(
&
tty
->
write_wait
);
if
((
tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
tty
->
ldisc
.
write_wakeup
)
(
tty
->
ldisc
.
write_wakeup
)(
tty
);
}
sunsab_convert_to_sab
(
up
,
cflag
,
iflag
,
baud
);
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static
void
sab82532_send_xchar
(
struct
tty_struct
*
tty
,
char
ch
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_send_xchar"
))
return
;
save_flags
(
flags
);
cli
();
sab82532_tec_wait
(
info
);
writeb
(
ch
,
&
info
->
regs
->
w
.
tic
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
/*
* ------------------------------------------------------------
* sab82532_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static
void
sab82532_throttle
(
struct
tty_struct
*
tty
)
static
const
char
*
sunsab_type
(
struct
uart_port
*
port
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
#ifdef SERIAL_DEBUG_THROTTLE
char
buf
[
64
];
printk
(
"throttle %s: %d....
\n
"
,
_tty_name
(
tty
,
buf
),
tty
->
ldisc
.
chars_in_buffer
(
tty
));
#endif
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_throttle"
))
return
;
if
(
I_IXOFF
(
tty
))
sab82532_send_xchar
(
tty
,
STOP_CHAR
(
tty
));
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
{
u8
mode
=
readb
(
&
info
->
regs
->
r
.
mode
);
mode
&=
~
(
SAB82532_MODE_FRTS
|
SAB82532_MODE_RTS
);
writeb
(
mode
,
&
info
->
regs
->
w
.
mode
);
}
return
"SunSAB"
;
}
static
void
s
ab82532_unthrottle
(
struct
tty_struct
*
tty
)
static
void
s
unsab_release_port
(
struct
uart_port
*
port
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
#ifdef SERIAL_DEBUG_THROTTLE
char
buf
[
64
];
printk
(
"unthrottle %s: %d....
\n
"
,
_tty_name
(
tty
,
buf
),
tty
->
ldisc
.
chars_in_buffer
(
tty
));
#endif
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_unthrottle"
))
return
;
if
(
I_IXOFF
(
tty
))
{
if
(
info
->
x_char
)
info
->
x_char
=
0
;
else
sab82532_send_xchar
(
tty
,
START_CHAR
(
tty
));
}
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
{
u8
mode
=
readb
(
&
info
->
regs
->
r
.
mode
);
mode
&=
~
(
SAB82532_MODE_RTS
);
mode
|=
SAB82532_MODE_FRTS
;
writeb
(
mode
,
&
info
->
regs
->
w
.
mode
);
}
}
/*
* ------------------------------------------------------------
* sab82532_ioctl() and friends
* ------------------------------------------------------------
*/
static
int
get_serial_info
(
struct
sab82532
*
info
,
struct
serial_struct
*
retinfo
)
static
int
sunsab_request_port
(
struct
uart_port
*
port
)
{
struct
serial_struct
tmp
;
if
(
!
retinfo
)
return
-
EFAULT
;
memset
(
&
tmp
,
0
,
sizeof
(
tmp
));
tmp
.
type
=
info
->
type
;
tmp
.
line
=
info
->
line
;
tmp
.
port
=
(
unsigned
long
)
info
->
regs
;
tmp
.
irq
=
info
->
irq
;
tmp
.
flags
=
info
->
flags
;
tmp
.
xmit_fifo_size
=
info
->
xmit_fifo_size
;
tmp
.
baud_base
=
info
->
baud_base
;
tmp
.
close_delay
=
info
->
close_delay
;
tmp
.
closing_wait
=
info
->
closing_wait
;
tmp
.
custom_divisor
=
info
->
custom_divisor
;
tmp
.
hub6
=
0
;
if
(
copy_to_user
(
retinfo
,
&
tmp
,
sizeof
(
*
retinfo
)))
return
-
EFAULT
;
return
0
;
}
static
int
set_serial_info
(
struct
sab82532
*
info
,
struct
serial_struct
*
new_info
)
static
void
sunsab_config_port
(
struct
uart_port
*
port
,
int
flags
)
{
return
0
;
}
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
static
int
get_lsr_info
(
struct
sab82532
*
info
,
unsigned
int
*
value
)
static
int
sunsab_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
{
unsigned
int
result
;
result
=
(
!
info
->
xmit
.
buf
&&
test_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
))
?
TIOCSER_TEMT
:
0
;
return
put_user
(
result
,
value
);
return
-
EINVAL
;
}
static
struct
uart_ops
sunsab_pops
=
{
.
tx_empty
=
sunsab_tx_empty
,
.
set_mctrl
=
sunsab_set_mctrl
,
.
get_mctrl
=
sunsab_get_mctrl
,
.
stop_tx
=
sunsab_stop_tx
,
.
start_tx
=
sunsab_start_tx
,
.
send_xchar
=
sunsab_send_xchar
,
.
stop_rx
=
sunsab_stop_rx
,
.
enable_ms
=
sunsab_enable_ms
,
.
break_ctl
=
sunsab_break_ctl
,
.
startup
=
sunsab_startup
,
.
shutdown
=
sunsab_shutdown
,
.
change_speed
=
sunsab_change_speed
,
.
type
=
sunsab_type
,
.
release_port
=
sunsab_release_port
,
.
request_port
=
sunsab_request_port
,
.
config_port
=
sunsab_config_port
,
.
verify_port
=
sunsab_verify_port
,
};
static
int
get_modem_info
(
struct
sab82532
*
info
,
unsigned
int
*
value
)
{
unsigned
int
result
;
result
=
((
readb
(
&
info
->
regs
->
r
.
mode
)
&
SAB82532_MODE_RTS
)
?
((
readb
(
&
info
->
regs
->
r
.
mode
)
&
SAB82532_MODE_FRTS
)
?
0
:
TIOCM_RTS
)
:
TIOCM_RTS
)
|
((
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dtr_bit
)
?
0
:
TIOCM_DTR
)
|
((
readb
(
&
info
->
regs
->
r
.
vstr
)
&
SAB82532_VSTR_CD
)
?
0
:
TIOCM_CAR
)
|
((
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dsr_bit
)
?
0
:
TIOCM_DSR
)
|
((
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_CTS
)
?
TIOCM_CTS
:
0
);
return
put_user
(
result
,
value
);
}
static
struct
uart_driver
sunsab_reg
=
{
.
owner
=
THIS_MODULE
,
.
driver_name
=
"serial"
,
#ifdef CONFIG_DEVFS_FS
.
dev_name
=
"tts/%d"
,
#else
.
dev_name
=
"ttyS"
,
#endif
.
major
=
TTY_MAJOR
,
};
static
int
set_modem_info
(
struct
sab82532
*
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
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_FRTS
),
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
}
if
(
arg
&
TIOCM_DTR
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
&
~
(
info
->
pvr_dtr_bit
),
&
info
->
regs
->
rw
.
pvr
);
}
break
;
case
TIOCMBIC
:
if
(
arg
&
TIOCM_RTS
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
}
if
(
arg
&
TIOCM_DTR
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
|
info
->
pvr_dtr_bit
,
&
info
->
regs
->
rw
.
pvr
);
}
break
;
case
TIOCMSET
:
if
(
arg
&
TIOCM_RTS
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_FRTS
),
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
}
else
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
}
if
(
arg
&
TIOCM_DTR
)
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
&
~
(
info
->
pvr_dtr_bit
),
&
info
->
regs
->
rw
.
pvr
);
}
else
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
|
info
->
pvr_dtr_bit
,
&
info
->
regs
->
rw
.
pvr
);
}
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
struct
uart_sunsab_port
*
sunsab_ports
;
static
int
num_channels
;
/*
* This routine sends a break character out the serial port.
*/
static
void
sab82532_break
(
struct
tty_struct
*
tty
,
int
break_state
)
static
__inline__
void
sunsab_console_putchar
(
struct
uart_sunsab_port
*
up
,
char
c
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_break"
))
return
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
if
(
!
info
->
regs
)
return
;
sunsab_tec_wait
(
up
);
writeb
(
c
,
&
up
->
regs
->
w
.
tic
)
;
#ifdef SERIAL_DEBUG_SEND_BREAK
printk
(
"sab82532_break(%d) jiff=%lu..."
,
break_state
,
jiffies
);
#endif
save_flags
(
flags
);
cli
();
if
(
break_state
==
-
1
)
writeb
(
readb
(
&
info
->
regs
->
rw
.
dafo
)
|
SAB82532_DAFO_XBRK
,
&
info
->
regs
->
rw
.
dafo
);
else
writeb
(
readb
(
&
info
->
regs
->
rw
.
dafo
)
&
~
(
SAB82532_DAFO_XBRK
),
&
info
->
regs
->
rw
.
dafo
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
static
int
sab82532_ioctl
(
struct
tty_struct
*
tty
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
static
void
sunsab_console_write
(
struct
console
*
con
,
const
char
*
s
,
unsigned
n
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
struct
async_icount
cprev
,
cnow
;
/* kernel counter temps */
struct
serial_icounter_struct
*
p_cuser
;
/* user space */
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_ioctl"
))
return
-
ENODEV
;
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
con
->
index
];
int
i
;
if
((
cmd
!=
TIOCGSERIAL
)
&&
(
cmd
!=
TIOCSSERIAL
)
&&
(
cmd
!=
TIOCSERCONFIG
)
&&
(
cmd
!=
TIOCSERGWILD
)
&&
(
cmd
!=
TIOCSERSWILD
)
&&
(
cmd
!=
TIOCSERGSTRUCT
)
&&
(
cmd
!=
TIOCMIWAIT
)
&&
(
cmd
!=
TIOCGICOUNT
))
{
if
(
tty
->
flags
&
(
1
<<
TTY_IO_ERROR
))
return
-
EIO
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
*
s
==
'\n'
)
sunsab_console_putchar
(
up
,
'\r'
);
sunsab_console_putchar
(
up
,
*
s
++
);
}
switch
(
cmd
)
{
case
TIOCGSOFTCAR
:
return
put_user
(
C_CLOCAL
(
tty
)
?
1
:
0
,
(
int
*
)
arg
);
case
TIOCSSOFTCAR
:
if
(
get_user
(
arg
,
(
unsigned
int
*
)
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
sab82532
*
)
arg
,
info
,
sizeof
(
struct
sab82532
)))
return
-
EFAULT
;
return
0
;
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case
TIOCMIWAIT
:
cli
();
/* note the counters on entry */
cprev
=
info
->
icount
;
sti
();
while
(
1
)
{
interruptible_sleep_on
(
&
info
->
delta_msr_wait
);
/* see if a signal did it */
if
(
signal_pending
(
current
))
return
-
ERESTARTSYS
;
cli
();
cnow
=
info
->
icount
;
/* atomic copy */
sti
();
if
(
cnow
.
rng
==
cprev
.
rng
&&
cnow
.
dsr
==
cprev
.
dsr
&&
cnow
.
dcd
==
cprev
.
dcd
&&
cnow
.
cts
==
cprev
.
cts
)
return
-
EIO
;
/* no change => error */
if
(
((
arg
&
TIOCM_RNG
)
&&
(
cnow
.
rng
!=
cprev
.
rng
))
||
((
arg
&
TIOCM_DSR
)
&&
(
cnow
.
dsr
!=
cprev
.
dsr
))
||
((
arg
&
TIOCM_CD
)
&&
(
cnow
.
dcd
!=
cprev
.
dcd
))
||
((
arg
&
TIOCM_CTS
)
&&
(
cnow
.
cts
!=
cprev
.
cts
))
)
{
return
0
;
}
cprev
=
cnow
;
}
/* NOTREACHED */
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case
TIOCGICOUNT
:
cli
();
cnow
=
info
->
icount
;
sti
();
p_cuser
=
(
struct
serial_icounter_struct
*
)
arg
;
if
(
put_user
(
cnow
.
cts
,
&
p_cuser
->
cts
)
||
put_user
(
cnow
.
dsr
,
&
p_cuser
->
dsr
)
||
put_user
(
cnow
.
rng
,
&
p_cuser
->
rng
)
||
put_user
(
cnow
.
dcd
,
&
p_cuser
->
dcd
))
return
-
EFAULT
;
return
0
;
default:
return
-
ENOIOCTLCMD
;
}
return
0
;
sunsab_tec_wait
(
up
);
}
static
void
sab82532_set_termios
(
struct
tty_struct
*
tty
,
struct
termios
*
old_termios
)
static
kdev_t
sunsab_console_device
(
struct
console
*
con
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
if
(
(
tty
->
termios
->
c_cflag
==
old_termios
->
c_cflag
)
&&
(
RELEVANT_IFLAG
(
tty
->
termios
->
c_iflag
)
==
RELEVANT_IFLAG
(
old_termios
->
c_iflag
)))
return
;
change_speed
(
info
);
/* Handle transition to B0 status */
if
((
old_termios
->
c_cflag
&
CBAUD
)
&&
!
(
tty
->
termios
->
c_cflag
&
CBAUD
))
{
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
w
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
w
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
w
.
pvr
)
|
info
->
pvr_dtr_bit
,
&
info
->
regs
->
w
.
pvr
);
}
/* Handle transition away from B0 status */
if
(
!
(
old_termios
->
c_cflag
&
CBAUD
)
&&
(
tty
->
termios
->
c_cflag
&
CBAUD
))
{
writeb
(
readb
(
&
info
->
regs
->
w
.
pvr
)
&
~
(
info
->
pvr_dtr_bit
),
&
info
->
regs
->
w
.
pvr
);
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
{
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
&
~
(
SAB82532_MODE_RTS
),
&
info
->
regs
->
w
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
w
.
mode
);
}
else
if
(
test_bit
(
TTY_THROTTLED
,
&
tty
->
flags
))
{
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
&
~
(
SAB82532_MODE_FRTS
|
SAB82532_MODE_RTS
),
&
info
->
regs
->
w
.
mode
);
}
else
{
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
&
~
(
SAB82532_MODE_FRTS
),
&
info
->
regs
->
w
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
w
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
w
.
mode
);
}
}
/* Handle turning off CRTSCTS */
if
((
old_termios
->
c_cflag
&
CRTSCTS
)
&&
!
(
tty
->
termios
->
c_cflag
&
CRTSCTS
))
{
tty
->
hw_stopped
=
0
;
sab82532_start
(
tty
);
}
return
mk_kdev
(
sunsab_reg
.
major
,
sunsab_reg
.
minor
+
con
->
index
);
}
/*
* ------------------------------------------------------------
* sab82532_close()
*
* This routine is called when the serial port gets closed. First, we
* wait for the last remaining data to be sent. Then, we unlink its
* async structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
* ------------------------------------------------------------
*/
static
void
sab82532_close
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
static
int
sunsab_console_setup
(
struct
console
*
con
,
char
*
options
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
con
->
index
]
;
unsigned
long
flags
;
int
baud
;
if
(
!
info
||
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_close"
))
return
;
save_flags
(
flags
);
cli
();
printk
(
"Console: ttyS%d (SAB82532)
\n
"
,
(
sunsab_reg
.
minor
-
64
)
+
con
->
index
);
if
(
tty_hung_up_p
(
filp
))
{
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
sunserial_console_termios
(
con
);
#ifdef SERIAL_DEBUG_OPEN
printk
(
"sab82532_close ttys%d, count = %d
\n
"
,
info
->
line
,
info
->
count
);
#endif
if
((
tty
->
count
==
1
)
&&
(
info
->
count
!=
1
))
{
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. info->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk
(
"sab82532_close: bad serial port count; tty->count is 1,"
" info->count is %d
\n
"
,
info
->
count
);
info
->
count
=
1
;
}
if
(
--
info
->
count
<
0
)
{
printk
(
"sab82532_close: bad serial port count for ttys%d: %d
\n
"
,
info
->
line
,
info
->
count
);
info
->
count
=
0
;
}
if
(
info
->
count
)
{
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
info
->
flags
|=
ASYNC_CLOSING
;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if
(
info
->
flags
&
ASYNC_NORMAL_ACTIVE
)
info
->
normal_termios
=
*
tty
->
termios
;
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
info
->
callout_termios
=
*
tty
->
termios
;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
/* Firmware console speed is limited to 150-->38400 baud so
* this hackish cflag thing is OK.
*/
tty
->
closing
=
1
;
if
(
info
->
closing_wait
!=
ASYNC_CLOSING_WAIT_NONE
)
tty_wait_until_sent
(
tty
,
info
->
closing_wait
);
switch
(
con
->
cflag
&
CBAUD
)
{
case
B150
:
baud
=
150
;
break
;
case
B300
:
baud
=
300
;
break
;
case
B600
:
baud
=
600
;
break
;
case
B1200
:
baud
=
1200
;
break
;
case
B2400
:
baud
=
2400
;
break
;
case
B4800
:
baud
=
4800
;
break
;
default:
case
B9600
:
baud
=
9600
;
break
;
case
B19200
:
baud
=
19200
;
break
;
case
B38400
:
baud
=
38400
;
break
;
};
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and turn off
* the receiver.
* Temporary fix.
*/
info
->
interrupt_mask0
|=
SAB82532_IMR0_TCD
;
writeb
(
info
->
interrupt_mask0
,
&
info
->
regs
->
w
.
imr0
);
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
sab82532_wait_until_sent
(
tty
,
info
->
timeout
);
}
shutdown
(
info
);
if
(
tty
->
driver
.
flush_buffer
)
tty
->
driver
.
flush_buffer
(
tty
);
if
(
tty
->
ldisc
.
flush_buffer
)
tty
->
ldisc
.
flush_buffer
(
tty
);
tty
->
closing
=
0
;
info
->
event
=
0
;
info
->
tty
=
0
;
if
(
info
->
blocked_open
)
{
if
(
info
->
close_delay
)
{
current
->
state
=
TASK_INTERRUPTIBLE
;
schedule_timeout
(
info
->
close_delay
);
}
wake_up_interruptible
(
&
info
->
open_wait
);
}
info
->
flags
&=
~
(
ASYNC_NORMAL_ACTIVE
|
ASYNC_CALLOUT_ACTIVE
|
ASYNC_CLOSING
);
wake_up_interruptible
(
&
info
->
close_wait
);
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
}
/*
* sab82532_wait_until_sent() --- wait until the transmitter is empty
*/
static
void
sab82532_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
unsigned
long
orig_jiffies
,
char_time
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_wait_until_sent"
))
return
;
spin_lock_init
(
&
up
->
port
.
lock
);
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
* interval should also be less than the timeout.
*
* Note: we have to use pretty tight timings here to satisfy
* the NIST-PCTS.
* Initialize the hardware
*/
char_time
=
(
info
->
timeout
-
HZ
/
50
)
/
info
->
xmit_fifo_size
;
char_time
=
char_time
/
5
;
if
(
char_time
==
0
)
char_time
=
1
;
if
(
timeout
)
char_time
=
MIN
(
char_time
,
timeout
);
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk
(
"In sab82532_wait_until_sent(%d) check=%lu "
"xmit_cnt = %ld, alls = %d (jiff=%lu)...
\n
"
,
timeout
,
char_time
,
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
),
test_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
),
jiffies
);
#endif
orig_jiffies
=
jiffies
;
while
((
info
->
xmit
.
head
!=
info
->
xmit
.
tail
)
||
!
test_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
))
{
current
->
state
=
TASK_INTERRUPTIBLE
;
schedule_timeout
(
char_time
);
if
(
signal_pending
(
current
))
break
;
if
(
timeout
&&
time_after
(
jiffies
,
orig_jiffies
+
timeout
))
break
;
}
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk
(
"xmit_cnt = %ld, alls = %d (jiff=%lu)...done
\n
"
,
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
),
test_bit
(
SAB82532_ALLS
,
&
info
->
irqflags
),
jiffies
);
#endif
}
/*
* sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
static
void
sab82532_hangup
(
struct
tty_struct
*
tty
)
{
struct
sab82532
*
info
=
(
struct
sab82532
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_hangup"
))
return
;
#ifdef CONFIG_SERIAL_CONSOLE
if
(
info
->
is_console
)
return
;
#endif
sab82532_flush_buffer
(
tty
);
shutdown
(
info
);
info
->
event
=
0
;
info
->
count
=
0
;
info
->
flags
&=
~
(
ASYNC_NORMAL_ACTIVE
|
ASYNC_CALLOUT_ACTIVE
);
info
->
tty
=
0
;
wake_up_interruptible
(
&
info
->
open_wait
);
}
sunsab_startup
(
&
up
->
port
);
/*
* ------------------------------------------------------------
* sab82532_open() and friends
* ------------------------------------------------------------
*/
static
int
block_til_ready
(
struct
tty_struct
*
tty
,
struct
file
*
filp
,
struct
sab82532
*
info
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
int
retval
;
int
do_clocal
=
0
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
* Finally, enable interrupts
*/
if
(
tty_hung_up_p
(
filp
)
||
(
info
->
flags
&
ASYNC_CLOSING
))
{
if
(
info
->
flags
&
ASYNC_CLOSING
)
interruptible_sleep_on
(
&
info
->
close_wait
);
#ifdef SERIAL_DO_RESTART
if
(
info
->
flags
&
ASYNC_HUP_NOTIFY
)
return
-
EAGAIN
;
else
return
-
ERESTARTSYS
;
#else
return
-
EAGAIN
;
#endif
}
up
->
interrupt_mask0
=
SAB82532_IMR0_PERR
|
SAB82532_IMR0_FERR
|
SAB82532_IMR0_PLLA
|
SAB82532_IMR0_CDSC
;
writeb
(
up
->
interrupt_mask0
,
&
up
->
regs
->
w
.
imr0
);
up
->
interrupt_mask1
=
SAB82532_IMR1_BRKT
|
SAB82532_IMR1_ALLS
|
SAB82532_IMR1_XOFF
|
SAB82532_IMR1_TIN
|
SAB82532_IMR1_CSC
|
SAB82532_IMR1_XON
|
SAB82532_IMR1_XPR
;
writeb
(
up
->
interrupt_mask1
,
&
up
->
regs
->
w
.
imr1
);
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if
(
tty
->
driver
.
subtype
==
SERIAL_TYPE_CALLOUT
)
{
if
(
info
->
flags
&
ASYNC_NORMAL_ACTIVE
)
return
-
EBUSY
;
if
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_SESSION_LOCKOUT
)
&&
(
info
->
session
!=
current
->
session
))
return
-
EBUSY
;
if
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_PGRP_LOCKOUT
)
&&
(
info
->
pgrp
!=
current
->
pgrp
))
return
-
EBUSY
;
info
->
flags
|=
ASYNC_CALLOUT_ACTIVE
;
return
0
;
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if
((
filp
->
f_flags
&
O_NONBLOCK
)
||
(
tty
->
flags
&
(
1
<<
TTY_IO_ERROR
)))
{
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
return
-
EBUSY
;
info
->
flags
|=
ASYNC_NORMAL_ACTIVE
;
return
0
;
}
sunsab_convert_to_sab
(
up
,
con
->
cflag
,
0
,
baud
);
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
{
if
(
info
->
normal_termios
.
c_cflag
&
CLOCAL
)
do_clocal
=
1
;
}
else
{
if
(
tty
->
termios
->
c_cflag
&
CLOCAL
)
do_clocal
=
1
;
}
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* sab82532_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval
=
0
;
add_wait_queue
(
&
info
->
open_wait
,
&
wait
);
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready before block: ttyS%d, count = %d
\n
"
,
info
->
line
,
info
->
count
);
#endif
cli
();
if
(
!
tty_hung_up_p
(
filp
))
info
->
count
--
;
sti
();
info
->
blocked_open
++
;
while
(
1
)
{
cli
();
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
tty
->
termios
->
c_cflag
&
CBAUD
))
{
writeb
(
readb
(
&
info
->
regs
->
rw
.
pvr
)
&
~
(
info
->
pvr_dtr_bit
),
&
info
->
regs
->
rw
.
pvr
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
&
~
(
SAB82532_MODE_RTS
),
&
info
->
regs
->
rw
.
mode
);
}
sti
();
set_current_state
(
TASK_INTERRUPTIBLE
);
if
(
tty_hung_up_p
(
filp
)
||
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
{
#ifdef SERIAL_DO_RESTART
if
(
info
->
flags
&
ASYNC_HUP_NOTIFY
)
retval
=
-
EAGAIN
;
else
retval
=
-
ERESTARTSYS
;
#else
retval
=
-
EAGAIN
;
#endif
break
;
}
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
!
(
info
->
flags
&
ASYNC_CLOSING
)
&&
(
do_clocal
||
!
(
readb
(
&
info
->
regs
->
r
.
vstr
)
&
SAB82532_VSTR_CD
)))
break
;
if
(
signal_pending
(
current
))
{
retval
=
-
ERESTARTSYS
;
break
;
}
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x
\n
"
,
info
->
line
,
info
->
count
,
info
->
flags
,
do_clocal
,
readb
(
&
info
->
regs
->
r
.
vstr
));
#endif
schedule
();
}
current
->
state
=
TASK_RUNNING
;
remove_wait_queue
(
&
info
->
open_wait
,
&
wait
);
if
(
!
tty_hung_up_p
(
filp
))
info
->
count
++
;
info
->
blocked_open
--
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready after blocking: ttys%d, count = %d
\n
"
,
info
->
line
,
info
->
count
);
#endif
if
(
retval
)
return
retval
;
info
->
flags
|=
ASYNC_NORMAL_ACTIVE
;
return
0
;
}
/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
static
int
sab82532_open
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
struct
sab82532
*
info
=
sab82532_chain
;
int
retval
,
line
;
unsigned
long
page
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"sab82532_open: count = %d
\n
"
,
info
->
count
);
#endif
line
=
minor
(
tty
->
device
)
-
tty
->
driver
.
minor_start
;
if
((
line
<
0
)
||
(
line
>=
NR_PORTS
))
return
-
ENODEV
;
while
(
info
)
{
if
(
info
->
line
==
line
)
break
;
info
=
info
->
next
;
}
if
(
!
info
)
{
printk
(
"sab82532_open: can't find info for line %d
\n
"
,
line
);
return
-
ENODEV
;
}
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"sab82532_open"
))
return
-
ENODEV
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"sab82532_open %s%d, count = %d
\n
"
,
tty
->
driver
.
name
,
info
->
line
,
info
->
count
);
#endif
if
(
!
tmp_buf
)
{
page
=
get_free_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
if
(
tmp_buf
)
free_page
(
page
);
else
tmp_buf
=
(
unsigned
char
*
)
page
;
}
info
->
count
++
;
tty
->
driver_data
=
info
;
info
->
tty
=
tty
;
/*
* If the port is in the middle of closing, bail out now.
*/
if
(
tty_hung_up_p
(
filp
)
||
(
info
->
flags
&
ASYNC_CLOSING
))
{
if
(
info
->
flags
&
ASYNC_CLOSING
)
interruptible_sleep_on
(
&
info
->
close_wait
);
#ifdef SERIAL_DO_RESTART
return
((
info
->
flags
&
ASYNC_HUP_NOTIFY
)
?
-
EAGAIN
:
-
ERESTARTSYS
);
#else
return
-
EAGAIN
;
#endif
}
/*
* Start up serial port
*/
retval
=
startup
(
info
);
if
(
retval
)
return
retval
;
MOD_INC_USE_COUNT
;
retval
=
block_til_ready
(
tty
,
filp
,
info
);
if
(
retval
)
{
#ifdef SERIAL_DEBUG_OPEN
printk
(
"sab82532_open returning after block_til_ready with %d
\n
"
,
retval
);
#endif
return
retval
;
}
if
((
info
->
count
==
1
)
&&
(
info
->
flags
&
ASYNC_SPLIT_TERMIOS
))
{
if
(
tty
->
driver
.
subtype
==
SERIAL_TYPE_NORMAL
)
*
tty
->
termios
=
info
->
normal_termios
;
else
*
tty
->
termios
=
info
->
callout_termios
;
change_speed
(
info
);
}
#ifdef CONFIG_SERIAL_CONSOLE
if
(
sab82532_console
.
cflag
&&
sab82532_console
.
index
==
line
)
{
tty
->
termios
->
c_cflag
=
sab82532_console
.
cflag
;
sab82532_console
.
cflag
=
0
;
change_speed
(
info
);
}
#endif
info
->
session
=
current
->
session
;
info
->
pgrp
=
current
->
pgrp
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"sab82532_open ttys%d successful... count %d"
,
info
->
line
,
info
->
count
);
#endif
return
0
;
}
/*
* /proc fs routines....
*/
static
struct
console
sunsab_console
=
{
.
name
=
"ttyS"
,
.
write
=
sunsab_console_write
,
.
device
=
sunsab_console_device
,
.
setup
=
sunsab_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
};
static
__inline__
int
line_info
(
char
*
buf
,
struct
sab82532
*
info
)
static
void
__init
sunsab_console_init
(
void
)
{
unsigned
long
flags
;
char
stat_buf
[
30
];
int
ret
;
int
i
;
ret
=
sprintf
(
buf
,
"%u: uart:SAB82532 "
,
info
->
line
);
switch
(
info
->
type
)
{
case
0
:
ret
+=
sprintf
(
buf
+
ret
,
"V1.0 "
);
break
;
case
1
:
ret
+=
sprintf
(
buf
+
ret
,
"V2.0 "
);
break
;
case
2
:
ret
+=
sprintf
(
buf
+
ret
,
"V3.2 "
);
break
;
default:
ret
+=
sprintf
(
buf
+
ret
,
"V?.? "
);
break
;
}
ret
+=
sprintf
(
buf
+
ret
,
"port:%lX irq:%s"
,
(
unsigned
long
)
info
->
regs
,
__irq_itoa
(
info
->
irq
));
if
(
con_is_present
())
return
;
if
(
!
info
->
regs
)
{
ret
+=
sprintf
(
buf
+
ret
,
"
\n
"
);
return
ret
;
}
for
(
i
=
0
;
i
<
num_channels
;
i
++
)
{
int
this_minor
=
sunsab_reg
.
minor
+
i
;
/*
* Figure out the current RS-232 lines
*/
stat_buf
[
0
]
=
0
;
stat_buf
[
1
]
=
0
;
save_flags
(
flags
);
cli
();
if
(
readb
(
&
info
->
regs
->
r
.
mode
)
&
SAB82532_MODE_RTS
)
{
if
(
!
(
readb
(
&
info
->
regs
->
r
.
mode
)
&
SAB82532_MODE_FRTS
))
strcat
(
stat_buf
,
"|RTS"
);
}
else
{
strcat
(
stat_buf
,
"|RTS"
);
if
((
this_minor
-
64
)
==
(
serial_console
-
1
))
break
;
}
if
(
readb
(
&
info
->
regs
->
r
.
star
)
&
SAB82532_STAR_CTS
)
strcat
(
stat_buf
,
"|CTS"
);
if
(
!
(
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dtr_bit
))
strcat
(
stat_buf
,
"|DTR"
);
if
(
!
(
readb
(
&
info
->
regs
->
r
.
pvr
)
&
info
->
pvr_dsr_bit
))
strcat
(
stat_buf
,
"|DSR"
);
if
(
!
(
readb
(
&
info
->
regs
->
r
.
vstr
)
&
SAB82532_VSTR_CD
))
strcat
(
stat_buf
,
"|CD"
);
restore_flags
(
flags
);
if
(
info
->
baud
)
ret
+=
sprintf
(
buf
+
ret
,
" baud:%u"
,
info
->
baud
);
ret
+=
sprintf
(
buf
+
ret
,
" tx:%u rx:%u"
,
info
->
icount
.
tx
,
info
->
icount
.
rx
);
if
(
info
->
icount
.
frame
)
ret
+=
sprintf
(
buf
+
ret
,
" fe:%u"
,
info
->
icount
.
frame
);
if
(
info
->
icount
.
parity
)
ret
+=
sprintf
(
buf
+
ret
,
" pe:%u"
,
info
->
icount
.
parity
);
if
(
info
->
icount
.
brk
)
ret
+=
sprintf
(
buf
+
ret
,
" brk:%u"
,
info
->
icount
.
brk
);
if
(
info
->
icount
.
overrun
)
ret
+=
sprintf
(
buf
+
ret
,
" oe:%u"
,
info
->
icount
.
overrun
);
/*
* Last thing is the RS-232 status lines.
*/
ret
+=
sprintf
(
buf
+
ret
,
" %s
\n
"
,
stat_buf
+
1
);
return
ret
;
}
if
(
i
==
num_channels
)
return
;
int
sab82532_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
struct
sab82532
*
info
=
sab82532_chain
;
off_t
begin
=
0
;
int
len
=
0
;
len
+=
sprintf
(
page
,
"serinfo:1.0 driver:%s
\n
"
,
serial_version
);
for
(
info
=
sab82532_chain
;
info
&&
len
<
4000
;
info
=
info
->
next
)
{
len
+=
line_info
(
page
+
len
,
info
);
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
);
sunsab_console
.
index
=
i
;
register_console
(
&
sunsab_console
);
}
/*
* ---------------------------------------------------------------------
* sab82532_init() and friends
*
* sab82532_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
static
int
__init
get_sab82532
(
unsigned
long
*
memory_start
)
static
void
__init
for_each_sab_edev
(
void
(
*
callback
)(
struct
linux_ebus_device
*
,
void
*
),
void
*
arg
)
{
struct
linux_ebus
*
ebus
;
struct
linux_ebus_device
*
edev
=
0
;
struct
sab82532
*
sab
;
unsigned
long
regs
,
offset
;
int
i
;
struct
linux_ebus_device
*
edev
=
NULL
;
for_each_ebus
(
ebus
)
{
for_each_ebusdev
(
edev
,
ebus
)
{
if
(
!
strcmp
(
edev
->
prom_name
,
"se"
))
goto
ebus_done
;
if
(
!
strcmp
(
edev
->
prom_name
,
"serial"
))
{
if
(
!
strcmp
(
edev
->
prom_name
,
"se"
))
{
callback
(
edev
,
arg
)
;
continue
;
}
else
if
(
!
strcmp
(
edev
->
prom_name
,
"serial"
))
{
char
compat
[
32
];
int
clen
;
...
...
@@ -2136,481 +988,183 @@ static int __init get_sab82532(unsigned long *memory_start)
compat
,
sizeof
(
compat
));
if
(
clen
>
0
)
{
if
(
strncmp
(
compat
,
"sab82532"
,
8
)
==
0
)
{
/* Yep. */
goto
ebus_don
e
;
callback
(
edev
,
arg
);
continu
e
;
}
}
}
}
}
ebus_done:
if
(
!
edev
)
return
-
ENODEV
;
}
static
void
__init
sab_count_callback
(
struct
linux_ebus_device
*
edev
,
void
*
arg
)
{
int
*
count_p
=
arg
;
(
*
count_p
)
++
;
}
static
void
__init
sab_attach_callback
(
struct
linux_ebus_device
*
edev
,
void
*
arg
)
{
int
*
instance_p
=
arg
;
struct
uart_sunsab_port
*
up
;
unsigned
long
regs
,
offset
;
int
i
;
regs
=
edev
->
resource
[
0
].
start
;
offset
=
sizeof
(
union
sab82532_async_regs
);
for
(
i
=
0
;
i
<
2
;
i
++
)
{
if
(
memory_start
)
{
*
memory_start
=
(
*
memory_start
+
7
)
&
~
(
7
);
sab
=
(
struct
sab82532
*
)
*
memory_start
;
*
memory_start
+=
sizeof
(
struct
sab82532
);
}
else
{
sab
=
(
struct
sab82532
*
)
kmalloc
(
sizeof
(
struct
sab82532
),
GFP_KERNEL
);
if
(
!
sab
)
{
printk
(
"sab82532: can't alloc sab struct
\n
"
);
break
;
}
}
memset
(
sab
,
0
,
sizeof
(
struct
sab82532
));
sab
->
regs
=
ioremap
(
regs
+
offset
,
sizeof
(
union
sab82532_async_regs
));
sab
->
irq
=
edev
->
irqs
[
0
];
sab
->
line
=
1
-
i
;
sab
->
xmit_fifo_size
=
32
;
sab
->
recv_fifo_size
=
32
;
up
=
&
sunsab_ports
[(
*
instance_p
*
2
)
+
i
];
writeb
(
SAB82532_IPC_IC_ACT_LOW
,
&
sab
->
regs
->
w
.
ipc
);
memset
(
up
,
0
,
sizeof
(
*
up
));
up
->
regs
=
ioremap
(
regs
+
offset
,
sizeof
(
union
sab82532_async_regs
));
up
->
irq
=
edev
->
irqs
[
0
];
up
->
sab_line
=
1
-
i
;
up
->
xmit_fifo_size
=
32
;
up
->
recv_fifo_size
=
32
;
sab
->
next
=
sab82532_chain
;
sab82532_chain
=
sab
;
writeb
(
SAB82532_IPC_IC_ACT_LOW
,
&
up
->
regs
->
w
.
ipc
);
offset
-=
sizeof
(
union
sab82532_async_regs
);
}
return
0
;
}
#ifndef MODULE
static
void
__init
sab82532_kgdb_hook
(
int
line
)
static
int
__init
probe_for_sabs
(
void
)
{
prom_printf
(
"sab82532: kgdb support is not implemented, yet
\n
"
);
prom_halt
();
}
#endif
int
this_sab
=
0
;
static
inline
void
__init
show_serial_version
(
void
)
{
char
*
revision
=
"$Revision: 1.66 $"
;
char
*
version
,
*
p
;
version
=
strchr
(
revision
,
' '
);
strcpy
(
serial_version
,
++
version
);
p
=
strchr
(
serial_version
,
' '
);
*
p
=
'\0'
;
printk
(
"SAB82532 serial driver version %s
\n
"
,
serial_version
);
}
/* Find device instances. */
for_each_sab_edev
(
&
sab_count_callback
,
&
this_sab
);
if
(
!
this_sab
)
return
-
ENODEV
;
extern
int
su_num_ports
;
/* Allocate tables. */
sunsab_ports
=
kmalloc
(
sizeof
(
struct
uart_sunsab_port
)
*
this_sab
*
2
,
GFP_KERNEL
);
if
(
!
sunsab_ports
)
return
-
ENOMEM
;
/*
* The serial driver boot-time initialization code!
*/
int
__init
sab82532_init
(
void
)
{
struct
sab82532
*
info
;
int
i
;
num_channels
=
this_sab
*
2
;
if
(
!
sab82532_chain
)
get_sab82532
(
0
);
if
(
!
sab82532_chain
)
return
-
ENODEV
;
this_sab
=
0
;
for_each_sab_edev
(
&
sab_attach_callback
,
&
this_sab
);
return
0
;
}
init_bh
(
SERIAL_BH
,
do_serial_bh
);
static
void
__init
sunsab_init_hw
(
void
)
{
int
i
;
show_serial_version
();
for
(
i
=
0
;
i
<
num_channels
;
i
++
)
{
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
i
];
/* Initialize the tty_driver structure */
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
+
su_num_ports
;
serial_driver
.
num
=
NR_PORTS
;
serial_driver
.
type
=
TTY_DRIVER_TYPE_SERIAL
;
serial_driver
.
subtype
=
SERIAL_TYPE_NORMAL
;
serial_driver
.
init_termios
=
tty_std_termios
;
serial_driver
.
init_termios
.
c_cflag
=
B9600
|
CS8
|
CREAD
|
HUPCL
|
CLOCAL
;
serial_driver
.
flags
=
TTY_DRIVER_REAL_RAW
;
serial_driver
.
refcount
=
&
sab82532_refcount
;
serial_driver
.
table
=
sab82532_table
;
serial_driver
.
termios
=
sab82532_termios
;
serial_driver
.
termios_locked
=
sab82532_termios_locked
;
serial_driver
.
open
=
sab82532_open
;
serial_driver
.
close
=
sab82532_close
;
serial_driver
.
write
=
sab82532_write
;
serial_driver
.
put_char
=
sab82532_put_char
;
serial_driver
.
flush_chars
=
sab82532_flush_chars
;
serial_driver
.
write_room
=
sab82532_write_room
;
serial_driver
.
chars_in_buffer
=
sab82532_chars_in_buffer
;
serial_driver
.
flush_buffer
=
sab82532_flush_buffer
;
serial_driver
.
ioctl
=
sab82532_ioctl
;
serial_driver
.
throttle
=
sab82532_throttle
;
serial_driver
.
unthrottle
=
sab82532_unthrottle
;
serial_driver
.
send_xchar
=
sab82532_send_xchar
;
serial_driver
.
set_termios
=
sab82532_set_termios
;
serial_driver
.
stop
=
sab82532_stop
;
serial_driver
.
start
=
sab82532_start
;
serial_driver
.
hangup
=
sab82532_hangup
;
serial_driver
.
break_ctl
=
sab82532_break
;
serial_driver
.
wait_until_sent
=
sab82532_wait_until_sent
;
serial_driver
.
read_proc
=
sab82532_read_proc
;
up
->
port
.
line
=
i
;
up
->
port
.
ops
=
&
sunsab_pops
;
up
->
port
.
type
=
PORT_SUNSAB
;
up
->
port
.
uartclk
=
SAB_BASE_BAUD
;
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
callout_driver
=
serial_driver
;
#ifdef CONFIG_DEVFS_FS
callout_driver
.
name
=
"cua/%d"
;
#else
callout_driver
.
name
=
"cua"
;
#endif
callout_driver
.
major
=
TTYAUX_MAJOR
;
callout_driver
.
subtype
=
SERIAL_TYPE_CALLOUT
;
callout_driver
.
read_proc
=
0
;
callout_driver
.
proc_entry
=
0
;
if
(
tty_register_driver
(
&
serial_driver
))
panic
(
"Couldn't register serial driver
\n
"
);
if
(
tty_register_driver
(
&
callout_driver
))
panic
(
"Couldn't register callout driver
\n
"
);
for
(
info
=
sab82532_chain
,
i
=
0
;
info
;
info
=
info
->
next
,
i
++
)
{
info
->
magic
=
SERIAL_MAGIC
;
info
->
type
=
readb
(
&
info
->
regs
->
r
.
vstr
)
&
0x0f
;
writeb
(
~
((
1
<<
1
)
|
(
1
<<
2
)
|
(
1
<<
4
)),
&
info
->
regs
->
w
.
pcr
);
writeb
(
0xff
,
&
info
->
regs
->
w
.
pim
);
if
(
info
->
line
==
0
)
{
info
->
pvr_dsr_bit
=
(
1
<<
0
);
info
->
pvr_dtr_bit
=
(
1
<<
1
);
up
->
type
=
readb
(
&
up
->
regs
->
r
.
vstr
)
&
0x0f
;
writeb
(
~
((
1
<<
1
)
|
(
1
<<
2
)
|
(
1
<<
4
)),
&
up
->
regs
->
w
.
pcr
);
writeb
(
0xff
,
&
up
->
regs
->
w
.
pim
);
if
(
up
->
sab_line
==
0
)
{
up
->
pvr_dsr_bit
=
(
1
<<
0
);
up
->
pvr_dtr_bit
=
(
1
<<
1
);
}
else
{
info
->
pvr_dsr_bit
=
(
1
<<
3
);
info
->
pvr_dtr_bit
=
(
1
<<
2
);
up
->
pvr_dsr_bit
=
(
1
<<
3
);
up
->
pvr_dtr_bit
=
(
1
<<
2
);
}
writeb
((
1
<<
1
)
|
(
1
<<
2
)
|
(
1
<<
4
),
&
info
->
regs
->
w
.
pvr
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
info
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
info
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
info
->
regs
->
rw
.
mode
);
info
->
custom_divisor
=
16
;
info
->
close_delay
=
5
*
HZ
/
10
;
info
->
closing_wait
=
30
*
HZ
;
info
->
tec_timeout
=
SAB82532_MAX_TEC_TIMEOUT
;
info
->
cec_timeout
=
SAB82532_MAX_CEC_TIMEOUT
;
info
->
x_char
=
0
;
info
->
event
=
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
);
init_waitqueue_head
(
&
info
->
delta_msr_wait
);
info
->
icount
.
cts
=
info
->
icount
.
dsr
=
info
->
icount
.
rng
=
info
->
icount
.
dcd
=
0
;
info
->
icount
.
rx
=
info
->
icount
.
tx
=
0
;
info
->
icount
.
frame
=
info
->
icount
.
parity
=
0
;
info
->
icount
.
overrun
=
info
->
icount
.
brk
=
0
;
if
(
!
(
info
->
line
&
0x01
))
{
if
(
request_irq
(
info
->
irq
,
sab82532_interrupt
,
SA_SHIRQ
,
"serial(sab82532)"
,
info
))
{
printk
(
"sab82532: can't get IRQ %x
\n
"
,
info
->
irq
);
panic
(
"sab82532 initialization failed"
);
writeb
((
1
<<
1
)
|
(
1
<<
2
)
|
(
1
<<
4
),
&
up
->
regs
->
w
.
pvr
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_FRTS
,
&
up
->
regs
->
rw
.
mode
);
writeb
(
readb
(
&
up
->
regs
->
rw
.
mode
)
|
SAB82532_MODE_RTS
,
&
up
->
regs
->
rw
.
mode
);
up
->
tec_timeout
=
SAB82532_MAX_TEC_TIMEOUT
;
up
->
cec_timeout
=
SAB82532_MAX_CEC_TIMEOUT
;
if
(
!
(
up
->
sab_line
&
0x01
))
{
if
(
request_irq
(
up
->
irq
,
sunsab_interrupt
,
SA_SHIRQ
,
"serial(sab82532)"
,
up
))
{
printk
(
"sunsab%d: can't get IRQ %x
\n
"
,
i
,
up
->
irq
);
continue
;
}
}
printk
(
KERN_INFO
"
ttyS%02
d at 0x%lx (irq = %s) is a SAB82532 %s
\n
"
,
i
nfo
->
line
+
su_num_ports
,
(
unsigned
long
)
info
->
regs
,
__irq_itoa
(
info
->
irq
),
sab82532_version
[
info
->
type
]);
"
sunsab%
d at 0x%lx (irq = %s) is a SAB82532 %s
\n
"
,
i
,
(
unsigned
long
)
up
->
regs
,
__irq_itoa
(
up
->
irq
),
sab82532_version
[
up
->
type
]);
}
#ifdef SERIAL_LOG_DEVICE
dprint_init
(
SERIAL_LOG_DEVICE
);
#endif
return
0
;
}
int
__init
sab82532_probe
(
void
)
static
int
__init
sunsab_init
(
void
)
{
int
node
,
enode
,
snode
;
char
model
[
32
];
int
len
;
int
ret
=
probe_for_sabs
();
int
i
;
node
=
prom_getchild
(
prom_root_node
);
node
=
prom_searchsiblings
(
node
,
"pci"
)
;
if
(
ret
<
0
)
return
ret
;
/*
* Check for SUNW,sabre on Ultra 5/10/AXi.
*/
len
=
prom_getproperty
(
node
,
"model"
,
model
,
sizeof
(
model
));
if
((
len
>
0
)
&&
!
strncmp
(
model
,
"SUNW,sabre"
,
len
))
{
node
=
prom_getchild
(
node
);
node
=
prom_searchsiblings
(
node
,
"pci"
);
}
sunsab_init_hw
();
/*
* For each PCI bus...
*/
while
(
node
)
{
enode
=
prom_getchild
(
node
);
enode
=
prom_searchsiblings
(
enode
,
"ebus"
);
sunsab_reg
.
minor
=
sunserial_current_minor
;
sunserial_current_minor
+=
num_channels
;
/*
* For each EBus on this PCI...
*/
while
(
enode
)
{
int
child
;
sunsab_reg
.
nr
=
num_channels
;
sunsab_reg
.
cons
=
&
sunsab_console
;
child
=
prom_getchild
(
enode
);
snode
=
prom_searchsiblings
(
child
,
"se"
);
if
(
snode
)
goto
found
;
ret
=
uart_register_driver
(
&
sunsab_reg
);
if
(
ret
<
0
)
{
int
i
;
snode
=
prom_searchsiblings
(
child
,
"serial"
);
if
(
snode
)
{
char
compat
[
32
];
int
clen
;
clen
=
prom_getproperty
(
snode
,
"compatible"
,
compat
,
sizeof
(
compat
));
if
(
clen
>
0
)
{
if
(
strncmp
(
compat
,
"sab82532"
,
8
)
==
0
)
goto
found
;
}
}
for
(
i
=
0
;
i
<
num_channels
;
i
++
)
{
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
i
];
enode
=
prom_getsibling
(
enode
);
enode
=
prom_searchsiblings
(
enode
,
"ebus"
);
if
(
!
(
up
->
sab_line
&
0x01
))
free_irq
(
up
->
irq
,
up
);
iounmap
(
up
->
regs
);
}
node
=
prom_getsibling
(
node
);
node
=
prom_searchsiblings
(
node
,
"pci"
);
}
return
-
ENODEV
;
found:
#ifdef CONFIG_SERIAL_CONSOLE
sunserial_setinitfunc
(
sab82532_console_init
);
#endif
#ifndef MODULE
sunserial_setinitfunc
(
sab82532_init
);
rs_ops
.
rs_kgdb_hook
=
sab82532_kgdb_hook
;
#endif
return
0
;
}
#ifdef MODULE
MODULE_LICENSE
(
"GPL"
);
int
init_module
(
void
)
{
if
(
get_sab82532
(
0
))
return
-
ENODEV
;
return
sab82532_init
();
}
void
cleanup_module
(
void
)
{
struct
sab82532
*
sab
;
unsigned
long
flags
;
int
e1
,
e2
;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
save_flags
(
flags
);
cli
();
remove_bh
(
SERIAL_BH
);
if
((
e1
=
tty_unregister_driver
(
&
serial_driver
)))
printk
(
"SERIAL: failed to unregister serial driver (%d)
\n
"
,
e1
);
if
((
e2
=
tty_unregister_driver
(
&
callout_driver
)))
printk
(
"SERIAL: failed to unregister callout driver (%d)
\n
"
,
e2
);
restore_flags
(
flags
);
if
(
tmp_buf
)
{
free_page
((
unsigned
long
)
tmp_buf
);
tmp_buf
=
NULL
;
}
for
(
sab
=
sab82532_chain
;
sab
;
sab
=
sab
->
next
)
{
if
(
!
(
sab
->
line
&
0x01
))
free_irq
(
sab
->
irq
,
sab
);
iounmap
(
sab
->
regs
);
}
}
#endif
/* MODULE */
#ifdef CONFIG_SERIAL_CONSOLE
static
__inline__
void
sab82532_console_putchar
(
struct
sab82532
*
info
,
char
c
)
{
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
sab82532_tec_wait
(
info
);
writeb
(
c
,
&
info
->
regs
->
w
.
tic
);
restore_flags
(
flags
);
}
kfree
(
sunsab_ports
);
sunsab_ports
=
NULL
;
static
void
sab82532_console_write
(
struct
console
*
con
,
const
char
*
s
,
unsigned
n
)
{
struct
sab82532
*
info
;
int
i
;
info
=
sab82532_chain
;
for
(
i
=
con
->
index
;
i
;
i
--
)
{
info
=
info
->
next
;
if
(
!
info
)
return
;
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
*
s
==
'\n'
)
sab82532_console_putchar
(
info
,
'\r'
);
sab82532_console_putchar
(
info
,
*
s
++
);
}
sab82532_tec_wait
(
info
);
}
static
kdev_t
sab82532_console_device
(
struct
console
*
con
)
{
return
mk_kdev
(
TTY_MAJOR
,
64
+
con
->
index
);
}
static
int
sab82532_console_setup
(
struct
console
*
con
,
char
*
options
)
{
static
struct
tty_struct
c_tty
;
static
struct
termios
c_termios
;
struct
sab82532
*
info
;
tcflag_t
cflag
;
int
i
;
info
=
sab82532_chain
;
for
(
i
=
con
->
index
;
i
;
i
--
)
{
info
=
info
->
next
;
if
(
!
info
)
return
-
ENODEV
;
return
ret
;
}
info
->
is_console
=
1
;
/*
* Initialize the hardware
*/
sab82532_init_line
(
info
);
/*
* Finally, enable interrupts
*/
info
->
interrupt_mask0
=
SAB82532_IMR0_PERR
|
SAB82532_IMR0_FERR
|
SAB82532_IMR0_PLLA
|
SAB82532_IMR0_CDSC
;
writeb
(
info
->
interrupt_mask0
,
&
info
->
regs
->
w
.
imr0
);
info
->
interrupt_mask1
=
SAB82532_IMR1_BRKT
|
SAB82532_IMR1_ALLS
|
SAB82532_IMR1_XOFF
|
SAB82532_IMR1_TIN
|
SAB82532_IMR1_CSC
|
SAB82532_IMR1_XON
|
SAB82532_IMR1_XPR
;
writeb
(
info
->
interrupt_mask1
,
&
info
->
regs
->
w
.
imr1
);
printk
(
"Console: ttyS%d (SAB82532)
\n
"
,
info
->
line
);
sunserial_console_termios
(
con
);
cflag
=
con
->
cflag
;
for
(
i
=
0
;
i
<
num_channels
;
i
++
)
{
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
i
]
;
/*
* Fake up the tty and tty->termios structures so we can use
* change_speed (and eliminate a lot of duplicate code).
*/
if
(
!
info
->
tty
)
{
memset
(
&
c_tty
,
0
,
sizeof
(
c_tty
));
info
->
tty
=
&
c_tty
;
}
if
(
!
info
->
tty
->
termios
)
{
memset
(
&
c_termios
,
0
,
sizeof
(
c_termios
));
info
->
tty
->
termios
=
&
c_termios
;
uart_add_one_port
(
&
sunsab_reg
,
&
up
->
port
);
}
info
->
tty
->
termios
->
c_cflag
=
con
->
cflag
;
change_speed
(
info
);
sunsab_console_init
(
);
/* Now take out the pointers to static structures if necessary */
if
(
info
->
tty
->
termios
==
&
c_termios
)
info
->
tty
->
termios
=
0
;
if
(
info
->
tty
==
&
c_tty
)
info
->
tty
=
0
;
return
0
;
}
static
struct
console
sab82532_console
=
{
.
name
=
"ttyS"
,
.
write
=
sab82532_console_write
,
.
device
=
sab82532_console_device
,
.
setup
=
sab82532_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
};
int
__init
sab82532_console_init
(
void
)
static
void
__exit
sunsab_exit
(
void
)
{
extern
int
su_console_registered
;
if
(
con_is_present
()
||
su_console_registered
)
return
0
;
int
i
;
if
(
!
sab82532_chain
)
{
prom_printf
(
"sab82532_console_setup: can't get SAB82532 chain"
);
prom_halt
();
}
for
(
i
=
0
;
i
<
num_channels
;
i
++
)
{
struct
uart_sunsab_port
*
up
=
&
sunsab_ports
[
i
];
sab82532_console
.
index
=
serial_console
-
1
;
register_console
(
&
sab82532_console
);
return
0
;
}
uart_remove_one_port
(
&
sunsab_reg
,
&
up
->
port
);
#ifdef SERIAL_LOG_DEVICE
if
(
!
(
up
->
sab_line
&
0x01
))
free_irq
(
up
->
irq
,
up
);
iounmap
(
up
->
regs
);
}
static
int
serial_log_device
=
0
;
uart_unregister_driver
(
&
sunsab_reg
)
;
static
void
dprint_init
(
int
tty
)
{
serial_console
=
tty
+
1
;
sab82532_console
.
index
=
tty
;
sab82532_console_setup
(
&
sab82532_console
,
""
);
serial_console
=
0
;
serial_log_device
=
tty
+
1
;
kfree
(
sunsab_ports
);
sunsab_ports
=
NULL
;
}
int
dprintf
(
const
char
*
fmt
,
...)
{
static
char
buffer
[
4096
];
va_list
args
;
int
i
;
module_init
(
sunsab_init
);
module_exit
(
sunsab_exit
);
if
(
!
serial_log_device
)
return
0
;
va_start
(
args
,
fmt
);
i
=
vsprintf
(
buffer
,
fmt
,
args
);
va_end
(
args
);
sab82532_console
.
write
(
&
sab82532_console
,
buffer
,
i
);
return
i
;
}
#endif
/* SERIAL_LOG_DEVICE */
#endif
/* CONFIG_SERIAL_CONSOLE */
MODULE_AUTHOR
(
"Eddie C. Dost and David S. Miller"
);
MODULE_DESCRIPTION
(
"Sun SAB82532 serial port driver"
);
MODULE_LICENSE
(
"GPL"
);
include/asm-sparc64/sab82532
.h
→
drivers/serial/sunsab
.h
View file @
e79e45cb
/* $Id: sab82532.h,v 1.7 2001/05/23 23:09:10 ecd Exp $
* sab82532.h: Register Definitions for the Siemens SAB82532 DUSCC
/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#ifndef _SPARC64_SAB82532_H
#define _SPARC64_SAB82532_H
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/circ_buf.h>
#ifndef _SUNSAB_H
#define _SUNSAB_H
struct
sab82532_async_rd_regs
{
u8
rfifo
[
0x20
];
/* Receive FIFO */
...
...
@@ -120,8 +115,6 @@ union sab82532_async_regs {
__volatile__
struct
sab82532_async_rw_regs
rw
;
};
#define NR_PORTS 2
union
sab82532_irq_status
{
unsigned
short
stat
;
struct
{
...
...
@@ -130,62 +123,10 @@ union sab82532_irq_status {
}
sreg
;
};
struct
sab82532
{
int
magic
;
int
baud_base
;
union
sab82532_async_regs
*
regs
;
int
irq
;
int
flags
;
/* defined in tty.h */
int
type
;
/* SAB82532 version */
struct
tty_struct
*
tty
;
int
read_status_mask
;
int
ignore_status_mask
;
int
timeout
;
int
xmit_fifo_size
;
int
recv_fifo_size
;
int
custom_divisor
;
int
baud
;
unsigned
int
cec_timeout
;
unsigned
int
tec_timeout
;
int
x_char
;
int
close_delay
;
unsigned
short
closing_wait
;
unsigned
short
closing_wait2
;
unsigned
long
irqflags
;
int
is_console
;
unsigned
char
interrupt_mask0
;
unsigned
char
interrupt_mask1
;
unsigned
char
pvr_dtr_bit
;
unsigned
char
pvr_dsr_bit
;
unsigned
char
dcd
;
unsigned
char
cts
;
unsigned
char
dsr
;
unsigned
long
event
;
unsigned
long
last_active
;
int
line
;
int
count
;
int
blocked_open
;
long
session
;
long
pgrp
;
struct
circ_buf
xmit
;
struct
tq_struct
tqueue
;
struct
tq_struct
tqueue_hangup
;
struct
async_icount
icount
;
struct
termios
normal_termios
;
struct
termios
callout_termios
;
wait_queue_head_t
open_wait
;
wait_queue_head_t
close_wait
;
wait_queue_head_t
delta_msr_wait
;
struct
sab82532
*
next
;
struct
sab82532
*
prev
;
};
/* irqflags bits */
#define SAB82532_ALLS 0x00000001
#define SAB82532_XPR 0x00000002
/* RFIFO Status Byte */
#define SAB82532_RSTAT_PE 0x80
#define SAB82532_RSTAT_FE 0x40
...
...
@@ -377,4 +318,4 @@ struct sab82532 {
#define SAB82532_CCR4_ICD 0x10
#endif
/* !(_S
PARC64_SAB82532
_H) */
#endif
/* !(_S
UNSAB
_H) */
drivers/serial/sunsu.c
View file @
e79e45cb
...
...
@@ -1362,7 +1362,7 @@ static void sunsu_console_write(struct console *co, const char *s,
static
kdev_t
sunsu_console_device
(
struct
console
*
co
)
{
return
mk_kdev
(
TTY_MAJOR
,
64
+
co
->
index
);
return
mk_kdev
(
sunsu_reg
.
major
,
sunsu_reg
.
minor
+
co
->
index
);
}
/*
...
...
@@ -1379,6 +1379,9 @@ static int __init sunsu_console_setup(struct console *co, char *options)
int
parity
=
'n'
;
int
flow
=
'n'
;
printk
(
"Console: ttyS%d (SU)
\n
"
,
(
sunsu_reg
.
minor
-
64
)
+
co
->
index
);
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
...
...
@@ -1414,15 +1417,23 @@ static struct console sunsu_cons = {
static
int
__init
sunsu_serial_console_init
(
void
)
{
int
i
ndex
;
int
i
;
if
(
con_is_present
())
return
0
;
index
=
serial_console
-
1
;
if
(
sunsu_ports
[
index
].
port_node
==
0
)
for
(
i
=
0
;
i
<
UART_NR
;
i
++
)
{
int
this_minor
=
sunsu_reg
.
minor
+
i
;
if
((
this_minor
-
64
)
==
(
serial_console
-
1
))
break
;
}
if
(
i
==
UART_NR
)
return
0
;
sunsu_cons
.
index
=
index
;
if
(
sunsu_ports
[
i
].
port_node
==
0
)
return
0
;
sunsu_cons
.
index
=
i
;
register_console
(
&
sunsu_cons
);
return
0
;
}
...
...
drivers/serial/sunzilog.c
View file @
e79e45cb
...
...
@@ -1013,7 +1013,6 @@ static struct uart_driver sunzilog_reg = {
.
dev_name
=
"ttyS"
,
#endif
.
major
=
TTY_MAJOR
,
.
minor
=
64
,
};
static
void
*
__init
alloc_one_table
(
unsigned
long
size
)
...
...
@@ -1386,7 +1385,7 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count)
static
kdev_t
sunzilog_console_device
(
struct
console
*
con
)
{
return
mk_kdev
(
TTY_MAJOR
,
64
+
con
->
index
);
return
mk_kdev
(
sunzilog_reg
.
major
,
sunzilog_reg
.
minor
+
con
->
index
);
}
static
int
__init
sunzilog_console_setup
(
struct
console
*
con
,
char
*
options
)
...
...
@@ -1395,7 +1394,8 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
unsigned
long
flags
;
int
baud
,
brg
;
printk
(
"Console: ttyS%d (Zilog8530)
\n
"
,
con
->
index
/
2
);
printk
(
"Console: ttyS%d (Zilog8530)
\n
"
,
(
sunzilog_reg
.
minor
-
64
)
+
con
->
index
);
/* Get firmware console settings. */
sunserial_console_termios
(
con
);
...
...
@@ -1447,10 +1447,21 @@ static struct console sunzilog_console = {
static
int
__init
sunzilog_console_init
(
void
)
{
int
i
;
if
(
con_is_present
())
return
0
;
sunzilog_console
.
index
=
serial_console
-
1
;
for
(
i
=
0
;
i
<
NUM_CHANNELS
;
i
++
)
{
int
this_minor
=
sunzilog_reg
.
minor
+
i
;
if
((
this_minor
-
64
)
==
(
serial_console
-
1
))
break
;
}
if
(
i
==
NUM_CHANNELS
)
return
0
;
sunzilog_console
.
index
=
i
;
register_console
(
&
sunzilog_console
);
return
0
;
}
...
...
@@ -1480,6 +1491,7 @@ static void __init sunzilog_prepare(void)
up
[(
chip
*
2
)
+
0
].
port
.
uartclk
=
ZS_CLOCK
;
up
[(
chip
*
2
)
+
0
].
port
.
fifosize
=
1
;
up
[(
chip
*
2
)
+
0
].
port
.
ops
=
&
sunzilog_pops
;
up
[(
chip
*
2
)
+
0
].
port
.
type
=
PORT_SUNZILOG
;
up
[(
chip
*
2
)
+
0
].
port
.
flags
=
0
;
up
[(
chip
*
2
)
+
0
].
port
.
line
=
(
chip
*
2
)
+
0
;
up
[(
chip
*
2
)
+
0
].
flags
|=
SUNZILOG_FLAG_IS_CHANNEL_A
;
...
...
@@ -1490,6 +1502,7 @@ static void __init sunzilog_prepare(void)
up
[(
chip
*
2
)
+
1
].
port
.
uartclk
=
ZS_CLOCK
;
up
[(
chip
*
2
)
+
1
].
port
.
fifosize
=
1
;
up
[(
chip
*
2
)
+
1
].
port
.
ops
=
&
sunzilog_pops
;
up
[(
chip
*
2
)
+
1
].
port
.
type
=
PORT_SUNZILOG
;
up
[(
chip
*
2
)
+
1
].
port
.
flags
=
0
;
up
[(
chip
*
2
)
+
1
].
port
.
line
=
(
chip
*
2
)
+
1
;
up
[(
chip
*
2
)
+
1
].
flags
|=
0
;
...
...
@@ -1607,12 +1620,10 @@ static int __init sunzilog_ports_init(void)
* in the system.
*/
sunzilog_reg
.
nr
=
NUM_CHANNELS
;
#ifdef CONFIG_SERIAL_CONSOLE
sunzilog_reg
.
cons
=
&
sunzilog_console
;
#else
sunzilog_reg
.
cons
=
NULL
;
#endif
sunzilog_reg
.
minor
=
sunserial_current_minor
;
sunserial_current_minor
+=
NUM_CHANNELS
;
ret
=
uart_register_driver
(
&
sunzilog_reg
);
if
(
ret
==
0
)
{
...
...
include/asm-sparc64/ns87303.h
View file @
e79e45cb
...
...
@@ -78,9 +78,13 @@
#ifdef __KERNEL__
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/io.h>
extern
spinlock_t
ns87303_lock
;
static
__inline__
int
ns87303_modify
(
unsigned
long
port
,
unsigned
int
index
,
unsigned
char
clr
,
unsigned
char
set
)
{
...
...
@@ -96,14 +100,16 @@ static __inline__ int ns87303_modify(unsigned long port, unsigned int index,
if
(
index
>
0x0d
)
return
-
EINVAL
;
save_flags
(
flags
);
cli
();
spin_lock_irqsave
(
&
ns87303_lock
,
flags
);
outb
(
index
,
port
);
value
=
inb
(
port
+
1
);
value
&=
~
(
reserved
[
index
]
|
clr
);
value
|=
set
;
outb
(
value
,
port
+
1
);
outb
(
value
,
port
+
1
);
restore_flags
(
flags
);
spin_unlock_irqrestore
(
&
ns87303_lock
,
flags
);
return
0
;
}
...
...
include/linux/serial_core.h
View file @
e79e45cb
...
...
@@ -51,6 +51,10 @@
#define PORT_UART00 35
#define PORT_21285 37
/* Sparc type numbers. */
#define PORT_SUNZILOG 38
#define PORT_SUNSAB 39
#ifdef __KERNEL__
#include <linux/config.h>
...
...
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